sgiant-ai-widget 1.0.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/LICENSE +21 -0
- package/README.md +155 -0
- package/dist/ai-invalidation.d.ts +29 -0
- package/dist/ai-invalidation.d.ts.map +1 -0
- package/dist/ai-invalidation.js +121 -0
- package/dist/ai-invalidation.js.map +1 -0
- package/dist/contrast.d.ts +87 -0
- package/dist/contrast.d.ts.map +1 -0
- package/dist/contrast.js +151 -0
- package/dist/contrast.js.map +1 -0
- package/dist/directive.d.ts +37 -0
- package/dist/directive.d.ts.map +1 -0
- package/dist/directive.js +87 -0
- package/dist/directive.js.map +1 -0
- package/dist/dom.d.ts +83 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +146 -0
- package/dist/dom.js.map +1 -0
- package/dist/host-actions.d.ts +192 -0
- package/dist/host-actions.d.ts.map +1 -0
- package/dist/host-actions.js +443 -0
- package/dist/host-actions.js.map +1 -0
- package/dist/icons.d.ts +21 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +84 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +600 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4833 -0
- package/dist/index.js.map +1 -0
- package/dist/labels.d.ts +187 -0
- package/dist/labels.d.ts.map +1 -0
- package/dist/labels.js +232 -0
- package/dist/labels.js.map +1 -0
- package/dist/limits.d.ts +43 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +48 -0
- package/dist/limits.js.map +1 -0
- package/dist/markdown.d.ts +23 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +0 -0
- package/dist/markdown.js.map +1 -0
- package/dist/message-chrome.d.ts +45 -0
- package/dist/message-chrome.d.ts.map +1 -0
- package/dist/message-chrome.js +101 -0
- package/dist/message-chrome.js.map +1 -0
- package/dist/pane-follow.d.ts +67 -0
- package/dist/pane-follow.d.ts.map +1 -0
- package/dist/pane-follow.js +61 -0
- package/dist/pane-follow.js.map +1 -0
- package/dist/prefix.d.ts +9 -0
- package/dist/prefix.d.ts.map +1 -0
- package/dist/prefix.js +9 -0
- package/dist/prefix.js.map +1 -0
- package/dist/proposal-summary.d.ts +19 -0
- package/dist/proposal-summary.d.ts.map +1 -0
- package/dist/proposal-summary.js +73 -0
- package/dist/proposal-summary.js.map +1 -0
- package/dist/replay.d.ts +125 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +172 -0
- package/dist/replay.js.map +1 -0
- package/dist/safe-url.d.ts +47 -0
- package/dist/safe-url.d.ts.map +1 -0
- package/dist/safe-url.js +80 -0
- package/dist/safe-url.js.map +1 -0
- package/dist/sgiant-ai-widget.global.js +778 -0
- package/dist/sgiant-ai-widget.global.js.map +7 -0
- package/dist/specs.d.ts +173 -0
- package/dist/specs.d.ts.map +1 -0
- package/dist/specs.js +231 -0
- package/dist/specs.js.map +1 -0
- package/dist/storage.d.ts +77 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +124 -0
- package/dist/storage.js.map +1 -0
- package/dist/styles.d.ts +29 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +876 -0
- package/dist/styles.js.map +1 -0
- package/dist/ui-control.d.ts +8 -0
- package/dist/ui-control.d.ts.map +1 -0
- package/dist/ui-control.js +8 -0
- package/dist/ui-control.js.map +1 -0
- package/dist/ui-render.d.ts +75 -0
- package/dist/ui-render.d.ts.map +1 -0
- package/dist/ui-render.js +617 -0
- package/dist/ui-render.js.map +1 -0
- package/dist/ui-spec.d.ts +143 -0
- package/dist/ui-spec.d.ts.map +1 -0
- package/dist/ui-spec.js +288 -0
- package/dist/ui-spec.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The component vocabulary the assistant composes chat UI from — `[[ui:{…}]]`.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS. Every rich thing the chat could draw used to need frontend
|
|
5
|
+
* code: a card kind here, a host renderer there, a release before the model
|
|
6
|
+
* could show it. So the chat could only ever show what the UI had been built to
|
|
7
|
+
* show, and each new scenario (a storyboard, a shortlist, a room-type picker)
|
|
8
|
+
* meant the same work again. This is the substrate that ends that: a small set
|
|
9
|
+
* of primitives the widget knows how to draw, composed BY THE MODEL per
|
|
10
|
+
* situation. A new scenario becomes a spec, not a release.
|
|
11
|
+
*
|
|
12
|
+
* It is deliberately a VOCABULARY and not a language. The model cannot send
|
|
13
|
+
* markup or code — it names primitives (a tile, a status, a button) and the
|
|
14
|
+
* widget draws them in its own styles. That keeps every card looking like the
|
|
15
|
+
* product rather than like whatever the model imagined this time, and it keeps
|
|
16
|
+
* model output out of the DOM.
|
|
17
|
+
*
|
|
18
|
+
* Everything here is PURE so it can be tested without a browser: the widget is
|
|
19
|
+
* imperative DOM with no test harness, and the rules below (what is dropped,
|
|
20
|
+
* what is capped, what a missing field means) are exactly the part worth
|
|
21
|
+
* pinning down.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* How `items` are laid out. Unknown values fall back to a strip.
|
|
25
|
+
*
|
|
26
|
+
* "carousel" shows ONE item at a time with paging, which is the right shape for
|
|
27
|
+
* a set the client is judging rather than scanning: a reel's scenes want the
|
|
28
|
+
* space to be looked at properly, and a strip of six thumbnails invites
|
|
29
|
+
* approving what was never really seen. The other layouts remain right for
|
|
30
|
+
* picking from many (grid) or reading down a short list (list).
|
|
31
|
+
*/
|
|
32
|
+
export type UiLayout = "strip" | "grid" | "list" | "carousel";
|
|
33
|
+
/**
|
|
34
|
+
* The one action name the WIDGET answers itself: send `data.text` (falling back
|
|
35
|
+
* to the button's label) back into the conversation as if the user had typed
|
|
36
|
+
* it. Everything else goes to the host by name.
|
|
37
|
+
*
|
|
38
|
+
* This is what makes the vocabulary self-sufficient. Without it, every button a
|
|
39
|
+
* composed card wants — "Redo scene 2", "Make it warmer", "Use the sunset one"
|
|
40
|
+
* — is a capability the HOST has to implement and name in advance, which is the
|
|
41
|
+
* per-scenario frontend work this whole design exists to remove. With it, the
|
|
42
|
+
* assistant's own tools are the implementation: the button says the thing, the
|
|
43
|
+
* assistant does it, and a card can offer an action nobody wrote code for.
|
|
44
|
+
*
|
|
45
|
+
* Precedent, not novelty: `[[chips:…]]` already answers by sending the chosen
|
|
46
|
+
* text as an ordinary message, which keeps the transcript in order and honest —
|
|
47
|
+
* the click is visible in the conversation as the sentence it stood for.
|
|
48
|
+
*/
|
|
49
|
+
export declare const UI_SAY_ACTION = "say";
|
|
50
|
+
/**
|
|
51
|
+
* A button on a card or tile. Dispatched through the host's existing
|
|
52
|
+
* `onWidgetAction(name, data)` — the same path `[[action:…]]` already uses, so
|
|
53
|
+
* a UI card can only ever do what the host already permits by name — except for
|
|
54
|
+
* `say` (see UI_SAY_ACTION), which the widget answers itself.
|
|
55
|
+
*/
|
|
56
|
+
export interface UiAction {
|
|
57
|
+
name: string;
|
|
58
|
+
label?: string;
|
|
59
|
+
/** When set, the user must approve before the host is called. */
|
|
60
|
+
confirm?: string;
|
|
61
|
+
data?: Record<string, string>;
|
|
62
|
+
variant?: "primary" | "default" | "danger";
|
|
63
|
+
}
|
|
64
|
+
/** One thing in the card: a picture/clip with a caption, a state and buttons. */
|
|
65
|
+
export interface UiItem {
|
|
66
|
+
/** Caller's own id for this item, echoed back in an action's `data.itemId`
|
|
67
|
+
* so a handler knows WHICH tile was acted on without the model having to
|
|
68
|
+
* repeat it in every button. */
|
|
69
|
+
id?: string;
|
|
70
|
+
title?: string;
|
|
71
|
+
caption?: string;
|
|
72
|
+
/** Library media to show. An ID, never a URL — see `sanitizeMediaId`. */
|
|
73
|
+
mediaId?: string;
|
|
74
|
+
/** Still to show for a video tile before it plays. */
|
|
75
|
+
posterMediaId?: string;
|
|
76
|
+
/** How to draw `mediaId`. Inferred by the widget when omitted — but a signed
|
|
77
|
+
* URL carries no extension, so AUDIO in particular must be declared or it
|
|
78
|
+
* is drawn as a broken picture. */
|
|
79
|
+
media?: "image" | "video" | "audio";
|
|
80
|
+
/** Drawn as a badge; a known one is also coloured. */
|
|
81
|
+
status?: UiStatus;
|
|
82
|
+
/**
|
|
83
|
+
* A running job this tile is the view of — the card stops being a snapshot.
|
|
84
|
+
*
|
|
85
|
+
* Without it, a storyboard card drawn while six clips render is stale the
|
|
86
|
+
* moment it paints: the client scrolls back an hour later to a rail of
|
|
87
|
+
* "rendering" badges over empty boxes, with the finished clips sitting in the
|
|
88
|
+
* library. The tile subscribes to the job's existing poll and fills in its
|
|
89
|
+
* own media and status when the render lands.
|
|
90
|
+
*
|
|
91
|
+
* The GENERIC job id (what `/accounts/:id/jobs/:jobId` answers), not a
|
|
92
|
+
* media_job detail id — the wrong one 404s, and a 404 makes the widget
|
|
93
|
+
* conclude the job is gone.
|
|
94
|
+
*/
|
|
95
|
+
jobId?: string;
|
|
96
|
+
actions?: UiAction[];
|
|
97
|
+
}
|
|
98
|
+
export interface UiSpec {
|
|
99
|
+
title?: string;
|
|
100
|
+
/** A sentence above the items — what the card is for. */
|
|
101
|
+
caption?: string;
|
|
102
|
+
layout: UiLayout;
|
|
103
|
+
/** Shape of every tile's media box. */
|
|
104
|
+
aspect: UiAspect;
|
|
105
|
+
items: UiItem[];
|
|
106
|
+
/** Card-level buttons, under the items. */
|
|
107
|
+
actions: UiAction[];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The shape tiles are drawn in — named format keywords ("square" 1:1,
|
|
111
|
+
* "portrait" 4:5, "story" 9:16, "landscape" 4:3, "wide" 16:9) rather than
|
|
112
|
+
* raw ratios, so the model describes a card in the same vocabulary the
|
|
113
|
+
* social platforms use.
|
|
114
|
+
*
|
|
115
|
+
* Card-level rather than per-tile: the items of one card share a format, and
|
|
116
|
+
* a strip whose tiles were each a different shape would read as a mistake. It
|
|
117
|
+
* matters because portrait media previewed in square tiles shows the client a
|
|
118
|
+
* crop of what they are looking at, and they approve it anyway.
|
|
119
|
+
*/
|
|
120
|
+
export type UiAspect = "square" | "portrait" | "story" | "landscape" | "wide";
|
|
121
|
+
/**
|
|
122
|
+
* States a tile can be in. Kept OPEN — an unrecognised status is still shown,
|
|
123
|
+
* just without semantic colour, because the alternative is that the model
|
|
124
|
+
* cannot describe a state until the frontend ships a name for it, which is the
|
|
125
|
+
* exact coupling this whole file exists to remove.
|
|
126
|
+
*/
|
|
127
|
+
export type UiStatus = string;
|
|
128
|
+
/** The four the widget colours. Everything else is drawn neutral. */
|
|
129
|
+
export declare const KNOWN_UI_STATUSES: readonly ["pending", "running", "ready", "failed"];
|
|
130
|
+
/**
|
|
131
|
+
* Turn whatever the model emitted into a spec the widget can draw, or null if
|
|
132
|
+
* there is nothing worth drawing.
|
|
133
|
+
*
|
|
134
|
+
* Null rather than an empty card on purpose: the caller leaves the reply's text
|
|
135
|
+
* alone, so a malformed directive costs the user nothing. A card that renders
|
|
136
|
+
* as an empty box would instead look like the assistant tried to show something
|
|
137
|
+
* and the product lost it.
|
|
138
|
+
*/
|
|
139
|
+
export declare function normalizeUiSpec(input: unknown): UiSpec | null;
|
|
140
|
+
/** Every media id a spec will need resolved, in one list — so the host makes
|
|
141
|
+
* ONE batched round-trip instead of a request per tile. */
|
|
142
|
+
export declare function uiSpecMediaIds(spec: UiSpec): string[];
|
|
143
|
+
//# sourceMappingURL=ui-spec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-spec.d.ts","sourceRoot":"","sources":["../src/ui-spec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH;;;;;;;;GAQG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAE9D;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,QAAQ,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;CAC5C;AAED,iFAAiF;AACjF,MAAM,WAAW,MAAM;IACrB;;qCAEiC;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;wCAEoC;IACpC,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IACpC,sDAAsD;IACtD,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,CAAC;IACjB,uCAAuC;IACvC,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,2CAA2C;IAC3C,OAAO,EAAE,QAAQ,EAAE,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AAU9E;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,oDAKpB,CAAC;AAoLX;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAiC7D;AAED;4DAC4D;AAC5D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAOrD"}
|
package/dist/ui-spec.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The component vocabulary the assistant composes chat UI from — `[[ui:{…}]]`.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS. Every rich thing the chat could draw used to need frontend
|
|
5
|
+
* code: a card kind here, a host renderer there, a release before the model
|
|
6
|
+
* could show it. So the chat could only ever show what the UI had been built to
|
|
7
|
+
* show, and each new scenario (a storyboard, a shortlist, a room-type picker)
|
|
8
|
+
* meant the same work again. This is the substrate that ends that: a small set
|
|
9
|
+
* of primitives the widget knows how to draw, composed BY THE MODEL per
|
|
10
|
+
* situation. A new scenario becomes a spec, not a release.
|
|
11
|
+
*
|
|
12
|
+
* It is deliberately a VOCABULARY and not a language. The model cannot send
|
|
13
|
+
* markup or code — it names primitives (a tile, a status, a button) and the
|
|
14
|
+
* widget draws them in its own styles. That keeps every card looking like the
|
|
15
|
+
* product rather than like whatever the model imagined this time, and it keeps
|
|
16
|
+
* model output out of the DOM.
|
|
17
|
+
*
|
|
18
|
+
* Everything here is PURE so it can be tested without a browser: the widget is
|
|
19
|
+
* imperative DOM with no test harness, and the rules below (what is dropped,
|
|
20
|
+
* what is capped, what a missing field means) are exactly the part worth
|
|
21
|
+
* pinning down.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The one action name the WIDGET answers itself: send `data.text` (falling back
|
|
25
|
+
* to the button's label) back into the conversation as if the user had typed
|
|
26
|
+
* it. Everything else goes to the host by name.
|
|
27
|
+
*
|
|
28
|
+
* This is what makes the vocabulary self-sufficient. Without it, every button a
|
|
29
|
+
* composed card wants — "Redo scene 2", "Make it warmer", "Use the sunset one"
|
|
30
|
+
* — is a capability the HOST has to implement and name in advance, which is the
|
|
31
|
+
* per-scenario frontend work this whole design exists to remove. With it, the
|
|
32
|
+
* assistant's own tools are the implementation: the button says the thing, the
|
|
33
|
+
* assistant does it, and a card can offer an action nobody wrote code for.
|
|
34
|
+
*
|
|
35
|
+
* Precedent, not novelty: `[[chips:…]]` already answers by sending the chosen
|
|
36
|
+
* text as an ordinary message, which keeps the transcript in order and honest —
|
|
37
|
+
* the click is visible in the conversation as the sentence it stood for.
|
|
38
|
+
*/
|
|
39
|
+
export const UI_SAY_ACTION = "say";
|
|
40
|
+
const ASPECTS = [
|
|
41
|
+
"square",
|
|
42
|
+
"portrait",
|
|
43
|
+
"story",
|
|
44
|
+
"landscape",
|
|
45
|
+
"wide",
|
|
46
|
+
];
|
|
47
|
+
/** The four the widget colours. Everything else is drawn neutral. */
|
|
48
|
+
export const KNOWN_UI_STATUSES = [
|
|
49
|
+
"pending",
|
|
50
|
+
"running",
|
|
51
|
+
"ready",
|
|
52
|
+
"failed",
|
|
53
|
+
];
|
|
54
|
+
/** Words the model reaches for that mean one of the four. Being forgiving here
|
|
55
|
+
* is cheaper than a badge that silently loses its colour because the model
|
|
56
|
+
* wrote "rendering" instead of "running". */
|
|
57
|
+
const STATUS_SYNONYMS = {
|
|
58
|
+
queued: "pending",
|
|
59
|
+
waiting: "pending",
|
|
60
|
+
rendering: "running",
|
|
61
|
+
generating: "running",
|
|
62
|
+
processing: "running",
|
|
63
|
+
in_progress: "running",
|
|
64
|
+
done: "ready",
|
|
65
|
+
complete: "ready",
|
|
66
|
+
completed: "ready",
|
|
67
|
+
ok: "ready",
|
|
68
|
+
success: "ready",
|
|
69
|
+
error: "failed",
|
|
70
|
+
cancelled: "failed",
|
|
71
|
+
canceled: "failed",
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Caps. A spec is model output, so it is not a promise of reasonable size: an
|
|
75
|
+
* 800-item strip would lock the DOM, and a 4000-character caption would push
|
|
76
|
+
* the composer off screen. Generous enough that no honest card hits them.
|
|
77
|
+
*/
|
|
78
|
+
const MAX_ITEMS = 24;
|
|
79
|
+
const MAX_ACTIONS = 4;
|
|
80
|
+
const MAX_TEXT = 240;
|
|
81
|
+
const MAX_DATA_KEYS = 12;
|
|
82
|
+
const clamp = (v, max = MAX_TEXT) => {
|
|
83
|
+
if (typeof v !== "string")
|
|
84
|
+
return undefined;
|
|
85
|
+
const s = v.trim();
|
|
86
|
+
if (!s)
|
|
87
|
+
return undefined;
|
|
88
|
+
return s.length > max ? `${s.slice(0, max - 1)}…` : s;
|
|
89
|
+
};
|
|
90
|
+
const isRecord = (v) => Boolean(v) && typeof v === "object" && !Array.isArray(v);
|
|
91
|
+
/**
|
|
92
|
+
* A media id, or nothing.
|
|
93
|
+
*
|
|
94
|
+
* The model may name library media only BY ID — never by URL. Model output is
|
|
95
|
+
* untrusted (it routinely carries text scraped from third-party pages), and a
|
|
96
|
+
* URL it chose would be fetched by the client's browser the moment the card
|
|
97
|
+
* painted: a tracking pixel, or a request to an attacker's host carrying the
|
|
98
|
+
* user's IP, drawn inside the product's own chat. An id can only ever be
|
|
99
|
+
* resolved by the host against media the ACCOUNT owns, so the worst case is a
|
|
100
|
+
* tile that resolves to nothing.
|
|
101
|
+
*
|
|
102
|
+
* Shape-checked rather than trusted: ids are opaque to the widget, so anything
|
|
103
|
+
* that isn't a plain id-looking token is refused.
|
|
104
|
+
*/
|
|
105
|
+
function sanitizeMediaId(v) {
|
|
106
|
+
if (typeof v !== "string")
|
|
107
|
+
return undefined;
|
|
108
|
+
const s = v.trim();
|
|
109
|
+
if (!s || s.length > 64)
|
|
110
|
+
return undefined;
|
|
111
|
+
return /^[A-Za-z0-9_-]+$/.test(s) ? s : undefined;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Canonicalize a status we recognise; otherwise hand back the model's own
|
|
115
|
+
* wording untouched.
|
|
116
|
+
*
|
|
117
|
+
* The match is done on a squashed key so "In Progress" and "in-progress" both
|
|
118
|
+
* land on `running`, but an unrecognised status is DISPLAYED, and displaying
|
|
119
|
+
* the squashed key would put `awaiting_client_sign_off` on a badge in front of
|
|
120
|
+
* a client. The open half of this vocabulary is only useful if the words the
|
|
121
|
+
* model chose survive it.
|
|
122
|
+
*/
|
|
123
|
+
function normalizeStatus(v) {
|
|
124
|
+
const s = clamp(v, 32);
|
|
125
|
+
if (!s)
|
|
126
|
+
return undefined;
|
|
127
|
+
const key = s.toLowerCase().replace(/[\s-]+/g, "_");
|
|
128
|
+
if (STATUS_SYNONYMS[key])
|
|
129
|
+
return STATUS_SYNONYMS[key];
|
|
130
|
+
return KNOWN_UI_STATUSES.includes(key) ? key : s;
|
|
131
|
+
}
|
|
132
|
+
/** Action `data` is passed to the host verbatim, so it is flattened to plain
|
|
133
|
+
* strings — a nested object would reach a handler expecting a value and fail
|
|
134
|
+
* somewhere far from here. */
|
|
135
|
+
function normalizeData(v) {
|
|
136
|
+
if (!isRecord(v))
|
|
137
|
+
return undefined;
|
|
138
|
+
const out = {};
|
|
139
|
+
for (const [k, raw] of Object.entries(v)) {
|
|
140
|
+
if (Object.keys(out).length >= MAX_DATA_KEYS)
|
|
141
|
+
break;
|
|
142
|
+
if (typeof raw === "string" ||
|
|
143
|
+
typeof raw === "number" ||
|
|
144
|
+
typeof raw === "boolean")
|
|
145
|
+
out[k] = String(raw);
|
|
146
|
+
}
|
|
147
|
+
return Object.keys(out).length ? out : undefined;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* One button, or null.
|
|
151
|
+
*
|
|
152
|
+
* An action with no `name` is DROPPED rather than drawn inert: a button that
|
|
153
|
+
* dispatches nothing is indistinguishable from one that works, so the user
|
|
154
|
+
* clicks it, nothing happens, and they have no way to tell whether the product
|
|
155
|
+
* is broken or their request was refused.
|
|
156
|
+
*/
|
|
157
|
+
function normalizeAction(v) {
|
|
158
|
+
if (!isRecord(v))
|
|
159
|
+
return null;
|
|
160
|
+
const name = clamp(v.name, 64);
|
|
161
|
+
if (!name)
|
|
162
|
+
return null;
|
|
163
|
+
const label = clamp(v.label, 48);
|
|
164
|
+
const confirm = clamp(v.confirm);
|
|
165
|
+
const data = normalizeData(v.data);
|
|
166
|
+
const variant = v.variant === "primary" || v.variant === "danger" ? v.variant : undefined;
|
|
167
|
+
// A `say` button with nothing to say is inert for the same reason a nameless
|
|
168
|
+
// one is: it looks exactly like a button that works.
|
|
169
|
+
if (name === UI_SAY_ACTION && !data?.text && !label)
|
|
170
|
+
return null;
|
|
171
|
+
return {
|
|
172
|
+
name,
|
|
173
|
+
...(label ? { label } : {}),
|
|
174
|
+
...(confirm ? { confirm } : {}),
|
|
175
|
+
...(data ? { data } : {}),
|
|
176
|
+
...(variant ? { variant } : {}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function normalizeActions(v) {
|
|
180
|
+
if (!Array.isArray(v))
|
|
181
|
+
return [];
|
|
182
|
+
const out = [];
|
|
183
|
+
for (const raw of v) {
|
|
184
|
+
if (out.length >= MAX_ACTIONS)
|
|
185
|
+
break;
|
|
186
|
+
const a = normalizeAction(raw);
|
|
187
|
+
if (a)
|
|
188
|
+
out.push(a);
|
|
189
|
+
}
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* One tile, or null when there is nothing to show.
|
|
194
|
+
*
|
|
195
|
+
* "Nothing to show" is deliberate: an item with no media, no text and no state
|
|
196
|
+
* would paint as an empty box, which reads as a broken card rather than as an
|
|
197
|
+
* item the model chose not to describe.
|
|
198
|
+
*/
|
|
199
|
+
function normalizeItem(v) {
|
|
200
|
+
if (!isRecord(v))
|
|
201
|
+
return null;
|
|
202
|
+
const id = clamp(v.id, 64);
|
|
203
|
+
const title = clamp(v.title, 80);
|
|
204
|
+
const caption = clamp(v.caption);
|
|
205
|
+
const mediaId = sanitizeMediaId(v.mediaId);
|
|
206
|
+
const posterMediaId = sanitizeMediaId(v.posterMediaId);
|
|
207
|
+
const status = normalizeStatus(v.status);
|
|
208
|
+
const media = v.media === "video" || v.media === "image" || v.media === "audio"
|
|
209
|
+
? v.media
|
|
210
|
+
: undefined;
|
|
211
|
+
// Same opaque-token shape as a media id, and checked for the same reason: it
|
|
212
|
+
// is put straight into a URL the widget then polls.
|
|
213
|
+
const jobId = sanitizeMediaId(v.jobId);
|
|
214
|
+
const actions = normalizeActions(v.actions);
|
|
215
|
+
// A tile that is NOTHING but a pending render is the whole point of `jobId` —
|
|
216
|
+
// "scene 3 is being made, watch this space" — so it must survive the
|
|
217
|
+
// has-anything-to-show check that would otherwise drop it.
|
|
218
|
+
if (!mediaId && !title && !caption && !status && !jobId && !actions.length)
|
|
219
|
+
return null;
|
|
220
|
+
return {
|
|
221
|
+
...(id ? { id } : {}),
|
|
222
|
+
...(title ? { title } : {}),
|
|
223
|
+
...(caption ? { caption } : {}),
|
|
224
|
+
...(mediaId ? { mediaId } : {}),
|
|
225
|
+
...(posterMediaId ? { posterMediaId } : {}),
|
|
226
|
+
...(media ? { media } : {}),
|
|
227
|
+
...(status ? { status } : {}),
|
|
228
|
+
...(jobId ? { jobId } : {}),
|
|
229
|
+
...(actions.length ? { actions } : {}),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Turn whatever the model emitted into a spec the widget can draw, or null if
|
|
234
|
+
* there is nothing worth drawing.
|
|
235
|
+
*
|
|
236
|
+
* Null rather than an empty card on purpose: the caller leaves the reply's text
|
|
237
|
+
* alone, so a malformed directive costs the user nothing. A card that renders
|
|
238
|
+
* as an empty box would instead look like the assistant tried to show something
|
|
239
|
+
* and the product lost it.
|
|
240
|
+
*/
|
|
241
|
+
export function normalizeUiSpec(input) {
|
|
242
|
+
if (!isRecord(input))
|
|
243
|
+
return null;
|
|
244
|
+
const items = [];
|
|
245
|
+
if (Array.isArray(input.items)) {
|
|
246
|
+
for (const raw of input.items) {
|
|
247
|
+
if (items.length >= MAX_ITEMS)
|
|
248
|
+
break;
|
|
249
|
+
const it = normalizeItem(raw);
|
|
250
|
+
if (it)
|
|
251
|
+
items.push(it);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const actions = normalizeActions(input.actions);
|
|
255
|
+
const title = clamp(input.title, 80);
|
|
256
|
+
const caption = clamp(input.caption);
|
|
257
|
+
if (!items.length && !actions.length)
|
|
258
|
+
return null;
|
|
259
|
+
const layout = input.layout === "grid" ||
|
|
260
|
+
input.layout === "list" ||
|
|
261
|
+
input.layout === "carousel"
|
|
262
|
+
? input.layout
|
|
263
|
+
: "strip";
|
|
264
|
+
const aspect = (typeof input.aspect === "string" && ASPECTS.includes(input.aspect)
|
|
265
|
+
? input.aspect
|
|
266
|
+
: "square");
|
|
267
|
+
return {
|
|
268
|
+
...(title ? { title } : {}),
|
|
269
|
+
...(caption ? { caption } : {}),
|
|
270
|
+
layout,
|
|
271
|
+
aspect,
|
|
272
|
+
items,
|
|
273
|
+
actions,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/** Every media id a spec will need resolved, in one list — so the host makes
|
|
277
|
+
* ONE batched round-trip instead of a request per tile. */
|
|
278
|
+
export function uiSpecMediaIds(spec) {
|
|
279
|
+
const ids = new Set();
|
|
280
|
+
for (const it of spec.items) {
|
|
281
|
+
if (it.mediaId)
|
|
282
|
+
ids.add(it.mediaId);
|
|
283
|
+
if (it.posterMediaId)
|
|
284
|
+
ids.add(it.posterMediaId);
|
|
285
|
+
}
|
|
286
|
+
return [...ids];
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=ui-spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-spec.js","sourceRoot":"","sources":["../src/ui-spec.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sgiant-ai-widget",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Framework-agnostic embeddable AI chat widget. One call mounts a streaming chat bubble into any page — no React, no framework, themeable.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"ai",
|
|
8
|
+
"chat",
|
|
9
|
+
"widget",
|
|
10
|
+
"chatbot",
|
|
11
|
+
"streaming",
|
|
12
|
+
"embeddable",
|
|
13
|
+
"vanilla",
|
|
14
|
+
"no-framework",
|
|
15
|
+
"llm",
|
|
16
|
+
"assistant"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/vedatugur/sgiant-ai-widget.git"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://vedatugur.github.io/sgiant-ai-widget/",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/vedatugur/sgiant-ai-widget/issues"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./global": "./dist/sgiant-ai-widget.global.js"
|
|
36
|
+
},
|
|
37
|
+
"unpkg": "./dist/sgiant-ai-widget.global.js",
|
|
38
|
+
"jsdelivr": "./dist/sgiant-ai-widget.global.js",
|
|
39
|
+
"files": [
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -b && node scripts/fix-esm-extensions.mjs && npm run build:global",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
46
|
+
"build:global": "node scripts/build-global.mjs",
|
|
47
|
+
"audit": "./scripts/audit-tarball.sh",
|
|
48
|
+
"prepublishOnly": "npm run build && npm run audit"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"sgiant-ai-agent-bridge": "1.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"typescript": "^5.7.2",
|
|
55
|
+
"esbuild": "^0.24.0"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public",
|
|
59
|
+
"registry": "https://registry.npmjs.org/"
|
|
60
|
+
}
|
|
61
|
+
}
|