@frankieai/react 0.2.8 → 0.2.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/dist/frankie.css +1 -1
- package/dist/index.cjs +596 -203
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +114 -10
- package/dist/index.d.ts +114 -10
- package/dist/index.js +579 -189
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
- package/src/components/chat/ChatPanel.tsx +9 -1
- package/src/components/chat/Choices.tsx +52 -0
- package/src/components/chat/Composer.tsx +33 -18
- package/src/components/chat/FrankieChatPage.tsx +4 -1
- package/src/components/chat/FrankieInline.tsx +9 -3
- package/src/components/chat/FrankieWidget.tsx +12 -3
- package/src/components/chat/Markdown.tsx +15 -8
- package/src/components/chat/MessageBubble.tsx +53 -10
- package/src/components/chat/ToolActivityList.tsx +2 -1
- package/src/generated/css-content.ts +2 -0
- package/src/hooks/FrankieProvider.tsx +30 -3
- package/src/hooks/useFrankieChat.ts +57 -1
- package/src/index.ts +4 -1
- package/src/lib/choices.ts +49 -0
- package/src/lib/client.ts +136 -5
- package/src/lib/ensure-frankie-styles.ts +32 -0
- package/src/lib/identity.ts +39 -0
- package/src/lib/styles-ready.ts +83 -0
- package/src/lib/theme-inherit.test.ts +68 -0
- package/src/lib/theme-inherit.ts +89 -0
- package/src/styles/frankie.css +136 -3
- package/src/styles-ready.test.tsx +98 -0
- package/src/widget.test.tsx +218 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, CSSProperties, ButtonHTMLAttributes, HTMLAttributes, TextareaHTMLAttributes } from 'react';
|
|
2
|
+
import { ReactNode, CSSProperties, MutableRefObject, ButtonHTMLAttributes, HTMLAttributes, TextareaHTMLAttributes } from 'react';
|
|
3
3
|
import { PublicWidgetConfig, CreateSessionResponse, ChatAttachment, FrankieServerEvent, WidgetTheme, LoadedSkill, ApiToolInventory } from '@frankieai/core';
|
|
4
4
|
export { FrankieServerEvent, PublicWidgetConfig, WidgetTheme } from '@frankieai/core';
|
|
5
5
|
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
@@ -7,6 +7,26 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
7
7
|
import { ClassValue } from 'clsx';
|
|
8
8
|
|
|
9
9
|
type PageContext = Record<string, string | number | boolean | null>;
|
|
10
|
+
/**
|
|
11
|
+
* How the widget reaches the host's own API for client-side tool execution.
|
|
12
|
+
*
|
|
13
|
+
* When a workspace marks an API source `execution: "client"`, Frankie asks the
|
|
14
|
+
* browser to make the call itself — from the user's existing session — instead
|
|
15
|
+
* of calling the API from Frankie's servers. This says where that API lives and
|
|
16
|
+
* how the browser authenticates to it: normally the app's own cookies
|
|
17
|
+
* (`credentials: "include"` on a same-origin or CORS-allowed API), or a bearer
|
|
18
|
+
* token the app already holds, added via `headers`. Frankie sends no credential
|
|
19
|
+
* of its own; the host API authorizes the call exactly as it would any request
|
|
20
|
+
* from the host's frontend.
|
|
21
|
+
*/
|
|
22
|
+
interface HostApiClientOptions {
|
|
23
|
+
/** Base URL that operation paths resolve against, e.g. "/api" or "https://api.acme.com". */
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
/** fetch credentials mode. "include" sends the app's cookies. Defaults to "same-origin". */
|
|
26
|
+
credentials?: RequestCredentials;
|
|
27
|
+
/** Extra headers for every host call (e.g. an app access token). Awaited per call. */
|
|
28
|
+
headers?: () => Promise<Record<string, string>> | Record<string, string>;
|
|
29
|
+
}
|
|
10
30
|
/**
|
|
11
31
|
* The hosted Frankie data plane. Embedding needs only a publishable key, so
|
|
12
32
|
* `baseUrl` defaults here — override it only when self-hosting the server.
|
|
@@ -24,6 +44,13 @@ interface FrankieClientOptions {
|
|
|
24
44
|
*/
|
|
25
45
|
getUserToken?: () => Promise<string | undefined> | string | undefined;
|
|
26
46
|
fetchImpl?: typeof fetch;
|
|
47
|
+
/**
|
|
48
|
+
* Reaches the host's own API for `execution: "client"` tool calls (see
|
|
49
|
+
* `HostApiClientOptions`). Required only for workspaces that use client-side
|
|
50
|
+
* execution; omit it and a client-mode tool reports that no host API is
|
|
51
|
+
* configured rather than calling anything.
|
|
52
|
+
*/
|
|
53
|
+
api?: HostApiClientOptions;
|
|
27
54
|
/**
|
|
28
55
|
* Remember the session (and, in the hook, the transcript) in localStorage so a
|
|
29
56
|
* page reload resumes the same anonymous conversation. Authenticated chat
|
|
@@ -51,7 +78,8 @@ declare class FrankieClient {
|
|
|
51
78
|
private readonly baseUrl;
|
|
52
79
|
private readonly doFetch;
|
|
53
80
|
private session?;
|
|
54
|
-
|
|
81
|
+
/** Identity (JWT `sub`, else the raw token) the cached session was issued for. */
|
|
82
|
+
private sessionUserKey?;
|
|
55
83
|
constructor(options: FrankieClientOptions);
|
|
56
84
|
get sessionId(): string | undefined;
|
|
57
85
|
get conversationId(): string | undefined;
|
|
@@ -79,6 +107,14 @@ declare class FrankieClient {
|
|
|
79
107
|
attachments?: ChatAttachment[];
|
|
80
108
|
signal?: AbortSignal;
|
|
81
109
|
}, onEvent: (event: FrankieServerEvent) => void): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Execute one host API call in the browser and report the outcome back to the
|
|
112
|
+
* server. Never throws: a transport failure or a missing `api` client becomes
|
|
113
|
+
* a result the model can see and explain, exactly like a non-2xx status.
|
|
114
|
+
*/
|
|
115
|
+
private runClientTool;
|
|
116
|
+
private callHostApi;
|
|
117
|
+
private postToolResult;
|
|
82
118
|
private unwrap;
|
|
83
119
|
}
|
|
84
120
|
declare function readEventStream(body: ReadableStream<Uint8Array>): AsyncGenerator<FrankieServerEvent>;
|
|
@@ -88,6 +124,15 @@ interface FrankieProviderProps extends FrankieClientOptions {
|
|
|
88
124
|
context?: PageContext;
|
|
89
125
|
theme?: WidgetTheme;
|
|
90
126
|
config?: PublicWidgetConfig;
|
|
127
|
+
/**
|
|
128
|
+
* Set to `false` if the host compiles Frankie's classes itself instead of
|
|
129
|
+
* loading `@frankieai/react/styles.css` — e.g. a Tailwind v4 host that
|
|
130
|
+
* `@source`s this package — so it isn't given a second, independently-themed
|
|
131
|
+
* copy of the stylesheet when its own build never sets the styles-ready
|
|
132
|
+
* sentinel. Defaults to `true`: a host that simply forgot the import, or
|
|
133
|
+
* whose CSS chunk hasn't landed yet, still gets a correctly-styled widget.
|
|
134
|
+
*/
|
|
135
|
+
injectStyles?: boolean;
|
|
91
136
|
}
|
|
92
137
|
interface FrankieContextValue {
|
|
93
138
|
client: FrankieClient;
|
|
@@ -96,8 +141,9 @@ interface FrankieContextValue {
|
|
|
96
141
|
loading: boolean;
|
|
97
142
|
context?: PageContext;
|
|
98
143
|
theme?: WidgetTheme;
|
|
144
|
+
injectStyles: boolean;
|
|
99
145
|
}
|
|
100
|
-
declare function FrankieProvider({ children, context, theme, config: providedConfig, ...clientOptions }: FrankieProviderProps): react.JSX.Element;
|
|
146
|
+
declare function FrankieProvider({ children, context, theme, config: providedConfig, injectStyles, ...clientOptions }: FrankieProviderProps): react.JSX.Element;
|
|
101
147
|
declare function useFrankie(): FrankieContextValue;
|
|
102
148
|
declare function useFrankieTheme(): WidgetTheme | undefined;
|
|
103
149
|
|
|
@@ -265,8 +311,10 @@ interface ComposerProps {
|
|
|
265
311
|
attachments?: ComposerAttachmentsConfig;
|
|
266
312
|
/** Enables a model dropdown when provided with at least one model. */
|
|
267
313
|
modelPicker?: ComposerModelPicker;
|
|
314
|
+
/** Populated with a function that focuses the input, so a "type your own" choice can jump here. */
|
|
315
|
+
focusHandleRef?: MutableRefObject<(() => void) | null>;
|
|
268
316
|
}
|
|
269
|
-
declare function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, attachments, modelPicker }: ComposerProps): react.JSX.Element;
|
|
317
|
+
declare function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, attachments, modelPicker, focusHandleRef }: ComposerProps): react.JSX.Element;
|
|
270
318
|
|
|
271
319
|
interface MessageBubbleProps {
|
|
272
320
|
message: ChatMessage;
|
|
@@ -274,22 +322,53 @@ interface MessageBubbleProps {
|
|
|
274
322
|
avatarUrl?: string;
|
|
275
323
|
showToolActivity: boolean;
|
|
276
324
|
renderTool?: (tool: ToolActivity) => ReactNode;
|
|
325
|
+
/** Send a picked choice as the next message. Omitted when the host renders its own. */
|
|
326
|
+
onChoose?: (choice: string) => void;
|
|
327
|
+
/** Focus the composer so the user can type their own answer instead of picking. */
|
|
328
|
+
onRequestInput?: () => void;
|
|
329
|
+
/** True only for the newest message, so stale choices from earlier turns aren't clickable. */
|
|
330
|
+
active?: boolean;
|
|
331
|
+
/** A turn is in flight, so choices should be disabled. */
|
|
332
|
+
busy?: boolean;
|
|
277
333
|
}
|
|
278
|
-
declare
|
|
334
|
+
declare const MessageBubble: react.NamedExoticComponent<MessageBubbleProps>;
|
|
279
335
|
declare function TypingDots(): react.JSX.Element;
|
|
336
|
+
/**
|
|
337
|
+
* A shimmering skeleton shown while the reply is still being produced — before
|
|
338
|
+
* the first words, or during the pause after a tool result while the model
|
|
339
|
+
* composes its answer — so those gaps read as "loading" rather than frozen.
|
|
340
|
+
*/
|
|
341
|
+
declare function Thinking(): react.JSX.Element;
|
|
280
342
|
|
|
281
343
|
/**
|
|
282
|
-
* A deliberately small Markdown subset — paragraphs, lists, fenced
|
|
283
|
-
* italic, inline code and links.
|
|
344
|
+
* A deliberately small Markdown subset — headings, paragraphs, lists, fenced
|
|
345
|
+
* code, bold, italic, inline code and links.
|
|
284
346
|
*
|
|
285
347
|
* Everything is rendered as React elements; nothing goes through
|
|
286
348
|
* `dangerouslySetInnerHTML`. Model output lands in someone else's production
|
|
287
349
|
* page, so it is never trusted as markup.
|
|
350
|
+
*
|
|
351
|
+
* Memoized on `text`: while a reply streams, the parent re-renders every frame,
|
|
352
|
+
* but the block tree is only recomputed when the text actually grows — the
|
|
353
|
+
* settled messages above never re-parse at all.
|
|
288
354
|
*/
|
|
289
|
-
declare
|
|
355
|
+
declare const Markdown: react.NamedExoticComponent<{
|
|
290
356
|
text: string;
|
|
291
357
|
className?: string;
|
|
292
|
-
}
|
|
358
|
+
}>;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Clickable answer options the agent offered via a `frankie:choices` block.
|
|
362
|
+
* Picking one sends it as the next message. A trailing "Something else" row
|
|
363
|
+
* doesn't send — it moves the caret to the composer so the user can type their
|
|
364
|
+
* own answer, while the offered options stay put in case they change their mind.
|
|
365
|
+
*/
|
|
366
|
+
declare function Choices({ options, onPick, onSomethingElse, disabled, }: {
|
|
367
|
+
options: string[];
|
|
368
|
+
onPick: (choice: string) => void;
|
|
369
|
+
onSomethingElse?: () => void;
|
|
370
|
+
disabled?: boolean;
|
|
371
|
+
}): react.JSX.Element | null;
|
|
293
372
|
|
|
294
373
|
declare function SuggestedPrompts({ prompts, onPick, disabled, }: {
|
|
295
374
|
prompts: string[];
|
|
@@ -351,6 +430,31 @@ declare function Separator({ className }: {
|
|
|
351
430
|
/** `forwardRef` rather than React 19's ref-as-prop, so React 18 hosts work too. */
|
|
352
431
|
declare const Textarea: react.ForwardRefExoticComponent<TextareaHTMLAttributes<HTMLTextAreaElement> & react.RefAttributes<HTMLTextAreaElement>>;
|
|
353
432
|
|
|
433
|
+
/**
|
|
434
|
+
* Interactive choice blocks.
|
|
435
|
+
*
|
|
436
|
+
* The agent can end a reply with a fenced block to offer the user a small set
|
|
437
|
+
* of discrete answers:
|
|
438
|
+
*
|
|
439
|
+
* ```frankie:choices
|
|
440
|
+
* British
|
|
441
|
+
* Indian
|
|
442
|
+
* Australian
|
|
443
|
+
* ```
|
|
444
|
+
*
|
|
445
|
+
* The SDK lifts that block out of the prose and renders it as clickable options
|
|
446
|
+
* (the composer stays open, so the user can always type their own answer). The
|
|
447
|
+
* block is a plain fenced code block, so a host that doesn't understand it just
|
|
448
|
+
* shows a harmless code listing instead — never broken markup.
|
|
449
|
+
*/
|
|
450
|
+
interface ExtractedChoices {
|
|
451
|
+
/** The reply text with the choices block removed. */
|
|
452
|
+
body: string;
|
|
453
|
+
/** The offered options, or null when there is no complete block. */
|
|
454
|
+
options: string[] | null;
|
|
455
|
+
}
|
|
456
|
+
declare function extractChoices(text: string): ExtractedChoices;
|
|
457
|
+
|
|
354
458
|
/**
|
|
355
459
|
* Map a theme to the CSS custom properties the widget's styles read.
|
|
356
460
|
*
|
|
@@ -365,4 +469,4 @@ declare function resolveMode(mode: WidgetTheme["mode"]): "light" | "dark";
|
|
|
365
469
|
/** shadcn/ui's class helper: conditional classes with Tailwind conflict resolution. */
|
|
366
470
|
declare function cn(...inputs: ClassValue[]): string;
|
|
367
471
|
|
|
368
|
-
export { type AssistantChatMessage, Avatar, Badge, Button, type ButtonProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type ChatMessage, ChatPanel, type ChatPanelProps, Composer, FRANKIE_CLOUD_URL, FrankieApiError, FrankieChatPage, type FrankieChatPageProps, FrankieClient, type FrankieClientOptions, FrankieInline, type FrankieInlineProps, FrankieProvider, type FrankieProviderProps, FrankieWidget, type FrankieWidgetProps, Markdown, type MessageAttachment, MessageBubble, type PageContext, ScrollArea, type SendOptions, Separator, SuggestedPrompts, Textarea, type ToolActivity, ToolActivityList, ToolActivityRow, TypingDots, type UseFrankieChatResult, type UserChatMessage, applyEvent, buttonVariants, cn, newAssistantMessage, newUserMessage, readEventStream, resolveMode, themeToCssVars, useFrankie, useFrankieChat, useFrankieTheme };
|
|
472
|
+
export { type AssistantChatMessage, Avatar, Badge, Button, type ButtonProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type ChatMessage, ChatPanel, type ChatPanelProps, Choices, Composer, type ExtractedChoices, FRANKIE_CLOUD_URL, FrankieApiError, FrankieChatPage, type FrankieChatPageProps, FrankieClient, type FrankieClientOptions, FrankieInline, type FrankieInlineProps, FrankieProvider, type FrankieProviderProps, FrankieWidget, type FrankieWidgetProps, Markdown, type MessageAttachment, MessageBubble, type PageContext, ScrollArea, type SendOptions, Separator, SuggestedPrompts, Textarea, Thinking, type ToolActivity, ToolActivityList, ToolActivityRow, TypingDots, type UseFrankieChatResult, type UserChatMessage, applyEvent, buttonVariants, cn, extractChoices, newAssistantMessage, newUserMessage, readEventStream, resolveMode, themeToCssVars, useFrankie, useFrankieChat, useFrankieTheme };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { ReactNode, CSSProperties, ButtonHTMLAttributes, HTMLAttributes, TextareaHTMLAttributes } from 'react';
|
|
2
|
+
import { ReactNode, CSSProperties, MutableRefObject, ButtonHTMLAttributes, HTMLAttributes, TextareaHTMLAttributes } from 'react';
|
|
3
3
|
import { PublicWidgetConfig, CreateSessionResponse, ChatAttachment, FrankieServerEvent, WidgetTheme, LoadedSkill, ApiToolInventory } from '@frankieai/core';
|
|
4
4
|
export { FrankieServerEvent, PublicWidgetConfig, WidgetTheme } from '@frankieai/core';
|
|
5
5
|
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
@@ -7,6 +7,26 @@ import { VariantProps } from 'class-variance-authority';
|
|
|
7
7
|
import { ClassValue } from 'clsx';
|
|
8
8
|
|
|
9
9
|
type PageContext = Record<string, string | number | boolean | null>;
|
|
10
|
+
/**
|
|
11
|
+
* How the widget reaches the host's own API for client-side tool execution.
|
|
12
|
+
*
|
|
13
|
+
* When a workspace marks an API source `execution: "client"`, Frankie asks the
|
|
14
|
+
* browser to make the call itself — from the user's existing session — instead
|
|
15
|
+
* of calling the API from Frankie's servers. This says where that API lives and
|
|
16
|
+
* how the browser authenticates to it: normally the app's own cookies
|
|
17
|
+
* (`credentials: "include"` on a same-origin or CORS-allowed API), or a bearer
|
|
18
|
+
* token the app already holds, added via `headers`. Frankie sends no credential
|
|
19
|
+
* of its own; the host API authorizes the call exactly as it would any request
|
|
20
|
+
* from the host's frontend.
|
|
21
|
+
*/
|
|
22
|
+
interface HostApiClientOptions {
|
|
23
|
+
/** Base URL that operation paths resolve against, e.g. "/api" or "https://api.acme.com". */
|
|
24
|
+
baseUrl: string;
|
|
25
|
+
/** fetch credentials mode. "include" sends the app's cookies. Defaults to "same-origin". */
|
|
26
|
+
credentials?: RequestCredentials;
|
|
27
|
+
/** Extra headers for every host call (e.g. an app access token). Awaited per call. */
|
|
28
|
+
headers?: () => Promise<Record<string, string>> | Record<string, string>;
|
|
29
|
+
}
|
|
10
30
|
/**
|
|
11
31
|
* The hosted Frankie data plane. Embedding needs only a publishable key, so
|
|
12
32
|
* `baseUrl` defaults here — override it only when self-hosting the server.
|
|
@@ -24,6 +44,13 @@ interface FrankieClientOptions {
|
|
|
24
44
|
*/
|
|
25
45
|
getUserToken?: () => Promise<string | undefined> | string | undefined;
|
|
26
46
|
fetchImpl?: typeof fetch;
|
|
47
|
+
/**
|
|
48
|
+
* Reaches the host's own API for `execution: "client"` tool calls (see
|
|
49
|
+
* `HostApiClientOptions`). Required only for workspaces that use client-side
|
|
50
|
+
* execution; omit it and a client-mode tool reports that no host API is
|
|
51
|
+
* configured rather than calling anything.
|
|
52
|
+
*/
|
|
53
|
+
api?: HostApiClientOptions;
|
|
27
54
|
/**
|
|
28
55
|
* Remember the session (and, in the hook, the transcript) in localStorage so a
|
|
29
56
|
* page reload resumes the same anonymous conversation. Authenticated chat
|
|
@@ -51,7 +78,8 @@ declare class FrankieClient {
|
|
|
51
78
|
private readonly baseUrl;
|
|
52
79
|
private readonly doFetch;
|
|
53
80
|
private session?;
|
|
54
|
-
|
|
81
|
+
/** Identity (JWT `sub`, else the raw token) the cached session was issued for. */
|
|
82
|
+
private sessionUserKey?;
|
|
55
83
|
constructor(options: FrankieClientOptions);
|
|
56
84
|
get sessionId(): string | undefined;
|
|
57
85
|
get conversationId(): string | undefined;
|
|
@@ -79,6 +107,14 @@ declare class FrankieClient {
|
|
|
79
107
|
attachments?: ChatAttachment[];
|
|
80
108
|
signal?: AbortSignal;
|
|
81
109
|
}, onEvent: (event: FrankieServerEvent) => void): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Execute one host API call in the browser and report the outcome back to the
|
|
112
|
+
* server. Never throws: a transport failure or a missing `api` client becomes
|
|
113
|
+
* a result the model can see and explain, exactly like a non-2xx status.
|
|
114
|
+
*/
|
|
115
|
+
private runClientTool;
|
|
116
|
+
private callHostApi;
|
|
117
|
+
private postToolResult;
|
|
82
118
|
private unwrap;
|
|
83
119
|
}
|
|
84
120
|
declare function readEventStream(body: ReadableStream<Uint8Array>): AsyncGenerator<FrankieServerEvent>;
|
|
@@ -88,6 +124,15 @@ interface FrankieProviderProps extends FrankieClientOptions {
|
|
|
88
124
|
context?: PageContext;
|
|
89
125
|
theme?: WidgetTheme;
|
|
90
126
|
config?: PublicWidgetConfig;
|
|
127
|
+
/**
|
|
128
|
+
* Set to `false` if the host compiles Frankie's classes itself instead of
|
|
129
|
+
* loading `@frankieai/react/styles.css` — e.g. a Tailwind v4 host that
|
|
130
|
+
* `@source`s this package — so it isn't given a second, independently-themed
|
|
131
|
+
* copy of the stylesheet when its own build never sets the styles-ready
|
|
132
|
+
* sentinel. Defaults to `true`: a host that simply forgot the import, or
|
|
133
|
+
* whose CSS chunk hasn't landed yet, still gets a correctly-styled widget.
|
|
134
|
+
*/
|
|
135
|
+
injectStyles?: boolean;
|
|
91
136
|
}
|
|
92
137
|
interface FrankieContextValue {
|
|
93
138
|
client: FrankieClient;
|
|
@@ -96,8 +141,9 @@ interface FrankieContextValue {
|
|
|
96
141
|
loading: boolean;
|
|
97
142
|
context?: PageContext;
|
|
98
143
|
theme?: WidgetTheme;
|
|
144
|
+
injectStyles: boolean;
|
|
99
145
|
}
|
|
100
|
-
declare function FrankieProvider({ children, context, theme, config: providedConfig, ...clientOptions }: FrankieProviderProps): react.JSX.Element;
|
|
146
|
+
declare function FrankieProvider({ children, context, theme, config: providedConfig, injectStyles, ...clientOptions }: FrankieProviderProps): react.JSX.Element;
|
|
101
147
|
declare function useFrankie(): FrankieContextValue;
|
|
102
148
|
declare function useFrankieTheme(): WidgetTheme | undefined;
|
|
103
149
|
|
|
@@ -265,8 +311,10 @@ interface ComposerProps {
|
|
|
265
311
|
attachments?: ComposerAttachmentsConfig;
|
|
266
312
|
/** Enables a model dropdown when provided with at least one model. */
|
|
267
313
|
modelPicker?: ComposerModelPicker;
|
|
314
|
+
/** Populated with a function that focuses the input, so a "type your own" choice can jump here. */
|
|
315
|
+
focusHandleRef?: MutableRefObject<(() => void) | null>;
|
|
268
316
|
}
|
|
269
|
-
declare function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, attachments, modelPicker }: ComposerProps): react.JSX.Element;
|
|
317
|
+
declare function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, attachments, modelPicker, focusHandleRef }: ComposerProps): react.JSX.Element;
|
|
270
318
|
|
|
271
319
|
interface MessageBubbleProps {
|
|
272
320
|
message: ChatMessage;
|
|
@@ -274,22 +322,53 @@ interface MessageBubbleProps {
|
|
|
274
322
|
avatarUrl?: string;
|
|
275
323
|
showToolActivity: boolean;
|
|
276
324
|
renderTool?: (tool: ToolActivity) => ReactNode;
|
|
325
|
+
/** Send a picked choice as the next message. Omitted when the host renders its own. */
|
|
326
|
+
onChoose?: (choice: string) => void;
|
|
327
|
+
/** Focus the composer so the user can type their own answer instead of picking. */
|
|
328
|
+
onRequestInput?: () => void;
|
|
329
|
+
/** True only for the newest message, so stale choices from earlier turns aren't clickable. */
|
|
330
|
+
active?: boolean;
|
|
331
|
+
/** A turn is in flight, so choices should be disabled. */
|
|
332
|
+
busy?: boolean;
|
|
277
333
|
}
|
|
278
|
-
declare
|
|
334
|
+
declare const MessageBubble: react.NamedExoticComponent<MessageBubbleProps>;
|
|
279
335
|
declare function TypingDots(): react.JSX.Element;
|
|
336
|
+
/**
|
|
337
|
+
* A shimmering skeleton shown while the reply is still being produced — before
|
|
338
|
+
* the first words, or during the pause after a tool result while the model
|
|
339
|
+
* composes its answer — so those gaps read as "loading" rather than frozen.
|
|
340
|
+
*/
|
|
341
|
+
declare function Thinking(): react.JSX.Element;
|
|
280
342
|
|
|
281
343
|
/**
|
|
282
|
-
* A deliberately small Markdown subset — paragraphs, lists, fenced
|
|
283
|
-
* italic, inline code and links.
|
|
344
|
+
* A deliberately small Markdown subset — headings, paragraphs, lists, fenced
|
|
345
|
+
* code, bold, italic, inline code and links.
|
|
284
346
|
*
|
|
285
347
|
* Everything is rendered as React elements; nothing goes through
|
|
286
348
|
* `dangerouslySetInnerHTML`. Model output lands in someone else's production
|
|
287
349
|
* page, so it is never trusted as markup.
|
|
350
|
+
*
|
|
351
|
+
* Memoized on `text`: while a reply streams, the parent re-renders every frame,
|
|
352
|
+
* but the block tree is only recomputed when the text actually grows — the
|
|
353
|
+
* settled messages above never re-parse at all.
|
|
288
354
|
*/
|
|
289
|
-
declare
|
|
355
|
+
declare const Markdown: react.NamedExoticComponent<{
|
|
290
356
|
text: string;
|
|
291
357
|
className?: string;
|
|
292
|
-
}
|
|
358
|
+
}>;
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Clickable answer options the agent offered via a `frankie:choices` block.
|
|
362
|
+
* Picking one sends it as the next message. A trailing "Something else" row
|
|
363
|
+
* doesn't send — it moves the caret to the composer so the user can type their
|
|
364
|
+
* own answer, while the offered options stay put in case they change their mind.
|
|
365
|
+
*/
|
|
366
|
+
declare function Choices({ options, onPick, onSomethingElse, disabled, }: {
|
|
367
|
+
options: string[];
|
|
368
|
+
onPick: (choice: string) => void;
|
|
369
|
+
onSomethingElse?: () => void;
|
|
370
|
+
disabled?: boolean;
|
|
371
|
+
}): react.JSX.Element | null;
|
|
293
372
|
|
|
294
373
|
declare function SuggestedPrompts({ prompts, onPick, disabled, }: {
|
|
295
374
|
prompts: string[];
|
|
@@ -351,6 +430,31 @@ declare function Separator({ className }: {
|
|
|
351
430
|
/** `forwardRef` rather than React 19's ref-as-prop, so React 18 hosts work too. */
|
|
352
431
|
declare const Textarea: react.ForwardRefExoticComponent<TextareaHTMLAttributes<HTMLTextAreaElement> & react.RefAttributes<HTMLTextAreaElement>>;
|
|
353
432
|
|
|
433
|
+
/**
|
|
434
|
+
* Interactive choice blocks.
|
|
435
|
+
*
|
|
436
|
+
* The agent can end a reply with a fenced block to offer the user a small set
|
|
437
|
+
* of discrete answers:
|
|
438
|
+
*
|
|
439
|
+
* ```frankie:choices
|
|
440
|
+
* British
|
|
441
|
+
* Indian
|
|
442
|
+
* Australian
|
|
443
|
+
* ```
|
|
444
|
+
*
|
|
445
|
+
* The SDK lifts that block out of the prose and renders it as clickable options
|
|
446
|
+
* (the composer stays open, so the user can always type their own answer). The
|
|
447
|
+
* block is a plain fenced code block, so a host that doesn't understand it just
|
|
448
|
+
* shows a harmless code listing instead — never broken markup.
|
|
449
|
+
*/
|
|
450
|
+
interface ExtractedChoices {
|
|
451
|
+
/** The reply text with the choices block removed. */
|
|
452
|
+
body: string;
|
|
453
|
+
/** The offered options, or null when there is no complete block. */
|
|
454
|
+
options: string[] | null;
|
|
455
|
+
}
|
|
456
|
+
declare function extractChoices(text: string): ExtractedChoices;
|
|
457
|
+
|
|
354
458
|
/**
|
|
355
459
|
* Map a theme to the CSS custom properties the widget's styles read.
|
|
356
460
|
*
|
|
@@ -365,4 +469,4 @@ declare function resolveMode(mode: WidgetTheme["mode"]): "light" | "dark";
|
|
|
365
469
|
/** shadcn/ui's class helper: conditional classes with Tailwind conflict resolution. */
|
|
366
470
|
declare function cn(...inputs: ClassValue[]): string;
|
|
367
471
|
|
|
368
|
-
export { type AssistantChatMessage, Avatar, Badge, Button, type ButtonProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type ChatMessage, ChatPanel, type ChatPanelProps, Composer, FRANKIE_CLOUD_URL, FrankieApiError, FrankieChatPage, type FrankieChatPageProps, FrankieClient, type FrankieClientOptions, FrankieInline, type FrankieInlineProps, FrankieProvider, type FrankieProviderProps, FrankieWidget, type FrankieWidgetProps, Markdown, type MessageAttachment, MessageBubble, type PageContext, ScrollArea, type SendOptions, Separator, SuggestedPrompts, Textarea, type ToolActivity, ToolActivityList, ToolActivityRow, TypingDots, type UseFrankieChatResult, type UserChatMessage, applyEvent, buttonVariants, cn, newAssistantMessage, newUserMessage, readEventStream, resolveMode, themeToCssVars, useFrankie, useFrankieChat, useFrankieTheme };
|
|
472
|
+
export { type AssistantChatMessage, Avatar, Badge, Button, type ButtonProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type ChatMessage, ChatPanel, type ChatPanelProps, Choices, Composer, type ExtractedChoices, FRANKIE_CLOUD_URL, FrankieApiError, FrankieChatPage, type FrankieChatPageProps, FrankieClient, type FrankieClientOptions, FrankieInline, type FrankieInlineProps, FrankieProvider, type FrankieProviderProps, FrankieWidget, type FrankieWidgetProps, Markdown, type MessageAttachment, MessageBubble, type PageContext, ScrollArea, type SendOptions, Separator, SuggestedPrompts, Textarea, Thinking, type ToolActivity, ToolActivityList, ToolActivityRow, TypingDots, type UseFrankieChatResult, type UserChatMessage, applyEvent, buttonVariants, cn, extractChoices, newAssistantMessage, newUserMessage, readEventStream, resolveMode, themeToCssVars, useFrankie, useFrankieChat, useFrankieTheme };
|