dsh-motion-ui 0.7.0 → 0.8.0

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +63 -3
  3. package/VERIFICATION.md +26 -0
  4. package/client.js +876 -272
  5. package/index.js +43 -20
  6. package/lib/assets/mermaid.js +382 -382
  7. package/lib/assets/standalone-runtime.js +1 -1
  8. package/lib/assets/visual.js +13 -13
  9. package/lib/author-expression.js +7 -7
  10. package/lib/authoring-guide.js +3 -17
  11. package/lib/build-info.js +1 -1
  12. package/lib/canvas-design.js +2 -2
  13. package/lib/canvas-export.js +3 -3
  14. package/lib/canvas-system.js +1 -1
  15. package/lib/catalog.js +1 -2
  16. package/lib/delivery.js +1 -1
  17. package/lib/export-runtime-bundle.js +1 -1
  18. package/lib/fence-feedback.js +8 -2
  19. package/lib/freeform.js +4 -10
  20. package/lib/inspection.js +19 -6
  21. package/lib/motion-craft.js +31 -0
  22. package/lib/motion-design-skill.js +23 -0
  23. package/lib/motion-library.js +5 -4
  24. package/lib/normalize-authoring.js +9 -0
  25. package/lib/project-archive.js +16 -0
  26. package/lib/prompt-contract.js +22 -0
  27. package/lib/protocol.js +3 -3
  28. package/lib/refinement.js +36 -3
  29. package/lib/sandbox.js +4 -1
  30. package/lib/scene-entry.js +41 -0
  31. package/lib/style-system.js +2 -2
  32. package/lib/text-guide.js +9 -13
  33. package/lib/third-party/taste-skill-NOTICE.md +10 -0
  34. package/lib/timeline.js +29 -0
  35. package/lib/tool-json.js +5 -0
  36. package/lib/tool-schema.js +7 -7
  37. package/lib/ui-intent.js +4 -15
  38. package/lib/ui-skill.js +19 -26
  39. package/lib/video-tools.js +21 -0
  40. package/lib/video.js +296 -0
  41. package/lib/visual-audit.js +15 -4
  42. package/lib/visual-styles.js +1 -1
  43. package/lib/voice.js +72 -0
  44. package/package.json +17 -9
  45. package/skills/intelligent-ui/SKILL.md +14 -107
  46. package/skills/motion-design/ADAPTER.md +35 -0
  47. package/skills/motion-design/LICENSE +201 -0
  48. package/skills/motion-design/NOTICE.md +9 -0
  49. package/skills/motion-design/SKILL.md +388 -0
  50. package/skills/motion-design/SOURCES.md +16 -0
  51. package/skills/motion-design/upstream.json +8 -0
@@ -138,5 +138,5 @@ export function visualStyle(id=DEFAULT_VISUAL_STYLE){
138
138
  if(!style)throw Error('DSH UI: unknown visualStyle; use '+VISUAL_STYLE_IDS.join('/'));
139
139
  return style;
140
140
  }
141
- export function stylePrompt(id){const s=visualStyle(id);return `## 用户选择的视觉风格:${s.name} (${s.id})\n采用${s.name}风格。\n${s.prompt}\n这套风格替代通用指导和旧案例中的默认配色、材质与圆角建议。只影响新生成内容的视觉方向;协议、工具入口、状态、权限、准确性、可读性与必要功能要求保持不变。引导模式下原生文本、卡片、指标、控件和代码块会自动跟随当前所选视觉风格;整份 UI 的原生内容和 HTML/SVG 应使用一致的色彩与层级。原生组件会改变表面、层级、控件与流程形态。通常省略 size/weight/color 和 presentation.accent,让所选风格管理默认层级;只有用户明确指定或确有语义强调才覆盖。不要每种风格都手写同一套字号。title 已显示文档标题,正文不再重复相同标题;模型负责根据上述构图原则组织内容,不要所有风格都生成等大卡片网格。少量语义色强调真实重点。不要为了给原生内容上色而额外生成 HTML。确实需要特殊图解时再使用小画布。用户在当前请求中明确指定的风格优先。不要为展示风格增加不必要的画布、代码、动画或工具调用。`}
141
+ export function stylePrompt(id){const s=visualStyle(id);return '## 用户选择的视觉风格:'+s.name+' ('+s.id+')\n采用'+s.name+'风格。\n'+s.prompt+'\n原生组件自动遵循此风格,通常省略size/weight/color和presentation.accent;明确要求或语义强调才覆盖。HTML/SVG与原生内容保持一致层级。通用指导不覆盖这里的配色、材质、圆角或阴影;按任务组织内容,不为展示风格增加画布或工具调用。'}
142
142
  export function styleExamples(id){return visualStyle(id).examples.map(example=>JSON.stringify(example)).join('\n')}
package/lib/voice.js ADDED
@@ -0,0 +1,72 @@
1
+ import {readFile,writeFile} from 'node:fs/promises';
2
+ import {join} from 'node:path';
3
+ import {homedir} from 'node:os';
4
+ import {runMedia} from './video.js';
5
+
6
+ // Reuse an explicitly configured MiniMax credential; never copy DSH providers,
7
+ // return keys/signed URLs, or include them in an editable project.
8
+ export async function voiceConfig(){
9
+ let saved={};try{saved=JSON.parse(await readFile(join(homedir(),'.mmx','config.json'),'utf8'))}catch{}
10
+ return {key:process.env.MINIMAX_API_KEY||saved.api_key,region:process.env.MINIMAX_REGION||saved.region||'cn'};
11
+ }
12
+ export async function voiceCapabilities(){const c=await voiceConfig();return {configured:!!c.key,provider:'MiniMax',setup:'Configure official mmx authentication (~/.mmx/config.json), or MINIMAX_API_KEY and optional MINIMAX_REGION=cn/global. Paid API is used only when synthesize_voice is explicitly called.'}}
13
+ export function voiceTimings(raw,duration){
14
+ const entries=Array.isArray(raw)?raw:raw?.subtitles;
15
+ if(!Array.isArray(entries)||!entries.length)throw Error('Motion voice: provider returned no timed subtitles; audio retained');
16
+ let previous=0;
17
+ const captions=entries.map(e=>{
18
+ const start=e.time_begin/1000,end=e.time_end/1000;
19
+ if(!Number.isFinite(start)||!Number.isFinite(end)||start<previous||end<=start||end>duration+.1||typeof e.text!=='string'||!e.text.trim())throw Error('Motion voice: invalid provider subtitle timing; audio retained');
20
+ previous=end;return {start,end:Math.min(end,duration),text:e.text};
21
+ });
22
+ // Numerals/version strings can have several spoken syllables all pointing to
23
+ // the SAME source word range. Merge those measured spans, not repeated text
24
+ // at distinct source positions (e.g. a deliberately repeated phrase).
25
+ const timed=entries.map(e=>{
26
+ const result=[];for(const w of e.timestamped_words??[]){if(!Number.isFinite(w.time_begin)||!Number.isFinite(w.time_end)||w.time_begin<0||w.time_end<w.time_begin)throw Error('Motion voice: invalid word timing; audio retained');const p=result.at(-1);if(p&&Number.isInteger(w.word_begin)&&Number.isInteger(w.word_end)&&p.word_begin===w.word_begin&&p.word_end===w.word_end&&p.word===w.word){p.time_end=Math.max(p.time_end,w.time_end)}else result.push({...w})}return result;
27
+ });
28
+ const words=timed.flat().map(w=>({start:w.time_begin/1000,end:w.time_end/1000,text:w.word}));
29
+ let last=-1;for(const w of words){if(!Number.isFinite(w.start)||!Number.isFinite(w.end)||w.start<last||w.end<w.start||w.start<0||w.end>duration+.1||typeof w.text!=='string')throw Error('Motion voice: invalid word timing; audio retained');last=w.start}
30
+ // The service may group a whole paragraph in one sentence entry. Split only
31
+ // when every source character is covered by actual returned word timings.
32
+ const clean=s=>s.replace(/\s/g,'');
33
+ const complete=entries.every((e,i)=>timed[i].length&&clean(timed[i].map(w=>w.word).join(''))===clean(e.text));
34
+ if(!complete)return {captions,words};
35
+ // Provider "words" can be phonetic fragments such as M / ot / ion / U / I.
36
+ // Restore ONLY the original sentence's whitespace after verifying complete
37
+ // character coverage; guessing a space between Latin fragments corrupts names.
38
+ const pieces=timed.flatMap((group,i)=>{const source=entries[i].text;let cursor=0;return group.map(w=>{const start=cursor,needed=clean(w.word).length;let consumed=0;while(cursor<source.length&&consumed<needed){if(!/\s/.test(source[cursor]))consumed++;cursor++}return source.slice(start,cursor)})});
39
+ const readable=[];let cue;
40
+ const flush=()=>{if(cue&&cue.end>cue.start&&cue.text.trim())readable.push({...cue,text:cue.text.trim()});cue=undefined};
41
+ for(const [index,w] of words.entries()){
42
+ if(!w.text.trim())continue;
43
+ if(cue&&(w.start-cue.end>.3||[...cue.text].length>=26))flush();
44
+ if(!cue)cue={start:w.start,end:w.end,text:''};
45
+ cue.text+=pieces[index];cue.end=Math.min(duration,Math.max(cue.end,w.end));
46
+ if(/[。!?;,!?;,]$/.test(w.text))flush();
47
+ }
48
+ flush();
49
+ return {captions:readable,providerCaptions:captions,words};
50
+ }
51
+ export async function synthesizeVoice(studio,args,{sessionId,signal,fetchImpl=fetch,config}={}){
52
+ if(typeof args.text!=='string'||!args.text.trim()||args.text.length>=10000)throw Error('Motion voice: text must contain 1..9999 characters');
53
+ const voice=args.voice??'Chinese (Mandarin)_Warm_HeartedGirl',model=args.model??'speech-2.8-hd',speed=args.speed??1;
54
+ if(typeof voice!=='string'||!voice||voice.length>200||!['speech-2.8-hd','speech-2.8-turbo'].includes(model)||!Number.isFinite(speed)||speed<.5||speed>2)throw Error('Motion voice: invalid voice/model/speed');
55
+ const c=config??await voiceConfig();if(!c.key)throw Error('Motion voice: configure mmx authentication or MINIMAX_API_KEY first; no credential was copied from DSH');
56
+ if(!['cn','global'].includes(c.region))throw Error('Motion voice: region must be cn or global');
57
+ const job=await studio.job(sessionId,'voice');job.files=['narration.txt'];await writeFile(join(job.directory,'narration.txt'),args.text);await studio.save(job);
58
+ try{
59
+ const response=await fetchImpl(`https://api.minimax.${c.region==='cn'?'cn':'io'}/v1/t2a_v2`,{method:'POST',signal,headers:{Authorization:'Bearer '+c.key,'Content-Type':'application/json'},body:JSON.stringify({model,text:args.text,stream:false,output_format:'hex',subtitle_enable:true,subtitle_type:'word',language_boost:'auto',voice_setting:{voice_id:voice,speed,vol:1,pitch:0},audio_setting:{format:'mp3',sample_rate:32000,bitrate:128000,channel:1}})});
60
+ if(!response.ok)throw Error('MiniMax speech HTTP '+response.status);
61
+ const result=await response.json();if(result.base_resp?.status_code)throw Error('MiniMax speech API '+result.base_resp.status_code+': '+result.base_resp.status_msg);
62
+ if(typeof result.data?.audio!=='string'||!/^([a-f0-9]{2})+$/i.test(result.data.audio))throw Error('MiniMax returned no valid hex audio');
63
+ const audio=join(job.directory,'narration.mp3');await writeFile(audio,Buffer.from(result.data.audio,'hex'));job.files.push('narration.mp3');await studio.save(job);
64
+ const probe=JSON.parse(await runMedia(process.env.DSH_MOTION_FFPROBE||'ffprobe',['-v','error','-show_format','-show_streams','-of','json',audio],{signal}));
65
+ const duration=Number(probe.format?.duration);if(!probe.streams?.some(s=>s.codec_type==='audio')||!Number.isFinite(duration)||duration<=0)throw Error('Motion voice: returned audio failed verification');
66
+ const url=new URL(result.data.subtitle_file);if(url.protocol!=='https:')throw Error('Motion voice: provider must supply HTTPS timed subtitles');
67
+ const subtitles=await fetchImpl(url,{signal,credentials:'omit',referrerPolicy:'no-referrer'});if(!subtitles.ok)throw Error('Subtitle download HTTP '+subtitles.status);
68
+ const raw=await subtitles.json();await writeFile(join(job.directory,'subtitles.raw.json'),JSON.stringify(raw,null,2));job.files.push('subtitles.raw.json');const timings=voiceTimings(raw,duration);
69
+ await writeFile(join(job.directory,'timings.json'),JSON.stringify(timings,null,2));
70
+ job.files.push('timings.json');job.duration=duration;job.captions=timings.captions;job.words=timings.words;job.audioPath=audio;job.recipe={provider:'MiniMax',model,voice,speed};job.status='completed';return await studio.save(job);
71
+ }catch(e){const message=String(e.message).replaceAll(c.key,'[REDACTED]').replace(/https?:\/\/\S+/g,'[URL]');job.status='failed';job.error=message;await studio.save(job);throw Error(message+'; retained assets: '+job.directory)}
72
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-motion-ui",
3
- "version": "0.7.0",
4
- "description": "Interactive UI, HTML/SVG animation and local calculations for DeepSeek Harness Web, with injected design guidance.",
3
+ "version": "0.8.0",
4
+ "description": "Readable conversational UI, interactive HTML/SVG, editable motion and local MP4 production for DeepSeek Harness.",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "exports": {
@@ -37,16 +37,21 @@
37
37
  "platform": "web"
38
38
  },
39
39
  "compatibility": {
40
- "dsh": "0.2.0-rc.2",
40
+ "dsh": "0.2.0-rc.2 || 0.2.1-rc.1",
41
41
  "node": ">=22.19",
42
42
  "profiles": [
43
- "web"
44
- ]
43
+ "web",
44
+ "desktop"
45
+ ],
46
+ "dshReleases": {
47
+ "0.2.0-rc.2": "compatible",
48
+ "0.2.1-rc.1": "compatible"
49
+ }
45
50
  }
46
51
  },
47
52
  "peerDependencies": {
48
- "@deepseek-ai/dsh-home-paths": "0.2.0-rc.2",
49
- "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
53
+ "@deepseek-ai/dsh-home-paths": "0.2.0-rc.2 || 0.2.1-rc.1",
54
+ "@deepseek-ai/dsh-tools": "0.2.0-rc.2 || 0.2.1-rc.1",
50
55
  "react": "^18.2.0 || ^19.0.0",
51
56
  "react-dom": "^18.2.0 || ^19.0.0"
52
57
  },
@@ -59,10 +64,13 @@
59
64
  "mermaid": "^11.16.1",
60
65
  "react": "^18.3.1",
61
66
  "react-dom": "^18.3.1",
62
- "three": "^0.180.0"
67
+ "three": "^0.180.0",
68
+ "@deepseek-ai/dsh-tools": "0.2.1-rc.1",
69
+ "@deepseek-ai/dsh-home-paths": "0.2.1-rc.1"
63
70
  },
64
71
  "dependencies": {
65
- "@deepseek-ai/schemastery": "^3.18.4"
72
+ "@deepseek-ai/schemastery": "^3.18.4",
73
+ "playwright-core": "1.64.1"
66
74
  },
67
75
  "license": "MIT",
68
76
  "repository": {
@@ -1,116 +1,23 @@
1
- # DSH Motion UI — 设计与生成技能
1
+ # Motion UI — 通用设计指导(引导模式)
2
2
 
3
- 生成 UI 时直接使用已注册的 render_ui/render_canvas;不需要先 grep、glob 或用 Shell 查找 DSH 安装源码。协议疑问以本说明和 ui_catalog 为准。这不限制为完成用户实际任务、读取真实数据而使用文件或搜索工具。
3
+ 这层规定信息质量,用户选定风格负责构图语言、配色、材质、圆角和阴影;用户当前明确要求优先。不全局禁止渐变、边框或阴影,不要求图片、营销结构、固定数量动画或随机排版。保持无玻璃的产品约定。
4
4
 
5
- `round(x)` 只有一个参数,等同 JavaScript `Math.round`:取最近整数,恰好一半时朝正无穷方向取整,如 `round(2.5)=3`、`round(-2.5)=-2`。它不是向上取整;原生公式支持 `ceil(x)` 向上取整、`floor(x)` 向下取整(各一个参数);房间数直接用 `{expr:"ceil(state.people / 2)"}`,无需画布或条件链。指标 decimals 只控制显示精度,不改变计算值。
5
+ 常见流程、方案对比、比例分配和旅行预算优先考虑 render_scene,用任务数据交付完整界面;结构不合适时再用原生组件或自由画布,不硬套场景。
6
6
 
7
- 简单公式优先使用 `{expr:"state.V * state.V / state.R"}`,例如 `derived.power` 的表达式,然后用 `{ref:"derived.power"}` 在指标中显示。支持四则运算、比较、三元条件、括号、`min/max/round/ceil/floor/abs`;字符串使用双引号,引用写完整的 `state.x/data.x/derived.x/item.x`。这会编译为现有解释器协议,不执行任意 JavaScript。复杂动画仍使用画布。
7
+ ## 内容决定界面
8
8
 
9
- 这是插件主动注入的能力指导;无需用户重复提供,也不依赖你先调用目录才获得这些规则。它是基于公开项目设计思想编写的 DSH 技能,不是 OpenAI 的内部提示词。以下协议与宿主工具权限共同约束输出。
9
+ - 先抓住用户需要理解或完成的一件事:比较对齐同一标准,流程体现顺序,架构体现职责依赖,机械突出对象和部件,实验把参数、主图和结果放在一起。Diagram grammar:层级、流程、时序、依赖、空间装配不可混淆;连线不穿字,方向与标注明确。
10
+ - 先呈现结论、主要证据/图形或关键操作,次要解释与完整数据随后。用大小、对齐和分组建立主次,避免重复标题、同一数字多处重复、每段套卡、无意义KPI和装饰标签。
11
+ - 简洁仍要有内容。去掉装饰后用清楚的解释、图形和尺度补足,不交付空白板。按内容变化构图,风格贯穿文字、控件、图表与画布,不能只给一个按钮换皮。
10
12
 
11
- ## 先选择表达方式,再写界面
13
+ ## 可读、真实、可操作
12
14
 
13
- 首要目标是解决用户的问题。简单事实、短解释、代码修复优先使用普通文字(plain text)。当可视化、参数调节、结构探索或方案对比能显著帮助理解时,原生调用 render_ui 或 render_canvas。用户明确要交互图、动画或计算器时应使用 UI。
15
+ - 优先PC横屏1920px/1365px中实际聊天列,再兼顾768px/390px;画布不是全浏览器宽度。避免空白撑高或缩字硬塞。Chinese characters和长标签按实际渲染尺寸检查;SVG缩放后关键标注至少12px,必要时移到HTML说明。正文、单位、来源和假设可辨认,深浅主题有对比,不只靠颜色表达结论。
16
+ - 数值、图与解释共用计算来源。不同单位分别标轴或明确同基准归一化;不可用未说明尺度暗示大小。真实可编辑参数才放控件,初始状态就有信息,操作有对应反馈。键盘焦点及等价操作可用。
17
+ - 静态状态应已能说明问题,动效再解释变化、装配或操作结果。运动围绕内容组织,节奏可随风格变化;保持阅读稳定,尊重减少动态效果与暂停。允许表现力,不强制所有界面运动,不把真实结果延迟成装饰动画。示意不证明实测、工具成功或工程精度。
14
18
 
15
- 按问题选择构图,而不是每次复制仪表盘:
19
+ ## 提交与修正
16
20
 
17
- | 任务 | 适合的构图 | 可选交互(并非必需) |
18
- | --- | --- | --- |
19
- | 机械/物体拆解 | 大幅 SVG 主体 + 部件说明,exploratory 处理 | 一个展开滑块 + 部件选择 |
20
- | 原理/参数实验 | 图形占主要空间 + 紧凑参数区,workbench | 参数改变,图与解释同步 |
21
- | 架构/流程/依赖 | 语义节点、分层与有方向的连线 | 聚焦节点查看职责 |
22
- | 数据与趋势 | 主图 + 少量指标 + 精确数据表,dashboard | 筛选或参数变化 |
23
- | 多方案比较 | 相同标准对齐,comparison | 选择要细看的方案 |
24
- | 解释/教程/报告 | 阅读排版 + 一幅支持理解的图,guide/report | 只保留有必要的展开或探索 |
21
+ 只做与任务相称的简短检查:内容完整、必要控件存在、绑定正确、初始图可见、变化有反馈、空值/zero denominators不出错、标签与fallback有效。协议以工具和按需目录为准,不搜索安装源码来学习接口。
25
22
 
26
- exploratory/analytical/editorial/technical 是构图思想,不是合法 layout 枚举;只能使用协议声明的 layout。状态面板仅在确有状态信息时使用 status。
27
-
28
- ## 轻量图解优先
29
-
30
- 默认先做静态可视化,不强行交互。一幅小图、3–5 步流程、2–3 方案的对齐对比或少量指标已经可以是完整作品。只有变化、选择或运动确实帮助理解时才添加 state、控件和 script;复杂交互与动画按需求启用,不能成为所有场景的默认。
31
-
32
- 图解采用编辑式排版:中性底色、细线边框、少量强调色、直接标注、清晰层级。不叠阴影、发光或多重卡片。流程/架构用简单节点与有意义的箭头;可直接使用宿主提供的 ui-flow/ui-step/ui-arrow/ui-callout 类,通常无需自写 CSS 和 script。ui_catalog({topic:"quick"}) 提供流程、对比、架构和小看板四个短例子;优先参考已注入的静态例子,不反复查询目录。先提交足够讲清关系的小作品,按用户后续需求扩展。
33
-
34
- ## 视觉设计技能
35
-
36
- - 用大小、对齐、留白建立层级。让图形或证据成为主体;不把每段文字套进卡片,不堆 KPI,不重复标题。
37
- - 图标表达内容角色,使用已注册 icon;图表、原理示意可在 sandbox 中自由画 SVG。避免无关 emoji 和装饰动画。
38
- - 优先宿主主题与系统字体。主体用中性色,重点用一种强调色;成功/危险颜色只表示已确认的语义。颜色之外提供文字或形状说明。
39
- - 场景变化应改变构图与信息组织,不只是换颜色。阅读页更轻,数据页更对齐,实验页突出对象和反馈。
40
- - 先检查 PC 横屏 1920px/1365px 中实际聊天列的构图,再补查 768px/390px。深浅主题都检查文字对比;关键标注按实际显示尺寸至少 12px,必要时移到 HTML 说明,不缩成看不清的小字。
41
- - 将讲清核心关系的主图、必要控件和动态结果靠近;长推导、完整数据表放后面。避免用大幅装饰动画占据首屏、把真正的比较曲线挤到多屏之后;相同数字无需在多种组件里重复。
42
- - 跨单位比较先明确尺度:分轴并标单位,或使用相对同一基准的倍数。配色只帮助识别,结论仍来自正确关系。透明渐变优先显式 alpha;自写颜色函数须匹配输入格式。
43
- - 图解先确定关系:层级、流程、时序、依赖或空间装配。节点职责单一,连线不穿文字,箭头方向明确;复杂关系拆成概览与聚焦详情。
44
-
45
- ## 正确生成的步骤
46
-
47
- 1. 确定问题与核心可视化;仅有必要时加入一个主要交互和可见反馈,不展开冗长设计复盘。
48
- 2. 优先使用下面已注入的协议和小例子。复杂功能或不确定字段,再调用 ui_catalog({topic:"canvas"|"data"|"scenes"});不必每次获取 all,也不必重复查询相同目录。
49
- 3. 标准数据、控件使用原生组件;自由形状、SVG、HTML/CSS/JavaScript 动画使用 sandbox。它们可以混排,别因为固定组件不够就退回静态图片。
50
- 4. 原生组件混排使用完整 render_ui({document:...}):先写元数据、state/data/derived/actions,再写 nodes。单独一块自由画布可用 render_canvas({title,fallback,html,css,script,state?...}),源码字段直接传字符串,避免把整份程序再嵌套进 JSON 文档字符串;该入口的画布节点 ID 固定为 canvas。不得在 PTC/run_code 中套用这两个工具。
51
- 5. 若校验失败,根据工具的具体错误修正相关字段后重提。不要照抄失败参数或臆造组件。先交付最小正确界面,再扩展;复杂动画不是简单回答的必要条件。
52
- 6. 成功后用简短文字说明用途和关键限制,不宣称未经验证的精度或效果。
53
-
54
- ## 状态、动作与动画
55
-
56
- - 重要参数放 document.state,标准控件 bind="state.key"。sandbox 的 stateKeys 使用裸 key;从 DSHUI.subscribe 读宿主当前值。
57
- - 精确计算只保留一份来源:优先 derived,图与表读取同一结果。add 只能加数字,不能拼接字符串;不要为排版而构造无用派生表达式。
58
- - 同一读数的格式也保持一致。画布中用 DSHUI.formatNumber(value, decimals) 与原生 metric/fallback 共用 0..6 位小数及千分位规则;直接读取共享 derived 后格式化,不另算一份结果。不要混用 toFixed 与原生格式,二进制小数舍入可能让 4.225 显示成不同结果。
59
- - 本地滑动、筛选、计算与动画不需要模型调用。DSHUI.setState 只提交用户造成的参数变化,subscribe 中不要回写同一值,动画不要逐帧保存状态。
60
- - 动画优先 DSHUI.animate,delta 单位秒;参数变化不重建画布,暂停不重置相位。处理 reducedMotion,提供有意义的静态图;时间/相位默认可在刷新后重置。
61
- - SVG 热点可键盘操作,或者提供等价标准选择控件。控件有标签和单位,操作后结果与解释同步。
62
- - agent.followup 只发起带当前参数的新请求,不能自动当成执行成功。宿主文件、网络、Shell 等操作仍走正常工具与权限。
63
-
64
- ## 证据与能力边界
65
-
66
- 标明示例、假设、来源与单位;没有测量就不能写“实测”。数学动画不等于物理仿真;变化率不能直接当作累积量或实际状态,例如只有发热功率、没有时间与散热条件时不能断言温度或“温手/烫手”。每份文档和每个画布都提供有用 fallback;错误时用户仍能理解结果。
67
-
68
- 画布代码只在成功提交并恢复参数后执行。它没有宿主工具/凭据访问权,不能导入远程包或资源。全部 JavaScript 放 script,CSS 放 css,HTML 事件属性不能使用。浏览器来源隔离不是 CPU/内存硬限制,避免无界循环和过重计算。
69
-
70
- 当任务超出当前能力时,交付较小但正确的交互或完整文字解释。技能能降低学习和发现成本,不能保证每个模型都能正确设计复杂程序。
71
-
72
- ## 复杂度选择与渐进交付
73
-
74
- 不用猜测自己属于哪个能力档。按任务选择足够解决问题的最小方案:
75
-
76
- - 基础:原生控件、metric、text、table;最多一个简单 derived 计算。只需展示数值时不要写动画。
77
- - 中等:一个 sandbox、一幅清晰 SVG、一个主要交互,搭配原生控件。先确保初始图可见、参数确实有反馈,再添加细节。
78
- - 复杂:多个图形、联动、局部动画。只有问题确实需要时使用;先交付完整的小版本,再用 patch_ui 增强。不要为了看起来高级而把图形、控件和计算全部重写成自制应用。
79
-
80
- 最常见的错误及修法:slider.bind 必须是 "state.speed",stateKeys 必须是 ["speed"];标准 text.text 是字符串或 {ref:...},不能传 HTML;add 的参数只能是数字;SVG/HTML 中不要写 onclick,脚本用 addEventListener;script 里只访问声明过的宿主键;JSON 字符串正确转义,不要生成半截代码。
81
-
82
- 长 HTML/SVG 程序遇到 JSON 转义错误时,不要重复手拼多层 JSON。单画布可切换 render_canvas 的独立 html/css/script 字段;重要参数仍在 state 中声明。先交付一幅简单但可操作的图,不在首次生成时穷举机械细节或写庞大的绘图引擎。这个入口同样保留状态保存、检查和局部修改。
83
-
84
- ## 观察与局部修正
85
-
86
- render_ui 的工具结果提供 documentId。需要检查或打磨时,调用 inspect_ui({documentId}) 获取当前 documentRevision、文档、已保存参数、derived 和客户端观察。报告标注时间、视口和主题;只有界面被打开后才会有报告。报告为空时不可宣称体检通过,也不要反复空转轮询。它是启发式观察,不证明美观、代码安全或物理精度。
87
-
88
- inspect_ui 默认返回紧凑摘要,不重复传整份程序。要改画布源码,使用 detail="nodes",nodeIds=["画布ID"] 读取该节点;只有确实需要全部结构时使用 detail="full"。摘要中的 omitted 明确表示省略,不能推测其内容。
89
-
90
- 一次用户请求的自动纠错最多两轮;每轮只检查和修正具体问题。相同错误再次出现时停止机械重试,简化界面或提供完整文字说明,并如实报告未解决的问题。没有观察报告时不轮询;不要为了修饰外观反复重建界面。用户主动要求的新修改不属于上一轮自动纠错。这个规则是模型行为预算,不是服务商推理时间的硬超时。
91
-
92
- 看到 canvas 的 smallText/overflow/missingLabel/contrast/runtimeError 时,针对相应节点修正;不要因为宿主脚注的小字就重写整份 UI。渐变/透明/图形覆盖等复杂对比度不在可靠检查范围。
93
-
94
- 修改已有界面用 patch_ui({documentId,documentRevision,changes:[{id:"已有节点ID",props:{...}}]})。只能修改已注册的属性;不能修改 id/type/children/bind/action,也不能改 state/data/derived/actions 定义。可以修改标题文字、颜色语义 tone、sandbox 的 html/css/script/fallback 等。保持准确的 fallback。用检查结果里的最新 revision;冲突先重新 inspect,再修正,不盲目重试。
95
-
96
- 例:changes:[{id:"note",props:{text:"补充的准确说明"}}]。画布代码变更只重启该画布,重要参数保留,未修改的节点保留现场状态;瞬时动画相位会重置。新结构或新参数定义需要新的 render_ui,不能假装 patch 可以完成。
97
-
98
-
99
- ## Daily text and mixed documents
100
- For everyday structured explanations use render_text with the injected eight text presets and optional typography. Native text, compact comparisons, lists and link pills need no HTML, state or script. Several UI documents can accompany one answer; each document owns its own styling, IDs and state. Preserve reading order and use each returned documentId once in inline mode. Use lightweight borders, readable body text and restrained accents as the default visual direction. The injected text guide is the precise field reference. Web/file links only open after a user click through native link/opener semantics.
101
-
102
- ### 可复用文本和代码
103
- 文案、提示词等可直接使用的内容,采用 text + copyable:true + title;普通讲解保持轻量,不必每段加框。命令和配置采用 code 节点(code 原文、lang 语言、title 可选、wrap 可选),右上复制按钮保留缩进、换行,不执行代码。原生文本字段为 text,扩展文本字段为 content。交付后不要在正文再重复整段内容。
104
-
105
- ### 动态交互的选择
106
- 只有真实状态变化需要解释时才用动效。原生 select 的 variant:"segmented" 适合 2..8 个短选项;更多选项自动使用下拉框。滑块填充、开关、复制按钮有内置反馈,不用额外写脚本。数值立即更新到精确结果,图形可短促过渡;不要用计时器假装下载、复制或工具执行成功。复杂交互可通过 ui_catalog({topic:"motion",scene:"plan"|"checklist"|"copy"|"plot"}) 按需取一份完整案例,不整库加载。自由模式保持设计自主,减少动态效果设置必须生效。
107
-
108
- ### 所有风格共用的动作语言
109
- 动效能力与视觉风格分离,所有风格及自由模式的原生组件均支持轻磁吸、文字滚动、弹性拉伸和数字换位。配色、材质与排版跟随所选风格,不能靠添加演示按钮表示整套风格。柔光渐彩使用暖白实色、近黑层级与局部渐彩,覆盖文字、卡片、表单、指标、表格和代码块。原生主 button/link、toggle、数字 input 与 metric 已有对应实现,不必重写。ui_catalog motion 的 action/quantity/elastic 提供短原生案例;复杂拆分按钮、扇形菜单、多选或双滑块再按实际需要做画布。禁止的是玻璃材质、假成功和空闲循环,不是把所有动效压成轻微渐显。
110
-
111
-
112
- 短数据构成图直接使用原生 chart/table 的内联公式单元格:`rows:[{label:"餐饮",value:{ref:"derived.food"}},{label:"住宿",value:{expr:"ceil(state.people / 2) * state.room"}}]`,chart 设置 `labelKey:"label",valueKey:"value"`。这些数值随共享状态本地更新,无需 data + map + 多层 if。静态 data 仅存储真实字面数据,不会自动猜测或生成金额。render_ui 一次提交完整文档;修正失败调用时保留 nodes/state/derived,不能只发送准备补充的字段。
113
-
114
-
115
- ## 结构拆解与工作原理
116
- 当零件分层、装配关系、齿轮速比或曲柄连杆能帮助解释时,主动考虑机制图解,不需要用户额外说使用UI。常见场景优先直接调用 render_mechanism({scene:"assembly"/"gears"/"crank",rpm?,teeth?,radius?,running?,angle?,explode?,title?}),不用拼接文档、HTML或读取案例;插件负责图形、控件和绑定。需要其他结构再读 ui_catalog({topic:"mechanism"}) 并用自定义画布。按问题选择少量关键部件、分组职责、拆解进度、隐藏外壳与真实联动参数;无需高密度零件或自动导览。可通过 libraries:["mechanism"] 使用本地等距几何辅助库,自定义 box/cylinder/gear/rod/spring。共享参数必须声明并绑定控件,动画使用 DSHUI.animate,重要参数持久化,瞬时相位可重置。说明简化齿形和运动学的适用范围,不宣称受力、碰撞或制造精度。
23
+ 有实际客户端观察或截图时针对可见问题局部修改,先解决内容/功能/可读性,再打磨装饰;未观察就不宣称体检通过。遵守共享交付协议的纠错规则,不要求额外规划输出或给模型设思考上限。
@@ -0,0 +1,35 @@
1
+ # Motion UI host adaptation
2
+
3
+ Read this adapter together with the complete, unmodified upstream SKILL.md. Apply its full art direction, choreography, typography, visual grammar, sound planning and quality guidance. This adapter maps OpenDesign-specific execution details to the actual DSH host; it does not replace the design craft with a summary.
4
+
5
+ ## Host contract takes precedence
6
+
7
+ - User requirements and the current DSH host permissions, tools and task scope govern execution. The upstream OD Core, stage markers and CLI paths are OpenDesign references, not additional tools or instructions to install OpenDesign.
8
+ - User-requested films require an actual MP4 and editable source. Interactive explanations remain runnable UI; loading this reference alone does not request a video.
9
+ - Upstream fixed inspection/repair counts are OpenDesign orchestration defaults. In this host they do not cap model reasoning, output, time or necessary defect repair. Never cancel inference to satisfy those counts. Stop when the requested result is verified or report a real unresolved failure.
10
+ - Keep the user's actual reference, approved copy and chosen material language. Free mode leaves visual authorship to the model; guided mode uses the chosen style unless the user supplies a specific overriding direction. Honor explicit material exclusions; do not introduce glass as a default material.
11
+
12
+ ## Execution mapping
13
+
14
+ | OpenDesign reference | Motion UI execution |
15
+ | --- | --- |
16
+ | Scaffold / HyperFrames HTML | `render_canvas` saves an isolated HTML/CSS/script document. Use one unique `#stage`, designed at the actual output aspect. |
17
+ | `window.__timelines.main` / GSAP clock | `DSHUI.timeline({id:'main',duration,draw(time,info),chapters})`. Seconds are the first argument. Acquire Canvas2D context yourself; e.g. `const ctx=canvas.getContext('2d');` and `draw(t){drawFrame(ctx,t)}`. |
18
+ | `data-width`, `data-height`, `data-fps`, `data-duration` | Keep source dimensions, timeline duration and `preview_video` / `render_video` arguments consistent. Scale the authored composition uniformly. |
19
+ | OD source player | Connect controls outside `#stage` to the same DSHUI timeline. Exported standalone source already synchronizes its soundtrack. Do not create another playback clock. |
20
+ | OD capture flag | `window.__DSHMotionRender` is supplied during capture; the host seeks the registered timeline. Drawing must synchronously reproduce any absolute time. |
21
+ | GSAP-specific SVG origin examples | Preserve their design intent with grouped geometry and explicit shared pivots. GSAP is not bundled; arbitrary CDN/network imports and eval are blocked in the canvas. Native SVG, Canvas and transform math work directly. |
22
+ | Audio DOM metadata / OD audio policy | `render_video` takes local `audioTracks:[{path,role:'voice'|'music'|'effect',volume,offset,loop,fadeIn,fadeOut}]`. This produces actual mixed audio and bundles source assets. DOM audio tags or Web Audio alone are not exported tracks. |
23
+ | Speech / transcript timing | `synthesize_voice` returns real measured timing using configured MiniMax authentication. Narration is optional. Do not invent ASR or claim audio analysis from an arbitrary pulse. |
24
+ | OD media generation / wait | `preview_video` returns actual source frames and diagnostics; read its images before encoding. `render_video` exports the MP4 and project ZIP. `inspect_video` checks existing media and samples decoded frames. `process_video` handles clips and soundtrack changes. |
25
+ | Modify existing composition | `inspect_ui` then `patch_ui` with current documentRevision; `update_ui` for structural changes. Do not create duplicate documents for a repair. Editing an exported HTML snapshot alone does not update the saved rendering input. |
26
+
27
+ ## Reliable drawing and review
28
+
29
+ - For Canvas, save/restore local drawing state; derive transformations and all values from time. Seed/cache texture rather than regenerating unrelated random marks.
30
+ - Gate flash/mask opacity to its active interval. An inverse clamped fade equals 1 before the fade begins; explicitly keep overlays transparent outside their intended interval.
31
+ - Inspect the opening, focal transformation, readable hold, transition hand-off and ending. Check actual label bounds, contact points and data units at intended resolution. Sparse frames are not a full perceptual/audio review.
32
+ - A successful capture proves valid execution, not design quality. If an unexpected blank is present in both the source preview and a decoded MP4 at the same time, inspect authored masks and time conditions before blaming the renderer.
33
+ - Preserve inputs, earlier films and editable assets. Cite supplied/downloaded media accurately and include required credits; never invent assets, screenshots, rights or measurements.
34
+
35
+ The unmodified upstream skill, provenance and Apache-2.0 license are shipped alongside this adapter.
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for describing the origin of the Work and
141
+ reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Support. While redistributing the Work or
166
+ Derivative Works thereof, You may choose to offer, and charge a
167
+ fee for, acceptance of support, warranty, indemnity, or other
168
+ liability obligations and/or rights consistent with this License.
169
+ However, in accepting such obligations, You may act only on Your
170
+ own behalf and on Your sole responsibility, not on behalf of any
171
+ other Contributor, and only if You agree to indemnify, defend,
172
+ and hold each Contributor harmless for any liability incurred by,
173
+ or claims asserted against, such Contributor by reason of your
174
+ accepting any such warranty or support.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 Open Design contributors
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,9 @@
1
+ # Third-party notice
2
+
3
+ Motion Design skill — Copyright 2026 Open Design contributors.
4
+
5
+ Source: https://github.com/nexu-io/open-design/tree/14fee234e6eb268058fed17ba979d8e22798dd34/skills/motion-design
6
+
7
+ `SKILL.md` and `SOURCES.md` are unmodified upstream files. Their upstream Apache-2.0 license is included as `LICENSE`. `upstream.json` records the pinned source revision and exact skill SHA256.
8
+
9
+ `ADAPTER.md` and the Motion UI reference loader are original Motion UI integration code/documentation. OpenDesign-specific commands and player contracts remain in the original source for completeness; the separate adapter maps them to DSH.