@artooi/ag-ui-web-component 0.18.0 → 0.20.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.
@@ -141,6 +141,10 @@ export class SkillsMenu {
141
141
  button.className = "skill-chip";
142
142
  button.setAttribute("part", "skill-chip");
143
143
  button.textContent = skill.title;
144
+ // The chip stays a label — it is a one-click affordance, not something to
145
+ // memorise — but naming the command it stands for makes the two
146
+ // discoverable as the same thing.
147
+ button.title = `/${skill.name}`;
144
148
  button.addEventListener("click", () => this.#pick(skill));
145
149
  this.chips.appendChild(button);
146
150
  }
@@ -159,7 +163,15 @@ export class SkillsMenu {
159
163
  const title = document.createElement("span");
160
164
  title.className = "skill-item-title";
161
165
  title.setAttribute("part", "skill-item-title");
162
- title.textContent = skill.title;
166
+
167
+ // The token leads, because it is the thing the user has to type. A row
168
+ // showing only the label taught the label and not the command, so the
169
+ // palette could not be used to learn its own vocabulary.
170
+ const token = document.createElement("code");
171
+ token.className = "skill-item-token";
172
+ token.setAttribute("part", "skill-item-token");
173
+ token.textContent = `/${skill.name}`;
174
+ title.append(token, document.createTextNode(` ${skill.title}`));
163
175
  item.appendChild(title);
164
176
 
165
177
  if (skill.description !== undefined) {
package/src/ui/styles.ts CHANGED
@@ -168,7 +168,72 @@ export const STYLES = `
168
168
  padding-inline: max(var(--ag-ui-pad), calc((100% - var(--ag-ui-content-max-width)) / 2));
169
169
  }
170
170
 
171
- :host([placement="page"]) .input-row {
171
+ :host([placement="page"]) /* Resize handle: a corner grip on a floating panel, an edge grip on a docked
172
+ one. Absent entirely where the placement is full-bleed, since there is
173
+ nothing to drag. */
174
+ .resize-handle {
175
+ position: absolute;
176
+ z-index: 2;
177
+ background: transparent;
178
+ touch-action: none;
179
+ }
180
+
181
+ .resize-handle:focus-visible {
182
+ outline: 2px solid var(--ag-ui-accent);
183
+ outline-offset: -2px;
184
+ }
185
+
186
+ /* The grip sits at the corner the panel grows toward. Which corner that is
187
+ depends on the host's layout, not on placement, so the element measures it
188
+ and stamps data-resize-anchor with the edges that stay put.
189
+
190
+ Default (bottom-right pinned, the floating case) puts the grip top-left. */
191
+ .resize-handle {
192
+ top: 0;
193
+ left: 0;
194
+ width: 14px;
195
+ height: 14px;
196
+ cursor: nwse-resize;
197
+ }
198
+
199
+ :host([data-resize-anchor~="left"]) .resize-handle {
200
+ left: auto;
201
+ right: 0;
202
+ }
203
+
204
+ :host([data-resize-anchor~="top"]) .resize-handle {
205
+ top: auto;
206
+ bottom: 0;
207
+ }
208
+
209
+ /* One axis flipped means the drag runs along the other diagonal. */
210
+ :host([data-resize-anchor="top-right"]) .resize-handle,
211
+ :host([data-resize-anchor="bottom-left"]) .resize-handle {
212
+ cursor: nesw-resize;
213
+ }
214
+
215
+ /* Docked: the placement owns the height, so only the inner edge is the user's. */
216
+ :host([placement="sidebar"]) .resize-handle,
217
+ :host([placement="side"]) .resize-handle {
218
+ top: 0;
219
+ bottom: auto;
220
+ width: 8px;
221
+ height: 100%;
222
+ cursor: ew-resize;
223
+ }
224
+
225
+ /* Full-bleed: nothing to drag. */
226
+ :host([placement="full"]) .resize-handle,
227
+ :host([placement="page"]) .resize-handle {
228
+ display: none;
229
+ }
230
+
231
+ .resize-handle[data-dragging] {
232
+ background: var(--ag-ui-accent);
233
+ opacity: 0.35;
234
+ }
235
+
236
+ .input-row {
172
237
  padding-inline: max(12px, calc((100% - var(--ag-ui-content-max-width)) / 2));
173
238
  }
174
239
 
@@ -772,10 +837,9 @@ export const STYLES = `
772
837
  }
773
838
  }
774
839
 
775
- /* Inline display mode: the lightest card drop the box chrome so the
776
- status row reads as one line of the answer; the result toggle still expands
777
- below it. */
778
- .tool-call[data-display="inline"] {
840
+ /* Inline display mode: the lightest card. Drop the box chrome so the status row
841
+ reads as one line of the answer; the result toggle still expands below it. */
842
+ :host([data-tool-display="inline"]) .tool-call {
779
843
  max-width: 100%;
780
844
  background: transparent;
781
845
  border: none;
@@ -805,6 +869,44 @@ export const STYLES = `
805
869
  color: var(--ag-ui-muted);
806
870
  }
807
871
 
872
+ .tool-call-body {
873
+ display: flex;
874
+ flex-direction: column;
875
+ gap: 6px;
876
+ }
877
+
878
+ .tool-call-section {
879
+ display: flex;
880
+ flex-direction: column;
881
+ gap: 3px;
882
+ }
883
+
884
+ /* The heading that tells the two payloads apart. Without it the arguments and
885
+ the result were one run of text and a reader had to guess the boundary. */
886
+ .tool-call-section-label {
887
+ font-size: 10px;
888
+ font-weight: 700;
889
+ letter-spacing: 0.06em;
890
+ text-transform: uppercase;
891
+ color: var(--ag-ui-muted);
892
+ }
893
+
894
+ /* The record of a human decision on a gated call. An approved call used to
895
+ look exactly like one that was never gated. */
896
+ .skill-item-token {
897
+ font-family: ui-monospace, "SF Mono", Menlo, monospace;
898
+ font-size: 0.92em;
899
+ color: var(--ag-ui-accent);
900
+ margin-right: 6px;
901
+ }
902
+
903
+ .tool-call-decision {
904
+ flex: none;
905
+ font-size: 11px;
906
+ font-style: italic;
907
+ color: var(--ag-ui-muted);
908
+ }
909
+
808
910
  .tool-call-args,
809
911
  .tool-call-result {
810
912
  margin: 0;
@@ -819,6 +921,43 @@ export const STYLES = `
819
921
  color: var(--ag-ui-fg);
820
922
  }
821
923
 
924
+ /* Display modes are pure visibility over one DOM shape, selected from the host
925
+ attribute rather than a value stamped on the card when it was built. That is
926
+ what lets a host flip data-tool-display and have every card already on screen
927
+ re-read it, the way data-answer-well behaves. Baking the structure per mode
928
+ meant the setting only reached cards created afterwards.
929
+
930
+ Default (no attribute) is the full mode: arguments always visible, result
931
+ behind the toggle. */
932
+ .tool-call[data-expanded="false"] .tool-call-section--result {
933
+ display: none;
934
+ }
935
+
936
+ /* Compact: one toggle over both regions, so a settled card is a single line
937
+ until asked. */
938
+ :host([data-tool-display="compact"]) .tool-call[data-expanded="false"] .tool-call-section {
939
+ display: none;
940
+ }
941
+
942
+ /* Inline: the result only; the call's arguments are noise at this density. */
943
+ :host([data-tool-display="inline"]) .tool-call .tool-call-section--args {
944
+ display: none;
945
+ }
946
+
947
+ /* Minimal: the status row and nothing else, so there is no toggle to press. */
948
+ :host([data-tool-display="minimal"]) .tool-call .tool-call-toggle,
949
+ :host([data-tool-display="minimal"]) .tool-call .tool-call-body {
950
+ display: none;
951
+ }
952
+
953
+ /* A pending card has no result yet, and in the modes where the arguments are
954
+ hidden too there is nothing behind the toggle. Hide the control rather than
955
+ offer one that expands onto nothing. */
956
+ .tool-call[data-status="pending"] .tool-call-toggle,
957
+ :host([data-tool-display="inline"]) .tool-call[data-status="pending"] .tool-call-toggle {
958
+ display: none;
959
+ }
960
+
822
961
  .tool-call-toggle {
823
962
  align-self: flex-start;
824
963
  border: none;
@@ -838,6 +977,75 @@ export const STYLES = `
838
977
  content: "▾ ";
839
978
  }
840
979
 
980
+ /* Resize handle: a corner grip on a floating panel, an edge grip on a docked
981
+ one. Absent entirely where the placement is full-bleed, since there is
982
+ nothing to drag. */
983
+ .resize-handle {
984
+ position: absolute;
985
+ z-index: 2;
986
+ background: transparent;
987
+ touch-action: none;
988
+ }
989
+
990
+ .resize-handle:focus-visible {
991
+ outline: 2px solid var(--ag-ui-accent);
992
+ outline-offset: -2px;
993
+ }
994
+
995
+ /* The grip sits on the corner opposite the panel's anchor, because a resize
996
+ measures from whichever edge is not moving. Selected from the host attribute,
997
+ so switching placement moves the grip immediately.
998
+
999
+ Default is floating: pinned bottom-right, so the grip is top-left. */
1000
+ .resize-handle {
1001
+ top: 0;
1002
+ left: 0;
1003
+ width: 14px;
1004
+ height: 14px;
1005
+ cursor: nwse-resize;
1006
+ }
1007
+
1008
+ /* Embedded sits in normal flow, pinned top-left, so it grows bottom-right. */
1009
+ :host([placement="embedded"]) .resize-handle {
1010
+ top: auto;
1011
+ left: auto;
1012
+ right: 0;
1013
+ bottom: 0;
1014
+ cursor: nwse-resize;
1015
+ }
1016
+
1017
+ /* Pinned bottom-left, so the free corner is top-right. */
1018
+ :host([placement="bottom-left"]) .resize-handle {
1019
+ left: auto;
1020
+ right: 0;
1021
+ cursor: nesw-resize;
1022
+ }
1023
+
1024
+ /* Docked: the placement owns the height, so only the inner edge is the user's. */
1025
+ :host([placement="sidebar"]) .resize-handle,
1026
+ :host([placement="side"]) .resize-handle {
1027
+ width: 8px;
1028
+ height: 100%;
1029
+ cursor: ew-resize;
1030
+ }
1031
+
1032
+ /* Docked to the left, so the inner edge is the right-hand one. */
1033
+ :host([placement="sidebar"][data-side="left"]) .resize-handle {
1034
+ left: auto;
1035
+ right: 0;
1036
+ }
1037
+
1038
+ /* Full-bleed: nothing to drag. */
1039
+ :host([placement="full"]) .resize-handle,
1040
+ :host([placement="page"]) .resize-handle {
1041
+ display: none;
1042
+ }
1043
+
1044
+ .resize-handle[data-dragging] {
1045
+ background: var(--ag-ui-accent);
1046
+ opacity: 0.35;
1047
+ }
1048
+
841
1049
  .input-row {
842
1050
  display: flex;
843
1051
  gap: 8px;
@@ -1315,9 +1523,12 @@ export const STYLES = `
1315
1523
  color: var(--ag-ui-muted);
1316
1524
  }
1317
1525
 
1526
+ /* The hint sits directly above the composer's top border, so a zero bottom
1527
+ margin left the text touching the divider. */
1318
1528
  .skill-hint {
1319
- margin: 8px 12px 0;
1529
+ margin: 8px 12px;
1320
1530
  font-size: 0.85em;
1531
+ line-height: 1.4;
1321
1532
  color: var(--ag-ui-danger);
1322
1533
  }
1323
1534
 
@@ -1,4 +1,4 @@
1
- import { TOOL_CALL_STATUS, TOOL_DISPLAY } from "../constants.js";
1
+ import { TOOL_CALL_STATUS, type TOOL_DISPLAY } from "../constants.js";
2
2
  import { DEFAULT_UI_STRINGS, type UiStrings } from "./ui_strings.js";
3
3
 
4
4
  /** Any state a tool-call card can be in. */
@@ -20,7 +20,7 @@ function statusLabels(strings: UiStrings): Record<ToolCallStatus, string> {
20
20
  };
21
21
  }
22
22
 
23
- /** Toggle-button label for each settled outcome's collapsible body (full mode). */
23
+ /** Section-heading text for each settled outcome's result region. */
24
24
  function resultLabels(strings: UiStrings): Record<SettledStatus, string> {
25
25
  return {
26
26
  [TOOL_CALL_STATUS.DONE]: strings.resultLabel,
@@ -29,19 +29,32 @@ function resultLabels(strings: UiStrings): Record<SettledStatus, string> {
29
29
  };
30
30
  }
31
31
 
32
+ /** Pretty-print a JSON payload; fall back to the raw text if it isn't JSON. */
33
+ function formatPayload(text: string): string {
34
+ try {
35
+ return JSON.stringify(JSON.parse(text), null, 2);
36
+ } catch {
37
+ return text;
38
+ }
39
+ }
40
+
32
41
  /**
33
42
  * A live tool-call card for the chat transcript.
34
43
  *
35
- * Construction renders a status icon, the tool name, and a `running…` status
36
- * pill; in `full` mode it also shows the pretty-printed arguments inline.
37
- * {@link settle} later flips the pill to the outcome and depending on the
38
- * {@link ToolDisplayMode} appends a collapsible body:
44
+ * Construction renders a status icon, the tool name, a `running…` pill, and the
45
+ * card's body: an **arguments** region and a **result** region, each with its own
46
+ * heading and its own `part`. {@link settle} fills in the result and flips the
47
+ * pill. Both payloads are pretty-printed, and the two are never concatenated —
48
+ * the previous compact layout ran `args: {...}` and the result together in one
49
+ * `<pre>`, leaving no way to see where the call ended and the answer began.
39
50
  *
40
- * - `inline` no args; the result behind its own toggle (like `full` but with
41
- * the card chrome stripped to a single light row).
42
- * - `minimal` pill only; nothing to expand.
43
- * - `compact` one "Details" toggle revealing args *and* result together.
44
- * - `full` the result (or error / decline message) behind its own toggle.
51
+ * **The card renders one DOM shape in every display mode, and CSS decides what
52
+ * shows.** That is what makes `data-tool-display` behave like `data-answer-well`
53
+ * flip it on the host and every card already on screen re-reads it. Building a
54
+ * different structure per mode meant only cards created *after* the change
55
+ * picked it up, so the setting appeared not to work until the next conversation.
56
+ * Visibility is selected from the host attribute rather than a value copied onto
57
+ * the card at construction, for the same reason.
45
58
  *
46
59
  * The leading icon carries no text of its own: its glyph/spinner is drawn by
47
60
  * the shadow CSS keyed off the card's `data-status`, so a host themes it via
@@ -57,20 +70,20 @@ export class ToolCallCard {
57
70
  readonly element: HTMLDivElement;
58
71
 
59
72
  readonly #status: HTMLSpanElement;
60
- readonly #mode: ToolDisplayMode;
61
- readonly #args: Record<string, unknown>;
73
+ readonly #decision: HTMLSpanElement;
74
+ readonly #toggle: HTMLButtonElement;
75
+ readonly #resultSection: HTMLDivElement;
76
+ readonly #resultLabel: HTMLSpanElement;
77
+ readonly #resultBody: HTMLPreElement;
62
78
  readonly #strings: UiStrings;
63
79
  #settled = false;
64
80
 
65
81
  constructor(
66
82
  name: string,
67
83
  args: Record<string, unknown>,
68
- mode: ToolDisplayMode = TOOL_DISPLAY.FULL,
69
84
  summary?: string,
70
85
  strings: UiStrings = DEFAULT_UI_STRINGS,
71
86
  ) {
72
- this.#mode = mode;
73
- this.#args = args;
74
87
  this.#strings = strings;
75
88
 
76
89
  this.element = document.createElement("div");
@@ -78,7 +91,7 @@ export class ToolCallCard {
78
91
  this.element.setAttribute("part", "tool-card");
79
92
  this.element.setAttribute("data-tool-name", name);
80
93
  this.element.setAttribute("data-status", TOOL_CALL_STATUS.PENDING);
81
- this.element.setAttribute("data-display", mode);
94
+ this.element.setAttribute("data-expanded", "false");
82
95
 
83
96
  const head = document.createElement("div");
84
97
  head.className = "tool-call-head";
@@ -103,16 +116,56 @@ export class ToolCallCard {
103
116
  this.#status.setAttribute("part", "tool-card-status");
104
117
  this.#status.textContent = statusLabels(strings)[TOOL_CALL_STATUS.PENDING];
105
118
 
106
- head.append(icon, label, this.#status);
107
- this.element.append(head);
119
+ this.#decision = document.createElement("span");
120
+ this.#decision.className = "tool-call-decision";
121
+ this.#decision.setAttribute("part", "tool-card-decision");
122
+ this.#decision.hidden = true;
123
+
124
+ head.append(icon, label, this.#status, this.#decision);
125
+
126
+ const argsSection = this.#section("args", strings.argumentsLabel);
127
+ argsSection.body.textContent = JSON.stringify(args, null, 2);
128
+ // A call with no arguments renders an empty object in a box of its own,
129
+ // which is a frame around nothing. Drop the region instead.
130
+ argsSection.root.hidden = Object.keys(args).length === 0;
131
+
132
+ const resultSection = this.#section("result", strings.resultLabel);
133
+ this.#resultSection = resultSection.root;
134
+ this.#resultLabel = resultSection.label;
135
+ this.#resultBody = resultSection.body;
136
+ // Nothing to show until `settle` supplies it; a pending card would
137
+ // otherwise expand onto an empty region.
138
+ resultSection.root.hidden = true;
139
+
140
+ this.#toggle = document.createElement("button");
141
+ this.#toggle.type = "button";
142
+ this.#toggle.className = "tool-call-toggle";
143
+ this.#toggle.setAttribute("part", "tool-card-toggle");
144
+ this.#toggle.setAttribute("aria-expanded", "false");
145
+ this.#toggle.textContent = strings.details;
146
+ this.#toggle.addEventListener("click", () => this.#setExpanded(!this.#expanded()));
147
+
148
+ const body = document.createElement("div");
149
+ body.className = "tool-call-body";
150
+ body.setAttribute("part", "tool-card-body");
151
+ body.append(argsSection.root, resultSection.root);
152
+
153
+ this.element.append(head, this.#toggle, body);
154
+ }
108
155
 
109
- if (mode === TOOL_DISPLAY.FULL) {
110
- const argsEl = document.createElement("pre");
111
- argsEl.className = "tool-call-args";
112
- argsEl.setAttribute("part", "tool-card-args");
113
- argsEl.textContent = JSON.stringify(args, null, 2);
114
- this.element.append(argsEl);
115
- }
156
+ /**
157
+ * Record that a human approved or declined this call, as a line in the card.
158
+ *
159
+ * Approval used to leave no trace at all: a declined call became a tool
160
+ * result saying so, while an approved one simply ran, making the transcript
161
+ * of a gated call byte-identical to one that was never gated. The prompt is
162
+ * gone once answered, so this is where the decision lives.
163
+ */
164
+ recordDecision(kind: "approved" | "declined"): void {
165
+ this.element.setAttribute("data-decision", kind);
166
+ this.#decision.textContent =
167
+ kind === "approved" ? this.#strings.decisionApproved : this.#strings.decisionDeclined;
168
+ this.#decision.hidden = false;
116
169
  }
117
170
 
118
171
  /** Whether {@link settle} has already run (so a terminal sweep can skip it). */
@@ -121,50 +174,55 @@ export class ToolCallCard {
121
174
  }
122
175
 
123
176
  /**
124
- * Flip the status pill to ``status`` and, unless in `minimal` mode, append a
125
- * collapsed body behind a click-to-expand toggle: the result alone (`full` /
126
- * `inline`), or the args + result together (`compact`).
177
+ * Flip the status pill to `status` and fill in the result region, whose
178
+ * heading names the outcome (result / error / declined).
127
179
  */
128
180
  settle(status: SettledStatus, text: string): void {
129
181
  // Idempotent: a duplicate `TOOL_CALL_RESULT`, or a replayed tool message
130
- // for an already-settled card, must not append a second toggle+body. The
131
- // first settle wins; later calls are ignored.
182
+ // for an already-settled card, must not overwrite the first outcome.
132
183
  if (this.#settled) {
133
184
  return;
134
185
  }
135
186
  this.#settled = true;
136
187
  this.element.setAttribute("data-status", status);
137
188
  this.#status.textContent = statusLabels(this.#strings)[status];
189
+ this.#resultLabel.textContent = resultLabels(this.#strings)[status];
190
+ this.#resultBody.textContent = formatPayload(text);
191
+ this.#resultSection.hidden = false;
192
+ }
138
193
 
139
- if (this.#mode === TOOL_DISPLAY.MINIMAL) {
140
- return;
141
- }
194
+ /** Build one labelled region of the body: a heading plus a payload block. */
195
+ #section(
196
+ kind: string,
197
+ labelText: string,
198
+ ): {
199
+ root: HTMLDivElement;
200
+ label: HTMLSpanElement;
201
+ body: HTMLPreElement;
202
+ } {
203
+ const root = document.createElement("div");
204
+ root.className = `tool-call-section tool-call-section--${kind}`;
205
+ root.setAttribute("part", `tool-card-section tool-card-${kind}-section`);
142
206
 
143
- const toggle = document.createElement("button");
144
- toggle.type = "button";
145
- toggle.className = "tool-call-toggle";
146
- toggle.setAttribute("part", "tool-card-toggle");
147
- toggle.setAttribute("aria-expanded", "false");
148
-
149
- const output = document.createElement("pre");
150
- output.className = "tool-call-result";
151
- output.setAttribute("part", "tool-card-result");
152
- output.hidden = true;
153
-
154
- if (this.#mode === TOOL_DISPLAY.COMPACT) {
155
- toggle.textContent = this.#strings.details;
156
- output.textContent = `args: ${JSON.stringify(this.#args)}\n\n${text}`;
157
- } else {
158
- toggle.textContent = resultLabels(this.#strings)[status];
159
- output.textContent = text;
160
- }
207
+ const label = document.createElement("span");
208
+ label.className = "tool-call-section-label";
209
+ label.setAttribute("part", `tool-card-section-label tool-card-${kind}-label`);
210
+ label.textContent = labelText;
161
211
 
162
- toggle.addEventListener("click", () => {
163
- const expand = output.hidden;
164
- output.hidden = !expand;
165
- toggle.setAttribute("aria-expanded", String(expand));
166
- });
212
+ const body = document.createElement("pre");
213
+ body.className = `tool-call-${kind}`;
214
+ body.setAttribute("part", `tool-card-${kind}`);
215
+
216
+ root.append(label, body);
217
+ return { root, label, body };
218
+ }
219
+
220
+ #expanded(): boolean {
221
+ return this.element.getAttribute("data-expanded") === "true";
222
+ }
167
223
 
168
- this.element.append(toggle, output);
224
+ #setExpanded(expand: boolean): void {
225
+ this.element.setAttribute("data-expanded", String(expand));
226
+ this.#toggle.setAttribute("aria-expanded", String(expand));
169
227
  }
170
228
  }
@@ -89,13 +89,21 @@ export interface UiStrings {
89
89
  toolError: string;
90
90
  /** Status pill on a declined call. */
91
91
  toolDeclined: string;
92
- /** Toggle label revealing a successful result (full mode). */
92
+ /** Accessible label on the panel resize handle. */
93
+ resizePanel: string;
94
+ /** Note on a tool card whose call a human approved. */
95
+ decisionApproved: string;
96
+ /** Note on a tool card whose call a human declined. */
97
+ decisionDeclined: string;
98
+ /** Heading over a tool card's arguments region. */
99
+ argumentsLabel: string;
100
+ /** Heading over a tool card's result region when the call succeeded. */
93
101
  resultLabel: string;
94
- /** Toggle label revealing an error (full mode). */
102
+ /** Heading over a tool card's result region when the call failed. */
95
103
  errorLabel: string;
96
- /** Toggle label revealing a declined call (full mode). */
104
+ /** Heading over a tool card's result region when the call was declined. */
97
105
  declinedLabel: string;
98
- /** Toggle label revealing args + result together (compact mode). */
106
+ /** Label on the toggle that expands a tool card's body. */
99
107
  details: string;
100
108
 
101
109
  // ── Confirmation card ───────────────────────────────────────────────────────
@@ -221,7 +229,7 @@ export const DEFAULT_UI_STRINGS: UiStrings = {
221
229
  "The page changed since you last looked at it. Call read_page to see the current page, then retry.",
222
230
  attachmentsStillUploading:
223
231
  "{n} file still uploading — it was not sent with this message and is still attached.",
224
- skillNeeds: "“{title}” needs: {fields}",
232
+ skillNeeds: "“{title}” needs {fields} — fill it in below, then send.",
225
233
 
226
234
  message: "Message",
227
235
  inputPlaceholder: "Ask anything…",
@@ -237,6 +245,10 @@ export const DEFAULT_UI_STRINGS: UiStrings = {
237
245
  toolDone: "✓ done",
238
246
  toolError: "⚠ error",
239
247
  toolDeclined: "⊘ declined",
248
+ resizePanel: "Resize the chat panel",
249
+ decisionApproved: "approved by you",
250
+ decisionDeclined: "declined by you",
251
+ argumentsLabel: "Arguments",
240
252
  resultLabel: "Result",
241
253
  errorLabel: "Error",
242
254
  declinedLabel: "Declined",
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION: string = "0.18.0";
1
+ export const VERSION: string = "0.20.0";