dsh-motion-ui 0.7.1 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/README.md +48 -2
- package/VERIFICATION.md +24 -1
- package/client.js +693 -99
- package/index.js +15 -7
- package/lib/assets/mermaid.js +382 -382
- package/lib/assets/standalone-runtime.js +1 -1
- package/lib/assets/visual.js +13 -13
- package/lib/build-info.js +1 -1
- package/lib/canvas-export.js +3 -3
- package/lib/canvas-source.js +27 -0
- package/lib/export-runtime-bundle.js +1 -1
- package/lib/freeform.js +2 -2
- package/lib/inspection.js +8 -0
- package/lib/motion-craft.js +31 -0
- package/lib/motion-design-skill.js +23 -0
- package/lib/motion-library.js +4 -2
- package/lib/project-archive.js +16 -0
- package/lib/prompt-contract.js +1 -1
- package/lib/refinement.js +33 -1
- package/lib/sandbox.js +6 -2
- package/lib/timeline.js +29 -0
- package/lib/tool-json.js +5 -0
- package/lib/video-tools.js +21 -0
- package/lib/video.js +296 -0
- package/lib/voice.js +72 -0
- package/package.json +17 -9
- package/skills/motion-design/ADAPTER.md +35 -0
- package/skills/motion-design/LICENSE +201 -0
- package/skills/motion-design/NOTICE.md +9 -0
- package/skills/motion-design/SKILL.md +388 -0
- package/skills/motion-design/SOURCES.md +16 -0
- package/skills/motion-design/upstream.json +8 -0
package/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import {sceneDocument,SCENE_PARAMETERS,sceneReference} from './lib/scene-entry.js';
|
|
2
|
+
import {registerVideoTools} from './lib/video-tools.js';
|
|
3
|
+
import {VIDEO_GUIDE,PROMO_GUIDE} from './lib/motion-craft.js';
|
|
4
|
+
import {loadMotionDesignSkill,MOTION_DESIGN_REFERENCE} from './lib/motion-design-skill.js';
|
|
5
|
+
import {normalizeNodeEdits} from './lib/refinement.js';
|
|
2
6
|
import {TEXT_GUIDE} from './lib/text-guide.js';
|
|
3
7
|
import {CANVAS_CONTRACT,TOOL_DIRECTORY} from './lib/prompt-contract.js';
|
|
4
8
|
import {mechanismDocument} from './lib/mechanism-entry.js';
|
|
@@ -22,7 +26,7 @@ import { toolJson } from './lib/tool-json.js';
|
|
|
22
26
|
import { DOCUMENT_PARAMETER,TEXT_BLOCKS_PARAMETER } from './lib/tool-schema.js';
|
|
23
27
|
import {textDocument} from './lib/text-document.js';
|
|
24
28
|
import {TEXT_PRESETS} from './lib/typography.js';
|
|
25
|
-
import { inspectRecord } from './lib/inspection.js';
|
|
29
|
+
import { inspectRecord,normalizeNodeIds } from './lib/inspection.js';
|
|
26
30
|
import {QUICK_EXAMPLES,QUICK_GUIDE} from './lib/quick-examples.js';
|
|
27
31
|
import { PROTOCOL_GUIDE, calculatorExample, comparisonExample, budgetExample, curveExample, sceneExamples, bicycleExample, oscillatorExample } from './lib/catalog.js';
|
|
28
32
|
import {VISUAL_GUIDE,VISUAL_EXAMPLES,visualCatalog} from './lib/visual.js';
|
|
@@ -42,13 +46,14 @@ function defineRenderTool(options) {
|
|
|
42
46
|
// string branch that encourages models to manually escape whole documents.
|
|
43
47
|
// Canonicalize adapter quirks before native schema checks, so a lossless
|
|
44
48
|
// items:{item:[...]} wrapper receives the same treatment as stored/replayed UI.
|
|
45
|
-
const normalized=args&&Object.hasOwn(args,'document')?{...args,document:validateDocument(toolJson(args.document,'document'))}:args&&Object.hasOwn(args,'blocks')?{...args,blocks:textDocument({...args,blocks:toolJson(args.blocks,'blocks')}).nodes}:args;
|
|
49
|
+
const normalized=args&&Object.hasOwn(args,'document')?{...args,document:validateDocument(toolJson(args.document,'document'))}:args&&Object.hasOwn(args,'blocks')?{...args,blocks:textDocument({...args,blocks:toolJson(args.blocks,'blocks')}).nodes}:args&&Object.hasOwn(args,'changes')?{...args,changes:normalizeNodeEdits(args.changes)}:args&&Object.hasOwn(args,'nodeIds')?{...args,nodeIds:normalizeNodeIds(args.nodeIds)}:args;
|
|
46
50
|
return execute(normalized,exec);
|
|
47
51
|
}};
|
|
48
52
|
}
|
|
49
53
|
export const Config=z.object({autoRepair:z.boolean().default(true).description('自动反馈无法交付的 UI 围栏、渲染失败及仅思考没有正文或工具的空交付,最多两次去重提醒;不限制模型思考。'),visualStyle:z.union(VISUAL_STYLE_IDS).default(DEFAULT_VISUAL_STYLE).description('引导模式的视觉风格;同步原生组件外观、风格提示词与案例,自由模式不应用。'),defaultPreset:z.union(TEXT_PRESETS).default('reading').description('兼容旧配置的保留字段,已停用;排版由模型按内容选择。'),designMode:z.union(['guided','free']).default('guided').description('guided:设计技能与示例引导;free:只注入画布接口与交互约束,由模型自主设计。'),displayMode:z.union(['tail','inline','sidebar']).default('inline').description('兼容旧配置的保留字段,已停用;统一使用正文标记,侧栏随时可打开。'),showDetails:z.boolean().default(false).description('显示参数状态、原始说明、视觉诊断和版本历史。默认关闭,保持界面简洁。')}).volatile();
|
|
50
54
|
export function apply(ctx,config={}) {
|
|
51
55
|
const store = new UiStore(dshHomePath('plugins', 'dsh-generative-ui'));
|
|
56
|
+
|
|
52
57
|
const settings=()=>typeof config.get==='function'?config.get():config;
|
|
53
58
|
const feedback=registerFenceFeedback(ctx,()=>settings().autoRepair!==false);
|
|
54
59
|
const placement=value=>`\nPlace this exact fenced marker in your answer at the intended UI position:\n\`\`\`dsh-ui\n${value.documentId}\n\`\`\``;
|
|
@@ -89,9 +94,11 @@ export function apply(ctx,config={}) {
|
|
|
89
94
|
})));
|
|
90
95
|
ctx.effect(() => ctx.tools.register(defineTool({
|
|
91
96
|
name: 'ui_catalog', description: "Read one optional reference topic or scene. text=native typography; native=v1 fields/formulas; visual=extended components; canvas=API/construction/examples; style=selected guided style/examples; templates/motion/mechanism=targeted recipes. overview/quick/data/scenes/all remain available; avoid all unless needed.",
|
|
92
|
-
parameters: { topic: { type: 'string', enum: ['overview','quick','text','native','style','visual','motion','mechanism','templates','canvas','data','scenes','all'], description:'Optional topic; default overview.' },scene:{type:'string',description:'Scene for canvas/templates/motion/mechanism, or explicit style ID for style. Omit to get summaries/current guided style.'} }, output: { schema: { type: 'json' }, render: (_args, value) => [{ type: 'text', text: JSON.stringify(value) }] },
|
|
97
|
+
parameters: { topic: { type: 'string', enum: ['overview','quick','text','native','style','visual','motion','video','mechanism','templates','canvas','data','scenes','all'], description:'Optional topic; default overview.' },scene:{type:'string',description:'Scene for canvas/templates/motion/mechanism, or explicit style ID for style. Omit to get summaries/current guided style.'} }, output: { schema: { type: 'json' }, render: (_args, value) => [{ type: 'text', text: JSON.stringify(value) }] },
|
|
93
98
|
isConcurrencySafe: () => true,
|
|
94
99
|
async execute({ topic = 'overview',scene } = {}) {
|
|
100
|
+
if((topic==='motion'||topic==='video')&&scene==='motion-design')return {topic,designSkill:await loadMotionDesignSkill()};
|
|
101
|
+
if(topic==='video')return{topic,guide:VIDEO_GUIDE,designReference:MOTION_DESIGN_REFERENCE,...(scene==='promo'?{production:PROMO_GUIDE,designSkill:await loadMotionDesignSkill()}:{}),capabilities:await video.capabilities(),workflow:'Author render_canvas with exactly one DSHUI.timeline({id:"main",duration,draw}) and #stage. Then render_video({documentId,duration,...}). For input videos inspect_video then view sampled frames, process_video clips/audio/captions. No unrequested video export. Output URLs are authenticated same-host downloads; HTML sources are attachments, never executed in the Host origin.'};
|
|
95
102
|
if(topic==='text')return {topic,guide:TEXT_GUIDE};
|
|
96
103
|
if(topic==='native')return {topic,protocol:PROTOCOL_GUIDE,components:componentCatalog(),example:calculatorExample};
|
|
97
104
|
if(topic==='style'){if(settings().designMode==='free'&&!scene)return{topic,mode:'free',note:'No style is selected in free mode. An explicit scene equal to a style ID requests reference guidance only.',styles:VISUAL_STYLE_IDS};const id=scene??settings().visualStyle??DEFAULT_VISUAL_STYLE;return{topic,style:id,guide:stylePrompt(id),examples:styleExamples(id)}}
|
|
@@ -104,7 +111,7 @@ export function apply(ctx,config={}) {
|
|
|
104
111
|
if(topic==='quick')return{...sceneReference(),design:QUICK_GUIDE,fullDocuments:QUICK_EXAMPLES};
|
|
105
112
|
if(topic==='templates')return sceneCatalog(scene);
|
|
106
113
|
if(topic==='mechanism')return mechanismCatalog(scene);
|
|
107
|
-
if(topic==='motion')return motionCatalog(scene);
|
|
114
|
+
if(topic==='motion')return {...motionCatalog(scene),...(!scene?{designReferences:[MOTION_DESIGN_REFERENCE]}:{})};
|
|
108
115
|
const groups = { overview: [calculatorExample, oscillatorExample], canvas: [bicycleExample, oscillatorExample, architectureExample, scatterExample], data: [calculatorExample, comparisonExample, budgetExample, curveExample, scatterExample], scenes: sceneExamples, all: [calculatorExample, comparisonExample, budgetExample, curveExample, ...sceneExamples, bicycleExample, oscillatorExample, architectureExample, scatterExample] };
|
|
109
116
|
groups.quick=QUICK_EXAMPLES;groups.overview=QUICK_EXAMPLES.slice(0,2);
|
|
110
117
|
groups.visual=VISUAL_EXAMPLES;
|
|
@@ -114,19 +121,20 @@ export function apply(ctx,config={}) {
|
|
|
114
121
|
}
|
|
115
122
|
})));
|
|
116
123
|
ctx.effect(() => ctx.connection.fetch.register({ path: '/api/plugins/dsh-generative-ui/state', methods: ['GET', 'POST'], requestBody: 'buffered', fetch: stateRoute(store) }));
|
|
124
|
+
const video=registerVideoTools(ctx,store);
|
|
117
125
|
ctx.effect(() => ctx.connection.fetch.register({ path: '/api/plugins/dsh-generative-ui/document', methods: ['GET', 'POST'], requestBody: 'buffered', fetch: documentRoute(store) }));
|
|
118
126
|
ctx.effect(() => ctx.connection.fetch.register({ path: '/api/plugins/dsh-generative-ui/history', methods: ['GET', 'POST'], requestBody: 'buffered', fetch: historyRoute(store) }));
|
|
119
127
|
registerVisualAssets(ctx);
|
|
120
128
|
ctx.effect(()=>ctx.connection.fetch.register({path:'/api/plugins/dsh-generative-ui/diagnostics',methods:['GET'],requestBody:'buffered',fetch:diagnosticsRoute(feedback)}));
|
|
121
129
|
ctx.effect(()=>ctx.connection.fetch.register({path:'/api/plugins/dsh-generative-ui/fence',methods:['POST'],requestBody:'buffered',fetch:fenceRoute(store)}));
|
|
122
130
|
ctx.effect(()=>ctx.tools.register(defineTool({name:'validate_ui',description:"Validate native or extended UI without saving/displaying. valid with complete=false means rejected components. Returns canonical document/diagnostics; a rendering tool still delivers the UI.",parameters:{document:{type:'json',required:true}},output:{schema:{type:'json'},render:(_a,v)=>[{type:'text',text:JSON.stringify(v)}]},isConcurrencySafe:()=>true,async execute({document}){try{const report=diagnoseDelivery(toolJson(document,'document'));return {...report,...(report.valid?{fallback:fallbackText(report.document)}:{error:report.diagnostics[0].message})}}catch(e){return{valid:false,complete:false,error:e.message}}}})));
|
|
123
|
-
ctx.effect(()=>ctx.tools.register(
|
|
131
|
+
ctx.effect(()=>ctx.tools.register(defineRenderTool({
|
|
124
132
|
name:'inspect_ui',description:"Read current revision/state/derived and heuristic observations. Summary includes node paths/positions/editable fields. query finds text/labels/types (max200 chars); detail=nodes with query OR nodeIds reads matching source. Full only when needed. Missing observations mean unobserved; do not poll. Inspect before editing.",
|
|
125
|
-
parameters:{documentId:{type:'string',required:true},detail:{type:'string',enum:['summary','nodes','full']},query:{type:'string',description:'Find by text/label/title/type; mutually exclusive with nodeIds. Up to16 matches; blank means all.'},nodeIds:{type:'
|
|
133
|
+
parameters:{documentId:{type:'string',required:true},detail:{type:'string',enum:['summary','nodes','full']},query:{type:'string',description:'Find by text/label/title/type; mutually exclusive with nodeIds. Up to16 matches; blank means all.'},nodeIds:{type:'array',items:{type:'string'},description:'For nodes detail: direct array of up to16 existing node IDs, e.g. ["canvas"], not HTML DOM IDs.'}},
|
|
126
134
|
output:{schema:{type:'json'},render:(_args,value)=>[{type:'text',text:JSON.stringify(value)}]},isConcurrencySafe:()=>true,
|
|
127
135
|
async execute({documentId,detail='summary',nodeIds=[],query=''},exec){const record=await store.owned(documentId,exec.agent?.id);const saved=await store.readState(documentId,record.sessionId);return inspectRecord(record,saved.state,evaluate(record.document,saved.state).derived,await store.audits(documentId),{detail,nodeIds:toolJson(nodeIds,'nodeIds'),query});}
|
|
128
136
|
})));
|
|
129
|
-
ctx.effect(()=>ctx.tools.register(
|
|
137
|
+
ctx.effect(()=>ctx.tools.register(defineRenderTool({name:'patch_ui',description:"Edit existing scalar/source properties using the inspected documentRevision. changes:[{id,props}]. IDs/structure/bindings/actions stay unchanged; canvas source replacement restarts that frame. Saved parameters persist. Use update_ui for structure changes.",parameters:{documentId:{type:'string',required:true},documentRevision:{type:'number',required:true},changes:{type:'array',required:true,description:'Direct array [{id,props}]; combine changed properties for one node in props.',items:{type:'object',additionalProperties:false,properties:{id:{type:'string',required:true},props:{type:'json',required:true}}}}},output:{schema:{type:'json'},render:(_a,v)=>[{type:'text',text:`Updated ${v.document.title}; documentId=${v.documentId}; documentRevision=${v.documentRevision}. Saved parameters retained.`}],presentationMeta:(_a,v)=>({dshGenerativeUiPatch:v})},async execute({documentId,documentRevision,changes},exec){exec.signal.throwIfAborted();return store.refine(documentId,exec.agent?.id,documentRevision,toolJson(changes,'changes'));}})));
|
|
130
138
|
ctx.effect(()=>ctx.tools.register(defineTool({name:'update_ui',description:"Append/replace existing content at the inspected revision, keeping documentId and compatible state. targetId selects a node; an extended fragment appends to the sole visual when unambiguous. Supply a stable operationId; reuse only for an identical retry (latest64 replay IDs). Preserve state/derived definitions; new inputs get defaults.",parameters:{documentId:{type:'string',required:true},documentRevision:{type:'number',required:true},operationId:{type:'string',required:true},mode:{type:'string',enum:['append','replace'],required:true},targetId:{type:'string'},document:{type:'json',required:true}},output:{schema:{type:'json'},render:(_a,v)=>[{type:'text',text:'Updated existing UI '+v.documentId+'; documentRevision='+v.documentRevision+'. Keep its existing inline marker; do not duplicate it.'}],presentationMeta:(_a,v)=>({dshGenerativeUiPatch:v})},async execute({documentId,documentRevision,operationId,mode,targetId,document},exec){exec.signal.throwIfAborted();return store.update(documentId,exec.agent?.id,documentRevision,operationId,{mode,...(targetId?{targetId}:{}),document:toolJson(document,'document')})}})));
|
|
131
139
|
for(const[i,section]of UI_SKILL_SECTIONS.entries())ctx.effect(()=>ctx.systemPrompt.section({...section,text:()=>promptSections(settings().designMode??'guided',settings().visualStyle??DEFAULT_VISUAL_STYLE)[i].text}));
|
|
132
140
|
}
|