31 lines
5.5 KiB
TypeScript
31 lines
5.5 KiB
TypeScript
import {referencePreview} from '../lib/layout-images-client';
|
||
import {useState} from 'react';
|
||
import Head from 'next/head';
|
||
import {tutorialPost} from '../content/tool-links';
|
||
import {ideaMarkdown,type Idea,type Slide} from '../content/ideas';
|
||
import {Ref17Cover} from './Ref17Cover';
|
||
export function TemplateTrial({idea}:{idea:Idea}){
|
||
const template=idea.template!;
|
||
const [slides,setSlides]=useState<Slide[]>(idea.slides),[active,setActive]=useState(0),[masthead,setMasthead]=useState(template.masthead),[guides,setGuides]=useState(false),[notice,setNotice]=useState('');
|
||
const selected=slides[active],current={...idea,slides,template:{...template,masthead}};
|
||
const update=(patch:Partial<Slide>)=>setSlides(old=>old.map((slide,i)=>i===active?{...slide,...patch}:slide));
|
||
const download=(text:string,type:string,name:string)=>{const url=URL.createObjectURL(new Blob([text],{type})),a=document.createElement('a');a.href=url;a.download=name;a.click();setTimeout(()=>URL.revokeObjectURL(url),500)};
|
||
async function exportSvg(){await document.fonts.ready;const svg=document.querySelector(`[data-proof="${selected.key}"] svg`);if(!svg)return;download(new XMLSerializer().serializeToString(svg),'image/svg+xml',`ref-17-${selected.key}.svg`);setNotice('已下载当前封面 SVG,可继续编辑文字与图形。')}
|
||
const reset=()=>{setSlides(idea.slides);setMasthead(template.masthead);setGuides(false);setNotice('已恢复初始试版。')};
|
||
return <main className="template-trial"><Head><title>{idea.title} · Studio</title><link rel="alternate" type="text/markdown" href={'/studio/api/export/'+idea.slug+'?format=markdown'}/></Head>
|
||
<header className="trial-nav"><a href={tutorialPost}>← PROMPT 使用教程</a><span>STUDIO / TEMPLATE PROOF</span><a href={template.tutorialUrl} target="_blank" rel="noreferrer">排版教程 ↗</a></header>
|
||
<section className="trial-intro"><div><small>REF-17 · 同一母版 / 四张试版</small><h1>先试封面,再填内容。</h1><p>英文刊头、圆形叠标、左下主图与右侧窄栏保持同一套坐标。三组合集先用主题占位,暂不放入具体 Skills。</p></div><div className="trial-source"><b>01 母版</b><span>02 笔记复习</span><span>03 教学资料</span><span>04 英语学习</span></div></section>
|
||
<div className="trial-workspace"><aside className="trial-controls"><h2>试一试</h2><label>当前封面<select value={active} onChange={e=>{setActive(Number(e.target.value));setNotice('')}}>{slides.map((s,i)=><option value={i} key={s.key}>{s.label}</option>)}</select></label>
|
||
<fieldset><legend>共享英文刊头</legend><label>第一行<input value={masthead[0]} maxLength={8} onChange={e=>setMasthead([e.target.value.toUpperCase().replace(/[^A-Z ]/g,''),masthead[1]])}/></label><label>第二行<input value={masthead[1]} maxLength={8} onChange={e=>setMasthead([masthead[0],e.target.value.replace(/[^A-Za-z ]/g,'')])}/></label></fieldset>
|
||
<label>本张标题 <small>建议 4–6 字</small><input maxLength={6} value={selected.heading} onChange={e=>update({heading:e.target.value})}/></label>
|
||
<label>右侧简介 <small>最多三行</small><textarea rows={3} value={selected.body} onChange={e=>update({body:e.target.value.split('\n').slice(0,3).join('\n')})}/></label>
|
||
<div className="trial-colors"><label>主图色块<input type="color" value={selected.color} onChange={e=>update({color:e.target.value})}/></label><label>圆形与贴条<input type="color" value={selected.accent} onChange={e=>update({accent:e.target.value})}/></label></div>
|
||
<label className="trial-switch"><input type="checkbox" checked={guides} onChange={e=>setGuides(e.target.checked)}/>显示构件边界</label>
|
||
<div className="trial-actions"><button className="trial-primary" onClick={exportSvg}>下载当前 SVG</button><button onClick={()=>download(JSON.stringify({version:2,...current},null,2),'application/json',idea.slug+'.json')}>导出当前试版 JSON</button><button onClick={()=>download(ideaMarkdown(current),'text/markdown',idea.slug+'.md')}>导出说明 Markdown</button><button onClick={reset}>恢复初始试版</button></div>
|
||
<p className="trial-help">修改仅保留在当前页面,刷新会恢复;可下载 JSON 保存试版。主视觉仍是色块占位。</p><p role="status" className="trial-notice">{notice}</p>
|
||
</aside><div><div className="trial-grid">{slides.map((slide,i)=><figure className={'proof-card '+(i===active?'proof-active':'')} key={slide.key} data-proof={slide.key}><button className="proof-select" onClick={()=>setActive(i)} aria-label={'编辑'+slide.label} aria-pressed={i===active}><Ref17Cover template={template} slide={slide} masthead={masthead} guides={guides}/></button><figcaption><b>{slide.label}</b><span>共用 REF-17</span><a href={'/studio/api/export/'+idea.slug+'?format=svg&slide='+i}>初始 SVG ↗</a></figcaption></figure>)}</div>
|
||
<details className="trial-reference"><summary>查看原图与构件对应</summary><div className="trial-reference-body"><img src={referencePreview(template.referenceUrl)} alt="ref-17 原始参考裁片"/><div><p>保留 {template.width}×{template.height} 的画板比例与九个构件位置;本试版重新绘制可编辑文字与色块,替换了文案和色调。</p><table><thead><tr><th>构件</th><th>用途</th><th>原图坐标</th></tr></thead><tbody>{template.slots.map(s=><tr key={s.id}><td>{s.id}</td><td>{s.role}</td><td>{s.box.join(', ')}</td></tr>)}</tbody></table><a href={template.tutorialUrl+'?agent_mode=True'}>读取教程纯文本 ↗</a></div></div></details>
|
||
</div></div>
|
||
</main>;
|
||
}
|