From 6ad29335480d64e96c3a9696e6c2a1072227f67e Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Mon, 26 Oct 2026 20:29:47 +0800 Subject: [PATCH] Document and publish Studio tools through ordinary Wiki APIs --- content/studio-tools/tutorial.md | 129 +++++++++++++++++++++++++++++++ scripts/publish-studio-tools.mjs | 18 +++++ 2 files changed, 147 insertions(+) create mode 100644 content/studio-tools/tutorial.md create mode 100644 scripts/publish-studio-tools.mjs diff --git a/content/studio-tools/tutorial.md b/content/studio-tools/tutorial.md new file mode 100644 index 0000000..b30f085 --- /dev/null +++ b/content/studio-tools/tutorial.md @@ -0,0 +1,129 @@ +这篇 post 是两件 Studio 封面工具的使用入口与 API 教程。工具负责可编辑预览和渲染;waterfall-wiki 保存说明、图片、来源链接与调用示例。当前只做模板与主题占位,不填入具体 Skills 合集。 + +## 打开工具 + +- [同一母版/四张试版](/studio/ideas/learning-skills-template):一张空白母版,加笔记复习、教学资料、英语学习三张占位封面。四张共用 ref-17 九个构件的坐标;可统一改英文刊头,分别改中文标题、简介和配色。 +- [三种构图/模板选择工具](/studio/ideas/layout-selection):切换 ref-01、ref-07、ref-26,每种构图独立编辑文字、主图色、副图色。切换模板保留当前页内各模板的修改;刷新恢复初始值。 + +Studio 没有独立目录首页;访问 /studio 会回到 waterfall-wiki。请从本文的具体工具链接进入。页面上的下载可以保存 SVG、参数 JSON;工具不自动把试版发布为 Wiki post。 + +## 先看图,再选模板 + +{{GALLERY}} + +## 构件对应与来源 + +| 工具 | 模板 | 构图 | 构件数 | +| --- | --- | --- | --- | +| 同一母版四张试版 | [ref-17](/explore/7b1b2fc5b9690213bbbe07ac) | 两行英文刊头、圆形叠标、左下主图、右侧窄栏 | 9 | +| 三种构图 | [ref-01](/explore/eb937b42be92a9f6feee684f) | 双矩形错位、品牌字标、左下说明 | 6 | +| 三种构图 | [ref-07](/explore/7d4cefdfada28bbe9e570af3) | 同心圆主图、环形文字、底部年份与说明 | 7 | +| 三种构图 | {{REF26}} | 三行英文、右侧竖排标题、曲线与小图 | 11 | + +完整来源:[27 张原图 Router](/explore/8eb8c76500580f7a4b56ba3c)。各原图 post 内有文本、图片、原画板与 T/I/D 坐标,阅读纯文本可加 ?agent_mode=True。构件必须带上完整键,例如 ref-01/T01;不同模板不能混用同名构件。 + +T 是可编辑文字,I 是图片位或色块,D 是装饰。ref-01 的品牌字标只有一个 T01,左下说明为 T02,没有重复 T03。工具保持每张原图的画板比例。ref-17 为重新绘制的文字、色块与线条;另外三种模板的图位保留原始形状蒙版,装饰沿用从原图分离的 PNG 图层,文字重新排版。它们是来源约束下的试版,替换内容后的字形与色调不会与原图逐像素相同。低分辨率原图的装饰放大仍可能有像素感。 + +## API:读取模板与原始试版 + +接口统一使用 waterfall-wiki 的同一域名。所有接口说明先读: + +- [Studio API 目录](/studio/api) +- [封面工具 API 纯文本说明](/studio/api/export/learning-skills-template?agent_mode=True) + +```bash +BASE='http://8.211.154.134:18131' +# version:2,四张试版、共享模板、原始几何和来源链接 +curl "$BASE/studio/api/export/learning-skills-template" +# 第 2 张初始封面,slide 从 0 开始 +curl "$BASE/studio/api/export/learning-skills-template?format=svg&slide=1" -o notes.svg +# version:3,三种模板的 slots、fields、内嵌图位蒙版与装饰 +curl "$BASE/studio/api/export/layout-selection" +# 指定原图模板 +curl "$BASE/studio/api/export/layout-selection?format=svg&layoutId=ref-07" -o circle.svg +``` + +GET 省略 format 返回 JSON;format=markdown 返回文本资料;format=svg 返回初始封面。JSON 是 AI 可读取的几何与文字数据,SVG 是可独立打开的图像。 + +## API:提交自己的参数生成 SVG + +POST 是无状态渲染,不修改 Wiki 或工具初始数据。Content-Type 为 application/json;format=svg 为默认值,format=json 返回经过校验的参数与模板。请求最大 32KB,返回的正式 SVG 不带构件辅助线。 + +```bash +curl -X POST "$BASE/studio/api/export/learning-skills-template?format=svg" \ + -H 'Content-Type: application/json' \ + --data '{"slide":1,"heading":"笔记复习","body":"课程 · 笔记\n复习主题占位\n正文稍后填入","color":"#7a8b7e","accent":"#d5ddcf","masthead":["STUDY","Skills"]}' \ + -o notes-custom.svg + +curl -X POST "$BASE/studio/api/export/layout-selection?format=svg" \ + -H 'Content-Type: application/json' \ + --data '{"layoutId":"ref-01","fields":{"T01":"STUDY","T02":"笔记复习\n主题说明占位\n正文稍后填入"},"color":"#83918a","accent":"#c3cec5"}' \ + -o ref-01-custom.svg +``` + +四张试版参数:slide 为 0–3 的整数;heading 为 1–6 字;body 最多 360 字、3 行;masthead 为两行英文,每行 1–8 个英文字母或空格。多模板参数:layoutId 必填;fields 只能使用目标模板真实存在的 T 编号,每项 1–120 字、最多 4 行;未提供的字段保留默认值。颜色必须是 #RRGGBB。未知模板、构件 ID、参数、超长文字或无效颜色返回 400;不存在的工具返回 404;不支持的方法返回 405;请求过大返回 413。 + +## HTML / CSS 使用 + +只需展示初始封面时,直接把 SVG 接口作为图像,无需部署新页面或复制图层: + +```html +
+ 笔记复习占位封面 +
+ +``` + +## React 使用 + +```tsx +export function TemplateCover({ slide = 1 }: { slide?: number }) { + return 同一母版的合集封面; +} +``` + +自定义渲染:通过你的服务端调用 POST,保存返回的 SVG 或将其作为图像响应交给浏览器。接口没有开放任意跨站 JavaScript 的 CORS;跨域浏览器可以用 img 展示 GET 图片,POST 则从你自己的后端调用。 + +## Next.js 使用 + +```ts +// app/api/cover/route.ts:为自己应用生成 SVG,不把远端 SVG 插入 innerHTML。 +export async function GET() { + const upstream = await fetch( + 'http://8.211.154.134:18131/studio/api/export/layout-selection?format=svg', + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + layoutId: 'ref-26', + fields: { T01: 'Study 01', T02: 'Learn', T03: 'Create', T04: '学习合集' }, + color: '#7c8e9b' + }), + cache: 'no-store' + } + ); + if (!upstream.ok) return new Response('封面生成失败', { status: 502 }); + return new Response(await upstream.arrayBuffer(), { + headers: { 'Content-Type': 'image/svg+xml; charset=utf-8' } + }); +} +// 页面中使用 学习合集封面。 +``` + +如果自己的站点使用 HTTPS,应通过同源后端代理此 HTTP 服务,避免浏览器混合内容限制。 + +## 试版验收 + +1. 四张同母版封面的 ref-17 构件数、坐标、图层次序完全相同;只替换文字与颜色。 +2. 多模板工具的 ref-01、ref-07、ref-26 分别有 6、7、11 个构件,不能偷换构图。 +3. T 文本可编辑,I 图片位是占位色块,D 装饰不冒充文字;原图未辨识的微字不猜写为“原文”。 +4. 浏览器预览与 API 使用同一渲染组件。修改后不溢出;环形、竖排、两矩形错位保持原位置。 +5. SVG 独立打开无缺图;API 不需要先访问 Studio 首页;教程可作为普通 Wiki post 的纯文本读取。 +6. 本阶段只有模板和主题占位,没有自动生成或发布三篇具体 Skills 合集。 diff --git a/scripts/publish-studio-tools.mjs b/scripts/publish-studio-tools.mjs new file mode 100644 index 0000000..cf618ff --- /dev/null +++ b/scripts/publish-studio-tools.mjs @@ -0,0 +1,18 @@ +/** Publish a normal Wiki tutorial using only generic post, tag and media APIs. */ +import {readFile,writeFile,mkdir} from 'node:fs/promises'; +const base=(process.env.WIKI_BASE||'http://8.211.154.134:18131').replace(/\/$/,''),out='output/studio-tools',publicId='9ebd8f78d54b4b9db741b4e8'; +await mkdir(out,{recursive:true}); +async function api(path,options={}){const r=await fetch(base+path,{...options,signal:AbortSignal.timeout(60000)});if(r.status===404)return null;if(!r.ok)throw Error(path+': '+r.status+' '+(await r.text()).slice(0,300));return r.json()} +const post=(path,body,method='POST')=>api(path,{method,headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}); +let state;try{state=JSON.parse(await readFile(out+'/published.json','utf8'))}catch{state={base,assets:{}}}if(state.base!==base)throw Error('Wrong publish target'); +for(const name of ['cover.png','ref17-master.png','ref17-notes.png','ref17-teaching.png','ref17-english.png','ref-01.png','ref-07.png','ref-26.png']){ + if(state.assets[name])continue;const form=new FormData();form.set('kind','image');form.set('assetOnly','true');form.append('files',new Blob([await readFile(out+'/'+name)],{type:'image/png'}),name); + const data=await api('/api/upload',{method:'POST',body:form});state.assets[name]=data.assets[0];await writeFile(out+'/published.json',JSON.stringify(state,null,2)); +} +const ids=JSON.parse(await readFile('content/layout-reference/post-ids.json','utf8')); +const gallery=Object.entries(state.assets).filter(([name])=>name!=='cover.png').map(([name,id])=>`![${name.replace('.png','')} 模板试版](/api/media/${id})`).join('\n\n'); +const text=(await readFile('content/studio-tools/tutorial.md','utf8')).replace('{{GALLERY}}',gallery).replace('{{REF26}}',`[ref-26](/explore/${ids['ref-26']})`); +const tag=await post('/api/tags',{name:'Studio工具',slug:'studio-tools'}),title='Studio 封面工具 · 同一母版与三种构图',description='从 waterfall-wiki 进入两件 Studio 工具:ref-17 四张同母版试版,以及 ref-01/07/26 三种构图。含可访问图像、GET/POST API、HTML/React/Next.js 示例与构件验收。'; +const assets=Object.values(state.assets).map(id=>({id,role:'content'})),coverAssetId=state.assets['cover.png'],old=await api('/api/notes/'+publicId); +state.post=old?await post('/api/notes/'+publicId,{title,description,markdown:text,tags:[tag.id],assets,coverAssetId,revision:old.entry.revision},'PUT'):await post('/api/posts',{publicId,slug:'studio-cover-tools',kind:'article',title,description,text,tags:[tag.id],assets,coverAssetId}); +await writeFile(out+'/published.json',JSON.stringify(state,null,2));console.log(base+'/explore/'+publicId);