@artooi/ag-ui-web-component 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.
Files changed (65) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +471 -0
  4. package/dist/ag-ui-web-component.bundle.js +319 -0
  5. package/dist/ag-ui-web-component.bundle.js.map +7 -0
  6. package/dist/ag_ui_chat.d.ts +85 -0
  7. package/dist/ag_ui_chat.d.ts.map +1 -0
  8. package/dist/agui_client.d.ts +99 -0
  9. package/dist/agui_client.d.ts.map +1 -0
  10. package/dist/animations.d.ts +33 -0
  11. package/dist/animations.d.ts.map +1 -0
  12. package/dist/client_tool_registry.d.ts +32 -0
  13. package/dist/client_tool_registry.d.ts.map +1 -0
  14. package/dist/confirmation_modal.d.ts +14 -0
  15. package/dist/confirmation_modal.d.ts.map +1 -0
  16. package/dist/constants.d.ts +40 -0
  17. package/dist/constants.d.ts.map +1 -0
  18. package/dist/conversation_store.d.ts +54 -0
  19. package/dist/conversation_store.d.ts.map +1 -0
  20. package/dist/create_http_agent.d.ts +22 -0
  21. package/dist/create_http_agent.d.ts.map +1 -0
  22. package/dist/define_ag_ui_chat.d.ts +9 -0
  23. package/dist/define_ag_ui_chat.d.ts.map +1 -0
  24. package/dist/dom_driver.d.ts +24 -0
  25. package/dist/dom_driver.d.ts.map +1 -0
  26. package/dist/index.d.ts +18 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +1159 -0
  29. package/dist/index.js.map +7 -0
  30. package/dist/is_destructive.d.ts +8 -0
  31. package/dist/is_destructive.d.ts.map +1 -0
  32. package/dist/is_navigates.d.ts +9 -0
  33. package/dist/is_navigates.d.ts.map +1 -0
  34. package/dist/page_map.d.ts +16 -0
  35. package/dist/page_map.d.ts.map +1 -0
  36. package/dist/route_map.d.ts +27 -0
  37. package/dist/route_map.d.ts.map +1 -0
  38. package/dist/state_hook.d.ts +23 -0
  39. package/dist/state_hook.d.ts.map +1 -0
  40. package/dist/styles.d.ts +2 -0
  41. package/dist/styles.d.ts.map +1 -0
  42. package/dist/tool_call_card.d.ts +29 -0
  43. package/dist/tool_call_card.d.ts.map +1 -0
  44. package/dist/version.d.ts +2 -0
  45. package/dist/version.d.ts.map +1 -0
  46. package/package.json +79 -0
  47. package/src/ag_ui_chat.ts +411 -0
  48. package/src/agui_client.ts +212 -0
  49. package/src/animations.ts +86 -0
  50. package/src/client_tool_registry.ts +56 -0
  51. package/src/confirmation_modal.ts +69 -0
  52. package/src/constants.ts +48 -0
  53. package/src/conversation_store.ts +103 -0
  54. package/src/create_http_agent.ts +40 -0
  55. package/src/define_ag_ui_chat.ts +15 -0
  56. package/src/dom_driver.ts +60 -0
  57. package/src/index.ts +60 -0
  58. package/src/is_destructive.ts +11 -0
  59. package/src/is_navigates.ts +12 -0
  60. package/src/page_map.ts +25 -0
  61. package/src/route_map.ts +83 -0
  62. package/src/state_hook.ts +44 -0
  63. package/src/styles.ts +296 -0
  64. package/src/tool_call_card.ts +95 -0
  65. package/src/version.ts +1 -0
package/src/styles.ts ADDED
@@ -0,0 +1,296 @@
1
+ // Shadow-DOM-scoped styles for the chat shell. Kept as a string constant so
2
+ // the Custom Element can inject it into its shadow root without a build-time
3
+ // CSS pipeline. Scoped by the shadow boundary, so class names stay terse.
4
+
5
+ export const STYLES = `
6
+ :host {
7
+ /* Colors */
8
+ --ag-ui-bg: #ffffff;
9
+ --ag-ui-fg: #1a1a2e;
10
+ --ag-ui-accent: #4f46e5;
11
+ --ag-ui-user-bg: #4f46e5;
12
+ --ag-ui-user-fg: #ffffff;
13
+ --ag-ui-assistant-bg: #f1f1f6;
14
+ --ag-ui-input-bg: var(--ag-ui-bg);
15
+ --ag-ui-tool-bg: var(--ag-ui-assistant-bg);
16
+ --ag-ui-tool-fg: var(--ag-ui-accent);
17
+ --ag-ui-header-bg: var(--ag-ui-accent);
18
+ --ag-ui-header-fg: #ffffff;
19
+ --ag-ui-border: #e2e2ec;
20
+ --ag-ui-radius: 12px;
21
+
22
+ /* Status accents for tool-call cards. */
23
+ --ag-ui-success: #15803d;
24
+ --ag-ui-danger: #b91c1c;
25
+ --ag-ui-muted: #6b7280;
26
+
27
+ /* Surface — set --ag-ui-shadow: none for a flush, embedded panel. */
28
+ --ag-ui-shadow: 0 12px 32px rgba(20, 20, 50, 0.18);
29
+ --ag-ui-font: inherit;
30
+ --ag-ui-font-size: 14px;
31
+
32
+ /* Layout — override from outside to dock the widget anywhere.
33
+ Set --ag-ui-position: static (and place this element in your own
34
+ grid/flex layout) to embed it in the page flow instead of floating. */
35
+ --ag-ui-position: fixed;
36
+ --ag-ui-z-index: 2147483000;
37
+ --ag-ui-width: 380px;
38
+ --ag-ui-height: 560px;
39
+ --ag-ui-inset: auto 24px 24px auto;
40
+ --ag-ui-max-width: calc(100vw - 48px);
41
+ --ag-ui-max-height: calc(100vh - 48px);
42
+
43
+ position: var(--ag-ui-position);
44
+ inset: var(--ag-ui-inset);
45
+ z-index: var(--ag-ui-z-index);
46
+ width: var(--ag-ui-width);
47
+ max-width: var(--ag-ui-max-width);
48
+ height: var(--ag-ui-height);
49
+ max-height: var(--ag-ui-max-height);
50
+ display: flex;
51
+ font-family: var(--ag-ui-font);
52
+ font-size: var(--ag-ui-font-size);
53
+ color: var(--ag-ui-fg);
54
+ }
55
+
56
+ .chat {
57
+ position: relative;
58
+ display: flex;
59
+ flex-direction: column;
60
+ flex: 1;
61
+ min-height: 0;
62
+ background: var(--ag-ui-bg);
63
+ border: 1px solid var(--ag-ui-border);
64
+ border-radius: var(--ag-ui-radius);
65
+ box-shadow: var(--ag-ui-shadow);
66
+ overflow: hidden;
67
+ }
68
+
69
+ .header {
70
+ padding: 12px 16px;
71
+ font-weight: 600;
72
+ border-bottom: 1px solid var(--ag-ui-border);
73
+ background: var(--ag-ui-header-bg);
74
+ color: var(--ag-ui-header-fg);
75
+ }
76
+
77
+ .messages {
78
+ flex: 1;
79
+ overflow-y: auto;
80
+ padding: 16px;
81
+ display: flex;
82
+ flex-direction: column;
83
+ gap: 10px;
84
+ }
85
+
86
+ .message {
87
+ max-width: 80%;
88
+ padding: 8px 12px;
89
+ border-radius: 14px;
90
+ line-height: 1.4;
91
+ white-space: pre-wrap;
92
+ word-break: break-word;
93
+ }
94
+
95
+ .message--user {
96
+ align-self: flex-end;
97
+ background: var(--ag-ui-user-bg);
98
+ color: var(--ag-ui-user-fg);
99
+ border-bottom-right-radius: 4px;
100
+ }
101
+
102
+ .message--assistant {
103
+ align-self: flex-start;
104
+ background: var(--ag-ui-assistant-bg);
105
+ border-bottom-left-radius: 4px;
106
+ }
107
+
108
+ .tool-call {
109
+ align-self: flex-start;
110
+ max-width: 80%;
111
+ box-sizing: border-box;
112
+ display: flex;
113
+ flex-direction: column;
114
+ gap: 6px;
115
+ font-size: 12px;
116
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
117
+ padding: 8px 10px;
118
+ border-radius: 8px;
119
+ background: var(--ag-ui-tool-bg);
120
+ border: 1px solid var(--ag-ui-border);
121
+ color: var(--ag-ui-tool-fg);
122
+ }
123
+
124
+ .tool-call-head {
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: space-between;
128
+ gap: 8px;
129
+ }
130
+
131
+ .tool-call-name {
132
+ font-weight: 600;
133
+ word-break: break-word;
134
+ }
135
+
136
+ .tool-call-status {
137
+ flex: none;
138
+ padding: 1px 8px;
139
+ border-radius: 999px;
140
+ font-size: 11px;
141
+ font-weight: 600;
142
+ background: rgba(127, 127, 127, 0.16);
143
+ color: var(--ag-ui-muted);
144
+ }
145
+
146
+ .tool-call[data-status="done"] .tool-call-status {
147
+ color: var(--ag-ui-success);
148
+ }
149
+
150
+ .tool-call[data-status="error"] .tool-call-status {
151
+ color: var(--ag-ui-danger);
152
+ }
153
+
154
+ .tool-call[data-status="declined"] .tool-call-status {
155
+ color: var(--ag-ui-muted);
156
+ }
157
+
158
+ .tool-call-args,
159
+ .tool-call-result {
160
+ margin: 0;
161
+ padding: 6px 8px;
162
+ max-height: 160px;
163
+ overflow: auto;
164
+ background: var(--ag-ui-bg);
165
+ border: 1px solid var(--ag-ui-border);
166
+ border-radius: 6px;
167
+ white-space: pre-wrap;
168
+ word-break: break-word;
169
+ color: var(--ag-ui-fg);
170
+ }
171
+
172
+ .tool-call-toggle {
173
+ align-self: flex-start;
174
+ border: none;
175
+ padding: 0;
176
+ background: none;
177
+ font: inherit;
178
+ font-weight: 600;
179
+ color: var(--ag-ui-accent);
180
+ cursor: pointer;
181
+ }
182
+
183
+ .tool-call-toggle::before {
184
+ content: "▸ ";
185
+ }
186
+
187
+ .tool-call-toggle[aria-expanded="true"]::before {
188
+ content: "▾ ";
189
+ }
190
+
191
+ .input-row {
192
+ display: flex;
193
+ gap: 8px;
194
+ padding: 12px;
195
+ border-top: 1px solid var(--ag-ui-border);
196
+ }
197
+
198
+ .input {
199
+ flex: 1;
200
+ resize: none;
201
+ background: var(--ag-ui-input-bg);
202
+ border: 1px solid var(--ag-ui-border);
203
+ border-radius: 8px;
204
+ padding: 8px 10px;
205
+ font: inherit;
206
+ color: inherit;
207
+ outline: none;
208
+ }
209
+
210
+ .input:focus {
211
+ border-color: var(--ag-ui-accent);
212
+ }
213
+
214
+ .send {
215
+ border: none;
216
+ border-radius: 8px;
217
+ padding: 0 16px;
218
+ background: var(--ag-ui-accent);
219
+ color: #ffffff;
220
+ font: inherit;
221
+ font-weight: 600;
222
+ cursor: pointer;
223
+ }
224
+
225
+ .send:disabled {
226
+ opacity: 0.5;
227
+ cursor: default;
228
+ }
229
+
230
+ .modal-overlay {
231
+ position: absolute;
232
+ inset: 0;
233
+ display: flex;
234
+ align-items: center;
235
+ justify-content: center;
236
+ padding: 16px;
237
+ background: rgba(20, 20, 50, 0.4);
238
+ backdrop-filter: blur(2px);
239
+ }
240
+
241
+ .modal {
242
+ width: 100%;
243
+ max-width: 320px;
244
+ background: var(--ag-ui-bg);
245
+ border-radius: 12px;
246
+ box-shadow: 0 16px 40px rgba(20, 20, 50, 0.3);
247
+ overflow: hidden;
248
+ }
249
+
250
+ .modal-title {
251
+ padding: 12px 16px;
252
+ font-weight: 600;
253
+ border-bottom: 1px solid var(--ag-ui-border);
254
+ }
255
+
256
+ .modal-body {
257
+ padding: 12px 16px 4px;
258
+ }
259
+
260
+ .modal-args {
261
+ margin: 0 16px 12px;
262
+ padding: 8px 10px;
263
+ max-height: 140px;
264
+ overflow: auto;
265
+ font-size: 12px;
266
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
267
+ background: var(--ag-ui-assistant-bg);
268
+ border-radius: 8px;
269
+ white-space: pre-wrap;
270
+ word-break: break-word;
271
+ }
272
+
273
+ .modal-actions {
274
+ display: flex;
275
+ gap: 8px;
276
+ padding: 0 16px 16px;
277
+ justify-content: flex-end;
278
+ }
279
+
280
+ .modal-btn {
281
+ border: 1px solid var(--ag-ui-border);
282
+ border-radius: 8px;
283
+ padding: 8px 14px;
284
+ font: inherit;
285
+ font-weight: 600;
286
+ cursor: pointer;
287
+ background: var(--ag-ui-bg);
288
+ color: var(--ag-ui-fg);
289
+ }
290
+
291
+ .modal-btn--confirm {
292
+ border-color: var(--ag-ui-accent);
293
+ background: var(--ag-ui-accent);
294
+ color: #ffffff;
295
+ }
296
+ `;
@@ -0,0 +1,95 @@
1
+ import { TOOL_CALL_STATUS } from "./constants.js";
2
+
3
+ /** Any state a tool-call card can be in. */
4
+ export type ToolCallStatus = (typeof TOOL_CALL_STATUS)[keyof typeof TOOL_CALL_STATUS];
5
+
6
+ /** The terminal states a card settles into (everything but `pending`). */
7
+ export type SettledStatus = Exclude<ToolCallStatus, typeof TOOL_CALL_STATUS.PENDING>;
8
+
9
+ /** Short pill text shown for each status. */
10
+ const STATUS_LABEL: Record<ToolCallStatus, string> = {
11
+ [TOOL_CALL_STATUS.PENDING]: "running…",
12
+ [TOOL_CALL_STATUS.DONE]: "✓ done",
13
+ [TOOL_CALL_STATUS.ERROR]: "⚠ error",
14
+ [TOOL_CALL_STATUS.DECLINED]: "⊘ declined",
15
+ };
16
+
17
+ /** Toggle-button label for each settled outcome's collapsible body. */
18
+ const RESULT_LABEL: Record<SettledStatus, string> = {
19
+ [TOOL_CALL_STATUS.DONE]: "Result",
20
+ [TOOL_CALL_STATUS.ERROR]: "Error",
21
+ [TOOL_CALL_STATUS.DECLINED]: "Declined",
22
+ };
23
+
24
+ /**
25
+ * A live tool-call card for the chat transcript.
26
+ *
27
+ * Construction renders the tool name, its pretty-printed arguments, and a
28
+ * `running…` status pill. {@link settle} later flips the pill to the outcome
29
+ * and appends a click-to-expand body holding the result (or error / decline
30
+ * message), collapsed by default.
31
+ *
32
+ * Pure DOM (no framework); the host appends {@link element} into its shadow
33
+ * root and themes it via the `--ag-ui-*` custom properties.
34
+ */
35
+ export class ToolCallCard {
36
+ /** The card's root element; append this into the message list. */
37
+ readonly element: HTMLDivElement;
38
+
39
+ readonly #status: HTMLSpanElement;
40
+
41
+ constructor(name: string, args: Record<string, unknown>) {
42
+ this.element = document.createElement("div");
43
+ this.element.className = "tool-call";
44
+ this.element.setAttribute("data-tool-name", name);
45
+ this.element.setAttribute("data-status", TOOL_CALL_STATUS.PENDING);
46
+
47
+ const head = document.createElement("div");
48
+ head.className = "tool-call-head";
49
+
50
+ const label = document.createElement("span");
51
+ label.className = "tool-call-name";
52
+ label.textContent = `🔧 ${name}`;
53
+
54
+ this.#status = document.createElement("span");
55
+ this.#status.className = "tool-call-status";
56
+ this.#status.textContent = STATUS_LABEL[TOOL_CALL_STATUS.PENDING];
57
+
58
+ head.append(label, this.#status);
59
+
60
+ const argsEl = document.createElement("pre");
61
+ argsEl.className = "tool-call-args";
62
+ argsEl.textContent = JSON.stringify(args, null, 2);
63
+
64
+ this.element.append(head, argsEl);
65
+ }
66
+
67
+ /**
68
+ * Flip the status pill to ``status`` and append a collapsed body holding
69
+ * ``text`` (the JSON result, an error message, a decline notice, or a
70
+ * server-executed note) behind a click-to-expand toggle.
71
+ */
72
+ settle(status: SettledStatus, text: string): void {
73
+ this.element.setAttribute("data-status", status);
74
+ this.#status.textContent = STATUS_LABEL[status];
75
+
76
+ const toggle = document.createElement("button");
77
+ toggle.type = "button";
78
+ toggle.className = "tool-call-toggle";
79
+ toggle.setAttribute("aria-expanded", "false");
80
+ toggle.textContent = RESULT_LABEL[status];
81
+
82
+ const output = document.createElement("pre");
83
+ output.className = "tool-call-result";
84
+ output.textContent = text;
85
+ output.hidden = true;
86
+
87
+ toggle.addEventListener("click", () => {
88
+ const expand = output.hidden;
89
+ output.hidden = !expand;
90
+ toggle.setAttribute("aria-expanded", String(expand));
91
+ });
92
+
93
+ this.element.append(toggle, output);
94
+ }
95
+ }
package/src/version.ts ADDED
@@ -0,0 +1 @@
1
+ export const VERSION: string = "0.1.0";