dsh-motion-ui 0.8.3 → 0.8.5

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 CHANGED
@@ -1,5 +1,42 @@
1
1
  # DSH Motion UI
2
2
 
3
+ ## 0.8.5 — 2026-10-18
4
+
5
+ ### 文档外壳与皮肤兼容
6
+
7
+ - 加固原生文档外壳的圆角边界,避免通用宿主或皮肤的 section 圆角重置将文档变成直角(Issue #3)。
8
+ - 沿用各视觉风格的外壳尺寸,明亮扁平、柔和笔记、技术图解等保留不同圆角;阅读/极简保持无外壳,设置预览保持原有样式。
9
+ - 标记插入位置只负责承载界面,清除其额外边框、背景、阴影和内边距,避免皮肤把它再次装饰成卡片。
10
+ - 不重写模型生成的 HTML/SVG 或画布内部样式,保留独立文档、交互状态与内容布局。
11
+
12
+ ### 验证
13
+
14
+ - 新增 576 个风格、排版、主题、宽度与样式加载顺序组合检查,覆盖皮肤先加载/后加载和设备缩放 1.5。
15
+ - 验证通用重置规则下的直角复现、各风格外壳恢复、内部卡片、画布内容、自由模式与预览样式。
16
+ - 保留 0.8.4 的阅读阴影与跨轮视频修复,并执行完整回归及正式包安装验证。
17
+
18
+
19
+ ## 0.8.4 — 2026-10-17
20
+
21
+ ### 阅读排版与视觉风格
22
+
23
+ - 修复阅读、极简排版被后续风格规则重新加上根节点阴影的问题,解决明亮扁平和柔光渐彩中文字周围露出偏移底块的现象(Issue #2)。
24
+ - 调整样式覆盖顺序,保持这两种排版的根节点透明、无边框和无额外内边距;深色主题及深色预览遵循相同规则。
25
+ - 保留内部卡片和其他排版应有的阴影、配色与组件样式。
26
+
27
+ ### 视频位置、成片与页面响应
28
+
29
+ - 历史视频固定在所属回复内,新一轮回复开始后不会继续堆在聊天列表末尾。
30
+ - 修复多轮视频的定位监听器互相触发、持续搬动播放器并可能卡住页面的问题;正常重排保留播放器实例及播放位置。
31
+ - 拼接成片生成成功后,用成片替代其使用的分镜播放器;独立视频继续保留,源文档和工程文件不删除。
32
+
33
+ ### 验证
34
+
35
+ - 149 项单元/集成测试,以及构建、浏览器和播放器回归。
36
+ - 新增 432 个风格、排版、主题和宽度组合检查,覆盖阅读外壳、深色预览、内部卡片及正常排版的阴影保留。
37
+ - 覆盖跨轮视频定位、折叠历史、播放与拖动定位、切换页面,以及多播放器定位循环的复现与防回归。
38
+
39
+
3
40
  ## 0.8.3 — 2026-10-17
4
41
 
5
42
  ### 动画预览控件
package/README.md CHANGED
@@ -6,6 +6,14 @@ Interactive, declarative tool views for **DeepSeek Harness Web / Desktop 0.2.0-r
6
6
 
7
7
  The plugin adds `render_scene` (short flow/comparison/allocation/budget data), `render_text` (quick native typography), `render_ui` (validated components), `render_canvas` (HTML/SVG), `render_mechanism` (compact assembly/kinematics scenes), and `ui_catalog` (protocol + examples). It saves per-document input state on the Host and sends explicit follow-up requests with the current parameter snapshot.
8
8
 
9
+ ### v0.8.5:文档外壳与皮肤兼容
10
+
11
+ 保护文档外壳的风格圆角,避免宿主/皮肤通用重置造成直角;标记承载节点保持透明、不添加第二层卡片。阅读/极简、设置预览和模型画布内部样式保持独立。
12
+
13
+ ### v0.8.4:阅读阴影与跨轮视频修复
14
+
15
+ 修复阅读/极简排版露出偏移阴影,保留内部卡片样式。历史视频留在所属回复,拼接成功后只展示成片及独立视频,并修复多轮播放器互相触发定位导致页面卡住的问题。
16
+
9
17
  ### v0.8.3:控件可读性与阴影卡片排版
10
18
 
11
19
  改善动画控件的主题对比度,修正画布内边距造成的内部滚动。普通文字卡片保留阴影并随内容自动撑高;SVG 与定位布局增加越界诊断。
package/VERIFICATION.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # 验证记录
2
2
 
3
+ ## v0.8.5 发布验证(2026-10-18)
4
+
5
+ - 149 项单元/集成测试、28 组构建与浏览器回归,保留 20 项播放器检查及多轮定位循环回归。
6
+ - 新增 576 个宿主/皮肤边界组合检查,覆盖六种风格、八种排版、深浅主题、三档宽度、两种加载顺序及设备缩放 1.5。
7
+ - 不带第三方皮肤的 rc.2 Web 基线圆角正常;通用高优先级 section 重置可复现直角。补丁保护自有文档边界,保持风格差异、阅读外壳、内部卡片、自由模式与设置预览,并验证模型 iframe 内部样式不被重写。
8
+ - 未取得报告者完整 orca-link 自定义 CSS,不将通用覆盖复现表述为已确认的宿主版本缺陷。
9
+
10
+
11
+ ## v0.8.4 发布验证(2026-10-17)
12
+
13
+ - 149 项单元/集成测试、27 组构建与浏览器回归、20 项播放器检查及多轮定位循环对照回归。
14
+ - 阅读外壳检查覆盖 432 个组合:6 种风格、8 种排版、3 种主题状态及 3 档宽度(设备缩放 1.5)。阅读/极简根节点保持透明、无阴影,内部卡片及其他排版保留阴影。
15
+ - 多轮历史视频留在所属回复;完成的拼接成片替代其输入分镜,独立影片保留。覆盖历史折叠、展开、后续回复、播放器实例与播放位置保持、进度定位及导航资源释放。
16
+ - Issue #2 根据原作者补充的原生样式复现资料修正 CSS 规则顺序;HTML/SVG 卡片撑高仍保留独立验证。
17
+
18
+
3
19
  ## v0.8.3 发布验证(2026-10-17)
4
20
 
5
21
  - 148 项单元/集成测试、26 组构建与浏览器回归、18 项播放器检查。
package/client.js CHANGED
@@ -288,7 +288,7 @@ var canvasSource = (node) => ({ html: normalizeCanvasHtml(node.html), css: norma
288
288
  var MECHANISM_RUNTIME = 'var __MotionMechanism=(()=>{var it=Object.defineProperty;var Ot=Object.getOwnPropertyDescriptor;var Et=Object.getOwnPropertyNames;var It=Object.prototype.hasOwnProperty;var zt=(t,a)=>{for(var n in a)it(t,n,{get:a[n],enumerable:!0})},Pt=(t,a,n,o)=>{if(a&&typeof a=="object"||typeof a=="function")for(let e of Et(a))!It.call(t,e)&&e!==n&&it(t,e,{get:()=>a[e],enumerable:!(o=Ot(a,e))||o.enumerable});return t};var _t=t=>Pt(it({},"__esModule",{value:!0}),t);var Tt={};zt(Tt,{createMechanismAPI:()=>Ft});var st=Math.PI/180,Ct=Math.sqrt(1.6);function yt({scale:t,azimuth:a=45,elevation:n=30}){let o=a*st,e=n*st;return{sinA:Math.sin(o),cosA:Math.cos(o),sinE:Math.sin(e),cosE:Math.cos(e),k:t*Ct}}function q([t,a,n],o){let{origin:e}=o,{sinA:i,cosA:r,sinE:p,cosE:d,k:m}=yt(o);return[e[0]+(t*i-a*r)*m,e[1]+((t*r+a*i)*p-n*d)*m]}function tt([t,a,n],o){let{sinA:e,cosA:i,sinE:r,cosE:p}=yt(o);return(t*i+a*e)*p+n*r}function vt({x:t,y:a,w:n,d:o,r:e},i=8){let r=Math.max(0,Math.min(e,n/2,o/2)),p=[[t+n-r,a+r,-90],[t+n-r,a+o-r,0],[t+r,a+o-r,90],[t+r,a+r,180]],d=[];for(let[m,s,c]of p){let u=r>0?i:0;for(let f=0;f<=u;f++){let g=(c+(u?90*f/u:45))*st;d.push([m+r*Math.cos(g),s+r*Math.sin(g)])}}return d}function lt(t,a,n,o=40){return Array.from({length:o},(e,i)=>{let r=i/o*Math.PI*2;return[t+n*Math.cos(r),a+n*Math.sin(r)]})}function ct(t,a,n,{pad:o=24,headroom:e=0,azimuth:i=45,elevation:r=30}={}){let p={origin:[0,0],scale:1,azimuth:i,elevation:r},d=t.map(M=>q(M,p)),m=d.map(([M])=>M),s=d.map(([,M])=>M),c=Math.min(...m),u=Math.max(...m),f=Math.min(...s),g=Math.max(...s),h=Math.min((a-o*2)/(u-c),(n-o*2-e)/(g-f));return{origin:[a/2-(c+u)/2*h,e+(n-e)/2-(f+g)/2*h],scale:h,azimuth:i,elevation:r}}var Nt=Object.freeze({parts:96,teeth:64,faces:8192}),Ht=t=>typeof t=="number"&&Number.isFinite(t),y=t=>{throw Error("Motion mechanism: "+t)},v=(t,a,n=-1e4,o=1e4)=>((!Ht(t)||t<n||t>o)&&y(a+" must be a finite number in "+n+".."+o),t),U=(t,a)=>((!Array.isArray(t)||t.length!==3)&&y(a+" must be [x,y,z]"),t.map((n,o)=>v(n,a+"["+o+"]"))),et=t=>typeof t=="string"&&/^[\\p{L}_][\\p{L}\\p{M}\\p{N}_]{0,63}$/u.test(t)&&!["constructor","prototype","__proto__"].includes(t),kt=["box","cylinder","gear","rod","spring"],I=(t,a)=>t.map((n,o)=>n+a[o]),B=(t,a)=>t.map((n,o)=>n-a[o]),_=(t,a)=>t.map(n=>n*a),dt=(t,a)=>t.reduce((n,o,e)=>n+o*a[e],0),V=(t,a)=>[t[1]*a[2]-t[2]*a[1],t[2]*a[0]-t[0]*a[2],t[0]*a[1]-t[1]*a[0]],J=t=>{let a=Math.hypot(...t);return a<1e-6&&y("zero-length axis"),_(t,1/a)};function Y(t){(!t||typeof t!="object"||Array.isArray(t))&&y("scene must be an object"),(!Array.isArray(t.parts)||!t.parts.length||t.parts.length>Nt.parts)&&y("parts requires 1..96 objects");let a=new Set,n=new Set;for(let[o,e]of t.parts.entries()){let i="parts["+o+"]";(!e||typeof e!="object"||!et(e.id)||a.has(e.id))&&y(i+".id must be a unique identifier"),a.add(e.id),kt.includes(e.type)||y(i+".type must be "+kt.join("/")),e.group!==void 0&&(et(e.group)||y(i+".group must be an identifier"),n.add(e.group));for(let r of["label","description"])e[r]!==void 0&&(typeof e[r]!="string"||e[r].length>1024)&&y(i+"."+r+" must be short text");if(e.cover!==void 0&&typeof e.cover!="boolean"&&y(i+".cover must be boolean"),e.material!==void 0&&!["metal","dark","accent","warm","cool"].includes(e.material)&&y(i+".material must be metal/dark/accent/warm/cool"),e.at!==void 0&&U(e.at,i+".at"),e.explode!==void 0&&U(e.explode,i+".explode"),e.rotation!==void 0&&v(e.rotation,i+".rotation",-1e9,1e9),e.axis!==void 0&&!["x","y","z"].includes(e.axis)&&y(i+".axis must be x/y/z"),e.type==="box"){let r=U(e.size,i+".size");r.some(p=>p<=0||p>2e3)&&y(i+".size must be positive and <=2000"),e.round!==void 0&&v(e.round,i+".round",0,Math.min(r[0],r[1])/2)}if(["cylinder","gear"].includes(e.type)&&(v(e.radius,i+".radius",.1,1e3),v(e.height??8,i+".height",.1,1e3),e.inner!==void 0&&v(e.inner,i+".inner",0,e.radius-.01)),e.type==="gear"){let r=v(e.teeth,i+".teeth",6,64);Number.isInteger(r)||y(i+".teeth must be an integer"),e.module!==void 0&&v(e.module,i+".module",.1,e.radius/3)}if(["rod","spring"].includes(e.type)&&(U(e.from,i+".from"),U(e.to,i+".to"),Math.hypot(...B(e.to,e.from))<.1&&y(i+" endpoints must differ"),v(e.radius??2,i+".radius",.1,200)),e.type==="spring"){let r=v(e.turns??8,i+".turns",2,24);Number.isInteger(r)||y(i+".turns must be integer")}}if(t.groups!==void 0){(!Array.isArray(t.groups)||t.groups.length>12)&&y("groups must be an array of <=12 labels");let o=new Set;for(let e of t.groups)(!e||!et(e.id)||o.has(e.id)||!n.has(e.id)||typeof e.label!="string"||e.label.length>128)&&y("groups must name unique existing part groups"),o.add(e.id)}if(t.bounds!==void 0){(!Array.isArray(t.bounds)||t.bounds.length!==2)&&y("bounds must be [minXYZ,maxXYZ]");let[o,e]=t.bounds.map((i,r)=>U(i,"bounds["+r+"]"));o.some((i,r)=>i>=e[r])&&y("bounds min must be below max on every axis")}if(t.azimuth!==void 0&&v(t.azimuth,"azimuth",10,80),t.elevation!==void 0&&v(t.elevation,"elevation",15,70),t.title!==void 0&&(typeof t.title!="string"||t.title.length>256)&&y("title must be short text"),t.bind!==void 0){(!t.bind||typeof t.bind!="object"||Array.isArray(t.bind))&&y("bind must be an object");for(let[o,e]of Object.entries(t.bind))(!["explode","selected","cutaway"].includes(o)||!et(e))&&y("bind only accepts declared state keys for explode/selected/cutaway")}return t}function Lt(t){return t==="x"?[[0,1,0],[0,0,1],[1,0,0]]:t==="y"?[[0,0,1],[1,0,0],[0,1,0]]:[[1,0,0],[0,1,0],[0,0,1]]}var at=(t,a,n,o)=>I(I(_(t[0],a),_(t[1],n)),_(t[2],o));function Rt(t,a,n,o){let e=t.map(([m,s])=>at(n,m,s,-a/2)),i=t.map(([m,s])=>at(n,m,s,a/2)),r=[],p=o?.map(([m,s])=>at(n,m,s,a/2)),d=o?.map(([m,s])=>at(n,m,s,-a/2));r.push({points:i,holes:p?[p.slice().reverse()]:[],normal:n[2]},{points:e.slice().reverse(),holes:d?[d]:[],normal:_(n[2],-1)});for(let m=0;m<t.length;m++){let s=(m+1)%t.length;Math.hypot(...B(e[s],e[m]))<1e-6||(r.push({points:[e[m],e[s],i[s],i[m]],normal:J(V(B(e[s],e[m]),n[2]))}),o&&r.push({points:[d[s],d[m],p[m],p[s]],normal:J(V(B(d[m],d[s]),n[2]))}))}return r}function ot(t){let a=Lt(t.axis),n,o,e;if(t.type==="box"&&(n=vt({x:-t.size[0]/2,y:-t.size[1]/2,w:t.size[0],d:t.size[1],r:t.round??Math.min(2,t.size[0]/2,t.size[1]/2)},3),o=t.size[2]),t.type==="cylinder"&&(n=lt(0,0,t.radius,24),o=t.height??8),t.type==="gear"){let s=Math.PI*2/t.teeth,c=t.module??t.radius*2/t.teeth;n=[];for(let u=0;u<t.teeth;u++)for(let[f,g]of[[-.5,t.radius-c*.85],[-.32,t.radius-c*.85],[-.17,t.radius+c*.7],[.17,t.radius+c*.7],[.32,t.radius-c*.85]]){let h=(u+f)*s;n.push([Math.cos(h)*g,Math.sin(h)*g])}o=t.height??8}if(t.type==="rod"){let s=J(B(t.to,t.from)),c=J(V(s,Math.abs(s[2])<.9?[0,0,1]:[0,1,0]));a=[c,V(s,c),s],n=lt(0,0,t.radius??2,16),o=Math.hypot(...B(t.to,t.from))}if(t.type==="spring"){let s=J(B(t.to,t.from)),c=J(V(s,Math.abs(s[2])<.9?[0,0,1]:[0,1,0])),u=V(s,c),f=[];for(let g=0;g<=128;g++){let h=g/128,M=h*(t.turns??8)*Math.PI*2,O=Math.min(1,h*12,(1-h)*12);f.push(I(I(t.from,_(B(t.to,t.from),h)),I(_(c,Math.cos(M)*(t.radius??6)*O),_(u,Math.sin(M)*(t.radius??6)*O))))}return{line:f,points:f}}let i=t.rotation??0,r=Math.cos(i),p=Math.sin(i);n=n.map(([s,c])=>[s*r-c*p,s*p+c*r]);let d=t.inner?n.map(([s,c])=>{let u=Math.atan2(c,s);return[Math.cos(u)*t.inner,Math.sin(u)*t.inner]}):void 0;e=Rt(n,o,a,d);let m=t.type==="rod"?_(I(t.from,t.to),.5):t.at??[0,0,0];return e=e.map(s=>({...s,points:s.points.map(c=>I(c,m)),holes:s.holes?.map(c=>c.map(u=>I(u,m)))})),{faces:e,points:e.flatMap(s=>s.points)}}function wt(t){if(t.bounds){let[a,n]=t.bounds;return Array.from({length:8},(o,e)=>[e&1?n[0]:a[0],e&2?n[1]:a[1],e&4?n[2]:a[2]])}return t.parts.flatMap(a=>ot(a).points.flatMap(o=>[o,I(o,a.explode??[0,0,0])]))}function W({driverTeeth:t=16,drivenTeeth:a=32,rpm:n=30,angle:o=0,module:e=2}={}){for(let[i,r]of Object.entries({driverTeeth:t,drivenTeeth:a}))(!Number.isInteger(r)||r<6||r>64)&&y(i+" must be integer 6..64");return v(n,"rpm",-600,600),v(o,"angle",-1e9,1e9),v(e,"module",.1,10),{driverRadius:t*e/2,drivenRadius:a*e/2,centerDistance:(t+a)*e/2,driverAngle:o,drivenAngle:-o*t/a+Math.PI-Math.PI/a,driverRpm:n,drivenRpm:-n*t/a,ratio:a/t}}function X({angle:t=0,radius:a=20,rod:n=70,z:o=20}={}){v(t,"angle",-1e9,1e9),v(a,"radius",.1,200),v(n,"rod",.1,1e3),v(o,"z"),n<=a&&y("connecting rod length must exceed crank radius");let e=[a*Math.cos(t),a*Math.sin(t),o],i=e[0]+Math.sqrt(n*n-e[1]*e[1]);return{crank:e,piston:[i,0,o],position:i,stroke:a*2,angle:t}}function Mt(t,a,n,o=.16){return v(t,"value",-1e9,1e9),v(a,"target",-1e9,1e9),v(n,"delta",0,.1),v(o,"tau",.01,10),t+(a-t)*(1-Math.exp(-n/o))}var $t=Math.PI*2;function nt(t,a){Y(t);let n=a&&!t.bounds?t.parts.filter(o=>!a.cutaway||!o.cover).flatMap(o=>ot(o).points.map(e=>I(e,_(o.explode??[0,0,0],a.explode??0)))):wt(t);return ct(n.length?n:[[0,0,0],[1,1,1]],600,340,{pad:24,azimuth:t.azimuth??45,elevation:t.elevation??30})}var At=t=>t.map(([a,n],o)=>(o?"L":"M")+Math.round(a*10)/10+" "+Math.round(n*10)/10).join("")+"Z";function mt(t,a,{explode:n=0,selected:o="all",cutaway:e=!1,pose:i={}}={},r=new Map){if(typeof n!="number"||!Number.isFinite(n)||n<0||n>1)throw Error("Motion mechanism: explode must be 0..1");if(typeof o!="string"||typeof e!="boolean")throw Error("Motion mechanism: selected must be text and cutaway boolean");if(!i||typeof i!="object"||Array.isArray(i))throw Error("Motion mechanism: pose must be an object");for(let c of Object.keys(i))if(!t.parts.some(u=>u.id===c))throw Error("Motion mechanism: unknown pose part "+c);let p=(a.azimuth??45)*$t/360,d=(a.elevation??30)*$t/360,m=[Math.cos(p)*Math.cos(d),Math.sin(p)*Math.cos(d),Math.sin(d)],s=[];for(let c of t.parts){if(e&&c.cover)continue;let u=i[c.id]??{};for(let $ of Object.keys(u))if(!["at","rotation","from","to"].includes($))throw Error("Motion mechanism: unsupported pose field "+$);let f={...c,...u},g=JSON.stringify(f),h=r.get(c.id);h?.key!==g&&(Y({parts:[f]}),h={key:g,...ot(f)},r.set(c.id,h));let M=_(f.explode??[0,0,0],n),O=Math.min(...h.points.map($=>$[2]))+M[2],N=o==="all"||o===c.id||o===c.group;if(h.line){let $=h.line.map(P=>I(P,M));s.push({floor:O,key:f.id+"_line",part:f.id,group:f.group,d:$.map((P,A)=>(A?"L":"M")+q(P,a).join(" ")).join(""),depth:tt($[Math.floor($.length/2)],a),line:!0,tone:1,material:f.material??"metal",active:N});continue}for(let[$,P]of h.faces.entries()){if(dt(P.normal,m)<=1e-4)continue;let w=P.points.map(S=>I(S,M)),T=P.holes?.map(S=>S.map(H=>I(H,M))),D=_(w.reduce((S,H)=>I(S,H),[0,0,0]),1/w.length),G=dt(P.normal,[-.25,-.4,.88]);s.push({floor:O,key:f.id+"_"+$,part:f.id,group:f.group,d:At(w.map(S=>q(S,a)))+(T??[]).map(S=>At(S.map(H=>q(H,a)))).join(""),depth:tt(D,a),line:!1,tone:G>.5?0:G>.1?1:G>-.4?2:3,material:f.material??"metal",active:N})}}if(s.length>8192)throw Error("Motion mechanism: face budget exceeded");return s.sort((c,u)=>c.floor-u.floor||c.depth-u.depth||c.key.localeCompare(u.key))}var z=(t,a,n,o,e,i={})=>({id:t,type:"box",label:a,at:n,size:o,group:e,...i}),F=(t,a,n,o,e,i,r={})=>({id:t,type:"cylinder",label:a,at:n,radius:o,height:e,group:i,...r});function ut(t,a,n,o,e=3,i=18){return[[-1,-1],[-1,1],[1,-1],[1,1]].map(([r,p],d)=>F(o+"_bolt_"+d,"\\u56FA\\u5B9A\\u87BA\\u6813",[t+r*i,a+p*i,n],e,3,o,{material:"dark",explode:[0,0,26]}))}function ft(){return{title:"\\u5206\\u5C42\\u7ED3\\u6784 \\xB7 \\u793A\\u610F\\u88C5\\u914D",groups:[{id:"base",label:"\\u5E95\\u5EA7",description:"\\u652F\\u6491\\u6574\\u4F53\\uFF0C\\u5E76\\u63D0\\u4F9B\\u5B89\\u88C5\\u4F4D\\u7F6E\\u3002"},{id:"board",label:"\\u529F\\u80FD\\u677F",description:"\\u8FDE\\u63A5\\u8F93\\u5165\\u4E0E\\u5904\\u7406\\u6A21\\u5757\\uFF1B\\u793A\\u610F\\u7ED3\\u6784\\uFF0C\\u4E0D\\u5BF9\\u5E94\\u7279\\u5B9A\\u4EA7\\u54C1\\u3002"},{id:"sink",label:"\\u6563\\u70ED\\u5C42",description:"\\u5C06\\u70ED\\u91CF\\u4F20\\u5230\\u66F4\\u5927\\u7684\\u8868\\u9762\\u79EF\\u3002"},{id:"cover",label:"\\u5916\\u58F3",description:"\\u4FDD\\u62A4\\u5185\\u90E8\\uFF1B\\u5256\\u5F00\\u9009\\u9879\\u901A\\u8FC7\\u9690\\u85CF\\u5916\\u58F3\\u5C55\\u793A\\u7ED3\\u6784\\u3002"}],parts:[z("base","\\u5B89\\u88C5\\u5E95\\u5EA7",[0,0,0],[120,85,10],"base",{round:6,material:"metal"}),...[-1,1].flatMap((t,a)=>[-1,1].map((n,o)=>F("foot_"+a+o,"\\u9632\\u6ED1\\u811A",[t*46,n*30,-8],6,7,"base",{material:"dark"}))),z("board","\\u529F\\u80FD\\u677F",[0,0,15],[96,62,4],"board",{round:3,material:"cool",explode:[0,0,35]}),z("chip","\\u5904\\u7406\\u6A21\\u5757",[-16,0,22],[25,24,10],"board",{round:2,material:"dark",explode:[0,0,35]}),z("connector","\\u8FDE\\u63A5\\u7AEF\\u53E3",[32,0,24],[16,36,14],"board",{round:2,material:"warm",explode:[0,0,35]}),...ut(0,0,20,"board",2,24),z("spreader","\\u5BFC\\u70ED\\u5E95\\u677F",[-16,0,32],[44,42,5],"sink",{round:2,material:"metal",explode:[-10,0,65]}),...Array.from({length:7},(t,a)=>z("fin_"+a,"\\u6563\\u70ED\\u9CCD\\u7247",[-34+a*6,0,44],[2.4,40,20],"sink",{round:1,material:"metal",explode:[-10,0,65]})),z("top","\\u9876\\u76D6",[0,0,61],[124,89,5],"cover",{round:6,cover:!0,material:"metal",explode:[12,0,85]}),z("side","\\u4FA7\\u677F",[0,-44,32],[124,4,58],"cover",{round:2,cover:!0,material:"metal",explode:[12,-22,50]})]}}function pt({driverTeeth:t=16,drivenTeeth:a=32,module:n=1.5}={}){let o=W({driverTeeth:t,drivenTeeth:a,module:n}),e=o.centerDistance;return{title:"\\u9F7F\\u8F6E\\u4F20\\u52A8 \\xB7 \\u7B80\\u5316\\u76F4\\u9F7F\\u8F6E\\u793A\\u610F",azimuth:70,elevation:55,groups:[{id:"frame",label:"\\u652F\\u67B6",description:"\\u56FA\\u5B9A\\u8F74\\u5FC3\\u4F4D\\u7F6E\\uFF1B\\u4E24\\u8F74\\u4E2D\\u5FC3\\u8DDD\\u7531\\u9F7F\\u6570\\u4E0E\\u6A21\\u6570\\u5171\\u540C\\u786E\\u5B9A\\u3002"},{id:"driver",label:"\\u4E3B\\u52A8\\u8F6E",description:"\\u8F93\\u5165\\u8F6C\\u901F\\uFF0C\\u7531\\u9F7F\\u6570\\u51B3\\u5B9A\\u8282\\u5706\\u534A\\u5F84\\u3002"},{id:"driven",label:"\\u4ECE\\u52A8\\u8F6E",description:"\\u5916\\u556E\\u5408\\u8F6C\\u5411\\u76F8\\u53CD\\uFF0C\\u8F6C\\u901F\\u6309\\u9F7F\\u6570\\u6BD4\\u53D8\\u5316\\u3002"},{id:"cover",label:"\\u5916\\u58F3",description:"\\u9690\\u85CF\\u76D6\\u677F\\u4EE5\\u89C2\\u5BDF\\u4F20\\u52A8\\uFF1B\\u4E0D\\u662F\\u52A0\\u5DE5\\u6216\\u53D7\\u529B\\u4EFF\\u771F\\u3002"}],parts:[z("base","\\u5B89\\u88C5\\u5E95\\u5EA7",[30,0,-8],[140,90,10],"frame",{round:6,material:"metal"}),...ut(30,0,0,"frame",3,32),...[0,e].flatMap((i,r)=>[F("bearing_"+r,"\\u8F74\\u627F\\u5EA7",[i,0,4],10,12,"frame",{inner:4,material:"dark"}),F("shaft_"+r,"\\u4F20\\u52A8\\u8F74",[i,0,16],3.8,28,r?"driven":"driver",{material:"metal",explode:[r?24:-24,0,20]}),F("hub_"+r,"\\u8F6E\\u6BC2",[i,0,27],6,6,r?"driven":"driver",{inner:3.8,material:"warm",explode:[r?24:-24,0,20]})]),{id:"driverGear",type:"gear",label:"\\u4E3B\\u52A8\\u9F7F\\u8F6E",group:"driver",at:[0,0,20],radius:o.driverRadius,teeth:t,module:n,height:7,inner:4,material:"accent",explode:[-24,0,20]},{id:"drivenGear",type:"gear",label:"\\u4ECE\\u52A8\\u9F7F\\u8F6E",group:"driven",at:[e,0,20],radius:o.drivenRadius,teeth:a,module:n,height:7,inner:4,material:"cool",rotation:o.drivenAngle,explode:[24,0,20]},z("cover","\\u9632\\u62A4\\u76D6\\u677F",[30,0,36],[140,90,4],"cover",{round:6,cover:!0,material:"metal",explode:[0,0,62]})]}}function ht({radius:t=20,rod:a=70,angle:n=0}={}){let o=X({radius:t,rod:a,angle:n,z:28});return{title:"\\u66F2\\u67C4\\u8FDE\\u6746 \\xB7 \\u65CB\\u8F6C\\u8F6C\\u4E3A\\u5F80\\u590D",groups:[{id:"frame",label:"\\u652F\\u67B6\\u4E0E\\u5BFC\\u8F68",description:"\\u652F\\u6491\\u8F74\\u5FC3\\uFF0C\\u9650\\u5236\\u6ED1\\u5757\\u6CBF\\u540C\\u4E00\\u65B9\\u5411\\u8FD0\\u52A8\\u3002"},{id:"crank",label:"\\u66F2\\u67C4",description:"\\u66F2\\u67C4\\u7ED5\\u56FA\\u5B9A\\u8F74\\u5FC3\\u65CB\\u8F6C\\u3002"},{id:"rod",label:"\\u8FDE\\u6746",description:"\\u8FDE\\u63A5\\u66F2\\u67C4\\u9500\\u548C\\u6ED1\\u5757\\uFF0C\\u957F\\u5EA6\\u4FDD\\u6301\\u4E0D\\u53D8\\u3002"},{id:"piston",label:"\\u6ED1\\u5757",description:"\\u6CBF\\u5BFC\\u8F68\\u5F80\\u590D\\uFF1B\\u5B8C\\u6574\\u884C\\u7A0B\\u7B49\\u4E8E\\u66F2\\u67C4\\u534A\\u5F84\\u7684\\u4E24\\u500D\\u3002"}],parts:[z("base","\\u5B89\\u88C5\\u5E95\\u5EA7",[35,0,-6],[175,80,10],"frame",{round:6,material:"metal"}),...ut(35,0,1,"frame",3,30),F("bearing","\\u66F2\\u8F74\\u8F74\\u627F",[0,0,7],11,15,"frame",{inner:4,material:"dark"}),F("axle","\\u66F2\\u8F74",[0,0,15],3.8,26,"crank",{material:"metal",explode:[0,-18,18]}),F("flywheel","\\u66F2\\u67C4\\u76D8",[0,0,20],t+6,6,"crank",{inner:4,material:"accent",explode:[0,-18,18]}),z("railA","\\u5BFC\\u8F68",[74,-16,13],[85,5,6],"frame",{round:1,material:"dark"}),z("railB","\\u5BFC\\u8F68",[74,16,13],[85,5,6],"frame",{round:1,material:"dark"}),F("crankPin","\\u66F2\\u67C4\\u9500",o.crank,3,12,"crank",{material:"warm",explode:[0,-18,18]}),{id:"link",type:"rod",label:"\\u8FDE\\u6746",from:o.crank,to:o.piston,radius:2.4,group:"rod",material:"metal",explode:[0,0,45]},z("piston","\\u6ED1\\u5757",[o.position,0,22],[18,24,16],"piston",{round:3,material:"cool",explode:[20,0,18]}),F("pistonPin","\\u6ED1\\u5757\\u9500",o.piston,3,10,"piston",{material:"warm",explode:[20,0,18]})]}}var St=`\n.mui-mechanism{font:15px/1.7 system-ui,sans-serif;color:var(--canvas-ink);background:var(--canvas-bg);border:1px solid var(--canvas-line);border-radius:24px;padding:20px;max-width:100%;box-sizing:border-box}.mui-mechanism *{box-sizing:border-box}.mui-mechanism h2{font-size:22px;line-height:1.35;margin:0 0 8px;font-weight:750}.mui-mechanism p{margin:0;color:var(--canvas-muted)}\n.mui-mechanism-svg{display:block;width:100%;height:auto;max-height:420px;touch-action:pan-y}.mui-mechanism-svg path{stroke:color-mix(in srgb,var(--canvas-ink) 38%,var(--canvas-bg));stroke-width:.8;stroke-linejoin:round;vector-effect:non-scaling-stroke;cursor:pointer;fill:var(--mech-base,var(--canvas-soft))}.mui-mechanism-svg [data-material=metal]{--mech-base:color-mix(in srgb,var(--canvas-muted) 32%,var(--canvas-bg))}.mui-mechanism-svg [data-material=dark]{--mech-base:color-mix(in srgb,var(--canvas-ink) 78%,var(--canvas-bg))}.mui-mechanism-svg [data-material=accent]{--mech-base:var(--canvas-accent)}.mui-mechanism-svg [data-material=cool]{--mech-base:color-mix(in srgb,#22998a 70%,var(--canvas-bg))}.mui-mechanism-svg [data-material=warm]{--mech-base:color-mix(in srgb,#dd9c42 75%,var(--canvas-bg))}\n.mui-mechanism-svg [data-tone="0"]{fill:color-mix(in srgb,var(--mech-base) 74%,var(--canvas-bg))}.mui-mechanism-svg [data-tone="1"]{fill:var(--mech-base)}.mui-mechanism-svg [data-tone="2"]{fill:color-mix(in srgb,var(--mech-base) 85%,var(--canvas-ink))}.mui-mechanism-svg [data-tone="3"]{fill:color-mix(in srgb,var(--mech-base) 68%,var(--canvas-ink))}.mui-mechanism-svg [data-line]{fill:none;stroke:var(--mech-base);stroke-width:2}.mui-mechanism-svg [data-active=false]{opacity:.2}.mui-mechanism-svg [data-active=true]{opacity:1}\n.mui-mechanism-nav{display:flex;flex-wrap:wrap;gap:7px;margin:8px 0 12px}.mui-mechanism button{font:inherit;color:var(--canvas-ink);background:var(--canvas-bg);border:1px solid var(--canvas-line);border-radius:999px;padding:6px 13px;cursor:pointer;min-height:36px}.mui-mechanism button[aria-pressed=true]{color:var(--canvas-bg);background:var(--canvas-ink);border-color:var(--canvas-ink)}.mui-mechanism button:focus-visible,.mui-mechanism input:focus-visible{outline:2px solid var(--canvas-accent);outline-offset:3px}.mui-mechanism button:disabled{opacity:.5;cursor:default}\n.mui-mechanism-controls{display:flex;align-items:center;flex-wrap:wrap;gap:14px;padding:12px 14px;border-radius:16px;background:var(--canvas-soft);margin:8px 0}.mui-mechanism-controls label{display:flex;align-items:center;gap:9px;flex:1 1 180px;min-width:0}.mui-mechanism-controls input[type=range]{flex:1;min-width:70px;accent-color:var(--canvas-accent)}.mui-mechanism-controls input[type=checkbox]{accent-color:var(--canvas-accent)}.mui-mechanism-controls output{font-variant-numeric:tabular-nums;min-width:40px;text-align:right;font-weight:650}.mui-mechanism-readout{display:block;padding:10px 0;font-weight:700;font-variant-numeric:tabular-nums;color:var(--canvas-ink)}.mui-mechanism-description{min-height:0;font-size:14px}\n.mui-mechanism[data-style=editorial]{border:0;border-top:1px solid var(--canvas-line);border-radius:0;padding:16px 0}.mui-mechanism[data-style=editorial] h2{font-family:ui-serif,Georgia,serif;font-weight:650}.mui-mechanism[data-style=editorial] .mui-mechanism-controls{border-radius:0;background:transparent;border-block:1px solid var(--canvas-line)}\n.mui-mechanism[data-style=technical]{border-radius:10px}.mui-mechanism[data-style=technical] button,.mui-mechanism[data-style=technical] .mui-mechanism-controls{border-radius:6px}.mui-mechanism[data-style=technical] .mui-mechanism-readout{font-family:ui-monospace,Consolas,monospace}\n.mui-mechanism[data-style=soft]{border:0;background:var(--canvas-soft);border-radius:28px 28px 28px 10px}.mui-mechanism[data-style=soft] .mui-mechanism-controls{background:var(--canvas-bg);border-radius:22px}.mui-mechanism[data-style=expressive] h2{font-size:28px;font-weight:850;border-left:5px solid var(--canvas-accent);padding-left:14px}.mui-mechanism[data-style=expressive]{border:0;border-radius:0}.mui-mechanism[data-style=prismframe]{border:1.5px solid var(--canvas-line);box-shadow:4px 5px 0 color-mix(in srgb,var(--canvas-accent) 14%,var(--canvas-bg))}.mui-mechanism[data-style=aurora]{border-radius:28px;box-shadow:0 16px 30px -25px #30204d66}.mui-mechanism[data-style=aurora] .mui-mechanism-controls{border-radius:24px}\n@media(max-width:500px){.mui-mechanism{padding:14px;border-radius:20px}.mui-mechanism h2{font-size:20px}.mui-mechanism-controls{padding:10px}.mui-mechanism-nav{gap:5px}.mui-mechanism button{padding:6px 10px;font-size:14px}}\n@media(prefers-reduced-motion:reduce){.mui-mechanism *{animation:none!important;transition:none!important}}\n@media(forced-colors:active){.mui-mechanism-svg path{fill:Canvas;stroke:CanvasText}.mui-mechanism button[aria-pressed=true]{background:Highlight;color:HighlightText}}\n`;var xt="http://www.w3.org/2000/svg",C=(t,a,n)=>{let o=document.createElement(t);return a&&(o.className=a),n!==void 0&&(o.textContent=n),o};function Ft(t){function a(o,e){let i=typeof o=="string"?document.querySelector(o):o;if(!i||!i.replaceChildren)throw Error("Motion mechanism: target element not found");Y(e);let r=structuredClone({...e,onSelect:void 0}),p=nt(r),d={explode:0,selected:"all",cutaway:!1,pose:{}},m=!1,s="",c="",u=0,f=new Map,g=new Map,h=new Map,M=[],O=C("section","mui-mechanism"),N=C("h2",null,r.title??"\\u7ED3\\u6784\\u56FE\\u89E3"),$=document.createElementNS(xt,"svg"),P=document.createElementNS(xt,"g"),A=C("div","mui-mechanism-nav"),w=C("div","mui-mechanism-controls"),T=C("p","mui-mechanism-description"),D=C("output","mui-mechanism-readout"),G=C("style");G.textContent=St,N.hidden=r.title==="",O.setAttribute("aria-label",r.title??"\\u7ED3\\u6784\\u56FE\\u89E3"),$.setAttribute("class","mui-mechanism-svg"),$.setAttribute("viewBox","0 0 600 340"),$.setAttribute("role","img"),$.setAttribute("aria-label","\\u53EF\\u62C6\\u89E3\\u7684\\u7ED3\\u6784\\u793A\\u610F\\u56FE\\uFF1B\\u4F7F\\u7528\\u4E0B\\u65B9\\u6309\\u94AE\\u9009\\u62E9\\u90E8\\u4EF6"),$.append(P),O.append(G,N,$,A,w,D,T),i.replaceChildren(O);let S=()=>r.groups?.length?r.groups:r.parts.map(l=>({id:l.id,label:l.label??l.id,description:l.description??""}));function H(l){r.bind?.selected?t.setState({[r.bind.selected]:l}):typeof e.onSelect=="function"?e.onSelect(l):Q({selected:l})}function K(){if(A.replaceChildren(),h.clear(),!r.bind?.selected&&typeof e.onSelect!="function"){A.hidden=!0;return}A.hidden=!1;for(let l of[{id:"all",label:"\\u5168\\u90E8",description:""},...S()]){let b=C("button",null,l.label);b.type="button",b.setAttribute("aria-pressed",String(d.selected===l.id)),b.addEventListener("click",()=>H(l.id)),h.set(l.id,b),A.append(b)}}let x,rt,L;if(r.bind?.explode){let l=C("label",null,"\\u62C6\\u89E3\\u8FDB\\u5EA6");x=C("input"),x.type="range",x.min="0",x.max="1",x.step=".01",x.setAttribute("aria-label","\\u62C6\\u89E3\\u8FDB\\u5EA6"),rt=C("output"),x.addEventListener("input",()=>t.setState({[r.bind.explode]:Number(x.value)})),l.append(x,rt),w.append(l)}if(r.bind?.cutaway&&r.parts.some(l=>l.cover)){let l=C("label",null,"\\u5256\\u5F00\\u5916\\u58F3");L=C("input"),L.type="checkbox",L.setAttribute("aria-label","\\u5256\\u5F00\\u5916\\u58F3"),L.addEventListener("change",()=>t.setState({[r.bind.cutaway]:L.checked})),l.prepend(L),w.append(l)}w.hidden=!w.childNodes.length;function jt(l){let b=JSON.stringify([l.explode,l.cutaway]);b!==s&&(p=nt(r,l),s=b);let E=mt(r,p,l,f),k=new Set;for(let j of E){k.add(j.key);let R=g.get(j.key);R||(R=document.createElementNS(xt,"path"),R.setAttribute("fill-rule","evenodd"),g.set(j.key,R));for(let[gt,bt]of Object.entries({d:j.d,"data-part":j.part,"data-material":j.material,"data-tone":j.tone,"data-active":j.active}))R.getAttribute(gt)!==String(bt)&&R.setAttribute(gt,String(bt));j.line?R.setAttribute("data-line",""):R.removeAttribute("data-line")}for(let[j,R]of g)k.has(j)||(R.remove(),g.delete(j));let Z=E.map(j=>j.key).join("|");if(Z!==c){for(let j of E)P.append(g.get(j.key));c=Z}u++}function Q(l={}){if(m)return;for(let k of Object.keys(l))if(!["explode","selected","cutaway","pose","parts","readout"].includes(k))throw Error("Motion mechanism: unknown update field "+k);if(l.parts){let k={...r,parts:l.parts};Y(k);let Z=nt(k);mt(k,Z,{...d,...l},new Map),r=k,p=Z,f.clear(),c="",s=""}let b={...d,...l};l.pose&&(b.pose=l.pose),jt(b),d=b;for(let[k,Z]of h)Z.setAttribute("aria-pressed",String(b.selected===k));let E=S().find(k=>k.id===b.selected)??r.parts.find(k=>k.id===b.selected);T.textContent=E?.description??(b.selected==="all"?"\\u9009\\u62E9\\u4E00\\u4E2A\\u90E8\\u4EF6\\uFF0C\\u67E5\\u770B\\u5B83\\u7684\\u804C\\u8D23\\uFF1B\\u62C6\\u89E3\\u53EA\\u5C55\\u793A\\u88C5\\u914D\\u5173\\u7CFB\\u3002":""),x&&(x.value=String(b.explode),rt.textContent=Math.round(b.explode*100)+"%"),L&&(L.checked=b.cutaway),l.readout!==void 0&&(D.textContent=String(l.readout))}return $.addEventListener("click",l=>{let b=l.target.closest("[data-part]");if(!b)return;let E=r.parts.find(k=>k.id===b.getAttribute("data-part"));E&&H(r.groups?.length?E.group??E.id:E.id)}),K(),M.push(t.subscribe(l=>{if(m)return;let b={};for(let[E,k]of Object.entries(r.bind??{})){if(!Object.hasOwn(l.state,k))throw Error("Motion mechanism: bind."+E+" names undeclared state "+k);b[E]=l.state[k]}O.dataset.style=l.theme?.visualStyle??"";for(let E of h.values())E.disabled=l.disabled;x&&(x.disabled=l.disabled),L&&(L.disabled=l.disabled),Q(b)})),Object.freeze({update:Q,pose:l=>Q({pose:l}),stats:()=>({frames:u,faces:g.size,parts:r.parts.length}),destroy(){m||(m=!0,M.forEach(l=>l()),g.clear(),f.clear(),i.replaceChildren())}})}function n(o,e,i={}){if(!["assembly","gears","crank"].includes(e))throw Error("Motion mechanism: demo must be assembly/gears/crank");let r=i.bind??{},p=()=>t.snapshot.state,d=(A,w)=>Object.hasOwn(r,A)?p()[r[A]]:w;for(let[A,w]of Object.entries(r))if(!["explode","selected","cutaway","rpm","teeth","running","angle","radius"].includes(A)||typeof w!="string"||!Object.hasOwn(p(),w))throw Error("Motion mechanism: invalid demo binding "+A);let m=d("angle",0)*Math.PI/180,s=m,c="",u=!1,f=Object.fromEntries(Object.entries(r).filter(([A])=>["explode","selected","cutaway"].includes(A)));function g(){return e==="assembly"?ft():e==="gears"?pt({driverTeeth:i.driverTeeth??16,drivenTeeth:d("teeth",32),module:i.module??1.5}):ht({radius:d("radius",20),rod:i.rod??70})}let h=a(o,{...g(),...i.title===void 0?{}:{title:i.title},bind:f}),M=!1;function O(A=0,w=!1,T=!0){if(M)return;let D=d("rpm",30),G=d("running",!1),S=d("angle",0)*Math.PI/180,H=s;S!==m?(H=S,m=S):G&&T&&!w&&(H+=D*Math.PI*2/60*A),s=H;let K=JSON.stringify([e,d("teeth",32),d("radius",20)]);if(K!==c&&(h.update({parts:g().parts}),c=K),e==="gears"){let x=W({driverTeeth:i.driverTeeth??16,drivenTeeth:d("teeth",32),rpm:D,angle:s,module:i.module??1.5});h.update({pose:{driverGear:{rotation:x.driverAngle},drivenGear:{rotation:x.drivenAngle}},readout:"\\u4E3B\\u52A8\\u8F6E "+t.formatNumber(x.driverRpm,0)+" rpm \\xB7 \\u4ECE\\u52A8\\u8F6E "+t.formatNumber(x.drivenRpm,2)+" rpm \\xB7 \\u9F7F\\u6570\\u6BD4 "+t.formatNumber(x.ratio,2)+"\\uFF08\\u8D1F\\u53F7\\u8868\\u793A\\u53CD\\u5411\\uFF09"})}else if(e==="crank"){let x=X({angle:s,radius:d("radius",20),rod:i.rod??70,z:28});h.update({pose:{crankPin:{at:x.crank},link:{from:x.crank,to:x.piston},piston:{at:[x.position,0,22]},pistonPin:{at:x.piston}},readout:"\\u66F2\\u67C4\\u89D2 "+t.formatNumber((s*180/Math.PI%360+360)%360,1)+"\\xB0 \\xB7 \\u6ED1\\u5757\\u4F4D\\u7F6E "+t.formatNumber(x.position,2)+" \\xB7 \\u884C\\u7A0B "+t.formatNumber(x.stroke,1)+"\\uFF08\\u793A\\u610F\\u957F\\u5EA6\\u5355\\u4F4D\\uFF09"})}u=w}let N=null,$=()=>{O(0,u,!0),e!=="assembly"&&(d("running",!1)&&!N?N=t.animate(({delta:A,reducedMotion:w,visible:T})=>{T&&!w?O(A,!1,!0):u!==w&&O(0,w,T)}):!d("running",!1)&&N&&(N(),N=null))},P=t.subscribe($);return O(),Object.freeze({...h,destroy(){M||(M=!0,N?.(),P(),h.destroy())}})}return Object.freeze({mount:a,mountDemo:n,gearPair:W,sliderCrank:X,follow:Mt,scenes:Object.freeze({assembly:ft,gears:pt,crank:ht})})}return _t(Tt);})();\n';
289
289
 
290
290
  // lib/visual-audit.js
291
- function measureVisual(root2, nodeId = "", inspectCards = () => []) {
291
+ function measureVisual(root3, nodeId = "", inspectCards = () => []) {
292
292
  const issues = [], seen = /* @__PURE__ */ new Set(), coverage = { nodeId, elements: 0, truncated: false, contrastChecked: 0, contrastUncovered: 0 };
293
293
  const styleCache = /* @__PURE__ */ new WeakMap(), styleOf = (el2) => {
294
294
  let css = styleCache.get(el2);
@@ -345,10 +345,10 @@ function measureVisual(root2, nodeId = "", inspectCards = () => []) {
345
345
  }
346
346
  return null;
347
347
  };
348
- let contentBottom = root2.getBoundingClientRect().top;
349
- const widthRoot = root2.id === "canvas" ? document.documentElement : root2;
348
+ let contentBottom = root3.getBoundingClientRect().top;
349
+ const widthRoot = root3.id === "canvas" ? document.documentElement : root3;
350
350
  if (widthRoot.scrollWidth > widthRoot.clientWidth + 2) add2("overflow", widthRoot.scrollWidth - widthRoot.clientWidth);
351
- const walker = document.createTreeWalker(root2, NodeFilter.SHOW_ELEMENT);
351
+ const walker = document.createTreeWalker(root3, NodeFilter.SHOW_ELEMENT);
352
352
  let el;
353
353
  while (el = walker.nextNode()) {
354
354
  if (coverage.elements >= 2e3) {
@@ -385,7 +385,7 @@ function measureVisual(root2, nodeId = "", inspectCards = () => []) {
385
385
  range.selectNodeContents(el);
386
386
  const bounds = range.getBoundingClientRect();
387
387
  let a = el, depth = 0;
388
- while (a && a !== root2.parentElement && depth++ < 24) {
388
+ while (a && a !== root3.parentElement && depth++ < 24) {
389
389
  const c = styleOf(a), clip = a.getBoundingClientRect();
390
390
  if (["hidden", "clip"].includes(c.overflowX) && (bounds.left < clip.left - 2 || bounds.right > clip.right + 2) || ["hidden", "clip"].includes(c.overflowY) && (bounds.top < clip.top - 2 || bounds.bottom > clip.bottom + 2)) {
391
391
  add2("clippedText", void 0, el);
@@ -418,9 +418,9 @@ function measureVisual(root2, nodeId = "", inspectCards = () => []) {
418
418
  if (text3 || el.matches("svg,canvas,img,video,input,select,textarea,button")) contentBottom = Math.max(contentBottom, box2.bottom);
419
419
  if (el.matches("input:not([type=hidden]),select,textarea,button,[role=button]") && !el.getAttribute("aria-label") && !el.getAttribute("aria-labelledby") && !el.labels?.length && !el.textContent.trim() && !el.getAttribute("title")) add2("missingLabel", void 0, el);
420
420
  }
421
- for (const issue of inspectCards(root2)) add2("cardTextOverflow", Math.max(0, issue.excess), issue.target);
422
- const rect = root2.getBoundingClientRect(), gap = rect.bottom - contentBottom;
423
- if (!coverage.truncated && root2.id === "canvas" && rect.height > 320 && gap > Math.max(96, rect.height * 0.25)) add2("bottomWhitespace", gap, root2);
421
+ for (const issue of inspectCards(root3)) add2("cardTextOverflow", Math.max(0, issue.excess), issue.target);
422
+ const rect = root3.getBoundingClientRect(), gap = rect.bottom - contentBottom;
423
+ if (!coverage.truncated && root3.id === "canvas" && rect.height > 320 && gap > Math.max(96, rect.height * 0.25)) add2("bottomWhitespace", gap, root3);
424
424
  Object.defineProperty(issues, "coverage", { value: coverage });
425
425
  return issues;
426
426
  }
@@ -532,9 +532,9 @@ function createTimelineControlRepair() {
532
532
  return (Math.max(x, y2) + 0.05) / (Math.min(x, y2) + 0.05);
533
533
  };
534
534
  return () => {
535
- const stage = document.getElementById("stage"), root2 = document.getElementById("canvas");
536
- if (!stage || !root2) return;
537
- for (const el of root2.querySelectorAll("button,output,select,input:not([type=range]):not([type=checkbox]):not([type=radio])")) {
535
+ const stage = document.getElementById("stage"), root3 = document.getElementById("canvas");
536
+ if (!stage || !root3) return;
537
+ for (const el of root3.querySelectorAll("button,output,select,input:not([type=range]):not([type=checkbox]):not([type=radio])")) {
538
538
  if (stage.contains(el)) continue;
539
539
  let saved = originals.get(el);
540
540
  if (saved) for (const [key2, value, priority] of saved) value ? el.style.setProperty(key2, value, priority) : el.style.removeProperty(key2);
@@ -567,19 +567,19 @@ function createTimelineControlRepair() {
567
567
  }
568
568
  };
569
569
  }
570
- function canvasContentHeight(root2) {
570
+ function canvasContentHeight(root3) {
571
571
  const body = getComputedStyle(document.body);
572
- return Math.ceil(root2.getBoundingClientRect().bottom + window.scrollY + (parseFloat(body.paddingBottom) || 0) + (parseFloat(body.marginBottom) || 0));
572
+ return Math.ceil(root3.getBoundingClientRect().bottom + window.scrollY + (parseFloat(body.paddingBottom) || 0) + (parseFloat(body.marginBottom) || 0));
573
573
  }
574
574
 
575
575
  // lib/canvas-layout.js
576
- function inspectTextCards(root2, cards) {
576
+ function inspectTextCards(root3, cards) {
577
577
  const result = [];
578
578
  const cache = /* @__PURE__ */ new WeakMap(), styleOf = (el) => {
579
579
  if (!cache.has(el)) cache.set(el, getComputedStyle(el));
580
580
  return cache.get(el);
581
581
  };
582
- for (const card2 of cards ?? [...root2.querySelectorAll("article,section,aside,figure,div")].slice(0, 256).reverse()) {
582
+ for (const card2 of cards ?? [...root3.querySelectorAll("article,section,aside,figure,div")].slice(0, 256).reverse()) {
583
583
  if (card2.closest("#stage,svg,pre,code,[role=img],[data-motion-layout=preserve]")) continue;
584
584
  const css = styleOf(card2), box2 = card2.getBoundingClientRect();
585
585
  const radius = Math.max(parseFloat(css.borderBottomLeftRadius) || 0, parseFloat(css.borderBottomRightRadius) || 0);
@@ -630,9 +630,9 @@ function inspectTextCards(root2, cards) {
630
630
  }
631
631
  return result;
632
632
  }
633
- function fitTextCards(root2, inspect) {
634
- for (const { card: card2 } of inspect(root2)) {
635
- const issue = inspect(root2, [card2])[0];
633
+ function fitTextCards(root3, inspect) {
634
+ for (const { card: card2 } of inspect(root3)) {
635
+ const issue = inspect(root3, [card2])[0];
636
636
  if (!issue?.flow) continue;
637
637
  const css = getComputedStyle(card2), box2 = card2.getBoundingClientRect();
638
638
  if (issue.corner) card2.style.setProperty("padding-bottom", Math.max(parseFloat(css.paddingBottom) || 0, Math.min(issue.radius, box2.height / 2)) + "px", "important");
@@ -3713,22 +3713,22 @@ function parseToolArguments(raw) {
3713
3713
  return { value: JSON.parse(token), complete: true };
3714
3714
  }
3715
3715
  try {
3716
- const root2 = parse();
3716
+ const root3 = parse();
3717
3717
  ws();
3718
3718
  if (i !== raw.length) return null;
3719
- return root2;
3719
+ return root3;
3720
3720
  } catch {
3721
3721
  return null;
3722
3722
  }
3723
3723
  }
3724
3724
  function previewDocument(raw) {
3725
3725
  try {
3726
- const root2 = parseToolArguments(raw);
3727
- if (!root2) return null;
3728
- const document2 = root2.members?.get("document");
3729
- if (document2?.complete && root2.members.size === 1) return validateDocument(toolJson(document2.value, "document"));
3726
+ const root3 = parseToolArguments(raw);
3727
+ if (!root3) return null;
3728
+ const document2 = root3.members?.get("document");
3729
+ if (document2?.complete && root3.members.size === 1) return validateDocument(toolJson(document2.value, "document"));
3730
3730
  if (!document2?.members) return null;
3731
- if ([...root2.members.keys()].some((k2) => k2 !== "document")) return null;
3731
+ if ([...root3.members.keys()].some((k2) => k2 !== "document")) return null;
3732
3732
  const doc = { ...document2.value }, nodes = document2.members.get("nodes");
3733
3733
  const items = document2.members.get("items");
3734
3734
  if (!nodes && items && Array.isArray(items.value) && items.value.length) {
@@ -3751,19 +3751,19 @@ function previewDocument(raw) {
3751
3751
  return null;
3752
3752
  }
3753
3753
  function previewCanvas(raw) {
3754
- const root2 = parseToolArguments(raw);
3755
- if (!root2?.members) return null;
3756
- const get = (k2) => root2.members.get(k2), html = get("html"), css = get("css");
3754
+ const root3 = parseToolArguments(raw);
3755
+ if (!root3?.members) return null;
3756
+ const get = (k2) => root3.members.get(k2), html = get("html"), css = get("css");
3757
3757
  if (!get("title")?.complete || !get("fallback")?.complete || typeof html?.value !== "string" || !html.value || !css?.complete || typeof css.value !== "string") return null;
3758
3758
  if (html.value.length > 49152 || css.value.length > 49152) return null;
3759
3759
  return { id: "canvas-preview", title: String(get("title").value).slice(0, 160), fallback: String(get("fallback").value).slice(0, 2e3), html: html.value, css: css.value, height: Number.isFinite(get("height")?.value) ? Math.max(160, Math.min(1e3, get("height").value)) : 420 };
3760
3760
  }
3761
3761
  function previewText(raw) {
3762
- const root2 = parseToolArguments(raw), blocks = root2?.members?.get("blocks");
3762
+ const root3 = parseToolArguments(raw), blocks = root3?.members?.get("blocks");
3763
3763
  if (!Array.isArray(blocks?.value) || !blocks.value.length) return null;
3764
3764
  for (let n2 = blocks.value.length; n2 > 0; n2--) {
3765
3765
  try {
3766
- return textDocument({ ...root2.value, blocks: blocks.value.slice(0, n2) });
3766
+ return textDocument({ ...root3.value, blocks: blocks.value.slice(0, n2) });
3767
3767
  } catch {
3768
3768
  }
3769
3769
  }
@@ -4075,11 +4075,11 @@ function useNativeStyle() {
4075
4075
  // src/action-menu.js
4076
4076
  var import_react7 = __toESM(require("react"), 1);
4077
4077
  function ActionMenu({ label, children }) {
4078
- const [open, setOpen] = (0, import_react7.useState)(false), root2 = (0, import_react7.useRef)(null), button = (0, import_react7.useRef)(null);
4078
+ const [open, setOpen] = (0, import_react7.useState)(false), root3 = (0, import_react7.useRef)(null), button = (0, import_react7.useRef)(null);
4079
4079
  (0, import_react7.useEffect)(() => {
4080
4080
  if (!open) return;
4081
4081
  const outside = (e) => {
4082
- if (!root2.current?.contains(e.target)) setOpen(false);
4082
+ if (!root3.current?.contains(e.target)) setOpen(false);
4083
4083
  };
4084
4084
  const escape = (e) => {
4085
4085
  if (e.key === "Escape") {
@@ -4096,7 +4096,7 @@ function ActionMenu({ label, children }) {
4096
4096
  }, [open]);
4097
4097
  return import_react7.default.createElement(
4098
4098
  "div",
4099
- { className: "dgui-action-menu", ref: root2 },
4099
+ { className: "dgui-action-menu", ref: root3 },
4100
4100
  import_react7.default.createElement("button", { ref: button, type: "button", className: "dgui-menu-trigger", "aria-label": label, "aria-expanded": open, onClick: () => setOpen(!open) }, "\u22EF"),
4101
4101
  open && import_react7.default.createElement("div", { className: "dgui-menu-content", "aria-label": label, onClick: (e) => {
4102
4102
  if (e.target.closest("button")) setOpen(false);
@@ -4181,8 +4181,8 @@ function Sandbox({ node, document: doc, state: state2, data, derived, disabled,
4181
4181
  }, 5e3);
4182
4182
  const observer = new MutationObserver(publish2);
4183
4183
  observer.observe(document.body, { attributes: true });
4184
- const root2 = frame2.current?.closest(".dgui-root");
4185
- if (root2) observer.observe(root2, { attributes: true });
4184
+ const root3 = frame2.current?.closest(".dgui-root");
4185
+ if (root3) observer.observe(root3, { attributes: true });
4186
4186
  const intersection = new IntersectionObserver((entries) => {
4187
4187
  visible.current = entries[0]?.isIntersecting ?? true;
4188
4188
  publish2();
@@ -4260,7 +4260,7 @@ function Sandbox({ node, document: doc, state: state2, data, derived, disabled,
4260
4260
  }
4261
4261
 
4262
4262
  // lib/build-info.js
4263
- var BUILD_INFO = Object.freeze({ "version": "0.8.3", "id": "0.8.3+da9b5c9a709d", "sourceHash": "da9b5c9a709de333a5c0348d1e9544ced197459e293f22dcfdc302b8dbb5f3d0" });
4263
+ var BUILD_INFO = Object.freeze({ "version": "0.8.5", "id": "0.8.5+64909b292cda", "sourceHash": "64909b292cda52745fc063409ac48c8c665d9be60ca7212ca716748c0f8fe5fd" });
4264
4264
 
4265
4265
  // lib/refinement.js
4266
4266
  var AUDIT_MESSAGES = Object.freeze({ overflow: "\u5185\u5BB9\u8D85\u51FA\u89C6\u53E3\u5BBD\u5EA6", smallText: "\u6587\u5B57\u5B9E\u9645\u663E\u793A\u5C3A\u5BF8\u5C0F\u4E8E 12px", contrast: "\u6587\u5B57\u4E0E\u80CC\u666F\u5BF9\u6BD4\u5EA6\u504F\u4F4E", missingLabel: "\u4EA4\u4E92\u63A7\u4EF6\u7F3A\u5C11\u53EF\u8BBF\u95EE\u540D\u79F0", runtimeError: "\u753B\u5E03\u62A5\u544A\u8FD0\u884C\u5F02\u5E38", clippedText: "\u6587\u5B57\u53EF\u80FD\u88AB\u9690\u85CF\u6216\u88C1\u5207", cardTextOverflow: "\u6587\u5B57\u8D85\u51FA\u5361\u7247\u5185\u5BB9\u8FB9\u754C\uFF1B\u8BF7\u4FDD\u7559\u9634\u5F71\u5E76\u8C03\u6574\u5BB9\u5668\u5C3A\u5BF8\u3001\u5185\u8FB9\u8DDD\u6216\u6587\u5B57\u6392\u7248", bottomWhitespace: "\u5185\u5BB9\u4E0B\u65B9\u5B58\u5728\u660E\u663E\u7A7A\u767D" });
@@ -4436,7 +4436,7 @@ function markedUiId(block) {
4436
4436
  const id2 = (code?.textContent ?? pre?.textContent ?? "").trim();
4437
4437
  return idPattern.test(id2) ? id2 : null;
4438
4438
  }
4439
- function startUiMarkers(root2, sessionId) {
4439
+ function startUiMarkers(root3, sessionId) {
4440
4440
  const claims = /* @__PURE__ */ new Map(), references = /* @__PURE__ */ new Map();
4441
4441
  let scheduled = false, disposed = false;
4442
4442
  const scan = () => {
@@ -4453,7 +4453,7 @@ function startUiMarkers(root2, sessionId) {
4453
4453
  references.delete(block);
4454
4454
  }
4455
4455
  const known = new Set(uiRecords().filter((r) => r.sessionId === sessionId).map((r) => r.raw.documentId));
4456
- for (const block of root2.querySelectorAll("div.md-code-block,pre")) {
4456
+ for (const block of root3.querySelectorAll("div.md-code-block,pre")) {
4457
4457
  if (block.matches("pre") && block.closest("div.md-code-block")) continue;
4458
4458
  if (block.closest("[data-dsh-generative-ui]") || claims.has(block)) continue;
4459
4459
  const id2 = markedUiId(block);
@@ -4465,6 +4465,7 @@ function startUiMarkers(root2, sessionId) {
4465
4465
  }
4466
4466
  const host = block.ownerDocument.createElement("div");
4467
4467
  host.dataset.dguiMarker = id2;
4468
+ host.className = "dgui-marker";
4468
4469
  host.uiMarkerBlock = block;
4469
4470
  block.before(host);
4470
4471
  claims.set(block, { id: id2, host, display: block.style.display });
@@ -4478,7 +4479,7 @@ function startUiMarkers(root2, sessionId) {
4478
4479
  }
4479
4480
  };
4480
4481
  const observer = new MutationObserver(queue);
4481
- observer.observe(root2, { childList: true, subtree: true, characterData: true });
4482
+ observer.observe(root3, { childList: true, subtree: true, characterData: true });
4482
4483
  const unsubscribe = subscribeUi(queue);
4483
4484
  scan();
4484
4485
  return () => {
@@ -4504,12 +4505,12 @@ function usePresentationOwner(anchor, documentId, sessionId) {
4504
4505
  (0, import_react12.useEffect)(() => {
4505
4506
  const element = anchor.current;
4506
4507
  if (!element) return;
4507
- const root2 = element.closest("[data-conversation-scroll]") ?? element.parentElement;
4508
- if (!root2) return;
4509
- let documents = roots.get(root2);
4508
+ const root3 = element.closest("[data-conversation-scroll]") ?? element.parentElement;
4509
+ if (!root3) return;
4510
+ let documents = roots.get(root3);
4510
4511
  if (!documents) {
4511
4512
  documents = /* @__PURE__ */ new Map();
4512
- roots.set(root2, documents);
4513
+ roots.set(root3, documents);
4513
4514
  }
4514
4515
  const key2 = sessionId + ":" + documentId;
4515
4516
  let entries = documents.get(key2);
@@ -4549,23 +4550,23 @@ function PresentationView({ raw, sessionId, sendPrompt, openFile, scope: scope2
4549
4550
  rememberUi(raw, sessionId, sendPrompt, openFile);
4550
4551
  }, [raw, sessionId, sendPrompt, openFile]);
4551
4552
  (0, import_react13.useEffect)(() => {
4552
- const root2 = anchor.current?.closest("[data-conversation-scroll]");
4553
- if (!root2) return;
4554
- let entry = scanners.get(root2);
4553
+ const root3 = anchor.current?.closest("[data-conversation-scroll]");
4554
+ if (!root3) return;
4555
+ let entry = scanners.get(root3);
4555
4556
  if (entry && entry.sessionId !== sessionId) {
4556
4557
  entry.stop();
4557
- scanners.delete(root2);
4558
+ scanners.delete(root3);
4558
4559
  entry = null;
4559
4560
  }
4560
4561
  if (!entry) {
4561
- entry = { sessionId, count: 0, stop: startUiMarkers(root2, sessionId) };
4562
- scanners.set(root2, entry);
4562
+ entry = { sessionId, count: 0, stop: startUiMarkers(root3, sessionId) };
4563
+ scanners.set(root3, entry);
4563
4564
  }
4564
4565
  entry.count++;
4565
4566
  return () => {
4566
4567
  if (--entry.count === 0) {
4567
4568
  entry.stop();
4568
- scanners.delete(root2);
4569
+ scanners.delete(root3);
4569
4570
  }
4570
4571
  };
4571
4572
  }, [sessionId]);
@@ -5555,12 +5556,12 @@ function UiView({ record, sessionId, sendPrompt, openFile, preview = false, repl
5555
5556
  const nativeStyle = useNativeStyle();
5556
5557
  const doc = record.document;
5557
5558
  const viewId = import_react18.default.useId();
5558
- const root2 = (0, import_react18.useRef)(null), canvasAudits = (0, import_react18.useRef)(/* @__PURE__ */ new Map()), [audit, setAudit] = (0, import_react18.useState)(null), auditPending = (0, import_react18.useRef)(0);
5559
+ const root3 = (0, import_react18.useRef)(null), canvasAudits = (0, import_react18.useRef)(/* @__PURE__ */ new Map()), [audit, setAudit] = (0, import_react18.useState)(null), auditPending = (0, import_react18.useRef)(0);
5559
5560
  const collectAudit = import_react18.default.useCallback(() => {
5560
5561
  clearTimeout(auditPending.current);
5561
5562
  auditPending.current = setTimeout(() => {
5562
- if (!root2.current || preview) return;
5563
- const native = measureVisual(root2.current), canvases = [...canvasAudits.current.values()];
5563
+ if (!root3.current || preview) return;
5564
+ const native = measureVisual(root3.current), canvases = [...canvasAudits.current.values()];
5564
5565
  const issues = [...native, ...canvases.flatMap((v2) => v2.issues)].slice(0, 40), coverage = [native.coverage, ...canvases.map((v2) => v2.coverage).filter(Boolean)].slice(0, 32);
5565
5566
  const report = { width: window.innerWidth, theme: document.body.hasAttribute("data-ds-dark-theme") ? "dark" : "light", issues, coverage };
5566
5567
  setAudit(report);
@@ -5716,7 +5717,7 @@ function UiView({ record, sessionId, sendPrompt, openFile, preview = false, repl
5716
5717
  if (doc.nodes.every((n2) => n2.type === "sandbox" && exportedCanvases.has(n2.id))) return null;
5717
5718
  return h12(
5718
5719
  "section",
5719
- { ref: root2, className: "dgui-root", "data-document-revision": record.documentRevision ?? 0, "data-dgui-build": BUILD_INFO.id, "data-dsh-generative-ui": record.documentId, "data-preset": doc.presentation?.preset ?? ({ comparison: "comparison", guide: "list", report: "reading" }[doc.presentation?.layout] ?? "brief"), "data-visual-style": nativeStyle, "data-layout": doc.presentation?.layout, "data-accent": doc.presentation?.accent, "data-density": doc.presentation?.density, "data-preview": preview || void 0, "aria-label": doc.title, "aria-busy": preview || void 0 },
5720
+ { ref: root3, className: "dgui-root", "data-document-revision": record.documentRevision ?? 0, "data-dgui-build": BUILD_INFO.id, "data-dsh-generative-ui": record.documentId, "data-preset": doc.presentation?.preset ?? ({ comparison: "comparison", guide: "list", report: "reading" }[doc.presentation?.layout] ?? "brief"), "data-visual-style": nativeStyle, "data-layout": doc.presentation?.layout, "data-accent": doc.presentation?.accent, "data-density": doc.presentation?.density, "data-preview": preview || void 0, "aria-label": doc.title, "aria-busy": preview || void 0 },
5720
5721
  doc.title && doc.title !== "\u5185\u5BB9" && !(doc.nodes[0]?.type === "heading" && typeof doc.nodes[0].text === "string" && doc.nodes[0].text.trim() === doc.title.trim()) && h12("header", { className: "dgui-header" }, h12("strong", { className: "dgui-header-title" }, doc.title)),
5721
5722
  !preview && deliveryDiagnostics(doc).length > 0 && h12("p", { className: "dgui-tool-error", role: "status" }, "\u90E8\u5206\u5185\u5BB9\u672A\u751F\u6210\uFF1B\u6A21\u578B\u53EF\u4FEE\u590D\u5F53\u524D\u754C\u9762\u3002"),
5722
5723
  h12("div", { key: epoch, className: "dgui-content" }, doc.nodes.map(renderNode)),
@@ -5873,50 +5874,59 @@ var streamUiDefinition = {
5873
5874
  }
5874
5875
  };
5875
5876
 
5877
+ // src/host-boundary.js
5878
+ var root = ".dgui-root[data-preset]:not(.dgui-style-preview)";
5879
+ var HOST_BOUNDARY_CSS = `
5880
+ ${root}{border-radius:var(--du-shell-radius,16px)!important;corner-shape:round}
5881
+ ` + Object.entries(STYLE_SYSTEM).map(([id2, { native }]) => `${root}[data-visual-style=${id2}]{--du-shell-radius:${id2 === "editorial" ? 0 : id2 === "soft" ? 30 : native.radius}px}`).join("\n") + `
5882
+ ${root}:is([data-preset=reading],[data-preset=minimal]){--du-shell-radius:0px}
5883
+ .dgui-marker[data-dgui-marker]{border:0!important;background:transparent!important;box-shadow:none!important;border-radius:0!important;padding:0!important;min-width:0;box-sizing:border-box}
5884
+ `;
5885
+
5876
5886
  // src/native-style.js
5877
- var root = ".dgui-root[data-preset][data-visual-style]";
5878
- var grammar = (id2, css) => css.replaceAll("&", root + "[data-visual-style=" + id2 + "]");
5887
+ var root2 = ".dgui-root[data-preset][data-visual-style]";
5888
+ var grammar = (id2, css) => css.replaceAll("&", root2 + "[data-visual-style=" + id2 + "]");
5879
5889
  var NATIVE_STYLE_CSS = VISUAL_STYLES.map((s) => {
5880
5890
  const n2 = STYLE_SYSTEM[s.id].native;
5881
- return `${root}[data-visual-style=${s.id}]{--du-style-base:${s.preview.accent};--du-style-color:var(--du-style-base);--du-style-wash:${s.preview.bg};--du-title-size:${n2.title}px;--du-title-weight:${n2.weight};--du-content-gap:${n2.gap}px;--du-radius:${n2.radius}px;--du-radius-inner:${n2.radius - 4}px;--du-metric-size:${n2.metric}px;--du-feedback-ms:${n2.feedback};--du-motion-gain:${n2.gain}}`;
5891
+ return `${root2}[data-visual-style=${s.id}]{--du-style-base:${s.preview.accent};--du-style-color:var(--du-style-base);--du-style-wash:${s.preview.bg};--du-title-size:${n2.title}px;--du-title-weight:${n2.weight};--du-content-gap:${n2.gap}px;--du-radius:${n2.radius}px;--du-radius-inner:${n2.radius - 4}px;--du-metric-size:${n2.metric}px;--du-feedback-ms:${n2.feedback};--du-motion-gain:${n2.gain}}`;
5882
5892
  }).join("\n") + `
5883
- ${root}{--du-style-scale:1;--du-line:color-mix(in srgb,var(--du-style-color) 24%,var(--du-bg));--du-soft:color-mix(in srgb,var(--du-style-color) 6%,var(--du-bg));--du-tint:color-mix(in srgb,var(--du-style-color) 10%,var(--du-bg));--du-motion-fill:linear-gradient(115deg,var(--du-accent),color-mix(in srgb,var(--du-accent) 65%,#202832));--du-motion-orb:var(--du-motion-fill);--du-action-radius:999px;--du-orb-radius:50%;border:1px solid var(--du-line);border-radius:var(--du-radius);background:var(--du-soft);padding:calc(24px * var(--du-style-scale));font-size:calc(16px * var(--du-style-scale));line-height:1.75;box-shadow:none;container:motion-surface / inline-size}
5884
- ${root}:not([data-accent]){--du-accent:color-mix(in srgb,var(--du-style-color) 82%,#000)}
5885
- ${root} .dgui-header{margin:0 0 var(--du-content-gap);padding:0;border:0;gap:12px}
5886
- ${root} .dgui-header-title{color:var(--du-ink);font-size:calc(var(--du-title-size) * var(--du-style-scale));font-weight:var(--du-title-weight);line-height:1.3;letter-spacing:-.5px}
5887
- ${root} .dgui-content,${root} .dgui-column{gap:calc(var(--du-content-gap) * var(--du-style-scale))}
5888
- ${root} .dgui-row{gap:calc(var(--du-content-gap) * var(--du-style-scale));align-items:stretch}
5889
- ${root} .dgui-section-title{font-size:calc(20px * var(--du-style-scale));font-weight:700;line-height:1.45}
5890
- ${root}:not(:has(>.dgui-header))>.dgui-content>.dgui-section-title:first-child{font-size:calc(var(--du-title-size) * var(--du-style-scale));font-weight:var(--du-title-weight);line-height:1.3}
5891
- ${root} .dgui-copy{font-size:inherit;line-height:1.8;color:var(--du-ink)}
5892
- ${root} .dgui-card{background:var(--du-bg);border:1px solid var(--du-line);border-radius:var(--du-radius-inner);padding:calc(20px * var(--du-style-scale));gap:calc(14px * var(--du-style-scale));box-shadow:none}
5893
- ${root} .dgui-card>.dgui-section-title{border:0;padding:0;font-size:calc(18px * var(--du-style-scale));color:var(--du-ink)}
5894
- ${root} .dgui-metric{background:var(--du-bg);border:1px solid var(--du-line);border-radius:var(--du-radius-inner);padding:calc(18px * var(--du-style-scale));gap:7px;box-shadow:none;--du-tone:var(--du-accent)}
5895
- ${root} .dgui-metric[data-tone=success]{--du-tone:var(--du-green)}${root} .dgui-metric[data-tone=warning]{--du-tone:var(--du-amber)}${root} .dgui-metric[data-tone=danger]{--du-tone:var(--du-red)}
5896
- ${root} .dgui-metric strong{font-size:calc(var(--du-metric-size) * var(--du-style-scale));font-weight:760;line-height:1.15;letter-spacing:-.6px;color:var(--du-tone)}
5897
- ${root} .dgui-metric-label{font-size:calc(13px * var(--du-style-scale))}${root} .dgui-metric small{font-size:calc(14px * var(--du-style-scale));letter-spacing:0}
5898
- ${root} .dgui-control{border:0;padding:12px 14px;background:var(--du-soft);border-radius:14px;gap:14px}
5899
- ${root} .dgui-control>label,${root} .dgui-control-label{font-size:inherit;color:var(--du-ink);font-weight:550}
5900
- ${root} .dgui-control output{color:var(--du-accent);background:var(--du-bg);border:1px solid var(--du-line);border-radius:10px;padding:4px 10px;min-width:58px;font-size:inherit}
5901
- ${root} .dgui-control input:not([type=range]):not([type=checkbox]),${root} .dgui-control select{border-radius:12px;background:var(--du-bg);font-size:inherit}
5902
- ${root} .dgui-control input[type=range]{accent-color:var(--du-accent)}
5903
- ${root} .dgui-button{border-radius:var(--du-action-radius);background:var(--du-accent);border-color:var(--du-accent)}
5904
- ${root} .dgui-button[data-variant=secondary]{background:transparent;color:var(--du-ink);border-color:var(--du-line)}${root} .dgui-button[data-variant=text]{background:none;color:var(--du-accent);border-color:transparent}
5905
- ${root} .dgui-steps{gap:12px;padding:0;margin:0}
5906
- ${root} .dgui-steps li{position:relative;padding:14px 16px;display:flex;flex-direction:row;align-items:flex-start;text-align:left;gap:14px;border-radius:var(--du-radius-inner);background:var(--du-bg)}
5907
- ${root} .dgui-steps li:before,${root} .dgui-steps li:after{display:none}${root} .dgui-step-dot{display:flex;flex:none;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;font:650 14px/1 system-ui;background:var(--du-tint);color:var(--du-tone)}
5908
- ${root} .dgui-steps strong{font-size:inherit}${root} .dgui-steps p{font-size:calc(14px * var(--du-style-scale));line-height:1.7}
5909
- ${root} .dgui-chart{margin:0;border-radius:var(--du-radius-inner);background:var(--du-bg);padding:16px;border:1px solid var(--du-line)}
5910
- ${root} .dgui-table-wrap{border-radius:var(--du-radius-inner);background:var(--du-bg);border:1px solid var(--du-line)}${root} table{font-size:calc(14px * var(--du-style-scale))}${root} th{font-weight:650;color:var(--du-ink);background:var(--du-soft)}
5911
- ${root} .motion-copy-block{border-radius:var(--du-radius-inner);background:var(--du-bg);border:1px solid var(--du-line)}${root} .motion-copy-block[data-code]{background:var(--du-soft)}
5912
- ${root} .dgui-progress:not([data-tone]),${root} .dgui-progress[data-tone=neutral]{--du-tone:var(--du-accent)}
5913
- ${root} [data-motion-component=card],${root} [data-motion-component=stat]{border:1px solid var(--du-line);border-radius:var(--du-radius-inner);background:var(--du-bg);box-shadow:none}
5914
- ${root} [data-motion-component=stat] [class*="statValue"]{font-size:var(--du-metric-size);font-weight:var(--du-title-weight);line-height:1.15;letter-spacing:-.7px}
5915
- ${root} [data-motion-component=card] [class*="cardTitle"]{font-size:20px;font-weight:var(--du-title-weight)}
5916
- ${root} [data-motion-visual] [class*="block"]{--dsl-g-radius-surface:var(--du-radius-inner);--dsl-g-radius-control:var(--du-action-radius);--dsl-g-font-body:16px;--dsl-g-font-title:18px}
5917
- ${root}[data-preset=reading],${root}[data-preset=minimal]{padding:0;border:0;background:transparent;box-shadow:none}
5918
- body[data-ds-dark-theme] ${root},${root}[data-preview-theme=dark]{--du-style-color:color-mix(in srgb,var(--du-style-wash) 58%,var(--du-style-base));--du-line:color-mix(in srgb,var(--du-style-color) 24%,var(--du-bg));--du-soft:color-mix(in srgb,var(--du-style-color) 8%,var(--du-bg))}
5919
- body[data-ds-dark-theme] ${root}:not([data-accent]),${root}[data-preview-theme=dark]:not([data-accent]){--du-accent:var(--du-style-color)}
5893
+ ${root2}{--du-style-scale:1;--du-line:color-mix(in srgb,var(--du-style-color) 24%,var(--du-bg));--du-soft:color-mix(in srgb,var(--du-style-color) 6%,var(--du-bg));--du-tint:color-mix(in srgb,var(--du-style-color) 10%,var(--du-bg));--du-motion-fill:linear-gradient(115deg,var(--du-accent),color-mix(in srgb,var(--du-accent) 65%,#202832));--du-motion-orb:var(--du-motion-fill);--du-action-radius:999px;--du-orb-radius:50%;border:1px solid var(--du-line);border-radius:var(--du-radius);background:var(--du-soft);padding:calc(24px * var(--du-style-scale));font-size:calc(16px * var(--du-style-scale));line-height:1.75;box-shadow:none;container:motion-surface / inline-size}
5894
+ ${root2}:not([data-accent]){--du-accent:color-mix(in srgb,var(--du-style-color) 82%,#000)}
5895
+ ${root2} .dgui-header{margin:0 0 var(--du-content-gap);padding:0;border:0;gap:12px}
5896
+ ${root2} .dgui-header-title{color:var(--du-ink);font-size:calc(var(--du-title-size) * var(--du-style-scale));font-weight:var(--du-title-weight);line-height:1.3;letter-spacing:-.5px}
5897
+ ${root2} .dgui-content,${root2} .dgui-column{gap:calc(var(--du-content-gap) * var(--du-style-scale))}
5898
+ ${root2} .dgui-row{gap:calc(var(--du-content-gap) * var(--du-style-scale));align-items:stretch}
5899
+ ${root2} .dgui-section-title{font-size:calc(20px * var(--du-style-scale));font-weight:700;line-height:1.45}
5900
+ ${root2}:not(:has(>.dgui-header))>.dgui-content>.dgui-section-title:first-child{font-size:calc(var(--du-title-size) * var(--du-style-scale));font-weight:var(--du-title-weight);line-height:1.3}
5901
+ ${root2} .dgui-copy{font-size:inherit;line-height:1.8;color:var(--du-ink)}
5902
+ ${root2} .dgui-card{background:var(--du-bg);border:1px solid var(--du-line);border-radius:var(--du-radius-inner);padding:calc(20px * var(--du-style-scale));gap:calc(14px * var(--du-style-scale));box-shadow:none}
5903
+ ${root2} .dgui-card>.dgui-section-title{border:0;padding:0;font-size:calc(18px * var(--du-style-scale));color:var(--du-ink)}
5904
+ ${root2} .dgui-metric{background:var(--du-bg);border:1px solid var(--du-line);border-radius:var(--du-radius-inner);padding:calc(18px * var(--du-style-scale));gap:7px;box-shadow:none;--du-tone:var(--du-accent)}
5905
+ ${root2} .dgui-metric[data-tone=success]{--du-tone:var(--du-green)}${root2} .dgui-metric[data-tone=warning]{--du-tone:var(--du-amber)}${root2} .dgui-metric[data-tone=danger]{--du-tone:var(--du-red)}
5906
+ ${root2} .dgui-metric strong{font-size:calc(var(--du-metric-size) * var(--du-style-scale));font-weight:760;line-height:1.15;letter-spacing:-.6px;color:var(--du-tone)}
5907
+ ${root2} .dgui-metric-label{font-size:calc(13px * var(--du-style-scale))}${root2} .dgui-metric small{font-size:calc(14px * var(--du-style-scale));letter-spacing:0}
5908
+ ${root2} .dgui-control{border:0;padding:12px 14px;background:var(--du-soft);border-radius:14px;gap:14px}
5909
+ ${root2} .dgui-control>label,${root2} .dgui-control-label{font-size:inherit;color:var(--du-ink);font-weight:550}
5910
+ ${root2} .dgui-control output{color:var(--du-accent);background:var(--du-bg);border:1px solid var(--du-line);border-radius:10px;padding:4px 10px;min-width:58px;font-size:inherit}
5911
+ ${root2} .dgui-control input:not([type=range]):not([type=checkbox]),${root2} .dgui-control select{border-radius:12px;background:var(--du-bg);font-size:inherit}
5912
+ ${root2} .dgui-control input[type=range]{accent-color:var(--du-accent)}
5913
+ ${root2} .dgui-button{border-radius:var(--du-action-radius);background:var(--du-accent);border-color:var(--du-accent)}
5914
+ ${root2} .dgui-button[data-variant=secondary]{background:transparent;color:var(--du-ink);border-color:var(--du-line)}${root2} .dgui-button[data-variant=text]{background:none;color:var(--du-accent);border-color:transparent}
5915
+ ${root2} .dgui-steps{gap:12px;padding:0;margin:0}
5916
+ ${root2} .dgui-steps li{position:relative;padding:14px 16px;display:flex;flex-direction:row;align-items:flex-start;text-align:left;gap:14px;border-radius:var(--du-radius-inner);background:var(--du-bg)}
5917
+ ${root2} .dgui-steps li:before,${root2} .dgui-steps li:after{display:none}${root2} .dgui-step-dot{display:flex;flex:none;align-items:center;justify-content:center;width:32px;height:32px;border-radius:50%;font:650 14px/1 system-ui;background:var(--du-tint);color:var(--du-tone)}
5918
+ ${root2} .dgui-steps strong{font-size:inherit}${root2} .dgui-steps p{font-size:calc(14px * var(--du-style-scale));line-height:1.7}
5919
+ ${root2} .dgui-chart{margin:0;border-radius:var(--du-radius-inner);background:var(--du-bg);padding:16px;border:1px solid var(--du-line)}
5920
+ ${root2} .dgui-table-wrap{border-radius:var(--du-radius-inner);background:var(--du-bg);border:1px solid var(--du-line)}${root2} table{font-size:calc(14px * var(--du-style-scale))}${root2} th{font-weight:650;color:var(--du-ink);background:var(--du-soft)}
5921
+ ${root2} .motion-copy-block{border-radius:var(--du-radius-inner);background:var(--du-bg);border:1px solid var(--du-line)}${root2} .motion-copy-block[data-code]{background:var(--du-soft)}
5922
+ ${root2} .dgui-progress:not([data-tone]),${root2} .dgui-progress[data-tone=neutral]{--du-tone:var(--du-accent)}
5923
+ ${root2} [data-motion-component=card],${root2} [data-motion-component=stat]{border:1px solid var(--du-line);border-radius:var(--du-radius-inner);background:var(--du-bg);box-shadow:none}
5924
+ ${root2} [data-motion-component=stat] [class*="statValue"]{font-size:var(--du-metric-size);font-weight:var(--du-title-weight);line-height:1.15;letter-spacing:-.7px}
5925
+ ${root2} [data-motion-component=card] [class*="cardTitle"]{font-size:20px;font-weight:var(--du-title-weight)}
5926
+ ${root2} [data-motion-visual] [class*="block"]{--dsl-g-radius-surface:var(--du-radius-inner);--dsl-g-radius-control:var(--du-action-radius);--dsl-g-font-body:16px;--dsl-g-font-title:18px}
5927
+
5928
+ body[data-ds-dark-theme] ${root2},${root2}[data-preview-theme=dark]{--du-style-color:color-mix(in srgb,var(--du-style-wash) 58%,var(--du-style-base));--du-line:color-mix(in srgb,var(--du-style-color) 24%,var(--du-bg));--du-soft:color-mix(in srgb,var(--du-style-color) 8%,var(--du-bg))}
5929
+ body[data-ds-dark-theme] ${root2}:not([data-accent]),${root2}[data-preview-theme=dark]:not([data-accent]){--du-accent:var(--du-style-color)}
5920
5930
  ` + grammar("editorial", `
5921
5931
  &{background:var(--du-bg);border:0;border-radius:0;padding:16px 0;--du-motion-fill:linear-gradient(115deg,#44546a,#182537);--du-motion-orb:#44546a;--du-action-radius:10px;--du-orb-size:28px;--du-feedback-travel:18%;--du-label-ms:220ms}
5922
5932
  & .dgui-header{border-bottom:1px solid var(--du-line);padding-bottom:16px}& .dgui-header-title,& .dgui-section-title{font-family:ui-serif,Georgia,'Noto Serif SC',serif;font-weight:650}
@@ -5985,10 +5995,13 @@ body[data-ds-dark-theme] ${root}:not([data-accent]),${root}[data-preview-theme=d
5985
5995
  body[data-ds-dark-theme] &,&[data-preview-theme=dark]{background:var(--du-soft);--du-ink:var(--dsw-alias-label-primary,#f4f0e8);--du-muted:var(--dsw-alias-label-secondary,#bcb5c7)}
5986
5996
  `) + `
5987
5997
  .dgui-root[data-motion-demo]{display:flex;flex-direction:column;gap:16px}.dgui-root[data-motion-demo]>.dgui-segmented{flex:0 0 auto}
5988
- ${root} .dgui-card[data-surface=featured]{border-color:var(--du-accent)}${root} .dgui-card[data-surface=soft]{background:var(--du-soft)}${root} .dgui-card[data-tone]>.dgui-section-title{color:var(--du-tone)}
5989
- ${root} .dgui-steps li[data-step-status=active]{border-color:var(--du-accent)}${root} .dgui-step-dot .dgui-icon{color:inherit}
5990
- @media(max-width:600px){${root}{padding:16px}${root}[data-preset=reading],${root}[data-preset=minimal]{padding:0}${root} .dgui-card,${root} .dgui-metric{padding:16px}${root} .dgui-control{padding:10px}${root}[data-visual-style=editorial] .dgui-card{padding:12px 0}${root}[data-visual-style=expressive] .dgui-metric{padding:10px 0}${root} .dgui-row:has(>.dgui-metric){flex-wrap:wrap}}
5991
- @media(forced-colors:active){${root},${root} .dgui-card,${root} .dgui-metric,${root} .dgui-control,${root} .dgui-chart,${root} .motion-copy-block{background:Canvas;color:CanvasText;box-shadow:none;border-color:CanvasText}${root} .dgui-steps li{background:Canvas}}
5998
+ ${root2} .dgui-card[data-surface=featured]{border-color:var(--du-accent)}${root2} .dgui-card[data-surface=soft]{background:var(--du-soft)}${root2} .dgui-card[data-tone]>.dgui-section-title{color:var(--du-tone)}
5999
+ ${root2} .dgui-steps li[data-step-status=active]{border-color:var(--du-accent)}${root2} .dgui-step-dot .dgui-icon{color:inherit}
6000
+ @media(max-width:600px){${root2}{padding:16px}${root2}[data-preset=reading],${root2}[data-preset=minimal]{padding:0}${root2} .dgui-card,${root2} .dgui-metric{padding:16px}${root2} .dgui-control{padding:10px}${root2}[data-visual-style=editorial] .dgui-card{padding:12px 0}${root2}[data-visual-style=expressive] .dgui-metric{padding:10px 0}${root2} .dgui-row:has(>.dgui-metric){flex-wrap:wrap}}
6001
+ /* Unframed presets win after every style grammar, including dark preview rules. */
6002
+ ${root2}[data-preset=reading],${root2}[data-preset=minimal]{padding:0;border:0;background:transparent;box-shadow:none}
6003
+ body[data-ds-dark-theme] ${root2}[data-preset=reading],body[data-ds-dark-theme] ${root2}[data-preset=minimal],${root2}[data-preview-theme=dark][data-preset=reading],${root2}[data-preview-theme=dark][data-preset=minimal]{background:transparent}
6004
+ @media(forced-colors:active){${root2},${root2} .dgui-card,${root2} .dgui-metric,${root2} .dgui-control,${root2} .dgui-chart,${root2} .motion-copy-block{background:Canvas;color:CanvasText;box-shadow:none;border-color:CanvasText}${root2} .dgui-steps li{background:Canvas}}
5992
6005
  `;
5993
6006
 
5994
6007
  // src/style-previews.js
@@ -6201,7 +6214,7 @@ body[data-ds-dark-theme] .dgui-root,.dgui-root[data-preview-theme=dark]{--du-acc
6201
6214
  .dgui-panel{padding:16px;min-width:0;overflow:auto;height:100%;box-sizing:border-box}.dgui-panel>.dgui-root{margin-bottom:16px}.dgui-panel label{display:flex;align-items:center;flex-wrap:wrap;gap:10px}.dgui-panel select{max-width:100%;flex:1;min-width:120px;border:1px solid var(--du-line);border-radius:8px;padding:8px;background:var(--du-bg);color:var(--du-ink)}
6202
6215
  .dgui-history-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 0;border-bottom:1px solid var(--du-line)}.dgui-history-row>span{flex:1;min-width:140px}.dgui-version-preview{margin-top:14px;border:1px solid var(--du-line);border-radius:12px;padding:12px}.dgui-version-preview .dgui-root{margin-top:12px}
6203
6216
  [data-dgui-presentation]>.dgui-secondary{margin:8px 0}[data-dgui-marker]{min-width:0}
6204
- ` + SOFT_CSS + READING_CSS + NATIVE_STYLE_CSS + MOTION_CSS + EXPRESSIVE_CSS + STYLE_SETTINGS_CSS;
6217
+ ` + SOFT_CSS + READING_CSS + NATIVE_STYLE_CSS + MOTION_CSS + EXPRESSIVE_CSS + STYLE_SETTINGS_CSS + HOST_BOUNDARY_CSS;
6205
6218
 
6206
6219
  // src/motion-library.js
6207
6220
  var import_react20 = __toESM(require("react"), 1);
@@ -6574,6 +6587,19 @@ var VISUAL_CSS = `
6574
6587
  // src/media-history.js
6575
6588
  var uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/;
6576
6589
  var allowedFiles = /* @__PURE__ */ new Set(["video.mp4", "poster.jpg", "source.html", "project.zip"]);
6590
+ function inputAssetIds(value) {
6591
+ const clips = value.kind === "process" ? value.clips ?? value.recipe?.clips : null;
6592
+ if (!Array.isArray(clips)) return [];
6593
+ return [...new Set(clips.slice(0, 256).flatMap((clip) => {
6594
+ if (typeof clip?.path !== "string") return [];
6595
+ const parts = clip.path.replaceAll("\\", "/").split("/"), id2 = parts.at(-2);
6596
+ return parts.at(-1) === "video.mp4" && uuid.test(id2) ? [id2] : [];
6597
+ }))];
6598
+ }
6599
+ function visibleMediaRecords(records2) {
6600
+ const consumed = new Set(records2.flatMap((r) => r.inputAssetIds ?? []));
6601
+ return records2.filter((r) => !consumed.has(r.id));
6602
+ }
6577
6603
  function mediaRecord(message) {
6578
6604
  if (message?.isError) return null;
6579
6605
  for (const part of message?.content ?? []) {
@@ -6584,7 +6610,7 @@ function mediaRecord(message) {
6584
6610
  const files = (value.files ?? []).map((f) => typeof f === "string" ? f : f?.name).filter((f) => allowedFiles.has(f));
6585
6611
  if (!files.includes("video.mp4")) continue;
6586
6612
  const output = value.output ?? {}, positive = (n2) => typeof n2 === "number" && Number.isFinite(n2) && n2 > 0 ? n2 : null;
6587
- return { id: value.id, sessionId: value.sessionId, kind: value.kind, files: [...new Set(files)], documentId: uuid.test(value.documentId) ? value.documentId : null, documentRevision: Number.isSafeInteger(value.documentRevision) && value.documentRevision >= 0 ? value.documentRevision : null, nodeId: typeof value.nodeId === "string" ? value.nodeId : null, duration: positive(output.duration), width: positive(output.width), height: positive(output.height), hasAudio: Array.isArray(output.audio) ? output.audio.length > 0 : null };
6613
+ return { id: value.id, sessionId: value.sessionId, kind: value.kind, inputAssetIds: inputAssetIds(value), files: [...new Set(files)], documentId: uuid.test(value.documentId) ? value.documentId : null, documentRevision: Number.isSafeInteger(value.documentRevision) && value.documentRevision >= 0 ? value.documentRevision : null, nodeId: typeof value.nodeId === "string" ? value.nodeId : null, duration: positive(output.duration), width: positive(output.width), height: positive(output.height), hasAudio: Array.isArray(output.audio) ? output.audio.length > 0 : null };
6588
6614
  } catch {
6589
6615
  }
6590
6616
  }
@@ -6682,7 +6708,7 @@ function remember(v2) {
6682
6708
  }
6683
6709
  }
6684
6710
  function VideoPlayer({ record }) {
6685
- const video = (0, import_react27.useRef)(null), root2 = (0, import_react27.useRef)(null), [prefs] = (0, import_react27.useState)(preferences), [playing, setPlaying] = (0, import_react27.useState)(false), [time, setTime] = (0, import_react27.useState)(0), [duration, setDuration] = (0, import_react27.useState)(record.duration ?? 0), [ready, setReady] = (0, import_react27.useState)(false), [volume, setVolume] = (0, import_react27.useState)(prefs.volume), [muted, setMuted] = (0, import_react27.useState)(prefs.muted), [rate, setRate] = (0, import_react27.useState)(prefs.rate), [fullscreen, setFullscreen] = (0, import_react27.useState)(false), [error, setError] = (0, import_react27.useState)(""), [notice, setNotice] = (0, import_react27.useState)("");
6711
+ const video = (0, import_react27.useRef)(null), root3 = (0, import_react27.useRef)(null), [prefs] = (0, import_react27.useState)(preferences), [playing, setPlaying] = (0, import_react27.useState)(false), [time, setTime] = (0, import_react27.useState)(0), [duration, setDuration] = (0, import_react27.useState)(record.duration ?? 0), [ready, setReady] = (0, import_react27.useState)(false), [volume, setVolume] = (0, import_react27.useState)(prefs.volume), [muted, setMuted] = (0, import_react27.useState)(prefs.muted), [rate, setRate] = (0, import_react27.useState)(prefs.rate), [fullscreen, setFullscreen] = (0, import_react27.useState)(false), [error, setError] = (0, import_react27.useState)(""), [notice, setNotice] = (0, import_react27.useState)("");
6686
6712
  const mediaUrl = mediaAssetUrl(record, "video.mp4"), transport = useMediaSource(mediaUrl), src = transport.src, poster = mediaAssetUrl(record, "poster.jpg"), playbackError = error || transport.error;
6687
6713
  const lastVolume = (0, import_react27.useRef)(prefs.volume || 0.8);
6688
6714
  const drag = (0, import_react27.useRef)(null), pendingSeek = (0, import_react27.useRef)(null);
@@ -6709,7 +6735,7 @@ function VideoPlayer({ record }) {
6709
6735
  });
6710
6736
  }
6711
6737
  (0, import_react27.useEffect)(() => {
6712
- const v2 = video.current, container = root2.current;
6738
+ const v2 = video.current, container = root3.current;
6713
6739
  if (!v2) return;
6714
6740
  players.add(v2);
6715
6741
  v2.volume = prefs.volume;
@@ -6762,8 +6788,8 @@ function VideoPlayer({ record }) {
6762
6788
  async function full() {
6763
6789
  setNotice("");
6764
6790
  try {
6765
- if (document.fullscreenElement === root2.current) await document.exitFullscreen();
6766
- else if (root2.current.requestFullscreen) await root2.current.requestFullscreen();
6791
+ if (document.fullscreenElement === root3.current) await document.exitFullscreen();
6792
+ else if (root3.current.requestFullscreen) await root3.current.requestFullscreen();
6767
6793
  else if (video.current.webkitEnterFullscreen) video.current.webkitEnterFullscreen();
6768
6794
  else setNotice("\u6B64\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u5168\u5C4F\u3002");
6769
6795
  } catch {
@@ -6771,7 +6797,7 @@ function VideoPlayer({ record }) {
6771
6797
  }
6772
6798
  }
6773
6799
  function key2(e) {
6774
- if (e.target !== video.current && e.target !== root2.current) return;
6800
+ if (e.target !== video.current && e.target !== root3.current) return;
6775
6801
  const k2 = e.key.toLowerCase();
6776
6802
  if ([" ", "k", "m", "f", "arrowleft", "arrowright"].includes(k2)) {
6777
6803
  e.preventDefault();
@@ -6784,7 +6810,7 @@ function VideoPlayer({ record }) {
6784
6810
  const button = (name, icon, fn2, extra = {}) => h20("button", { type: "button", "aria-label": name, title: name, onClick: fn2, ...extra }, h20(Icon, { name: icon }));
6785
6811
  return h20(
6786
6812
  "section",
6787
- { ref: root2, className: "motion-video-player", "data-motion-video": record.id, "aria-label": "\u89C6\u9891\u64AD\u653E\u5668", onKeyDown: key2 },
6813
+ { ref: root3, className: "motion-video-player", "data-motion-video": record.id, "aria-label": "\u89C6\u9891\u64AD\u653E\u5668", onKeyDown: key2 },
6788
6814
  h20("video", {
6789
6815
  ref: video,
6790
6816
  src: src ?? void 0,
@@ -6865,7 +6891,7 @@ function VideoPlayer({ record }) {
6865
6891
  function MediaRecords({ records: records2, sessionId }) {
6866
6892
  const owner = (0, import_react27.useRef)({});
6867
6893
  (0, import_react27.useLayoutEffect)(() => presentMedia(owner.current, sessionId, records2), [records2, sessionId]);
6868
- return h20(import_react27.default.Fragment, null, records2.filter((r) => r.sessionId === sessionId).map((record) => h20(VideoPlayer, { key: record.id, record })));
6894
+ return h20(import_react27.default.Fragment, null, visibleMediaRecords(records2.filter((r) => r.sessionId === sessionId)).map((record) => h20(VideoPlayer, { key: record.id, record })));
6869
6895
  }
6870
6896
  var mediaRoots = /* @__PURE__ */ new WeakMap();
6871
6897
  var allMediaRoots = /* @__PURE__ */ new Set();
@@ -6881,12 +6907,15 @@ function disposeSurface(entry) {
6881
6907
  function disposeMediaSurfaces() {
6882
6908
  for (const entry of [...allMediaRoots]) disposeSurface(entry);
6883
6909
  }
6910
+ function mediaSeat(anchor, scroll) {
6911
+ let seat = anchor.closest("[data-chat-turn]") ?? anchor;
6912
+ for (let el = anchor.parentElement; el && el !== scroll; el = el.parentElement)
6913
+ if (el.getAttribute("hidden") === "until-found") seat = el;
6914
+ return seat;
6915
+ }
6884
6916
  function placeOutsideFold(entry) {
6885
6917
  if (!entry.anchor?.isConnected) return;
6886
- let folded = null;
6887
- for (let el = entry.anchor; el && el !== entry.scroll; el = el.parentElement) if (el.getAttribute("hidden") === "until-found") folded = el;
6888
- if (!folded) return;
6889
- const seat = folded.parentElement, parent = seat === entry.scroll ? entry.scroll : seat.parentElement, before = seat === entry.scroll ? null : seat.nextSibling;
6918
+ const seat = mediaSeat(entry.anchor, entry.scroll), parent = seat.parentElement, before = seat.nextSibling;
6890
6919
  if (before === entry.host || !before && parent.lastChild === entry.host) return;
6891
6920
  try {
6892
6921
  if (parent.moveBefore) {
@@ -6926,9 +6955,7 @@ function MediaNodeView({ node, sessionId }) {
6926
6955
  return;
6927
6956
  }
6928
6957
  if (!entry) {
6929
- let folded = null;
6930
- for (let el = anchor.current; el && el !== scroll; el = el.parentElement) if (el.getAttribute("hidden") === "until-found") folded = el;
6931
- const seat = folded?.parentElement ?? anchor.current.closest("[data-chat-turn]") ?? anchor.current;
6958
+ const seat = mediaSeat(anchor.current, scroll);
6932
6959
  const host = document.createElement("div");
6933
6960
  host.dataset.motionMediaTurn = String(state2.turn);
6934
6961
  if (seat === scroll) scroll.append(host);
package/lib/build-info.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by npm run build.
2
- export const BUILD_INFO = Object.freeze({"version":"0.8.3","id":"0.8.3+da9b5c9a709d","sourceHash":"da9b5c9a709de333a5c0348d1e9544ced197459e293f22dcfdc302b8dbb5f3d0"});
2
+ export const BUILD_INFO = Object.freeze({"version":"0.8.5","id":"0.8.5+64909b292cda","sourceHash":"64909b292cda52745fc063409ac48c8c665d9be60ca7212ca716748c0f8fe5fd"});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-motion-ui",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
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",