@artooi/ag-ui-web-component 0.28.0 → 0.29.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/CHANGELOG.md +417 -1
- package/README.md +371 -5
- package/dist/ag-ui-web-component.bundle.js +308 -50
- package/dist/ag-ui-web-component.bundle.js.map +4 -4
- package/dist/constants.d.ts +69 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/core/ag_ui_chat.d.ts +207 -0
- package/dist/core/ag_ui_chat.d.ts.map +1 -1
- package/dist/core/agui_client.d.ts +38 -0
- package/dist/core/agui_client.d.ts.map +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1516 -76
- package/dist/index.js.map +4 -4
- package/dist/ui/approval_card.d.ts +18 -0
- package/dist/ui/approval_card.d.ts.map +1 -1
- package/dist/ui/checkpoint_menu.d.ts +10 -0
- package/dist/ui/checkpoint_menu.d.ts.map +1 -1
- package/dist/ui/confirmation_card.d.ts +16 -0
- package/dist/ui/confirmation_card.d.ts.map +1 -1
- package/dist/ui/message_actions.d.ts +46 -0
- package/dist/ui/message_actions.d.ts.map +1 -0
- package/dist/ui/page_quote_offer.d.ts +33 -0
- package/dist/ui/page_quote_offer.d.ts.map +1 -0
- package/dist/ui/quote_selection.d.ts +66 -0
- package/dist/ui/quote_selection.d.ts.map +1 -0
- package/dist/ui/relative_time.d.ts +10 -0
- package/dist/ui/relative_time.d.ts.map +1 -1
- package/dist/ui/stick_to_bottom.d.ts +55 -0
- package/dist/ui/stick_to_bottom.d.ts.map +1 -0
- package/dist/ui/styles.d.ts +1 -1
- package/dist/ui/styles.d.ts.map +1 -1
- package/dist/ui/suggestion_chips.d.ts +29 -0
- package/dist/ui/suggestion_chips.d.ts.map +1 -0
- package/dist/ui/thread_drawer.d.ts +10 -0
- package/dist/ui/thread_drawer.d.ts.map +1 -1
- package/dist/ui/tool_call_card.d.ts +8 -0
- package/dist/ui/tool_call_card.d.ts.map +1 -1
- package/dist/ui/ui_strings.d.ts +40 -0
- package/dist/ui/ui_strings.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +75 -0
- package/src/core/ag_ui_chat.ts +927 -73
- package/src/core/agui_client.ts +63 -0
- package/src/index.ts +39 -0
- package/src/ui/approval_card.ts +90 -2
- package/src/ui/checkpoint_menu.ts +22 -5
- package/src/ui/confirmation_card.ts +29 -1
- package/src/ui/message_actions.ts +158 -0
- package/src/ui/page_quote_offer.ts +215 -0
- package/src/ui/quote_selection.ts +345 -0
- package/src/ui/relative_time.ts +11 -0
- package/src/ui/stick_to_bottom.ts +126 -0
- package/src/ui/styles.ts +227 -0
- package/src/ui/suggestion_chips.ts +73 -0
- package/src/ui/thread_drawer.ts +22 -2
- package/src/ui/tool_call_card.ts +9 -0
- package/src/ui/ui_strings.ts +60 -0
- package/src/version.ts +1 -1
package/src/ui/styles.ts
CHANGED
|
@@ -571,6 +571,7 @@ export const STYLES = `
|
|
|
571
571
|
opacity: 0;
|
|
572
572
|
}
|
|
573
573
|
|
|
574
|
+
:host([collapsed]:is([placement="embedded"], [placement="page"])) .messages-wrap,
|
|
574
575
|
:host([collapsed]:is([placement="embedded"], [placement="page"])) .messages,
|
|
575
576
|
:host([collapsed]:is([placement="embedded"], [placement="page"])) .input-row,
|
|
576
577
|
:host([collapsed]:is([placement="embedded"], [placement="page"])) .skill-chips,
|
|
@@ -579,9 +580,113 @@ export const STYLES = `
|
|
|
579
580
|
display: none;
|
|
580
581
|
}
|
|
581
582
|
|
|
583
|
+
/* Jump-to-latest: shown only once the reader has scrolled away *and* missed
|
|
584
|
+
something. Anchored to the panel rather than the list so it does not scroll
|
|
585
|
+
with the content it is offering to scroll to. */
|
|
586
|
+
/* The transcript's own box, and the only one whose foot is the transcript's
|
|
587
|
+
foot. The panel's foot is below the composer, the chips and the footer. */
|
|
588
|
+
.messages-wrap {
|
|
589
|
+
position: relative;
|
|
590
|
+
flex: 1;
|
|
591
|
+
min-height: 0;
|
|
592
|
+
display: flex;
|
|
593
|
+
flex-direction: column;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.jump-latest {
|
|
597
|
+
position: absolute;
|
|
598
|
+
left: 50%;
|
|
599
|
+
transform: translateX(-50%);
|
|
600
|
+
bottom: var(--_pad);
|
|
601
|
+
z-index: 2;
|
|
602
|
+
display: none;
|
|
603
|
+
align-items: center;
|
|
604
|
+
gap: 0.35em;
|
|
605
|
+
padding: 0.4em 0.9em;
|
|
606
|
+
border: 1px solid var(--_border);
|
|
607
|
+
border-radius: 999px;
|
|
608
|
+
/* A raised surface, not the panel's own background. Reusing --_bg made the
|
|
609
|
+
pill the same colour as everything behind it, leaving a 1px border and a
|
|
610
|
+
shadow to carry the whole affordance -- and a dark-on-dark shadow carries
|
|
611
|
+
nothing. --_hover is the token that already means "lifted off the panel",
|
|
612
|
+
and it separates in both themes without competing with the accent the send
|
|
613
|
+
button owns. */
|
|
614
|
+
background: var(--_hover);
|
|
615
|
+
color: var(--_text);
|
|
616
|
+
font: inherit;
|
|
617
|
+
font-size: 0.85em;
|
|
618
|
+
cursor: pointer;
|
|
619
|
+
box-shadow: 0 2px 10px rgb(0 0 0 / 0.18);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
.jump-latest[data-missed="true"] {
|
|
623
|
+
display: flex;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.jump-latest:hover {
|
|
627
|
+
border-color: var(--_accent);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/* The offer to quote a selection. Positioned in script against the transcript
|
|
631
|
+
box, which is the only ancestor whose top and foot are the transcript's --
|
|
632
|
+
the same reason .jump-latest lives here. The translate is the half the
|
|
633
|
+
script does not do: script sets the point the offer hangs from, CSS decides
|
|
634
|
+
which corner of the offer that point is. */
|
|
635
|
+
.quote-selection {
|
|
636
|
+
position: absolute;
|
|
637
|
+
z-index: 2;
|
|
638
|
+
transform: translate(-50%, -100%);
|
|
639
|
+
padding: 0.25em 0.7em;
|
|
640
|
+
border: 1px solid var(--_border);
|
|
641
|
+
border-radius: 999px;
|
|
642
|
+
background: var(--_hover);
|
|
643
|
+
color: var(--_text);
|
|
644
|
+
font: inherit;
|
|
645
|
+
font-size: 0.8em;
|
|
646
|
+
line-height: 1.6;
|
|
647
|
+
white-space: nowrap;
|
|
648
|
+
cursor: pointer;
|
|
649
|
+
box-shadow: 0 2px 10px rgb(0 0 0 / 0.18);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/* Flipped under the selection when there was no room above it. Only the
|
|
653
|
+
vertical half of the translate changes: it still hangs from its own centre
|
|
654
|
+
horizontally. */
|
|
655
|
+
.quote-selection[data-below="true"] {
|
|
656
|
+
transform: translate(-50%, 0);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.quote-selection:hover {
|
|
660
|
+
border-color: var(--_accent);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/* Screen-reader-only status region. Off-screen rather than display:none or
|
|
664
|
+
visibility:hidden, both of which take the element out of the accessibility
|
|
665
|
+
tree entirely -- a hidden live region announces nothing at all, which is the
|
|
666
|
+
classic way this pattern is written wrong.
|
|
667
|
+
|
|
668
|
+
The 1px box with clip-path, rather than width/height 0, is the shape that
|
|
669
|
+
survives: a zero-sized element is dropped from the tree by some engines. */
|
|
670
|
+
.sr-only {
|
|
671
|
+
position: absolute;
|
|
672
|
+
width: 1px;
|
|
673
|
+
height: 1px;
|
|
674
|
+
margin: -1px;
|
|
675
|
+
padding: 0;
|
|
676
|
+
border: 0;
|
|
677
|
+
overflow: hidden;
|
|
678
|
+
white-space: nowrap;
|
|
679
|
+
clip-path: inset(50%);
|
|
680
|
+
}
|
|
681
|
+
|
|
582
682
|
.messages {
|
|
583
683
|
flex: 1;
|
|
584
684
|
overflow-y: auto;
|
|
685
|
+
/* The browser's own scroll anchoring competes with the scroller for the same
|
|
686
|
+
job and wins unpredictably -- it can hold the view still exactly when we
|
|
687
|
+
want to follow. Turned off so following is decided in one place. Safari
|
|
688
|
+
does not implement it, which is itself a reason not to depend on it. */
|
|
689
|
+
overflow-anchor: none;
|
|
585
690
|
padding: var(--_pad);
|
|
586
691
|
display: flex;
|
|
587
692
|
flex-direction: column;
|
|
@@ -1747,6 +1852,7 @@ export const STYLES = `
|
|
|
1747
1852
|
|
|
1748
1853
|
.confirm-actions {
|
|
1749
1854
|
display: flex;
|
|
1855
|
+
flex-wrap: wrap;
|
|
1750
1856
|
gap: 8px;
|
|
1751
1857
|
justify-content: flex-end;
|
|
1752
1858
|
}
|
|
@@ -1773,6 +1879,99 @@ export const STYLES = `
|
|
|
1773
1879
|
color: #ffffff;
|
|
1774
1880
|
}
|
|
1775
1881
|
|
|
1882
|
+
/* The session waiver. Deliberately the quietest of the three: it is the widest
|
|
1883
|
+
decision on the card, so it should be reachable without being the one the eye
|
|
1884
|
+
lands on when the user means to say yes once. */
|
|
1885
|
+
.confirm-btn--always {
|
|
1886
|
+
font-weight: 500;
|
|
1887
|
+
opacity: 0.85;
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
.confirm-btn--always:hover,
|
|
1891
|
+
.confirm-btn--always:focus-visible {
|
|
1892
|
+
opacity: 1;
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
/* Editable arguments on an approval card. A plain field rather than a code
|
|
1896
|
+
editor: it holds the JSON a card already displays, and the only interaction
|
|
1897
|
+
is correcting a value before letting the call run. */
|
|
1898
|
+
.approval-edit {
|
|
1899
|
+
display: flex;
|
|
1900
|
+
flex-direction: column;
|
|
1901
|
+
gap: 6px;
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
.approval-args {
|
|
1905
|
+
box-sizing: border-box;
|
|
1906
|
+
width: 100%;
|
|
1907
|
+
resize: vertical;
|
|
1908
|
+
border: 1px solid var(--_border);
|
|
1909
|
+
border-radius: 8px;
|
|
1910
|
+
padding: 8px;
|
|
1911
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1912
|
+
font-size: 0.85em;
|
|
1913
|
+
background: var(--_bg);
|
|
1914
|
+
color: var(--_fg);
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
.approval-args:focus-visible {
|
|
1918
|
+
border-color: var(--_accent);
|
|
1919
|
+
outline: none;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
.approval-error {
|
|
1923
|
+
font-size: 0.85em;
|
|
1924
|
+
color: var(--_danger);
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
/* Message action row. Sits under a finished assistant bubble.
|
|
1928
|
+
|
|
1929
|
+
The wrap is insurance rather than a fix: these buttons are glyph-only, so at
|
|
1930
|
+
every width tested they fit on one line and removing the wrap changes
|
|
1931
|
+
nothing. It is here because the confirmation row one release earlier did
|
|
1932
|
+
overflow when it gained a third button, off the left edge and outside its own
|
|
1933
|
+
card, and the difference between the two rows is only that this one's labels
|
|
1934
|
+
are icons today. */
|
|
1935
|
+
.message-actions {
|
|
1936
|
+
display: flex;
|
|
1937
|
+
flex-wrap: wrap;
|
|
1938
|
+
gap: 4px;
|
|
1939
|
+
/* Negative, and that is the point. The answer group is a flex column with its
|
|
1940
|
+
own gap, so a positive margin here pushes the row further from the message
|
|
1941
|
+
it acts on than the next card is below it -- the buttons then read as
|
|
1942
|
+
belonging to whatever follows. Pulling back inside the gap is what makes
|
|
1943
|
+
them the message's own. */
|
|
1944
|
+
margin-top: -6px;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
.message-action {
|
|
1948
|
+
border: none;
|
|
1949
|
+
border-radius: 6px;
|
|
1950
|
+
padding: 2px 6px;
|
|
1951
|
+
font: inherit;
|
|
1952
|
+
line-height: 1.2;
|
|
1953
|
+
cursor: pointer;
|
|
1954
|
+
background: transparent;
|
|
1955
|
+
color: var(--_muted);
|
|
1956
|
+
opacity: 0.75;
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
.message-action:hover,
|
|
1960
|
+
.message-action:focus-visible {
|
|
1961
|
+
opacity: 1;
|
|
1962
|
+
background: var(--_border);
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
.message-action[aria-pressed="true"] {
|
|
1966
|
+
opacity: 1;
|
|
1967
|
+
color: var(--_accent);
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
.message-action--confirmed {
|
|
1971
|
+
opacity: 1;
|
|
1972
|
+
color: var(--_accent);
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1776
1975
|
/* Approval card — the server-side-tool gate (approve/deny an interrupt). */
|
|
1777
1976
|
.approval {
|
|
1778
1977
|
align-self: stretch;
|
|
@@ -1917,6 +2116,34 @@ export const STYLES = `
|
|
|
1917
2116
|
border-color: var(--_accent);
|
|
1918
2117
|
}
|
|
1919
2118
|
|
|
2119
|
+
/* Follow-up suggestion chips. Deliberately the skill chips' shape rather than a
|
|
2120
|
+
second chip vocabulary -- both are "a question you could ask", and the only
|
|
2121
|
+
difference is who chose it. Inside the transcript, so they scroll with the
|
|
2122
|
+
answer they follow instead of hovering above the composer. */
|
|
2123
|
+
.suggestions {
|
|
2124
|
+
display: flex;
|
|
2125
|
+
flex-wrap: wrap;
|
|
2126
|
+
gap: 6px;
|
|
2127
|
+
align-self: stretch;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
.suggestion-chip {
|
|
2131
|
+
border: 1px solid var(--_border);
|
|
2132
|
+
border-radius: 999px;
|
|
2133
|
+
padding: 4px 12px;
|
|
2134
|
+
font: inherit;
|
|
2135
|
+
font-size: 0.9em;
|
|
2136
|
+
text-align: left;
|
|
2137
|
+
cursor: pointer;
|
|
2138
|
+
background: var(--_assistant-bg);
|
|
2139
|
+
color: var(--_fg);
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
.suggestion-chip:hover,
|
|
2143
|
+
.suggestion-chip:focus-visible {
|
|
2144
|
+
border-color: var(--_accent);
|
|
2145
|
+
}
|
|
2146
|
+
|
|
1920
2147
|
.skill-palette {
|
|
1921
2148
|
margin: 8px 12px 0;
|
|
1922
2149
|
display: flex;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { UiStrings } from "./ui_strings.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Most prompts one push may draw.
|
|
5
|
+
*
|
|
6
|
+
* **Keep in step with `MAX_SUGGESTIONS` in django-ag-ui's
|
|
7
|
+
* `agent/suggestions_activity.py`**, which raises past it. Mirroring is the
|
|
8
|
+
* whole point: this side silently draws no more than its limit and has no
|
|
9
|
+
* channel to report the difference, so a producer that does not know the same
|
|
10
|
+
* number ships suggestions that never appear. That is the hole the chart bounds
|
|
11
|
+
* exist to close, and it was found there by shipping it.
|
|
12
|
+
*/
|
|
13
|
+
export const MAX_SUGGESTIONS = 4;
|
|
14
|
+
|
|
15
|
+
/** Longest one prompt may be. Mirrored for the same reason as the count. */
|
|
16
|
+
export const MAX_SUGGESTION_CHARS = 120;
|
|
17
|
+
|
|
18
|
+
/** The `prompts` a `suggestions` activity carries, or `null` when it carries none. */
|
|
19
|
+
export function suggestionPrompts(content: unknown): string[] | null {
|
|
20
|
+
if (typeof content !== "object" || content === null) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const raw = (content as { prompts?: unknown }).prompts;
|
|
24
|
+
if (!Array.isArray(raw)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const prompts = raw
|
|
28
|
+
.filter((prompt): prompt is string => typeof prompt === "string")
|
|
29
|
+
.map((prompt) => prompt.trim())
|
|
30
|
+
.filter((prompt) => prompt !== "" && prompt.length <= MAX_SUGGESTION_CHARS)
|
|
31
|
+
.slice(0, MAX_SUGGESTIONS);
|
|
32
|
+
return prompts.length === 0 ? null : prompts;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Draw follow-up prompts as chips that send themselves when clicked.
|
|
37
|
+
*
|
|
38
|
+
* Returns `null` when nothing survives, which is the registry's signal to draw
|
|
39
|
+
* nothing rather than an empty row -- the same contract the chart renderer uses
|
|
40
|
+
* for a spec it cannot draw.
|
|
41
|
+
*
|
|
42
|
+
* Buttons rather than links or list items: each one performs an action in the
|
|
43
|
+
* page, and the thing it sends is the label, so the accessible name is the
|
|
44
|
+
* prompt itself and needs no `aria-label` restating it.
|
|
45
|
+
*/
|
|
46
|
+
export function renderSuggestionChips(
|
|
47
|
+
content: unknown,
|
|
48
|
+
strings: UiStrings,
|
|
49
|
+
onPick: (prompt: string) => void,
|
|
50
|
+
): HTMLElement | null {
|
|
51
|
+
const prompts = suggestionPrompts(content);
|
|
52
|
+
if (prompts === null) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const row = document.createElement("div");
|
|
56
|
+
row.className = "suggestions";
|
|
57
|
+
row.setAttribute("part", "suggestions");
|
|
58
|
+
// A group, labelled: without it a screen reader meets a row of unrelated
|
|
59
|
+
// buttons with no hint that they are the assistant's offer rather than the
|
|
60
|
+
// page's own controls.
|
|
61
|
+
row.setAttribute("role", "group");
|
|
62
|
+
row.setAttribute("aria-label", strings.suggestions);
|
|
63
|
+
for (const prompt of prompts) {
|
|
64
|
+
const chip = document.createElement("button");
|
|
65
|
+
chip.type = "button";
|
|
66
|
+
chip.className = "suggestion-chip";
|
|
67
|
+
chip.setAttribute("part", "suggestion-chip");
|
|
68
|
+
chip.textContent = prompt;
|
|
69
|
+
chip.addEventListener("click", () => onPick(prompt));
|
|
70
|
+
row.appendChild(chip);
|
|
71
|
+
}
|
|
72
|
+
return row;
|
|
73
|
+
}
|
package/src/ui/thread_drawer.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ThreadMeta } from "../core/conversation_store.js";
|
|
2
|
-
import { relativeTime } from "./relative_time.js";
|
|
2
|
+
import { type RelativeTimeFormatter, relativeTime } from "./relative_time.js";
|
|
3
3
|
import { DEFAULT_UI_STRINGS, type UiStrings } from "./ui_strings.js";
|
|
4
4
|
|
|
5
5
|
/** Actions the host ({@link AgUiChat}) wires to the drawer's rows. */
|
|
@@ -35,6 +35,7 @@ export class ThreadDrawer {
|
|
|
35
35
|
readonly #heading: HTMLSpanElement;
|
|
36
36
|
readonly #newButton: HTMLButtonElement;
|
|
37
37
|
readonly #list: HTMLDivElement;
|
|
38
|
+
#formatRelativeTime: RelativeTimeFormatter | null = null;
|
|
38
39
|
#strings: UiStrings;
|
|
39
40
|
#threads: readonly ThreadMeta[] = [];
|
|
40
41
|
#activeId = "";
|
|
@@ -91,6 +92,25 @@ export class ThreadDrawer {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
/** Re-localize the drawer's chrome and rows (the host calls this on connect). */
|
|
95
|
+
/**
|
|
96
|
+
* Replace the timestamp formatter, or restore the built-in with `null`.
|
|
97
|
+
*
|
|
98
|
+
* The built-in is deliberately locale-neutral -- there is no `Intl` anywhere
|
|
99
|
+
* in this component, so it never disagrees with a host's own formatting by
|
|
100
|
+
* guessing a locale. That is a defensible default and a poor requirement, so
|
|
101
|
+
* this is the way out.
|
|
102
|
+
*/
|
|
103
|
+
setRelativeTimeFormatter(format: RelativeTimeFormatter | null): void {
|
|
104
|
+
this.#formatRelativeTime = format;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** This row's timestamp, through the host's formatter when it set one. */
|
|
108
|
+
#formatTime(timestamp: number): string {
|
|
109
|
+
return this.#formatRelativeTime !== null
|
|
110
|
+
? this.#formatRelativeTime(timestamp)
|
|
111
|
+
: relativeTime(timestamp, undefined, this.#strings);
|
|
112
|
+
}
|
|
113
|
+
|
|
94
114
|
setStrings(strings: UiStrings): void {
|
|
95
115
|
this.#strings = strings;
|
|
96
116
|
this.#panel.setAttribute("aria-label", strings.chatHistory);
|
|
@@ -202,7 +222,7 @@ export class ThreadDrawer {
|
|
|
202
222
|
const time = document.createElement("span");
|
|
203
223
|
time.className = "drawer-row-time";
|
|
204
224
|
time.setAttribute("part", "drawer-row-time");
|
|
205
|
-
time.textContent =
|
|
225
|
+
time.textContent = this.#formatTime(meta.updatedAt);
|
|
206
226
|
const preview = document.createElement("span");
|
|
207
227
|
preview.className = "drawer-row-preview";
|
|
208
228
|
preview.setAttribute("part", "drawer-row-preview");
|
package/src/ui/tool_call_card.ts
CHANGED
|
@@ -90,6 +90,14 @@ export class ToolCallCard {
|
|
|
90
90
|
readonly #resultLabel: HTMLSpanElement;
|
|
91
91
|
readonly #resultBody: HTMLPreElement;
|
|
92
92
|
readonly #strings: UiStrings;
|
|
93
|
+
/**
|
|
94
|
+
* The arguments this call was made with.
|
|
95
|
+
*
|
|
96
|
+
* Retained rather than only rendered, because an approval interrupt names a
|
|
97
|
+
* `toolCallId` and nothing else -- so this card is the only place the args
|
|
98
|
+
* still exist when the user is asked to approve, edit or deny the call.
|
|
99
|
+
*/
|
|
100
|
+
readonly args: Record<string, unknown>;
|
|
93
101
|
#settled = false;
|
|
94
102
|
|
|
95
103
|
constructor(
|
|
@@ -99,6 +107,7 @@ export class ToolCallCard {
|
|
|
99
107
|
strings: UiStrings = DEFAULT_UI_STRINGS,
|
|
100
108
|
) {
|
|
101
109
|
this.#strings = strings;
|
|
110
|
+
this.args = args;
|
|
102
111
|
|
|
103
112
|
this.element = document.createElement("div");
|
|
104
113
|
this.element.className = "tool-call";
|
package/src/ui/ui_strings.ts
CHANGED
|
@@ -33,6 +33,18 @@ export interface UiStrings {
|
|
|
33
33
|
// ── Messages region ─────────────────────────────────────────────────────────
|
|
34
34
|
/** `aria-label` of the scrolling message log. */
|
|
35
35
|
conversation: string;
|
|
36
|
+
/** The button offering to return to the foot of the transcript. */
|
|
37
|
+
jumpToLatest: string;
|
|
38
|
+
/** Announced when a turn starts. Screen-reader only; never rendered. */
|
|
39
|
+
announceResponding: string;
|
|
40
|
+
/** Announced when the answer has finished arriving. Screen-reader only. */
|
|
41
|
+
announceAnswerReady: string;
|
|
42
|
+
/** Announced when a card is waiting for the user's decision. Token: `{count}`. */
|
|
43
|
+
announceAwaitingDecision: string;
|
|
44
|
+
/** Announced when the user stopped the run. Screen-reader only. */
|
|
45
|
+
announceStopped: string;
|
|
46
|
+
/** Announced when the run failed. Screen-reader only. */
|
|
47
|
+
announceFailed: string;
|
|
36
48
|
/** `aria-label` of the "thinking" pending indicator, and the thoughts region's
|
|
37
49
|
* header while the model is still reasoning. */
|
|
38
50
|
thinking: string;
|
|
@@ -48,6 +60,10 @@ export interface UiStrings {
|
|
|
48
60
|
declinedAction: string;
|
|
49
61
|
/** A navigating tool's card text while the page reloads. */
|
|
50
62
|
navigating: string;
|
|
63
|
+
/** Notice shown when the server replaced the conversation wholesale. */
|
|
64
|
+
historyReplaced: string;
|
|
65
|
+
/** Notice shown when a pushed chart could not be drawn and was removed. */
|
|
66
|
+
chartUndrawable: string;
|
|
51
67
|
/** Missing-placeholder skill hint. Tokens: `{title}`, `{fields}`. */
|
|
52
68
|
skillNeeds: string;
|
|
53
69
|
/** Notice shown when the agent condensed earlier turns. Token: `{count}`. */
|
|
@@ -116,8 +132,32 @@ export interface UiStrings {
|
|
|
116
132
|
details: string;
|
|
117
133
|
|
|
118
134
|
// ── Confirmation card ───────────────────────────────────────────────────────
|
|
135
|
+
/** `aria-label` of the editable arguments field on an approval card. */
|
|
136
|
+
approvalEditArgs: string;
|
|
137
|
+
/** Shown when the edited arguments are not valid JSON. */
|
|
138
|
+
approvalArgsInvalid: string;
|
|
139
|
+
/** Shown when the edited arguments parse but are not a JSON object. */
|
|
140
|
+
approvalArgsNotAnObject: string;
|
|
141
|
+
/** `aria-label` of the follow-up suggestion chips row. */
|
|
142
|
+
suggestions: string;
|
|
143
|
+
/** `aria-label` of a message's action row. */
|
|
144
|
+
messageActions: string;
|
|
145
|
+
/** The offer that floats beside a selection in the transcript. */
|
|
146
|
+
quoteSelection: string;
|
|
147
|
+
/** Copy this message (button `title` / `aria-label`). Its confirmation and
|
|
148
|
+
* failure text are the code block's `copied` / `copyFailed`, which say the
|
|
149
|
+
* same thing about the same clipboard. */
|
|
150
|
+
copyMessage: string;
|
|
151
|
+
/** Ask for a different answer to the same question. */
|
|
152
|
+
retryMessage: string;
|
|
153
|
+
/** Rate this answer as good. */
|
|
154
|
+
feedbackUp: string;
|
|
155
|
+
/** Rate this answer as poor. */
|
|
156
|
+
feedbackDown: string;
|
|
119
157
|
/** `aria-label` of the inline confirmation card. */
|
|
120
158
|
confirmAction: string;
|
|
159
|
+
/** Waive confirmation for this tool for the rest of the session. Token: `{tool}`. */
|
|
160
|
+
confirmAlways: string;
|
|
121
161
|
/** Generic confirmation prompt when a tool has no `x-confirm`. Token: `{tool}`. */
|
|
122
162
|
confirmRun: string;
|
|
123
163
|
/** Confirm button. */
|
|
@@ -229,6 +269,12 @@ export const DEFAULT_UI_STRINGS: UiStrings = {
|
|
|
229
269
|
forkedRun: "branched",
|
|
230
270
|
|
|
231
271
|
conversation: "Conversation",
|
|
272
|
+
jumpToLatest: "Jump to latest",
|
|
273
|
+
announceResponding: "Assistant is responding",
|
|
274
|
+
announceAnswerReady: "Assistant answered",
|
|
275
|
+
announceAwaitingDecision: "{count} action is waiting for your approval",
|
|
276
|
+
announceStopped: "Response stopped",
|
|
277
|
+
announceFailed: "The response failed",
|
|
232
278
|
thinking: "Assistant is thinking…",
|
|
233
279
|
thoughts: "Thoughts",
|
|
234
280
|
stopped: "⏹ Stopped",
|
|
@@ -236,6 +282,9 @@ export const DEFAULT_UI_STRINGS: UiStrings = {
|
|
|
236
282
|
noResult: "No result returned.",
|
|
237
283
|
declinedAction: "User declined the action.",
|
|
238
284
|
navigating: "Navigating…",
|
|
285
|
+
historyReplaced:
|
|
286
|
+
"The server replaced this conversation's history. Reload to see the updated transcript.",
|
|
287
|
+
chartUndrawable: "A chart could not be drawn from the data sent, so it was removed.",
|
|
239
288
|
historyCompacted: "Earlier turns condensed to fit the context window ({count} removed)",
|
|
240
289
|
usingSkill: "Using skill {name}",
|
|
241
290
|
runInterrupted: "The previous response didn’t finish — the page changed before it arrived.",
|
|
@@ -270,7 +319,18 @@ export const DEFAULT_UI_STRINGS: UiStrings = {
|
|
|
270
319
|
declinedLabel: "Declined",
|
|
271
320
|
details: "Details",
|
|
272
321
|
|
|
322
|
+
approvalEditArgs: "Edit the arguments before approving",
|
|
323
|
+
approvalArgsInvalid: "That is not valid JSON, so nothing was sent.",
|
|
324
|
+
approvalArgsNotAnObject: "Arguments have to be a JSON object.",
|
|
325
|
+
suggestions: "Suggested follow-ups",
|
|
326
|
+
messageActions: "Message actions",
|
|
327
|
+
quoteSelection: "Quote",
|
|
328
|
+
copyMessage: "Copy message",
|
|
329
|
+
retryMessage: "Try again",
|
|
330
|
+
feedbackUp: "Good answer",
|
|
331
|
+
feedbackDown: "Poor answer",
|
|
273
332
|
confirmAction: "Confirm action",
|
|
333
|
+
confirmAlways: "Always allow",
|
|
274
334
|
confirmRun: "Run “{tool}”?",
|
|
275
335
|
confirm: "Confirm",
|
|
276
336
|
cancel: "Cancel",
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION: string = "0.
|
|
1
|
+
export const VERSION: string = "0.29.0";
|