@guuey/chat 0.16.8 → 0.16.10
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/README.md +23 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +28 -7
- package/dist/react/components.d.ts +1 -1
- package/dist/react/components.d.ts.map +1 -1
- package/dist/react/guuey-chat.d.ts +69 -1
- package/dist/react/guuey-chat.d.ts.map +1 -1
- package/dist/react/guuey-chat.js +116 -6
- package/dist/react/markdown.d.ts +0 -26
- package/dist/react/markdown.d.ts.map +1 -1
- package/dist/react/markdown.js +115 -2
- package/dist/react/theme-css.d.ts +8 -0
- package/dist/react/theme-css.d.ts.map +1 -1
- package/dist/react/theme-css.js +24 -13
- package/dist/react/transcript.d.ts +8 -0
- package/dist/react/transcript.d.ts.map +1 -1
- package/dist/react/transcript.js +2 -2
- package/dist/strings.d.ts +19 -0
- package/dist/strings.d.ts.map +1 -1
- package/dist/strings.js +6 -0
- package/dist/theme.d.ts +28 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +67 -2
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/plan.ts +27 -8
- package/src/react/components.tsx +1 -0
- package/src/react/guuey-chat.tsx +237 -2
- package/src/react/markdown.tsx +104 -2
- package/src/react/theme-css.ts +24 -13
- package/src/react/transcript.tsx +10 -1
- package/src/strings.ts +30 -0
- package/src/theme.ts +71 -2
- package/src/types.ts +14 -1
- package/styles.css +239 -100
package/README.md
CHANGED
|
@@ -44,8 +44,29 @@ jump-to-latest release, windowed rendering for long transcripts,
|
|
|
44
44
|
`aria-live`/keyboard/focus accessibility, sanitized markdown (typed AST —
|
|
45
45
|
raw HTML is unrepresentable, links are scheme-allowlisted), generative-UI
|
|
46
46
|
views mounting through `@guuey/mcp-apps-host`'s sandboxed host, and theming
|
|
47
|
-
from the `GuueyChatTheme` token schema (
|
|
48
|
-
|
|
47
|
+
from the `GuueyChatTheme` token schema (`GUUEY_CHAT_THEME` ships beside the
|
|
48
|
+
neutral default).
|
|
49
|
+
|
|
50
|
+
**Looking native is the default, not a theming project.** The unthemed kit
|
|
51
|
+
is monochrome — ink, no vendor accent — so a zero-config embed disappears
|
|
52
|
+
into your product. Two knobs cover most brand work before any theme
|
|
53
|
+
exists:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
<GuueyChat surface="bare" … /> // no message cards — transcript sits on YOUR background
|
|
57
|
+
<GuueyChat composer={false} … /> // bring your own input; drive sends via the ref handle
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The two theming channels **compose** — set any `--guuey-chat-*` CSS custom
|
|
61
|
+
property on an ancestor and it wins per-token over the resolved `theme`
|
|
62
|
+
prop (the kit stamps its resolved tokens under internal `--_guuey-chat-*`
|
|
63
|
+
names and reads `var(--guuey-chat-<t>, var(--_guuey-chat-<t>))`):
|
|
64
|
+
|
|
65
|
+
```css
|
|
66
|
+
.my-app-chat {
|
|
67
|
+
--guuey-chat-accent: var(--brand);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
49
70
|
|
|
50
71
|
Override one row without forfeiting the rest:
|
|
51
72
|
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,6 @@ export { defaultChatStrings, humanizeToolName, type ChatStrings, } from "./strin
|
|
|
16
16
|
export { transcriptInputsFromHistory } from "./history-inputs.js";
|
|
17
17
|
export { buildHitlAnswer, grantModeDisplay, hitlPromptsFromFold, type HitlAnswerRecord, type HitlPromptAction, } from "./hitl.js";
|
|
18
18
|
export { OAUTH_LINK_PARAMS, OAUTH_RETURN_PARAMS, OAUTH_SCHEME, oauthAuthorizeAsk, oauthAuthorizeHref, parseOAuthReturn, stripOAuthReturn, type OAuthAuthorizeAsk, type OAuthReturn, } from "./oauth.js";
|
|
19
|
-
export { DEFAULT_CHAT_THEME, GUUEY_CHAT_THEME, GuueyChatAccentRamp, GuueyChatErrorRamp, GuueyChatPalette, GuueyChatRamps, GuueyChatRampSet, GuueyChatTheme, GuueyChatToneRamp, resolveTheme, } from "./theme.js";
|
|
19
|
+
export { DEFAULT_CHAT_THEME, GUUEY_CHAT_THEME, GuueyChatAccentRamp, GuueyChatErrorRamp, GuueyChatPalette, GuueyChatRamps, GuueyChatRampSet, GuueyChatTheme, GuueyChatToneRamp, resolveTheme, resolveCourtTheme, } from "./theme.js";
|
|
20
20
|
export type { ChatDebugEvent, CitationsItem, CodeItem, CompactionItem, DataResultItem, DisplayItem, ErrorItem, HistoryBoundaryItem, ItemKey, HitlPromptInput, HitlPromptItem, MediaItem, NoticeItem, ProfilePromptInput, ProfilePromptItem, PromptItem, PromptItemInput, ReasoningItem, StatusLineItem, TextItem, ToolGroupItem, ToolItem, TranscriptInputs, TranscriptMessage, TranscriptOverrides, TranscriptPlan, PlanViewSummary, UnknownItem, UserMessageItem, ViewMountItem, ViewRefItem, } from "./types.js";
|
|
21
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,aAAa,EACb,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,UAAU,EACV,KAAK,yBAAyB,EAC9B,WAAW,EACX,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,GACtB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EACL,aAAa,EACb,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,UAAU,EACV,KAAK,yBAAyB,EAC9B,WAAW,EACX,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,WAAW,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,GACtB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,cAAc,EACd,aAAa,EACb,QAAQ,EACR,cAAc,EACd,cAAc,EACd,WAAW,EACX,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,eAAe,EACf,cAAc,EACd,SAAS,EACT,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,eAAe,EACf,aAAa,EACb,cAAc,EACd,QAAQ,EACR,aAAa,EACb,QAAQ,EACR,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,WAAW,EACX,eAAe,EACf,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -16,4 +16,4 @@ export { defaultChatStrings, humanizeToolName, } from "./strings.js";
|
|
|
16
16
|
export { transcriptInputsFromHistory } from "./history-inputs.js";
|
|
17
17
|
export { buildHitlAnswer, grantModeDisplay, hitlPromptsFromFold, } from "./hitl.js";
|
|
18
18
|
export { OAUTH_LINK_PARAMS, OAUTH_RETURN_PARAMS, OAUTH_SCHEME, oauthAuthorizeAsk, oauthAuthorizeHref, parseOAuthReturn, stripOAuthReturn, } from "./oauth.js";
|
|
19
|
-
export { DEFAULT_CHAT_THEME, GUUEY_CHAT_THEME, GuueyChatAccentRamp, GuueyChatErrorRamp, GuueyChatPalette, GuueyChatRamps, GuueyChatRampSet, GuueyChatTheme, GuueyChatToneRamp, resolveTheme, } from "./theme.js";
|
|
19
|
+
export { DEFAULT_CHAT_THEME, GUUEY_CHAT_THEME, GuueyChatAccentRamp, GuueyChatErrorRamp, GuueyChatPalette, GuueyChatRamps, GuueyChatRampSet, GuueyChatTheme, GuueyChatToneRamp, resolveTheme, resolveCourtTheme, } from "./theme.js";
|
package/dist/plan.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAkCA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,KAAK,EAUV,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EAIf,MAAM,YAAY,CAAC;AAuqBpB,uCAAuC;AACvC,wBAAgB,cAAc,CAC5B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,gBAAgB,EACxB,SAAS,GAAE,mBAAwB,GAClC,cAAc,
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAkCA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,KAAK,EAUV,gBAAgB,EAChB,mBAAmB,EACnB,cAAc,EAIf,MAAM,YAAY,CAAC;AAuqBpB,uCAAuC;AACvC,wBAAgB,cAAc,CAC5B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,gBAAgB,EACxB,SAAS,GAAE,mBAAwB,GAClC,cAAc,CAkXhB;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,cAAc,CAAC,GACxD;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,CAAA;CAAE,GAAG,SAAS,CAiB5D"}
|
package/dist/plan.js
CHANGED
|
@@ -760,14 +760,35 @@ export function planTranscript(inputs, policy, overrides = {}) {
|
|
|
760
760
|
liveViewScopes.add(item.actionScope);
|
|
761
761
|
}
|
|
762
762
|
const cards = [...(inputs.historyCards ?? [])].sort((a, b) => a.seq - b.seq);
|
|
763
|
-
|
|
764
|
-
|
|
763
|
+
// guuey#535: ONE render session = ONE surface. Multiple history cards
|
|
764
|
+
// sharing an actionScope are the same surface referenced across turns
|
|
765
|
+
// (ggui SPEC §7.1.2.1 makes the amend result's {sessionId, resourceUri}
|
|
766
|
+
// the normative re-anchor reference — ggui#652; the stamp rides the
|
|
767
|
+
// existing locator persistence lane untouched). The LATEST reference
|
|
768
|
+
// anchors the surface; earlier references emit nothing — and latest
|
|
769
|
+
// wins even when it is locator-only, because the locator re-fetch
|
|
770
|
+
// restores CURRENT session state, better than an older frozen snapshot.
|
|
771
|
+
const scopes = cards.map((card) => {
|
|
765
772
|
const mount = snapshotViewMount(card.cardSnapshot);
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
773
|
+
return {
|
|
774
|
+
card,
|
|
775
|
+
mount,
|
|
776
|
+
scope: mount === undefined
|
|
777
|
+
? null
|
|
778
|
+
: mount.channel === "locator"
|
|
779
|
+
? mount.resourceUri
|
|
780
|
+
: mount.resource.uri,
|
|
781
|
+
};
|
|
782
|
+
});
|
|
783
|
+
const latestSeqByScope = new Map();
|
|
784
|
+
for (const { card, scope } of scopes) {
|
|
785
|
+
if (scope !== null)
|
|
786
|
+
latestSeqByScope.set(scope, card.seq);
|
|
787
|
+
}
|
|
788
|
+
for (const { card, mount, scope } of scopes) {
|
|
789
|
+
const key = `card.${card.seq}`;
|
|
790
|
+
if (scope !== null && latestSeqByScope.get(scope) !== card.seq)
|
|
791
|
+
continue; // a later turn re-anchored it
|
|
771
792
|
if (scope !== null && liveViewScopes.has(scope))
|
|
772
793
|
continue; // live mount owns it
|
|
773
794
|
const view = {
|
|
@@ -69,7 +69,7 @@ export interface TranscriptItemContext {
|
|
|
69
69
|
viewProps?: ViewSlotProps | ((item: ViewMountItem, mount: ResolvedViewMount) => ViewSlotProps);
|
|
70
70
|
}
|
|
71
71
|
/** The per-view slice of `GuueyViewProps` a transcript may configure. */
|
|
72
|
-
export type ViewSlotProps = Pick<GuueyViewProps, "hostCapabilities" | "hostInfo" | "hostContext" | "onCallTool" | "onUpdateModelContext" | "onUserMessage" | "onReadResource" | "onSizeChanged" | "negotiationTimeoutMs" | "dangerouslyAddSandboxFlags" | "allow" | "sandboxPageUrl" | "autoResize" | "cspOrigins">;
|
|
72
|
+
export type ViewSlotProps = Pick<GuueyViewProps, "hostCapabilities" | "hostInfo" | "hostContext" | "onCallTool" | "onUpdateModelContext" | "onUserMessage" | "onOpenLink" | "onReadResource" | "onSizeChanged" | "negotiationTimeoutMs" | "dangerouslyAddSandboxFlags" | "allow" | "sandboxPageUrl" | "autoResize" | "cspOrigins">;
|
|
73
73
|
interface ItemProps<T> {
|
|
74
74
|
item: T;
|
|
75
75
|
ctx: TranscriptItemContext;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/react/components.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/react/components.tsx"],"names":[],"mappings":"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"}
|
|
@@ -61,7 +61,7 @@ import type { TranscriptComponents, TranscriptItemContext, ViewSlotProps } from
|
|
|
61
61
|
* `hostContext` key wins field-by-field; `theme` is only filled in.
|
|
62
62
|
* Exported for tests; not part of the package surface.
|
|
63
63
|
*/
|
|
64
|
-
export declare function viewPropsWithThemeAnnounce(viewProps: TranscriptItemContext["viewProps"], mode: ThemeMode, defaults?: Pick<ViewSlotProps, "onCallTool" | "onUpdateModelContext" | "onUserMessage">): TranscriptItemContext["viewProps"];
|
|
64
|
+
export declare function viewPropsWithThemeAnnounce(viewProps: TranscriptItemContext["viewProps"], mode: ThemeMode, defaults?: Pick<ViewSlotProps, "onCallTool" | "onUpdateModelContext" | "onUserMessage" | "onOpenLink">): TranscriptItemContext["viewProps"];
|
|
65
65
|
/**
|
|
66
66
|
* The imperative seam (guuey#210): programmatic send/prefill/focus for
|
|
67
67
|
* hosts that stay on the batteries-included path (suggested-prompt chips
|
|
@@ -118,6 +118,28 @@ export interface GuueyChatHandle {
|
|
|
118
118
|
* to the transcript).
|
|
119
119
|
*/
|
|
120
120
|
viewSlotProps(): ViewSlotProps;
|
|
121
|
+
/**
|
|
122
|
+
* Forget this conversation (guuey#526 — the public-computer story).
|
|
123
|
+
*
|
|
124
|
+
* Local half, unconditional and immediate: aborts any in-flight turn
|
|
125
|
+
* FIRST (a completing turn can re-persist its own rows), drops the
|
|
126
|
+
* durable thread pointer AND its persisted storage key, wipes the
|
|
127
|
+
* visible transcript, clears the typed draft and any pending link ask —
|
|
128
|
+
* the next send mints a fresh thread.
|
|
129
|
+
*
|
|
130
|
+
* Server half, best-effort: when the surface has a platform door
|
|
131
|
+
* (`apiBaseUrl`) and a minted thread, fires
|
|
132
|
+
* `DELETE /v1/threads/:threadId` with the SAME identity the thread was
|
|
133
|
+
* created under (captured before any rotation) — real, child-first
|
|
134
|
+
* server-side erasure. A denied or failed delete still clears the
|
|
135
|
+
* device (the contract's unlinkability fallback) and surfaces nothing
|
|
136
|
+
* louder than the `thread-delete` debug event.
|
|
137
|
+
*
|
|
138
|
+
* Afterwards `onGuestSecretRotate` fires (when wired), so the identity
|
|
139
|
+
* owner mints a fresh guest secret — even an orphaned server row is
|
|
140
|
+
* unreachable from a rotated identity.
|
|
141
|
+
*/
|
|
142
|
+
clearConversation(): void;
|
|
121
143
|
}
|
|
122
144
|
export interface GuueyChatProps {
|
|
123
145
|
/** Pod base URL (with or without `/agent/invoke`). `null` disables chat. */
|
|
@@ -167,6 +189,19 @@ export interface GuueyChatProps {
|
|
|
167
189
|
strings?: Partial<ChatStrings>;
|
|
168
190
|
theme?: GuueyChatTheme;
|
|
169
191
|
mode?: ThemeMode;
|
|
192
|
+
/**
|
|
193
|
+
* Message-surface presentation (guuey#521). `"card"` (default) keeps
|
|
194
|
+
* assistant text on its own card; `"bare"` sits the transcript directly
|
|
195
|
+
* on the host's background so the embed reads native to the page.
|
|
196
|
+
*/
|
|
197
|
+
surface?: "card" | "bare";
|
|
198
|
+
/**
|
|
199
|
+
* Render the built-in composer (default `true`). `false` hides it for
|
|
200
|
+
* hosts that drive the conversation through the imperative handle
|
|
201
|
+
* (guuey#210 `ask`/`compose`) or their own input — pairs with the
|
|
202
|
+
* handle instead of forcing a CSS hide (guuey#521).
|
|
203
|
+
*/
|
|
204
|
+
composer?: boolean;
|
|
170
205
|
/** DOM windowing (§3.2). `false` renders everything. */
|
|
171
206
|
window?: TranscriptWindowing | false;
|
|
172
207
|
/**
|
|
@@ -175,6 +210,39 @@ export interface GuueyChatProps {
|
|
|
175
210
|
* pod door) when omitted; an explicit reader always wins.
|
|
176
211
|
*/
|
|
177
212
|
reader?: UiResourceReader;
|
|
213
|
+
/**
|
|
214
|
+
* The forget-this-device affordance (guuey#526 — the public-computer
|
|
215
|
+
* story): a quiet "Clear conversation" control with a two-tap confirm,
|
|
216
|
+
* wired to {@link GuueyChatHandle.clearConversation}. Default: ON when
|
|
217
|
+
* the surface runs GUEST identity (`getGuestSecret` present — the
|
|
218
|
+
* founder's seamless principle; a signed-in surface has account-level
|
|
219
|
+
* recourse), OFF otherwise; pass a boolean to override either way.
|
|
220
|
+
*/
|
|
221
|
+
clearAffordance?: boolean;
|
|
222
|
+
/**
|
|
223
|
+
* Guest-secret rotation hook (guuey#526): called at the END of every
|
|
224
|
+
* {@link GuueyChatHandle.clearConversation} — after the server delete
|
|
225
|
+
* has been dispatched with the OLD identity — so the owner of the
|
|
226
|
+
* secret's storage (the kit never stores it; `getGuestSecret` is a
|
|
227
|
+
* getter) mints a fresh one. Rotation is what makes even an orphaned
|
|
228
|
+
* server row unreachable: the new identity is a stranger to it. Guest
|
|
229
|
+
* KV state is (user,mcp)-scoped and is orphaned by rotation, by design.
|
|
230
|
+
*/
|
|
231
|
+
onGuestSecretRotate?: () => void;
|
|
232
|
+
/**
|
|
233
|
+
* Suggestion chips (guuey#533 — declared content, the one-truth design):
|
|
234
|
+
* rendered on the EMPTY transcript only (they retire the moment a
|
|
235
|
+
* message exists — live send or restored history both count), one tap
|
|
236
|
+
* SENDS the chip text verbatim through exactly the Send button's gate.
|
|
237
|
+
* Default-ON when content exists; `false` opts the surface out. The kit
|
|
238
|
+
* mirrors the card projection's caps render-side (4 shown, ≤80 chars
|
|
239
|
+
* each, blank-rejected) — a direct consumer bypassing the platform's
|
|
240
|
+
* emit gate gets the same bounds. Content is the host's (the widget
|
|
241
|
+
* passes the card's `suggestions`; a direct consumer passes its own);
|
|
242
|
+
* the kit never invents chips — an honest empty state beats invented
|
|
243
|
+
* questions in the builder's voice.
|
|
244
|
+
*/
|
|
245
|
+
suggestions?: readonly string[] | false;
|
|
178
246
|
/** The debug sink (spec §5) — fires only under the debug policy. */
|
|
179
247
|
onDebugEvent?: (event: ChatDebugEvent) => void;
|
|
180
248
|
/** R6 pass-through (relay hook, sandbox page/flags, host context…). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guuey-chat.d.ts","sourceRoot":"","sources":["../../src/react/guuey-chat.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,OAAO,EASL,KAAK,aAAa,EAEnB,MAAM,OAAO,CAAC;AACf,OAAO,
|
|
1
|
+
{"version":3,"file":"guuey-chat.d.ts","sourceRoot":"","sources":["../../src/react/guuey-chat.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,OAAO,EASL,KAAK,aAAa,EAEnB,MAAM,OAAO,CAAC;AACf,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGtE,OAAO,KAAK,EAAsC,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EAA2B,KAAK,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAEvF,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,KAAK,EACV,cAAc,EACd,SAAS,EACT,eAAe,EACf,UAAU,EAEV,WAAW,EACZ,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIlG;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,EAC7C,IAAI,EAAE,SAAS,EACf,QAAQ,GAAE,IAAI,CACZ,aAAa,EACb,YAAY,GAAG,sBAAsB,GAAG,eAAe,GAAG,YAAY,CAClE,GACL,qBAAqB,CAAC,WAAW,CAAC,CAgBpC;AAED;;;;;GAKG;AACH;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;;;OAOG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1E,gCAAgC;IAChC,aAAa,IAAI,IAAI,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;;;;;OAQG;IACH,aAAa,IAAI,aAAa,CAAC;IAC/B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,iBAAiB,IAAI,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,4EAA4E;IAC5E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC/E,cAAc,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACrC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,gEAAgE;IAChE,UAAU,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC3C,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wDAAwD;IACxD,MAAM,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;IACrC;;;;OAIG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;;OAQG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IACjC;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAAC;IACxC,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC/C,uEAAuE;IACvE,SAAS,CAAC,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAC/C;;;;OAIG;IACH,cAAc,CAAC,EAAE,CACf,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,KAC/D,IAAI,CAAC;IACV;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC;IAChE;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC;IAC5D,qDAAqD;IACrD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IAC1C;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACrD;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC5C;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAQD,eAAO,MAAM,SAAS,4GA8rBpB,CAAC"}
|
package/dist/react/guuey-chat.js
CHANGED
|
@@ -46,7 +46,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
46
46
|
* native GuueyChat, so there is no native surface to put a handle on yet.
|
|
47
47
|
*/
|
|
48
48
|
import { forwardRef, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState, } from "react";
|
|
49
|
-
import { createUiActionRelay, createUiResourceReader, createWebAdapters, } from "@guuey/agent-client";
|
|
49
|
+
import { createUiActionRelay, createUiResourceReader, createWebAdapters, deleteThread, } from "@guuey/agent-client";
|
|
50
50
|
import { useAgentInvoke } from "@guuey/agent-client/react";
|
|
51
51
|
import { unavailableToolCallResult } from "@guuey/mcp-apps-host";
|
|
52
52
|
import { calmPolicy, debugPolicy } from "../policy.js";
|
|
@@ -85,7 +85,7 @@ const PROMPT_STATE = {
|
|
|
85
85
|
dismiss: "dismissed",
|
|
86
86
|
};
|
|
87
87
|
export const GuueyChat = forwardRef(function GuueyChat(props, ref) {
|
|
88
|
-
const { endpointUrl, appId, apiBaseUrl, getAccessToken, getGuestSecret, adapters: adaptersProp, preset = "calm", policy: policyOverrides, components, strings: stringOverrides, theme = DEFAULT_CHAT_THEME, mode = "light", window: windowing, reader, onDebugEvent, viewProps, promotedViewKey, onViewRef, onViewsChange, onPromptAction, onHitlAnswer, oauthReturnTo, onOAuthAuthorize, onErrorAction, onActivity, onReady, onThread, className, style, } = props;
|
|
88
|
+
const { endpointUrl, appId, apiBaseUrl, getAccessToken, getGuestSecret, adapters: adaptersProp, preset = "calm", policy: policyOverrides, components, strings: stringOverrides, theme = DEFAULT_CHAT_THEME, mode = "light", surface = "card", composer = true, window: windowing, reader, onDebugEvent, viewProps, promotedViewKey, onViewRef, onViewsChange, onPromptAction, onHitlAnswer, oauthReturnTo, onOAuthAuthorize, onErrorAction, clearAffordance, onGuestSecretRotate, suggestions, onActivity, onReady, onThread, className, style, } = props;
|
|
89
89
|
// Identity stabilization (guuey#303 QA — the template's own chat-rail
|
|
90
90
|
// shipped the failure): hosts pass inline literals and arrows, so prop
|
|
91
91
|
// IDENTITY is noise. The getter props route through refs (presence, not
|
|
@@ -96,8 +96,14 @@ export const GuueyChat = forwardRef(function GuueyChat(props, ref) {
|
|
|
96
96
|
// host back → setState → re-render → "Maximum update depth exceeded".
|
|
97
97
|
const getAccessTokenRef = useRef(getAccessToken);
|
|
98
98
|
getAccessTokenRef.current = getAccessToken;
|
|
99
|
+
// The handle is created once ([] deps) but must read the LIVE base —
|
|
100
|
+
// same ref discipline as every other handle-read value in this file.
|
|
101
|
+
const apiBaseUrlRef = useRef(apiBaseUrl);
|
|
102
|
+
apiBaseUrlRef.current = apiBaseUrl;
|
|
99
103
|
const getGuestSecretRef = useRef(getGuestSecret);
|
|
100
104
|
getGuestSecretRef.current = getGuestSecret;
|
|
105
|
+
const onGuestSecretRotateRef = useRef(onGuestSecretRotate);
|
|
106
|
+
onGuestSecretRotateRef.current = onGuestSecretRotate;
|
|
101
107
|
const hasAccessToken = getAccessToken !== undefined;
|
|
102
108
|
const hasGuestSecret = getGuestSecret !== undefined;
|
|
103
109
|
const adapters = useMemo(() => adaptersProp ??
|
|
@@ -350,11 +356,63 @@ export const GuueyChat = forwardRef(function GuueyChat(props, ref) {
|
|
|
350
356
|
}
|
|
351
357
|
void live.invoke.send(next).catch(() => { });
|
|
352
358
|
}, [busy]);
|
|
359
|
+
// guuey#522: the kit default for `ui/open-link` NEVER navigates
|
|
360
|
+
// sight-unseen — the ask surfaces as a disclosure affordance (host +
|
|
361
|
+
// full URL + Open/Dismiss) and the OPEN is the human's own anchor
|
|
362
|
+
// click, so the browser's activation gate and the review posture agree
|
|
363
|
+
// by construction. ONE pending ask at a time, newest replaces (a
|
|
364
|
+
// card spamming asks can never grow an unbounded stack).
|
|
365
|
+
const [pendingLink, setPendingLink] = useState(null);
|
|
366
|
+
// guuey#526: the two-tap confirm window for the clear affordance (an
|
|
367
|
+
// accidental tap must not nuke a conversation; a second tap within the
|
|
368
|
+
// window does — the state auto-expires).
|
|
369
|
+
const [clearArmed, setClearArmed] = useState(false);
|
|
370
|
+
useEffect(() => {
|
|
371
|
+
if (!clearArmed)
|
|
372
|
+
return;
|
|
373
|
+
const timer = setTimeout(() => setClearArmed(false), 4000);
|
|
374
|
+
return () => clearTimeout(timer);
|
|
375
|
+
}, [clearArmed]);
|
|
376
|
+
const showClearAffordance = clearAffordance ?? getGuestSecret !== undefined;
|
|
377
|
+
// guuey#533: the render-side mirror of the card projection's emit caps
|
|
378
|
+
// (4 shown / <=80 chars / blank-rejected) — same defense posture as the
|
|
379
|
+
// table normalizer: the kit is public npm and cannot assume every host
|
|
380
|
+
// routed content through platform's write gate.
|
|
381
|
+
const chips = useMemo(() => {
|
|
382
|
+
if (suggestions === false || suggestions === undefined)
|
|
383
|
+
return [];
|
|
384
|
+
return suggestions
|
|
385
|
+
.map((c) => c.trim())
|
|
386
|
+
.filter((c) => c.length > 0 && c.length <= 80)
|
|
387
|
+
.slice(0, 4);
|
|
388
|
+
}, [suggestions]);
|
|
389
|
+
const sendSuggestion = useCallback((text) => {
|
|
390
|
+
const live = liveRef.current;
|
|
391
|
+
// Exactly the Send button's gate (the #210 rule): unavailable, busy,
|
|
392
|
+
// or blank -> the tap is a no-op, never a bypass. The typed draft is
|
|
393
|
+
// left untouched (a chip send must not eat a half-typed message).
|
|
394
|
+
if (!live.available || live.busy || text.trim() === "")
|
|
395
|
+
return;
|
|
396
|
+
void live.invoke.send(text).catch(() => {
|
|
397
|
+
// Same contract as submit: the hook owns failure surfacing.
|
|
398
|
+
});
|
|
399
|
+
}, []);
|
|
400
|
+
const defaultOnOpenLink = useCallback((url) => {
|
|
401
|
+
setPendingLink(url);
|
|
402
|
+
}, []);
|
|
353
403
|
const effectiveViewProps = useMemo(() => viewPropsWithThemeAnnounce(viewProps, mode, {
|
|
354
404
|
...(stagedDefaultOnCallTool !== undefined ? { onCallTool: stagedDefaultOnCallTool } : {}),
|
|
355
405
|
onUpdateModelContext: defaultOnUpdateModelContext,
|
|
356
406
|
onUserMessage: defaultOnUserMessage,
|
|
357
|
-
|
|
407
|
+
onOpenLink: defaultOnOpenLink,
|
|
408
|
+
}), [
|
|
409
|
+
viewProps,
|
|
410
|
+
mode,
|
|
411
|
+
stagedDefaultOnCallTool,
|
|
412
|
+
defaultOnUpdateModelContext,
|
|
413
|
+
defaultOnUserMessage,
|
|
414
|
+
defaultOnOpenLink,
|
|
415
|
+
]);
|
|
358
416
|
// The canvas-host door (guuey#335): a host mounting views DIRECTLY from
|
|
359
417
|
// the roster (the chat-rail shell) needs the SAME slot wiring the kit's
|
|
360
418
|
// inline mounts get — theme announce, action relay, context sink. Read
|
|
@@ -368,6 +426,7 @@ export const GuueyChat = forwardRef(function GuueyChat(props, ref) {
|
|
|
368
426
|
...(stagedDefaultOnCallTool !== undefined ? { onCallTool: stagedDefaultOnCallTool } : {}),
|
|
369
427
|
onUpdateModelContext: defaultOnUpdateModelContext,
|
|
370
428
|
onUserMessage: defaultOnUserMessage,
|
|
429
|
+
onOpenLink: defaultOnOpenLink,
|
|
371
430
|
hostContext: { theme: mode },
|
|
372
431
|
}
|
|
373
432
|
: (effectiveViewProps ?? {});
|
|
@@ -394,6 +453,49 @@ export const GuueyChat = forwardRef(function GuueyChat(props, ref) {
|
|
|
394
453
|
focusComposer: () => {
|
|
395
454
|
inputRef.current?.focus();
|
|
396
455
|
},
|
|
456
|
+
clearConversation: () => {
|
|
457
|
+
const live = liveRef.current;
|
|
458
|
+
// Contract ordering (guuey#526, platform's frozen comment): abort
|
|
459
|
+
// FIRST — a completing turn can re-persist its own rows after the
|
|
460
|
+
// delete. Capture the id + guest secret BEFORE anything clears or
|
|
461
|
+
// rotates them: the delete must authenticate as the identity that
|
|
462
|
+
// OWNS the thread.
|
|
463
|
+
live.invoke.abort();
|
|
464
|
+
const threadId = threadIdRef.current;
|
|
465
|
+
const getGuest = getGuestSecretRef.current;
|
|
466
|
+
const guestSecret = getGuest !== undefined ? getGuest() : null;
|
|
467
|
+
if (apiBaseUrlRef.current !== undefined && threadId !== null) {
|
|
468
|
+
// Best-effort server erasure — fire-and-forget, never blocks the
|
|
469
|
+
// local clear. 200 and 404 are both "deleted" (idempotent by
|
|
470
|
+
// contract); denied/failed still cleared locally below (the
|
|
471
|
+
// unlinkability fallback) and surface ONLY as a debug line.
|
|
472
|
+
void deleteThread({
|
|
473
|
+
apiBaseUrl: apiBaseUrlRef.current,
|
|
474
|
+
threadId,
|
|
475
|
+
...(getAccessTokenRef.current !== undefined
|
|
476
|
+
? { getAccessToken: getAccessTokenRef.current }
|
|
477
|
+
: {}),
|
|
478
|
+
guestSecret,
|
|
479
|
+
}).then((outcome) => {
|
|
480
|
+
onDebugEventRef.current?.({ type: "thread-delete", threadId, outcome });
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
onDebugEventRef.current?.({ type: "thread-delete", threadId, outcome: "skipped" });
|
|
485
|
+
}
|
|
486
|
+
// The hook's reset owns the durable local half (pointer + storage
|
|
487
|
+
// key, transcript/fold/cards); the kit clears ITS OWN residue —
|
|
488
|
+
// the typed draft, a pending link ask, queued doorbells — so the
|
|
489
|
+
// device holds nothing of the conversation.
|
|
490
|
+
live.invoke.reset();
|
|
491
|
+
setInput("");
|
|
492
|
+
setPendingLink(null);
|
|
493
|
+
pendingDoorbellsRef.current = [];
|
|
494
|
+
// Rotation LAST: the delete above already captured the old
|
|
495
|
+
// identity, so the fresh secret is a stranger to even an orphaned
|
|
496
|
+
// server row.
|
|
497
|
+
onGuestSecretRotateRef.current?.();
|
|
498
|
+
},
|
|
397
499
|
// A getter, not a captured value: the handle is created once, but the
|
|
398
500
|
// thread hydrates after mount and can change — reads go through the
|
|
399
501
|
// same ref the default reader uses, so it is always the live id.
|
|
@@ -472,9 +574,17 @@ export const GuueyChat = forwardRef(function GuueyChat(props, ref) {
|
|
|
472
574
|
onPromptAction?.(item, action);
|
|
473
575
|
}, [resolvePrompt, answerHitlPrompt, onHitlAnswer, onPromptAction, oauthReturnTo, onOAuthAuthorize]);
|
|
474
576
|
const strings = policy.strings;
|
|
475
|
-
return (_jsxs("div", { className: `guuey-chat-surface${className !== undefined ? ` ${className}` : ""}`, style: style, children: [_jsx(Transcript, { plan: plan, strings: strings, theme: theme, mode: mode, ...(windowing !== undefined ? { window: windowing } : {}), ...(components !== undefined ? { components } : {}), onToggle: toggle, onRetry: handleRetry, onPromptAction: handlePromptAction, ...(onErrorAction !== undefined ? { onErrorAction } : {}), resolvedMounts: resolvedMounts, onViewPhase: onViewPhase, onViewDiagnosis: onViewDiagnosis, ...(onViewRef !== undefined ? { onViewRef } : {}), viewProps: effectiveViewProps }),
|
|
577
|
+
return (_jsxs("div", { className: `guuey-chat-surface${className !== undefined ? ` ${className}` : ""}`, style: style, children: [_jsx(Transcript, { plan: plan, strings: strings, theme: theme, mode: mode, surface: surface, ...(windowing !== undefined ? { window: windowing } : {}), ...(components !== undefined ? { components } : {}), onToggle: toggle, onRetry: handleRetry, onPromptAction: handlePromptAction, ...(onErrorAction !== undefined ? { onErrorAction } : {}), resolvedMounts: resolvedMounts, onViewPhase: onViewPhase, onViewDiagnosis: onViewDiagnosis, ...(onViewRef !== undefined ? { onViewRef } : {}), viewProps: effectiveViewProps }), chips.length > 0 && inputs.messages.length === 0 && (_jsx("nav", { className: "guuey-chat-chips-row", "aria-label": strings.suggestionsLabel, children: chips.map((chip) => (_jsx("button", { type: "button", className: "guuey-chat-chip", onClick: () => sendSuggestion(chip), children: chip }, chip))) })), showClearAffordance && inputs.messages.length > 0 && (_jsx("div", { className: "guuey-chat-clear-row", children: _jsx("button", { type: "button", className: `guuey-chat-clear${clearArmed ? " guuey-chat-clear-armed" : ""}`, onClick: () => {
|
|
578
|
+
if (clearArmed) {
|
|
579
|
+
setClearArmed(false);
|
|
580
|
+
handle.clearConversation();
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
setClearArmed(true);
|
|
584
|
+
}
|
|
585
|
+
}, children: clearArmed ? strings.clearConversationConfirm : strings.clearConversationLabel }) })), pendingLink !== null && (_jsxs("div", { role: "status", className: "guuey-chat-link-ask", children: [_jsx("span", { className: "guuey-chat-link-ask-label", children: strings.linkAskLabel(new URL(pendingLink).host) }), _jsx("span", { className: "guuey-chat-link-ask-url", children: pendingLink }), _jsx("a", { className: "guuey-chat-link-ask-open", href: pendingLink, target: "_blank", rel: "noopener noreferrer", onClick: () => setPendingLink(null), children: strings.linkOpen }), _jsx("button", { type: "button", className: "guuey-chat-link-ask-dismiss", onClick: () => setPendingLink(null), children: strings.linkDismiss })] })), oauthReturn.notice !== null && (_jsxs("p", { role: "status", className: `guuey-chat-oauth-notice guuey-chat-oauth-${oauthReturn.notice.kind}`, children: [oauthReturn.notice.kind === "connected"
|
|
476
586
|
? strings.oauthConnected(oauthReturn.notice.serverName)
|
|
477
|
-
: strings.oauthFailed(oauthReturn.notice.reason), _jsx("button", { type: "button", className: "guuey-chat-oauth-dismiss", onClick: oauthReturn.dismiss, children: strings.promptDismissed })] })), _jsxs("form", { className: "guuey-chat-composer", onSubmit: (e) => {
|
|
587
|
+
: strings.oauthFailed(oauthReturn.notice.reason), _jsx("button", { type: "button", className: "guuey-chat-oauth-dismiss", onClick: oauthReturn.dismiss, children: strings.promptDismissed })] })), composer && (_jsxs("form", { className: "guuey-chat-composer", onSubmit: (e) => {
|
|
478
588
|
e.preventDefault();
|
|
479
589
|
submit();
|
|
480
590
|
}, children: [_jsx("textarea", { ref: inputRef, id: composerId, name: "message", className: "guuey-chat-composer-input", rows: 1, value: input, onChange: (e) => setInput(e.target.value), onKeyDown: (e) => {
|
|
@@ -484,5 +594,5 @@ export const GuueyChat = forwardRef(function GuueyChat(props, ref) {
|
|
|
484
594
|
e.preventDefault();
|
|
485
595
|
submit();
|
|
486
596
|
}
|
|
487
|
-
}, disabled: !available, "aria-label": strings.composerLabel, placeholder: available ? strings.composerPlaceholder : strings.composerUnavailable }), busy ? (_jsx("button", { type: "button", className: "guuey-chat-composer-stop", onClick: () => invoke.abort(), children: strings.stop })) : (_jsx("button", { type: "submit", className: "guuey-chat-composer-send", disabled: !canSend, children: strings.send }))] })] }));
|
|
597
|
+
}, disabled: !available, "aria-label": strings.composerLabel, placeholder: available ? strings.composerPlaceholder : strings.composerUnavailable }), busy ? (_jsx("button", { type: "button", className: "guuey-chat-composer-stop", onClick: () => invoke.abort(), children: strings.stop })) : (_jsx("button", { type: "submit", className: "guuey-chat-composer-send", disabled: !canSend, children: strings.send }))] }))] }));
|
|
488
598
|
});
|
package/dist/react/markdown.d.ts
CHANGED
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The R1 markdown surface — the ONE place `@guuey/chat` renders
|
|
3
|
-
* agent-influenced content in the embedder's origin, and therefore the
|
|
4
|
-
* package's sanitizer boundary (spec §3.1).
|
|
5
|
-
*
|
|
6
|
-
* The pipeline is `@silverprotocol/richtext`: markdown → a TYPED AST in
|
|
7
|
-
* which raw HTML is structurally unrepresentable (no HTML node type
|
|
8
|
-
* exists; angle brackets can only ever be literal text) → React elements.
|
|
9
|
-
* That is deliberately STRONGER than the spec's letter (markdown → HTML →
|
|
10
|
-
* sanitize): there is no HTML string at any point, so there is nothing to
|
|
11
|
-
* sanitize incorrectly — no `dangerouslySetInnerHTML` anywhere in this
|
|
12
|
-
* package.
|
|
13
|
-
*
|
|
14
|
-
* Safety properties, each pinned by a test:
|
|
15
|
-
* - raw HTML (`<img onerror=…>`, `<script>`) renders as literal text;
|
|
16
|
-
* - link `href` is populated upstream ONLY for http/https/mailto —
|
|
17
|
-
* `javascript:`, `data:`, `vbscript:`, and relative targets parse as
|
|
18
|
-
* styled text with NO anchor `href` (richtext's `SAFE_HREF` allowlist);
|
|
19
|
-
* - navigable links carry `rel="noopener noreferrer" target="_blank"`;
|
|
20
|
-
* - there is NO image syntax in the node vocabulary at all — the spec's
|
|
21
|
-
* F5 ruling (markdown images off; R7 media blocks are the sanctioned
|
|
22
|
-
* image path) holds structurally, not by configuration.
|
|
23
|
-
*
|
|
24
|
-
* Streaming-tolerant by upstream design: unclosed emphasis mid-delta
|
|
25
|
-
* renders as formatted-so-far.
|
|
26
|
-
*/
|
|
27
1
|
import type { ReactNode } from "react";
|
|
28
2
|
import { type RichTextTableBlock } from "../richtext-table.js";
|
|
29
3
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/react/markdown.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/react/markdown.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AA0GlF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,GAAG,SAAS,CA4B9E;AAwED,qEAAqE;AACrE,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAQ9D"}
|
package/dist/react/markdown.js
CHANGED
|
@@ -1,11 +1,102 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The R1 markdown surface — the ONE place `@guuey/chat` renders
|
|
4
|
+
* agent-influenced content in the embedder's origin, and therefore the
|
|
5
|
+
* package's sanitizer boundary (spec §3.1).
|
|
6
|
+
*
|
|
7
|
+
* The pipeline is `@silverprotocol/richtext`: markdown → a TYPED AST in
|
|
8
|
+
* which raw HTML is structurally unrepresentable (no HTML node type
|
|
9
|
+
* exists; angle brackets can only ever be literal text) → React elements.
|
|
10
|
+
* That is deliberately STRONGER than the spec's letter (markdown → HTML →
|
|
11
|
+
* sanitize): there is no HTML string at any point, so there is nothing to
|
|
12
|
+
* sanitize incorrectly — no `dangerouslySetInnerHTML` anywhere in this
|
|
13
|
+
* package.
|
|
14
|
+
*
|
|
15
|
+
* Safety properties, each pinned by a test:
|
|
16
|
+
* - raw HTML (`<img onerror=…>`, `<script>`) renders as literal text;
|
|
17
|
+
* - link `href` is populated upstream ONLY for http/https/mailto —
|
|
18
|
+
* `javascript:`, `data:`, `vbscript:`, and relative targets parse as
|
|
19
|
+
* styled text with NO anchor `href` (richtext's `SAFE_HREF` allowlist);
|
|
20
|
+
* - navigable links carry `rel="noopener noreferrer" target="_blank"`;
|
|
21
|
+
* - there is NO image syntax in the node vocabulary at all — the spec's
|
|
22
|
+
* F5 ruling (markdown images off; R7 media blocks are the sanctioned
|
|
23
|
+
* image path) holds structurally, not by configuration.
|
|
24
|
+
*
|
|
25
|
+
* Streaming-tolerant by upstream design: unclosed emphasis mid-delta
|
|
26
|
+
* renders as formatted-so-far.
|
|
27
|
+
*/
|
|
28
|
+
import { useState } from "react";
|
|
2
29
|
import { parseRichText, } from "@silverprotocol/richtext";
|
|
3
30
|
import { normalizeTableRow } from "../richtext-table.js";
|
|
31
|
+
/**
|
|
32
|
+
* Bare-URL autolink (guuey#515): richtext mints `link` nodes only from
|
|
33
|
+
* explicit `[text](url)` markdown, so a plain "https://…" in agent prose
|
|
34
|
+
* parses as a text node and rendered as untappable static text — on a
|
|
35
|
+
* phone that means retyping a URL out of a chat bubble. Linkify at the
|
|
36
|
+
* render boundary, conservatively:
|
|
37
|
+
* - the scheme is part of the match (http/https only), so no
|
|
38
|
+
* `javascript:`/`data:` target can ever qualify — the SAFE_HREF
|
|
39
|
+
* property holds by construction;
|
|
40
|
+
* - built as React elements (no HTML string exists to inject into),
|
|
41
|
+
* same `rel`/`target` contract as explicit links;
|
|
42
|
+
* - trailing punctuation stays prose ("see https://guuey.com." must not
|
|
43
|
+
* link the dot), while a balanced closing paren/bracket stays in the
|
|
44
|
+
* URL (wiki-style paths);
|
|
45
|
+
* - schemeless "www.foo.com" is deliberately NOT linkified — guessing
|
|
46
|
+
* schemes is how autolinkers start lying;
|
|
47
|
+
* - code spans are untouched (they render `node.code`, not this path).
|
|
48
|
+
*/
|
|
49
|
+
const BARE_URL = /https?:\/\/[^\s<>]+/g;
|
|
50
|
+
function trimTrailingPunctuation(url) {
|
|
51
|
+
for (;;) {
|
|
52
|
+
const last = url[url.length - 1];
|
|
53
|
+
if (last === undefined)
|
|
54
|
+
return url;
|
|
55
|
+
if (last === ")" || last === "]") {
|
|
56
|
+
const open = last === ")" ? "(" : "[";
|
|
57
|
+
const opens = url.split(open).length - 1;
|
|
58
|
+
const closes = url.split(last).length - 1;
|
|
59
|
+
if (closes > opens) {
|
|
60
|
+
url = url.slice(0, -1);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
return url;
|
|
64
|
+
}
|
|
65
|
+
if (".,;:!?'\"»›".includes(last)) {
|
|
66
|
+
url = url.slice(0, -1);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
return url;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function linkify(text) {
|
|
73
|
+
if (!text.includes("http"))
|
|
74
|
+
return text;
|
|
75
|
+
const out = [];
|
|
76
|
+
let consumed = 0;
|
|
77
|
+
for (const match of text.matchAll(BARE_URL)) {
|
|
78
|
+
const start = match.index;
|
|
79
|
+
if (start === undefined)
|
|
80
|
+
continue;
|
|
81
|
+
const url = trimTrailingPunctuation(match[0]);
|
|
82
|
+
if (url.length === 0)
|
|
83
|
+
continue;
|
|
84
|
+
if (start > consumed)
|
|
85
|
+
out.push(text.slice(consumed, start));
|
|
86
|
+
out.push(_jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", children: url }, `url-${start}`));
|
|
87
|
+
consumed = start + url.length;
|
|
88
|
+
}
|
|
89
|
+
if (out.length === 0)
|
|
90
|
+
return text;
|
|
91
|
+
if (consumed < text.length)
|
|
92
|
+
out.push(text.slice(consumed));
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
4
95
|
function Inline({ nodes }) {
|
|
5
96
|
return nodes.map((node, i) => {
|
|
6
97
|
switch (node.type) {
|
|
7
98
|
case "text":
|
|
8
|
-
return _jsx("span", { children: node.text }, i);
|
|
99
|
+
return _jsx("span", { children: linkify(node.text) }, i);
|
|
9
100
|
case "break":
|
|
10
101
|
return _jsx("br", {}, i);
|
|
11
102
|
case "strong":
|
|
@@ -39,13 +130,35 @@ function Block({ block }) {
|
|
|
39
130
|
case "heading":
|
|
40
131
|
return (_jsx("p", { className: `guuey-chat-heading${block.level <= 3 ? " guuey-chat-heading-major" : ""}`, children: _jsx(Inline, { nodes: block.children }) }));
|
|
41
132
|
case "code-fence":
|
|
42
|
-
return _jsx(
|
|
133
|
+
return _jsx(CodeFence, { code: block.code });
|
|
43
134
|
case "list": {
|
|
44
135
|
const Tag = block.ordered ? "ol" : "ul";
|
|
45
136
|
return (_jsx(Tag, { ...(block.ordered && block.start !== undefined ? { start: block.start } : {}), children: block.items.map((item, j) => (_jsx("li", { children: _jsx(Inline, { nodes: item.children }) }, j))) }));
|
|
46
137
|
}
|
|
47
138
|
}
|
|
48
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* A fenced code block with its copy affordance (guuey#529 — the
|
|
142
|
+
* seamless-default family: every embedder's visitors get it with zero
|
|
143
|
+
* setup). The button lives in a wrapper DIV so the `<pre>` holds ONLY the
|
|
144
|
+
* code (select-all inside the fence never picks up UI text); the
|
|
145
|
+
* transcript's sibling-margin rules name the wrapper class alongside
|
|
146
|
+
* `pre`. Copy failure is shown truthfully: the label simply stays "Copy"
|
|
147
|
+
* (clipboard access needs a secure context + the iframe's
|
|
148
|
+
* clipboard-write grant — manual selection always still works).
|
|
149
|
+
*/
|
|
150
|
+
function CodeFence({ code }) {
|
|
151
|
+
const [copied, setCopied] = useState(false);
|
|
152
|
+
return (_jsxs("div", { className: "guuey-chat-code-wrap", children: [_jsx("pre", { className: "guuey-chat-code-fence", children: code }), _jsx("button", { type: "button", className: "guuey-chat-code-copy", "aria-label": copied ? "Copied" : "Copy code", onClick: () => {
|
|
153
|
+
const clipboard = navigator.clipboard;
|
|
154
|
+
if (clipboard === undefined)
|
|
155
|
+
return;
|
|
156
|
+
clipboard.writeText(code).then(() => {
|
|
157
|
+
setCopied(true);
|
|
158
|
+
setTimeout(() => setCopied(false), 1600);
|
|
159
|
+
}, () => setCopied(false));
|
|
160
|
+
}, children: copied ? "Copied" : "Copy" })] }));
|
|
161
|
+
}
|
|
49
162
|
/** Sanitized markdown → React elements (see the module docblock). */
|
|
50
163
|
export function Markdown({ text }) {
|
|
51
164
|
return (_jsx(_Fragment, { children: parseRichText(text).map((block, i) => (_jsx(Block, { block: block }, i))) }));
|