a2ui-render-in-dsh 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -5919,7 +5919,7 @@ var KNOWN_COMPONENTS = /* @__PURE__ */ new Set([
5919
5919
  ]);
5920
5920
  var CATALOG_DOC = `# a2ui_render authoring guide
5921
5921
 
5922
- Forms: each question = one field; options -> MultipleChoice/Select; free answers -> TextField (multiline for long); offering suggestions? ALSO add a TextField for a custom answer; several questions = ONE card, ONE submit Button labeled with the action ("\u63D0\u4EA4", "\u786E\u8BA4\u5EFA\u6863"). Multiple items or several charts = ONE call with a Grid. Display-only cards (no Button) are fine. ALWAYS accompany the card with 1-3 sentences of normal prose in your reply (the takeaway, what to look at, or caveats) \u2014 a bare card with no text reads as unfinished; but never restate the card's data in text.
5922
+ Forms: each question = one field; options -> MultipleChoice/Select; free answers -> TextField (multiline for long); offering suggestions? ALSO add a TextField for a custom answer; several questions = ONE card, ONE submit Button labeled with the action ("\u63D0\u4EA4", "\u786E\u8BA4\u5EFA\u6863"). Multiple items or several charts = ONE call with a Grid. Display-only cards (no Button) are fine. Pair every card with 1-3 sentences of prose (takeaway/caveats) \u2014 never restate its data in text.
5923
5923
 
5924
5924
  ## Components (adjacency list; MUST include id "root"; only these names render)
5925
5925
  Layout:
@@ -5928,20 +5928,20 @@ Layout:
5928
5928
  - Card {children, title?} \xB7 List {children, direction?} \xB7 Divider {}
5929
5929
  - Tabs {tabs: ["\u540D"\u2026], children, bind?} \u2014 one child per tab; dataset switching / grouping
5930
5930
  - When {value: {"path": "/x"}, equals?, includes?, notEmpty?, children} \u2014 children render only when the bound value matches; use for follow-up fields (e.g. reason box when choice includes "\u5176\u4ED6")
5931
- - Wizard {steps: ["\u6B65\u9AA4\u540D"\u2026], children, submitLabel?} \u2014 multi-step form: one child (a Column of fields) per step, built-in \u4E0A\u4E00\u6B65/\u4E0B\u4E00\u6B65/progress, the last step's submit sends ALL collected fields at once; use when a form has 5+ questions
5931
+ - Wizard {steps: ["\u6B65\u9AA4\u540D"\u2026], children, submitLabel?} \u2014 multi-step form: one child (Column of fields) per step, built-in prev/next/progress, last step submits ALL fields; use for 5+ questions
5932
5932
  Content (Chart/Mermaid/Image auto-get fullscreen zoom; don't oversize):
5933
5933
  - Text {text, variant?: "h1"|"h2"|"h3"|"body"|"caption"|"strong"}
5934
- - Markdown {text} \u2014 rich long-form text (headings, bold/italic, links, lists, quotes, fenced code, $...$ math); USE instead of stacking Text components for any multi-paragraph explanation inside a card
5934
+ - Markdown {text} \u2014 rich long-form (headings, bold/italic, links, lists, quotes, fenced code, $...$ math); use for any multi-paragraph text instead of stacked Texts
5935
5935
  - Image {url, alt?, width?, height?} \xB7 Tag {text, color?: "blue"|"green"|"red"|"orange"|"gray"}
5936
5936
  - Icon {name, size?, color?} \u2014 check x plus minus warning info star heart calendar clock location user search settings mail phone home file link download upload play music image cart tag gift trophy fire bolt sun moon cloud thumbs-up
5937
5937
  - Math {tex, block?} \u2014 KaTeX. ALL math notation goes here, matrices included: never raw arrays like [[2,1],[0,3]] in Text \u2014 write {"tex": "\\\\begin{pmatrix}2&1\\\\\\\\0&3\\\\end{pmatrix}", "block": true} (row sep \\\\\\\\). Equation above its Anim when animating.
5938
5938
  - Mermaid {code, caption?} \u2014 flowchart ("graph TD; A[\u5F00\u59CB]-->B{\u5224\u65AD}"), mindmap, sequenceDiagram, gantt, pie, stateDiagram
5939
5939
  - Chart {option, height?, functions?, params?, xMin?, xMax?, yClip?} \u2014 ECharts (viewers get a PNG-download button). Data mode: standard option verbatim. Function mode: functions=[{"expr":"tan(x)","name"?}], xMin/xMax (\xB110), yClip (10); sampled automatically, asymptotes break \u2014 NEVER hand-enumerate curve points. Ops: + - * / ^ %, sin cos tan cot sec csc asin acos atan sinh cosh tanh sqrt cbrt abs exp ln log2 log10 floor ceil round sign min max pow atan2, pi/e, x. params: {"mu":{"path":"/mu"}} injects bound values as constants \u2014 pair with Slider for live curves.
5940
- - Table {columns, rows, caption?, sortable?, filter?, pageSize?} \u2014 any 2+ item attribute comparison beats prose. Interactive by default: click-to-sort headers, auto filter box and pagination on long data, copy/CSV export built in. Dict binding: rows={"source":{"k1":[[\u2026]],\u2026},"pick":{"path":"/k"}} + a Select/Tabs on that path switches datasets live.
5940
+ - Table {columns, rows, caption?, sortable?, filter?, pageSize?} \u2014 for any 2+ item comparison; sort/filter/pagination/copy/CSV are automatic. Dict binding: rows={"source":{"k1":[[\u2026]],\u2026},"pick":{"path":"/k"}} + a Select/Tabs on that path switches datasets live.
5941
5941
  - Stat {label, value, unit?, trend?, hint?} \u2014 KPI tile ("+12%" green / "-3%" red); several in a Grid
5942
5942
  - Steps {items: [{title, description?, status?: "done"|"current"|"pending"}]} \u2014 linear procedures; Mermaid flowchart only when it BRANCHES
5943
5943
  - Progress {value, max?, label?} \xB7 Timeline {items: [{time?, title, description?}]} (past events; Steps = to-dos)
5944
- - CodeBlock {code, language?, title?} \u2014 always for code snippets
5944
+ - CodeBlock {code, language?, title?} \u2014 ALWAYS for code, including quiz/form stems: compose Text(\u9898\u5E72) + CodeBlock(\u4EE3\u7801) + inputs; NEVER inline code in a Text prop
5945
5945
  - Video {url, poster?\u2026} \xB7 Audio {url, title?} \xB7 Flashcard {front, back} \xB7 Countdown {to?: "2026-09-01 10:00", seconds?, label?}
5946
5946
  - Map {data: [{name: "\u5E7F\u4E1C", value: 100}\u2026], title?, unit?, height?} \u2014 China province choropleth (regional sales/distribution); province names in Chinese
5947
5947
  - ImageCompare {before, after} \u2014 drag-divider image comparison (before/after, A/B)
@@ -5955,15 +5955,15 @@ Interaction:
5955
5955
  - Select {options, bind, label?, placeholder?, multiple?, maxAllowedSelections?, disabled?} \u2014 dropdown; single stores value, multiple stores array
5956
5956
  - Rate {bind, label?, max?, disabled?} \u2014 stars 1..max (5)
5957
5957
  - Slider {bind, label?, min?, max?, step?, unit?, disabled?} \u2014 numbers in a range; parameter exploration with Chart params
5958
- - Calc {expr, inputs: {name: {"path": "/x"}\u2026}, out, digits?} \u2014 invisible derived number written at out; chainable (one Calc's out feeds another's inputs). digits rounds DISPLAY values only \u2014 omit on intermediate results (rates, ratios) to keep full precision
5958
+ - Calc {expr, inputs: {name: {"path": "/x"}\u2026}, out, digits?} \u2014 invisible derived number written at out; chainable. digits rounds display only \u2014 omit on intermediates to keep precision
5959
5959
  - CheckBox {label, bind, disabled?} \xB7 TextField {label?, placeholder?, multiline?, kind?: "text"|"number"|"date"|"time", bind, disabled?}
5960
- - Upload {bind?, label?, max?} \u2014 image picker; chosen images are SENT TO YOU with the submission (screenshots, photos, receipts \u2014 use whenever seeing an image would help)
5961
- - Calendar {bind, label?, min?, max?} \u2014 month-view date picker (friendlier than kind:"date" for choosing among nearby dates)
5960
+ - Upload {bind?, label?, max?} \u2014 image picker; chosen images are SENT TO YOU with the submission (use whenever seeing an image helps)
5961
+ - Calendar {bind, label?, min?, max?, range?} \u2014 month-view date picker; range: true picks start+end (value ["\u8D77","\u6B62"])
5962
5962
  - RankList {items: ["\u9009\u9879"\u2026], bind, label?} \u2014 user reorders by priority; submits the ordered list
5963
5963
  - Signature {label?} \u2014 handwritten signature pad; the drawing is sent to you as an image
5964
5964
  - EditableTable {columns, rows, bind, label?} \u2014 user edits cells, the whole grid submits at bind
5965
- - Suggestions {items: ["\u8FFD\u95EE1", "\u8FFD\u95EE2"\u2026]} \u2014 tappable follow-up questions below an answer; tapping sends that question as the user's message. ADD 2-4 to display-only cards when natural next questions exist
5966
- Preselect by seeding dataModel at bind paths; disable via disabled on a control or an option.
5965
+ - Suggestions {items: ["\u8FFD\u95EE1"\u2026]} \u2014 tappable follow-ups; a tap sends that question as the user's message. ADD 2-4 to display-only cards
5966
+ Preselect by seeding dataModel at bind paths; disable via disabled on a control or option. Any input takes required: true \u2014 submit is blocked (field highlighted) until filled; give required fields labels.
5967
5967
 
5968
5968
  ## Reactivity
5969
5969
  Bindings are live: inputs write bind paths, every {"path"} display binding updates instantly.
@@ -5974,7 +5974,7 @@ Bindings are live: inputs write bind paths, every {"path"} display binding updat
5974
5974
  Seed every bound path in dataModel.
5975
5975
 
5976
5976
  ## Scenario map
5977
- Input collection -> form (TextField/kind date|time|number, MultipleChoice 2-7 options, Select for long lists, CheckBox, Rate). Decisions -> Grid Cards + Table; lone confirm Button gets submit:true. Data -> Chart (trend/rank/share) + Stat tiles + Table. Learning -> MultipleChoice quizzes (options may be formulas), Math, Chart functions (+Slider), Anim, mindmap, Flashcard, CodeBlock. Procedures -> Steps (Timeline for history, Progress for completion, gantt/Table for schedules). Entertainment -> Grid recommendation cards, Rate, polls via MultipleChoice.
5977
+ Input collection -> form (TextField/kind date|time|number, MultipleChoice 2-7 options, Select for long lists, CheckBox, Rate). Decisions -> Grid Cards + Table; lone confirm Button gets submit:true. Data -> Chart (trend/rank/share) + Stat tiles + Table; regional -> Map. Learning -> MultipleChoice quizzes (options may be formulas), Math, Chart functions (+Slider), Anim, mindmap, Flashcard; \u8BFB\u4EE3\u7801\u9898 = Text(\u9898\u5E72) + CodeBlock(\u4EE3\u7801) + TextField. Procedures -> Steps (Timeline for history, Progress for completion, gantt/Table for schedules). Entertainment -> Grid recommendation cards, Rate, polls via MultipleChoice.
5978
5978
 
5979
5979
  ## Inline math
5980
5980
  Any text prop (Text, option labels/descriptions, Table cells, Steps, Flashcard, Anim notes) embeds formulas with $...$, e.g. option {"label": "$\\\\frac{x^2}{2}+C$"}. Math component = display equations; $...$ = math inside sentences/choices. In math quizzes, option labels with fractions/roots/integrals/exponents SHOULD be $...$ formulas (e.g. {"label": "$3x^2$"}), not Unicode approximations.
@@ -5983,15 +5983,15 @@ Any text prop (Text, option labels/descriptions, Table cells, Steps, Flashcard,
5983
5983
  "bind" = write path WITHOUT leading slash ("answer", "form/name"). Display reads use {"path": "/answer"} (WITH slash). Avoid literal string props starting with "/".
5984
5984
 
5985
5985
  ## Submission
5986
- Button click sends a plain-language user message: button label + chosen values ("\u63D0\u4EA4\u7B54\u6848\uFF1AB\u3001C"; multi-line for forms); MultipleChoice arrives as option LABELS. action.event.context optional (touched fields auto-included); entries may be literals or {"path": "/x", "label": "\u540D"}. Locking (submitMode "once", default): cards WITH inputs lock after submit and stay recorded; input-less cards' buttons are query buttons, always clickable; per-Button submit: true|false overrides; "multi" never locks. After an interactive card, end your turn with one short line; a plain typed reply also counts as the answer. Uploaded images/signatures arrive attached to the submission message. Users can hit \u91CD\u65B0\u586B\u5199 on a locked card \u2014 the corrected submission is prefixed \uFF08\u4FEE\u6B63\uFF09.
5986
+ Button click sends a plain-language user message: button label + chosen values ("\u63D0\u4EA4\u7B54\u6848\uFF1AB\u3001C"; multi-line for forms); MultipleChoice arrives as option LABELS. action.event.context optional (touched fields auto-included); entries may be literals or {"path": "/x", "label": "\u540D"}. Locking (submitMode "once", default): cards WITH inputs lock after submit and stay recorded; input-less cards' buttons are query buttons, always clickable; per-Button submit: true|false overrides; "multi" never locks. A plain typed reply also counts as the answer. Uploads/signatures arrive attached to the submission. \u91CD\u65B0\u586B\u5199 unlocks a locked card; corrections arrive prefixed \uFF08\u4FEE\u6B63\uFF09.
5987
5987
 
5988
5988
  ## Updating a rendered card
5989
- a2ui_render returns a surfaceId. Call a2ui_update {surfaceId, components?, dataModel?} to change that card IN PLACE \u2014 replace/add components by id, overwrite dataModel keys. Use for long-task progress (render a Progress card first, then update /percent as you work), corrections, and appending results \u2014 never render a duplicate card for the same content. Cards render progressively while you stream \u2014 no need to keep them small.
5989
+ a2ui_render returns a surfaceId. Call a2ui_update {surfaceId, components?, dataModel?} to change that card IN PLACE \u2014 replace/add components by id, overwrite dataModel keys. Use for long-task progress, corrections, appended results \u2014 never render a duplicate card. Cards stream progressively; don't shrink them.
5990
5990
 
5991
5991
  ## Example (quiz)
5992
5992
  components=[{"id":"root","component":"Column","children":["q","opts","btn"]},{"id":"q","component":"Text","variant":"h3","text":"\u2026\u9898\u76EE\u2026"},{"id":"opts","component":"MultipleChoice","bind":"answer","maxAllowedSelections":1,"options":[{"label":"A\u2026","value":"A"},{"label":"B\u2026","value":"B"}]},{"id":"btn","component":"Button","label":"\u63D0\u4EA4\u7B54\u6848","variant":"primary","action":{"event":{"name":"submit"}}}]
5993
5993
  dataModel={"answer":[]}`;
5994
- var RENDER_DESCRIPTION = "Render a UI card (A2UI) inline in the chat. Judge EVERY reply semantically from user intent (they never say \u5361\u7247/\u56FE\u8868/\u52A8\u753B): (1) UTILITY \u2014 would a card serve any of UI's three purposes better than prose? ACT \u64CD\u4F5C: the user must answer/choose/fill/adjust/rate/confirm -> form. BROWSE \u6D4F\u89C8: the user wants to see/scan data or many items \u2014 statistics, rankings, distributions, trends, comparisons, or picking among several recommended items \u2014 movies/products/places/plans -> one Card per item in a Grid, or a Table (\u770B/\u67E5/\u5BF9\u6BD4/\u63A8\u8350\u51E0\u4E2A IS a card ask; dated or approximate data never cancels the card \u2014 chart the best you know, label the period, caveat in prose). UNDERSTAND \u7406\u89E3: structure, notation or MOTION aids comprehension \u2014 math, code, flows, schedules, stepwise processes, what-if exploration, and anything that unfolds over time (algorithms, simulations, physical processes) -> step animation. FEEDBACK \u53CD\u9988: before starting multi-step work (3+ tool steps), render a Progress/Steps card, a2ui_update it as steps complete, and put the final results in the card \u2014 never as a markdown table in prose. None of these (opinions, narration, short answers, translations, chat) -> prose. (2) FORM \u2014 fit each part and compose freely in one card: input->form, comparison->table, trend->chart, regional data->map, math->formula, code->code block, procedure->steps, relations->diagram, watchable process->animation, what-if->sliders+live chart. Cards PAIR with prose, never replace it: give your takeaway/analysis in 1-3 sentences of normal text and keep structured content in the card; don't duplicate data in both. Before your FIRST call in a conversation, call a2ui_catalog and follow it \u2014 never guess component names. After an interactive card, end your turn; the submission arrives as a plain-language user message.";
5994
+ var RENDER_DESCRIPTION = "Render a UI card (A2UI) inline in the chat. Judge EVERY reply from user intent (they never say \u5361\u7247/\u56FE\u8868): would a card serve any of UI's FOUR purposes better than prose? ACT \u64CD\u4F5C: user must answer/choose/fill/adjust/confirm -> form card. BROWSE \u6D4F\u89C8: user wants to see/scan data or items \u2014 statistics, rankings, distributions, trends, comparisons, multi-item recommendations -> chart/table/map/Grid of item cards; \u770B/\u67E5/\u5BF9\u6BD4/\u63A8\u8350\u51E0\u4E2A IS a card ask, and dated or approximate data never cancels the card \u2014 chart the best known, label the period, caveat in prose. UNDERSTAND \u7406\u89E3: structure, notation or motion aids comprehension \u2014 math, code, flows, what-if exploration, and time-unfolding processes (algorithms, simulations) -> step animation. FEEDBACK \u53CD\u9988: before multi-step work (3+ tool steps), render a Progress/Steps card, a2ui_update it as steps complete, final results into the card (not markdown tables in prose). None of the four (opinions, narration, short answers, translations, chat) -> prose. Compose freely \u2014 one card can mix form/chart/table/formula parts. Cards PAIR with prose: 1-3 sentences of takeaway as normal text, structured content in the card, no duplication. Call a2ui_catalog BEFORE your first render in a conversation \u2014 never guess component names. After an interactive card, end your turn; the submission arrives as a plain-language user message.";
5995
5995
  var CATALOG_DESCRIPTION = "Returns the full component catalog and authoring rules for a2ui_render. Call ONCE before your first render in a conversation; the result stays in context \u2014 call again only if it is no longer visible.";
5996
5996
  function apply(ctx) {
5997
5997
  ctx.tools.register(defineTool({
@@ -6045,13 +6045,21 @@ function apply(ctx) {
6045
6045
  }
6046
6046
  const byId = new Map(components.filter((n) => n !== null && typeof n === "object" && n.id !== void 0).map((n) => [n.id, n]));
6047
6047
  const reachable = /* @__PURE__ */ new Set();
6048
+ const missing = /* @__PURE__ */ new Set();
6048
6049
  const queue = ["root"];
6049
6050
  while (queue.length > 0) {
6050
6051
  const id = queue.pop();
6051
6052
  if (reachable.has(id)) continue;
6052
6053
  reachable.add(id);
6053
6054
  const node = byId.get(id);
6054
- for (const child of Array.isArray(node?.children) ? node.children : []) queue.push(child);
6055
+ if (node === void 0) {
6056
+ missing.add(id);
6057
+ continue;
6058
+ }
6059
+ for (const child of Array.isArray(node.children) ? node.children : []) queue.push(child);
6060
+ }
6061
+ if (missing.size > 0) {
6062
+ throw new Error(`a2ui_render: children reference undefined component id(s) ${[...missing].map((id) => `"${id}"`).join(", ")} \u2014 define them or fix the reference.`);
6055
6063
  }
6056
6064
  const orphans = [...byId.keys()].filter((id) => !reachable.has(id));
6057
6065
  if (orphans.length > 0) {
@@ -6070,7 +6078,7 @@ function apply(ctx) {
6070
6078
  }));
6071
6079
  ctx.tools.register(defineTool({
6072
6080
  name: "a2ui_update",
6073
- description: "Update a card you previously rendered, IN PLACE: pass the surfaceId returned by a2ui_render, plus replacement/new components (matched by id) and/or dataModel value changes. Use for progress updates during long tasks, corrections, and appending content \u2014 instead of rendering a duplicate card.",
6081
+ description: "Update a rendered card IN PLACE by its surfaceId (from a2ui_render): components replace/add by id, dataModel overwrites keys. For progress, corrections, appended results \u2014 never render a duplicate card.",
6074
6082
  parameters: {
6075
6083
  surfaceId: { type: "string", required: true, description: "The surfaceId returned by the a2ui_render call to update." },
6076
6084
  components: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "a2ui-render-in-dsh",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "A2UI interactive cards for the dsh web UI: the agent renders clickable interfaces (quizzes, forms, product cards) via @ant-design/x-card, and user interactions flow back to the agent as answers.",
5
5
  "author": "sondge <fengshangjie@lilybearing.com>",
6
6
  "license": "MIT",
@@ -52,15 +52,18 @@
52
52
  "scripts": {
53
53
  "build": "node scripts/build.mjs",
54
54
  "watch": "node scripts/build.mjs --watch",
55
- "prepublishOnly": "npm run build"
55
+ "prepublishOnly": "npm run build",
56
+ "test": "node test/run.mjs"
56
57
  },
57
58
  "devDependencies": {
58
59
  "@ant-design/x-card": "^2.9.0",
59
60
  "@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
60
61
  "echarts": "^6.1.0",
61
62
  "esbuild": "^0.25.0",
63
+ "jsdom": "^30.0.1",
62
64
  "katex": "^0.18.4",
63
65
  "mermaid": "^11.17.0",
66
+ "playwright": "^1.62.1",
64
67
  "react": "^18.2.0",
65
68
  "react-dom": "^18.2.0"
66
69
  },
@@ -75,6 +78,5 @@
75
78
  "publishConfig": {
76
79
  "registry": "https://registry.npmjs.org/",
77
80
  "access": "public"
78
- },
79
- "dependencies": {}
81
+ }
80
82
  }