Files
probably-rendering-one-more…/content/studio-tools/layouts-api.md
T
codex-bot 94932418ca
studio CI and deployment / verify-and-deploy (push) Successful in 5m55s
waterfall-wiki CI and deployment / verify-and-deploy (push) Failing after 18m23s
Connect all 27 source layouts to parameterized Studio pages and shared design storage
2026-10-27 14:51:17 +08:00

132 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 27 套排版 · Studio 参数 API 与保存教程
把这篇 post 交给 agent,即可选排版、预览、调整参数并保存可再次打开的效果。每套 ref-01…ref-27 都有独立页面,与原图的 T(文字)、I(图位)、D(装饰)构件对应。
## 先看效果,再通过 API 调整
{{LAYOUTS}}
以上页面都可以修改文字、图位颜色或图片、装饰颜色和显隐、纸色,也能输入完整参数 JSON。打开“显示 T/I/D 构件边界”核对位置;“原图与构件对应表”可查看原图及坐标。页面下方可找到已保存的此排版效果。
## Agent 调用顺序
1. 读取 [全部 27 套排版目录与接口](/studio/api/layouts?agent_mode=True),选择 ID。
2. GET `/studio/api/layouts/ref-01` 获取当前模板的真实构件 ID、坐标、方向与可辨识文字。也可以先读 [渲染接口说明](/studio/api/layouts/ref-01?agent_mode=True)。不要把其他模板的 T/I/D ID 混用。
3. POST 同地址传入参数,得到 SVG。渲染不会自动保存;迭代参数直到效果合适。
4. POST `/api/studio/designs` 保存新效果,返回 `design.id`、`design.url` 与 `design.revision`。通过 `design.url` 可直接看效果。
5. GET `/api/studio/designs/{id}` 读取上次参数;PATCH 同地址携带 revision 更新同一 ID。要保留两个不同版本,使用 POST 保存两个新 ID。
所有 API 与 Wiki 共用现有登录 Cookie 或 Bearer JWT。浏览器修改请求必须同源。Studio 本身不配置数据库连接,保存由 Wiki 写入同一个 PostgreSQL。
## 参数示例:ref-01
```json
{
"texts": {
"T01": {"value": "STUDY", "color": "#26362f", "scale": 1, "align": "start"},
"T02": {"value": "知识合集\n从记录到创作"}
},
"images": {
"I01": {"color": "#7f9188"},
"I02": {"color": "#b7c4bb"}
},
"decorations": {"D01": {"color": "#53645b", "hidden": false}},
"paper": "#f4f2eb"
}
```
文字最多 500 字、8 行,scale 范围 0.25–2,align 为 start/middle/end。文字 value 为空则移除这一组;删除其参数键则恢复原图字形。图片可以只改色,也可先通过 `/api/upload` 的 `assetOnly=true` 上传,再传入 `assetId` UUID 与 `fit: cover/contain`;不接受任意图片网址。装饰支持颜色与 hidden。参数在画板内裁切,未知字段、错误构件 ID、非法颜色和超限输入返回 400。请求最大 64KB。
原始参考来自低分辨率拼图。默认效果保留原图;未替换的构件继续使用原始分层,图位使用原始形状蒙版。可替换文字生成 SVG 文字;无法辨识的小字标为 unreadable,保留原图层而不编造文字。纸色作用于原有底层,不替代图位色块。页面预览通过同站媒体读取图片;API 导出时会使用当前登录读取 Wiki 有尺寸上限的 WebP 预览,并内嵌到 SVG,使下载结果不依赖外部图片链接。原始大图仍保留在 Wiki,导出使用最长 960 像素的预览。
## HTML / JavaScript:渲染与保存
```html
<div id="preview"></div>
<script type="module">
const layoutId = 'ref-01';
const parameters = {texts:{T01:{value:'STUDY'}},images:{I01:{color:'#7f9188'}}};
const response = await fetch(`/studio/api/layouts/${layoutId}`, {
method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(parameters)
});
if (!response.ok) throw new Error(await response.text());
const previewUrl = URL.createObjectURL(await response.blob());
const image = new Image(); image.src = previewUrl;
document.querySelector('#preview').replaceChildren(image);
// 预览完成后可 URL.revokeObjectURL(previewUrl)。
const saved = await fetch('/api/studio/designs', {
method:'POST', headers:{'Content-Type':'application/json'},
body:JSON.stringify({layoutId,name:'知识合集第一版',parameters})
});
if (!saved.ok) throw new Error(await saved.text());
const {design} = await saved.json();
console.log(design.id, design.url, design.revision);
</script>
```
## React:参数变化重新预览
```tsx
import {useEffect,useState} from 'react';
export function Preview(){
const [title,setTitle]=useState('STUDY'),[src,setSrc]=useState(''),[error,setError]=useState('');
useEffect(()=>{
const controller=new AbortController();let url='';
fetch('/studio/api/layouts/ref-01',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({texts:{T01:{value:title}}}),signal:controller.signal})
.then(async r=>{if(!r.ok)throw Error(await r.text());return r.blob()})
.then(blob=>{if(!controller.signal.aborted){url=URL.createObjectURL(blob);setSrc(url);setError('')}})
.catch(e=>{if(!controller.signal.aborted)setError(e.message)});
return()=>{controller.abort();if(url)URL.revokeObjectURL(url)};
},[title]);
return <><input value={title} onChange={e=>setTitle(e.target.value)}/>{error&&<p>{error}</p>}{src&&<img src={src} alt="当前排版效果"/>}</>;
}
```
## Next.js:客户端编辑组件
```tsx
// app/layout-playground/page.tsx
'use client';
import {useState} from 'react';
export default function Playground(){
const [title,setTitle]=useState('STUDY'),[link,setLink]=useState(''),[status,setStatus]=useState('');
async function save(){
const response=await fetch('/api/studio/designs',{method:'POST',headers:{'Content-Type':'application/json'},
body:JSON.stringify({layoutId:'ref-01',name:title||'排版效果',parameters:{texts:{T01:{value:title}}}})});
const data=await response.json();if(!response.ok){setStatus(data.error);return}setLink(data.design.url);setStatus('已保存');
}
return <><input value={title} onChange={e=>setTitle(e.target.value)}/><button onClick={save}>保存新效果</button><p>{status}</p>{link&&<a href={link}>打开效果</a>}</>;
}
```
这些前端示例应放在已登录的 PROMPT 同站环境。服务端 agent 使用既有 Bearer JWT,只在进程内读取凭据,勿把令牌写入 post、源码、日志或 URL。接口错误不可当作渲染结果处理。
## 按 ID 读取、修改、保留分支
```js
const headers = {'Content-Type':'application/json'};
const read = await fetch(`/api/studio/designs/${id}`);
if (!read.ok) throw Error(await read.text());
const {design} = await read.json();
const parameters = {...design.parameters,texts:{...design.parameters.texts,T01:{value:'第二版'}}};
const update = await fetch(`/api/studio/designs/${id}`, {
method:'PATCH',headers,
body:JSON.stringify({name:'知识合集第二版',parameters,revision:design.revision})
});
if (update.status===409) throw Error('有人已经修改,请重新读取后合并');
if (!update.ok) throw Error(await update.text());
// 想保留旧版时,改用 POST /api/studio/designs,传 {layoutId:design.layoutId,name,parameters}。
```
PATCH 的 parameters 整体替换,因此先 GET 并合并未修改的构件;layoutId 不可改变。错误:400 参数不合法、401 未登录、404 不存在、409 修订冲突、413 超限。[效果保存接口说明](/api/studio/designs?agent_mode=True)。列表可用 `layoutId` 筛选、`limit=1..50`;返回 nextCursor 后用 `before=<nextCursor>` 继续。
## 验收标准
- 27 个 ID 都有独立效果页、参数说明与渲染 API,来源链接与 T/I/D 构件一一对应。
- 默认效果展示原图;替换一组文字、一个图位或装饰后,只改变指定构件。
- API 渲染与界面使用相同模板、验证和渲染器;导出的 SVG 不包含辅助边界。
- 保存返回独立 UUID,刷新或再次访问效果 URL 能恢复参数;更新后 revision 增加,过期修订返回 409。
- 图片 assetId 必须是 Wiki 中已上传的图片;不接受未知构件或外站 URL。
- Wiki 与 Studio 共用现有登录和数据库,Studio 无独立持久化或数据库凭据。