From 2e5fa40c191d0f1baaf478bce0d161e98e02d79b Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Wed, 28 Oct 2026 18:33:43 +0800 Subject: [PATCH] Reserve serif glyph width inside vertical title slots --- apps/studio/src/lib/native-layout.ts | 2 +- scripts/verify-studio-visual.cjs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/studio/src/lib/native-layout.ts b/apps/studio/src/lib/native-layout.ts index ea19c4f..e44c158 100644 --- a/apps/studio/src/lib/native-layout.ts +++ b/apps/studio/src/lib/native-layout.ts @@ -41,7 +41,7 @@ export function slotPosition(layout:Layout,slot:Slot):CSSProperties { export function textSize(layout:Layout,slot:Slot,text:string):number { const [, ,w,h]=slot.box,lines=text.split('\n'); const length=(s:string)=>Array.from(s).reduce((n,c)=>n+(/[\u3400-\u9fff]/.test(c)?1:/[A-Z]/.test(c)?.8:.6),0); - if(slot.direction==='vertical')return Math.min(w*.9,h/Math.max(1,Array.from(text.replaceAll('\n','')).length)*.88); + if(slot.direction==='vertical')return Math.min(w*.75,h/Math.max(1,Array.from(text.replaceAll('\n','')).length)*.88); if(slot.direction==='rotated')return Math.min(w*.85,h/Math.max(1,length(text))); if(slot.direction==='curved')return Math.min(2.2,Math.max(w,h)*1.1/Math.max(1,length(text))); const fitted=Math.min(h/(lines.length*1.15),w/Math.max(1,...lines.map(length)))*.92; diff --git a/scripts/verify-studio-visual.cjs b/scripts/verify-studio-visual.cjs index 810681b..beb8608 100644 --- a/scripts/verify-studio-visual.cjs +++ b/scripts/verify-studio-visual.cjs @@ -17,7 +17,10 @@ const assert=require('node:assert/strict'); const metrics=process.argv.includes('--after-layouts')?JSON.parse(await readFile(dir+'/metrics.json','utf8')).metrics:[]; for(let i=1;i<=27&&!process.argv.includes('--after-layouts');i++){ const id='ref-'+String(i).padStart(2,'0');await page.goto(base+'/studio/layouts/'+id,{waitUntil:'networkidle'});await page.locator('article.native-layout').screenshot({path:dir+'/'+id+'.png',style:'.page-tools{visibility:hidden}'}); - const m=await page.locator('article.native-layout').evaluate(el=>({id:el.dataset.layoutId,elements:el.querySelectorAll('[data-element]').length,width:el.clientWidth,height:el.clientHeight,overflow:[...el.querySelectorAll('.native-copy')].filter(e=>e.scrollHeight>e.clientHeight+2||e.scrollWidth>e.clientWidth+2).map(e=>e.closest('[data-element]').dataset.element)}));metrics.push(m);await writeFile(dir+'/metrics.json',JSON.stringify({metrics,images,errors},null,2)); + const m=await page.locator('article.native-layout').evaluate(el=>{ + const overflow=[...el.querySelectorAll('.native-copy')].filter(e=>e.scrollHeight>e.clientHeight+2||e.scrollWidth>e.clientWidth+2); + return {id:el.dataset.layoutId,elements:el.querySelectorAll('[data-element]').length,width:el.clientWidth,height:el.clientHeight,overflow:overflow.map(e=>e.closest('[data-element]').dataset.element),overflowDetails:overflow.map(e=>{const style=getComputedStyle(e);return {id:e.closest('[data-element]').dataset.element,text:e.textContent,clientWidth:e.clientWidth,scrollWidth:e.scrollWidth,clientHeight:e.clientHeight,scrollHeight:e.scrollHeight,fontSize:style.fontSize,lineHeight:style.lineHeight,letterSpacing:style.letterSpacing,fontFamily:style.fontFamily,fontWeight:style.fontWeight,writingMode:style.writingMode}})}; + });metrics.push(m);await writeFile(dir+'/metrics.json',JSON.stringify({metrics,images,errors},null,2)); } if(!process.argv.includes('--after-layouts')&&!process.argv.includes('--diagnose'))assert.deepEqual(metrics.filter(m=>m.overflow.length),[],'Default copy must fit every native slot'); await page.goto(base+'/studio',{waitUntil:'networkidle'});assert.equal(await page.locator('.library-item').count(),27);await page.screenshot({path:dir+'/catalog-desktop.png',fullPage:true});