@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
|
@@ -9,6 +9,11 @@ export interface ApprovalRequest {
|
|
|
9
9
|
message?: string;
|
|
10
10
|
/** Tool name, surfaced as a `data-tool-name` attribute for styling/tests. */
|
|
11
11
|
toolName?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The call's arguments, shown for editing when {@link ApprovalOptions.onEdit}
|
|
14
|
+
* is set. Omitted when the interrupt names no call whose arguments are known.
|
|
15
|
+
*/
|
|
16
|
+
args?: Record<string, unknown>;
|
|
12
17
|
}
|
|
13
18
|
/** Options for {@link requestApproval}. */
|
|
14
19
|
export interface ApprovalOptions {
|
|
@@ -19,6 +24,19 @@ export interface ApprovalOptions {
|
|
|
19
24
|
signal?: AbortSignal;
|
|
20
25
|
/** Localized strings; defaults to the English {@link DEFAULT_UI_STRINGS}. */
|
|
21
26
|
strings?: UiStrings;
|
|
27
|
+
/**
|
|
28
|
+
* Offer the call's arguments for editing, and receive what the user approved.
|
|
29
|
+
*
|
|
30
|
+
* Called **only** on approval, and only when the text parses and differs from
|
|
31
|
+
* what was proposed -- an untouched call resolves as a plain approval, so a
|
|
32
|
+
* server sees `editedArgs` exactly when something was actually edited.
|
|
33
|
+
*
|
|
34
|
+
* Absent means no editor, which is deliberate: AG-UI gates this on the
|
|
35
|
+
* agent's own `approveWithEdits` capability, and a card that let a user
|
|
36
|
+
* rewrite arguments a server will discard is worse than one that does not
|
|
37
|
+
* offer to.
|
|
38
|
+
*/
|
|
39
|
+
onEdit?: (args: Record<string, unknown>) => void;
|
|
22
40
|
}
|
|
23
41
|
/**
|
|
24
42
|
* A fully custom renderer for a server-side-tool approval, set via
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval_card.d.ts","sourceRoot":"","sources":["../../src/ui/approval_card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"approval_card.d.ts","sourceRoot":"","sources":["../../src/ui/approval_card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAYD,2CAA2C;AAC3C,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAClD;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,KAC7B,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,GAAG,UAAU,EACvB,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,OAAO,CAAC,CA4DlB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RunRow } from "../core/run_index.js";
|
|
2
|
+
import { type RelativeTimeFormatter } from "./relative_time.js";
|
|
2
3
|
import { type UiStrings } from "./ui_strings.js";
|
|
3
4
|
/** How the host continues a picked run. */
|
|
4
5
|
export type CheckpointVerb = "resume" | "fork";
|
|
@@ -23,6 +24,15 @@ export declare class CheckpointMenu {
|
|
|
23
24
|
/** Replace the rows. The host passes only `continuable` runs. */
|
|
24
25
|
setRuns(runs: readonly RunRow[]): void;
|
|
25
26
|
/** Re-localize a panel built before the host's strings resolved. */
|
|
27
|
+
/**
|
|
28
|
+
* Replace the timestamp formatter, or restore the built-in with `null`.
|
|
29
|
+
*
|
|
30
|
+
* The built-in is deliberately locale-neutral -- there is no `Intl` anywhere
|
|
31
|
+
* in this component, so it never disagrees with a host's own formatting by
|
|
32
|
+
* guessing a locale. That is a defensible default and a poor requirement, so
|
|
33
|
+
* this is the way out.
|
|
34
|
+
*/
|
|
35
|
+
setRelativeTimeFormatter(format: RelativeTimeFormatter | null): void;
|
|
26
36
|
setStrings(strings: UiStrings): void;
|
|
27
37
|
open(): void;
|
|
28
38
|
close(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkpoint_menu.d.ts","sourceRoot":"","sources":["../../src/ui/checkpoint_menu.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"checkpoint_menu.d.ts","sourceRoot":"","sources":["../../src/ui/checkpoint_menu.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,KAAK,qBAAqB,EAAgB,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE,2CAA2C;AAC3C,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,CAAC;AAqB/C;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc;;IACzB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IAWjC,YACE,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,EACrD,OAAO,GAAE,SAA8B,EA+BxC;IAED,iEAAiE;IACjE,OAAO,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAGrC;IAED,oEAAoE;IACpE;;;;;;;OAOG;IACH,wBAAwB,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,GAAG,IAAI,CAEnE;IASD,UAAU,CAAC,OAAO,EAAE,SAAS,GAAG,IAAI,CAKnC;IAED,IAAI,IAAI,IAAI,CAYX;IAED,KAAK,IAAI,IAAI,CAOZ;IAqCD,IAAI,KAAK,IAAI,OAAO,CAEnB;CAoHF"}
|
|
@@ -19,6 +19,16 @@ export interface ConfirmationOptions {
|
|
|
19
19
|
signal?: AbortSignal;
|
|
20
20
|
/** Localized strings; defaults to the English {@link DEFAULT_UI_STRINGS}. */
|
|
21
21
|
strings?: UiStrings;
|
|
22
|
+
/**
|
|
23
|
+
* Offer a third button — "always allow, this session" — and call this when
|
|
24
|
+
* the user picks it. The card still resolves `true`: the extra decision is
|
|
25
|
+
* *in addition to* approving this call, not instead of it.
|
|
26
|
+
*
|
|
27
|
+
* Absent means no button, which is deliberate: presence of the handler is
|
|
28
|
+
* what enables it, so the affordance can never be rendered with nothing
|
|
29
|
+
* listening. A caller that cannot honour the waiver simply does not pass one.
|
|
30
|
+
*/
|
|
31
|
+
onAlwaysAllow?: () => void;
|
|
22
32
|
}
|
|
23
33
|
/**
|
|
24
34
|
* Append an inline confirmation card to `host` (the chat message list) and
|
|
@@ -30,6 +40,12 @@ export interface ConfirmationOptions {
|
|
|
30
40
|
* Answering it removes it: the record of the decision belongs to the tool card
|
|
31
41
|
* this gates, which settles to `done` or `declined` and carries it. A spent
|
|
32
42
|
* form left in place reads as still outstanding.
|
|
43
|
+
*
|
|
44
|
+
* With `onAlwaysAllow` set the card offers a third button. A prompt that is
|
|
45
|
+
* approved nearly every time is not a decision, it is a speed bump — and the
|
|
46
|
+
* reflex it trains is what makes the rare refusal easy to miss. Letting the
|
|
47
|
+
* user say "not this one again, this session" is the affordance that keeps the
|
|
48
|
+
* remaining prompts meaningful.
|
|
33
49
|
*/
|
|
34
50
|
export declare function requestConfirmation(host: Node & ParentNode, request: ConfirmationRequest, options?: ConfirmationOptions): Promise<boolean>;
|
|
35
51
|
//# sourceMappingURL=confirmation_card.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirmation_card.d.ts","sourceRoot":"","sources":["../../src/ui/confirmation_card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE,kDAAkD;AAClD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAYD,+CAA+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"confirmation_card.d.ts","sourceRoot":"","sources":["../../src/ui/confirmation_card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE,kDAAkD;AAClD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAYD,+CAA+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,IAAI,GAAG,UAAU,EACvB,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,OAAO,CAAC,CAqElB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { UiStrings } from "./ui_strings.js";
|
|
2
|
+
/** What an action bar can do, beyond copying. */
|
|
3
|
+
export interface MessageActionsOptions {
|
|
4
|
+
/** Localized strings. */
|
|
5
|
+
strings: UiStrings;
|
|
6
|
+
/**
|
|
7
|
+
* The text Copy puts on the clipboard. A function rather than a string
|
|
8
|
+
* because a bubble's content is rewritten while it streams, and the bar is
|
|
9
|
+
* attached to the element rather than to a snapshot of it.
|
|
10
|
+
*/
|
|
11
|
+
text: () => string;
|
|
12
|
+
/**
|
|
13
|
+
* Report a rating for this message. Absent means no feedback buttons.
|
|
14
|
+
*
|
|
15
|
+
* The component stores nothing: a rating is the host's to keep, and a
|
|
16
|
+
* write-only table nobody reads is not worth a schema.
|
|
17
|
+
*/
|
|
18
|
+
onFeedback?: (rating: "up" | "down") => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Give one finished message bubble its row of actions.
|
|
22
|
+
*
|
|
23
|
+
* **Finished** is load-bearing. A streaming bubble reassigns its `innerHTML` on
|
|
24
|
+
* every delta, so anything attached mid-stream is discarded and rebuilt for
|
|
25
|
+
* each one -- the same constraint `attachCopyButtons` records, one level up.
|
|
26
|
+
*
|
|
27
|
+
* Retry is deliberately **not** here: it belongs to the last turn only, so the
|
|
28
|
+
* element owns it and moves it as the transcript grows. Everything on this bar
|
|
29
|
+
* is safe to offer on any message, however old.
|
|
30
|
+
*
|
|
31
|
+
* Idempotent -- a bubble already given a bar is skipped, so a re-render or a
|
|
32
|
+
* second call cannot stack rows.
|
|
33
|
+
*/
|
|
34
|
+
export declare function attachMessageActions(bubble: HTMLElement, options: MessageActionsOptions): void;
|
|
35
|
+
/**
|
|
36
|
+
* The empty action row on `bubble`, created if it has none yet.
|
|
37
|
+
*
|
|
38
|
+
* Shared so a bubble that wants *only* Retry -- a failed run, which has nothing
|
|
39
|
+
* worth copying and nothing to rate -- gets the same row, the same part name
|
|
40
|
+
* and the same accessible grouping as every other message, rather than a
|
|
41
|
+
* second thing that looks like one.
|
|
42
|
+
*/
|
|
43
|
+
export declare function messageActionBar(bubble: HTMLElement, strings: UiStrings): HTMLElement;
|
|
44
|
+
/** Build one action button, labelled for screen readers rather than by glyph. */
|
|
45
|
+
export declare function messageActionButton(modifier: string, label: string, glyph: string): HTMLButtonElement;
|
|
46
|
+
//# sourceMappingURL=message_actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message_actions.d.ts","sourceRoot":"","sources":["../../src/ui/message_actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAKjD,iDAAiD;AACjD,MAAM,WAAW,qBAAqB;IACpC,yBAAyB;IACzB,OAAO,EAAE,SAAS,CAAC;IACnB;;;;OAIG;IACH,IAAI,EAAE,MAAM,MAAM,CAAC;IACnB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAY9F;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,GAAG,WAAW,CAmBrF;AAQD,iFAAiF;AACjF,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,iBAAiB,CAYnB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** A live page-side offer. */
|
|
2
|
+
export interface PageQuoteOffer {
|
|
3
|
+
/** The button itself, for a host that wants to style or inspect it. */
|
|
4
|
+
readonly element: HTMLButtonElement;
|
|
5
|
+
/** Stop offering: every listener removed, the button and its styles gone. */
|
|
6
|
+
detach(): void;
|
|
7
|
+
}
|
|
8
|
+
/** What {@link attachQuoteOffer} needs to know. */
|
|
9
|
+
export interface PageQuoteOfferOptions {
|
|
10
|
+
/** Where a selection is worth offering to quote. */
|
|
11
|
+
within: HTMLElement;
|
|
12
|
+
/** What the offer says. */
|
|
13
|
+
label: string;
|
|
14
|
+
/**
|
|
15
|
+
* A subtree to stay out of -- the chat widget itself.
|
|
16
|
+
*
|
|
17
|
+
* Its transcript runs this same gesture on the inside, so without this a
|
|
18
|
+
* selection there would be offered twice and quoted twice.
|
|
19
|
+
*/
|
|
20
|
+
exclude: Node;
|
|
21
|
+
/** Take the offer. */
|
|
22
|
+
onQuote: (text: string) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Offer to quote what the user selects inside `within`.
|
|
26
|
+
*
|
|
27
|
+
* Nothing is quoted until the offer is taken -- which is the whole point. An
|
|
28
|
+
* automatic version of this is easy to write and horrible to use: every drag
|
|
29
|
+
* made to read, to copy, or to fix a typo silently appends to whatever the user
|
|
30
|
+
* was in the middle of typing.
|
|
31
|
+
*/
|
|
32
|
+
export declare function attachQuoteOffer(options: PageQuoteOfferOptions): PageQuoteOffer;
|
|
33
|
+
//# sourceMappingURL=page_quote_offer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page_quote_offer.d.ts","sourceRoot":"","sources":["../../src/ui/page_quote_offer.ts"],"names":[],"mappings":"AAuDA,8BAA8B;AAC9B,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,6EAA6E;IAC7E,MAAM,IAAI,IAAI,CAAC;CAChB;AAED,mDAAmD;AACnD,MAAM,WAAW,qBAAqB;IACpC,oDAAoD;IACpD,MAAM,EAAE,WAAW,CAAC;IACpB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,OAAO,EAAE,IAAI,CAAC;IACd,sBAAsB;IACtB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CA4F/E"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a text selection out of the shadow tree, and shaping it for the
|
|
3
|
+
* composer.
|
|
4
|
+
*
|
|
5
|
+
* Selection is the one DOM API shadow encapsulation genuinely broke, and it
|
|
6
|
+
* broke it in two different directions. WebKit rescopes a selection made inside
|
|
7
|
+
* a shadow tree to the **host** element, so `document.getSelection()` reports
|
|
8
|
+
* the whole widget and none of the words; Chromium exposes the shadow nodes
|
|
9
|
+
* directly, so the same call reports exactly the words. `getComposedRanges` is
|
|
10
|
+
* the settled answer to both -- it hands back real endpoints for each shadow
|
|
11
|
+
* root it is *given permission to see* -- but it is recent enough that the
|
|
12
|
+
* direct read has to stay behind it.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The longest quotation put into the composer, in characters.
|
|
16
|
+
*
|
|
17
|
+
* A cap rather than no cap because the point of a quotation is to say *which*
|
|
18
|
+
* part of an answer the next question is about. Select-all-then-quote is a
|
|
19
|
+
* gesture the transcript already answers -- it is the whole conversation, which
|
|
20
|
+
* the model has -- and pasting it back costs the user tokens to say nothing.
|
|
21
|
+
*/
|
|
22
|
+
export declare const MAX_QUOTE_CHARS = 500;
|
|
23
|
+
/** A selection worth offering to quote. */
|
|
24
|
+
export interface QuotableSelection {
|
|
25
|
+
/** The selected text, trimmed. Never empty. */
|
|
26
|
+
readonly text: string;
|
|
27
|
+
/**
|
|
28
|
+
* One **line** of the selection, for placing the affordance beside it.
|
|
29
|
+
*
|
|
30
|
+
* Deliberately not the selection's bounding box. See {@link lineToHangFrom}:
|
|
31
|
+
* the union of a selection spanning several elements has a centre with no
|
|
32
|
+
* selected text anywhere near it.
|
|
33
|
+
*/
|
|
34
|
+
readonly rect: DOMRect;
|
|
35
|
+
}
|
|
36
|
+
/** Where a gesture ended, in viewport coordinates. */
|
|
37
|
+
export interface Point {
|
|
38
|
+
readonly x: number;
|
|
39
|
+
readonly y: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The current selection, when it lies wholly inside `container`.
|
|
43
|
+
*
|
|
44
|
+
* `roots` are the shadow roots the read is allowed to look inside, in the shape
|
|
45
|
+
* `getComposedRanges` itself takes. Pass the one holding `container` for a
|
|
46
|
+
* selection made in a shadow tree, or nothing at all for one made in the page.
|
|
47
|
+
*
|
|
48
|
+
* `near` is where the gesture ended, when a pointer made it -- it decides which
|
|
49
|
+
* line of a multi-line selection the offer is hung from.
|
|
50
|
+
*
|
|
51
|
+
* `null` for no selection, a collapsed one, whitespace only, or one that
|
|
52
|
+
* starts or ends outside `container` -- a drag that ran off the transcript and
|
|
53
|
+
* into the page is not a quotation from the transcript.
|
|
54
|
+
*/
|
|
55
|
+
export declare function quotableSelection(container: HTMLElement, roots?: readonly ShadowRoot[], near?: Point): QuotableSelection | null;
|
|
56
|
+
/**
|
|
57
|
+
* Wrap `text` as a markdown blockquote, ready to be followed by a question.
|
|
58
|
+
*
|
|
59
|
+
* Markdown rather than a bespoke fence because the transcript renders markdown
|
|
60
|
+
* and the server reads markdown: a quotation that survives both ends without a
|
|
61
|
+
* convention to agree on first. The trailing blank line is what leaves the
|
|
62
|
+
* caret on a fresh paragraph, which is the whole point of quoting into a
|
|
63
|
+
* composer rather than sending straight away.
|
|
64
|
+
*/
|
|
65
|
+
export declare function asQuote(text: string): string;
|
|
66
|
+
//# sourceMappingURL=quote_selection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote_selection.d.ts","sourceRoot":"","sources":["../../src/ui/quote_selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED,sDAAsD;AACtD,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,WAAW,EACtB,KAAK,GAAE,SAAS,UAAU,EAAO,EACjC,IAAI,CAAC,EAAE,KAAK,GACX,iBAAiB,GAAG,IAAI,CAqB1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAa5C"}
|
|
@@ -10,4 +10,14 @@ import { type UiStrings } from "./ui_strings.js";
|
|
|
10
10
|
* filled in here; the bucketing stays integer-rounded and locale-neutral.
|
|
11
11
|
*/
|
|
12
12
|
export declare function relativeTime(timestamp: number, now?: number, strings?: UiStrings): string;
|
|
13
|
+
/**
|
|
14
|
+
* A host's replacement for {@link relativeTime}.
|
|
15
|
+
*
|
|
16
|
+
* Takes an epoch-milliseconds timestamp and returns the text a row shows.
|
|
17
|
+
* `Intl.RelativeTimeFormat` is the obvious implementation and is deliberately
|
|
18
|
+
* *not* the default: a component that guessed a locale would disagree with the
|
|
19
|
+
* host page's own formatting, and being wrong in a second language is worse
|
|
20
|
+
* than being neutral in one.
|
|
21
|
+
*/
|
|
22
|
+
export type RelativeTimeFormatter = (timestamp: number) => string;
|
|
13
23
|
//# sourceMappingURL=relative_time.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relative_time.d.ts","sourceRoot":"","sources":["../../src/ui/relative_time.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,MAAmB,EACxB,OAAO,GAAE,SAA8B,GACtC,MAAM,CAqBR"}
|
|
1
|
+
{"version":3,"file":"relative_time.d.ts","sourceRoot":"","sources":["../../src/ui/relative_time.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAErE;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,MAAmB,EACxB,OAAO,GAAE,SAA8B,GACtC,MAAM,CAqBR;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** A transcript that follows new content, unless the reader has other ideas. */
|
|
2
|
+
export interface StickToBottom {
|
|
3
|
+
/**
|
|
4
|
+
* New content arrived. Scrolls to the bottom only while following, so
|
|
5
|
+
* reading older messages during a run is no longer undone on the next token.
|
|
6
|
+
*/
|
|
7
|
+
readonly follow: () => void;
|
|
8
|
+
/** Go to the bottom and resume following, whatever the reader was doing. */
|
|
9
|
+
readonly jump: () => void;
|
|
10
|
+
/** Whether the transcript is currently following new content. */
|
|
11
|
+
readonly following: () => boolean;
|
|
12
|
+
readonly dispose: () => void;
|
|
13
|
+
}
|
|
14
|
+
export interface StickToBottomOptions {
|
|
15
|
+
/** The scrolling element -- the message list. */
|
|
16
|
+
readonly viewport: HTMLElement;
|
|
17
|
+
/**
|
|
18
|
+
* Called whenever the answer to "should a jump-to-latest affordance show?"
|
|
19
|
+
* changes. True means the reader has scrolled away *and* has since missed
|
|
20
|
+
* something; scrolling up through a settled transcript is not a reason to
|
|
21
|
+
* nag.
|
|
22
|
+
*/
|
|
23
|
+
readonly onMissedContent: (missed: boolean) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Follow the foot of a scrolling transcript, and stop when the reader scrolls
|
|
27
|
+
* away.
|
|
28
|
+
*
|
|
29
|
+
* Before this, eleven separate sites assigned `scrollTop = scrollHeight`
|
|
30
|
+
* unconditionally and nothing anywhere listened for a `scroll` event -- so
|
|
31
|
+
* nothing knew the reader had scrolled up, and scrolling back through a run was
|
|
32
|
+
* undone by the next token. Stick-to-bottom with a jump-to-latest affordance is
|
|
33
|
+
* a named primitive elsewhere for exactly this reason: shadcn ships it as
|
|
34
|
+
* `MessageScroller`, AI Elements as `ConversationScrollButton`.
|
|
35
|
+
*
|
|
36
|
+
* **Telling a reader's scroll from our own is the whole problem**, and the
|
|
37
|
+
* answer here is that it does not have to be told. A programmatic scroll only
|
|
38
|
+
* ever happens while already following, and it lands at the bottom, so the
|
|
39
|
+
* `scroll` event it provokes recomputes "at the bottom" as true and changes
|
|
40
|
+
* nothing. A reader's scroll is the only kind that can move the answer.
|
|
41
|
+
*
|
|
42
|
+
* A `ResizeObserver` covers the case scroll events cannot see: the *viewport*
|
|
43
|
+
* changing size. Resizing the panel, or the keyboard opening on a phone, moves
|
|
44
|
+
* the foot without anything scrolling and without any content arriving, so a
|
|
45
|
+
* pinned transcript would silently come unpinned.
|
|
46
|
+
*
|
|
47
|
+
* ⚠ It does **not** cover content that grows after insertion -- an image
|
|
48
|
+
* decoding, a chart laying out. A `ResizeObserver` on a scroll container does
|
|
49
|
+
* not fire when its `scrollHeight` changes, so catching that means observing
|
|
50
|
+
* every child, and the payoff is one late nudge in a case the reader can fix by
|
|
51
|
+
* scrolling. Insertion itself is covered: every site that adds to the
|
|
52
|
+
* transcript calls {@link StickToBottom.follow}.
|
|
53
|
+
*/
|
|
54
|
+
export declare function createStickToBottom({ viewport, onMissedContent, }: StickToBottomOptions): StickToBottom;
|
|
55
|
+
//# sourceMappingURL=stick_to_bottom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stick_to_bottom.d.ts","sourceRoot":"","sources":["../../src/ui/stick_to_bottom.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACrD;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,eAAe,GAChB,EAAE,oBAAoB,GAAG,aAAa,CA0DtC"}
|