Files
probably-rendering-one-more…/apps/studio/src/components/NativeResult.tsx
T
codex-bot 6a7d924436
studio CI and deployment / verify-and-deploy (pull_request) Has been cancelled
waterfall-wiki CI and deployment / verify-and-deploy (pull_request) Has been cancelled
Preserve exported revisions and provide a remote delivery command
2026-10-28 23:51:01 +08:00

29 lines
4.2 KiB
TypeScript

import Head from 'next/head';
import type {CSSProperties} from 'react';
import type {AddedSlot,Layout,Parameters} from '../lib/layout-parameters';
import {CurvedCopy} from './NativeCurvedCopy';
import {decorationKind,defaultText,emptyParameters,imageShape,paperColor,slotPosition,textColor,textSize,type Design} from '../lib/native-layout';
/** A saved result contains artwork only: no editing state, controls or event handlers. */
export function NativeResult({layout,design}:{layout:Layout;design:Design|null}){
return <main className="native-layout-page"><Head><title>{design?.name||layout.name} · Prompt</title></Head><NativeArtwork layout={layout} design={design}/></main>;
}
export function NativeArtwork({layout,design,quality=false}:{layout:Layout;design:Design|null;quality?:boolean}){
const params:Parameters=design?.parameters||emptyParameters();
const elements=[...layout.elements,...(params.added||[])];
return <article className={'native-layout '+layout.id} aria-label={design?.name||layout.name} data-layout-id={layout.id} data-design-id={design?.id} style={{aspectRatio:`${layout.width}/${layout.height}`,height:quality?Math.round(1080*layout.height/layout.width):undefined,backgroundColor:params.paper||paperColor(layout.id)}}>
{['image','decoration','text'].flatMap(kind=>elements.filter(s=>s.kind===kind).map(s=>{
const t=params.texts[s.id],im=params.images[s.id],d=params.decorations[s.id],ls=params.layers?.[s.id]||{};
if(ls.hidden||(kind==='decoration'&&d?.hidden))return null;
const text=t?.value??defaultText(layout,s);
const position:CSSProperties={...slotPosition(layout,{...s,box:[ls.x??s.box[0],ls.y??s.box[1],ls.width??s.box[2],ls.height??s.box[3]]}),transform:`rotate(${ls.rotation||0}deg)`,opacity:ls.opacity??1,borderRadius:ls.radius===undefined?undefined:`${ls.radius/layout.width*100}cqw`,zIndex:ls.z??(kind==='text'?3:kind==='image'?1:2)};
const textStyle:CSSProperties={fontSize:`${(t?.fontSize??textSize(layout,s,text)*(t?.scale||1))/layout.width*100}cqw`,color:t?.color||textColor(layout.id,s),fontFamily:t?.fontFamily==='serif'?'Georgia,"Noto Serif CJK SC","Songti SC",serif':t?.fontFamily==='mono'?'"Noto Sans Mono CJK SC",monospace':t?.fontFamily==='sans'?'Arial,"Noto Sans CJK SC","PingFang SC","Microsoft YaHei",sans-serif':undefined,fontWeight:t?.fontWeight,lineHeight:t?.lineHeight,letterSpacing:t?.letterSpacing===undefined?undefined:`${t.letterSpacing/layout.width*100}cqw`,textAlign:t?.align==='middle'?'center':t?.align==='end'?'right':'left'};
return <div key={s.id} className={'native-element native-'+kind} data-element={s.id} aria-label={s.role} style={position}>
{kind==='text'?(s.direction==='curved'?<CurvedCopy layout={layout} slot={s} value={text} spacing={t?.letterSpacing} style={textStyle}/>:<div className={'native-copy direction-'+s.direction} style={textStyle}>{text}</div>):kind==='image'?<div className="native-image-fill" style={{...imageShape(layout,s),...(im?.color?{backgroundColor:im.color}:{}),...(ls.radius!==undefined?{borderRadius:`${ls.radius/layout.width*100}cqw`}:{})}}>
{im?.assetId&&<img src={'/api/preview?'+new URLSearchParams({source:'/api/media/'+im.assetId,width:quality?'2160':'960',...(quality?{profile:'export'}:{})})} srcSet={quality?undefined:[240,480,960,1280].map(width=>'/api/preview?'+new URLSearchParams({source:'/api/media/'+im.assetId!,width:String(width)})+' '+width+'w').join(',')} sizes={`(max-width: 860px) ${Math.min(100,(ls.width??s.box[2])/layout.width*100)}vw, ${Math.min(860,(ls.width??s.box[2])/layout.width*860)}px`} alt={s.role} style={{objectFit:im.fit||'cover',objectPosition:`${im.cropX??50}% ${im.cropY??50}%`,transform:`scale(${im.zoom??1})`}}/>}
</div>:<div className={'native-decoration decoration-'+((s as AddedSlot).shape?'solid':decorationKind(s))} style={{borderRadius:ls.radius!==undefined?`${ls.radius/layout.width*100}cqw`:(s as AddedSlot).shape==='ellipse'?'50%':undefined,color:d?.color||'#83908a',backgroundColor:(s as AddedSlot).shape?d?.color||'#83908a':decorationKind(s)==='band'?d?.color||'#dce1d8':undefined}}>{decorationKind(s)==='lines'&&Array.from({length:s.box[3]>4?3:1},(_,i)=><i key={i}/>)}{decorationKind(s)==='vertical-lines'&&<><i/><i/><i/></>}</div>}
</div>;
}))}
</article>;
}