@djangocfg/ui-tools 2.1.384 → 2.1.387
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 +25 -11
- package/dist/ChatRoot-4KM2JMGA.mjs +6 -0
- package/dist/{ChatRoot-JVR3M3H2.mjs.map → ChatRoot-4KM2JMGA.mjs.map} +1 -1
- package/dist/ChatRoot-OILWMMZ6.cjs +15 -0
- package/dist/{ChatRoot-LXIUBOXF.cjs.map → ChatRoot-OILWMMZ6.cjs.map} +1 -1
- package/dist/DictationField-AS2F33WI.cjs +13 -0
- package/dist/{DictationField-U25MEYAL.mjs.map → DictationField-AS2F33WI.cjs.map} +1 -1
- package/dist/DictationField-WPONUCYE.mjs +4 -0
- package/dist/{DictationField-XWR5VOID.cjs.map → DictationField-WPONUCYE.mjs.map} +1 -1
- package/dist/MapContainer-AKIPABJK.mjs +4 -0
- package/dist/MapContainer-AKIPABJK.mjs.map +1 -0
- package/dist/MapContainer-STVDMC36.cjs +17 -0
- package/dist/MapContainer-STVDMC36.cjs.map +1 -0
- package/dist/{MapContainer-76YL2JXL.cjs → chunk-5D2OCOPQ.cjs} +3 -2
- package/dist/chunk-5D2OCOPQ.cjs.map +1 -0
- package/dist/{MapContainer-7HXBI3OH.mjs → chunk-7CWGZPO3.mjs} +3 -3
- package/dist/chunk-7CWGZPO3.mjs.map +1 -0
- package/dist/{chunk-4PFW7MIJ.cjs → chunk-ADEN3UA4.cjs} +60 -5
- package/dist/chunk-ADEN3UA4.cjs.map +1 -0
- package/dist/chunk-BVESQTBM.mjs +1439 -0
- package/dist/chunk-BVESQTBM.mjs.map +1 -0
- package/dist/{chunk-PEKBT75W.mjs → chunk-DMX7W4XZ.mjs} +53 -1387
- package/dist/chunk-DMX7W4XZ.mjs.map +1 -0
- package/dist/chunk-HNIMIIFR.mjs +1361 -0
- package/dist/chunk-HNIMIIFR.mjs.map +1 -0
- package/dist/chunk-L25HA3TM.cjs +1478 -0
- package/dist/chunk-L25HA3TM.cjs.map +1 -0
- package/dist/{chunk-HPK3EWBF.cjs → chunk-TBSHZO5R.cjs} +50 -1409
- package/dist/chunk-TBSHZO5R.cjs.map +1 -0
- package/dist/chunk-TSNRU3UO.cjs +1387 -0
- package/dist/chunk-TSNRU3UO.cjs.map +1 -0
- package/dist/{chunk-C2YN6WEO.mjs → chunk-UNCS5V5F.mjs} +61 -7
- package/dist/chunk-UNCS5V5F.mjs.map +1 -0
- package/dist/index.cjs +1236 -1768
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +780 -780
- package/dist/index.d.ts +780 -780
- package/dist/index.mjs +853 -1423
- package/dist/index.mjs.map +1 -1
- package/dist/launcher-5WYPDPEP.mjs +7 -0
- package/dist/launcher-5WYPDPEP.mjs.map +1 -0
- package/dist/launcher-FCI3LTDY.css +7 -0
- package/dist/launcher-FCI3LTDY.css.map +1 -0
- package/dist/launcher-QAOG2NUI.cjs +60 -0
- package/dist/launcher-QAOG2NUI.cjs.map +1 -0
- package/package.json +23 -18
- package/src/tools/AudioPlayer/lazy.tsx +100 -0
- package/src/tools/Chat/README.md +85 -1
- package/src/tools/Chat/context/ChatProvider.tsx +42 -0
- package/src/tools/Chat/lazy.tsx +213 -1
- package/src/tools/CodeEditor/lazy.tsx +70 -0
- package/src/tools/Map/lazy.tsx +38 -1
- package/src/tools/MarkdownEditor/lazy.tsx +42 -0
- package/src/tools/SpeechRecognition/README.md +48 -0
- package/src/tools/SpeechRecognition/core/index.ts +6 -1
- package/src/tools/SpeechRecognition/core/logger.ts +107 -1
- package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +15 -4
- package/src/tools/SpeechRecognition/index.ts +9 -0
- package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +37 -2
- package/dist/ChatRoot-JVR3M3H2.mjs +0 -5
- package/dist/ChatRoot-LXIUBOXF.cjs +0 -14
- package/dist/DictationField-U25MEYAL.mjs +0 -4
- package/dist/DictationField-XWR5VOID.cjs +0 -13
- package/dist/MapContainer-76YL2JXL.cjs.map +0 -1
- package/dist/MapContainer-7HXBI3OH.mjs.map +0 -1
- package/dist/chunk-4PFW7MIJ.cjs.map +0 -1
- package/dist/chunk-C2YN6WEO.mjs.map +0 -1
- package/dist/chunk-HPK3EWBF.cjs.map +0 -1
- package/dist/chunk-PEKBT75W.mjs.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -2249,815 +2249,813 @@ interface ChatRootProps {
|
|
|
2249
2249
|
}
|
|
2250
2250
|
declare function ChatRoot(props: ChatRootProps): react_jsx_runtime.JSX.Element;
|
|
2251
2251
|
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
*/
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
/**
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
readonly toggleOpen: "mod+/";
|
|
2292
|
-
readonly focusComposer: "mod+l";
|
|
2293
|
-
};
|
|
2294
|
-
declare const CHAT_EVENT_NAME = "djc:chat:send";
|
|
2295
|
-
interface ChatEventDetail {
|
|
2296
|
-
content: string;
|
|
2297
|
-
sessionId?: string;
|
|
2298
|
-
attachments?: unknown[];
|
|
2299
|
-
metadata?: Record<string, unknown>;
|
|
2252
|
+
type ChatFABPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
2253
|
+
type ChatFABVariant = 'simple' | 'animated' | 'glass';
|
|
2254
|
+
type ChatFABSize = 'sm' | 'md' | 'lg' | 'responsive';
|
|
2255
|
+
interface ChatFABProps {
|
|
2256
|
+
/** Click handler — typically toggles a `ChatDock`. */
|
|
2257
|
+
onClick: () => void;
|
|
2258
|
+
/** Accessible label. */
|
|
2259
|
+
ariaLabel?: string;
|
|
2260
|
+
/** Icon inside the FAB. Defaults to a bot glyph. */
|
|
2261
|
+
icon?: ReactNode;
|
|
2262
|
+
/** Visual style. @default 'simple' */
|
|
2263
|
+
variant?: ChatFABVariant;
|
|
2264
|
+
/** Button size. @default 'md' */
|
|
2265
|
+
size?: ChatFABSize;
|
|
2266
|
+
/** Fixed-screen position. @default 'bottom-right' */
|
|
2267
|
+
position?: ChatFABPosition;
|
|
2268
|
+
/** Pixel offset from screen edges. @default 24 */
|
|
2269
|
+
offset?: number;
|
|
2270
|
+
/** z-index for the button. @default 9999 */
|
|
2271
|
+
zIndex?: number;
|
|
2272
|
+
/** Show a small attention dot (unread / new). */
|
|
2273
|
+
pulse?: boolean;
|
|
2274
|
+
/**
|
|
2275
|
+
* Numeric badge — unread count. Numbers > 9 render as "9+".
|
|
2276
|
+
* Overrides `pulse` when both set.
|
|
2277
|
+
*/
|
|
2278
|
+
badge?: number;
|
|
2279
|
+
/** Hover tooltip text. Shows next to the FAB on hover/focus. */
|
|
2280
|
+
tooltip?: string;
|
|
2281
|
+
/**
|
|
2282
|
+
* Render in-place (no fixed positioning) so the FAB sits inline in the
|
|
2283
|
+
* normal document flow. Useful for stories, screenshots, and previews
|
|
2284
|
+
* inside a contained playground panel. @default false
|
|
2285
|
+
*/
|
|
2286
|
+
inline?: boolean;
|
|
2287
|
+
/** Override classes on the button itself. */
|
|
2288
|
+
className?: string;
|
|
2289
|
+
/** Extra style on the button (caller-controlled overrides). */
|
|
2290
|
+
style?: CSSProperties;
|
|
2300
2291
|
}
|
|
2301
|
-
|
|
2302
2292
|
/**
|
|
2303
|
-
*
|
|
2304
|
-
*
|
|
2293
|
+
* Floating action button for opening a chat dock. Pure presentation — owns
|
|
2294
|
+
* no state. Wire it to whatever toggles your dock open.
|
|
2295
|
+
*
|
|
2296
|
+
* For the common "FAB + dock + hotkey" composition, use `<ChatLauncher>` —
|
|
2297
|
+
* this primitive is only useful when you need custom triggering.
|
|
2305
2298
|
*/
|
|
2306
|
-
declare function
|
|
2299
|
+
declare function ChatFAB({ onClick, ariaLabel, icon, variant, size, position, offset, zIndex, pulse, badge, tooltip, inline, className, style, }: ChatFABProps): react_jsx_runtime.JSX.Element;
|
|
2307
2300
|
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2301
|
+
type ChatDockMode = 'popover' | 'side';
|
|
2302
|
+
type ChatDockSide = 'left' | 'right';
|
|
2303
|
+
interface ChatDockProps {
|
|
2304
|
+
/** Controlled open state. */
|
|
2305
|
+
open: boolean;
|
|
2306
|
+
/** Called when the user clicks the close button. */
|
|
2307
|
+
onClose: () => void;
|
|
2308
|
+
/** Dock contents (typically a `<Chat>` component). */
|
|
2309
|
+
children: ReactNode;
|
|
2310
|
+
/**
|
|
2311
|
+
* Visual mode.
|
|
2312
|
+
* - `popover` (default): floating card anchored to a corner, fixed size, FAB-style.
|
|
2313
|
+
* - `side`: docked panel pinned to the left/right edge, full viewport height.
|
|
2314
|
+
*/
|
|
2315
|
+
mode?: ChatDockMode;
|
|
2316
|
+
/** Side for `mode='side'`. @default 'right' */
|
|
2317
|
+
side?: ChatDockSide;
|
|
2318
|
+
/** Header title text. */
|
|
2319
|
+
title?: ReactNode;
|
|
2320
|
+
/** Header icon. Defaults to a bot glyph. */
|
|
2321
|
+
icon?: ReactNode;
|
|
2322
|
+
/**
|
|
2323
|
+
* Header actions slot (right side, before the close button).
|
|
2324
|
+
* Use `ChatHeaderActionButton` to keep visual consistency.
|
|
2325
|
+
*/
|
|
2326
|
+
headerActions?: ReactNode;
|
|
2327
|
+
/** Hide the header entirely (you render your own inside `children`). */
|
|
2328
|
+
hideHeader?: boolean;
|
|
2329
|
+
/** ARIA label for the close button. @default 'Close' */
|
|
2330
|
+
closeLabel?: string;
|
|
2331
|
+
/** Dock width in px. Clamped to viewport. @default 480 (popover) / 420 (side) */
|
|
2332
|
+
width?: number;
|
|
2333
|
+
/** Dock height in px. Only used in `popover` mode. @default 720 */
|
|
2334
|
+
height?: number;
|
|
2335
|
+
/** Which screen corner to dock to in `popover` mode. @default 'bottom-right' */
|
|
2336
|
+
position?: ChatFABPosition;
|
|
2337
|
+
/** Offset from screen edges in px (popover only). @default 24 / 96 */
|
|
2338
|
+
offset?: {
|
|
2339
|
+
horizontal?: number;
|
|
2340
|
+
vertical?: number;
|
|
2341
|
+
};
|
|
2342
|
+
/** Transition duration in ms — should match CSS animation. @default 200 */
|
|
2343
|
+
exitDurationMs?: number;
|
|
2344
|
+
/** z-index. @default 10000 */
|
|
2345
|
+
zIndex?: number;
|
|
2346
|
+
/** Accessible dialog label. */
|
|
2347
|
+
ariaLabel?: string;
|
|
2348
|
+
/** Extra classes on the dock container. */
|
|
2349
|
+
className?: string;
|
|
2350
|
+
/**
|
|
2351
|
+
* Take over the full viewport on mobile (< 768px). Applies to both modes.
|
|
2352
|
+
* @default true
|
|
2353
|
+
*/
|
|
2354
|
+
mobileFullscreen?: boolean;
|
|
2355
|
+
/**
|
|
2356
|
+
* Render in-place (not in `document.body` via a portal). Useful for stories,
|
|
2357
|
+
* screenshots, or wrapping the dock inside a custom container. @default false
|
|
2358
|
+
*/
|
|
2359
|
+
disablePortal?: boolean;
|
|
2360
|
+
/**
|
|
2361
|
+
* Drop fixed positioning entirely — the dock renders as a normal flow
|
|
2362
|
+
* element sized by `width`/`height`. Combine with `disablePortal` for
|
|
2363
|
+
* stories/previews where the dock should sit inside the panel instead
|
|
2364
|
+
* of attaching to the viewport. @default false
|
|
2365
|
+
*/
|
|
2366
|
+
inline?: boolean;
|
|
2367
|
+
/**
|
|
2368
|
+
* In `mode='side'`, reserve space on the document body so page content
|
|
2369
|
+
* isn't covered by the dock. Sets `padding-{side}` on `<body>` while
|
|
2370
|
+
* the dock is open and exposes the width via the `--chat-dock-reserve`
|
|
2371
|
+
* CSS variable for custom layouts. @default true (when mode='side')
|
|
2372
|
+
*/
|
|
2373
|
+
reserveBodySpace?: boolean;
|
|
2320
2374
|
}
|
|
2321
|
-
declare function createTokenBuffer(onFlush: (delta: string) => void, windowMs?: 16): TokenBuffer;
|
|
2322
|
-
|
|
2323
|
-
declare function resolvePersona(message: Pick<ChatMessage, 'role' | 'sender'>, user?: ChatUserContext, assistant?: ChatAssistantContext): ChatPersona;
|
|
2324
|
-
/** Compute initials for an avatar fallback. */
|
|
2325
|
-
declare function deriveInitials(persona: ChatPersona, role?: string): string;
|
|
2326
|
-
|
|
2327
2375
|
/**
|
|
2328
|
-
*
|
|
2376
|
+
* Fixed-position chat surface. Two modes:
|
|
2329
2377
|
*
|
|
2330
|
-
*
|
|
2331
|
-
*
|
|
2332
|
-
*
|
|
2333
|
-
*
|
|
2334
|
-
*
|
|
2335
|
-
* DELETE /sessions/:id → 204
|
|
2378
|
+
* - `popover` — floating card anchored to a corner. Companion to `<ChatFAB>`.
|
|
2379
|
+
* - `side` — full-height panel pinned to the left/right edge. App-shell style.
|
|
2380
|
+
*
|
|
2381
|
+
* Renders only when `open` is true (plus the leave-transition tail). Uses
|
|
2382
|
+
* `useChatPresence` for the four-phase mount/animate/unmount cycle.
|
|
2336
2383
|
*/
|
|
2384
|
+
declare function ChatDock({ open, onClose, children, mode, side, title, icon, headerActions, hideHeader, closeLabel, width, height, position, offset, exitDurationMs, zIndex, ariaLabel, className, mobileFullscreen, disablePortal, inline, reserveBodySpace, }: ChatDockProps): react_jsx_runtime.JSX.Element;
|
|
2337
2385
|
|
|
2338
|
-
interface
|
|
2339
|
-
/**
|
|
2340
|
-
|
|
2341
|
-
/**
|
|
2342
|
-
|
|
2343
|
-
/**
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2386
|
+
interface ChatHeaderProps {
|
|
2387
|
+
/** Window title text. */
|
|
2388
|
+
title?: ReactNode;
|
|
2389
|
+
/** Icon next to the title. Defaults to a bot glyph. */
|
|
2390
|
+
icon?: ReactNode;
|
|
2391
|
+
/**
|
|
2392
|
+
* Action slot — appears to the right of the title, before the close button.
|
|
2393
|
+
* Use for reset / settings / minimize / etc. Compose with `ChatHeaderActionButton`.
|
|
2394
|
+
*/
|
|
2395
|
+
actions?: ReactNode;
|
|
2396
|
+
/** Show the close (×) button. @default true */
|
|
2397
|
+
showClose?: boolean;
|
|
2398
|
+
/** Close click handler. */
|
|
2399
|
+
onClose?: () => void;
|
|
2400
|
+
/** ARIA label for the close button. @default 'Close' */
|
|
2401
|
+
closeLabel?: string;
|
|
2402
|
+
/** Replace the close button entirely (rare — most hosts want the default). */
|
|
2403
|
+
closeSlot?: ReactNode;
|
|
2404
|
+
/** Extra classes on the `<header>` element. */
|
|
2405
|
+
className?: string;
|
|
2349
2406
|
}
|
|
2350
|
-
declare function createHttpTransport(config: HttpTransportConfig): ChatTransport;
|
|
2351
|
-
|
|
2352
2407
|
/**
|
|
2353
|
-
*
|
|
2408
|
+
* Standalone chat header — title + icon + action slot + close button.
|
|
2409
|
+
*
|
|
2410
|
+
* Used by `<ChatDock>` automatically, but can be rendered standalone when
|
|
2411
|
+
* the host owns the chat container (e.g. embedded inline in a page).
|
|
2354
2412
|
*/
|
|
2413
|
+
declare function ChatHeader({ title, icon, actions, showClose, onClose, closeLabel, closeSlot, className, }: ChatHeaderProps): react_jsx_runtime.JSX.Element;
|
|
2355
2414
|
|
|
2356
|
-
interface
|
|
2357
|
-
/**
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2415
|
+
interface ChatHeaderActionButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
|
|
2416
|
+
/** Icon (required). */
|
|
2417
|
+
icon: ReactNode;
|
|
2418
|
+
/** Accessible label + native tooltip. */
|
|
2419
|
+
ariaLabel: string;
|
|
2420
|
+
/** Optional unread / status badge — small number on top-right. */
|
|
2421
|
+
badge?: number;
|
|
2422
|
+
/** Mark as destructive — uses destructive hover tokens. */
|
|
2423
|
+
destructive?: boolean;
|
|
2424
|
+
/** Optional visual loading state (e.g. while reset is in flight). */
|
|
2425
|
+
loading?: boolean;
|
|
2365
2426
|
}
|
|
2366
|
-
declare function createMockTransport(opts?: MockTransportOptions): ChatTransport;
|
|
2367
|
-
|
|
2368
2427
|
/**
|
|
2369
|
-
*
|
|
2428
|
+
* Compact icon button for the chat header actions slot.
|
|
2370
2429
|
*
|
|
2371
|
-
*
|
|
2372
|
-
*
|
|
2373
|
-
*
|
|
2430
|
+
* Standard chrome: 28×28 ghost button, hover bg-accent, focus ring, optional
|
|
2431
|
+
* destructive variant, optional numeric badge for unread / pending.
|
|
2432
|
+
*
|
|
2433
|
+
* @example
|
|
2434
|
+
* ```tsx
|
|
2435
|
+
* <ChatHeader
|
|
2436
|
+
* title="Assistant"
|
|
2437
|
+
* onClose={close}
|
|
2438
|
+
* actions={
|
|
2439
|
+
* <>
|
|
2440
|
+
* <ChatHeaderActionButton
|
|
2441
|
+
* icon={<RotateCcw className="h-3.5 w-3.5" />}
|
|
2442
|
+
* ariaLabel="Clear context"
|
|
2443
|
+
* onClick={handleReset}
|
|
2444
|
+
* loading={isResetting}
|
|
2445
|
+
* />
|
|
2446
|
+
* <ChatHeaderActionButton
|
|
2447
|
+
* icon={<Settings className="h-3.5 w-3.5" />}
|
|
2448
|
+
* ariaLabel="Settings"
|
|
2449
|
+
* onClick={openSettings}
|
|
2450
|
+
* />
|
|
2451
|
+
* </>
|
|
2452
|
+
* }
|
|
2453
|
+
* />
|
|
2454
|
+
* ```
|
|
2374
2455
|
*/
|
|
2456
|
+
declare const ChatHeaderActionButton: react.ForwardRefExoticComponent<ChatHeaderActionButtonProps & react.RefAttributes<HTMLButtonElement>>;
|
|
2375
2457
|
|
|
2376
|
-
interface
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2458
|
+
interface ChatHeaderModeToggleProps {
|
|
2459
|
+
/** Current dock mode. */
|
|
2460
|
+
mode: ChatDockMode;
|
|
2461
|
+
/** Toggle handler. Wire to `useChatDockPrefs().toggleMode`. */
|
|
2462
|
+
onToggle: () => void;
|
|
2463
|
+
/** Override aria/tooltip label for popover→side. */
|
|
2464
|
+
expandLabel?: string;
|
|
2465
|
+
/** Override aria/tooltip label for side→popover. */
|
|
2466
|
+
collapseLabel?: string;
|
|
2467
|
+
/**
|
|
2468
|
+
* Always render — useful for stories. By default the toggle hides itself on
|
|
2469
|
+
* viewports below `lg` (1024px) since side mode falls back to popover there.
|
|
2470
|
+
*/
|
|
2471
|
+
forceVisible?: boolean;
|
|
2387
2472
|
}
|
|
2388
|
-
declare function parseSSE(response: Response, options?: ParseSSEOptions): AsyncGenerator<ChatStreamEvent, void, void>;
|
|
2389
|
-
|
|
2390
2473
|
/**
|
|
2391
|
-
*
|
|
2392
|
-
*
|
|
2474
|
+
* "Dock to side" / "back to popover" toggle.
|
|
2475
|
+
*
|
|
2476
|
+
* Side mode is desktop-only — on viewports below `lg` (1024px) `<ChatDock>`
|
|
2477
|
+
* silently falls back to popover anyway, so the toggle has nothing to do
|
|
2478
|
+
* and auto-hides. Pass `forceVisible` to override (e.g. in stories).
|
|
2393
2479
|
*/
|
|
2480
|
+
declare function ChatHeaderModeToggle({ mode, onToggle, expandLabel, collapseLabel, forceVisible, }: ChatHeaderModeToggleProps): react_jsx_runtime.JSX.Element;
|
|
2394
2481
|
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2482
|
+
interface ChatHeaderAudioToggleProps {
|
|
2483
|
+
/** Current muted state. */
|
|
2484
|
+
muted: boolean;
|
|
2485
|
+
/** Toggle handler. Wire to `useChatAudio().setMuted` or `toggleMute`. */
|
|
2486
|
+
onToggle: () => void;
|
|
2487
|
+
/** Override tooltip label for muted → unmuted. */
|
|
2488
|
+
unmuteLabel?: string;
|
|
2489
|
+
/** Override tooltip label for unmuted → muted. */
|
|
2490
|
+
muteLabel?: string;
|
|
2398
2491
|
}
|
|
2399
|
-
|
|
2400
2492
|
/**
|
|
2401
|
-
*
|
|
2402
|
-
*
|
|
2403
|
-
* Translates the event shape emitted by pydantic-AI–style Django backends
|
|
2404
|
-
* (text_delta / tool_call / tool_result / done / error / approval_required)
|
|
2405
|
-
* into the canonical `ChatStreamEvent` stream consumed by the Chat reducer.
|
|
2493
|
+
* Mute / unmute notification sounds. Drop into a `<ChatHeader>` actions
|
|
2494
|
+
* slot or into `<ChatDock headerActions>`.
|
|
2406
2495
|
*
|
|
2407
|
-
*
|
|
2408
|
-
*
|
|
2409
|
-
*
|
|
2410
|
-
*
|
|
2411
|
-
*
|
|
2496
|
+
* @example
|
|
2497
|
+
* ```tsx
|
|
2498
|
+
* const audio = useChatAudio({ sounds: {...} });
|
|
2499
|
+
* <ChatHeaderAudioToggle muted={audio.muted} onToggle={() => audio.setMuted(!audio.muted)} />
|
|
2500
|
+
* ```
|
|
2412
2501
|
*/
|
|
2502
|
+
declare function ChatHeaderAudioToggle({ muted, onToggle, unmuteLabel, muteLabel, }: ChatHeaderAudioToggleProps): react_jsx_runtime.JSX.Element;
|
|
2413
2503
|
|
|
2414
|
-
interface
|
|
2415
|
-
type: 'text_delta' | 'tool_call' | 'tool_result' | 'done' | 'error' | 'approval_required';
|
|
2416
|
-
delta?: string;
|
|
2417
|
-
tool?: string;
|
|
2418
|
-
args?: unknown;
|
|
2419
|
-
result?: unknown;
|
|
2504
|
+
interface ChatHeaderResetButtonProps {
|
|
2420
2505
|
/**
|
|
2421
|
-
*
|
|
2422
|
-
*
|
|
2423
|
-
* frontend uses `data` to render rich UI (e.g. vehicle cards).
|
|
2506
|
+
* Backend reset call. Should resolve to `true` on success.
|
|
2507
|
+
* Plugged into `useChatReset` for in-flight state.
|
|
2424
2508
|
*/
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
/**
|
|
2438
|
-
|
|
2509
|
+
onReset: () => Promise<boolean>;
|
|
2510
|
+
/** Called after a successful reset (e.g. clear local messages, refetch). */
|
|
2511
|
+
onSuccess?: () => void;
|
|
2512
|
+
/** Called when reset fails (returned `false` or threw). */
|
|
2513
|
+
onError?: (error?: unknown) => void;
|
|
2514
|
+
/**
|
|
2515
|
+
* Show a `window.dialog.confirm` before calling `onReset`. @default true
|
|
2516
|
+
*
|
|
2517
|
+
* Requires the host to mount `<DialogProvider>` from `@djangocfg/ui-core`
|
|
2518
|
+
* — it installs the `window.dialog` API used here.
|
|
2519
|
+
*/
|
|
2520
|
+
confirm?: boolean;
|
|
2521
|
+
/** Confirm dialog title. */
|
|
2522
|
+
confirmTitle?: string;
|
|
2523
|
+
/** Confirm dialog message. */
|
|
2524
|
+
confirmMessage?: string;
|
|
2525
|
+
/** Override tooltip / aria label. */
|
|
2526
|
+
ariaLabel?: string;
|
|
2439
2527
|
}
|
|
2440
|
-
declare function createToolIdQueue(): ToolIdQueue;
|
|
2441
|
-
/**
|
|
2442
|
-
* Translate a single pydantic-AI event into zero or more `ChatStreamEvent`s.
|
|
2443
|
-
* Pass a `ToolIdQueue` shared across the lifetime of one stream so that
|
|
2444
|
-
* `tool_call` / `tool_result` pairs match correctly.
|
|
2445
|
-
*/
|
|
2446
|
-
declare function mapPydanticAIEvent(ev: PydanticAIEvent, toolIds: ToolIdQueue): Generator<ChatStreamEvent>;
|
|
2447
2528
|
/**
|
|
2448
|
-
*
|
|
2449
|
-
*
|
|
2450
|
-
*
|
|
2529
|
+
* Standard chat-reset action: prompts the user via `window.dialog.confirm`,
|
|
2530
|
+
* then runs the backend reset call through `useChatReset` so the button
|
|
2531
|
+
* spins while it's in flight.
|
|
2451
2532
|
*
|
|
2452
|
-
*
|
|
2533
|
+
* @example
|
|
2534
|
+
* ```tsx
|
|
2535
|
+
* <ChatHeaderResetButton
|
|
2536
|
+
* onReset={api.clearChat}
|
|
2537
|
+
* onSuccess={() => chat.clearMessages()}
|
|
2538
|
+
* />
|
|
2539
|
+
* ```
|
|
2453
2540
|
*/
|
|
2454
|
-
declare function
|
|
2541
|
+
declare function ChatHeaderResetButton({ onReset, onSuccess, onError, confirm, confirmTitle, confirmMessage, ariaLabel, }: ChatHeaderResetButtonProps): react_jsx_runtime.JSX.Element;
|
|
2455
2542
|
|
|
2543
|
+
interface ChatHeaderLanguageButtonProps {
|
|
2544
|
+
/** Override aria-label. Default "Speech language". */
|
|
2545
|
+
ariaLabel?: string;
|
|
2546
|
+
/**
|
|
2547
|
+
* Subset of BCP-47 tags to offer. Default: every entry from the
|
|
2548
|
+
* Web Speech catalogue (~66 tags incl. regional variants). Pass a
|
|
2549
|
+
* tighter list when your backend STT only supports a subset.
|
|
2550
|
+
*/
|
|
2551
|
+
allowedTags?: string[];
|
|
2552
|
+
/** Hide the globe-fallback icon when no flag resolves. */
|
|
2553
|
+
hideFallbackIcon?: boolean;
|
|
2554
|
+
className?: string;
|
|
2555
|
+
}
|
|
2456
2556
|
/**
|
|
2457
|
-
*
|
|
2458
|
-
*
|
|
2459
|
-
*
|
|
2460
|
-
*
|
|
2461
|
-
* - `createPydanticAISSEMap` for normalizing pydantic-AI events into `ChatStreamEvent`.
|
|
2557
|
+
* Compact flag-button language picker for the chat header. Built on
|
|
2558
|
+
* top of the ui-core `<Combobox>` — searchable autocomplete with
|
|
2559
|
+
* flags, ~66 BCP-47 tags from the official Chrome Web Speech demo, and
|
|
2560
|
+
* a custom 28×28 trigger via `renderTrigger`.
|
|
2462
2561
|
*
|
|
2463
|
-
*
|
|
2464
|
-
*
|
|
2465
|
-
*
|
|
2466
|
-
* bootstrapping are caller responsibilities — pass them in.
|
|
2562
|
+
* The selection persists into `useSpeechPrefs` (zustand+localStorage)
|
|
2563
|
+
* so `useSpeechRecognition` picks it up as the second-priority resolver
|
|
2564
|
+
* value (above i18n locale, below an explicit `language` prop).
|
|
2467
2565
|
*/
|
|
2566
|
+
declare function ChatHeaderLanguageButton({ ariaLabel, allowedTags, hideFallbackIcon, className, }: ChatHeaderLanguageButtonProps): react.ReactElement;
|
|
2468
2567
|
|
|
2469
|
-
interface
|
|
2470
|
-
/**
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
* history.
|
|
2481
|
-
*/
|
|
2482
|
-
bootstrapSession?: (opts?: CreateSessionOptions) => Promise<SessionInfo>;
|
|
2483
|
-
/** Optional session teardown. */
|
|
2484
|
-
closeSession?: (sessionId: string) => Promise<void>;
|
|
2568
|
+
interface ChatGreetingProps {
|
|
2569
|
+
/** Controlled visibility — usually `!chatOpen && !userDismissed`. */
|
|
2570
|
+
open: boolean;
|
|
2571
|
+
/** Greeting text. Pass a string for the default bubble, or any ReactNode. */
|
|
2572
|
+
children: ReactNode;
|
|
2573
|
+
/** Click handler — typically opens the chat. Bubble is clickable when set. */
|
|
2574
|
+
onClick?: () => void;
|
|
2575
|
+
/** Close (×) button handler — typically marks the greeting as dismissed. */
|
|
2576
|
+
onDismiss?: () => void;
|
|
2577
|
+
/** Anchor relative to a FAB on the same side. @default 'bottom-right' */
|
|
2578
|
+
position?: ChatFABPosition;
|
|
2485
2579
|
/**
|
|
2486
|
-
*
|
|
2487
|
-
*
|
|
2488
|
-
* hosts only use streaming.
|
|
2580
|
+
* Horizontal pixel offset matching the FAB's `offset` prop, so the greeting
|
|
2581
|
+
* lines up under the FAB. @default 24
|
|
2489
2582
|
*/
|
|
2490
|
-
|
|
2491
|
-
/** Request headers (Authorization, content-type, etc.). */
|
|
2492
|
-
buildHeaders?: () => HeadersInit | Promise<HeadersInit>;
|
|
2493
|
-
/** Override fetch (tests, retry layers). */
|
|
2494
|
-
fetchImpl?: typeof fetch;
|
|
2583
|
+
fabOffset?: number;
|
|
2495
2584
|
/**
|
|
2496
|
-
*
|
|
2497
|
-
*
|
|
2498
|
-
* your backend embeds the message in the URL via `buildStreamUrl`.
|
|
2585
|
+
* Vertical pixel offset above/below the FAB centerline. @default 96
|
|
2586
|
+
* (room for an `md` FAB plus a small gap).
|
|
2499
2587
|
*/
|
|
2500
|
-
|
|
2501
|
-
/**
|
|
2502
|
-
|
|
2588
|
+
fabClearance?: number;
|
|
2589
|
+
/** Delay before the greeting appears, in ms. @default 1500 */
|
|
2590
|
+
delayMs?: number;
|
|
2591
|
+
/** z-index. @default 9998 (just below the default FAB at 9999). */
|
|
2592
|
+
zIndex?: number;
|
|
2593
|
+
/** Override classes on the bubble. */
|
|
2594
|
+
className?: string;
|
|
2595
|
+
/** Override styles on the bubble. */
|
|
2596
|
+
style?: CSSProperties;
|
|
2597
|
+
/** Optional sender avatar / icon shown on the left. */
|
|
2598
|
+
avatar?: ReactNode;
|
|
2599
|
+
/** Optional sender label rendered above the text. */
|
|
2600
|
+
senderName?: string;
|
|
2601
|
+
/** ARIA label for the dismiss button. @default 'Dismiss' */
|
|
2602
|
+
dismissLabel?: string;
|
|
2503
2603
|
/**
|
|
2504
|
-
*
|
|
2505
|
-
*
|
|
2506
|
-
* normal message stream). Called synchronously while parsing the SSE
|
|
2507
|
-
* frame; mutate caller-owned state, don't `await` long work here.
|
|
2604
|
+
* Render in-place (no fixed positioning). Useful for stories and inline previews.
|
|
2605
|
+
* @default false
|
|
2508
2606
|
*/
|
|
2509
|
-
|
|
2607
|
+
inline?: boolean;
|
|
2510
2608
|
}
|
|
2511
|
-
|
|
2609
|
+
/**
|
|
2610
|
+
* Greeting bubble shown next to a `ChatFAB` to invite the user to start a
|
|
2611
|
+
* conversation (LiveChat / Intercom-style proactive prompt).
|
|
2612
|
+
*
|
|
2613
|
+
* Renders fixed-position, anchored to the same corner as the FAB. Owns its
|
|
2614
|
+
* own delayed-mount + presence animation. Hide on chat open and/or after
|
|
2615
|
+
* user dismissal.
|
|
2616
|
+
*
|
|
2617
|
+
* @example
|
|
2618
|
+
* ```tsx
|
|
2619
|
+
* const [open, setOpen] = useState(false);
|
|
2620
|
+
* const [dismissed, setDismissed] = useState(false);
|
|
2621
|
+
*
|
|
2622
|
+
* <ChatLauncher
|
|
2623
|
+
* open={open}
|
|
2624
|
+
* onOpenChange={setOpen}
|
|
2625
|
+
* fab={{ variant: 'animated' }}
|
|
2626
|
+
* dock={{ title: 'Support' }}
|
|
2627
|
+
* >
|
|
2628
|
+
* <SupportChat />
|
|
2629
|
+
* </ChatLauncher>
|
|
2630
|
+
*
|
|
2631
|
+
* <ChatGreeting
|
|
2632
|
+
* open={!open && !dismissed}
|
|
2633
|
+
* onClick={() => setOpen(true)}
|
|
2634
|
+
* onDismiss={() => setDismissed(true)}
|
|
2635
|
+
* senderName="Anna from Support"
|
|
2636
|
+
* delayMs={2000}
|
|
2637
|
+
* >
|
|
2638
|
+
* Hi! 👋 Got a question? I'm here to help.
|
|
2639
|
+
* </ChatGreeting>
|
|
2640
|
+
* ```
|
|
2641
|
+
*/
|
|
2642
|
+
declare function ChatGreeting({ open, children, onClick, onDismiss, position, fabOffset, fabClearance, delayMs, zIndex, className, style, avatar, senderName, dismissLabel, inline, }: ChatGreetingProps): react_jsx_runtime.JSX.Element;
|
|
2512
2643
|
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
/** Visual style. @default 'simple' */
|
|
2524
|
-
variant?: ChatFABVariant;
|
|
2525
|
-
/** Button size. @default 'md' */
|
|
2526
|
-
size?: ChatFABSize;
|
|
2527
|
-
/** Fixed-screen position. @default 'bottom-right' */
|
|
2644
|
+
interface ChatUnreadPreviewProps {
|
|
2645
|
+
/** Controlled — usually `!dockOpen && !!message`. */
|
|
2646
|
+
open: boolean;
|
|
2647
|
+
/** Inbound message to preview. `null` hides the bubble. */
|
|
2648
|
+
message: ChatMessage | null;
|
|
2649
|
+
/** Tap → open chat + mark read. */
|
|
2650
|
+
onClick?: () => void;
|
|
2651
|
+
/** × → mark read without opening. */
|
|
2652
|
+
onDismiss?: () => void;
|
|
2653
|
+
/** Anchor corner — match the FAB so the bubble sits above it. @default 'bottom-right' */
|
|
2528
2654
|
position?: ChatFABPosition;
|
|
2529
|
-
/**
|
|
2530
|
-
|
|
2531
|
-
/**
|
|
2655
|
+
/** Horizontal offset from screen edge, matches the FAB. @default 24 */
|
|
2656
|
+
fabOffset?: number;
|
|
2657
|
+
/** Vertical clearance above/below the FAB. @default 96 */
|
|
2658
|
+
fabClearance?: number;
|
|
2659
|
+
/** Lines of body text before ellipsis. @default 2 */
|
|
2660
|
+
truncate?: number;
|
|
2661
|
+
/** z-index. @default 9998 */
|
|
2532
2662
|
zIndex?: number;
|
|
2533
|
-
/**
|
|
2534
|
-
pulse?: boolean;
|
|
2535
|
-
/**
|
|
2536
|
-
* Numeric badge — unread count. Numbers > 9 render as "9+".
|
|
2537
|
-
* Overrides `pulse` when both set.
|
|
2538
|
-
*/
|
|
2539
|
-
badge?: number;
|
|
2540
|
-
/** Hover tooltip text. Shows next to the FAB on hover/focus. */
|
|
2541
|
-
tooltip?: string;
|
|
2542
|
-
/**
|
|
2543
|
-
* Render in-place (no fixed positioning) so the FAB sits inline in the
|
|
2544
|
-
* normal document flow. Useful for stories, screenshots, and previews
|
|
2545
|
-
* inside a contained playground panel. @default false
|
|
2546
|
-
*/
|
|
2663
|
+
/** Render in-place (stories / previews). @default false */
|
|
2547
2664
|
inline?: boolean;
|
|
2548
|
-
/** Override classes on the
|
|
2665
|
+
/** Override classes on the bubble. */
|
|
2549
2666
|
className?: string;
|
|
2550
|
-
/**
|
|
2667
|
+
/** Override styles on the bubble. */
|
|
2551
2668
|
style?: CSSProperties;
|
|
2669
|
+
/** ARIA label for the dismiss button. @default 'Mark as read' */
|
|
2670
|
+
dismissLabel?: string;
|
|
2671
|
+
/** Override the avatar — defaults to derived from `message.sender`. */
|
|
2672
|
+
avatar?: ReactNode;
|
|
2673
|
+
/** Override the sender label — defaults to `message.sender?.name`. */
|
|
2674
|
+
senderName?: string;
|
|
2552
2675
|
}
|
|
2553
2676
|
/**
|
|
2554
|
-
*
|
|
2555
|
-
* no state. Wire it to whatever toggles your dock open.
|
|
2677
|
+
* Push-notification bubble next to the chat FAB.
|
|
2556
2678
|
*
|
|
2557
|
-
*
|
|
2558
|
-
*
|
|
2679
|
+
* Shows the last inbound message while the chat is closed —
|
|
2680
|
+
* Intercom / LiveChat-style. Tap → open chat (host wires `onClick`).
|
|
2681
|
+
* Dismiss × → keep chat closed but stop nagging.
|
|
2682
|
+
*
|
|
2683
|
+
* Pair with `useChatUnread()` (inside `<ChatProvider>`) for state.
|
|
2559
2684
|
*/
|
|
2560
|
-
declare function
|
|
2685
|
+
declare function ChatUnreadPreview({ open, message, onClick, onDismiss, position, fabOffset, fabClearance, truncate, zIndex, inline, className, style, dismissLabel, avatar, senderName, }: ChatUnreadPreviewProps): react_jsx_runtime.JSX.Element;
|
|
2561
2686
|
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
/**
|
|
2566
|
-
|
|
2567
|
-
/**
|
|
2568
|
-
|
|
2569
|
-
/**
|
|
2687
|
+
interface ChatLauncherHotkey {
|
|
2688
|
+
/** Key (case-sensitive single char or named like 'Escape'). */
|
|
2689
|
+
key: string;
|
|
2690
|
+
/** Require Cmd (mac) or Ctrl (other). */
|
|
2691
|
+
meta?: boolean;
|
|
2692
|
+
/** Require Shift. */
|
|
2693
|
+
shift?: boolean;
|
|
2694
|
+
/** Require Alt. */
|
|
2695
|
+
alt?: boolean;
|
|
2696
|
+
}
|
|
2697
|
+
interface ChatLauncherGreeting extends Omit<ChatGreetingProps, 'open' | 'onClick' | 'onDismiss' | 'position' | 'fabOffset' | 'children'> {
|
|
2698
|
+
/** Greeting body — string for the default style, or any ReactNode. */
|
|
2699
|
+
content: ReactNode;
|
|
2700
|
+
/** Persistence key for "user dismissed this greeting" in `localStorage`. Pass `null` to disable persistence. @default null */
|
|
2701
|
+
dismissStorageKey?: string | null;
|
|
2702
|
+
/** Hide the greeting once the user opens the chat. @default true */
|
|
2703
|
+
hideOnOpen?: boolean;
|
|
2704
|
+
}
|
|
2705
|
+
interface ChatLauncherProps {
|
|
2706
|
+
/** Dock contents — typically a `<Chat>` instance. */
|
|
2570
2707
|
children: ReactNode;
|
|
2708
|
+
/** FAB customization (icon, position, label, pulse, badge, tooltip, variant, size). */
|
|
2709
|
+
fab?: Omit<ChatFABProps, 'onClick'>;
|
|
2710
|
+
/** Dock customization (size, title, position, transition, mobileFullscreen). */
|
|
2711
|
+
dock?: Omit<ChatDockProps, 'open' | 'onClose' | 'children'>;
|
|
2571
2712
|
/**
|
|
2572
|
-
*
|
|
2573
|
-
*
|
|
2574
|
-
* - `side`: docked panel pinned to the left/right edge, full viewport height.
|
|
2713
|
+
* Proactive greeting bubble shown next to the FAB before the user opens the chat.
|
|
2714
|
+
* Set to a string or full config object. Omit to disable.
|
|
2575
2715
|
*/
|
|
2576
|
-
|
|
2577
|
-
/**
|
|
2578
|
-
|
|
2579
|
-
/**
|
|
2580
|
-
|
|
2581
|
-
/**
|
|
2582
|
-
|
|
2716
|
+
greeting?: string | ChatLauncherGreeting;
|
|
2717
|
+
/** Open/close via a keyboard shortcut. */
|
|
2718
|
+
hotkey?: ChatLauncherHotkey;
|
|
2719
|
+
/** Initial open state for uncontrolled mode. @default false */
|
|
2720
|
+
defaultOpen?: boolean;
|
|
2721
|
+
/** Controlled open state (pair with `onOpenChange`). */
|
|
2722
|
+
open?: boolean;
|
|
2723
|
+
/** Controlled open state setter. */
|
|
2724
|
+
onOpenChange?: (open: boolean) => void;
|
|
2583
2725
|
/**
|
|
2584
|
-
*
|
|
2585
|
-
*
|
|
2726
|
+
* Focus the composer textarea when the dock opens. Saves a click for
|
|
2727
|
+
* every "FAB → start typing" interaction. @default true
|
|
2586
2728
|
*/
|
|
2587
|
-
|
|
2588
|
-
/** Hide the header entirely (you render your own inside `children`). */
|
|
2589
|
-
hideHeader?: boolean;
|
|
2590
|
-
/** ARIA label for the close button. @default 'Close' */
|
|
2591
|
-
closeLabel?: string;
|
|
2592
|
-
/** Dock width in px. Clamped to viewport. @default 480 (popover) / 420 (side) */
|
|
2593
|
-
width?: number;
|
|
2594
|
-
/** Dock height in px. Only used in `popover` mode. @default 720 */
|
|
2595
|
-
height?: number;
|
|
2596
|
-
/** Which screen corner to dock to in `popover` mode. @default 'bottom-right' */
|
|
2597
|
-
position?: ChatFABPosition;
|
|
2598
|
-
/** Offset from screen edges in px (popover only). @default 24 / 96 */
|
|
2599
|
-
offset?: {
|
|
2600
|
-
horizontal?: number;
|
|
2601
|
-
vertical?: number;
|
|
2602
|
-
};
|
|
2603
|
-
/** Transition duration in ms — should match CSS animation. @default 200 */
|
|
2604
|
-
exitDurationMs?: number;
|
|
2605
|
-
/** z-index. @default 10000 */
|
|
2606
|
-
zIndex?: number;
|
|
2607
|
-
/** Accessible dialog label. */
|
|
2608
|
-
ariaLabel?: string;
|
|
2609
|
-
/** Extra classes on the dock container. */
|
|
2610
|
-
className?: string;
|
|
2729
|
+
autoFocusComposerOnOpen?: boolean;
|
|
2611
2730
|
/**
|
|
2612
|
-
*
|
|
2613
|
-
*
|
|
2731
|
+
* Close the dock on `Escape`. Mirrors standard popover / drawer UX.
|
|
2732
|
+
* Set to `false` to disable (e.g. if you want Escape to do something
|
|
2733
|
+
* else inside the chat). @default true
|
|
2614
2734
|
*/
|
|
2615
|
-
|
|
2735
|
+
closeOnEscape?: boolean;
|
|
2616
2736
|
/**
|
|
2617
|
-
*
|
|
2618
|
-
*
|
|
2737
|
+
* Last inbound message (admin reply / system notice / agent push) the
|
|
2738
|
+
* user hasn't seen yet. Drives the `<ChatUnreadPreview>` bubble next
|
|
2739
|
+
* to the FAB and (by default) the FAB badge.
|
|
2740
|
+
*
|
|
2741
|
+
* Source it from `useChatUnread()` inside your `<ChatProvider>`.
|
|
2619
2742
|
*/
|
|
2620
|
-
|
|
2743
|
+
unreadMessage?: ChatMessage | null;
|
|
2621
2744
|
/**
|
|
2622
|
-
*
|
|
2623
|
-
*
|
|
2624
|
-
* stories/previews where the dock should sit inside the panel instead
|
|
2625
|
-
* of attaching to the viewport. @default false
|
|
2745
|
+
* Called when the user opens the chat via FAB/preview/hotkey or
|
|
2746
|
+
* dismisses the preview with ×. Wire to `useChatUnread().markRead`.
|
|
2626
2747
|
*/
|
|
2627
|
-
|
|
2748
|
+
onMarkRead?: () => void;
|
|
2628
2749
|
/**
|
|
2629
|
-
*
|
|
2630
|
-
*
|
|
2631
|
-
*
|
|
2632
|
-
* CSS variable for custom layouts. @default true (when mode='side')
|
|
2750
|
+
* Customize the unread bubble (`truncate`, `dismissLabel`, …).
|
|
2751
|
+
* `open`/`message`/`onClick`/`onDismiss`/`position`/`fabOffset` are
|
|
2752
|
+
* wired automatically.
|
|
2633
2753
|
*/
|
|
2634
|
-
|
|
2635
|
-
}
|
|
2636
|
-
/**
|
|
2637
|
-
* Fixed-position chat surface. Two modes:
|
|
2638
|
-
*
|
|
2639
|
-
* - `popover` — floating card anchored to a corner. Companion to `<ChatFAB>`.
|
|
2640
|
-
* - `side` — full-height panel pinned to the left/right edge. App-shell style.
|
|
2641
|
-
*
|
|
2642
|
-
* Renders only when `open` is true (plus the leave-transition tail). Uses
|
|
2643
|
-
* `useChatPresence` for the four-phase mount/animate/unmount cycle.
|
|
2644
|
-
*/
|
|
2645
|
-
declare function ChatDock({ open, onClose, children, mode, side, title, icon, headerActions, hideHeader, closeLabel, width, height, position, offset, exitDurationMs, zIndex, ariaLabel, className, mobileFullscreen, disablePortal, inline, reserveBodySpace, }: ChatDockProps): react_jsx_runtime.JSX.Element;
|
|
2646
|
-
|
|
2647
|
-
interface ChatHeaderProps {
|
|
2648
|
-
/** Window title text. */
|
|
2649
|
-
title?: ReactNode;
|
|
2650
|
-
/** Icon next to the title. Defaults to a bot glyph. */
|
|
2651
|
-
icon?: ReactNode;
|
|
2754
|
+
unreadPreview?: Omit<ChatUnreadPreviewProps, 'open' | 'message' | 'onClick' | 'onDismiss' | 'position' | 'fabOffset'>;
|
|
2652
2755
|
/**
|
|
2653
|
-
*
|
|
2654
|
-
*
|
|
2756
|
+
* Auto-inject a mute / unmute button into the header. Pass the
|
|
2757
|
+
* `useChatAudio()` (or any compatible `{ muted, toggleMute }`)
|
|
2758
|
+
* instance — the launcher renders `<ChatHeaderAudioToggle>` in the
|
|
2759
|
+
* header's actions slot when audio is actually configured (not silent).
|
|
2760
|
+
*
|
|
2761
|
+
* Hosts that manage their own header can ignore this prop and render
|
|
2762
|
+
* `<ChatHeaderAudioToggle>` directly.
|
|
2655
2763
|
*/
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
/**
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
className?: string;
|
|
2764
|
+
audio?: {
|
|
2765
|
+
muted: boolean;
|
|
2766
|
+
toggleMute: () => void;
|
|
2767
|
+
isSilent?: boolean;
|
|
2768
|
+
} | null;
|
|
2769
|
+
/**
|
|
2770
|
+
* Suppress the auto-injected audio toggle even when `audio` is passed.
|
|
2771
|
+
* @default false
|
|
2772
|
+
*/
|
|
2773
|
+
hideAudioToggle?: boolean;
|
|
2667
2774
|
}
|
|
2668
2775
|
/**
|
|
2669
|
-
*
|
|
2776
|
+
* Floating chat launcher = FAB + Dock + presence + optional greeting + hotkey.
|
|
2670
2777
|
*
|
|
2671
|
-
*
|
|
2672
|
-
*
|
|
2778
|
+
* 99% of hosts use this directly. For non-FAB triggers (e.g. an inline
|
|
2779
|
+
* link in the page) compose `<ChatDock>` with your own button.
|
|
2673
2780
|
*/
|
|
2674
|
-
declare function
|
|
2781
|
+
declare function ChatLauncher({ children, fab, dock, greeting, hotkey, defaultOpen, open: controlledOpen, onOpenChange, autoFocusComposerOnOpen, closeOnEscape, unreadMessage, onMarkRead, unreadPreview, audio, hideAudioToggle, }: ChatLauncherProps): react_jsx_runtime.JSX.Element;
|
|
2675
2782
|
|
|
2676
|
-
|
|
2677
|
-
/** Icon (required). */
|
|
2678
|
-
icon: ReactNode;
|
|
2679
|
-
/** Accessible label + native tooltip. */
|
|
2680
|
-
ariaLabel: string;
|
|
2681
|
-
/** Optional unread / status badge — small number on top-right. */
|
|
2682
|
-
badge?: number;
|
|
2683
|
-
/** Mark as destructive — uses destructive hover tokens. */
|
|
2684
|
-
destructive?: boolean;
|
|
2685
|
-
/** Optional visual loading state (e.g. while reset is in flight). */
|
|
2686
|
-
loading?: boolean;
|
|
2687
|
-
}
|
|
2783
|
+
type ChatPresencePhase = 'hidden' | 'entering' | 'visible' | 'leaving';
|
|
2688
2784
|
/**
|
|
2689
|
-
*
|
|
2785
|
+
* Presence state machine for floating popovers.
|
|
2690
2786
|
*
|
|
2691
|
-
*
|
|
2692
|
-
* destructive variant, optional numeric badge for unread / pending.
|
|
2787
|
+
* Drives a four-phase lifecycle so enter/leave CSS transitions actually fire:
|
|
2693
2788
|
*
|
|
2694
|
-
*
|
|
2695
|
-
*
|
|
2696
|
-
*
|
|
2697
|
-
*
|
|
2698
|
-
*
|
|
2699
|
-
*
|
|
2700
|
-
*
|
|
2701
|
-
*
|
|
2702
|
-
*
|
|
2703
|
-
* ariaLabel="Clear context"
|
|
2704
|
-
* onClick={handleReset}
|
|
2705
|
-
* loading={isResetting}
|
|
2706
|
-
* />
|
|
2707
|
-
* <ChatHeaderActionButton
|
|
2708
|
-
* icon={<Settings className="h-3.5 w-3.5" />}
|
|
2709
|
-
* ariaLabel="Settings"
|
|
2710
|
-
* onClick={openSettings}
|
|
2711
|
-
* />
|
|
2712
|
-
* </>
|
|
2713
|
-
* }
|
|
2714
|
-
* />
|
|
2715
|
-
* ```
|
|
2789
|
+
* hidden → entering (mount, transition class starts) → visible
|
|
2790
|
+
* visible → leaving (transition runs) → hidden (unmount)
|
|
2791
|
+
*
|
|
2792
|
+
* Mounting in `entering` and ticking to `visible` on the next paint is what
|
|
2793
|
+
* lets transition classes animate. Without it the element appears already
|
|
2794
|
+
* at its final state and CSS transitions never observe a change.
|
|
2795
|
+
*
|
|
2796
|
+
* @param open - controlled open state
|
|
2797
|
+
* @param exitDurationMs - how long the leave transition runs; should match CSS
|
|
2716
2798
|
*/
|
|
2717
|
-
declare
|
|
2799
|
+
declare function useChatPresence(open: boolean, exitDurationMs?: number): ChatPresencePhase;
|
|
2718
2800
|
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2801
|
+
/**
|
|
2802
|
+
* Chat defaults and constants.
|
|
2803
|
+
*/
|
|
2804
|
+
declare const STORAGE_KEYS: {
|
|
2805
|
+
readonly mode: "djc-chat-mode";
|
|
2806
|
+
readonly sidebarWidth: "djc-chat-sidebar-width";
|
|
2807
|
+
readonly composerHistory: "djc-chat-composer-history";
|
|
2808
|
+
};
|
|
2809
|
+
declare const CSS_VARS: {
|
|
2810
|
+
readonly reserve: "--djc-chat-reserve";
|
|
2811
|
+
};
|
|
2812
|
+
declare const DEFAULT_Z_INDEX = 9000;
|
|
2813
|
+
declare const LIMITS: {
|
|
2814
|
+
/** Max characters per single message. */
|
|
2815
|
+
readonly messageMaxLength: 8000;
|
|
2816
|
+
/** Max attachments per message. */
|
|
2817
|
+
readonly attachmentsMax: 10;
|
|
2818
|
+
/** Composer history slots. */
|
|
2819
|
+
readonly composerHistorySize: 50;
|
|
2820
|
+
/** Coalesce stream tokens within this window before dispatching. */
|
|
2821
|
+
readonly streamCoalesceMs: 16;
|
|
2822
|
+
/** Default history page size. */
|
|
2823
|
+
readonly pageSize: 50;
|
|
2824
|
+
/** Virtualize list when >= this many messages (host-controlled threshold). */
|
|
2825
|
+
readonly virtualizeThreshold: 50;
|
|
2826
|
+
/** SSE idle timeout. */
|
|
2827
|
+
readonly sseIdleMs: 45000;
|
|
2828
|
+
};
|
|
2829
|
+
declare const DEFAULT_SIDEBAR: {
|
|
2830
|
+
readonly width: 420;
|
|
2831
|
+
readonly min: 320;
|
|
2832
|
+
readonly max: 720;
|
|
2833
|
+
};
|
|
2834
|
+
declare const HOTKEYS: {
|
|
2835
|
+
readonly send: "mod+enter";
|
|
2836
|
+
readonly cancel: "esc";
|
|
2837
|
+
readonly newChat: "mod+shift+n";
|
|
2838
|
+
readonly toggleOpen: "mod+/";
|
|
2839
|
+
readonly focusComposer: "mod+l";
|
|
2840
|
+
};
|
|
2841
|
+
declare const CHAT_EVENT_NAME = "djc:chat:send";
|
|
2842
|
+
interface ChatEventDetail {
|
|
2843
|
+
content: string;
|
|
2844
|
+
sessionId?: string;
|
|
2845
|
+
attachments?: unknown[];
|
|
2846
|
+
metadata?: Record<string, unknown>;
|
|
2733
2847
|
}
|
|
2848
|
+
|
|
2734
2849
|
/**
|
|
2735
|
-
*
|
|
2736
|
-
*
|
|
2737
|
-
* Side mode is desktop-only — on viewports below `lg` (1024px) `<ChatDock>`
|
|
2738
|
-
* silently falls back to popover anyway, so the toggle has nothing to do
|
|
2739
|
-
* and auto-hides. Pass `forceVisible` to override (e.g. in stories).
|
|
2850
|
+
* ID generation. Uses crypto.randomUUID when available with a fallback
|
|
2851
|
+
* for older environments (and SSR bundles that may not have crypto).
|
|
2740
2852
|
*/
|
|
2741
|
-
declare function
|
|
2853
|
+
declare function createId(prefix?: string): string;
|
|
2742
2854
|
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2855
|
+
/**
|
|
2856
|
+
* Token coalescer. Buffers stream tokens within a small time window before
|
|
2857
|
+
* dispatching a single aggregated chunk. Prevents 60+ re-renders per second
|
|
2858
|
+
* on fast streams.
|
|
2859
|
+
*/
|
|
2860
|
+
interface TokenBuffer {
|
|
2861
|
+
/** Append a delta. Returns immediately. */
|
|
2862
|
+
push(delta: string): void;
|
|
2863
|
+
/** Force flush and resolve any pending timer. */
|
|
2864
|
+
flush(): void;
|
|
2865
|
+
/** Stop accepting tokens; flush whatever is buffered. */
|
|
2866
|
+
close(): void;
|
|
2752
2867
|
}
|
|
2868
|
+
declare function createTokenBuffer(onFlush: (delta: string) => void, windowMs?: 16): TokenBuffer;
|
|
2869
|
+
|
|
2870
|
+
declare function resolvePersona(message: Pick<ChatMessage, 'role' | 'sender'>, user?: ChatUserContext, assistant?: ChatAssistantContext): ChatPersona;
|
|
2871
|
+
/** Compute initials for an avatar fallback. */
|
|
2872
|
+
declare function deriveInitials(persona: ChatPersona, role?: string): string;
|
|
2873
|
+
|
|
2753
2874
|
/**
|
|
2754
|
-
*
|
|
2755
|
-
* slot or into `<ChatDock headerActions>`.
|
|
2875
|
+
* HTTP + SSE transport. Default implementation for web hosts.
|
|
2756
2876
|
*
|
|
2757
|
-
* @
|
|
2758
|
-
*
|
|
2759
|
-
*
|
|
2760
|
-
*
|
|
2761
|
-
*
|
|
2877
|
+
* Backend contract (see @dev/@refactoring7-chat/06-integration.md):
|
|
2878
|
+
* POST /sessions → SessionInfo (JSON)
|
|
2879
|
+
* GET /sessions/:id/history?cursor= → HistoryPage (JSON)
|
|
2880
|
+
* POST /sessions/:id/messages → SSE stream of ChatStreamEvent
|
|
2881
|
+
* POST /sessions/:id/messages/buffered → ChatMessage (JSON, fallback)
|
|
2882
|
+
* DELETE /sessions/:id → 204
|
|
2762
2883
|
*/
|
|
2763
|
-
declare function ChatHeaderAudioToggle({ muted, onToggle, unmuteLabel, muteLabel, }: ChatHeaderAudioToggleProps): react_jsx_runtime.JSX.Element;
|
|
2764
2884
|
|
|
2765
|
-
interface
|
|
2766
|
-
/**
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
* Show a `window.dialog.confirm` before calling `onReset`. @default true
|
|
2777
|
-
*
|
|
2778
|
-
* Requires the host to mount `<DialogProvider>` from `@djangocfg/ui-core`
|
|
2779
|
-
* — it installs the `window.dialog` API used here.
|
|
2780
|
-
*/
|
|
2781
|
-
confirm?: boolean;
|
|
2782
|
-
/** Confirm dialog title. */
|
|
2783
|
-
confirmTitle?: string;
|
|
2784
|
-
/** Confirm dialog message. */
|
|
2785
|
-
confirmMessage?: string;
|
|
2786
|
-
/** Override tooltip / aria label. */
|
|
2787
|
-
ariaLabel?: string;
|
|
2885
|
+
interface HttpTransportConfig {
|
|
2886
|
+
/** Base URL without trailing slash, e.g. '/api/chat' or 'https://api.example.com/v1/chat'. */
|
|
2887
|
+
baseUrl: string;
|
|
2888
|
+
/** Optional slug appended/forwarded as project identifier. */
|
|
2889
|
+
slug?: string;
|
|
2890
|
+
/** Returns headers applied to every request — e.g. Authorization. */
|
|
2891
|
+
getAuthHeader?: () => Record<string, string> | Promise<Record<string, string>>;
|
|
2892
|
+
/** Default fetch timeout (per non-streaming request). */
|
|
2893
|
+
timeoutMs?: number;
|
|
2894
|
+
/** Override fetch implementation (useful for tests or custom retry layers). */
|
|
2895
|
+
fetchImpl?: typeof fetch;
|
|
2788
2896
|
}
|
|
2897
|
+
declare function createHttpTransport(config: HttpTransportConfig): ChatTransport;
|
|
2898
|
+
|
|
2789
2899
|
/**
|
|
2790
|
-
*
|
|
2791
|
-
|
|
2792
|
-
|
|
2900
|
+
* In-memory chat transport for stories and tests. Replays scripted replies.
|
|
2901
|
+
*/
|
|
2902
|
+
|
|
2903
|
+
interface MockTransportOptions {
|
|
2904
|
+
/** Each entry is the assistant's reply for one user turn. Strings are split
|
|
2905
|
+
* into chunks; arrays are taken as the exact event sequence (after a
|
|
2906
|
+
* prepended `message_start` and before a synthetic `message_end`). */
|
|
2907
|
+
replies?: Array<string | ChatStreamEvent[]>;
|
|
2908
|
+
latencyMs?: number;
|
|
2909
|
+
/** Initial history returned by `createSession`. */
|
|
2910
|
+
initialMessages?: ChatMessage[];
|
|
2911
|
+
shouldFail?: (attempt: number) => boolean;
|
|
2912
|
+
}
|
|
2913
|
+
declare function createMockTransport(opts?: MockTransportOptions): ChatTransport;
|
|
2914
|
+
|
|
2915
|
+
/**
|
|
2916
|
+
* Server-Sent Events parser as an AsyncGenerator.
|
|
2793
2917
|
*
|
|
2794
|
-
*
|
|
2795
|
-
*
|
|
2796
|
-
*
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2918
|
+
* Yields parsed events from a `Response` body. Handles the split-read case
|
|
2919
|
+
* where `event:` and `data:` arrive in separate TCP packets. Skips malformed
|
|
2920
|
+
* JSON gracefully. Honors AbortSignal (caller passes one to fetch).
|
|
2921
|
+
*/
|
|
2922
|
+
|
|
2923
|
+
interface RawEvent {
|
|
2924
|
+
event?: string;
|
|
2925
|
+
data?: string;
|
|
2926
|
+
}
|
|
2927
|
+
interface ParseSSEOptions {
|
|
2928
|
+
signal?: AbortSignal;
|
|
2929
|
+
/** Map a raw SSE event to zero, one, or many `ChatStreamEvent`s.
|
|
2930
|
+
* Default: parse `data` as JSON and assume the JSON shape already
|
|
2931
|
+
* matches `ChatStreamEvent`. */
|
|
2932
|
+
map?: (raw: RawEvent) => ChatStreamEvent | ChatStreamEvent[] | null;
|
|
2933
|
+
idleTimeoutMs?: number;
|
|
2934
|
+
}
|
|
2935
|
+
declare function parseSSE(response: Response, options?: ParseSSEOptions): AsyncGenerator<ChatStreamEvent, void, void>;
|
|
2936
|
+
|
|
2937
|
+
/**
|
|
2938
|
+
* Transport surface re-export. Lives in core so transport implementations
|
|
2939
|
+
* never need to reach into the public types module.
|
|
2801
2940
|
*/
|
|
2802
|
-
declare function ChatHeaderResetButton({ onReset, onSuccess, onError, confirm, confirmTitle, confirmMessage, ariaLabel, }: ChatHeaderResetButtonProps): react_jsx_runtime.JSX.Element;
|
|
2803
2941
|
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
/**
|
|
2808
|
-
* Subset of BCP-47 tags to offer. Default: every entry from the
|
|
2809
|
-
* Web Speech catalogue (~66 tags incl. regional variants). Pass a
|
|
2810
|
-
* tighter list when your backend STT only supports a subset.
|
|
2811
|
-
*/
|
|
2812
|
-
allowedTags?: string[];
|
|
2813
|
-
/** Hide the globe-fallback icon when no flag resolves. */
|
|
2814
|
-
hideFallbackIcon?: boolean;
|
|
2815
|
-
className?: string;
|
|
2942
|
+
declare class TransportError extends Error {
|
|
2943
|
+
code: string;
|
|
2944
|
+
constructor(message: string, code?: string);
|
|
2816
2945
|
}
|
|
2946
|
+
|
|
2817
2947
|
/**
|
|
2818
|
-
*
|
|
2819
|
-
* top of the ui-core `<Combobox>` — searchable autocomplete with
|
|
2820
|
-
* flags, ~66 BCP-47 tags from the official Chrome Web Speech demo, and
|
|
2821
|
-
* a custom 28×28 trigger via `renderTrigger`.
|
|
2948
|
+
* Pydantic-AI SSE event mapper.
|
|
2822
2949
|
*
|
|
2823
|
-
*
|
|
2824
|
-
*
|
|
2825
|
-
*
|
|
2950
|
+
* Translates the event shape emitted by pydantic-AI–style Django backends
|
|
2951
|
+
* (text_delta / tool_call / tool_result / done / error / approval_required)
|
|
2952
|
+
* into the canonical `ChatStreamEvent` stream consumed by the Chat reducer.
|
|
2953
|
+
*
|
|
2954
|
+
* Backends that don't expose a stable `tool_call_id` are supported via a
|
|
2955
|
+
* per-stream FIFO queue keyed by tool name. The earlier "Map<name, toolId>"
|
|
2956
|
+
* approach lost the first toolId when a tool was invoked twice in one turn
|
|
2957
|
+
* (e.g. `list_tasks` for search and again for confirmation) — using a queue
|
|
2958
|
+
* keeps each call/result pair correctly matched.
|
|
2826
2959
|
*/
|
|
2827
|
-
declare function ChatHeaderLanguageButton({ ariaLabel, allowedTags, hideFallbackIcon, className, }: ChatHeaderLanguageButtonProps): react.ReactElement;
|
|
2828
2960
|
|
|
2829
|
-
interface
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
onClick?: () => void;
|
|
2836
|
-
/** Close (×) button handler — typically marks the greeting as dismissed. */
|
|
2837
|
-
onDismiss?: () => void;
|
|
2838
|
-
/** Anchor relative to a FAB on the same side. @default 'bottom-right' */
|
|
2839
|
-
position?: ChatFABPosition;
|
|
2840
|
-
/**
|
|
2841
|
-
* Horizontal pixel offset matching the FAB's `offset` prop, so the greeting
|
|
2842
|
-
* lines up under the FAB. @default 24
|
|
2843
|
-
*/
|
|
2844
|
-
fabOffset?: number;
|
|
2845
|
-
/**
|
|
2846
|
-
* Vertical pixel offset above/below the FAB centerline. @default 96
|
|
2847
|
-
* (room for an `md` FAB plus a small gap).
|
|
2848
|
-
*/
|
|
2849
|
-
fabClearance?: number;
|
|
2850
|
-
/** Delay before the greeting appears, in ms. @default 1500 */
|
|
2851
|
-
delayMs?: number;
|
|
2852
|
-
/** z-index. @default 9998 (just below the default FAB at 9999). */
|
|
2853
|
-
zIndex?: number;
|
|
2854
|
-
/** Override classes on the bubble. */
|
|
2855
|
-
className?: string;
|
|
2856
|
-
/** Override styles on the bubble. */
|
|
2857
|
-
style?: CSSProperties;
|
|
2858
|
-
/** Optional sender avatar / icon shown on the left. */
|
|
2859
|
-
avatar?: ReactNode;
|
|
2860
|
-
/** Optional sender label rendered above the text. */
|
|
2861
|
-
senderName?: string;
|
|
2862
|
-
/** ARIA label for the dismiss button. @default 'Dismiss' */
|
|
2863
|
-
dismissLabel?: string;
|
|
2961
|
+
interface PydanticAIEvent {
|
|
2962
|
+
type: 'text_delta' | 'tool_call' | 'tool_result' | 'done' | 'error' | 'approval_required';
|
|
2963
|
+
delta?: string;
|
|
2964
|
+
tool?: string;
|
|
2965
|
+
args?: unknown;
|
|
2966
|
+
result?: unknown;
|
|
2864
2967
|
/**
|
|
2865
|
-
*
|
|
2866
|
-
*
|
|
2968
|
+
* Structured frontend payload — present on `tool_result` when the tool has
|
|
2969
|
+
* a `result_schema`. The LLM sees only `result` (compact text); the
|
|
2970
|
+
* frontend uses `data` to render rich UI (e.g. vehicle cards).
|
|
2867
2971
|
*/
|
|
2868
|
-
|
|
2972
|
+
data?: unknown;
|
|
2973
|
+
total_tokens?: number;
|
|
2974
|
+
error?: string;
|
|
2975
|
+
tool_call_id?: string;
|
|
2976
|
+
session_id?: string;
|
|
2977
|
+
}
|
|
2978
|
+
/** Per-stream FIFO queue keyed by tool name. Created via `createToolIdQueue`. */
|
|
2979
|
+
interface ToolIdQueue {
|
|
2980
|
+
/** Allocate a new toolId for a `tool_call` event and enqueue it under `name`. */
|
|
2981
|
+
push(name: string): string;
|
|
2982
|
+
/** Pop the oldest toolId for `name` (or return an orphan marker if none). */
|
|
2983
|
+
shift(name: string): string;
|
|
2984
|
+
/** Reset all queues (e.g. on stream close). */
|
|
2985
|
+
clear(): void;
|
|
2869
2986
|
}
|
|
2987
|
+
declare function createToolIdQueue(): ToolIdQueue;
|
|
2870
2988
|
/**
|
|
2871
|
-
*
|
|
2872
|
-
*
|
|
2873
|
-
*
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
*
|
|
2878
|
-
*
|
|
2879
|
-
*
|
|
2880
|
-
* const [open, setOpen] = useState(false);
|
|
2881
|
-
* const [dismissed, setDismissed] = useState(false);
|
|
2882
|
-
*
|
|
2883
|
-
* <ChatLauncher
|
|
2884
|
-
* open={open}
|
|
2885
|
-
* onOpenChange={setOpen}
|
|
2886
|
-
* fab={{ variant: 'animated' }}
|
|
2887
|
-
* dock={{ title: 'Support' }}
|
|
2888
|
-
* >
|
|
2889
|
-
* <SupportChat />
|
|
2890
|
-
* </ChatLauncher>
|
|
2989
|
+
* Translate a single pydantic-AI event into zero or more `ChatStreamEvent`s.
|
|
2990
|
+
* Pass a `ToolIdQueue` shared across the lifetime of one stream so that
|
|
2991
|
+
* `tool_call` / `tool_result` pairs match correctly.
|
|
2992
|
+
*/
|
|
2993
|
+
declare function mapPydanticAIEvent(ev: PydanticAIEvent, toolIds: ToolIdQueue): Generator<ChatStreamEvent>;
|
|
2994
|
+
/**
|
|
2995
|
+
* Convenience factory: returns a `ParseSSEOptions['map']` callback that
|
|
2996
|
+
* decodes raw SSE frames as `PydanticAIEvent` JSON and yields zero or
|
|
2997
|
+
* more `ChatStreamEvent`s through `mapPydanticAIEvent`.
|
|
2891
2998
|
*
|
|
2892
|
-
*
|
|
2893
|
-
* open={!open && !dismissed}
|
|
2894
|
-
* onClick={() => setOpen(true)}
|
|
2895
|
-
* onDismiss={() => setDismissed(true)}
|
|
2896
|
-
* senderName="Anna from Support"
|
|
2897
|
-
* delayMs={2000}
|
|
2898
|
-
* >
|
|
2899
|
-
* Hi! 👋 Got a question? I'm here to help.
|
|
2900
|
-
* </ChatGreeting>
|
|
2901
|
-
* ```
|
|
2999
|
+
* Allocates an internal `ToolIdQueue` — call this once per stream.
|
|
2902
3000
|
*/
|
|
2903
|
-
declare function
|
|
3001
|
+
declare function createPydanticAISSEMap(): NonNullable<ParseSSEOptions['map']>;
|
|
2904
3002
|
|
|
2905
|
-
interface ChatUnreadPreviewProps {
|
|
2906
|
-
/** Controlled — usually `!dockOpen && !!message`. */
|
|
2907
|
-
open: boolean;
|
|
2908
|
-
/** Inbound message to preview. `null` hides the bubble. */
|
|
2909
|
-
message: ChatMessage | null;
|
|
2910
|
-
/** Tap → open chat + mark read. */
|
|
2911
|
-
onClick?: () => void;
|
|
2912
|
-
/** × → mark read without opening. */
|
|
2913
|
-
onDismiss?: () => void;
|
|
2914
|
-
/** Anchor corner — match the FAB so the bubble sits above it. @default 'bottom-right' */
|
|
2915
|
-
position?: ChatFABPosition;
|
|
2916
|
-
/** Horizontal offset from screen edge, matches the FAB. @default 24 */
|
|
2917
|
-
fabOffset?: number;
|
|
2918
|
-
/** Vertical clearance above/below the FAB. @default 96 */
|
|
2919
|
-
fabClearance?: number;
|
|
2920
|
-
/** Lines of body text before ellipsis. @default 2 */
|
|
2921
|
-
truncate?: number;
|
|
2922
|
-
/** z-index. @default 9998 */
|
|
2923
|
-
zIndex?: number;
|
|
2924
|
-
/** Render in-place (stories / previews). @default false */
|
|
2925
|
-
inline?: boolean;
|
|
2926
|
-
/** Override classes on the bubble. */
|
|
2927
|
-
className?: string;
|
|
2928
|
-
/** Override styles on the bubble. */
|
|
2929
|
-
style?: CSSProperties;
|
|
2930
|
-
/** ARIA label for the dismiss button. @default 'Mark as read' */
|
|
2931
|
-
dismissLabel?: string;
|
|
2932
|
-
/** Override the avatar — defaults to derived from `message.sender`. */
|
|
2933
|
-
avatar?: ReactNode;
|
|
2934
|
-
/** Override the sender label — defaults to `message.sender?.name`. */
|
|
2935
|
-
senderName?: string;
|
|
2936
|
-
}
|
|
2937
3003
|
/**
|
|
2938
|
-
*
|
|
3004
|
+
* High-level transport factory for pydantic-AI–style backends.
|
|
2939
3005
|
*
|
|
2940
|
-
*
|
|
2941
|
-
*
|
|
2942
|
-
*
|
|
3006
|
+
* Composes:
|
|
3007
|
+
* - `parseSSE` for spec-compliant SSE framing (multi-line `data:`, comments, idle timeout).
|
|
3008
|
+
* - `createPydanticAISSEMap` for normalizing pydantic-AI events into `ChatStreamEvent`.
|
|
2943
3009
|
*
|
|
2944
|
-
*
|
|
3010
|
+
* Use when your backend speaks the canonical pydantic-AI stream shape
|
|
3011
|
+
* (`text_delta` / `tool_call` / `tool_result` / `done` / `error`).
|
|
3012
|
+
* URL building, auth headers, history loading, and optional session
|
|
3013
|
+
* bootstrapping are caller responsibilities — pass them in.
|
|
2945
3014
|
*/
|
|
2946
|
-
declare function ChatUnreadPreview({ open, message, onClick, onDismiss, position, fabOffset, fabClearance, truncate, zIndex, inline, className, style, dismissLabel, avatar, senderName, }: ChatUnreadPreviewProps): react_jsx_runtime.JSX.Element;
|
|
2947
3015
|
|
|
2948
|
-
interface
|
|
2949
|
-
/** Key (case-sensitive single char or named like 'Escape'). */
|
|
2950
|
-
key: string;
|
|
2951
|
-
/** Require Cmd (mac) or Ctrl (other). */
|
|
2952
|
-
meta?: boolean;
|
|
2953
|
-
/** Require Shift. */
|
|
2954
|
-
shift?: boolean;
|
|
2955
|
-
/** Require Alt. */
|
|
2956
|
-
alt?: boolean;
|
|
2957
|
-
}
|
|
2958
|
-
interface ChatLauncherGreeting extends Omit<ChatGreetingProps, 'open' | 'onClick' | 'onDismiss' | 'position' | 'fabOffset' | 'children'> {
|
|
2959
|
-
/** Greeting body — string for the default style, or any ReactNode. */
|
|
2960
|
-
content: ReactNode;
|
|
2961
|
-
/** Persistence key for "user dismissed this greeting" in `localStorage`. Pass `null` to disable persistence. @default null */
|
|
2962
|
-
dismissStorageKey?: string | null;
|
|
2963
|
-
/** Hide the greeting once the user opens the chat. @default true */
|
|
2964
|
-
hideOnOpen?: boolean;
|
|
2965
|
-
}
|
|
2966
|
-
interface ChatLauncherProps {
|
|
2967
|
-
/** Dock contents — typically a `<Chat>` instance. */
|
|
2968
|
-
children: ReactNode;
|
|
2969
|
-
/** FAB customization (icon, position, label, pulse, badge, tooltip, variant, size). */
|
|
2970
|
-
fab?: Omit<ChatFABProps, 'onClick'>;
|
|
2971
|
-
/** Dock customization (size, title, position, transition, mobileFullscreen). */
|
|
2972
|
-
dock?: Omit<ChatDockProps, 'open' | 'onClose' | 'children'>;
|
|
2973
|
-
/**
|
|
2974
|
-
* Proactive greeting bubble shown next to the FAB before the user opens the chat.
|
|
2975
|
-
* Set to a string or full config object. Omit to disable.
|
|
2976
|
-
*/
|
|
2977
|
-
greeting?: string | ChatLauncherGreeting;
|
|
2978
|
-
/** Open/close via a keyboard shortcut. */
|
|
2979
|
-
hotkey?: ChatLauncherHotkey;
|
|
2980
|
-
/** Initial open state for uncontrolled mode. @default false */
|
|
2981
|
-
defaultOpen?: boolean;
|
|
2982
|
-
/** Controlled open state (pair with `onOpenChange`). */
|
|
2983
|
-
open?: boolean;
|
|
2984
|
-
/** Controlled open state setter. */
|
|
2985
|
-
onOpenChange?: (open: boolean) => void;
|
|
2986
|
-
/**
|
|
2987
|
-
* Focus the composer textarea when the dock opens. Saves a click for
|
|
2988
|
-
* every "FAB → start typing" interaction. @default true
|
|
2989
|
-
*/
|
|
2990
|
-
autoFocusComposerOnOpen?: boolean;
|
|
2991
|
-
/**
|
|
2992
|
-
* Close the dock on `Escape`. Mirrors standard popover / drawer UX.
|
|
2993
|
-
* Set to `false` to disable (e.g. if you want Escape to do something
|
|
2994
|
-
* else inside the chat). @default true
|
|
2995
|
-
*/
|
|
2996
|
-
closeOnEscape?: boolean;
|
|
3016
|
+
interface PydanticAIChatTransportOpts {
|
|
2997
3017
|
/**
|
|
2998
|
-
*
|
|
2999
|
-
*
|
|
3000
|
-
* to the FAB and (by default) the FAB badge.
|
|
3001
|
-
*
|
|
3002
|
-
* Source it from `useChatUnread()` inside your `<ChatProvider>`.
|
|
3018
|
+
* Build the SSE stream URL for a user message turn.
|
|
3019
|
+
* @example (sessionId, message) => `${base}/stream?session_id=${sessionId}&message=${encodeURIComponent(message)}`
|
|
3003
3020
|
*/
|
|
3004
|
-
|
|
3021
|
+
buildStreamUrl: (sessionId: string, message: string) => string | URL;
|
|
3022
|
+
/** Optional history loader. If omitted, `loadHistory` returns an empty page. */
|
|
3023
|
+
loadHistory?: (sessionId: string, cursor?: string | null) => Promise<HistoryPage>;
|
|
3005
3024
|
/**
|
|
3006
|
-
*
|
|
3007
|
-
*
|
|
3025
|
+
* Optional session bootstrap. Called from `createSession`. Useful for
|
|
3026
|
+
* backends that need a `POST /sessions` round-trip or want to pre-seed
|
|
3027
|
+
* history.
|
|
3008
3028
|
*/
|
|
3009
|
-
|
|
3029
|
+
bootstrapSession?: (opts?: CreateSessionOptions) => Promise<SessionInfo>;
|
|
3030
|
+
/** Optional session teardown. */
|
|
3031
|
+
closeSession?: (sessionId: string) => Promise<void>;
|
|
3010
3032
|
/**
|
|
3011
|
-
*
|
|
3012
|
-
*
|
|
3013
|
-
*
|
|
3033
|
+
* Optional non-streaming send (for hosts that need a buffered fallback,
|
|
3034
|
+
* e.g. when the user disables streaming). Defaults to throwing — most
|
|
3035
|
+
* hosts only use streaming.
|
|
3014
3036
|
*/
|
|
3015
|
-
|
|
3037
|
+
send?: (sessionId: string, content: string, options?: SendOptions) => Promise<ChatMessage>;
|
|
3038
|
+
/** Request headers (Authorization, content-type, etc.). */
|
|
3039
|
+
buildHeaders?: () => HeadersInit | Promise<HeadersInit>;
|
|
3040
|
+
/** Override fetch (tests, retry layers). */
|
|
3041
|
+
fetchImpl?: typeof fetch;
|
|
3016
3042
|
/**
|
|
3017
|
-
*
|
|
3018
|
-
* `
|
|
3019
|
-
*
|
|
3020
|
-
* header's actions slot when audio is actually configured (not silent).
|
|
3021
|
-
*
|
|
3022
|
-
* Hosts that manage their own header can ignore this prop and render
|
|
3023
|
-
* `<ChatHeaderAudioToggle>` directly.
|
|
3043
|
+
* HTTP method for the stream request. Defaults to `'POST'` with
|
|
3044
|
+
* `{ content, attachments, metadata }` JSON body. Set to `'GET'` if
|
|
3045
|
+
* your backend embeds the message in the URL via `buildStreamUrl`.
|
|
3024
3046
|
*/
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
isSilent?: boolean;
|
|
3029
|
-
} | null;
|
|
3047
|
+
streamMethod?: 'GET' | 'POST';
|
|
3048
|
+
/** Idle timeout for the SSE connection, in ms. Forwarded to `parseSSE`. */
|
|
3049
|
+
idleTimeoutMs?: number;
|
|
3030
3050
|
/**
|
|
3031
|
-
*
|
|
3032
|
-
*
|
|
3051
|
+
* Side-channel for events that don't translate to `ChatStreamEvent`
|
|
3052
|
+
* (e.g. `approval_required` — surfaces interactive prompts outside the
|
|
3053
|
+
* normal message stream). Called synchronously while parsing the SSE
|
|
3054
|
+
* frame; mutate caller-owned state, don't `await` long work here.
|
|
3033
3055
|
*/
|
|
3034
|
-
|
|
3056
|
+
onPydanticEvent?: (event: PydanticAIEvent) => void;
|
|
3035
3057
|
}
|
|
3036
|
-
|
|
3037
|
-
* Floating chat launcher = FAB + Dock + presence + optional greeting + hotkey.
|
|
3038
|
-
*
|
|
3039
|
-
* 99% of hosts use this directly. For non-FAB triggers (e.g. an inline
|
|
3040
|
-
* link in the page) compose `<ChatDock>` with your own button.
|
|
3041
|
-
*/
|
|
3042
|
-
declare function ChatLauncher({ children, fab, dock, greeting, hotkey, defaultOpen, open: controlledOpen, onOpenChange, autoFocusComposerOnOpen, closeOnEscape, unreadMessage, onMarkRead, unreadPreview, audio, hideAudioToggle, }: ChatLauncherProps): react_jsx_runtime.JSX.Element;
|
|
3043
|
-
|
|
3044
|
-
type ChatPresencePhase = 'hidden' | 'entering' | 'visible' | 'leaving';
|
|
3045
|
-
/**
|
|
3046
|
-
* Presence state machine for floating popovers.
|
|
3047
|
-
*
|
|
3048
|
-
* Drives a four-phase lifecycle so enter/leave CSS transitions actually fire:
|
|
3049
|
-
*
|
|
3050
|
-
* hidden → entering (mount, transition class starts) → visible
|
|
3051
|
-
* visible → leaving (transition runs) → hidden (unmount)
|
|
3052
|
-
*
|
|
3053
|
-
* Mounting in `entering` and ticking to `visible` on the next paint is what
|
|
3054
|
-
* lets transition classes animate. Without it the element appears already
|
|
3055
|
-
* at its final state and CSS transitions never observe a change.
|
|
3056
|
-
*
|
|
3057
|
-
* @param open - controlled open state
|
|
3058
|
-
* @param exitDurationMs - how long the leave transition runs; should match CSS
|
|
3059
|
-
*/
|
|
3060
|
-
declare function useChatPresence(open: boolean, exitDurationMs?: number): ChatPresencePhase;
|
|
3058
|
+
declare function createPydanticAIChatTransport(opts: PydanticAIChatTransportOpts): ChatTransport;
|
|
3061
3059
|
|
|
3062
3060
|
/**
|
|
3063
3061
|
* @deprecated Plan64. As of ui-tools 2.1.369, `<MessageList>` is
|
|
@@ -3419,6 +3417,101 @@ declare function isGeoJSONFeatureCollection(v: unknown): v is {
|
|
|
3419
3417
|
};
|
|
3420
3418
|
declare function isStringValue(v: unknown): v is string;
|
|
3421
3419
|
|
|
3420
|
+
/**
|
|
3421
|
+
* Chat dev logger.
|
|
3422
|
+
*
|
|
3423
|
+
* A thin namespaced wrapper over `consola` that no-ops in production unless
|
|
3424
|
+
* the host app explicitly opts in via `<ChatRoot debug />`. The default
|
|
3425
|
+
* detection uses `isDev` from `@djangocfg/ui-core/lib/env` (NODE_ENV).
|
|
3426
|
+
*
|
|
3427
|
+
* Why a dedicated module: chat is async and event-heavy (bootstrap, transport,
|
|
3428
|
+
* SSE chunks, tool calls, regenerate, …). Inline `console.log`s rot fast and
|
|
3429
|
+
* leak into prod. A single `getChatLogger()` call gives every layer the same
|
|
3430
|
+
* namespaced sub-logger and keeps zero-cost gating in one place.
|
|
3431
|
+
*
|
|
3432
|
+
* Sub-loggers:
|
|
3433
|
+
* bootstrap — initial session bootstrap (createSession / loadHistory)
|
|
3434
|
+
* transport — outbound transport calls + responses
|
|
3435
|
+
* stream — SSE chunk / tool / message_end events
|
|
3436
|
+
* lifecycle — sendMessage, regenerate, newSession, edits
|
|
3437
|
+
* tools — tool_call_start / _delta / _end specifics
|
|
3438
|
+
* error — caught errors (always emitted as `error` level)
|
|
3439
|
+
*/
|
|
3440
|
+
|
|
3441
|
+
type ChatLogScope = 'bootstrap' | 'transport' | 'stream' | 'lifecycle' | 'tools' | 'error';
|
|
3442
|
+
interface ChatLogger {
|
|
3443
|
+
bootstrap: ConsolaInstance;
|
|
3444
|
+
transport: ConsolaInstance;
|
|
3445
|
+
stream: ConsolaInstance;
|
|
3446
|
+
lifecycle: ConsolaInstance;
|
|
3447
|
+
tools: ConsolaInstance;
|
|
3448
|
+
error: ConsolaInstance;
|
|
3449
|
+
/** True when this logger is actually emitting (host opted in or NODE_ENV=development). */
|
|
3450
|
+
enabled: boolean;
|
|
3451
|
+
}
|
|
3452
|
+
/**
|
|
3453
|
+
* Get the chat logger.
|
|
3454
|
+
* @param debug Explicit override from the host. `undefined` falls back to `isDev`.
|
|
3455
|
+
*/
|
|
3456
|
+
declare function getChatLogger(debug?: boolean): ChatLogger;
|
|
3457
|
+
|
|
3458
|
+
/**
|
|
3459
|
+
* sanitizeDraft — minimal pre-submit cleanup for chat-composer drafts.
|
|
3460
|
+
*
|
|
3461
|
+
* Mirrors the conservative behaviour ChatGPT / Claude / Telegram
|
|
3462
|
+
* actually ship: clean the obvious junk the user didn't intend to
|
|
3463
|
+
* send, touch nothing that *could* be intentional.
|
|
3464
|
+
*
|
|
3465
|
+
* **What we DO touch:**
|
|
3466
|
+
*
|
|
3467
|
+
* 1. Trim leading/trailing whitespace (spaces, tabs, newlines,
|
|
3468
|
+
* NBSP). The user typing `\n\n hello \n` meant `hello`.
|
|
3469
|
+
* 2. Normalise line endings — `\r\n` / `\r` → `\n`. Pasted Windows
|
|
3470
|
+
* / old-mac text gets the same internal shape, so the LLM
|
|
3471
|
+
* tokeniser and markdown renderer see one canonical form.
|
|
3472
|
+
* 3. Strip zero-width / invisible characters that web-paste
|
|
3473
|
+
* smuggles in: ZWSP (U+200B), ZWNJ (U+200C), ZWJ (U+200D),
|
|
3474
|
+
* BOM / ZWNBSP (U+FEFF). They're invisible, break LLM
|
|
3475
|
+
* tokenisation, and the user never meant to type them.
|
|
3476
|
+
*
|
|
3477
|
+
* **What we DO NOT touch (and why):**
|
|
3478
|
+
*
|
|
3479
|
+
* - **Internal whitespace runs** (3+ spaces, tabs, blank lines).
|
|
3480
|
+
* Code indentation depends on these. ChatGPT preserves them as
|
|
3481
|
+
* typed — " if (x):\n return" stays four-space-indented.
|
|
3482
|
+
* Collapsing them is the path to subtly broken code snippets.
|
|
3483
|
+
*
|
|
3484
|
+
* - **Bidi override marks** (U+200E LRM, U+200F RLM, U+202A..U+202E).
|
|
3485
|
+
* Legitimately used in Arabic / Hebrew / mixed-direction text.
|
|
3486
|
+
* Stripping silently breaks RTL users. If a specific deployment
|
|
3487
|
+
* wants to block them as a security measure, do it at that layer
|
|
3488
|
+
* with explicit user-visible feedback.
|
|
3489
|
+
*
|
|
3490
|
+
* - **Tabs vs spaces** beyond rule 1. Could be either code or
|
|
3491
|
+
* prose; without parsing markdown we can't tell.
|
|
3492
|
+
*
|
|
3493
|
+
* - **Emoji, mentions, URLs, code spans** — passthrough text.
|
|
3494
|
+
*
|
|
3495
|
+
* The function is intentionally tiny — every rule earns its keep
|
|
3496
|
+
* with a concrete "user pasted X from Y, got nonsense" story.
|
|
3497
|
+
*
|
|
3498
|
+
* **Idempotent**: `sanitizeDraft(sanitizeDraft(x)) === sanitizeDraft(x)`.
|
|
3499
|
+
*/
|
|
3500
|
+
declare function sanitizeDraft(input: string): string;
|
|
3501
|
+
/**
|
|
3502
|
+
* Convenience predicate: true when the draft is non-empty AFTER
|
|
3503
|
+
* sanitation. Use to gate Send buttons / Enter submits so an empty
|
|
3504
|
+
* or whitespace-only draft never produces a real message.
|
|
3505
|
+
*
|
|
3506
|
+
* Cheaper than sanitizeDraft(input).length > 0 only marginally —
|
|
3507
|
+
* we still allocate the cleaned string. Kept as a named helper for
|
|
3508
|
+
* call-site clarity.
|
|
3509
|
+
*/
|
|
3510
|
+
declare function isSubmittableDraft(input: string): boolean;
|
|
3511
|
+
|
|
3512
|
+
/** Walk the conversation and collect image attachments in chronological order. */
|
|
3513
|
+
declare function collectImageAttachments(messages: ChatMessage[]): ChatAttachment[];
|
|
3514
|
+
|
|
3422
3515
|
/**
|
|
3423
3516
|
* Chat color tokens — single source of truth.
|
|
3424
3517
|
*
|
|
@@ -3533,100 +3626,7 @@ interface ChatDestructiveStyles {
|
|
|
3533
3626
|
*/
|
|
3534
3627
|
declare function useChatDestructiveStyles(): ChatDestructiveStyles;
|
|
3535
3628
|
|
|
3536
|
-
|
|
3537
|
-
declare function collectImageAttachments(messages: ChatMessage[]): ChatAttachment[];
|
|
3538
|
-
|
|
3539
|
-
/**
|
|
3540
|
-
* sanitizeDraft — minimal pre-submit cleanup for chat-composer drafts.
|
|
3541
|
-
*
|
|
3542
|
-
* Mirrors the conservative behaviour ChatGPT / Claude / Telegram
|
|
3543
|
-
* actually ship: clean the obvious junk the user didn't intend to
|
|
3544
|
-
* send, touch nothing that *could* be intentional.
|
|
3545
|
-
*
|
|
3546
|
-
* **What we DO touch:**
|
|
3547
|
-
*
|
|
3548
|
-
* 1. Trim leading/trailing whitespace (spaces, tabs, newlines,
|
|
3549
|
-
* NBSP). The user typing `\n\n hello \n` meant `hello`.
|
|
3550
|
-
* 2. Normalise line endings — `\r\n` / `\r` → `\n`. Pasted Windows
|
|
3551
|
-
* / old-mac text gets the same internal shape, so the LLM
|
|
3552
|
-
* tokeniser and markdown renderer see one canonical form.
|
|
3553
|
-
* 3. Strip zero-width / invisible characters that web-paste
|
|
3554
|
-
* smuggles in: ZWSP (U+200B), ZWNJ (U+200C), ZWJ (U+200D),
|
|
3555
|
-
* BOM / ZWNBSP (U+FEFF). They're invisible, break LLM
|
|
3556
|
-
* tokenisation, and the user never meant to type them.
|
|
3557
|
-
*
|
|
3558
|
-
* **What we DO NOT touch (and why):**
|
|
3559
|
-
*
|
|
3560
|
-
* - **Internal whitespace runs** (3+ spaces, tabs, blank lines).
|
|
3561
|
-
* Code indentation depends on these. ChatGPT preserves them as
|
|
3562
|
-
* typed — " if (x):\n return" stays four-space-indented.
|
|
3563
|
-
* Collapsing them is the path to subtly broken code snippets.
|
|
3564
|
-
*
|
|
3565
|
-
* - **Bidi override marks** (U+200E LRM, U+200F RLM, U+202A..U+202E).
|
|
3566
|
-
* Legitimately used in Arabic / Hebrew / mixed-direction text.
|
|
3567
|
-
* Stripping silently breaks RTL users. If a specific deployment
|
|
3568
|
-
* wants to block them as a security measure, do it at that layer
|
|
3569
|
-
* with explicit user-visible feedback.
|
|
3570
|
-
*
|
|
3571
|
-
* - **Tabs vs spaces** beyond rule 1. Could be either code or
|
|
3572
|
-
* prose; without parsing markdown we can't tell.
|
|
3573
|
-
*
|
|
3574
|
-
* - **Emoji, mentions, URLs, code spans** — passthrough text.
|
|
3575
|
-
*
|
|
3576
|
-
* The function is intentionally tiny — every rule earns its keep
|
|
3577
|
-
* with a concrete "user pasted X from Y, got nonsense" story.
|
|
3578
|
-
*
|
|
3579
|
-
* **Idempotent**: `sanitizeDraft(sanitizeDraft(x)) === sanitizeDraft(x)`.
|
|
3580
|
-
*/
|
|
3581
|
-
declare function sanitizeDraft(input: string): string;
|
|
3582
|
-
/**
|
|
3583
|
-
* Convenience predicate: true when the draft is non-empty AFTER
|
|
3584
|
-
* sanitation. Use to gate Send buttons / Enter submits so an empty
|
|
3585
|
-
* or whitespace-only draft never produces a real message.
|
|
3586
|
-
*
|
|
3587
|
-
* Cheaper than sanitizeDraft(input).length > 0 only marginally —
|
|
3588
|
-
* we still allocate the cleaned string. Kept as a named helper for
|
|
3589
|
-
* call-site clarity.
|
|
3590
|
-
*/
|
|
3591
|
-
declare function isSubmittableDraft(input: string): boolean;
|
|
3592
|
-
|
|
3593
|
-
/**
|
|
3594
|
-
* Chat dev logger.
|
|
3595
|
-
*
|
|
3596
|
-
* A thin namespaced wrapper over `consola` that no-ops in production unless
|
|
3597
|
-
* the host app explicitly opts in via `<ChatRoot debug />`. The default
|
|
3598
|
-
* detection uses `isDev` from `@djangocfg/ui-core/lib/env` (NODE_ENV).
|
|
3599
|
-
*
|
|
3600
|
-
* Why a dedicated module: chat is async and event-heavy (bootstrap, transport,
|
|
3601
|
-
* SSE chunks, tool calls, regenerate, …). Inline `console.log`s rot fast and
|
|
3602
|
-
* leak into prod. A single `getChatLogger()` call gives every layer the same
|
|
3603
|
-
* namespaced sub-logger and keeps zero-cost gating in one place.
|
|
3604
|
-
*
|
|
3605
|
-
* Sub-loggers:
|
|
3606
|
-
* bootstrap — initial session bootstrap (createSession / loadHistory)
|
|
3607
|
-
* transport — outbound transport calls + responses
|
|
3608
|
-
* stream — SSE chunk / tool / message_end events
|
|
3609
|
-
* lifecycle — sendMessage, regenerate, newSession, edits
|
|
3610
|
-
* tools — tool_call_start / _delta / _end specifics
|
|
3611
|
-
* error — caught errors (always emitted as `error` level)
|
|
3612
|
-
*/
|
|
3613
|
-
|
|
3614
|
-
type ChatLogScope = 'bootstrap' | 'transport' | 'stream' | 'lifecycle' | 'tools' | 'error';
|
|
3615
|
-
interface ChatLogger {
|
|
3616
|
-
bootstrap: ConsolaInstance;
|
|
3617
|
-
transport: ConsolaInstance;
|
|
3618
|
-
stream: ConsolaInstance;
|
|
3619
|
-
lifecycle: ConsolaInstance;
|
|
3620
|
-
tools: ConsolaInstance;
|
|
3621
|
-
error: ConsolaInstance;
|
|
3622
|
-
/** True when this logger is actually emitting (host opted in or NODE_ENV=development). */
|
|
3623
|
-
enabled: boolean;
|
|
3624
|
-
}
|
|
3625
|
-
/**
|
|
3626
|
-
* Get the chat logger.
|
|
3627
|
-
* @param debug Explicit override from the host. `undefined` falls back to `isDev`.
|
|
3628
|
-
*/
|
|
3629
|
-
declare function getChatLogger(debug?: boolean): ChatLogger;
|
|
3629
|
+
declare const LazyChat: react.ComponentType<ChatRootProps>;
|
|
3630
3630
|
|
|
3631
3631
|
interface MessageListProps {
|
|
3632
3632
|
messages?: ChatMessage[];
|