a2ui-render-in-dsh 0.1.1 → 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/README.md +91 -37
- package/README.zh.md +90 -36
- package/lib/client.js +781 -549
- package/lib/index.js +149 -58
- package/package.json +6 -4
package/lib/index.js
CHANGED
|
@@ -5878,66 +5878,121 @@ var KNOWN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
5878
5878
|
"Card",
|
|
5879
5879
|
"List",
|
|
5880
5880
|
"Divider",
|
|
5881
|
+
"Tabs",
|
|
5882
|
+
"When",
|
|
5883
|
+
"Wizard",
|
|
5881
5884
|
"Text",
|
|
5885
|
+
"Markdown",
|
|
5882
5886
|
"Image",
|
|
5887
|
+
"Icon",
|
|
5883
5888
|
"Tag",
|
|
5884
5889
|
"Math",
|
|
5885
5890
|
"Mermaid",
|
|
5886
5891
|
"Chart",
|
|
5892
|
+
"Map",
|
|
5887
5893
|
"Video",
|
|
5894
|
+
"Audio",
|
|
5888
5895
|
"Anim",
|
|
5896
|
+
"Table",
|
|
5897
|
+
"Stat",
|
|
5898
|
+
"Steps",
|
|
5899
|
+
"Progress",
|
|
5900
|
+
"Timeline",
|
|
5901
|
+
"CodeBlock",
|
|
5902
|
+
"Flashcard",
|
|
5903
|
+
"Countdown",
|
|
5904
|
+
"ImageCompare",
|
|
5889
5905
|
"Button",
|
|
5890
5906
|
"MultipleChoice",
|
|
5891
5907
|
"Select",
|
|
5908
|
+
"Rate",
|
|
5909
|
+
"Slider",
|
|
5892
5910
|
"CheckBox",
|
|
5893
|
-
"TextField"
|
|
5911
|
+
"TextField",
|
|
5912
|
+
"Calc",
|
|
5913
|
+
"Upload",
|
|
5914
|
+
"Suggestions",
|
|
5915
|
+
"Calendar",
|
|
5916
|
+
"RankList",
|
|
5917
|
+
"Signature",
|
|
5918
|
+
"EditableTable"
|
|
5894
5919
|
]);
|
|
5895
5920
|
var CATALOG_DOC = `# a2ui_render authoring guide
|
|
5896
5921
|
|
|
5897
|
-
|
|
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.
|
|
5898
5923
|
|
|
5899
|
-
## Components (
|
|
5924
|
+
## Components (adjacency list; MUST include id "root"; only these names render)
|
|
5900
5925
|
Layout:
|
|
5901
5926
|
- Column / Row {children, gap?}
|
|
5902
|
-
- Grid {children, columns?, gap?, minWidth?} \u2014 side-by-side cards
|
|
5903
|
-
- Card {children, title?}
|
|
5904
|
-
-
|
|
5905
|
-
-
|
|
5906
|
-
|
|
5927
|
+
- Grid {children, columns?, gap?, minWidth?} \u2014 side-by-side cards, 2x2 dashboards (columns 2-4, omit for auto-fit)
|
|
5928
|
+
- Card {children, title?} \xB7 List {children, direction?} \xB7 Divider {}
|
|
5929
|
+
- Tabs {tabs: ["\u540D"\u2026], children, bind?} \u2014 one child per tab; dataset switching / grouping
|
|
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 (Column of fields) per step, built-in prev/next/progress, last step submits ALL fields; use for 5+ questions
|
|
5932
|
+
Content (Chart/Mermaid/Image auto-get fullscreen zoom; don't oversize):
|
|
5907
5933
|
- Text {text, variant?: "h1"|"h2"|"h3"|"body"|"caption"|"strong"}
|
|
5908
|
-
-
|
|
5909
|
-
- Tag {text, color?: "blue"|"green"|"red"|"orange"|"gray"}
|
|
5910
|
-
-
|
|
5911
|
-
-
|
|
5912
|
-
-
|
|
5913
|
-
-
|
|
5914
|
-
-
|
|
5915
|
-
|
|
5916
|
-
|
|
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
|
+
- Image {url, alt?, width?, height?} \xB7 Tag {text, color?: "blue"|"green"|"red"|"orange"|"gray"}
|
|
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
|
+
- 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
|
+
- Mermaid {code, caption?} \u2014 flowchart ("graph TD; A[\u5F00\u59CB]-->B{\u5224\u65AD}"), mindmap, sequenceDiagram, gantt, pie, stateDiagram
|
|
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 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
|
+
- Stat {label, value, unit?, trend?, hint?} \u2014 KPI tile ("+12%" green / "-3%" red); several in a Grid
|
|
5942
|
+
- Steps {items: [{title, description?, status?: "done"|"current"|"pending"}]} \u2014 linear procedures; Mermaid flowchart only when it BRANCHES
|
|
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, including quiz/form stems: compose Text(\u9898\u5E72) + CodeBlock(\u4EE3\u7801) + inputs; NEVER inline code in a Text prop
|
|
5945
|
+
- Video {url, poster?\u2026} \xB7 Audio {url, title?} \xB7 Flashcard {front, back} \xB7 Countdown {to?: "2026-09-01 10:00", seconds?, label?}
|
|
5946
|
+
- Map {data: [{name: "\u5E7F\u4E1C", value: 100}\u2026], title?, unit?, height?} \u2014 China province choropleth (regional sales/distribution); province names in Chinese
|
|
5947
|
+
- ImageCompare {before, after} \u2014 drag-divider image comparison (before/after, A/B)
|
|
5948
|
+
- Anim {frames, interval?, height?, autoplay?} \u2014 algorithm animation; plays ONCE, user replays via controls. Form auto-detected from frame shape; simulate the algorithm yourself, one frame per step with a "note" caption, FULL state each frame:
|
|
5949
|
+
bars (sorting/searching, 8-12 numbers): {"data":[5,3,8,1],"highlight":[0,1],"sorted":[3],"note":"\u2026"} \u2014 highlight=orange, sorted=green
|
|
5950
|
+
grid (matrix/DP): {"grids":[{"title":"A","data":[[1,2],[3,4]],"highlight":[[0,0]]},{"title":"C","data":[[17,null]],"accent":[[0,0]]}],"note":"\u2026"} \u2014 highlight=read(orange), accent=write(green), null=empty; single grid: {"grid":[[\u2026]],\u2026}
|
|
5951
|
+
graph (BFS/DFS/trees): {"graph":{"nodes":[{"id":"A","label"?,"x"?,"y"?,"state"?:"active"|"seen"|"done"}],"edges":[["A","B","active"|"done"?]]},"note":"\u2026"} \u2014 no x/y (0-100) = auto circle; active=orange, seen=blue, done=green
|
|
5917
5952
|
Interaction:
|
|
5918
5953
|
- Button {label, variant?: "primary"|"default"|"danger", submit?, action: {event: {name, context?}}}
|
|
5919
|
-
- MultipleChoice {options: [{label, value, description?, disabled?}], bind, maxAllowedSelections?, disabled?} \u2014
|
|
5920
|
-
- Select {options
|
|
5921
|
-
-
|
|
5922
|
-
-
|
|
5923
|
-
|
|
5954
|
+
- MultipleChoice {options: [{label, value, description?, disabled?}], bind, maxAllowedSelections?, disabled?} \u2014 array at bind; max 1 = single-select
|
|
5955
|
+
- Select {options, bind, label?, placeholder?, multiple?, maxAllowedSelections?, disabled?} \u2014 dropdown; single stores value, multiple stores array
|
|
5956
|
+
- Rate {bind, label?, max?, disabled?} \u2014 stars 1..max (5)
|
|
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. digits rounds display only \u2014 omit on intermediates to keep precision
|
|
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 (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
|
+
- RankList {items: ["\u9009\u9879"\u2026], bind, label?} \u2014 user reorders by priority; submits the ordered list
|
|
5963
|
+
- Signature {label?} \u2014 handwritten signature pad; the drawing is sent to you as an image
|
|
5964
|
+
- EditableTable {columns, rows, bind, label?} \u2014 user edits cells, the whole grid submits at bind
|
|
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
|
+
|
|
5968
|
+
## Reactivity
|
|
5969
|
+
Bindings are live: inputs write bind paths, every {"path"} display binding updates instantly.
|
|
5970
|
+
- Follow-ups: MultipleChoice bind:"a" + When includes:"\u5176\u4ED6" wrapping a TextField
|
|
5971
|
+
- Exploration: Slider bind:"mu" + Chart params:{"mu":{"path":"/mu"}}
|
|
5972
|
+
- Calculators: Sliders -> Calc {expr, inputs, out:"monthly"} -> Stat value:{"path":"/monthly"}
|
|
5973
|
+
- Dataset switch: Tabs panes, or Table dict binding
|
|
5974
|
+
Seed every bound path in dataModel.
|
|
5975
|
+
|
|
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; 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
|
+
|
|
5979
|
+
## Inline math
|
|
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.
|
|
5924
5981
|
|
|
5925
5982
|
## Data binding
|
|
5926
|
-
|
|
5927
|
-
- Display props may read live values with {"path": "/answer"} (WITH leading slash).
|
|
5928
|
-
- "dataModel" seeds initial values. Avoid literal string props that start with "/".
|
|
5983
|
+
"bind" = write path WITHOUT leading slash ("answer", "form/name"). Display reads use {"path": "/answer"} (WITH slash). Avoid literal string props starting with "/".
|
|
5929
5984
|
|
|
5930
5985
|
## Submission
|
|
5931
|
-
|
|
5932
|
-
- action.event.context is optional (touched fields are included automatically); entries may be literals or {"path": "/x", "label": "\u5B57\u6BB5\u540D"}.
|
|
5933
|
-
- Locking: under submitMode "once" (default), a click locks the card ONLY when the card has input components (MultipleChoice/CheckBox/TextField) \u2014 a form/quiz submits once and stays recorded. Buttons on cards WITHOUT inputs (product \u67E5\u770B\u8BE6\u60C5, menus) are query buttons and stay clickable. Override per Button with submit: true (always lock \u2014 e.g. a lone confirm button) or submit: false (never lock). submitMode "multi" disables locking entirely.
|
|
5934
|
-
- After rendering an interactive card, end your turn with one short line telling the user to use the card. If the user types a plain reply instead, accept it as the answer.
|
|
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.
|
|
5935
5987
|
|
|
5936
|
-
##
|
|
5937
|
-
|
|
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, corrections, appended results \u2014 never render a duplicate card. Cards stream progressively; don't shrink them.
|
|
5990
|
+
|
|
5991
|
+
## Example (quiz)
|
|
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"}}}]
|
|
5938
5993
|
dataModel={"answer":[]}`;
|
|
5939
|
-
var RENDER_DESCRIPTION = "Render a UI card (A2UI) inline in the chat:
|
|
5940
|
-
var CATALOG_DESCRIPTION = "Returns the
|
|
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
|
+
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.";
|
|
5941
5996
|
function apply(ctx) {
|
|
5942
5997
|
ctx.tools.register(defineTool({
|
|
5943
5998
|
name: "a2ui_catalog",
|
|
@@ -5960,33 +6015,15 @@ function apply(ctx) {
|
|
|
5960
6015
|
name: "a2ui_render",
|
|
5961
6016
|
description: RENDER_DESCRIPTION,
|
|
5962
6017
|
parameters: {
|
|
5963
|
-
title: {
|
|
5964
|
-
type: "string",
|
|
5965
|
-
description: "Card title (optional)."
|
|
5966
|
-
},
|
|
6018
|
+
title: { type: "string", description: "Card title." },
|
|
5967
6019
|
components: {
|
|
5968
6020
|
type: "array",
|
|
5969
6021
|
required: true,
|
|
5970
|
-
description: 'A2UI
|
|
5971
|
-
items: {
|
|
5972
|
-
type: "object",
|
|
5973
|
-
additionalProperties: true,
|
|
5974
|
-
properties: {
|
|
5975
|
-
id: { type: "string", required: true },
|
|
5976
|
-
component: { type: "string", required: true },
|
|
5977
|
-
children: { type: "array", items: { type: "string" } }
|
|
5978
|
-
}
|
|
5979
|
-
}
|
|
5980
|
-
},
|
|
5981
|
-
dataModel: {
|
|
5982
|
-
type: "object",
|
|
5983
|
-
additionalProperties: true,
|
|
5984
|
-
description: "Initial data model (optional)."
|
|
6022
|
+
description: 'A2UI adjacency list incl. a node with id "root"; see a2ui_catalog.',
|
|
6023
|
+
items: { type: "object", additionalProperties: true }
|
|
5985
6024
|
},
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
description: '"once" (default, locks after submit) or "multi" \u2014 see a2ui_catalog.'
|
|
5989
|
-
}
|
|
6025
|
+
dataModel: { type: "object", additionalProperties: true, description: "Initial values." },
|
|
6026
|
+
submitMode: { type: "string", description: '"once" (default) | "multi".' }
|
|
5990
6027
|
},
|
|
5991
6028
|
output: {
|
|
5992
6029
|
schema: {
|
|
@@ -5994,27 +6031,81 @@ function apply(ctx) {
|
|
|
5994
6031
|
additionalProperties: false,
|
|
5995
6032
|
properties: {
|
|
5996
6033
|
status: { type: "string", required: true },
|
|
6034
|
+
surfaceId: { type: "string" },
|
|
5997
6035
|
hint: { type: "string" }
|
|
5998
6036
|
}
|
|
5999
6037
|
},
|
|
6000
6038
|
render: (_args, value) => [{ type: "text", text: JSON.stringify(value) }]
|
|
6001
6039
|
},
|
|
6002
6040
|
isConcurrencySafe: () => true,
|
|
6003
|
-
async execute(args) {
|
|
6041
|
+
async execute(args, exec) {
|
|
6004
6042
|
const components = Array.isArray(args.components) ? args.components : [];
|
|
6005
6043
|
if (!components.some((node) => node !== null && typeof node === "object" && node.id === "root")) {
|
|
6006
6044
|
throw new Error('a2ui_render: components must include a node with id "root". Call a2ui_catalog for the authoring guide.');
|
|
6007
6045
|
}
|
|
6046
|
+
const byId = new Map(components.filter((n) => n !== null && typeof n === "object" && n.id !== void 0).map((n) => [n.id, n]));
|
|
6047
|
+
const reachable = /* @__PURE__ */ new Set();
|
|
6048
|
+
const missing = /* @__PURE__ */ new Set();
|
|
6049
|
+
const queue = ["root"];
|
|
6050
|
+
while (queue.length > 0) {
|
|
6051
|
+
const id = queue.pop();
|
|
6052
|
+
if (reachable.has(id)) continue;
|
|
6053
|
+
reachable.add(id);
|
|
6054
|
+
const node = byId.get(id);
|
|
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.`);
|
|
6063
|
+
}
|
|
6064
|
+
const orphans = [...byId.keys()].filter((id) => !reachable.has(id));
|
|
6065
|
+
if (orphans.length > 0) {
|
|
6066
|
+
throw new Error(`a2ui_render: component(s) ${orphans.map((id) => `"${id}"`).join(", ")} are defined but not reachable from "root" \u2014 add them to a parent's children or remove them.`);
|
|
6067
|
+
}
|
|
6008
6068
|
const unknown = [...new Set(components.map((node) => node?.component).filter((component) => typeof component === "string" && !KNOWN_COMPONENTS.has(component)))];
|
|
6009
6069
|
if (unknown.length > 0) {
|
|
6010
6070
|
throw new Error(`a2ui_render: unknown component(s) ${unknown.map((component) => `"${component}"`).join(", ")} \u2014 nothing would render. Call a2ui_catalog for the list of valid components.`);
|
|
6011
6071
|
}
|
|
6012
6072
|
return {
|
|
6073
|
+
surfaceId: String(exec.callId),
|
|
6013
6074
|
status: "presented",
|
|
6014
|
-
hint: "Card rendered
|
|
6075
|
+
hint: "Card rendered. The user's action arrives as the next user message; end your turn if waiting."
|
|
6015
6076
|
};
|
|
6016
6077
|
}
|
|
6017
6078
|
}));
|
|
6079
|
+
ctx.tools.register(defineTool({
|
|
6080
|
+
name: "a2ui_update",
|
|
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.",
|
|
6082
|
+
parameters: {
|
|
6083
|
+
surfaceId: { type: "string", required: true, description: "The surfaceId returned by the a2ui_render call to update." },
|
|
6084
|
+
components: {
|
|
6085
|
+
type: "array",
|
|
6086
|
+
description: "Components to add or replace (matched by id); same schema as a2ui_render.",
|
|
6087
|
+
items: { type: "object", additionalProperties: true }
|
|
6088
|
+
},
|
|
6089
|
+
dataModel: { type: "object", additionalProperties: true, description: "Data model values to overwrite, keyed by top-level path." }
|
|
6090
|
+
},
|
|
6091
|
+
output: {
|
|
6092
|
+
schema: {
|
|
6093
|
+
type: "object",
|
|
6094
|
+
additionalProperties: false,
|
|
6095
|
+
properties: { status: { type: "string", required: true } }
|
|
6096
|
+
},
|
|
6097
|
+
render: (_args, value) => [{ type: "text", text: JSON.stringify(value) }]
|
|
6098
|
+
},
|
|
6099
|
+
isConcurrencySafe: () => true,
|
|
6100
|
+
async execute(args) {
|
|
6101
|
+
if ((!Array.isArray(args.components) || args.components.length === 0) && (args.dataModel === null || typeof args.dataModel !== "object" || Object.keys(args.dataModel ?? {}).length === 0)) {
|
|
6102
|
+
throw new Error("a2ui_update: provide components and/or dataModel to change.");
|
|
6103
|
+
}
|
|
6104
|
+
const unknown = [...new Set((Array.isArray(args.components) ? args.components : []).map((node) => node?.component).filter((component) => typeof component === "string" && !KNOWN_COMPONENTS.has(component)))];
|
|
6105
|
+
if (unknown.length > 0) throw new Error(`a2ui_update: unknown component(s) ${unknown.join(", ")} \u2014 call a2ui_catalog.`);
|
|
6106
|
+
return { status: "updated" };
|
|
6107
|
+
}
|
|
6108
|
+
}));
|
|
6018
6109
|
}
|
|
6019
6110
|
export {
|
|
6020
6111
|
apply,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "a2ui-render-in-dsh",
|
|
3
|
-
"version": "0.
|
|
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
|
}
|