dsh-motion-ui 0.5.2 → 0.5.3
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 +7 -0
- package/README.md +38 -2
- package/VERIFICATION.md +6 -0
- package/client.js +110 -60
- package/index.js +8 -6
- package/lib/build-info.js +1 -1
- package/lib/canvas-design.js +3 -3
- package/lib/canvas-system.js +3 -0
- package/lib/catalog.js +1 -1
- package/lib/export-runtime-bundle.js +1 -1
- package/lib/freeform.js +1 -1
- package/lib/protocol.js +1 -0
- package/lib/quick-examples.js +8 -0
- package/lib/ui-intent.js +2 -2
- package/lib/ui-skill.js +2 -1
- package/package.json +1 -1
- package/skills/intelligent-ui/SKILL.md +8 -2
package/client.js
CHANGED
|
@@ -21678,10 +21678,10 @@ __export(client_exports, {
|
|
|
21678
21678
|
inject: () => inject
|
|
21679
21679
|
});
|
|
21680
21680
|
module.exports = __toCommonJS(client_exports);
|
|
21681
|
-
var
|
|
21681
|
+
var import_react12 = __toESM(require("react"), 1);
|
|
21682
21682
|
|
|
21683
21683
|
// src/view.js
|
|
21684
|
-
var
|
|
21684
|
+
var import_react10 = __toESM(require("react"), 1);
|
|
21685
21685
|
|
|
21686
21686
|
// lib/presentation.js
|
|
21687
21687
|
var UI_ICONS = Object.freeze(["sparkles", "sliders", "layers", "chart", "gauge", "cpu", "database", "info", "check", "warning", "arrow", "target"]);
|
|
@@ -21775,6 +21775,8 @@ var CANVAS_SYSTEM_CSS = `
|
|
|
21775
21775
|
html,body{margin:0;min-width:0;background:var(--canvas-bg,#fff);color:var(--canvas-ink,#182537);font:14px/1.6 system-ui,"Microsoft YaHei",sans-serif}*{box-sizing:border-box}svg{max-width:100%;height:auto}button,input,select,textarea{font:inherit;color:inherit}button,input,select,textarea{border:1px solid var(--canvas-line,#ced4df);border-radius:8px;background:var(--canvas-bg,#fff);padding:8px 12px;max-width:100%}button{cursor:pointer}button:disabled{cursor:default;opacity:.55}input[type=range]{padding:0;accent-color:var(--canvas-accent,#3e60d3)}input[type=checkbox],input[type=radio]{accent-color:var(--canvas-accent,#3e60d3)}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--canvas-accent,#3e60d3);outline-offset:3px}
|
|
21776
21776
|
.ui-stack{display:grid;gap:var(--canvas-gap)}.ui-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:var(--canvas-gap)}.ui-surface{padding:20px;border:1px solid var(--canvas-line,#ced4df);border-radius:var(--canvas-radius);background:var(--canvas-soft,#f3f5f8)}.ui-title{margin:0;font-size:clamp(20px,3vw,28px);line-height:1.3;letter-spacing:-.02em}.ui-caption{color:var(--canvas-muted,#526176);margin:8px 0;font-size:14px}.ui-value{font-size:clamp(24px,4vw,36px);font-variant-numeric:tabular-nums;line-height:1.2}.ui-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.ui-field{display:grid;gap:6px;min-width:0}.ui-legend{display:flex;gap:16px;flex-wrap:wrap;font-size:13px}.ui-status{min-height:1.6em}.ui-error{color:var(--canvas-negative)}.ui-primary{background:var(--canvas-accent,#3e60d3);color:var(--canvas-accent-ink,#fff);border-color:transparent}
|
|
21777
21777
|
svg .ui-node{fill:var(--canvas-soft,#f3f5f8);stroke:var(--canvas-line,#ced4df);stroke-width:1}svg .ui-edge{fill:none;stroke:var(--canvas-muted,#526176);stroke-width:1.5}svg .ui-label{fill:var(--canvas-ink,#182537);font:500 14px system-ui,sans-serif}svg .ui-note{fill:var(--canvas-muted,#526176);font:400 13px system-ui,sans-serif}svg .ui-leader{fill:none;stroke:var(--canvas-muted,#526176);stroke-dasharray:3 4}svg .ui-accent{fill:color-mix(in srgb,var(--canvas-accent,#3e60d3) 12%,var(--canvas-bg,#fff));stroke:var(--canvas-accent,#3e60d3)}
|
|
21778
|
+
.ui-flow{display:flex;align-items:center;justify-content:center;gap:16px;padding:24px 8px;flex-wrap:wrap}.ui-step{display:grid;gap:6px;min-width:130px;max-width:240px;padding:16px 20px;border:1px solid var(--canvas-line);border-radius:6px;background:var(--canvas-bg)}.ui-step strong{font-size:16px;font-weight:600}.ui-step span{font-size:14px;color:var(--canvas-muted)}.ui-arrow{color:var(--canvas-muted);font-size:22px}.ui-callout{border-inline-start:3px solid var(--canvas-accent);padding:8px 16px;color:var(--canvas-ink)}
|
|
21779
|
+
@media(max-width:480px){.ui-flow{flex-direction:column;padding:16px 8px;gap:12px}.ui-step{width:100%;max-width:none}.ui-arrow{transform:rotate(90deg)}}
|
|
21778
21780
|
@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}}`;
|
|
21779
21781
|
|
|
21780
21782
|
// lib/sandbox.js
|
|
@@ -22297,6 +22299,7 @@ function validateDocument(input) {
|
|
|
22297
22299
|
if (!own(doc.actions, n.action)) fail("unknown action");
|
|
22298
22300
|
break;
|
|
22299
22301
|
case "table": {
|
|
22302
|
+
if (Array.isArray(n.rows)) fail('table.rows must use {ref:"data.rows"}; move the array into document.data.rows, not inline in the table');
|
|
22300
22303
|
value(n.rows, "table rows");
|
|
22301
22304
|
if (!Array.isArray(n.columns) || !n.columns.length || n.columns.length > 16) fail("invalid table columns");
|
|
22302
22305
|
const cols = /* @__PURE__ */ new Set();
|
|
@@ -22495,10 +22498,10 @@ function useSavedState(record, sessionId, preview = false) {
|
|
|
22495
22498
|
const timer = setTimeout(async () => {
|
|
22496
22499
|
r.saving = true;
|
|
22497
22500
|
if (r.alive) setStatus("\u6B63\u5728\u4FDD\u5B58\u53C2\u6570\u2026");
|
|
22498
|
-
const
|
|
22501
|
+
const revision2 = r.revision;
|
|
22499
22502
|
try {
|
|
22500
|
-
const saved = checked(await request(STATE_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ documentId: record.documentId, sessionId, revision, state }) }));
|
|
22501
|
-
if (saved.revision !==
|
|
22503
|
+
const saved = checked(await request(STATE_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ documentId: record.documentId, sessionId, revision: revision2, state }) }));
|
|
22504
|
+
if (saved.revision !== revision2 + 1 || JSON.stringify(saved.state) !== signature) throw new Error("Unexpected save response");
|
|
22502
22505
|
r.revision = saved.revision;
|
|
22503
22506
|
r.saved = signature;
|
|
22504
22507
|
broadcastState(record.documentId, sessionId, saved, receive.current);
|
|
@@ -22732,7 +22735,7 @@ function CanvasPreview({ node }) {
|
|
|
22732
22735
|
var import_react4 = __toESM(require("react"), 1);
|
|
22733
22736
|
|
|
22734
22737
|
// lib/export-runtime-bundle.js
|
|
22735
|
-
var EXPORT_RUNTIME = '(() => {\n // lib/presentation.js\n var UI_ICONS = Object.freeze(["sparkles", "sliders", "layers", "chart", "gauge", "cpu", "database", "info", "check", "warning", "arrow", "target"]);\n var UI_TONES = Object.freeze(["neutral", "accent", "success", "warning", "danger"]);\n var UI_LAYOUTS = Object.freeze(["dashboard", "workbench", "comparison", "guide", "status", "report"]);\n var UI_ACCENTS = Object.freeze(["blue", "teal", "violet", "amber", "rose"]);\n var UI_SURFACES = Object.freeze(["outline", "soft", "featured"]);\n\n // lib/visual-audit.js\n function measureVisual(root, nodeId = "") {\n const issues = [], seen = /* @__PURE__ */ new Set(), coverage = { nodeId, elements: 0, truncated: false, contrastChecked: 0, contrastUncovered: 0 };\n const add = (code, value, el2) => {\n const id = el2?.closest("[data-ui-node]")?.getAttribute("data-ui-node") ?? nodeId;\n const k = id + ":" + code + ":" + Math.round(value ?? 0);\n if (!seen.has(k) && issues.length < 40) {\n seen.add(k);\n issues.push({ code, nodeId: id, ...value === void 0 ? {} : { value: Number(value.toFixed(2)) } });\n }\n };\n const color = (s) => {\n let m = s.match(/^rgba?\\(([^)]+)\\)/);\n if (m) {\n const a = m[1].split(/[\\s,\\/]+/).map(Number);\n return [a[0] / 255, a[1] / 255, a[2] / 255, a[3] ?? 1];\n }\n m = s.match(/^color\\(srgb ([^)]+)\\)/);\n if (m) {\n const a = m[1].split(/[\\s\\/]+/).map(Number);\n return [a[0], a[1], a[2], a[3] ?? 1];\n }\n return null;\n };\n const luminance = (c) => c.slice(0, 3).map((v) => v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4).reduce((s, v, i) => s + v * [0.2126, 0.7152, 0.0722][i], 0);\n const widthRoot = root.id === "canvas" ? document.documentElement : root;\n if (widthRoot.scrollWidth > widthRoot.clientWidth + 2) add("overflow", widthRoot.scrollWidth - widthRoot.clientWidth);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n let el;\n while (el = walker.nextNode()) {\n if (coverage.elements >= 2e3) {\n coverage.truncated = true;\n break;\n }\n coverage.elements++;\n if (el.matches("[data-canvas-phase=error]")) add("runtimeError", void 0, el);\n const css = getComputedStyle(el), box = el.getBoundingClientRect();\n if (!box.width || !box.height || css.visibility === "hidden" || css.display === "none") continue;\n const text2 = [...el.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim());\n if (text2) {\n let size = parseFloat(css.fontSize);\n if (el instanceof SVGElement && el.getScreenCTM) {\n const m = el.getScreenCTM();\n if (m) size *= Math.hypot(m.c, m.d);\n }\n if (size < 11.9) add("smallText", size, el);\n const fg = color(css.color);\n let ancestor = el, bg = null, depth = 0;\n while (ancestor && depth++ < 64) {\n const style = getComputedStyle(ancestor);\n if (style.backgroundImage !== "none") break;\n const c = color(style.backgroundColor);\n if (c?.[3] === 1) {\n bg = c;\n break;\n }\n if (c?.[3] > 0) break;\n ancestor = ancestor.parentElement;\n }\n if (!(el instanceof SVGElement) && fg?.[3] === 1 && bg) {\n coverage.contrastChecked++;\n const a = luminance(fg), b = luminance(bg), ratio = (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);\n if (ratio < (size >= 24 || size >= 18.66 && parseFloat(css.fontWeight) >= 700 ? 3 : 4.5)) add("contrast", ratio, el);\n } else coverage.contrastUncovered++;\n }\n 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")) add("missingLabel", void 0, el);\n }\n Object.defineProperty(issues, "coverage", { value: coverage });\n return issues;\n }\n\n // lib/format-number.js\n function formatNumber(value, decimals = 2) {\n if (typeof value !== "number" || !Number.isFinite(value)) throw new Error("DSH UI: formatNumber requires a finite number");\n if (!Number.isInteger(decimals) || decimals < 0 || decimals > 6) throw new Error("DSH UI: formatNumber decimals must be an integer from 0 to 6");\n return value.toLocaleString("en-US", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });\n }\n\n // lib/canvas-system.js\n var CANVAS_SYSTEM_CSS = `\n:root{--canvas-radius:12px;--canvas-gap:16px;--canvas-positive:#167456;--canvas-negative:#b13b3b;--canvas-warning:#875b15}\n:root[data-canvas-scheme=dark]{--canvas-positive:#78d9b4;--canvas-negative:#ffaaaa;--canvas-warning:#edc87e}\nhtml,body{margin:0;min-width:0;background:var(--canvas-bg,#fff);color:var(--canvas-ink,#182537);font:14px/1.6 system-ui,"Microsoft YaHei",sans-serif}*{box-sizing:border-box}svg{max-width:100%;height:auto}button,input,select,textarea{font:inherit;color:inherit}button,input,select,textarea{border:1px solid var(--canvas-line,#ced4df);border-radius:8px;background:var(--canvas-bg,#fff);padding:8px 12px;max-width:100%}button{cursor:pointer}button:disabled{cursor:default;opacity:.55}input[type=range]{padding:0;accent-color:var(--canvas-accent,#3e60d3)}input[type=checkbox],input[type=radio]{accent-color:var(--canvas-accent,#3e60d3)}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--canvas-accent,#3e60d3);outline-offset:3px}\n.ui-stack{display:grid;gap:var(--canvas-gap)}.ui-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:var(--canvas-gap)}.ui-surface{padding:20px;border:1px solid var(--canvas-line,#ced4df);border-radius:var(--canvas-radius);background:var(--canvas-soft,#f3f5f8)}.ui-title{margin:0;font-size:clamp(20px,3vw,28px);line-height:1.3;letter-spacing:-.02em}.ui-caption{color:var(--canvas-muted,#526176);margin:8px 0;font-size:14px}.ui-value{font-size:clamp(24px,4vw,36px);font-variant-numeric:tabular-nums;line-height:1.2}.ui-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.ui-field{display:grid;gap:6px;min-width:0}.ui-legend{display:flex;gap:16px;flex-wrap:wrap;font-size:13px}.ui-status{min-height:1.6em}.ui-error{color:var(--canvas-negative)}.ui-primary{background:var(--canvas-accent,#3e60d3);color:var(--canvas-accent-ink,#fff);border-color:transparent}\nsvg .ui-node{fill:var(--canvas-soft,#f3f5f8);stroke:var(--canvas-line,#ced4df);stroke-width:1}svg .ui-edge{fill:none;stroke:var(--canvas-muted,#526176);stroke-width:1.5}svg .ui-label{fill:var(--canvas-ink,#182537);font:500 14px system-ui,sans-serif}svg .ui-note{fill:var(--canvas-muted,#526176);font:400 13px system-ui,sans-serif}svg .ui-leader{fill:none;stroke:var(--canvas-muted,#526176);stroke-dasharray:3 4}svg .ui-accent{fill:color-mix(in srgb,var(--canvas-accent,#3e60d3) 12%,var(--canvas-bg,#fff));stroke:var(--canvas-accent,#3e60d3)}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}}`;\n\n // lib/sandbox.js\n var SANDBOX_LIMITS = Object.freeze({ source: 49152, height: 1e3, message: 16384, rate: 30 });\n function canvasSnapshot(node2, state2, data, derived, disabled, theme) {\n const pick = (keys, values) => Object.fromEntries((keys ?? []).map((k) => [k, values[k]]));\n return { state: pick(node2.stateKeys, state2), data: pick(node2.dataKeys, data), derived: pick(node2.derivedKeys, derived), disabled, theme };\n }\n function canvasPatch(message, node2) {\n if (!message || typeof message !== "object" || Array.isArray(message)) throw new Error("Invalid canvas patch");\n const keys = Object.keys(message);\n if (!keys.length || keys.length > 64 || keys.some((k) => !(node2.stateKeys ?? []).includes(k))) throw new Error("Canvas state key is not declared");\n if (JSON.stringify(message).length > SANDBOX_LIMITS.message) throw new Error("Canvas patch is too large");\n return message;\n }\n function sandboxDocument(node2, channel2, nonce2) {\n const json = (value) => JSON.stringify(value).replace(/</g, "\\\\u003c").replace(/\\u2028/g, "\\\\u2028").replace(/\\u2029/g, "\\\\u2029");\n return `<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src \'none\'; script-src \'nonce-${nonce2}\'; style-src \'unsafe-inline\'; img-src data:; font-src \'none\'; connect-src \'none\'; media-src \'none\'; object-src \'none\'; frame-src \'none\'; worker-src \'none\'; base-uri \'none\'; form-action \'none\'"><meta name="viewport" content="width=device-width,initial-scale=1"><style>${CANVAS_SYSTEM_CSS}</style></head><body><main id="canvas"></main><script nonce="${nonce2}">(()=>{\n const channel=${json(channel2)}, source=${json({ html: node2.html, css: node2.css ?? "", script: node2.script ?? "" })};\n let snapshot={state:{},data:{},derived:{},disabled:true,theme:{}}, started=false;\n const listeners=new Set(), send=(type,payload)=>parent.postMessage({dshCanvas:1,channel,type,payload},\'*\');\n const report=e=>send(\'error\',String(e?.message??e).slice(0,1000));\n const audit=${measureVisual.toString()};let auditTimer;const scheduleAudit=()=>{clearTimeout(auditTimer);auditTimer=setTimeout(()=>{const issues=audit(document.getElementById(\'canvas\'),${json(node2.id ?? "")});send(\'audit\',{issues,coverage:issues.coverage})},400)};\n addEventListener(\'resize\',scheduleAudit);\n addEventListener(\'error\',e=>report(e.error??e.message));addEventListener(\'unhandledrejection\',e=>report(e.reason));\n const animations=new Set(), motion=matchMedia(\'(prefers-reduced-motion: reduce)\');\n let raf=0,last=0,time=0;\n const visible=()=>snapshot.visible!==false&&!document.hidden;\n const canAnimate=()=>visible()&&!snapshot.disabled&&!motion.matches;\n const draw=delta=>{for(const fn of animations)try{fn({time,delta,reducedMotion:motion.matches,visible:visible()})}catch(e){animations.delete(fn);report(e)}};\n const tick=t=>{raf=0;if(!canAnimate()||!animations.size){last=0;draw(0);return}const delta=last?Math.min(.05,(t-last)/1000):0;last=t;time+=delta;draw(delta);if(animations.size)raf=requestAnimationFrame(tick)};\n const refresh=()=>{draw(0);if(canAnimate()&&animations.size){if(!raf){last=0;raf=requestAnimationFrame(tick)}}else{cancelAnimationFrame(raf);raf=0;last=0}};\n motion.addEventListener(\'change\',refresh);document.addEventListener(\'visibilitychange\',refresh);\n const formatNumber=${formatNumber.toString()};\n const api={formatNumber,get snapshot(){return structuredClone(snapshot)},subscribe(fn){if(typeof fn!==\'function\')throw Error(\'Listener required\');listeners.add(fn);fn(api.snapshot);return()=>listeners.delete(fn)},setState(patch){if(!snapshot.disabled)send(\'patch\',patch)},animate(fn){if(typeof fn!==\'function\')throw Error(\'Animation callback required\');animations.add(fn);refresh();return()=>{animations.delete(fn);if(!animations.size){cancelAnimationFrame(raf);raf=0;last=0}}}};\n Object.defineProperty(window,\'DSHUI\',{value:Object.freeze(api),writable:false});\n for(const type of [\'click\',\'input\',\'change\',\'keydown\',\'pointerdown\'])document.addEventListener(type,e=>{if(snapshot.disabled){e.preventDefault();e.stopImmediatePropagation()}},true);\n addEventListener(\'message\',event=>{\n const m=event.data;if(event.source!==parent||m?.dshCanvas!==1||m.channel!==channel||m.type!==\'snapshot\')return;\n snapshot=m.payload;for(const [k,v] of Object.entries(snapshot.theme??{}))document.documentElement.style.setProperty(\'--canvas-\'+k,v);\n document.documentElement.style.colorScheme=snapshot.theme?.scheme??\'light\';document.documentElement.dataset.canvasScheme=snapshot.theme?.scheme??\'light\';\n const wasStarted=started;\n if(!started){started=true;document.getElementById(\'canvas\').innerHTML=source.html;const style=document.createElement(\'style\');style.textContent=source.css;document.head.append(style);const script=document.createElement(\'script\');script.nonce=${json(nonce2)};script.textContent=source.script;document.body.append(script)}\n if(wasStarted)for(const fn of listeners)try{fn(api.snapshot)}catch(e){report(e)}\n refresh();if(!wasStarted)send(\'mounted\');scheduleAudit();\n });\n let resizePending=false;new ResizeObserver(()=>{if(!started||resizePending)return;resizePending=true;requestAnimationFrame(()=>{resizePending=false;send(\'height\',Math.ceil(document.getElementById(\'canvas\').getBoundingClientRect().height))})}).observe(document.getElementById(\'canvas\'));\n send(\'ready\');\n})();<\\/script></body></html>`;\n }\n\n // lib/components.js\n var define = (fields2, purpose, presentation = "") => Object.freeze({ fields: Object.freeze(fields2), purpose, presentation });\n var COMPONENTS = Object.freeze({\n heading: define(["text"], "Section heading", "tone"),\n text: define(["text"], "Readable explanation or bound text", "tone"),\n row: define(["children"], "Wrapping parallel content"),\n column: define(["children"], "Ordered vertical content"),\n card: define(["title", "children", "surface"], "Meaningful grouped section", "tone"),\n details: define(["title", "children", "open"], "Collapsible supporting content", "tone"),\n badge: define(["text"], "Short explicit status label", "tone"),\n progress: define(["label", "value", "max", "unit"], "Declared numeric progress snapshot", "tone"),\n steps: define(["items"], "Ordered declared steps"),\n metric: define(["label", "value", "unit", "decimals"], "Numeric result with units", "tone"),\n input: define(["label", "bind"], "String or numeric state input"),\n slider: define(["label", "bind"], "Numeric state control"),\n select: define(["label", "bind"], "Selection from declared state options"),\n toggle: define(["label", "bind"], "Boolean state control"),\n button: define(["text", "action"], "Declared Agent follow-up", "icon"),\n table: define(["rows", "columns"], "Exact supplied or derived values"),\n chart: define(["kind", "rows", "labelKey", "valueKey", "label", "unit", "xScale"], "Bar/line comparison with accessible data"),\n sandbox: define(["title", "html", "css", "script", "fallback", "height", "stateKeys", "dataKeys", "derivedKeys"], "Freeform isolated HTML/SVG/CSS/JavaScript visualization")\n });\n function componentFields(type) {\n const c = Object.hasOwn(COMPONENTS, type) ? COMPONENTS[type] : void 0;\n return c && ["id", "type", ...c.fields, ...c.presentation ? ["icon"] : [], ...c.presentation === "tone" ? ["tone"] : []];\n }\n\n // lib/author-expression.js\n function compileExpression(source) {\n const fail2 = () => {\n throw new Error("DSH UI: invalid formula; use state.x, data.x, derived.x, item.x, numbers, strings, + - * /, comparisons, ?: and min/max/round/abs");\n };\n if (typeof source !== "string" || !source.trim() || source.length > 2048) fail2();\n const tokens = [];\n let offset = 0;\n const token2 = /\\s*(?:(\\d+(?:\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+)?|("(?:[^"\\\\]|\\\\.)*")|([a-zA-Z][a-zA-Z0-9_]*(?:\\.[a-zA-Z][a-zA-Z0-9_]*)?)|(===|!==|<=|>=|==|!=|[+*/()?,:<>-]))/y;\n while (offset < source.length) {\n if (!source.slice(offset).trim()) break;\n token2.lastIndex = offset;\n const m = token2.exec(source);\n if (!m) fail2();\n offset = token2.lastIndex;\n tokens.push(m[1] ? { number: Number(m[1] + (m[2] ?? "")) } : m[3] ? { string: JSON.parse(m[3]) } : m[4] ?? m[5]);\n if (tokens.length > 256) fail2();\n }\n let pos = 0;\n const operators = { "==": ["eq", 1], "===": ["eq", 1], "!=": ["neq", 1], "!==": ["neq", 1], "<": ["lt", 2], "<=": ["lte", 2], ">": ["gt", 2], ">=": ["gte", 2], "+": ["add", 3], "-": ["sub", 3], "*": ["mul", 4], "/": ["div", 4] };\n const take = (t) => {\n if (tokens[pos++] !== t) fail2();\n };\n function parse(min = 0, depth = 0) {\n if (depth > 12) fail2();\n const t = tokens[pos++];\n let out;\n if (t && typeof t === "object") out = { const: Object.hasOwn(t, "number") ? t.number : t.string };\n else if (t === "true" || t === "false") out = { const: t === "true" };\n else if (t === "(") {\n out = parse(0, depth + 1);\n take(")");\n } else if (t === "-" || t === "+") {\n const arg = parse(5, depth + 1);\n out = t === "-" ? { op: "sub", args: [{ const: 0 }, arg] } : arg;\n } else if (typeof t === "string" && /^(state|data|derived|item)\\.[a-zA-Z][a-zA-Z0-9_]*$/.test(t)) out = { ref: t };\n else if (["min", "max", "round", "abs"].includes(t)) {\n take("(");\n const args = [parse(0, depth + 1)];\n while (tokens[pos] === ",") {\n pos++;\n args.push(parse(0, depth + 1));\n if (args.length > 8) fail2();\n }\n take(")");\n out = { op: t, args };\n } else fail2();\n while (typeof tokens[pos] === "string" && Object.hasOwn(operators, tokens[pos]) && operators[tokens[pos]][1] >= min) {\n const [op, precedence] = operators[tokens[pos++]];\n out = { op, args: [out, parse(precedence + 1, depth + 1)] };\n }\n if (min === 0 && tokens[pos] === "?") {\n pos++;\n const yes = parse(0, depth + 1);\n take(":");\n out = { op: "if", args: [out, yes, parse(0, depth + 1)] };\n }\n return out;\n }\n const result = parse();\n if (pos !== tokens.length) fail2();\n return result;\n }\n function expandExpressions(doc2) {\n const expression = (e) => {\n if (!e || typeof e !== "object" || Array.isArray(e)) return e;\n if (Object.hasOwn(e, "expr")) {\n if (Object.keys(e).length !== 1) throw new Error("DSH UI: expr must be the only formula field");\n return compileExpression(e.expr);\n }\n if (Array.isArray(e.args)) e.args = e.args.map(expression);\n if (e.map?.fields) for (const k of Object.keys(e.map.fields)) e.map.fields[k] = expression(e.map.fields[k]);\n return e;\n };\n if (doc2.derived && typeof doc2.derived === "object") for (const k of Object.keys(doc2.derived)) doc2.derived[k] = expression(doc2.derived[k]);\n const walk = (nodes) => {\n if (!Array.isArray(nodes)) return;\n for (const node2 of nodes) {\n if (!node2 || typeof node2 !== "object") continue;\n for (const field of ["text", "value", "max", "rows"]) if (Object.hasOwn(node2, field)) node2[field] = expression(node2[field]);\n walk(node2.children);\n }\n };\n walk(doc2.nodes);\n return doc2;\n }\n\n // lib/protocol.js\n var LIMITS = Object.freeze({ bytes: 131072, nodes: 160, depth: 12, rows: 500, expressions: 128, steps: 4096 });\n var own = (o, k) => Object.hasOwn(o, k);\n var fail = (message) => {\n throw new Error(`DSH UI: ${message}`);\n };\n var object = (x) => x !== null && typeof x === "object" && !Array.isArray(x);\n var key = (x) => typeof x === "string" && /^[a-zA-Z][a-zA-Z0-9_]{0,63}$/.test(x) && !["constructor", "prototype", "__proto__"].includes(x);\n var finite = (x) => typeof x === "number" && Number.isFinite(x);\n var scalar = (x) => typeof x === "string" || typeof x === "boolean" || finite(x);\n var text = (x, at) => {\n if (typeof x !== "string" || x.length > 8192) fail(`${at} must be a string of at most 8192 characters`);\n };\n function fields(value, allowed, at) {\n if (!object(value)) fail(`${at} must be an object`);\n for (const k of Object.keys(value)) if (!allowed.includes(k)) {\n const hint = at === "document" ? " Controls and visual components belong in nodes as registered typed nodes, not extra document fields." : "";\n fail(`unknown field ${at}.${k}; allowed fields at ${at}: ${allowed.join(", ")}.${hint}`);\n }\n }\n function safeJson(value, depth = 0, source = false) {\n if (depth > 48) fail("JSON nesting is too deep");\n if (value === null || typeof value === "boolean") return;\n if (typeof value === "string") {\n if (source) {\n if (value.length > SANDBOX_LIMITS.source) fail("canvas source exceeds 48 Ki characters");\n return;\n }\n return text(value, "value");\n }\n if (typeof value === "number") {\n if (!finite(value)) fail("numbers must be finite");\n return;\n }\n if (typeof value !== "object") fail("only JSON values are accepted");\n if (Array.isArray(value) && value.length > LIMITS.rows) fail("too many array items");\n for (const k of Object.keys(value)) {\n if (["__proto__", "prototype", "constructor"].includes(k)) fail("unsafe property name");\n safeJson(value[k], depth + 1, value.type === "sandbox" && ["html", "css", "script"].includes(k));\n }\n }\n function initialState(doc2) {\n return Object.fromEntries(Object.entries(doc2.state).map(([k, spec]) => [k, spec.default]));\n }\n function validateState(doc2, state2) {\n if (!object(state2) || Object.keys(state2).length !== Object.keys(doc2.state).length) fail("state keys do not match the document");\n const result = {};\n for (const [k, s] of Object.entries(doc2.state)) {\n if (!own(state2, k)) fail(`missing state.${k}`);\n const v = state2[k];\n if (s.type === "number") {\n if (!finite(v) || v < s.min || v > s.max) fail(`state.${k} is outside its numeric range`);\n const offset = (v - s.min) / s.step;\n if (Math.abs(offset - Math.round(offset)) > 1e-6) fail(`state.${k} does not match its step`);\n } else if (s.type === "string") text(v, `state.${k}`);\n else if (typeof v !== "boolean") fail(`state.${k} must be boolean`);\n if (s.options && !s.options.includes(v)) fail(`invalid option for state.${k}`);\n result[k] = v;\n }\n return result;\n }\n var ARITY = { add: [1, 8], sub: [2, 2], mul: [1, 8], div: [2, 2], min: [1, 8], max: [1, 8], round: [1, 1], abs: [1, 1], eq: [2, 2], neq: [2, 2], lt: [2, 2], lte: [2, 2], gt: [2, 2], gte: [2, 2], if: [3, 3] };\n function reference(doc2, ref) {\n if (typeof ref !== "string" || !/^(state|data|derived)\\.[a-zA-Z][a-zA-Z0-9_]{0,63}$/.test(ref)) fail("invalid reference");\n const [group, k] = ref.split(".");\n if (!own(doc2[group], k)) fail(`unknown reference ${ref}`);\n return [group, k];\n }\n function evaluate(doc2, state2) {\n const derived = {}, active = /* @__PURE__ */ new Set();\n let steps = 0, generatedRows = 0;\n const budget = () => {\n if (++steps > LIMITS.steps) fail("calculation budget exceeded");\n };\n const resolve = (ref) => {\n budget();\n const [group, k] = reference(doc2, ref);\n if (group !== "derived") return (group === "state" ? state2 : doc2.data)[k];\n if (own(derived, k)) return derived[k];\n if (active.has(k)) fail(`cyclic calculation ${k}`);\n active.add(k);\n const result2 = expression(doc2.derived[k]);\n active.delete(k);\n derived[k] = result2;\n return result2;\n };\n const expression = (e, item) => {\n budget();\n if (own(e, "map")) {\n const rows = resolve(e.map.source);\n if ((generatedRows += rows.length) > LIMITS.rows) fail("generated row limit exceeded");\n return rows.map((row) => Object.fromEntries(Object.entries(e.map.fields).map(([k, value]) => {\n const out2 = expression(value, row);\n if (!scalar(out2)) fail("generated cells must be scalar");\n return [k, out2];\n })));\n }\n if (own(e, "const")) return e.const;\n if (own(e, "ref")) {\n if (e.ref.startsWith("item.")) {\n const k = e.ref.slice(5);\n if (!item || !own(item, k) || !scalar(item[k])) fail("invalid item reference");\n return item[k];\n }\n return resolve(e.ref);\n }\n if (e.op === "if") {\n const condition = expression(e.args[0], item);\n if (typeof condition !== "boolean") fail("if condition must be boolean");\n return expression(e.args[condition ? 1 : 2], item);\n }\n const a = e.args.map((arg) => expression(arg, item));\n if (e.op === "eq") return a[0] === a[1];\n if (e.op === "neq") return a[0] !== a[1];\n if (!a.every(finite)) fail(`operator ${e.op} requires finite numbers`);\n let out;\n switch (e.op) {\n case "add":\n out = a.reduce((x, y) => x + y, 0);\n break;\n case "sub":\n out = a[0] - a[1];\n break;\n case "mul":\n out = a.reduce((x, y) => x * y, 1);\n break;\n case "div":\n if (a[1] === 0) fail("division by zero");\n out = a[0] / a[1];\n break;\n case "min":\n out = Math.min(...a);\n break;\n case "max":\n out = Math.max(...a);\n break;\n case "round":\n out = Math.round(a[0]);\n break;\n case "abs":\n out = Math.abs(a[0]);\n break;\n case "lt":\n return a[0] < a[1];\n case "lte":\n return a[0] <= a[1];\n case "gt":\n return a[0] > a[1];\n case "gte":\n return a[0] >= a[1];\n default:\n fail(`unsupported operator ${e.op}`);\n }\n if (!finite(out)) fail("non-finite calculation result");\n return out;\n };\n for (const k of Object.keys(doc2.derived)) resolve(`derived.${k}`);\n if (new TextEncoder().encode(JSON.stringify(derived)).length > LIMITS.bytes) fail("derived output exceeds 128 KiB");\n const result = { derived, resolveValue: (value) => {\n if (!object(value)) return value;\n return expression(value);\n } };\n checkViewValues(doc2, result);\n return result;\n }\n function checkViewValues(doc2, evaluated) {\n function walk(list) {\n for (const n of list) {\n if (n.type === "table" || n.type === "chart") {\n const rows = evaluated.resolveValue(n.rows);\n if (!Array.isArray(rows) || rows.length > LIMITS.rows || !rows.every(object)) fail("table/chart rows must reference an array of objects");\n if (n.type === "chart" && rows.some((r) => !own(r, n.labelKey) || !scalar(r[n.labelKey]) || !own(r, n.valueKey) || !finite(r[n.valueKey]))) fail("chart requires labels and finite numeric values");\n if (n.type === "chart" && n.xScale === "linear" && rows.some((r) => !finite(r[n.labelKey]))) fail("linear chart requires numeric x values");\n }\n if (n.type === "metric" && !finite(evaluated.resolveValue(n.value))) fail("metric requires a numeric value");\n if (n.type === "progress") {\n const v = evaluated.resolveValue(n.value), max = evaluated.resolveValue(n.max);\n if (!finite(v) || !finite(max) || max <= 0 || v < 0 || v > max) fail("progress requires 0 <= value <= positive max");\n }\n if (n.children) walk(n.children);\n }\n }\n walk(doc2.nodes);\n }\n function validateDocument(input) {\n safeJson(input);\n const json = JSON.stringify(input);\n if (new TextEncoder().encode(json).length > LIMITS.bytes) fail("document exceeds 128 KiB");\n const doc2 = expandExpressions(JSON.parse(json));\n fields(doc2, ["version", "title", "fallback", "state", "data", "derived", "nodes", "actions", "presentation"], "document");\n if (doc2.presentation !== void 0) {\n fields(doc2.presentation, ["layout", "accent", "density"], "presentation");\n for (const [k, allowed] of Object.entries({ layout: UI_LAYOUTS, accent: UI_ACCENTS, density: ["comfortable", "compact"] }))\n if (doc2.presentation[k] !== void 0 && !allowed.includes(doc2.presentation[k])) fail(`unknown presentation ${k}`);\n }\n if (doc2.version !== 1) fail("unsupported protocol version");\n text(doc2.title, "title");\n text(doc2.fallback, "fallback");\n if (!doc2.title.trim() || !doc2.fallback.trim()) fail("title and fallback cannot be empty");\n for (const group of ["state", "data", "derived", "actions"]) {\n doc2[group] ??= {};\n if (!object(doc2[group]) || Object.keys(doc2[group]).length > 64) fail(`invalid ${group}`);\n for (const k of Object.keys(doc2[group])) if (!key(k)) fail(`invalid key in ${group}`);\n }\n for (const [k, s] of Object.entries(doc2.state)) {\n fields(s, ["type", "default", "min", "max", "step", "options"], `state.${k}`);\n if (!["number", "string", "boolean"].includes(s.type)) fail(`unsupported state type ${k}`);\n if (s.type === "number") {\n if (![s.min, s.max, s.step].every(finite) || s.min >= s.max || s.step <= 0) fail(`invalid numeric limits for ${k}`);\n if ((s.max - s.min) / s.step > 1e9) fail(`numeric step too small for ${k}`);\n } else if (["min", "max", "step"].some((p) => own(s, p))) fail(`numeric limits on non-number ${k}`);\n if (s.options && (!Array.isArray(s.options) || !s.options.length || s.options.length > 64 || !s.options.every(scalar))) fail(`invalid options for ${k}`);\n }\n const defaults = validateState(doc2, initialState(doc2));\n let expressions = 0;\n for (const [k, spec] of Object.entries(doc2.state))\n for (const option of spec.options ?? []) validateState(doc2, { ...defaults, [k]: option });\n function validateExpression(e, depth = 0, itemRows) {\n if (++expressions > LIMITS.expressions || depth > LIMITS.depth) fail("expression limit exceeded");\n if (!object(e)) fail("expression must be an object");\n if (own(e, "map")) {\n if (depth !== 0) fail("map is only allowed at the root of a derived value");\n fields(e, ["map"], "expression");\n fields(e.map, ["source", "fields"], "map");\n if (typeof e.map.source !== "string" || !e.map.source.startsWith("data.")) fail(\'map source must reference static data using a string such as "data.samples"; put object rows in document.data.samples, not inline in map.source\');\n const [, k] = reference(doc2, e.map.source), rows = doc2.data[k];\n if (!Array.isArray(rows) || rows.length > LIMITS.rows || !rows.every(object)) fail("map source must be object rows");\n if (!object(e.map.fields) || !Object.keys(e.map.fields).length || Object.keys(e.map.fields).length > 16) fail("map requires 1..16 fields");\n for (const [k2, value2] of Object.entries(e.map.fields)) {\n if (!key(k2)) fail(`invalid map field ${JSON.stringify(k2).slice(0, 100)}; use an ASCII identifier matching [a-zA-Z][a-zA-Z0-9_]{0,63}, such as powerW. Put Chinese display text in column/chart labels, not internal field keys`);\n validateExpression(value2, depth + 1, rows);\n }\n return;\n }\n if (own(e, "const")) {\n fields(e, ["const"], "expression");\n if (!scalar(e.const)) fail("constant must be scalar");\n return;\n }\n if (own(e, "ref")) {\n fields(e, ["ref"], "expression");\n if (typeof e.ref === "string" && e.ref.startsWith("item.")) {\n const k = e.ref.slice(5);\n if (!itemRows || !key(k) || itemRows.some((row) => !own(row, k) || !scalar(row[k]))) fail("invalid or unscoped item reference");\n } else reference(doc2, e.ref);\n return;\n }\n fields(e, ["op", "args"], "expression");\n const arity = ARITY[e.op];\n if (!arity || !Array.isArray(e.args) || e.args.length < arity[0] || e.args.length > arity[1]) fail("invalid operator or argument count");\n e.args.forEach((a) => validateExpression(a, depth + 1, itemRows));\n }\n Object.values(doc2.derived).forEach((e) => validateExpression(e));\n const visited = /* @__PURE__ */ new Set(), active = /* @__PURE__ */ new Set();\n function visit(k) {\n if (active.has(k)) fail(`cyclic calculation ${k}`);\n if (visited.has(k)) return;\n active.add(k);\n function walk(e) {\n if (e.ref?.startsWith("derived.")) visit(e.ref.split(".")[1]);\n for (const a of e.args ?? []) walk(a);\n for (const value2 of Object.values(e.map?.fields ?? {})) walk(value2);\n }\n walk(doc2.derived[k]);\n active.delete(k);\n visited.add(k);\n }\n Object.keys(doc2.derived).forEach(visit);\n for (const [k, a] of Object.entries(doc2.actions)) {\n fields(a, ["type", "intent", "stateKeys"], `actions.${k}`);\n if (a.type !== "agent.followup") fail("only agent.followup actions are supported");\n text(a.intent, "action intent");\n if (!a.intent.trim() || !Array.isArray(a.stateKeys) || a.stateKeys.length > 64 || new Set(a.stateKeys).size !== a.stateKeys.length || a.stateKeys.some((s) => !own(doc2.state, s))) fail("invalid action stateKeys");\n }\n const ids = /* @__PURE__ */ new Set();\n let count2 = 0;\n const value = (v, at) => {\n if (object(v)) {\n if (own(v, "map")) fail("map belongs in derived");\n validateExpression(v);\n } else if (!scalar(v)) fail(`invalid value at ${at}`);\n };\n function nodes(list, depth = 0) {\n if (!Array.isArray(list) || depth > LIMITS.depth) fail("invalid node children or depth");\n for (const n of list) {\n if (++count2 > LIMITS.nodes) fail("too many nodes");\n if (!object(n) || !key(n.id) || ids.has(n.id)) fail("node ids must be unique identifiers");\n ids.add(n.id);\n if (!Object.hasOwn(COMPONENTS, n.type)) fail("unsupported component " + n.type);\n switch (n.type) {\n case "sandbox": {\n text(n.title, "canvas title");\n text(n.fallback, "canvas fallback");\n if (!n.title.trim() || !n.fallback.trim() || typeof n.html !== "string" || !n.html.trim()) fail("canvas requires title, HTML and fallback");\n for (const field of ["html", "css", "script"]) if (n[field] !== void 0 && (typeof n[field] !== "string" || n[field].length > SANDBOX_LIMITS.source)) fail("canvas source exceeds 48 Ki characters");\n if (n.height !== void 0 && (!Number.isInteger(n.height) || n.height < 160 || n.height > SANDBOX_LIMITS.height)) fail("canvas height must be 160..1000");\n for (const [field, group] of [["stateKeys", "state"], ["dataKeys", "data"], ["derivedKeys", "derived"]]) if (n[field] !== void 0 && (!Array.isArray(n[field]) || n[field].length > 64 || new Set(n[field]).size !== n[field].length || n[field].some((k) => !own(doc2[group], k)))) fail("invalid canvas " + field);\n break;\n }\n case "row":\n case "column":\n break;\n case "card":\n if (n.title !== void 0) text(n.title, "card title");\n if (n.surface !== void 0 && !UI_SURFACES.includes(n.surface)) fail("unknown card surface");\n break;\n case "details":\n text(n.title, "details title");\n if (n.open !== void 0 && typeof n.open !== "boolean") fail("details open must be boolean");\n break;\n case "badge":\n value(n.text, "badge text");\n break;\n case "progress":\n text(n.label, "progress label");\n value(n.value, "progress value");\n value(n.max, "progress max");\n if (n.unit !== void 0) text(n.unit, "progress unit");\n break;\n case "steps": {\n if (!Array.isArray(n.items) || !n.items.length || n.items.length > 12) fail("steps require 1..12 items");\n for (const item of n.items) {\n fields(item, ["title", "description", "status"], "step");\n text(item.title, "step title");\n if (item.description !== void 0) text(item.description, "step description");\n if (!["pending", "active", "done", "error"].includes(item.status)) fail("unknown step status");\n }\n break;\n }\n case "heading":\n case "text":\n value(n.text, "text");\n break;\n case "metric":\n text(n.label, "metric label");\n value(n.value, "metric value");\n if (n.unit !== void 0) text(n.unit, "unit");\n if (n.decimals !== void 0 && (!Number.isInteger(n.decimals) || n.decimals < 0 || n.decimals > 6)) fail("invalid decimals");\n break;\n case "input":\n case "slider":\n case "select":\n case "toggle": {\n text(n.label, "control label");\n if (typeof n.bind !== "string" || !n.bind.startsWith("state.")) fail("controls must bind state");\n const [, k] = reference(doc2, n.bind), spec = doc2.state[k];\n if (n.type === "slider" && spec.type !== "number") fail("slider requires numeric state");\n if (n.type === "toggle" && spec.type !== "boolean") fail("toggle requires boolean state");\n if (n.type === "input" && spec.type === "boolean") fail("use toggle for boolean state");\n if (n.type === "select" && !spec.options) fail("select requires options");\n break;\n }\n case "button":\n text(n.text, "button text");\n if (!own(doc2.actions, n.action)) fail("unknown action");\n break;\n case "table": {\n value(n.rows, "table rows");\n if (!Array.isArray(n.columns) || !n.columns.length || n.columns.length > 16) fail("invalid table columns");\n const cols = /* @__PURE__ */ new Set();\n for (const c of n.columns) {\n fields(c, ["key", "label", "unit"], "column");\n if (!key(c.key) || cols.has(c.key)) fail("invalid or duplicate column key");\n cols.add(c.key);\n text(c.label, "column label");\n if (c.unit !== void 0) text(c.unit, "column unit");\n }\n break;\n }\n case "chart":\n value(n.rows, "chart rows");\n if (!["bar", "line"].includes(n.kind) || !key(n.labelKey) || !key(n.valueKey)) fail("invalid chart");\n text(n.label, "chart label");\n if (n.unit !== void 0) text(n.unit, "chart unit");\n if (n.xScale !== void 0 && (!["category", "linear"].includes(n.xScale) || n.xScale === "linear" && n.kind !== "line")) fail("invalid xScale");\n break;\n default:\n fail(`unsupported component ${n.type}`);\n }\n if (["heading", "text", "card", "metric", "button", "badge", "progress", "details"].includes(n.type)) {\n if (n.icon !== void 0 && !UI_ICONS.includes(n.icon)) fail("unknown UI icon");\n if (n.type !== "button") {\n if (n.tone !== void 0 && !UI_TONES.includes(n.tone)) fail("unknown UI tone");\n }\n }\n fields(n, componentFields(n.type), `node.${n.id}`);\n if (["row", "column", "card", "details"].includes(n.type)) nodes(n.children, depth + 1);\n }\n }\n nodes(doc2.nodes);\n if (!count2) fail("at least one node is required");\n evaluate(doc2, defaults);\n return doc2;\n }\n\n // src/export-runtime.js\n var payload = JSON.parse(document.getElementById("dsh-ui-export").textContent);\n var doc = validateDocument(payload.document);\n var node = doc.nodes.flatMap(function flatten(n) {\n return [n, ...(n.children ?? []).flatMap(flatten)];\n }).find((n) => n.id === payload.nodeId && n.type === "sandbox");\n if (!node) throw Error("\\u5BFC\\u51FA\\u7684\\u753B\\u5E03\\u4E0D\\u5B58\\u5728");\n var state = validateState(doc, payload.state);\n var connected = false;\n var start = Date.now();\n var count = 0;\n var token = () => Array.from(crypto.getRandomValues(new Uint8Array(16)), (b) => b.toString(16).padStart(2, "0")).join("");\n var channel = token();\n var nonce = payload.nonce;\n var frame = document.createElement("iframe");\n frame.title = node.title;\n frame.sandbox = "allow-scripts";\n frame.referrerPolicy = "no-referrer";\n frame.style.cssText = "display:block;width:100%;height:" + (node.height ?? 420) + "px;border:0";\n var status = document.getElementById("status");\n var publish = () => {\n try {\n frame.contentWindow.postMessage({ dshCanvas: 1, channel, type: "snapshot", payload: { ...canvasSnapshot(node, state, doc.data, evaluate(doc, state).derived, false, payload.theme), visible: !document.hidden } }, "*");\n } catch (e) {\n status.textContent = "\\u8BA1\\u7B97\\u5931\\u8D25\\uFF1A" + e.message;\n }\n };\n addEventListener("message", (event) => {\n const m = event.data;\n if (event.source !== frame.contentWindow || m?.channel !== channel || m.dshCanvas !== 1) return;\n if (Date.now() - start > 1e3) {\n start = Date.now();\n count = 0;\n }\n if (++count > SANDBOX_LIMITS.rate) return;\n if (m.type === "ready") {\n connected = true;\n publish();\n } else if (m.type === "patch" && connected) {\n try {\n const next = validateState(doc, { ...state, ...canvasPatch(m.payload, node) });\n evaluate(doc, next);\n state = next;\n status.textContent = "\\u672C\\u5730\\u53C2\\u6570\\u5DF2\\u66F4\\u65B0\\uFF1B\\u5237\\u65B0\\u6587\\u4EF6\\u4F1A\\u56DE\\u5230\\u5BFC\\u51FA\\u65F6\\u7684\\u53C2\\u6570\\u3002";\n } catch (e) {\n status.textContent = "\\u53C2\\u6570\\u65E0\\u6548\\uFF1A" + e.message;\n }\n publish();\n } else if (m.type === "height" && Number.isFinite(m.payload)) frame.style.height = Math.max(160, Math.min(1e3, Math.ceil(m.payload))) + "px";\n else if (m.type === "error") status.textContent = "\\u753B\\u5E03\\u8FD0\\u884C\\u5F02\\u5E38\\uFF1A" + String(m.payload).slice(0, 1e3);\n });\n document.addEventListener("visibilitychange", () => {\n if (connected) publish();\n });\n document.getElementById("surface").append(frame);\n frame.srcdoc = sandboxDocument(node, channel, nonce);\n})();\n';
|
|
22738
|
+
var EXPORT_RUNTIME = '(() => {\n // lib/presentation.js\n var UI_ICONS = Object.freeze(["sparkles", "sliders", "layers", "chart", "gauge", "cpu", "database", "info", "check", "warning", "arrow", "target"]);\n var UI_TONES = Object.freeze(["neutral", "accent", "success", "warning", "danger"]);\n var UI_LAYOUTS = Object.freeze(["dashboard", "workbench", "comparison", "guide", "status", "report"]);\n var UI_ACCENTS = Object.freeze(["blue", "teal", "violet", "amber", "rose"]);\n var UI_SURFACES = Object.freeze(["outline", "soft", "featured"]);\n\n // lib/visual-audit.js\n function measureVisual(root, nodeId = "") {\n const issues = [], seen = /* @__PURE__ */ new Set(), coverage = { nodeId, elements: 0, truncated: false, contrastChecked: 0, contrastUncovered: 0 };\n const add = (code, value, el2) => {\n const id = el2?.closest("[data-ui-node]")?.getAttribute("data-ui-node") ?? nodeId;\n const k = id + ":" + code + ":" + Math.round(value ?? 0);\n if (!seen.has(k) && issues.length < 40) {\n seen.add(k);\n issues.push({ code, nodeId: id, ...value === void 0 ? {} : { value: Number(value.toFixed(2)) } });\n }\n };\n const color = (s) => {\n let m = s.match(/^rgba?\\(([^)]+)\\)/);\n if (m) {\n const a = m[1].split(/[\\s,\\/]+/).map(Number);\n return [a[0] / 255, a[1] / 255, a[2] / 255, a[3] ?? 1];\n }\n m = s.match(/^color\\(srgb ([^)]+)\\)/);\n if (m) {\n const a = m[1].split(/[\\s\\/]+/).map(Number);\n return [a[0], a[1], a[2], a[3] ?? 1];\n }\n return null;\n };\n const luminance = (c) => c.slice(0, 3).map((v) => v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4).reduce((s, v, i) => s + v * [0.2126, 0.7152, 0.0722][i], 0);\n const widthRoot = root.id === "canvas" ? document.documentElement : root;\n if (widthRoot.scrollWidth > widthRoot.clientWidth + 2) add("overflow", widthRoot.scrollWidth - widthRoot.clientWidth);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n let el;\n while (el = walker.nextNode()) {\n if (coverage.elements >= 2e3) {\n coverage.truncated = true;\n break;\n }\n coverage.elements++;\n if (el.matches("[data-canvas-phase=error]")) add("runtimeError", void 0, el);\n const css = getComputedStyle(el), box = el.getBoundingClientRect();\n if (!box.width || !box.height || css.visibility === "hidden" || css.display === "none") continue;\n const text2 = [...el.childNodes].some((n) => n.nodeType === 3 && n.textContent.trim());\n if (text2) {\n let size = parseFloat(css.fontSize);\n if (el instanceof SVGElement && el.getScreenCTM) {\n const m = el.getScreenCTM();\n if (m) size *= Math.hypot(m.c, m.d);\n }\n if (size < 11.9) add("smallText", size, el);\n const fg = color(css.color);\n let ancestor = el, bg = null, depth = 0;\n while (ancestor && depth++ < 64) {\n const style = getComputedStyle(ancestor);\n if (style.backgroundImage !== "none") break;\n const c = color(style.backgroundColor);\n if (c?.[3] === 1) {\n bg = c;\n break;\n }\n if (c?.[3] > 0) break;\n ancestor = ancestor.parentElement;\n }\n if (!(el instanceof SVGElement) && fg?.[3] === 1 && bg) {\n coverage.contrastChecked++;\n const a = luminance(fg), b = luminance(bg), ratio = (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);\n if (ratio < (size >= 24 || size >= 18.66 && parseFloat(css.fontWeight) >= 700 ? 3 : 4.5)) add("contrast", ratio, el);\n } else coverage.contrastUncovered++;\n }\n 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")) add("missingLabel", void 0, el);\n }\n Object.defineProperty(issues, "coverage", { value: coverage });\n return issues;\n }\n\n // lib/format-number.js\n function formatNumber(value, decimals = 2) {\n if (typeof value !== "number" || !Number.isFinite(value)) throw new Error("DSH UI: formatNumber requires a finite number");\n if (!Number.isInteger(decimals) || decimals < 0 || decimals > 6) throw new Error("DSH UI: formatNumber decimals must be an integer from 0 to 6");\n return value.toLocaleString("en-US", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });\n }\n\n // lib/canvas-system.js\n var CANVAS_SYSTEM_CSS = `\n:root{--canvas-radius:12px;--canvas-gap:16px;--canvas-positive:#167456;--canvas-negative:#b13b3b;--canvas-warning:#875b15}\n:root[data-canvas-scheme=dark]{--canvas-positive:#78d9b4;--canvas-negative:#ffaaaa;--canvas-warning:#edc87e}\nhtml,body{margin:0;min-width:0;background:var(--canvas-bg,#fff);color:var(--canvas-ink,#182537);font:14px/1.6 system-ui,"Microsoft YaHei",sans-serif}*{box-sizing:border-box}svg{max-width:100%;height:auto}button,input,select,textarea{font:inherit;color:inherit}button,input,select,textarea{border:1px solid var(--canvas-line,#ced4df);border-radius:8px;background:var(--canvas-bg,#fff);padding:8px 12px;max-width:100%}button{cursor:pointer}button:disabled{cursor:default;opacity:.55}input[type=range]{padding:0;accent-color:var(--canvas-accent,#3e60d3)}input[type=checkbox],input[type=radio]{accent-color:var(--canvas-accent,#3e60d3)}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--canvas-accent,#3e60d3);outline-offset:3px}\n.ui-stack{display:grid;gap:var(--canvas-gap)}.ui-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:var(--canvas-gap)}.ui-surface{padding:20px;border:1px solid var(--canvas-line,#ced4df);border-radius:var(--canvas-radius);background:var(--canvas-soft,#f3f5f8)}.ui-title{margin:0;font-size:clamp(20px,3vw,28px);line-height:1.3;letter-spacing:-.02em}.ui-caption{color:var(--canvas-muted,#526176);margin:8px 0;font-size:14px}.ui-value{font-size:clamp(24px,4vw,36px);font-variant-numeric:tabular-nums;line-height:1.2}.ui-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.ui-field{display:grid;gap:6px;min-width:0}.ui-legend{display:flex;gap:16px;flex-wrap:wrap;font-size:13px}.ui-status{min-height:1.6em}.ui-error{color:var(--canvas-negative)}.ui-primary{background:var(--canvas-accent,#3e60d3);color:var(--canvas-accent-ink,#fff);border-color:transparent}\nsvg .ui-node{fill:var(--canvas-soft,#f3f5f8);stroke:var(--canvas-line,#ced4df);stroke-width:1}svg .ui-edge{fill:none;stroke:var(--canvas-muted,#526176);stroke-width:1.5}svg .ui-label{fill:var(--canvas-ink,#182537);font:500 14px system-ui,sans-serif}svg .ui-note{fill:var(--canvas-muted,#526176);font:400 13px system-ui,sans-serif}svg .ui-leader{fill:none;stroke:var(--canvas-muted,#526176);stroke-dasharray:3 4}svg .ui-accent{fill:color-mix(in srgb,var(--canvas-accent,#3e60d3) 12%,var(--canvas-bg,#fff));stroke:var(--canvas-accent,#3e60d3)}\n.ui-flow{display:flex;align-items:center;justify-content:center;gap:16px;padding:24px 8px;flex-wrap:wrap}.ui-step{display:grid;gap:6px;min-width:130px;max-width:240px;padding:16px 20px;border:1px solid var(--canvas-line);border-radius:6px;background:var(--canvas-bg)}.ui-step strong{font-size:16px;font-weight:600}.ui-step span{font-size:14px;color:var(--canvas-muted)}.ui-arrow{color:var(--canvas-muted);font-size:22px}.ui-callout{border-inline-start:3px solid var(--canvas-accent);padding:8px 16px;color:var(--canvas-ink)}\n@media(max-width:480px){.ui-flow{flex-direction:column;padding:16px 8px;gap:12px}.ui-step{width:100%;max-width:none}.ui-arrow{transform:rotate(90deg)}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}}`;\n\n // lib/sandbox.js\n var SANDBOX_LIMITS = Object.freeze({ source: 49152, height: 1e3, message: 16384, rate: 30 });\n function canvasSnapshot(node2, state2, data, derived, disabled, theme) {\n const pick = (keys, values) => Object.fromEntries((keys ?? []).map((k) => [k, values[k]]));\n return { state: pick(node2.stateKeys, state2), data: pick(node2.dataKeys, data), derived: pick(node2.derivedKeys, derived), disabled, theme };\n }\n function canvasPatch(message, node2) {\n if (!message || typeof message !== "object" || Array.isArray(message)) throw new Error("Invalid canvas patch");\n const keys = Object.keys(message);\n if (!keys.length || keys.length > 64 || keys.some((k) => !(node2.stateKeys ?? []).includes(k))) throw new Error("Canvas state key is not declared");\n if (JSON.stringify(message).length > SANDBOX_LIMITS.message) throw new Error("Canvas patch is too large");\n return message;\n }\n function sandboxDocument(node2, channel2, nonce2) {\n const json = (value) => JSON.stringify(value).replace(/</g, "\\\\u003c").replace(/\\u2028/g, "\\\\u2028").replace(/\\u2029/g, "\\\\u2029");\n return `<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src \'none\'; script-src \'nonce-${nonce2}\'; style-src \'unsafe-inline\'; img-src data:; font-src \'none\'; connect-src \'none\'; media-src \'none\'; object-src \'none\'; frame-src \'none\'; worker-src \'none\'; base-uri \'none\'; form-action \'none\'"><meta name="viewport" content="width=device-width,initial-scale=1"><style>${CANVAS_SYSTEM_CSS}</style></head><body><main id="canvas"></main><script nonce="${nonce2}">(()=>{\n const channel=${json(channel2)}, source=${json({ html: node2.html, css: node2.css ?? "", script: node2.script ?? "" })};\n let snapshot={state:{},data:{},derived:{},disabled:true,theme:{}}, started=false;\n const listeners=new Set(), send=(type,payload)=>parent.postMessage({dshCanvas:1,channel,type,payload},\'*\');\n const report=e=>send(\'error\',String(e?.message??e).slice(0,1000));\n const audit=${measureVisual.toString()};let auditTimer;const scheduleAudit=()=>{clearTimeout(auditTimer);auditTimer=setTimeout(()=>{const issues=audit(document.getElementById(\'canvas\'),${json(node2.id ?? "")});send(\'audit\',{issues,coverage:issues.coverage})},400)};\n addEventListener(\'resize\',scheduleAudit);\n addEventListener(\'error\',e=>report(e.error??e.message));addEventListener(\'unhandledrejection\',e=>report(e.reason));\n const animations=new Set(), motion=matchMedia(\'(prefers-reduced-motion: reduce)\');\n let raf=0,last=0,time=0;\n const visible=()=>snapshot.visible!==false&&!document.hidden;\n const canAnimate=()=>visible()&&!snapshot.disabled&&!motion.matches;\n const draw=delta=>{for(const fn of animations)try{fn({time,delta,reducedMotion:motion.matches,visible:visible()})}catch(e){animations.delete(fn);report(e)}};\n const tick=t=>{raf=0;if(!canAnimate()||!animations.size){last=0;draw(0);return}const delta=last?Math.min(.05,(t-last)/1000):0;last=t;time+=delta;draw(delta);if(animations.size)raf=requestAnimationFrame(tick)};\n const refresh=()=>{draw(0);if(canAnimate()&&animations.size){if(!raf){last=0;raf=requestAnimationFrame(tick)}}else{cancelAnimationFrame(raf);raf=0;last=0}};\n motion.addEventListener(\'change\',refresh);document.addEventListener(\'visibilitychange\',refresh);\n const formatNumber=${formatNumber.toString()};\n const api={formatNumber,get snapshot(){return structuredClone(snapshot)},subscribe(fn){if(typeof fn!==\'function\')throw Error(\'Listener required\');listeners.add(fn);fn(api.snapshot);return()=>listeners.delete(fn)},setState(patch){if(!snapshot.disabled)send(\'patch\',patch)},animate(fn){if(typeof fn!==\'function\')throw Error(\'Animation callback required\');animations.add(fn);refresh();return()=>{animations.delete(fn);if(!animations.size){cancelAnimationFrame(raf);raf=0;last=0}}}};\n Object.defineProperty(window,\'DSHUI\',{value:Object.freeze(api),writable:false});\n for(const type of [\'click\',\'input\',\'change\',\'keydown\',\'pointerdown\'])document.addEventListener(type,e=>{if(snapshot.disabled){e.preventDefault();e.stopImmediatePropagation()}},true);\n addEventListener(\'message\',event=>{\n const m=event.data;if(event.source!==parent||m?.dshCanvas!==1||m.channel!==channel||m.type!==\'snapshot\')return;\n snapshot=m.payload;for(const [k,v] of Object.entries(snapshot.theme??{}))document.documentElement.style.setProperty(\'--canvas-\'+k,v);\n document.documentElement.style.colorScheme=snapshot.theme?.scheme??\'light\';document.documentElement.dataset.canvasScheme=snapshot.theme?.scheme??\'light\';\n const wasStarted=started;\n if(!started){started=true;document.getElementById(\'canvas\').innerHTML=source.html;const style=document.createElement(\'style\');style.textContent=source.css;document.head.append(style);const script=document.createElement(\'script\');script.nonce=${json(nonce2)};script.textContent=source.script;document.body.append(script)}\n if(wasStarted)for(const fn of listeners)try{fn(api.snapshot)}catch(e){report(e)}\n refresh();if(!wasStarted)send(\'mounted\');scheduleAudit();\n });\n let resizePending=false;new ResizeObserver(()=>{if(!started||resizePending)return;resizePending=true;requestAnimationFrame(()=>{resizePending=false;send(\'height\',Math.ceil(document.getElementById(\'canvas\').getBoundingClientRect().height))})}).observe(document.getElementById(\'canvas\'));\n send(\'ready\');\n})();<\\/script></body></html>`;\n }\n\n // lib/components.js\n var define = (fields2, purpose, presentation = "") => Object.freeze({ fields: Object.freeze(fields2), purpose, presentation });\n var COMPONENTS = Object.freeze({\n heading: define(["text"], "Section heading", "tone"),\n text: define(["text"], "Readable explanation or bound text", "tone"),\n row: define(["children"], "Wrapping parallel content"),\n column: define(["children"], "Ordered vertical content"),\n card: define(["title", "children", "surface"], "Meaningful grouped section", "tone"),\n details: define(["title", "children", "open"], "Collapsible supporting content", "tone"),\n badge: define(["text"], "Short explicit status label", "tone"),\n progress: define(["label", "value", "max", "unit"], "Declared numeric progress snapshot", "tone"),\n steps: define(["items"], "Ordered declared steps"),\n metric: define(["label", "value", "unit", "decimals"], "Numeric result with units", "tone"),\n input: define(["label", "bind"], "String or numeric state input"),\n slider: define(["label", "bind"], "Numeric state control"),\n select: define(["label", "bind"], "Selection from declared state options"),\n toggle: define(["label", "bind"], "Boolean state control"),\n button: define(["text", "action"], "Declared Agent follow-up", "icon"),\n table: define(["rows", "columns"], "Exact supplied or derived values"),\n chart: define(["kind", "rows", "labelKey", "valueKey", "label", "unit", "xScale"], "Bar/line comparison with accessible data"),\n sandbox: define(["title", "html", "css", "script", "fallback", "height", "stateKeys", "dataKeys", "derivedKeys"], "Freeform isolated HTML/SVG/CSS/JavaScript visualization")\n });\n function componentFields(type) {\n const c = Object.hasOwn(COMPONENTS, type) ? COMPONENTS[type] : void 0;\n return c && ["id", "type", ...c.fields, ...c.presentation ? ["icon"] : [], ...c.presentation === "tone" ? ["tone"] : []];\n }\n\n // lib/author-expression.js\n function compileExpression(source) {\n const fail2 = () => {\n throw new Error("DSH UI: invalid formula; use state.x, data.x, derived.x, item.x, numbers, strings, + - * /, comparisons, ?: and min/max/round/abs");\n };\n if (typeof source !== "string" || !source.trim() || source.length > 2048) fail2();\n const tokens = [];\n let offset = 0;\n const token2 = /\\s*(?:(\\d+(?:\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+)?|("(?:[^"\\\\]|\\\\.)*")|([a-zA-Z][a-zA-Z0-9_]*(?:\\.[a-zA-Z][a-zA-Z0-9_]*)?)|(===|!==|<=|>=|==|!=|[+*/()?,:<>-]))/y;\n while (offset < source.length) {\n if (!source.slice(offset).trim()) break;\n token2.lastIndex = offset;\n const m = token2.exec(source);\n if (!m) fail2();\n offset = token2.lastIndex;\n tokens.push(m[1] ? { number: Number(m[1] + (m[2] ?? "")) } : m[3] ? { string: JSON.parse(m[3]) } : m[4] ?? m[5]);\n if (tokens.length > 256) fail2();\n }\n let pos = 0;\n const operators = { "==": ["eq", 1], "===": ["eq", 1], "!=": ["neq", 1], "!==": ["neq", 1], "<": ["lt", 2], "<=": ["lte", 2], ">": ["gt", 2], ">=": ["gte", 2], "+": ["add", 3], "-": ["sub", 3], "*": ["mul", 4], "/": ["div", 4] };\n const take = (t) => {\n if (tokens[pos++] !== t) fail2();\n };\n function parse(min = 0, depth = 0) {\n if (depth > 12) fail2();\n const t = tokens[pos++];\n let out;\n if (t && typeof t === "object") out = { const: Object.hasOwn(t, "number") ? t.number : t.string };\n else if (t === "true" || t === "false") out = { const: t === "true" };\n else if (t === "(") {\n out = parse(0, depth + 1);\n take(")");\n } else if (t === "-" || t === "+") {\n const arg = parse(5, depth + 1);\n out = t === "-" ? { op: "sub", args: [{ const: 0 }, arg] } : arg;\n } else if (typeof t === "string" && /^(state|data|derived|item)\\.[a-zA-Z][a-zA-Z0-9_]*$/.test(t)) out = { ref: t };\n else if (["min", "max", "round", "abs"].includes(t)) {\n take("(");\n const args = [parse(0, depth + 1)];\n while (tokens[pos] === ",") {\n pos++;\n args.push(parse(0, depth + 1));\n if (args.length > 8) fail2();\n }\n take(")");\n out = { op: t, args };\n } else fail2();\n while (typeof tokens[pos] === "string" && Object.hasOwn(operators, tokens[pos]) && operators[tokens[pos]][1] >= min) {\n const [op, precedence] = operators[tokens[pos++]];\n out = { op, args: [out, parse(precedence + 1, depth + 1)] };\n }\n if (min === 0 && tokens[pos] === "?") {\n pos++;\n const yes = parse(0, depth + 1);\n take(":");\n out = { op: "if", args: [out, yes, parse(0, depth + 1)] };\n }\n return out;\n }\n const result = parse();\n if (pos !== tokens.length) fail2();\n return result;\n }\n function expandExpressions(doc2) {\n const expression = (e) => {\n if (!e || typeof e !== "object" || Array.isArray(e)) return e;\n if (Object.hasOwn(e, "expr")) {\n if (Object.keys(e).length !== 1) throw new Error("DSH UI: expr must be the only formula field");\n return compileExpression(e.expr);\n }\n if (Array.isArray(e.args)) e.args = e.args.map(expression);\n if (e.map?.fields) for (const k of Object.keys(e.map.fields)) e.map.fields[k] = expression(e.map.fields[k]);\n return e;\n };\n if (doc2.derived && typeof doc2.derived === "object") for (const k of Object.keys(doc2.derived)) doc2.derived[k] = expression(doc2.derived[k]);\n const walk = (nodes) => {\n if (!Array.isArray(nodes)) return;\n for (const node2 of nodes) {\n if (!node2 || typeof node2 !== "object") continue;\n for (const field of ["text", "value", "max", "rows"]) if (Object.hasOwn(node2, field)) node2[field] = expression(node2[field]);\n walk(node2.children);\n }\n };\n walk(doc2.nodes);\n return doc2;\n }\n\n // lib/protocol.js\n var LIMITS = Object.freeze({ bytes: 131072, nodes: 160, depth: 12, rows: 500, expressions: 128, steps: 4096 });\n var own = (o, k) => Object.hasOwn(o, k);\n var fail = (message) => {\n throw new Error(`DSH UI: ${message}`);\n };\n var object = (x) => x !== null && typeof x === "object" && !Array.isArray(x);\n var key = (x) => typeof x === "string" && /^[a-zA-Z][a-zA-Z0-9_]{0,63}$/.test(x) && !["constructor", "prototype", "__proto__"].includes(x);\n var finite = (x) => typeof x === "number" && Number.isFinite(x);\n var scalar = (x) => typeof x === "string" || typeof x === "boolean" || finite(x);\n var text = (x, at) => {\n if (typeof x !== "string" || x.length > 8192) fail(`${at} must be a string of at most 8192 characters`);\n };\n function fields(value, allowed, at) {\n if (!object(value)) fail(`${at} must be an object`);\n for (const k of Object.keys(value)) if (!allowed.includes(k)) {\n const hint = at === "document" ? " Controls and visual components belong in nodes as registered typed nodes, not extra document fields." : "";\n fail(`unknown field ${at}.${k}; allowed fields at ${at}: ${allowed.join(", ")}.${hint}`);\n }\n }\n function safeJson(value, depth = 0, source = false) {\n if (depth > 48) fail("JSON nesting is too deep");\n if (value === null || typeof value === "boolean") return;\n if (typeof value === "string") {\n if (source) {\n if (value.length > SANDBOX_LIMITS.source) fail("canvas source exceeds 48 Ki characters");\n return;\n }\n return text(value, "value");\n }\n if (typeof value === "number") {\n if (!finite(value)) fail("numbers must be finite");\n return;\n }\n if (typeof value !== "object") fail("only JSON values are accepted");\n if (Array.isArray(value) && value.length > LIMITS.rows) fail("too many array items");\n for (const k of Object.keys(value)) {\n if (["__proto__", "prototype", "constructor"].includes(k)) fail("unsafe property name");\n safeJson(value[k], depth + 1, value.type === "sandbox" && ["html", "css", "script"].includes(k));\n }\n }\n function initialState(doc2) {\n return Object.fromEntries(Object.entries(doc2.state).map(([k, spec]) => [k, spec.default]));\n }\n function validateState(doc2, state2) {\n if (!object(state2) || Object.keys(state2).length !== Object.keys(doc2.state).length) fail("state keys do not match the document");\n const result = {};\n for (const [k, s] of Object.entries(doc2.state)) {\n if (!own(state2, k)) fail(`missing state.${k}`);\n const v = state2[k];\n if (s.type === "number") {\n if (!finite(v) || v < s.min || v > s.max) fail(`state.${k} is outside its numeric range`);\n const offset = (v - s.min) / s.step;\n if (Math.abs(offset - Math.round(offset)) > 1e-6) fail(`state.${k} does not match its step`);\n } else if (s.type === "string") text(v, `state.${k}`);\n else if (typeof v !== "boolean") fail(`state.${k} must be boolean`);\n if (s.options && !s.options.includes(v)) fail(`invalid option for state.${k}`);\n result[k] = v;\n }\n return result;\n }\n var ARITY = { add: [1, 8], sub: [2, 2], mul: [1, 8], div: [2, 2], min: [1, 8], max: [1, 8], round: [1, 1], abs: [1, 1], eq: [2, 2], neq: [2, 2], lt: [2, 2], lte: [2, 2], gt: [2, 2], gte: [2, 2], if: [3, 3] };\n function reference(doc2, ref) {\n if (typeof ref !== "string" || !/^(state|data|derived)\\.[a-zA-Z][a-zA-Z0-9_]{0,63}$/.test(ref)) fail("invalid reference");\n const [group, k] = ref.split(".");\n if (!own(doc2[group], k)) fail(`unknown reference ${ref}`);\n return [group, k];\n }\n function evaluate(doc2, state2) {\n const derived = {}, active = /* @__PURE__ */ new Set();\n let steps = 0, generatedRows = 0;\n const budget = () => {\n if (++steps > LIMITS.steps) fail("calculation budget exceeded");\n };\n const resolve = (ref) => {\n budget();\n const [group, k] = reference(doc2, ref);\n if (group !== "derived") return (group === "state" ? state2 : doc2.data)[k];\n if (own(derived, k)) return derived[k];\n if (active.has(k)) fail(`cyclic calculation ${k}`);\n active.add(k);\n const result2 = expression(doc2.derived[k]);\n active.delete(k);\n derived[k] = result2;\n return result2;\n };\n const expression = (e, item) => {\n budget();\n if (own(e, "map")) {\n const rows = resolve(e.map.source);\n if ((generatedRows += rows.length) > LIMITS.rows) fail("generated row limit exceeded");\n return rows.map((row) => Object.fromEntries(Object.entries(e.map.fields).map(([k, value]) => {\n const out2 = expression(value, row);\n if (!scalar(out2)) fail("generated cells must be scalar");\n return [k, out2];\n })));\n }\n if (own(e, "const")) return e.const;\n if (own(e, "ref")) {\n if (e.ref.startsWith("item.")) {\n const k = e.ref.slice(5);\n if (!item || !own(item, k) || !scalar(item[k])) fail("invalid item reference");\n return item[k];\n }\n return resolve(e.ref);\n }\n if (e.op === "if") {\n const condition = expression(e.args[0], item);\n if (typeof condition !== "boolean") fail("if condition must be boolean");\n return expression(e.args[condition ? 1 : 2], item);\n }\n const a = e.args.map((arg) => expression(arg, item));\n if (e.op === "eq") return a[0] === a[1];\n if (e.op === "neq") return a[0] !== a[1];\n if (!a.every(finite)) fail(`operator ${e.op} requires finite numbers`);\n let out;\n switch (e.op) {\n case "add":\n out = a.reduce((x, y) => x + y, 0);\n break;\n case "sub":\n out = a[0] - a[1];\n break;\n case "mul":\n out = a.reduce((x, y) => x * y, 1);\n break;\n case "div":\n if (a[1] === 0) fail("division by zero");\n out = a[0] / a[1];\n break;\n case "min":\n out = Math.min(...a);\n break;\n case "max":\n out = Math.max(...a);\n break;\n case "round":\n out = Math.round(a[0]);\n break;\n case "abs":\n out = Math.abs(a[0]);\n break;\n case "lt":\n return a[0] < a[1];\n case "lte":\n return a[0] <= a[1];\n case "gt":\n return a[0] > a[1];\n case "gte":\n return a[0] >= a[1];\n default:\n fail(`unsupported operator ${e.op}`);\n }\n if (!finite(out)) fail("non-finite calculation result");\n return out;\n };\n for (const k of Object.keys(doc2.derived)) resolve(`derived.${k}`);\n if (new TextEncoder().encode(JSON.stringify(derived)).length > LIMITS.bytes) fail("derived output exceeds 128 KiB");\n const result = { derived, resolveValue: (value) => {\n if (!object(value)) return value;\n return expression(value);\n } };\n checkViewValues(doc2, result);\n return result;\n }\n function checkViewValues(doc2, evaluated) {\n function walk(list) {\n for (const n of list) {\n if (n.type === "table" || n.type === "chart") {\n const rows = evaluated.resolveValue(n.rows);\n if (!Array.isArray(rows) || rows.length > LIMITS.rows || !rows.every(object)) fail("table/chart rows must reference an array of objects");\n if (n.type === "chart" && rows.some((r) => !own(r, n.labelKey) || !scalar(r[n.labelKey]) || !own(r, n.valueKey) || !finite(r[n.valueKey]))) fail("chart requires labels and finite numeric values");\n if (n.type === "chart" && n.xScale === "linear" && rows.some((r) => !finite(r[n.labelKey]))) fail("linear chart requires numeric x values");\n }\n if (n.type === "metric" && !finite(evaluated.resolveValue(n.value))) fail("metric requires a numeric value");\n if (n.type === "progress") {\n const v = evaluated.resolveValue(n.value), max = evaluated.resolveValue(n.max);\n if (!finite(v) || !finite(max) || max <= 0 || v < 0 || v > max) fail("progress requires 0 <= value <= positive max");\n }\n if (n.children) walk(n.children);\n }\n }\n walk(doc2.nodes);\n }\n function validateDocument(input) {\n safeJson(input);\n const json = JSON.stringify(input);\n if (new TextEncoder().encode(json).length > LIMITS.bytes) fail("document exceeds 128 KiB");\n const doc2 = expandExpressions(JSON.parse(json));\n fields(doc2, ["version", "title", "fallback", "state", "data", "derived", "nodes", "actions", "presentation"], "document");\n if (doc2.presentation !== void 0) {\n fields(doc2.presentation, ["layout", "accent", "density"], "presentation");\n for (const [k, allowed] of Object.entries({ layout: UI_LAYOUTS, accent: UI_ACCENTS, density: ["comfortable", "compact"] }))\n if (doc2.presentation[k] !== void 0 && !allowed.includes(doc2.presentation[k])) fail(`unknown presentation ${k}`);\n }\n if (doc2.version !== 1) fail("unsupported protocol version");\n text(doc2.title, "title");\n text(doc2.fallback, "fallback");\n if (!doc2.title.trim() || !doc2.fallback.trim()) fail("title and fallback cannot be empty");\n for (const group of ["state", "data", "derived", "actions"]) {\n doc2[group] ??= {};\n if (!object(doc2[group]) || Object.keys(doc2[group]).length > 64) fail(`invalid ${group}`);\n for (const k of Object.keys(doc2[group])) if (!key(k)) fail(`invalid key in ${group}`);\n }\n for (const [k, s] of Object.entries(doc2.state)) {\n fields(s, ["type", "default", "min", "max", "step", "options"], `state.${k}`);\n if (!["number", "string", "boolean"].includes(s.type)) fail(`unsupported state type ${k}`);\n if (s.type === "number") {\n if (![s.min, s.max, s.step].every(finite) || s.min >= s.max || s.step <= 0) fail(`invalid numeric limits for ${k}`);\n if ((s.max - s.min) / s.step > 1e9) fail(`numeric step too small for ${k}`);\n } else if (["min", "max", "step"].some((p) => own(s, p))) fail(`numeric limits on non-number ${k}`);\n if (s.options && (!Array.isArray(s.options) || !s.options.length || s.options.length > 64 || !s.options.every(scalar))) fail(`invalid options for ${k}`);\n }\n const defaults = validateState(doc2, initialState(doc2));\n let expressions = 0;\n for (const [k, spec] of Object.entries(doc2.state))\n for (const option of spec.options ?? []) validateState(doc2, { ...defaults, [k]: option });\n function validateExpression(e, depth = 0, itemRows) {\n if (++expressions > LIMITS.expressions || depth > LIMITS.depth) fail("expression limit exceeded");\n if (!object(e)) fail("expression must be an object");\n if (own(e, "map")) {\n if (depth !== 0) fail("map is only allowed at the root of a derived value");\n fields(e, ["map"], "expression");\n fields(e.map, ["source", "fields"], "map");\n if (typeof e.map.source !== "string" || !e.map.source.startsWith("data.")) fail(\'map source must reference static data using a string such as "data.samples"; put object rows in document.data.samples, not inline in map.source\');\n const [, k] = reference(doc2, e.map.source), rows = doc2.data[k];\n if (!Array.isArray(rows) || rows.length > LIMITS.rows || !rows.every(object)) fail("map source must be object rows");\n if (!object(e.map.fields) || !Object.keys(e.map.fields).length || Object.keys(e.map.fields).length > 16) fail("map requires 1..16 fields");\n for (const [k2, value2] of Object.entries(e.map.fields)) {\n if (!key(k2)) fail(`invalid map field ${JSON.stringify(k2).slice(0, 100)}; use an ASCII identifier matching [a-zA-Z][a-zA-Z0-9_]{0,63}, such as powerW. Put Chinese display text in column/chart labels, not internal field keys`);\n validateExpression(value2, depth + 1, rows);\n }\n return;\n }\n if (own(e, "const")) {\n fields(e, ["const"], "expression");\n if (!scalar(e.const)) fail("constant must be scalar");\n return;\n }\n if (own(e, "ref")) {\n fields(e, ["ref"], "expression");\n if (typeof e.ref === "string" && e.ref.startsWith("item.")) {\n const k = e.ref.slice(5);\n if (!itemRows || !key(k) || itemRows.some((row) => !own(row, k) || !scalar(row[k]))) fail("invalid or unscoped item reference");\n } else reference(doc2, e.ref);\n return;\n }\n fields(e, ["op", "args"], "expression");\n const arity = ARITY[e.op];\n if (!arity || !Array.isArray(e.args) || e.args.length < arity[0] || e.args.length > arity[1]) fail("invalid operator or argument count");\n e.args.forEach((a) => validateExpression(a, depth + 1, itemRows));\n }\n Object.values(doc2.derived).forEach((e) => validateExpression(e));\n const visited = /* @__PURE__ */ new Set(), active = /* @__PURE__ */ new Set();\n function visit(k) {\n if (active.has(k)) fail(`cyclic calculation ${k}`);\n if (visited.has(k)) return;\n active.add(k);\n function walk(e) {\n if (e.ref?.startsWith("derived.")) visit(e.ref.split(".")[1]);\n for (const a of e.args ?? []) walk(a);\n for (const value2 of Object.values(e.map?.fields ?? {})) walk(value2);\n }\n walk(doc2.derived[k]);\n active.delete(k);\n visited.add(k);\n }\n Object.keys(doc2.derived).forEach(visit);\n for (const [k, a] of Object.entries(doc2.actions)) {\n fields(a, ["type", "intent", "stateKeys"], `actions.${k}`);\n if (a.type !== "agent.followup") fail("only agent.followup actions are supported");\n text(a.intent, "action intent");\n if (!a.intent.trim() || !Array.isArray(a.stateKeys) || a.stateKeys.length > 64 || new Set(a.stateKeys).size !== a.stateKeys.length || a.stateKeys.some((s) => !own(doc2.state, s))) fail("invalid action stateKeys");\n }\n const ids = /* @__PURE__ */ new Set();\n let count2 = 0;\n const value = (v, at) => {\n if (object(v)) {\n if (own(v, "map")) fail("map belongs in derived");\n validateExpression(v);\n } else if (!scalar(v)) fail(`invalid value at ${at}`);\n };\n function nodes(list, depth = 0) {\n if (!Array.isArray(list) || depth > LIMITS.depth) fail("invalid node children or depth");\n for (const n of list) {\n if (++count2 > LIMITS.nodes) fail("too many nodes");\n if (!object(n) || !key(n.id) || ids.has(n.id)) fail("node ids must be unique identifiers");\n ids.add(n.id);\n if (!Object.hasOwn(COMPONENTS, n.type)) fail("unsupported component " + n.type);\n switch (n.type) {\n case "sandbox": {\n text(n.title, "canvas title");\n text(n.fallback, "canvas fallback");\n if (!n.title.trim() || !n.fallback.trim() || typeof n.html !== "string" || !n.html.trim()) fail("canvas requires title, HTML and fallback");\n for (const field of ["html", "css", "script"]) if (n[field] !== void 0 && (typeof n[field] !== "string" || n[field].length > SANDBOX_LIMITS.source)) fail("canvas source exceeds 48 Ki characters");\n if (n.height !== void 0 && (!Number.isInteger(n.height) || n.height < 160 || n.height > SANDBOX_LIMITS.height)) fail("canvas height must be 160..1000");\n for (const [field, group] of [["stateKeys", "state"], ["dataKeys", "data"], ["derivedKeys", "derived"]]) if (n[field] !== void 0 && (!Array.isArray(n[field]) || n[field].length > 64 || new Set(n[field]).size !== n[field].length || n[field].some((k) => !own(doc2[group], k)))) fail("invalid canvas " + field);\n break;\n }\n case "row":\n case "column":\n break;\n case "card":\n if (n.title !== void 0) text(n.title, "card title");\n if (n.surface !== void 0 && !UI_SURFACES.includes(n.surface)) fail("unknown card surface");\n break;\n case "details":\n text(n.title, "details title");\n if (n.open !== void 0 && typeof n.open !== "boolean") fail("details open must be boolean");\n break;\n case "badge":\n value(n.text, "badge text");\n break;\n case "progress":\n text(n.label, "progress label");\n value(n.value, "progress value");\n value(n.max, "progress max");\n if (n.unit !== void 0) text(n.unit, "progress unit");\n break;\n case "steps": {\n if (!Array.isArray(n.items) || !n.items.length || n.items.length > 12) fail("steps require 1..12 items");\n for (const item of n.items) {\n fields(item, ["title", "description", "status"], "step");\n text(item.title, "step title");\n if (item.description !== void 0) text(item.description, "step description");\n if (!["pending", "active", "done", "error"].includes(item.status)) fail("unknown step status");\n }\n break;\n }\n case "heading":\n case "text":\n value(n.text, "text");\n break;\n case "metric":\n text(n.label, "metric label");\n value(n.value, "metric value");\n if (n.unit !== void 0) text(n.unit, "unit");\n if (n.decimals !== void 0 && (!Number.isInteger(n.decimals) || n.decimals < 0 || n.decimals > 6)) fail("invalid decimals");\n break;\n case "input":\n case "slider":\n case "select":\n case "toggle": {\n text(n.label, "control label");\n if (typeof n.bind !== "string" || !n.bind.startsWith("state.")) fail("controls must bind state");\n const [, k] = reference(doc2, n.bind), spec = doc2.state[k];\n if (n.type === "slider" && spec.type !== "number") fail("slider requires numeric state");\n if (n.type === "toggle" && spec.type !== "boolean") fail("toggle requires boolean state");\n if (n.type === "input" && spec.type === "boolean") fail("use toggle for boolean state");\n if (n.type === "select" && !spec.options) fail("select requires options");\n break;\n }\n case "button":\n text(n.text, "button text");\n if (!own(doc2.actions, n.action)) fail("unknown action");\n break;\n case "table": {\n if (Array.isArray(n.rows)) fail(\'table.rows must use {ref:"data.rows"}; move the array into document.data.rows, not inline in the table\');\n value(n.rows, "table rows");\n if (!Array.isArray(n.columns) || !n.columns.length || n.columns.length > 16) fail("invalid table columns");\n const cols = /* @__PURE__ */ new Set();\n for (const c of n.columns) {\n fields(c, ["key", "label", "unit"], "column");\n if (!key(c.key) || cols.has(c.key)) fail("invalid or duplicate column key");\n cols.add(c.key);\n text(c.label, "column label");\n if (c.unit !== void 0) text(c.unit, "column unit");\n }\n break;\n }\n case "chart":\n value(n.rows, "chart rows");\n if (!["bar", "line"].includes(n.kind) || !key(n.labelKey) || !key(n.valueKey)) fail("invalid chart");\n text(n.label, "chart label");\n if (n.unit !== void 0) text(n.unit, "chart unit");\n if (n.xScale !== void 0 && (!["category", "linear"].includes(n.xScale) || n.xScale === "linear" && n.kind !== "line")) fail("invalid xScale");\n break;\n default:\n fail(`unsupported component ${n.type}`);\n }\n if (["heading", "text", "card", "metric", "button", "badge", "progress", "details"].includes(n.type)) {\n if (n.icon !== void 0 && !UI_ICONS.includes(n.icon)) fail("unknown UI icon");\n if (n.type !== "button") {\n if (n.tone !== void 0 && !UI_TONES.includes(n.tone)) fail("unknown UI tone");\n }\n }\n fields(n, componentFields(n.type), `node.${n.id}`);\n if (["row", "column", "card", "details"].includes(n.type)) nodes(n.children, depth + 1);\n }\n }\n nodes(doc2.nodes);\n if (!count2) fail("at least one node is required");\n evaluate(doc2, defaults);\n return doc2;\n }\n\n // src/export-runtime.js\n var payload = JSON.parse(document.getElementById("dsh-ui-export").textContent);\n var doc = validateDocument(payload.document);\n var node = doc.nodes.flatMap(function flatten(n) {\n return [n, ...(n.children ?? []).flatMap(flatten)];\n }).find((n) => n.id === payload.nodeId && n.type === "sandbox");\n if (!node) throw Error("\\u5BFC\\u51FA\\u7684\\u753B\\u5E03\\u4E0D\\u5B58\\u5728");\n var state = validateState(doc, payload.state);\n var connected = false;\n var start = Date.now();\n var count = 0;\n var token = () => Array.from(crypto.getRandomValues(new Uint8Array(16)), (b) => b.toString(16).padStart(2, "0")).join("");\n var channel = token();\n var nonce = payload.nonce;\n var frame = document.createElement("iframe");\n frame.title = node.title;\n frame.sandbox = "allow-scripts";\n frame.referrerPolicy = "no-referrer";\n frame.style.cssText = "display:block;width:100%;height:" + (node.height ?? 420) + "px;border:0";\n var status = document.getElementById("status");\n var publish = () => {\n try {\n frame.contentWindow.postMessage({ dshCanvas: 1, channel, type: "snapshot", payload: { ...canvasSnapshot(node, state, doc.data, evaluate(doc, state).derived, false, payload.theme), visible: !document.hidden } }, "*");\n } catch (e) {\n status.textContent = "\\u8BA1\\u7B97\\u5931\\u8D25\\uFF1A" + e.message;\n }\n };\n addEventListener("message", (event) => {\n const m = event.data;\n if (event.source !== frame.contentWindow || m?.channel !== channel || m.dshCanvas !== 1) return;\n if (Date.now() - start > 1e3) {\n start = Date.now();\n count = 0;\n }\n if (++count > SANDBOX_LIMITS.rate) return;\n if (m.type === "ready") {\n connected = true;\n publish();\n } else if (m.type === "patch" && connected) {\n try {\n const next = validateState(doc, { ...state, ...canvasPatch(m.payload, node) });\n evaluate(doc, next);\n state = next;\n status.textContent = "\\u672C\\u5730\\u53C2\\u6570\\u5DF2\\u66F4\\u65B0\\uFF1B\\u5237\\u65B0\\u6587\\u4EF6\\u4F1A\\u56DE\\u5230\\u5BFC\\u51FA\\u65F6\\u7684\\u53C2\\u6570\\u3002";\n } catch (e) {\n status.textContent = "\\u53C2\\u6570\\u65E0\\u6548\\uFF1A" + e.message;\n }\n publish();\n } else if (m.type === "height" && Number.isFinite(m.payload)) frame.style.height = Math.max(160, Math.min(1e3, Math.ceil(m.payload))) + "px";\n else if (m.type === "error") status.textContent = "\\u753B\\u5E03\\u8FD0\\u884C\\u5F02\\u5E38\\uFF1A" + String(m.payload).slice(0, 1e3);\n });\n document.addEventListener("visibilitychange", () => {\n if (connected) publish();\n });\n document.getElementById("surface").append(frame);\n frame.srcdoc = sandboxDocument(node, channel, nonce);\n})();\n';
|
|
22736
22739
|
|
|
22737
22740
|
// lib/canvas-export.js
|
|
22738
22741
|
function standaloneCanvas(document2, node, state, theme = {}) {
|
|
@@ -22888,7 +22891,7 @@ function Sandbox({ node, document: doc, state, data, derived, disabled, preview,
|
|
|
22888
22891
|
}
|
|
22889
22892
|
|
|
22890
22893
|
// lib/build-info.js
|
|
22891
|
-
var BUILD_INFO = Object.freeze({ "version": "0.5.
|
|
22894
|
+
var BUILD_INFO = Object.freeze({ "version": "0.5.3", "id": "0.5.3+634080c7e02a", "sourceHash": "634080c7e02ae0667c17a2ce4f94d8999270f1827b7735058deee989caa7777a" });
|
|
22892
22895
|
|
|
22893
22896
|
// lib/refinement.js
|
|
22894
22897
|
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" });
|
|
@@ -22954,11 +22957,11 @@ function HistoryView({ record, sessionId }) {
|
|
|
22954
22957
|
setBusy(false);
|
|
22955
22958
|
}
|
|
22956
22959
|
}
|
|
22957
|
-
async function restore(
|
|
22960
|
+
async function restore(revision2) {
|
|
22958
22961
|
setBusy(true);
|
|
22959
22962
|
setError("");
|
|
22960
22963
|
try {
|
|
22961
|
-
const next = await request2({ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ documentRevision: record.documentRevision ?? 0, targetRevision:
|
|
22964
|
+
const next = await request2({ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ documentRevision: record.documentRevision ?? 0, targetRevision: revision2 }) });
|
|
22962
22965
|
publishDocument(next);
|
|
22963
22966
|
setPreview(null);
|
|
22964
22967
|
setVersions(await request2());
|
|
@@ -22968,11 +22971,11 @@ function HistoryView({ record, sessionId }) {
|
|
|
22968
22971
|
setBusy(false);
|
|
22969
22972
|
}
|
|
22970
22973
|
}
|
|
22971
|
-
async function inspect(
|
|
22974
|
+
async function inspect(revision2) {
|
|
22972
22975
|
setBusy(true);
|
|
22973
22976
|
setError("");
|
|
22974
22977
|
try {
|
|
22975
|
-
const r = await fetch(url + "&revision=" +
|
|
22978
|
+
const r = await fetch(url + "&revision=" + revision2, { credentials: "same-origin" }), v = await r.json();
|
|
22976
22979
|
if (!r.ok) throw new Error(v.error ?? "\u5386\u53F2\u8BFB\u53D6\u5931\u8D25");
|
|
22977
22980
|
setPreview({ ...v, document: validateDocument(v.document) });
|
|
22978
22981
|
} catch (e) {
|
|
@@ -23003,7 +23006,7 @@ function HistoryView({ record, sessionId }) {
|
|
|
23003
23006
|
}
|
|
23004
23007
|
|
|
23005
23008
|
// src/presentation.js
|
|
23006
|
-
var
|
|
23009
|
+
var import_react9 = __toESM(require("react"), 1);
|
|
23007
23010
|
var import_react_dom = __toESM(require_react_dom(), 1);
|
|
23008
23011
|
|
|
23009
23012
|
// src/presentation-store.js
|
|
@@ -23012,7 +23015,11 @@ var records = /* @__PURE__ */ new Map();
|
|
|
23012
23015
|
var listeners3 = /* @__PURE__ */ new Set();
|
|
23013
23016
|
var locations = /* @__PURE__ */ new Map();
|
|
23014
23017
|
var opener;
|
|
23015
|
-
var
|
|
23018
|
+
var revision = 0;
|
|
23019
|
+
var emit = () => {
|
|
23020
|
+
revision++;
|
|
23021
|
+
listeners3.forEach((fn) => fn());
|
|
23022
|
+
};
|
|
23016
23023
|
var notifyUi = emit;
|
|
23017
23024
|
function rememberUi(raw, sessionId, sendPrompt) {
|
|
23018
23025
|
if (raw?.version !== 1 || raw.sessionId !== sessionId) return;
|
|
@@ -23028,8 +23035,7 @@ var subscribeUi = (fn) => {
|
|
|
23028
23035
|
return () => listeners3.delete(fn);
|
|
23029
23036
|
};
|
|
23030
23037
|
function useUiRegistry() {
|
|
23031
|
-
|
|
23032
|
-
(0, import_react7.useEffect)(() => subscribeUi(() => wake((v) => v + 1)), []);
|
|
23038
|
+
(0, import_react7.useSyncExternalStore)(subscribeUi, () => revision, () => 0);
|
|
23033
23039
|
return uiRecords();
|
|
23034
23040
|
}
|
|
23035
23041
|
function setPanelOpener(fn) {
|
|
@@ -23120,16 +23126,58 @@ function startUiMarkers(root, sessionId) {
|
|
|
23120
23126
|
};
|
|
23121
23127
|
}
|
|
23122
23128
|
|
|
23129
|
+
// src/presentation-owner.js
|
|
23130
|
+
var import_react8 = require("react");
|
|
23131
|
+
var roots = /* @__PURE__ */ new WeakMap();
|
|
23132
|
+
function usePresentationOwner(anchor, documentId, sessionId) {
|
|
23133
|
+
const [owner, setOwner] = (0, import_react8.useState)(false);
|
|
23134
|
+
(0, import_react8.useEffect)(() => {
|
|
23135
|
+
const element = anchor.current;
|
|
23136
|
+
if (!element) return;
|
|
23137
|
+
const root = element.closest("[data-conversation-scroll]") ?? element.parentElement;
|
|
23138
|
+
if (!root) return;
|
|
23139
|
+
let documents = roots.get(root);
|
|
23140
|
+
if (!documents) {
|
|
23141
|
+
documents = /* @__PURE__ */ new Map();
|
|
23142
|
+
roots.set(root, documents);
|
|
23143
|
+
}
|
|
23144
|
+
const key3 = sessionId + ":" + documentId;
|
|
23145
|
+
let entries = documents.get(key3);
|
|
23146
|
+
if (!entries) {
|
|
23147
|
+
entries = /* @__PURE__ */ new Set();
|
|
23148
|
+
documents.set(key3, entries);
|
|
23149
|
+
}
|
|
23150
|
+
const entry = { element, update: setOwner };
|
|
23151
|
+
entries.add(entry);
|
|
23152
|
+
const elect = () => {
|
|
23153
|
+
let winner;
|
|
23154
|
+
for (const candidate of entries) {
|
|
23155
|
+
if (!candidate.element.isConnected) continue;
|
|
23156
|
+
if (!winner || winner.element.compareDocumentPosition(candidate.element) & 4) winner = candidate;
|
|
23157
|
+
}
|
|
23158
|
+
for (const candidate of entries) candidate.update(candidate === winner);
|
|
23159
|
+
};
|
|
23160
|
+
elect();
|
|
23161
|
+
return () => {
|
|
23162
|
+
entries.delete(entry);
|
|
23163
|
+
elect();
|
|
23164
|
+
if (!entries.size) documents.delete(key3);
|
|
23165
|
+
};
|
|
23166
|
+
}, [anchor, documentId, sessionId]);
|
|
23167
|
+
return owner;
|
|
23168
|
+
}
|
|
23169
|
+
|
|
23123
23170
|
// src/presentation.js
|
|
23124
|
-
var h5 =
|
|
23171
|
+
var h5 = import_react9.default.createElement;
|
|
23125
23172
|
var scanners = /* @__PURE__ */ new WeakMap();
|
|
23126
23173
|
var focused = /* @__PURE__ */ new Map();
|
|
23127
23174
|
function PresentationView({ raw, sessionId, sendPrompt, scope }) {
|
|
23128
|
-
const anchor = (0,
|
|
23129
|
-
(
|
|
23175
|
+
const anchor = (0, import_react9.useRef)(null), records2 = useUiRegistry(), mode = usePresentation(scope), host = markerLocation(raw.documentId);
|
|
23176
|
+
const owner = usePresentationOwner(anchor, raw.documentId, sessionId);
|
|
23177
|
+
(0, import_react9.useEffect)(() => {
|
|
23130
23178
|
rememberUi(raw, sessionId, sendPrompt);
|
|
23131
23179
|
}, [raw, sessionId, sendPrompt]);
|
|
23132
|
-
(0,
|
|
23180
|
+
(0, import_react9.useEffect)(() => {
|
|
23133
23181
|
const root = anchor.current?.closest("[data-conversation-scroll]");
|
|
23134
23182
|
if (!root) return;
|
|
23135
23183
|
let entry = scanners.get(root);
|
|
@@ -23150,31 +23198,31 @@ function PresentationView({ raw, sessionId, sendPrompt, scope }) {
|
|
|
23150
23198
|
}
|
|
23151
23199
|
};
|
|
23152
23200
|
}, [sessionId]);
|
|
23153
|
-
(0,
|
|
23201
|
+
(0, import_react9.useEffect)(() => {
|
|
23154
23202
|
const block = host?.uiMarkerBlock;
|
|
23155
|
-
if (!block) return;
|
|
23203
|
+
if (!block || !owner) return;
|
|
23156
23204
|
const previous = block.style.display;
|
|
23157
23205
|
block.style.display = mode === "inline" ? "none" : previous;
|
|
23158
23206
|
return () => {
|
|
23159
23207
|
block.style.display = previous;
|
|
23160
23208
|
};
|
|
23161
|
-
}, [host, mode]);
|
|
23209
|
+
}, [host, mode, owner]);
|
|
23162
23210
|
const available = panelAvailable();
|
|
23163
|
-
(0,
|
|
23164
|
-
if (mode === "sidebar" && available) openUiPanel(raw.documentId, sessionId);
|
|
23165
|
-
}, [mode, available, sessionId, raw.documentId]);
|
|
23211
|
+
(0, import_react9.useEffect)(() => {
|
|
23212
|
+
if (owner && mode === "sidebar" && available) openUiPanel(raw.documentId, sessionId);
|
|
23213
|
+
}, [mode, available, sessionId, raw.documentId, owner]);
|
|
23166
23214
|
const content = h5(ValidatedView, { raw, fallback: raw.document.fallback, sessionId, sendPrompt });
|
|
23167
23215
|
return h5(
|
|
23168
23216
|
"div",
|
|
23169
23217
|
{ ref: anchor, "data-dgui-presentation": raw.documentId },
|
|
23170
|
-
available && h5("button", { type: "button", className: "dgui-secondary", onClick: () => openUiPanel(raw.documentId, sessionId) }, "\u5728\u4FA7\u680F\u6253\u5F00"),
|
|
23171
|
-
mode === "sidebar" && available ? h5("p", { className: "dgui-copy" }, `${raw.document.title} \xB7 \u5728\u4FA7\u680F\u4E2D\u64CD\u4F5C`) : mode === "inline" && host ? (0, import_react_dom.createPortal)(content, host) : content,
|
|
23172
|
-
mode === "inline" && !host && h5("small", { className: "dgui-copy" }, "\u6B63\u6587\u6807\u8BB0\
|
|
23218
|
+
owner && available && h5("button", { type: "button", className: "dgui-secondary", onClick: () => openUiPanel(raw.documentId, sessionId) }, "\u5728\u4FA7\u680F\u6253\u5F00"),
|
|
23219
|
+
owner && (mode === "sidebar" && available ? h5("p", { className: "dgui-copy" }, `${raw.document.title} \xB7 \u5728\u4FA7\u680F\u4E2D\u64CD\u4F5C`) : mode === "inline" && host ? (0, import_react_dom.createPortal)(content, host) : content),
|
|
23220
|
+
owner && mode === "inline" && !host && h5("small", { className: "dgui-copy" }, "\u672A\u63D0\u4F9B\u6B63\u6587\u6807\u8BB0\uFF0C\u754C\u9762\u663E\u793A\u5728\u56DE\u7B54\u4E0B\u65B9\u3002")
|
|
23173
23221
|
);
|
|
23174
23222
|
}
|
|
23175
23223
|
function UiPanel({ sessionId, sendPrompt }) {
|
|
23176
|
-
const records2 = useUiRegistry().filter((r) => r.sessionId === sessionId), [selected, setSelected] = (0,
|
|
23177
|
-
(0,
|
|
23224
|
+
const records2 = useUiRegistry().filter((r) => r.sessionId === sessionId), [selected, setSelected] = (0, import_react9.useState)(focused.get(sessionId));
|
|
23225
|
+
(0, import_react9.useEffect)(() => subscribeUi(() => setSelected(focused.get(sessionId))), [sessionId]);
|
|
23178
23226
|
const record = records2.find((r) => r.raw.documentId === selected) ?? records2.at(-1);
|
|
23179
23227
|
return h5(
|
|
23180
23228
|
"div",
|
|
@@ -23192,11 +23240,11 @@ function focusUi(id, sessionId) {
|
|
|
23192
23240
|
}
|
|
23193
23241
|
|
|
23194
23242
|
// src/view.js
|
|
23195
|
-
var h6 =
|
|
23243
|
+
var h6 = import_react10.default.createElement;
|
|
23196
23244
|
var display = (value) => value === void 0 || value === null ? "\u2014" : typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
23197
23245
|
function NumericInput({ id, spec, value, onChange, invalidChanged, disabled }) {
|
|
23198
|
-
const [draft, setDraft] = (0,
|
|
23199
|
-
(0,
|
|
23246
|
+
const [draft, setDraft] = (0, import_react10.useState)(String(value)), [invalid, setInvalid] = (0, import_react10.useState)(false);
|
|
23247
|
+
(0, import_react10.useEffect)(() => {
|
|
23200
23248
|
setDraft(String(value));
|
|
23201
23249
|
setInvalid(false);
|
|
23202
23250
|
invalidChanged(id, false);
|
|
@@ -23263,9 +23311,9 @@ function Chart({ node, rows }) {
|
|
|
23263
23311
|
}
|
|
23264
23312
|
function UiView({ record, sessionId, sendPrompt, preview = false }) {
|
|
23265
23313
|
const doc = record.document;
|
|
23266
|
-
const viewId =
|
|
23267
|
-
const root = (0,
|
|
23268
|
-
const collectAudit =
|
|
23314
|
+
const viewId = import_react10.default.useId();
|
|
23315
|
+
const root = (0, import_react10.useRef)(null), canvasAudits = (0, import_react10.useRef)(/* @__PURE__ */ new Map()), [audit, setAudit] = (0, import_react10.useState)(null), auditPending = (0, import_react10.useRef)(0);
|
|
23316
|
+
const collectAudit = import_react10.default.useCallback(() => {
|
|
23269
23317
|
clearTimeout(auditPending.current);
|
|
23270
23318
|
auditPending.current = setTimeout(() => {
|
|
23271
23319
|
if (!root.current || preview) return;
|
|
@@ -23277,7 +23325,7 @@ function UiView({ record, sessionId, sendPrompt, preview = false }) {
|
|
|
23277
23325
|
});
|
|
23278
23326
|
}, 250);
|
|
23279
23327
|
}, [record.documentId, record.documentRevision, sessionId, preview]);
|
|
23280
|
-
(0,
|
|
23328
|
+
(0, import_react10.useEffect)(() => {
|
|
23281
23329
|
canvasAudits.current.clear();
|
|
23282
23330
|
collectAudit();
|
|
23283
23331
|
window.addEventListener("resize", collectAudit);
|
|
@@ -23295,22 +23343,22 @@ function UiView({ record, sessionId, sendPrompt, preview = false }) {
|
|
|
23295
23343
|
collectAudit();
|
|
23296
23344
|
};
|
|
23297
23345
|
const { state, setState, ready, status, issue, epoch, retry, useRemote, keepLocal } = useSavedState(record, sessionId, preview);
|
|
23298
|
-
const [error, setError] = (0,
|
|
23299
|
-
const [busy, setBusy] = (0,
|
|
23300
|
-
const pending = (0,
|
|
23301
|
-
(0,
|
|
23346
|
+
const [error, setError] = (0, import_react10.useState)(""), [actionStatus, setActionStatus] = (0, import_react10.useState)("");
|
|
23347
|
+
const [busy, setBusy] = (0, import_react10.useState)(false), [invalidFields, setInvalidFields] = (0, import_react10.useState)({});
|
|
23348
|
+
const pending = (0, import_react10.useRef)(false);
|
|
23349
|
+
(0, import_react10.useEffect)(() => {
|
|
23302
23350
|
setInvalidFields({});
|
|
23303
23351
|
}, [epoch]);
|
|
23304
|
-
const invalidChanged =
|
|
23352
|
+
const invalidChanged = import_react10.default.useCallback((id, invalid) => setInvalidFields((previous) => previous[id] === invalid ? previous : { ...previous, [id]: invalid }), []);
|
|
23305
23353
|
let computed;
|
|
23306
23354
|
try {
|
|
23307
23355
|
computed = evaluate(doc, state);
|
|
23308
23356
|
} catch (e) {
|
|
23309
23357
|
computed = { error: e.message };
|
|
23310
23358
|
}
|
|
23311
|
-
const currentState = (0,
|
|
23359
|
+
const currentState = (0, import_react10.useRef)(state);
|
|
23312
23360
|
currentState.current = state;
|
|
23313
|
-
(0,
|
|
23361
|
+
(0, import_react10.useEffect)(() => {
|
|
23314
23362
|
collectAudit();
|
|
23315
23363
|
}, [state, ready, collectAudit]);
|
|
23316
23364
|
const updatePatch = (patch) => {
|
|
@@ -23382,7 +23430,7 @@ function UiView({ record, sessionId, sendPrompt, preview = false }) {
|
|
|
23382
23430
|
let control;
|
|
23383
23431
|
if (n.type === "select") control = h6("select", { id, value: spec.options.indexOf(v), disabled: !ready, onChange: (e) => update(k, spec.options[Number(e.target.value)]) }, spec.options.map((o, i) => h6("option", { key: i, value: i }, display(o))));
|
|
23384
23432
|
else if (n.type === "toggle") control = h6("input", { id, className: "dgui-toggle", type: "checkbox", checked: v, disabled: !ready, onChange: (e) => update(k, e.target.checked) });
|
|
23385
|
-
else if (n.type === "slider") control = h6(
|
|
23433
|
+
else if (n.type === "slider") control = h6(import_react10.default.Fragment, null, h6("input", { id, type: "range", min: spec.min, max: spec.max, step: spec.step, value: v, disabled: !ready, onChange: (e) => update(k, Number(e.target.value)) }), h6("output", { htmlFor: id }, display(v)));
|
|
23386
23434
|
else if (spec.type === "number") control = h6(NumericInput, { id, spec, value: v, disabled: !ready, invalidChanged, onChange: (value) => update(k, value) });
|
|
23387
23435
|
else control = h6("input", { id, type: "text", value: v, maxLength: 8192, disabled: !ready, onChange: (e) => update(k, e.target.value) });
|
|
23388
23436
|
return h6("div", { ...props, className: "dgui-control" }, h6("label", { htmlFor: id }, h6(Icon, { name: n.type === "toggle" ? "layers" : n.type === "select" ? "target" : "sliders" }), h6("span", null, n.label)), control);
|
|
@@ -23401,14 +23449,14 @@ function UiView({ record, sessionId, sendPrompt, preview = false }) {
|
|
|
23401
23449
|
return h6(
|
|
23402
23450
|
"section",
|
|
23403
23451
|
{ ref: root, className: "dgui-root", "data-document-revision": record.documentRevision ?? 0, "data-dgui-build": BUILD_INFO.id, "data-dsh-generative-ui": record.documentId, "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 },
|
|
23404
|
-
h6("header", { className: "dgui-header" }, h6("span", { className: "dgui-heading-icon" }, h6(Icon, { name: sceneIcons[doc.presentation?.layout] ?? "sliders" })), h6("strong", { className: "dgui-header-title" }, doc.title), h6("span", { className: "dgui-badge" }, h6(Icon, { name: "sparkles" }), preview ? "\u751F\u6210\u4E2D" : "\
|
|
23452
|
+
h6("header", { className: "dgui-header" }, h6("span", { className: "dgui-heading-icon" }, h6(Icon, { name: sceneIcons[doc.presentation?.layout] ?? (Object.keys(doc.state).length ? "sliders" : "layers") })), h6("strong", { className: "dgui-header-title" }, doc.title), h6("span", { className: "dgui-badge" }, h6(Icon, { name: "sparkles" }), preview ? "\u751F\u6210\u4E2D" : "\u53EF\u89C6\u5316")),
|
|
23405
23453
|
h6("div", { key: epoch, className: "dgui-content" }, doc.nodes.map(renderNode)),
|
|
23406
|
-
h6("div", { className: "dgui-status", role: "status", "data-status": /失败|冲突/.test(status) ? "error" : /正在|等待/.test(status) ? "pending" : "saved" }, h6(Icon, { name: /失败|冲突/.test(status) ? "warning" : /正在|等待/.test(status) ? "database" : "check" }), h6("span", null, status), actionStatus && h6("span", null, ` \xB7 ${actionStatus}`)),
|
|
23454
|
+
(Object.keys(doc.state).length > 0 || issue || actionStatus) && h6("div", { className: "dgui-status", role: "status", "data-status": /失败|冲突/.test(status) ? "error" : /正在|等待/.test(status) ? "pending" : "saved" }, h6(Icon, { name: /失败|冲突/.test(status) ? "warning" : /正在|等待/.test(status) ? "database" : "check" }), h6("span", null, status), actionStatus && h6("span", null, ` \xB7 ${actionStatus}`)),
|
|
23407
23455
|
issue && h6(
|
|
23408
23456
|
"div",
|
|
23409
23457
|
{ className: "dgui-recovery" },
|
|
23410
23458
|
issue.kind === "conflict" ? h6(
|
|
23411
|
-
|
|
23459
|
+
import_react10.default.Fragment,
|
|
23412
23460
|
null,
|
|
23413
23461
|
h6("button", { type: "button", className: "dgui-secondary", disabled: status === "\u6B63\u5728\u6062\u590D\u53C2\u6570\u2026", onClick: useRemote }, "\u4F7F\u7528\u5DF2\u4FDD\u5B58\u53C2\u6570"),
|
|
23414
23462
|
h6("button", { type: "button", className: "dgui-secondary", disabled: status === "\u6B63\u5728\u6062\u590D\u53C2\u6570\u2026", onClick: keepLocal }, "\u4FDD\u7559\u672C\u9875\u53C2\u6570\u5E76\u91CD\u8BD5")
|
|
@@ -23421,7 +23469,7 @@ function UiView({ record, sessionId, sendPrompt, preview = false }) {
|
|
|
23421
23469
|
h6("details", { className: "dgui-fallback" }, h6("summary", null, Object.keys(doc.state).length ? "\u539F\u59CB\u6587\u5B57\u8BF4\u660E\u4E0E\u521D\u59CB\u53C2\u6570" : "\u539F\u59CB\u6587\u5B57\u8BF4\u660E"), h6("p", null, doc.fallback), Object.keys(doc.state).length > 0 && h6("pre", null, JSON.stringify(initialState(doc), null, 2)))
|
|
23422
23470
|
);
|
|
23423
23471
|
}
|
|
23424
|
-
var UiBoundary = class extends
|
|
23472
|
+
var UiBoundary = class extends import_react10.default.Component {
|
|
23425
23473
|
state = { failed: false };
|
|
23426
23474
|
static getDerivedStateFromError() {
|
|
23427
23475
|
return { failed: true };
|
|
@@ -23444,9 +23492,11 @@ function StreamingToolView(props) {
|
|
|
23444
23492
|
return h6(StreamingPreview, { raw, callId: props.callId, sessionId: props.sessionId });
|
|
23445
23493
|
}
|
|
23446
23494
|
function StreamingPreview({ raw, callId = "stream", sessionId }) {
|
|
23447
|
-
const
|
|
23448
|
-
const
|
|
23449
|
-
const
|
|
23495
|
+
const committed = useUiRegistry().some((r) => r.sessionId === sessionId && r.raw.callId === callId);
|
|
23496
|
+
const deferred = import_react10.default.useDeferredValue(raw);
|
|
23497
|
+
const doc = (0, import_react10.useMemo)(() => previewDocument(deferred), [deferred]);
|
|
23498
|
+
const canvas = (0, import_react10.useMemo)(() => doc ? null : previewCanvas(deferred), [doc, deferred]);
|
|
23499
|
+
if (committed) return h6("p", { className: "dgui-copy" }, "\u754C\u9762\u5DF2\u751F\u6210\u3002");
|
|
23450
23500
|
if (canvas) return h6("div", { className: "dgui-root", "data-preview": true }, h6(CanvasPreview, { node: canvas }));
|
|
23451
23501
|
if (!doc) return h6("div", { className: "dgui-pending", role: "status" }, "\u6B63\u5728\u7EC4\u7EC7\u5E76\u6821\u9A8C\u4EA4\u4E92\u754C\u9762\u2026");
|
|
23452
23502
|
return h6(UiBoundary, { key: callId, fallback: doc.fallback }, h6(UiView, { record: { document: doc, documentId: `preview-${callId}` }, sessionId, preview: true }));
|
|
@@ -23455,7 +23505,7 @@ function TurnUiView(props) {
|
|
|
23455
23505
|
return (props.turn.data.get("dshGenerativeUi") ?? []).filter((r) => r.seq < props.seq).map((r) => r.isPatch ? h6(PatchNotice, { key: `patch-${r.seq}`, raw: r.raw }) : h6(PresentationView, { key: r.raw.documentId, ...r, sessionId: props.sessionId, sendPrompt: props.sendPrompt, scope: props.presentationScope }));
|
|
23456
23506
|
}
|
|
23457
23507
|
function PatchNotice({ raw }) {
|
|
23458
|
-
(0,
|
|
23508
|
+
(0, import_react10.useEffect)(() => {
|
|
23459
23509
|
try {
|
|
23460
23510
|
if (raw) publishDocument(raw);
|
|
23461
23511
|
} catch {
|
|
@@ -23465,7 +23515,7 @@ function PatchNotice({ raw }) {
|
|
|
23465
23515
|
}
|
|
23466
23516
|
function ValidatedView({ raw, fallback, sessionId, sendPrompt }) {
|
|
23467
23517
|
const { record: current2, status, retry } = useCurrentDocument(raw ?? {}, sessionId);
|
|
23468
|
-
const result = (0,
|
|
23518
|
+
const result = (0, import_react10.useMemo)(() => {
|
|
23469
23519
|
try {
|
|
23470
23520
|
if (!current2 || current2.version !== 1 || typeof current2.documentId !== "string" || typeof current2.callId !== "string") return null;
|
|
23471
23521
|
return { ...current2, document: validateDocument(current2.document) };
|
|
@@ -23475,7 +23525,7 @@ function ValidatedView({ raw, fallback, sessionId, sendPrompt }) {
|
|
|
23475
23525
|
}, [current2]);
|
|
23476
23526
|
if (!result) return h6("div", { className: "dgui-root" }, h6("p", null, "\u6587\u5B57\u7ED3\u679C\uFF08\u672A\u63D0\u4F9B\u517C\u5BB9\u7684\u754C\u9762\u6570\u636E\uFF09"), h6("pre", null, fallback));
|
|
23477
23527
|
return h6(
|
|
23478
|
-
|
|
23528
|
+
import_react10.default.Fragment,
|
|
23479
23529
|
null,
|
|
23480
23530
|
status === "error" && h6("div", { className: "dgui-root dgui-document-warning", role: "status" }, h6("p", null, "\u672A\u80FD\u786E\u8BA4\u6700\u65B0\u754C\u9762\u7248\u672C\uFF0C\u5F53\u524D\u663E\u793A\u5DF2\u77E5\u7248\u672C\u3002"), h6("button", { type: "button", className: "dgui-secondary", onClick: retry }, "\u91CD\u65B0\u8BFB\u53D6\u6700\u65B0\u754C\u9762")),
|
|
23481
23531
|
status === "loading" && h6("p", { className: "dgui-copy", role: "status" }, "\u6B63\u5728\u8BFB\u53D6\u6700\u65B0\u754C\u9762\u7248\u672C\u2026"),
|
|
@@ -23565,11 +23615,11 @@ body[data-ds-dark-theme] .dgui-root,.dgui-root[data-preview-theme=dark]{--du-acc
|
|
|
23565
23615
|
`;
|
|
23566
23616
|
|
|
23567
23617
|
// src/settings.js
|
|
23568
|
-
var
|
|
23569
|
-
var h7 =
|
|
23618
|
+
var import_react11 = __toESM(require("react"), 1);
|
|
23619
|
+
var h7 = import_react11.default.createElement;
|
|
23570
23620
|
function ModeSettings({ scope }) {
|
|
23571
|
-
const [snapshot, setSnapshot] = (0,
|
|
23572
|
-
(0,
|
|
23621
|
+
const [snapshot, setSnapshot] = (0, import_react11.useState)(() => scope.getSnapshot()), [busy, setBusy] = (0, import_react11.useState)(false), [message, setMessage] = (0, import_react11.useState)(""), pending = (0, import_react11.useRef)(false);
|
|
23622
|
+
(0, import_react11.useEffect)(() => scope.subscribe(() => setSnapshot(scope.getSnapshot())), [scope]);
|
|
23573
23623
|
const ready = snapshot.status === "ready" && snapshot.value !== void 0;
|
|
23574
23624
|
async function change(mode, key3 = "designMode") {
|
|
23575
23625
|
if (pending.current || !ready) return;
|
|
@@ -23611,7 +23661,7 @@ function ModeSettings({ scope }) {
|
|
|
23611
23661
|
var inject = ["slots", "sessions", "conversation", "uiConversation", "configForms"];
|
|
23612
23662
|
function apply(ctx) {
|
|
23613
23663
|
const scope = ctx.configForms.get("motion-ui");
|
|
23614
|
-
ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({ name: "plugins.bundle.config", key: "dsh-motion-ui" }, (props) =>
|
|
23664
|
+
ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({ name: "plugins.bundle.config", key: "dsh-motion-ui" }, (props) => import_react12.default.createElement(ModeSettings, { ...props, scope })));
|
|
23615
23665
|
ctx.uiConversation.events.register(turnUiDefinition);
|
|
23616
23666
|
ctx.slots.inject("conversation.chat.turnTail", () => ctx.slots.register({
|
|
23617
23667
|
name: "conversation.chat.turnTail",
|