a2ui-render-in-dsh 0.1.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 +215 -0
- package/README.zh.md +214 -0
- package/cordis.patch.yml +5 -0
- package/lib/client.js +4095 -0
- package/lib/index.js +154 -0
- package/package.json +81 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// src/host/index.js
|
|
2
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
3
|
+
var name = "a2ui";
|
|
4
|
+
var inject = ["tools"];
|
|
5
|
+
var KNOWN_COMPONENTS = /* @__PURE__ */ new Set([
|
|
6
|
+
"Column",
|
|
7
|
+
"Row",
|
|
8
|
+
"Grid",
|
|
9
|
+
"Card",
|
|
10
|
+
"List",
|
|
11
|
+
"Divider",
|
|
12
|
+
"Text",
|
|
13
|
+
"Image",
|
|
14
|
+
"Tag",
|
|
15
|
+
"Math",
|
|
16
|
+
"Mermaid",
|
|
17
|
+
"Chart",
|
|
18
|
+
"Video",
|
|
19
|
+
"Anim",
|
|
20
|
+
"Button",
|
|
21
|
+
"MultipleChoice",
|
|
22
|
+
"Select",
|
|
23
|
+
"CheckBox",
|
|
24
|
+
"TextField"
|
|
25
|
+
]);
|
|
26
|
+
var CATALOG_DOC = `# a2ui_render authoring guide
|
|
27
|
+
|
|
28
|
+
Adaptive use: render a card ONLY when it clearly beats prose \u2014 (a) interaction: quiz/multiple-choice, short forms, option or confirm pickers, product cards; (b) visualization: charts, math function plots, flowcharts, mind maps, diagrams, LaTeX formulas. Otherwise answer in plain text. One call = one card; display-only cards (no Button) are fine. To show several items or a chart dashboard, make ONE call with a Grid \u2014 never several calls.
|
|
29
|
+
|
|
30
|
+
## Components ("components" is an A2UI v0.9 adjacency list; MUST include a node with id "root"; only these names render)
|
|
31
|
+
Layout:
|
|
32
|
+
- Column / Row {children, gap?}
|
|
33
|
+
- Grid {children, columns?, gap?, minWidth?} \u2014 side-by-side cards & 2x2 dashboards; columns 2\u20134, or omit for auto-fit at minWidth px (default 180)
|
|
34
|
+
- Card {children, title?}
|
|
35
|
+
- List {children, direction?: "vertical"|"horizontal"}
|
|
36
|
+
- Divider {}
|
|
37
|
+
Content (Chart/Mermaid/Image have built-in fullscreen zoom \u2014 do not oversize them):
|
|
38
|
+
- Text {text, variant?: "h1"|"h2"|"h3"|"body"|"caption"|"strong"}
|
|
39
|
+
- Image {url, alt?, width?, height?} \u2014 GIF ok
|
|
40
|
+
- Tag {text, color?: "blue"|"green"|"red"|"orange"|"gray"}
|
|
41
|
+
- Math {tex, block?} \u2014 KaTeX LaTeX, e.g. {"tex": "\\\\int_0^1 x^2\\\\,dx = \\\\frac{1}{3}", "block": true}. ALWAYS use Math for mathematical notation \u2014 matrices and vectors included: NEVER write raw nested arrays like [[2,1],[0,3]] inside Text; render them as a formula instead, e.g. {"tex": "\\\\begin{pmatrix}2&1\\\\\\\\0&3\\\\end{pmatrix}\\\\times\\\\begin{pmatrix}1&4\\\\\\\\5&2\\\\end{pmatrix}=\\\\begin{pmatrix}7&10\\\\\\\\15&6\\\\end{pmatrix}", "block": true} (row separator is \\\\\\\\). When animating matrix math with Anim, put the overall equation in a Math block above it.
|
|
42
|
+
- Mermaid {code, caption?} \u2014 flowchart ("graph TD; A[\u5F00\u59CB]-->B{\u5224\u65AD}"), mindmap ("mindmap\\n root((\u4E3B\u9898))\\n \u5206\u652F"), sequenceDiagram, gantt, pie, stateDiagram
|
|
43
|
+
- Chart {option, height?, functions?, xMin?, xMax?, samples?, yClip?} \u2014 ECharts. Data mode: standard ECharts option verbatim (line/bar/pie/scatter/radar/heatmap\u2026). Function mode: functions=[{"expr":"tan(x)","name"?:"y=tan(x)"}] with xMin/xMax (default -10..10) and yClip (default 10) \u2014 ~400 points sampled, asymptotes break automatically; NEVER hand-enumerate data points for a math curve. Expressions: + - * / ^ %, 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, constants pi/e, variable x.
|
|
44
|
+
- Video {url, poster?, loop?, muted?, autoplay?} \u2014 mp4/webm
|
|
45
|
+
- Anim {frames, interval?, height?, autoplay?, labels?} \u2014 step-by-step ALGORITHM animation; auto-plays through ONCE, then the user replays/steps via the built-in controls (do not loop). The form is auto-detected from the frame shape. Simulate the algorithm yourself, one frame per step, each with a clear "note" caption. Two forms:
|
|
46
|
+
(1) Array/bars (sorting, searching, heaps; 8\u201312 small numbers): frames=[{"data":[5,3,8,1],"highlight":[0,1],"sorted":[3],"note":"\u6BD4\u8F83 5 \u548C 3"},\u2026] \u2014 data is the FULL array state, highlight = indices being operated on (orange), sorted = finalized (green).
|
|
47
|
+
(2) Grid/matrix (matrix ops, DP tables, 2D grids): frames=[{"grids":[{"title":"A","data":[[1,2],[3,4]],"highlight":[[0,0],[0,1]]},{"title":"B","data":[[5,7],[6,8]],"highlight":[[0,0],[1,0]]},{"title":"C=A\xD7B","data":[[17,null],[null,null]],"accent":[[0,0]]}],"note":"C[0][0]=1\xD75+2\xD76=17"},\u2026] \u2014 grids render side by side; highlight = cells being read (orange), accent = cells being written (green), null cells render empty. A single grid may use {"grid": [[...]], "highlight": [[r,c]], "accent": [[r,c]]} directly.
|
|
48
|
+
Interaction:
|
|
49
|
+
- Button {label, variant?: "primary"|"default"|"danger", submit?, action: {event: {name, context?}}}
|
|
50
|
+
- MultipleChoice {options: [{label, value, description?, disabled?}], bind, maxAllowedSelections?, disabled?} \u2014 flat option list; stores an array of chosen values at bind; maxAllowedSelections 1 = single-select
|
|
51
|
+
- Select {options: [{label, value, description?, disabled?}], bind, label?, placeholder?, multiple?, maxAllowedSelections?, disabled?} \u2014 dropdown select, better for long option lists or compact forms; single-select stores the chosen value at bind, multiple: true stores an array (maxAllowedSelections caps it)
|
|
52
|
+
- CheckBox {label, bind, disabled?} \u2014 boolean at bind
|
|
53
|
+
- TextField {label?, placeholder?, multiline?, bind, disabled?} \u2014 string at bind
|
|
54
|
+
Input states: preselect by seeding dataModel at the bind path (e.g. dataModel={"city":"beijing"} or {"skills":["go","sql"]}); disable a whole control with disabled: true, or one option via disabled on that option.
|
|
55
|
+
|
|
56
|
+
## Data binding
|
|
57
|
+
- "bind" is a dataModel key path WITHOUT a leading slash ("answer", "form/name").
|
|
58
|
+
- Display props may read live values with {"path": "/answer"} (WITH leading slash).
|
|
59
|
+
- "dataModel" seeds initial values. Avoid literal string props that start with "/".
|
|
60
|
+
|
|
61
|
+
## Submission
|
|
62
|
+
- A Button click sends a plain-language user message: button label + chosen values ("\u63D0\u4EA4\u7B54\u6848\uFF1AB\u3001C"; multi-line for forms). MultipleChoice values arrive as their option LABELS \u2014 map back to values yourself if they differ.
|
|
63
|
+
- action.event.context is optional (touched fields are included automatically); entries may be literals or {"path": "/x", "label": "\u5B57\u6BB5\u540D"}.
|
|
64
|
+
- 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.
|
|
65
|
+
- 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.
|
|
66
|
+
|
|
67
|
+
## Example (single-select quiz)
|
|
68
|
+
components=[{"id":"root","component":"Column","children":["q","opts","submit"]},{"id":"q","component":"Text","variant":"h3","text":"TCP \u4E09\u6B21\u63E1\u624B\u7684\u7B2C\u4E8C\u6B65\u662F\u4EC0\u4E48\uFF1F"},{"id":"opts","component":"MultipleChoice","bind":"answer","maxAllowedSelections":1,"options":[{"label":"SYN","value":"SYN"},{"label":"SYN-ACK","value":"SYN-ACK"}]},{"id":"submit","component":"Button","label":"\u63D0\u4EA4\u7B54\u6848","variant":"primary","action":{"event":{"name":"submit"}}}]
|
|
69
|
+
dataModel={"answer":[]}`;
|
|
70
|
+
var RENDER_DESCRIPTION = "Render a UI card (A2UI) inline in the chat: quiz/multiple-choice questions, short forms, option/confirm pickers, product cards, ECharts charts and math function plots, Mermaid flowcharts/mind maps, LaTeX formulas, images, video, step-by-step algorithm animations (sorting etc.). Use ONLY when a card clearly beats prose; otherwise answer in plain text. IMPORTANT: before your FIRST call in a conversation, call a2ui_catalog and follow its authoring guide \u2014 do not guess component names or props. After rendering an interactive card, end your turn; the user's submission arrives as a plain-language user message.";
|
|
71
|
+
var CATALOG_DESCRIPTION = "Returns the A2UI authoring guide for a2ui_render: the full component catalog with props, data-binding rules, submission format, and an example. Call it ONCE before your first a2ui_render in a conversation; the result stays in context \u2014 call again only if the guide is no longer visible.";
|
|
72
|
+
function apply(ctx) {
|
|
73
|
+
ctx.tools.register(defineTool({
|
|
74
|
+
name: "a2ui_catalog",
|
|
75
|
+
description: CATALOG_DESCRIPTION,
|
|
76
|
+
parameters: {},
|
|
77
|
+
output: {
|
|
78
|
+
schema: {
|
|
79
|
+
type: "object",
|
|
80
|
+
additionalProperties: false,
|
|
81
|
+
properties: { doc: { type: "string", required: true } }
|
|
82
|
+
},
|
|
83
|
+
render: (_args, value) => [{ type: "text", text: value.doc }]
|
|
84
|
+
},
|
|
85
|
+
isConcurrencySafe: () => true,
|
|
86
|
+
async execute() {
|
|
87
|
+
return { doc: CATALOG_DOC };
|
|
88
|
+
}
|
|
89
|
+
}));
|
|
90
|
+
ctx.tools.register(defineTool({
|
|
91
|
+
name: "a2ui_render",
|
|
92
|
+
description: RENDER_DESCRIPTION,
|
|
93
|
+
parameters: {
|
|
94
|
+
title: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "Card title (optional)."
|
|
97
|
+
},
|
|
98
|
+
components: {
|
|
99
|
+
type: "array",
|
|
100
|
+
required: true,
|
|
101
|
+
description: 'A2UI v0.9 adjacency list; must include a node with id "root". Component names and props: see a2ui_catalog.',
|
|
102
|
+
items: {
|
|
103
|
+
type: "object",
|
|
104
|
+
additionalProperties: true,
|
|
105
|
+
properties: {
|
|
106
|
+
id: { type: "string", required: true },
|
|
107
|
+
component: { type: "string", required: true },
|
|
108
|
+
children: { type: "array", items: { type: "string" } }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
dataModel: {
|
|
113
|
+
type: "object",
|
|
114
|
+
additionalProperties: true,
|
|
115
|
+
description: "Initial data model (optional)."
|
|
116
|
+
},
|
|
117
|
+
submitMode: {
|
|
118
|
+
type: "string",
|
|
119
|
+
description: '"once" (default, locks after submit) or "multi" \u2014 see a2ui_catalog.'
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
output: {
|
|
123
|
+
schema: {
|
|
124
|
+
type: "object",
|
|
125
|
+
additionalProperties: false,
|
|
126
|
+
properties: {
|
|
127
|
+
status: { type: "string", required: true },
|
|
128
|
+
hint: { type: "string" }
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
render: (_args, value) => [{ type: "text", text: JSON.stringify(value) }]
|
|
132
|
+
},
|
|
133
|
+
isConcurrencySafe: () => true,
|
|
134
|
+
async execute(args) {
|
|
135
|
+
const components = Array.isArray(args.components) ? args.components : [];
|
|
136
|
+
if (!components.some((node) => node !== null && typeof node === "object" && node.id === "root")) {
|
|
137
|
+
throw new Error('a2ui_render: components must include a node with id "root". Call a2ui_catalog for the authoring guide.');
|
|
138
|
+
}
|
|
139
|
+
const unknown = [...new Set(components.map((node) => node?.component).filter((component) => typeof component === "string" && !KNOWN_COMPONENTS.has(component)))];
|
|
140
|
+
if (unknown.length > 0) {
|
|
141
|
+
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.`);
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
status: "presented",
|
|
145
|
+
hint: "Card rendered in the chat. The user's interaction arrives as the next user message (button label + chosen values in plain language). End your turn now if you are waiting for it."
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
apply,
|
|
152
|
+
inject,
|
|
153
|
+
name
|
|
154
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "a2ui-render-in-dsh",
|
|
3
|
+
"version": "0.1.0",
|
|
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
|
+
"author": "sondge <fengshangjie@lilybearing.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/baihui-ai/a2ui-render-in-dsh.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/baihui-ai/a2ui-render-in-dsh#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/baihui-ai/a2ui-render-in-dsh/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"dsh",
|
|
17
|
+
"a2ui",
|
|
18
|
+
"agent-ui",
|
|
19
|
+
"ant-design-x",
|
|
20
|
+
"interactive-cards",
|
|
21
|
+
"echarts",
|
|
22
|
+
"mermaid",
|
|
23
|
+
"katex"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "lib/index.js",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": "./lib/index.js",
|
|
29
|
+
"./client": "./lib/client.js",
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"dsh": {
|
|
33
|
+
"bundle": {
|
|
34
|
+
"patch": "./cordis.patch.yml"
|
|
35
|
+
},
|
|
36
|
+
"client": {
|
|
37
|
+
"platform": "web",
|
|
38
|
+
"inject": [
|
|
39
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
40
|
+
"@deepseek-ai/dsh-client-locale",
|
|
41
|
+
"@deepseek-ai/dsh-client-ui-tool"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"lib/index.js",
|
|
47
|
+
"lib/client.js",
|
|
48
|
+
"cordis.patch.yml",
|
|
49
|
+
"README.md",
|
|
50
|
+
"README.zh.md"
|
|
51
|
+
],
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "node scripts/build.mjs",
|
|
54
|
+
"watch": "node scripts/build.mjs --watch",
|
|
55
|
+
"prepublishOnly": "npm run build"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@deepseek-ai/dsh-tools": "^0.1.1-rc.2"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@ant-design/x-card": "^2.9.0",
|
|
62
|
+
"echarts": "^6.1.0",
|
|
63
|
+
"esbuild": "^0.25.0",
|
|
64
|
+
"katex": "^0.18.4",
|
|
65
|
+
"mermaid": "^11.17.0",
|
|
66
|
+
"react": "^18.2.0",
|
|
67
|
+
"react-dom": "^18.2.0"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"react": "^18.2.0"
|
|
71
|
+
},
|
|
72
|
+
"peerDependenciesMeta": {
|
|
73
|
+
"react": {
|
|
74
|
+
"optional": true
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"publishConfig": {
|
|
78
|
+
"registry": "https://registry.npmjs.org/",
|
|
79
|
+
"access": "public"
|
|
80
|
+
}
|
|
81
|
+
}
|