a2ui-render-in-dsh 0.1.1 → 0.1.2
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 +80 -34
- package/README.zh.md +79 -33
- package/lib/client.js +698 -529
- package/lib/index.js +141 -58
- package/package.json +1 -1
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. 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.
|
|
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 (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
|
|
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 text (headings, bold/italic, links, lists, quotes, fenced code, $...$ math); USE instead of stacking Text components for any multi-paragraph explanation inside a card
|
|
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 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.
|
|
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 snippets
|
|
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 (one Calc's out feeds another's inputs). digits rounds DISPLAY values only \u2014 omit on intermediate results (rates, ratios) to keep full 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 (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)
|
|
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", "\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.
|
|
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. 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.
|
|
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. 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.
|
|
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 (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.
|
|
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 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.";
|
|
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
|
-
}
|
|
6022
|
+
description: 'A2UI adjacency list incl. a node with id "root"; see a2ui_catalog.',
|
|
6023
|
+
items: { type: "object", additionalProperties: true }
|
|
5980
6024
|
},
|
|
5981
|
-
dataModel: {
|
|
5982
|
-
|
|
5983
|
-
additionalProperties: true,
|
|
5984
|
-
description: "Initial data model (optional)."
|
|
5985
|
-
},
|
|
5986
|
-
submitMode: {
|
|
5987
|
-
type: "string",
|
|
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,73 @@ 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 queue = ["root"];
|
|
6049
|
+
while (queue.length > 0) {
|
|
6050
|
+
const id = queue.pop();
|
|
6051
|
+
if (reachable.has(id)) continue;
|
|
6052
|
+
reachable.add(id);
|
|
6053
|
+
const node = byId.get(id);
|
|
6054
|
+
for (const child of Array.isArray(node?.children) ? node.children : []) queue.push(child);
|
|
6055
|
+
}
|
|
6056
|
+
const orphans = [...byId.keys()].filter((id) => !reachable.has(id));
|
|
6057
|
+
if (orphans.length > 0) {
|
|
6058
|
+
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.`);
|
|
6059
|
+
}
|
|
6008
6060
|
const unknown = [...new Set(components.map((node) => node?.component).filter((component) => typeof component === "string" && !KNOWN_COMPONENTS.has(component)))];
|
|
6009
6061
|
if (unknown.length > 0) {
|
|
6010
6062
|
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
6063
|
}
|
|
6012
6064
|
return {
|
|
6065
|
+
surfaceId: String(exec.callId),
|
|
6013
6066
|
status: "presented",
|
|
6014
|
-
hint: "Card rendered
|
|
6067
|
+
hint: "Card rendered. The user's action arrives as the next user message; end your turn if waiting."
|
|
6015
6068
|
};
|
|
6016
6069
|
}
|
|
6017
6070
|
}));
|
|
6071
|
+
ctx.tools.register(defineTool({
|
|
6072
|
+
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.",
|
|
6074
|
+
parameters: {
|
|
6075
|
+
surfaceId: { type: "string", required: true, description: "The surfaceId returned by the a2ui_render call to update." },
|
|
6076
|
+
components: {
|
|
6077
|
+
type: "array",
|
|
6078
|
+
description: "Components to add or replace (matched by id); same schema as a2ui_render.",
|
|
6079
|
+
items: { type: "object", additionalProperties: true }
|
|
6080
|
+
},
|
|
6081
|
+
dataModel: { type: "object", additionalProperties: true, description: "Data model values to overwrite, keyed by top-level path." }
|
|
6082
|
+
},
|
|
6083
|
+
output: {
|
|
6084
|
+
schema: {
|
|
6085
|
+
type: "object",
|
|
6086
|
+
additionalProperties: false,
|
|
6087
|
+
properties: { status: { type: "string", required: true } }
|
|
6088
|
+
},
|
|
6089
|
+
render: (_args, value) => [{ type: "text", text: JSON.stringify(value) }]
|
|
6090
|
+
},
|
|
6091
|
+
isConcurrencySafe: () => true,
|
|
6092
|
+
async execute(args) {
|
|
6093
|
+
if ((!Array.isArray(args.components) || args.components.length === 0) && (args.dataModel === null || typeof args.dataModel !== "object" || Object.keys(args.dataModel ?? {}).length === 0)) {
|
|
6094
|
+
throw new Error("a2ui_update: provide components and/or dataModel to change.");
|
|
6095
|
+
}
|
|
6096
|
+
const unknown = [...new Set((Array.isArray(args.components) ? args.components : []).map((node) => node?.component).filter((component) => typeof component === "string" && !KNOWN_COMPONENTS.has(component)))];
|
|
6097
|
+
if (unknown.length > 0) throw new Error(`a2ui_update: unknown component(s) ${unknown.join(", ")} \u2014 call a2ui_catalog.`);
|
|
6098
|
+
return { status: "updated" };
|
|
6099
|
+
}
|
|
6100
|
+
}));
|
|
6018
6101
|
}
|
|
6019
6102
|
export {
|
|
6020
6103
|
apply,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "a2ui-render-in-dsh",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|