@djangocfg/ui-tools 2.1.407 → 2.1.409
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 +9 -10
- package/dist/file-icon/index.cjs +449 -61
- package/dist/file-icon/index.cjs.map +1 -1
- package/dist/file-icon/index.d.cts +56 -18
- package/dist/file-icon/index.d.ts +56 -18
- package/dist/file-icon/index.mjs +448 -62
- package/dist/file-icon/index.mjs.map +1 -1
- package/dist/tree/index.cjs +49 -22
- package/dist/tree/index.cjs.map +1 -1
- package/dist/tree/index.d.cts +9 -3
- package/dist/tree/index.d.ts +9 -3
- package/dist/tree/index.mjs +49 -22
- package/dist/tree/index.mjs.map +1 -1
- package/dist/{types-B_zhyAqR.d.cts → types-eEu8SeiQ.d.cts} +4 -0
- package/dist/{types-B_zhyAqR.d.ts → types-eEu8SeiQ.d.ts} +4 -0
- package/package.json +8 -13
- package/src/components/FloatingToolbar/index.tsx +37 -3
- package/src/lib/page-snapshot/__tests__/capture-integration.test.ts +85 -0
- package/src/lib/page-snapshot/__tests__/engine.test.ts +36 -0
- package/src/lib/page-snapshot/__tests__/redaction-integration.test.ts +99 -0
- package/src/lib/page-snapshot/__tests__/tokens.test.ts +17 -0
- package/src/lib/page-snapshot/capture/__tests__/budget.test.ts +49 -0
- package/src/lib/page-snapshot/capture/__tests__/chrome-filter.test.ts +47 -0
- package/src/lib/page-snapshot/capture/__tests__/fold.test.ts +66 -0
- package/src/lib/page-snapshot/capture/__tests__/scope.test.ts +74 -0
- package/src/lib/page-snapshot/capture/__tests__/walk.test.ts +129 -0
- package/src/lib/page-snapshot/capture/accessible-name.ts +73 -0
- package/src/lib/page-snapshot/capture/budget.ts +95 -0
- package/src/lib/page-snapshot/capture/chrome-filter.ts +81 -0
- package/src/lib/page-snapshot/capture/classify.ts +111 -0
- package/src/lib/page-snapshot/capture/dom-utils.ts +111 -0
- package/src/lib/page-snapshot/capture/fold.ts +96 -0
- package/src/lib/page-snapshot/capture/scope.ts +169 -0
- package/src/lib/page-snapshot/capture/walk.ts +250 -0
- package/src/lib/page-snapshot/cst/__tests__/serialize.test.ts +50 -0
- package/src/lib/page-snapshot/cst/directives.ts +47 -0
- package/src/lib/page-snapshot/cst/payload.ts +50 -0
- package/src/lib/page-snapshot/cst/serialize.ts +84 -0
- package/src/lib/page-snapshot/cst/types.ts +115 -0
- package/src/lib/page-snapshot/engine.ts +176 -0
- package/src/lib/page-snapshot/index.ts +93 -0
- package/src/lib/page-snapshot/react/PageSnapshotChip.tsx +72 -0
- package/src/lib/page-snapshot/react/PageSnapshotPreview.tsx +78 -0
- package/src/lib/page-snapshot/react/__tests__/PageSnapshotChip.test.tsx +54 -0
- package/src/lib/page-snapshot/react/__tests__/provider.test.tsx +103 -0
- package/src/lib/page-snapshot/react/__tests__/use-page-snapshot-toggle.test.tsx +62 -0
- package/src/lib/page-snapshot/react/provider.tsx +162 -0
- package/src/lib/page-snapshot/react/use-page-snapshot-toggle.ts +47 -0
- package/src/lib/page-snapshot/react/use-page-snapshot.ts +67 -0
- package/src/lib/page-snapshot/redaction/__tests__/audit.test.ts +25 -0
- package/src/lib/page-snapshot/redaction/__tests__/heuristics.test.ts +73 -0
- package/src/lib/page-snapshot/redaction/__tests__/luhn.test.ts +26 -0
- package/src/lib/page-snapshot/redaction/__tests__/patterns.test.ts +60 -0
- package/src/lib/page-snapshot/redaction/audit.ts +58 -0
- package/src/lib/page-snapshot/redaction/heuristics.ts +75 -0
- package/src/lib/page-snapshot/redaction/index.ts +75 -0
- package/src/lib/page-snapshot/redaction/luhn.ts +25 -0
- package/src/lib/page-snapshot/redaction/patterns.ts +111 -0
- package/src/lib/page-snapshot/refs/__tests__/registry.test.ts +24 -0
- package/src/lib/page-snapshot/refs/registry.ts +46 -0
- package/src/lib/page-snapshot/staleness/__tests__/hash.test.ts +34 -0
- package/src/lib/page-snapshot/staleness/hash.ts +20 -0
- package/src/lib/page-snapshot/tokens.ts +15 -0
- package/src/tools/AudioPlayer/context/PlayerProvider.tsx +13 -14
- package/src/tools/AudioPlayer/hooks/useAudioElementEvents.ts +55 -6
- package/src/tools/AudioPlayer/parts/Meta/TimeDisplay.tsx +2 -5
- package/src/tools/Chat/README.md +277 -39
- package/src/tools/Chat/composer/Composer.tsx +471 -0
- package/src/tools/Chat/composer/ComposerActionBar.tsx +65 -0
- package/src/tools/Chat/composer/ComposerBanner.tsx +128 -0
- package/src/tools/Chat/composer/ComposerButton.tsx +64 -0
- package/src/tools/Chat/composer/ComposerFooter.tsx +90 -0
- package/src/tools/Chat/composer/ComposerMenuButton.tsx +62 -0
- package/src/tools/Chat/composer/ComposerModelPicker.tsx +104 -0
- package/src/tools/Chat/composer/ComposerRichTextarea.tsx +88 -0
- package/src/tools/Chat/composer/ComposerToolPill.tsx +95 -0
- package/src/tools/Chat/composer/index.ts +45 -0
- package/src/tools/Chat/composer/size-context.tsx +26 -0
- package/src/tools/Chat/composer/types.ts +143 -0
- package/src/tools/Chat/composer/useComposerActions.tsx +164 -0
- package/src/tools/Chat/context/ChatProvider.tsx +54 -3
- package/src/tools/Chat/core/__tests__/metadata.test.ts +69 -0
- package/src/tools/Chat/core/index.ts +23 -1
- package/src/tools/Chat/core/markdown.ts +1 -1
- package/src/tools/Chat/core/metadata.ts +47 -0
- package/src/tools/Chat/core/payload-dispatch.ts +1 -1
- package/src/tools/Chat/core/transport/http.ts +71 -32
- package/src/tools/Chat/core/transport/sse.ts +18 -10
- package/src/tools/Chat/highlight/HighlightOverlay.tsx +101 -0
- package/src/tools/Chat/highlight/README.md +103 -0
- package/src/tools/Chat/highlight/SpotlightCanvas.tsx +153 -0
- package/src/tools/Chat/highlight/__tests__/HighlightOverlay.test.tsx +112 -0
- package/src/tools/Chat/highlight/__tests__/resolveRef.test.ts +55 -0
- package/src/tools/Chat/highlight/index.ts +21 -0
- package/src/tools/Chat/highlight/resolveRef.ts +42 -0
- package/src/tools/Chat/highlight/types.ts +49 -0
- package/src/tools/Chat/highlight/useHighlightTargets.ts +128 -0
- package/src/tools/Chat/hooks/index.ts +0 -5
- package/src/tools/Chat/hooks/useAutoFocusOnStreamEnd.ts +28 -47
- package/src/tools/Chat/hooks/useChat.ts +47 -14
- package/src/tools/Chat/hooks/useChatComposer.ts +2 -2
- package/src/tools/Chat/hooks/useChatLayout.ts +1 -1
- package/src/tools/Chat/hooks/useStreamEndFocus.ts +54 -0
- package/src/tools/Chat/index.ts +25 -219
- package/src/tools/Chat/launcher/ChatDock.tsx +1 -1
- package/src/tools/Chat/launcher/ChatLauncher.tsx +1 -1
- package/src/tools/Chat/launcher/{ChatHeader.tsx → header/ChatHeader.tsx} +24 -11
- package/src/tools/Chat/launcher/{ChatHeaderActionButton.tsx → header/ChatHeaderActionButton.tsx} +34 -3
- package/src/tools/Chat/launcher/{ChatHeaderLanguageButton.tsx → header/ChatHeaderLanguageButton.tsx} +2 -2
- package/src/tools/Chat/launcher/{ChatHeaderModeToggle.tsx → header/ChatHeaderModeToggle.tsx} +1 -1
- package/src/tools/Chat/launcher/{ChatHeaderResetButton.tsx → header/ChatHeaderResetButton.tsx} +2 -1
- package/src/tools/Chat/launcher/{HeaderSlots.tsx → header/HeaderSlots.tsx} +3 -3
- package/src/tools/Chat/launcher/header/index.ts +26 -0
- package/src/tools/Chat/launcher/index.ts +3 -10
- package/src/tools/Chat/lazy.tsx +38 -284
- package/src/tools/Chat/{components → messages}/MessageBubble.tsx +58 -5
- package/src/tools/Chat/{components → messages}/MessageList.tsx +8 -25
- package/src/tools/Chat/messages/blocks/MessageBlocks.tsx +131 -0
- package/src/tools/Chat/messages/blocks/builtin.tsx +91 -0
- package/src/tools/Chat/messages/blocks/index.ts +12 -0
- package/src/tools/Chat/messages/blocks/registry.tsx +42 -0
- package/src/tools/Chat/messages/blocks/renderers/AudioBlock.tsx +20 -0
- package/src/tools/Chat/messages/blocks/renderers/CodeBlock.tsx +19 -0
- package/src/tools/Chat/messages/blocks/renderers/GalleryBlock.tsx +26 -0
- package/src/tools/Chat/messages/blocks/renderers/ImageBlock.tsx +27 -0
- package/src/tools/Chat/messages/blocks/renderers/JsonBlock.tsx +12 -0
- package/src/tools/Chat/messages/blocks/renderers/LottieBlock.tsx +11 -0
- package/src/tools/Chat/messages/blocks/renderers/MapBlock.tsx +36 -0
- package/src/tools/Chat/messages/blocks/renderers/MermaidBlock.tsx +11 -0
- package/src/tools/Chat/messages/blocks/renderers/VideoBlock.tsx +24 -0
- package/src/tools/Chat/messages/blocks/renderers/types.ts +8 -0
- package/src/tools/Chat/{components → messages}/index.ts +11 -5
- package/src/tools/Chat/public.ts +212 -0
- package/src/tools/Chat/shell/ChatRoot.tsx +345 -0
- package/src/tools/Chat/{components → shell}/EmptyState.tsx +4 -2
- package/src/tools/Chat/shell/index.ts +15 -0
- package/src/tools/Chat/types/block.ts +120 -0
- package/src/tools/Chat/types/config.ts +0 -5
- package/src/tools/Chat/types/index.ts +17 -0
- package/src/tools/Chat/types/message.ts +3 -0
- package/src/tools/Chat/utils/index.ts +4 -0
- package/src/tools/CodeEditor/README.md +4 -6
- package/src/tools/CodeEditor/components/DiffEditor.tsx +48 -13
- package/src/tools/CodeEditor/components/Editor.tsx +96 -44
- package/src/tools/CodeEditor/context/EditorProvider.tsx +34 -17
- package/src/tools/CodeEditor/hooks/useEditorTheme.ts +92 -99
- package/src/tools/CodeEditor/hooks/useMonaco.ts +37 -22
- package/src/tools/CodeEditor/lazy.tsx +6 -0
- package/src/tools/CodeEditor/lib/index.ts +1 -1
- package/src/tools/CodeEditor/lib/themes.ts +3 -39
- package/src/tools/CronScheduler/CronScheduler.client.tsx +230 -61
- package/src/tools/CronScheduler/components/CustomInput.tsx +21 -4
- package/src/tools/CronScheduler/components/DayChips.tsx +13 -11
- package/src/tools/CronScheduler/components/MonthDayGrid.tsx +4 -4
- package/src/tools/CronScheduler/components/SchedulePreview.tsx +7 -3
- package/src/tools/CronScheduler/components/TimeSelector.tsx +1 -1
- package/src/tools/CronScheduler/index.tsx +1 -1
- package/src/tools/CronScheduler/types/index.ts +8 -3
- package/src/tools/CronScheduler/utils/cron-humanize.ts +61 -16
- package/src/tools/CronScheduler/utils/cron-parser.ts +13 -4
- package/src/tools/FileIcon/FileIcon.tsx +24 -39
- package/src/tools/FileIcon/get-file-icon.ts +73 -0
- package/src/tools/FileIcon/icons/icon-data.ts +399 -0
- package/src/tools/FileIcon/index.ts +4 -0
- package/src/tools/FileIcon/loader.ts +17 -35
- package/src/tools/FileIcon/specialFolders.ts +18 -0
- package/src/tools/Gallery/components/lightbox/GalleryLightbox.tsx +112 -35
- package/src/tools/Gallery/components/media/GalleryVideo.tsx +21 -2
- package/src/tools/Gallery/components/preview/GalleryCarousel.tsx +11 -1
- package/src/tools/Gallery/hooks/usePreloadImages.ts +54 -7
- package/src/tools/ImageViewer/components/ImageInfo.tsx +12 -1
- package/src/tools/ImageViewer/components/ImageToolbar.tsx +51 -43
- package/src/tools/ImageViewer/components/ImageViewer.tsx +96 -24
- package/src/tools/ImageViewer/hooks/useImageLoading.ts +13 -0
- package/src/tools/ImageViewer/utils/constants.ts +3 -0
- package/src/tools/ImageViewer/utils/index.ts +1 -0
- package/src/tools/JsonForm/JsonSchemaForm.tsx +4 -1
- package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +5 -3
- package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +7 -4
- package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +3 -1
- package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +23 -3
- package/src/tools/JsonForm/widgets/ColorWidget.tsx +20 -12
- package/src/tools/JsonForm/widgets/NumberWidget.tsx +14 -9
- package/src/tools/JsonForm/widgets/RadioWidget.tsx +78 -0
- package/src/tools/JsonForm/widgets/SelectWidget.tsx +1 -0
- package/src/tools/JsonForm/widgets/SliderWidget.tsx +7 -4
- package/src/tools/JsonForm/widgets/TextWidget.tsx +41 -17
- package/src/tools/JsonForm/widgets/index.ts +1 -0
- package/src/tools/JsonTree/components/JsonContent.tsx +115 -40
- package/src/tools/LottiePlayer/LottiePlayer.client.tsx +177 -72
- package/src/tools/LottiePlayer/index.tsx +14 -4
- package/src/tools/LottiePlayer/lazy.tsx +11 -3
- package/src/tools/LottiePlayer/types.ts +31 -1
- package/src/tools/LottiePlayer/useLottie.ts +32 -9
- package/src/tools/LottiePlayer/usePrefersReducedMotion.ts +46 -0
- package/src/tools/Map/components/LayerSwitcher.tsx +54 -21
- package/src/tools/Map/components/MapCluster.tsx +28 -21
- package/src/tools/Map/components/MapContainer.tsx +11 -4
- package/src/tools/Map/components/MapLegend.tsx +46 -15
- package/src/tools/Map/components/MapMarker.tsx +31 -2
- package/src/tools/Map/hooks/useMapEvents.ts +64 -105
- package/src/tools/MarkdownEditor/MarkdownEditor.tsx +61 -6
- package/src/tools/MarkdownEditor/MentionList.tsx +37 -4
- package/src/tools/MarkdownEditor/createMentionSuggestion.ts +11 -0
- package/src/tools/MarkdownEditor/lazy.tsx +32 -7
- package/src/tools/MarkdownEditor/styles.css +13 -0
- package/src/tools/MarkdownMessage/CodeBlock.tsx +40 -17
- package/src/tools/MarkdownMessage/MarkdownMessage.tsx +26 -6
- package/src/tools/MarkdownMessage/components.tsx +22 -9
- package/src/tools/MarkdownMessage/types.ts +24 -1
- package/src/tools/Mermaid/Mermaid.client.tsx +27 -5
- package/src/tools/Mermaid/components/MermaidErrorPanel.tsx +31 -0
- package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +14 -17
- package/src/tools/Mermaid/hooks/useMermaidRenderer.ts +264 -168
- package/src/tools/Mermaid/hooks/useMermaidValidation.ts +76 -10
- package/src/tools/Mermaid/index.tsx +6 -0
- package/src/tools/Mermaid/utils/mermaid-helpers.ts +141 -18
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +11 -1
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +49 -20
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/index.tsx +7 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/grouping.ts +7 -4
- package/src/tools/OpenapiViewer/constants.ts +3 -0
- package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +73 -11
- package/src/tools/OpenapiViewer/utils/schemaExport.ts +26 -6
- package/src/tools/PrettyCode/PrettyCode.client.tsx +23 -16
- package/src/tools/PrettyCode/lazy.tsx +1 -1
- package/src/tools/SpeechRecognition/README.md +1 -1
- package/src/tools/SpeechRecognition/__tests__/language.test.ts +9 -3
- package/src/tools/SpeechRecognition/components/RecordingPulse.tsx +59 -0
- package/src/tools/SpeechRecognition/components/index.ts +2 -0
- package/src/tools/SpeechRecognition/core/engine/external.ts +24 -7
- package/src/tools/SpeechRecognition/core/language.ts +23 -6
- package/src/tools/SpeechRecognition/hooks/usePushToTalk.ts +36 -5
- package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +18 -11
- package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +94 -26
- package/src/tools/SpeechRecognition/widgets/index.ts +1 -1
- package/src/tools/Tree/README.md +4 -8
- package/src/tools/Tree/TreeRoot.tsx +22 -10
- package/src/tools/Tree/components/TreeContent.tsx +24 -4
- package/src/tools/Tree/components/TreeLabel.tsx +8 -2
- package/src/tools/Tree/components/TreeRow.tsx +16 -6
- package/src/tools/Tree/data/flatten.ts +10 -4
- package/src/tools/Tree/types.ts +4 -0
- package/src/tools/Uploader/components/UploadAddButton.tsx +29 -6
- package/src/tools/Uploader/components/UploadDropzone.tsx +63 -7
- package/src/tools/Uploader/components/UploadPageDropOverlay.tsx +19 -5
- package/src/tools/Uploader/components/UploadPreviewItem.tsx +47 -17
- package/src/tools/Uploader/components/UploadPreviewList.tsx +24 -12
- package/src/tools/Uploader/utils/formatters.ts +8 -3
- package/src/tools/VideoPlayer/canvas/hls-canvas.tsx +1 -0
- package/src/tools/VideoPlayer/canvas/{jsx.d.ts → jsx-augmentation.ts} +12 -19
- package/src/tools/VideoPlayer/canvas/vimeo-canvas.tsx +1 -0
- package/src/tools/VideoPlayer/canvas/youtube-canvas.tsx +1 -0
- package/src/tools/VideoPlayer/parts/fullscreen.tsx +1 -1
- package/src/tools/VideoPlayer/parts/pip.tsx +1 -1
- package/src/tools/VideoPlayer/parts/playback-rate.tsx +1 -1
- package/src/tools/VideoPlayer/parts/seek-bar.tsx +2 -2
- package/src/tools/VideoPlayer/parts/volume.tsx +2 -2
- package/src/tools/index.ts +2 -1
- package/src/tools/Chat/components/AudioToggle.tsx +0 -78
- package/src/tools/Chat/components/ChatRoot.tsx +0 -305
- package/src/tools/Chat/components/Composer.tsx +0 -216
- package/src/tools/Chat/hooks/useChatScroll.ts +0 -145
- package/src/tools/Chat/types.ts +0 -9
- package/src/tools/JsonTree/components/JsonToolbar.tsx +0 -95
- package/src/tools/JsonTree/hooks/useElementCorner.ts +0 -84
- package/src/tools/JsonTree/hooks/useNavbarHeight.ts +0 -83
- package/src/tools/OpenapiViewer/components/DocsLayout/schemaFields.ts +0 -121
- package/src/tools/Tour/README.md +0 -373
- package/src/tools/Tour/components/Tour.tsx +0 -12
- package/src/tools/Tour/components/TourContent.tsx +0 -171
- package/src/tools/Tour/components/TourNavigation.tsx +0 -77
- package/src/tools/Tour/components/TourProgress.tsx +0 -88
- package/src/tools/Tour/components/TourSpotlight.tsx +0 -199
- package/src/tools/Tour/components/index.ts +0 -5
- package/src/tools/Tour/context/TourContext.ts +0 -19
- package/src/tools/Tour/context/TourProvider.tsx +0 -292
- package/src/tools/Tour/context/index.ts +0 -2
- package/src/tools/Tour/hooks/index.ts +0 -3
- package/src/tools/Tour/hooks/useKeyboardNavigation.ts +0 -59
- package/src/tools/Tour/hooks/useStepTarget.ts +0 -121
- package/src/tools/Tour/hooks/useTour.ts +0 -42
- package/src/tools/Tour/index.ts +0 -38
- package/src/tools/Tour/types/index.ts +0 -224
- package/src/tools/Tour/utils/dom.ts +0 -98
- package/src/tools/Tour/utils/index.ts +0 -3
- package/src/tools/Tour/utils/logger.ts +0 -3
- package/src/tools/Tour/utils/scrollIntoView.ts +0 -24
- /package/src/tools/Chat/{config.ts → constants.ts} +0 -0
- /package/src/tools/Chat/launcher/{ChatHeaderAudioToggle.tsx → header/ChatHeaderAudioToggle.tsx} +0 -0
- /package/src/tools/Chat/{components → messages}/Attachments.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/JumpToLatest.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/MessageActions.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/Sources.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/StreamingIndicator.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/ToolCalls.tsx +0 -0
- /package/src/tools/Chat/{components → shell}/ErrorBanner.tsx +0 -0
package/src/tools/Chat/README.md
CHANGED
|
@@ -51,11 +51,13 @@ export function MyChat() {
|
|
|
51
51
|
- **Rich attachments.** `AttachmentsGrid` for thumbnails, `AttachmentsList` for custom renderers; `onAttachmentOpen` for lightbox.
|
|
52
52
|
- **Tool-payload dispatcher.** `dispatchToolPayload(matchers, fallback)` — pluggable predicates render `<LazyJsonTree>` / `<LazyMap>` / etc.
|
|
53
53
|
- **Persisted dock prefs.** `headerSlots.modeToggle.persistAs: 'my.key'` stores `mode` / `side` / `width` in localStorage; the toggle lets users flip popover ↔ side and survives reloads. (`useChatDockPrefs()` is now owned by the launcher internally — no longer a consumer-facing hook.)
|
|
54
|
-
- **UX guards.** Auto-focus composer on dock open, two-step Escape (textarea blur → close), click-to-focus on empty message area (Slack / Linear style), `useHotkey('mod+/')` toggle.
|
|
54
|
+
- **UX guards.** Auto-focus composer on dock open, two-step Escape (textarea blur → close), click-to-focus on empty message area **and** on the composer surface padding (Slack / Linear / ChatGPT style), `useHotkey('mod+/')` toggle.
|
|
55
55
|
- **ChatGPT-style autoscroll.** `MessageList` follows the bottom while the user is within `atBottomThreshold` px (default 120). Every user-sent message bumps `scrollAnchorId` and re-anchors the viewport with `behavior: 'smooth'` — sending no longer leaves your own bubble stuck above the fold. Scrolling up by hand breaks the lock; `<JumpToLatest>` brings it back.
|
|
56
|
-
- **Voice composer slot.** `<VoiceComposerSlot />` drops into `
|
|
56
|
+
- **Voice composer slot.** `<VoiceComposerSlot />` drops into `composer.slots.blockStart` with **zero props** — reads/writes the composer through the `ComposerHandle` registered in chat context. The built-in `<Composer>` and TipTap-backed `MarkdownEditor` register themselves automatically; custom composers wire it via `useRegisterComposer({ focus, moveCursorToEnd, getValue, setValue })`. Auto-gates on Firefox / in-app WebViews / missing `getUserMedia`, preserves typed prefix, 90-second countdown, silence auto-stop, Esc / Enter hotkeys, start / stop earcons. See [`SpeechRecognition`](../SpeechRecognition/README.md).
|
|
57
57
|
- **Language flag button.** `headerSlots.languagePicker: true` slots a 28×28 country flag into the dock header — opens a searchable `<Combobox>` with 66 BCP-47 tags from the Chrome Web Speech catalogue. Selection persists via `useSpeechPrefs`, picked up by every `useSpeechRecognition` downstream. (Raw `<ChatHeaderLanguageButton>` still exported for custom shells.)
|
|
58
|
-
- **Auto-focus on stream end.**
|
|
58
|
+
- **Auto-focus on stream end.** `<ChatProvider>` re-focuses the registered composer on the streaming → idle edge — type → send → read → keep typing without reaching for the mouse. Works for **every** usage pattern (`ChatRoot`, hand-rolled `ChatProvider` + `Composer`, headless), not just `ChatRoot`. Opt out with `<ChatProvider autoFocusOnStreamEnd={false}>`. The standalone `useAutoFocusOnStreamEnd()` hook is still exported for advanced cases (focus a non-composer target, drive `isStreaming` from your own store).
|
|
59
|
+
- **Page-context snapshot.** Optional `getDynamicMetadata` contributor on `<ChatProvider>` / `<ChatRoot>` — called fresh at send time, merged into transport `metadata`. Pairs with the `page-snapshot` engine (`src/lib/page-snapshot`) to attach a token-efficient, redacted snapshot of the page the user is looking at, so the assistant can answer in context. The snapshot rides a separate `metadata` field, never the message text.
|
|
60
|
+
- **AI highlight directives.** `highlight/` — when the assistant returns `point` directives, `<HighlightOverlay>` resolves each CST ref to a live element and draws an SVG-mask spotlight (optionally moves focus). Read-only: it points at the UI, never changes data. See [`highlight/README.md`](./highlight/README.md).
|
|
59
61
|
- **Centralized colors.** Role-aware className tokens (`BUBBLE_SURFACE` / `ANCHOR` / `TOGGLE` / `DESTRUCTIVE_SURFACE`) + hooks (`useChatBubbleStyles`, `useChatRoleStyles`, `useChatDestructiveStyles`).
|
|
60
62
|
- **Responsive.** FAB `size='responsive'` (default): phone → `sm`, tablet → `md`, desktop → `lg`. Side mode is desktop-only and falls back to popover below `lg`.
|
|
61
63
|
- **Mobile fullscreen.** Dock auto-fills viewport below 768px via `useIsMobile`. Heights use `dvh/svh/lvh` so iOS Safari URL bar doesn't clip the chat.
|
|
@@ -68,7 +70,7 @@ Transport (interface) ← Pydantic-AI / HTTP+SSE / Wails / mock
|
|
|
68
70
|
↓
|
|
69
71
|
Reducer (pure state machine)
|
|
70
72
|
↓
|
|
71
|
-
Hooks (useChat / useChatComposer /
|
|
73
|
+
Hooks (useChat / useChatComposer / useChatHistory / useChatLayout)
|
|
72
74
|
↓
|
|
73
75
|
ChatProvider (context)
|
|
74
76
|
↓
|
|
@@ -79,18 +81,25 @@ ChatRoot (one-line preset) ◄── optionally wrapped by ──► ChatLau
|
|
|
79
81
|
|
|
80
82
|
`ChatLauncher` mounts the `ChatProvider` itself — pass `transport` / `config` / `audio` / `initialSessionId` / `autoCreateSession` / `streaming` / `debug` to the launcher and use `<ChatRoot />` without props as the child. `ChatRoot` detects the ambient provider and reuses it; standalone `<ChatRoot transport={…}>` still works for non-launcher embeds. This is what makes declarative `headerSlots` (which render in the dock header) able to call `useChatContext()` and read `sessionId` / `clearMessages` / etc.
|
|
81
83
|
|
|
84
|
+
`ChatProvider` also owns chat-wide UX behaviour that must hold regardless of which preset wraps it — notably **stream-end composer re-focus** (`autoFocusOnStreamEnd`, default `true`). Putting it in the provider means a hand-rolled `ChatProvider` + `MessageList` + `Composer` layout behaves identically to `ChatRoot` with no extra wiring.
|
|
85
|
+
|
|
82
86
|
Module boundaries:
|
|
83
87
|
|
|
84
88
|
| Layer | May import | May NOT import |
|
|
85
89
|
| ---------------- | -------------------------------- | ------------------------ |
|
|
86
90
|
| `types/` | — | anything |
|
|
87
|
-
| `
|
|
91
|
+
| `constants.ts` | — | anything |
|
|
92
|
+
| `core/transport` | `types/` | React, hooks, UI |
|
|
88
93
|
| `core/reducer` | `types/` | React, transport |
|
|
89
|
-
| `hooks` | `core/*`, `ui-core` hooks |
|
|
90
|
-
| `context` | `hooks` |
|
|
91
|
-
| `styles/` | `types/` | hooks,
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
+
| `hooks` | `core/*`, `ui-core` hooks | UI folders |
|
|
95
|
+
| `context` | `hooks` | UI folders |
|
|
96
|
+
| `styles/` | `types/` | hooks, UI folders |
|
|
97
|
+
| `utils/` | `types/` | React, UI folders |
|
|
98
|
+
| `messages/` | `hooks`, `context`, `styles`, `ui-core` UI | transport implementations |
|
|
99
|
+
| `composer/` | `hooks`, `context`, `styles`, `messages`, `ui-core` UI | transport implementations |
|
|
100
|
+
| `shell/` | `messages`, `composer`, `hooks`, `context`, `ui-core` UI | transport implementations |
|
|
101
|
+
| `launcher/` | `shell`, `messages`, `composer`, `styles`, `ui-core` UI | transport implementations |
|
|
102
|
+
| `launcher/header/` | `context`, `hooks`, `launcher` siblings | transport implementations |
|
|
94
103
|
|
|
95
104
|
## Types
|
|
96
105
|
|
|
@@ -107,6 +116,7 @@ Single source of truth in [`types/`](./types/index.ts). Split by domain — impo
|
|
|
107
116
|
| `events` | `ChatStreamEvent` SSE union |
|
|
108
117
|
| `session` | `SessionInfo`, `HistoryPage`, `Stream/Send/CreateSessionOptions` |
|
|
109
118
|
| `transport` | `ChatTransport` |
|
|
119
|
+
| `block` | `MessageBlock` union + `BlockAppearance` (see Message blocks) |
|
|
110
120
|
|
|
111
121
|
## Launcher (FAB + Dock + Greeting)
|
|
112
122
|
|
|
@@ -525,6 +535,58 @@ function MyError() {
|
|
|
525
535
|
}
|
|
526
536
|
```
|
|
527
537
|
|
|
538
|
+
## Message blocks
|
|
539
|
+
|
|
540
|
+
A message can carry typed, serializable rich content beyond its markdown
|
|
541
|
+
`content`: an optional `blocks: MessageBlock[]`. `MessageBlock` is a
|
|
542
|
+
discriminated union on `kind` — `text`, `markdown`, `audio`, `video`,
|
|
543
|
+
`image`, `gallery`, `map`, `json`, `mermaid`, `code`, `lottie`, plus a
|
|
544
|
+
`custom` escape hatch. Payloads are plain JSON, so blocks survive history
|
|
545
|
+
persistence and SSE transport.
|
|
546
|
+
|
|
547
|
+
`<MessageBubble>` renders blocks **after** the markdown bubble, before
|
|
548
|
+
`toolCalls`. `text`/`markdown` blocks keep the bubble surface; media
|
|
549
|
+
blocks render full-width below it. Legacy messages (no `blocks`) render
|
|
550
|
+
byte-for-byte unchanged.
|
|
551
|
+
|
|
552
|
+
```ts
|
|
553
|
+
const msg: ChatMessage = {
|
|
554
|
+
id: 'm-42', role: 'assistant', createdAt: Date.now(), content: '',
|
|
555
|
+
blocks: [
|
|
556
|
+
{ kind: 'markdown', id: 'b1', markdown: 'Recording from **vps-audi**:' },
|
|
557
|
+
{ kind: 'audio', id: 'b2', src: 'https://cdn…/call.mp3', title: 'Call · 04:12' },
|
|
558
|
+
{ kind: 'map', id: 'b3', caption: 'Server', center: { lat: 52.52, lng: 13.405 }, zoom: 11 },
|
|
559
|
+
],
|
|
560
|
+
};
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### The registry
|
|
564
|
+
|
|
565
|
+
Each `kind` maps to a `BlockRenderer` in a `BlockRegistry` — same idea as
|
|
566
|
+
`AttachmentRendererMap`, but keyed by the discriminant. Built-in renderers
|
|
567
|
+
cover the in-house tools (`BUILTIN_BLOCK_REGISTRY`); each is reached
|
|
568
|
+
through a `React.lazy` chunk, so a chat with no map block never downloads
|
|
569
|
+
MapLibre.
|
|
570
|
+
|
|
571
|
+
Merge host overrides with `createBlockRegistry`:
|
|
572
|
+
|
|
573
|
+
```ts
|
|
574
|
+
const registry = createBlockRegistry({
|
|
575
|
+
json: (block) => <MyJsonCard data={block.data} />, // override one kind
|
|
576
|
+
});
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
Pass it via `<ChatRoot messages={{ blockRegistry }}>` — it is threaded
|
|
580
|
+
through context to every `<MessageBubble>`. `<MessageBubble>` also accepts
|
|
581
|
+
a direct `blockRegistry` prop (prop beats context) for standalone /
|
|
582
|
+
Storybook use. An unknown `kind` is skipped silently (lenient default);
|
|
583
|
+
`<MessageBlocks strict>` surfaces a dev notice. Each block is wrapped in
|
|
584
|
+
an error boundary so one malformed block can't blank the transcript.
|
|
585
|
+
|
|
586
|
+
Exports (`@djangocfg/ui-tools/chat`): `MessageBlocks`,
|
|
587
|
+
`createBlockRegistry`, `BUILTIN_BLOCK_REGISTRY`, and the `MessageBlock` /
|
|
588
|
+
`BlockRegistry` / `BlockRenderer` / `BlockRenderContext` types.
|
|
589
|
+
|
|
528
590
|
## Transport contract
|
|
529
591
|
|
|
530
592
|
A single I/O seam.
|
|
@@ -564,11 +626,169 @@ for await (const event of parseSSE(res, { map: createPydanticAISSEMap() })) {
|
|
|
564
626
|
}
|
|
565
627
|
```
|
|
566
628
|
|
|
567
|
-
##
|
|
629
|
+
## `ChatRoot` props
|
|
630
|
+
|
|
631
|
+
`<ChatRootProps>` groups ~13 keys by concern — no flat `composer*` namespace:
|
|
632
|
+
|
|
633
|
+
```tsx
|
|
634
|
+
<ChatRoot
|
|
635
|
+
// core wiring
|
|
636
|
+
transport={transport}
|
|
637
|
+
config={{ greeting: 'Hi!' }}
|
|
638
|
+
session={{ initialId, autoCreate: true }}
|
|
639
|
+
streaming
|
|
640
|
+
audio={{}}
|
|
641
|
+
debug
|
|
642
|
+
|
|
643
|
+
// presentation
|
|
644
|
+
appearance="compact" // 'compact' | 'full' — scales the whole chat
|
|
645
|
+
className="…"
|
|
646
|
+
listClassName="px-6 pt-6" // padding inside the scroll viewport
|
|
647
|
+
|
|
648
|
+
// composition (grouped)
|
|
649
|
+
slots={{
|
|
650
|
+
banner: <QuotaWarning />,
|
|
651
|
+
header: (ctx) => <MyHeader sessionId={ctx.sessionId} />, // node or fn
|
|
652
|
+
empty: ({ setValue, focus }) => <MyEmpty seed={setValue} />, // node or fn
|
|
653
|
+
jumpToLatest: <MyPill />,
|
|
654
|
+
}}
|
|
655
|
+
composer={{
|
|
656
|
+
size: 'lg', // 'sm' | 'md' | 'lg'
|
|
657
|
+
layout: 'stacked', // 'stacked' | 'inline'
|
|
658
|
+
className: '…',
|
|
659
|
+
hidden: false, // unmount the composer (human-in-the-loop pause)
|
|
660
|
+
showAttachmentButton: true,
|
|
661
|
+
onPickFiles: () => openPicker(),
|
|
662
|
+
slots: { actionsStart, actionsEnd, blockStart }, // ComposerSlots
|
|
663
|
+
footer: { showCounter: true } /* | false to hide */,
|
|
664
|
+
render: ({ composer, config }) => <MyComposer composer={composer} />,
|
|
665
|
+
}}
|
|
666
|
+
messages={{
|
|
667
|
+
render: (m, i) => <MyBubble message={m} />,
|
|
668
|
+
renderAfter: (m) => <SideChannelWidget messageId={m.id} />,
|
|
669
|
+
toolCallsProps: { defaultExpanded: true },
|
|
670
|
+
attachmentRenderers: { image: MyImageTile },
|
|
671
|
+
onAttachmentOpen: (a) => openLightbox(a),
|
|
672
|
+
}}
|
|
673
|
+
|
|
674
|
+
// behavior
|
|
675
|
+
focusOnEmptyClick // default true
|
|
676
|
+
|
|
677
|
+
// page context — extra metadata computed fresh per send
|
|
678
|
+
getDynamicMetadata={() => ({ pageContext })}
|
|
679
|
+
/>
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
`getDynamicMetadata` is forwarded to the `<ChatProvider>` that `ChatRoot`
|
|
683
|
+
creates; it is **ignored** when `ChatRoot` mounts under an ambient
|
|
684
|
+
provider — set it on that provider instead. Typically wired from
|
|
685
|
+
`usePageSnapshot().getChatMetadata` (see `src/lib/page-snapshot`).
|
|
686
|
+
|
|
687
|
+
The `slots.header` / `slots.empty` accept either a `ReactNode` or a render
|
|
688
|
+
function — there is no separate `renderHeader` / `renderEmpty` prop and no
|
|
689
|
+
"which one wins" precedence. `composer.render` fully replaces the built-in
|
|
690
|
+
`<Composer>`; it receives `{ composer, config }` — the live composer hook
|
|
691
|
+
plus the same config object. See **[Slots inventory](#slots-inventory)** below.
|
|
692
|
+
|
|
693
|
+
## Composer slot system
|
|
694
|
+
|
|
695
|
+
`<Composer>` is a single bordered input surface — textarea on top, an
|
|
696
|
+
action bar pinned to the bottom inside the same frame (attach
|
|
697
|
+
bottom-left, mic + send bottom-right). Two layouts:
|
|
568
698
|
|
|
569
|
-
|
|
699
|
+
- `layout="stacked"` (default) — Telegram-style two-tier surface.
|
|
700
|
+
- `layout="inline"` — compact single row. `size="sm"` defaults to this.
|
|
570
701
|
|
|
571
|
-
|
|
702
|
+
### Tier A — declarative actions
|
|
703
|
+
|
|
704
|
+
Pass `ComposerAction[]` arrays; the composer renders consistent,
|
|
705
|
+
correctly-aligned, fully-labelled buttons. Built-in send/stop/attach are
|
|
706
|
+
themselves descriptors — host extras land between attach and mic/send.
|
|
707
|
+
|
|
708
|
+
```tsx
|
|
709
|
+
<Composer
|
|
710
|
+
composer={composer}
|
|
711
|
+
composerSlots={{
|
|
712
|
+
actionsStart: [{ id: 'emoji', icon: <Smile />, label: 'Emoji', onClick }],
|
|
713
|
+
actionsEnd: [{ id: 'model', icon: <Sparkles />, label: 'Model', onClick }],
|
|
714
|
+
blockStart: <ReplyBanner />, // full-width row above the textarea
|
|
715
|
+
}}
|
|
716
|
+
/>
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
`ComposerAction` fields: `id`, `icon`, `label` (required — `aria-label` +
|
|
720
|
+
tooltip), `onClick`, `disabled?`, `pressed?` (→ `aria-pressed`),
|
|
721
|
+
`variant?`, `hideWhen?` (`streaming` | `empty` | `hasText` | `disabled`),
|
|
722
|
+
`order?`. Memoize the arrays so the action bar does not churn.
|
|
723
|
+
|
|
724
|
+
### Tier B — slot replacement
|
|
725
|
+
|
|
726
|
+
Replace a primitive entirely via `slots` / tweak its props via
|
|
727
|
+
`slotProps`:
|
|
728
|
+
|
|
729
|
+
```tsx
|
|
730
|
+
<Composer composer={composer}
|
|
731
|
+
slots={{ SendButton: MyBrandedSend, Textarea: MyEditor }}
|
|
732
|
+
slotProps={{ textarea: { className: 'font-mono' } }}
|
|
733
|
+
/>
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
Swappable: `SendButton`, `AttachButton`, `Textarea`, `ActionBar`.
|
|
737
|
+
|
|
738
|
+
### mic ↔ send swap
|
|
739
|
+
|
|
740
|
+
`micSendSwap` (default `true`) — Telegram behaviour: an action with
|
|
741
|
+
`id: 'mic'` (or `hideWhen: 'hasText'`) shows only while the draft is
|
|
742
|
+
empty; once there is text, send takes its place. Set `false` to keep
|
|
743
|
+
both visible (voice-message hosts).
|
|
744
|
+
|
|
745
|
+
### Footer toolbar
|
|
746
|
+
|
|
747
|
+
`<ComposerFooter>` is the quiet strip below the surface — three zones:
|
|
748
|
+
`start` (auto keyboard hint), `center` (host extras), `end` (auto char
|
|
749
|
+
counter that only appears near `maxLength`). Configure via the
|
|
750
|
+
`footer` prop on `<Composer>` or `composer.footer` on `<ChatRoot>`;
|
|
751
|
+
pass `false` to hide.
|
|
752
|
+
|
|
753
|
+
### Composer kit — ready-made slot widgets
|
|
754
|
+
|
|
755
|
+
ChatGPT / Gemini-style controls that drop straight into `composerSlots`.
|
|
756
|
+
All are exported from `@djangocfg/ui-tools/chat` and size-aware (they
|
|
757
|
+
inherit the composer `size` via context, no prop needed).
|
|
758
|
+
|
|
759
|
+
| Component | Slot | What it is |
|
|
760
|
+
|---|---|---|
|
|
761
|
+
| `<ComposerMenuButton items={MenuItem[]}>` | `inlineStart` | The ChatGPT `+` button — opens a declarative `MenuBuilder` dropdown (sections, submenus, shortcuts). |
|
|
762
|
+
| `<ComposerToolPill icon label active onRemove>` | `inlineStart` | Gemini-style capsule for a selected tool/mode with an optional `×` to clear it. |
|
|
763
|
+
| `<ComposerModelPicker value options onChange>` | `inlineEnd` | "Flash-Lite ▾" pill — opens a radio-group model picker. |
|
|
764
|
+
| `<ComposerBanner variant title description actions onDismiss>` | `blockStart` | Standalone notice bubble above the composer (upsell / quota / info). |
|
|
765
|
+
| `<ComposerRichTextarea mentions>` | `slots.Textarea` (Tier B) | Ready-made TipTap chat editor — unstyled, no toolbar, size-matched height, Enter-to-send, `@`-mention support. |
|
|
766
|
+
|
|
767
|
+
```tsx
|
|
768
|
+
<Composer
|
|
769
|
+
composer={composer}
|
|
770
|
+
composerSlots={{
|
|
771
|
+
blockStart: <ComposerBanner variant="upgrade" title="Free plan limit" … />,
|
|
772
|
+
inlineStart: <ComposerMenuButton items={MENU_ITEMS} />,
|
|
773
|
+
inlineEnd: <ComposerModelPicker value={model} options={MODELS} onChange={setModel} />,
|
|
774
|
+
}}
|
|
775
|
+
/>
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### `appearance` — compact vs full
|
|
779
|
+
|
|
780
|
+
`<Composer appearance="full">` layers extra spaciousness (radius,
|
|
781
|
+
padding, taller textarea, larger text) over the chosen `size` — for a
|
|
782
|
+
full-page ChatGPT/Gemini surface. `compact` (default) keeps the embedded
|
|
783
|
+
docked-chat geometry. Orthogonal to `size`; `<ChatRoot appearance="full">`
|
|
784
|
+
threads it to both the composer and the message bubbles.
|
|
785
|
+
|
|
786
|
+
### Click-to-focus surface
|
|
787
|
+
|
|
788
|
+
The whole input panel reads as a text field — the padding around the
|
|
789
|
+
editor shows a text caret and a `mousedown` on the bare surface (not a
|
|
790
|
+
button) focuses the editor. Works for both the plain `<textarea>` and the
|
|
791
|
+
TipTap (`contenteditable`) backend.
|
|
572
792
|
|
|
573
793
|
## Three usage patterns
|
|
574
794
|
|
|
@@ -627,7 +847,7 @@ type HttpTransportConfig, type MockTransportOptions, type ParseSSEOptions,
|
|
|
627
847
|
type PydanticAIChatTransportOpts, type PydanticAIEvent, type ToolIdQueue
|
|
628
848
|
|
|
629
849
|
// Hooks
|
|
630
|
-
useChat, useChatComposer,
|
|
850
|
+
useChat, useChatComposer, useChatHistory, useChatLayout,
|
|
631
851
|
useChatLightbox, useAutoFocusOnStreamEnd, useRegisterComposer,
|
|
632
852
|
useChatReset, useVisitorFingerprint, useChatUnread, useChatUnreadNotifier,
|
|
633
853
|
createBrowserNotifier, createCrossTabNotifier, createTitleRotator, createFaviconBadge,
|
|
@@ -682,22 +902,41 @@ JumpToLatest, StreamingIndicator
|
|
|
682
902
|
// `MessageList` exposes `atBottomThreshold` (default 120 px) and
|
|
683
903
|
// `scrollAnchorId` props for ChatGPT-style autoscroll — see "What you get".
|
|
684
904
|
|
|
905
|
+
// Composer kit — slot widgets + types
|
|
906
|
+
ComposerButton, ComposerActionBar, ComposerFooter,
|
|
907
|
+
ComposerMenuButton, ComposerToolPill, ComposerModelPicker,
|
|
908
|
+
ComposerBanner, ComposerRichTextarea, useComposerActions, useResolvedComposerSize
|
|
909
|
+
type ComposerProps, type ComposerSize, type ComposerAppearance,
|
|
910
|
+
type ComposerLayout, type ComposerAction, type ComposerActionVisibility,
|
|
911
|
+
type ComposerSlots, type ComposerSlotComponents, type ComposerSlotProps,
|
|
912
|
+
type ComposerFooterProps, type ComposerMenuButtonProps, type ComposerToolPillProps,
|
|
913
|
+
type ComposerModelPickerProps, type ComposerModelOption,
|
|
914
|
+
type ComposerBannerProps, type ComposerBannerAction, type ComposerRichTextareaProps,
|
|
915
|
+
type ComposerTextareaProps, type SendButtonProps, type AttachButtonProps
|
|
916
|
+
|
|
917
|
+
// Message blocks
|
|
918
|
+
MessageBlocks, createBlockRegistry, BUILTIN_BLOCK_REGISTRY
|
|
919
|
+
type MessageBlock, type BlockAppearance, type BlockRegistry,
|
|
920
|
+
type BlockRenderer, type BlockRenderContext
|
|
921
|
+
|
|
685
922
|
// Lazy preset
|
|
686
923
|
LazyChat
|
|
687
924
|
```
|
|
688
925
|
|
|
689
926
|
## Storybook
|
|
690
927
|
|
|
691
|
-
Stories
|
|
928
|
+
Stories are grouped into five feature sub-groups under `UI Tools/Chat`,
|
|
929
|
+
plus `Overview` (MDX) and `Showcase` (a one-screen living demo):
|
|
692
930
|
|
|
693
|
-
-
|
|
694
|
-
|
|
695
|
-
-
|
|
696
|
-
|
|
697
|
-
-
|
|
698
|
-
|
|
699
|
-
-
|
|
700
|
-
|
|
931
|
+
- **Getting Started** — `ChatRoot`, `Composition` (bring-your-own-layout),
|
|
932
|
+
`Appearance` (compact / full / huge).
|
|
933
|
+
- **Messages** — `Bubbles`, `Markdown`, `Message Blocks`,
|
|
934
|
+
`Tool Calls & Sources`, `Streaming`, `Personas`.
|
|
935
|
+
- **Composer** — `Playground`, `Layout & Actions`, `Menu & Tools`,
|
|
936
|
+
`Banner`, `Mentions`, `Speech & Attachments`.
|
|
937
|
+
- **Launcher** — `Playground`, `Parts`, `Header`,
|
|
938
|
+
`Unread & Notifications`.
|
|
939
|
+
- **Transports** — the three shipped transports with a stubbed `fetchImpl`.
|
|
701
940
|
|
|
702
941
|
---
|
|
703
942
|
|
|
@@ -705,37 +944,36 @@ Stories live next to components — open `@djangocfg/playground`:
|
|
|
705
944
|
|
|
706
945
|
## Slots inventory
|
|
707
946
|
|
|
708
|
-
|
|
|
947
|
+
| Key | Position | Type |
|
|
709
948
|
|---|---|---|
|
|
710
|
-
| `
|
|
711
|
-
| `
|
|
712
|
-
| `
|
|
713
|
-
| `
|
|
714
|
-
| `
|
|
715
|
-
| `
|
|
716
|
-
| `
|
|
717
|
-
| `
|
|
718
|
-
| `
|
|
719
|
-
| `
|
|
949
|
+
| `slots.banner` | Top of message list | `ReactNode` |
|
|
950
|
+
| `slots.header` | Above messages | `ReactNode \| (ctx) => ReactNode` |
|
|
951
|
+
| `slots.empty` | Empty state | `ReactNode \| ({ setValue, focus }) => ReactNode` |
|
|
952
|
+
| `slots.jumpToLatest` | Sticky overlay | `ReactNode` |
|
|
953
|
+
| `composer.slots.actionsStart` / `actionsEnd` | Composer action clusters | `ComposerAction[]` |
|
|
954
|
+
| `composer.slots.blockStart` | Above composer textarea | `ReactNode` |
|
|
955
|
+
| `composer.footer` | Below composer | `ComposerFooterProps \| false` |
|
|
956
|
+
| `composer.render` | Replace `<Composer>` | `({ composer, config }) => ReactNode` |
|
|
957
|
+
| `messages.render` | Replace each bubble | `(m, i) => ReactNode` |
|
|
958
|
+
| `messages.renderAfter` | Below every assistant bubble | `(m) => ReactNode` — fires for every message, independent of `toolCalls` |
|
|
720
959
|
|
|
721
960
|
Flags:
|
|
722
961
|
|
|
723
|
-
- `
|
|
724
|
-
- `hideToolCalls` — show only `renderAfterCalls` rich UI without raw tool panels.
|
|
962
|
+
- `composer.hidden` — agent-pause / human-in-the-loop pause; composer is unmounted.
|
|
725
963
|
|
|
726
|
-
### Which slot for product widgets — `renderAfterCalls` vs `
|
|
964
|
+
### Which slot for product widgets — `renderAfterCalls` vs `messages.renderAfter`?
|
|
727
965
|
|
|
728
966
|
Both put custom content under the assistant bubble; the difference is **what triggers them**.
|
|
729
967
|
|
|
730
968
|
- `renderAfterCalls` is gated on `message.toolCalls?.length > 0`. Use it when the widget is **derived from raw tool output** (read `call.output`) and you can rely on the host streaming the `tool_call` / `tool_result` SSE frames. Admin / dev flows typically work this way.
|
|
731
|
-
- `
|
|
969
|
+
- `messages.renderAfter` fires **for every message**, regardless of `toolCalls`. Use it when the widget is driven by a side channel — e.g. typed `ui_payload` SSE frames the host emits independently of the raw tool surface. This is the correct slot when the public-prod stream **hides** `tool_call` events for security: the message lands with `toolCalls === undefined`, so `renderAfterCalls` would never mount, but `messages.renderAfter` still runs and the widget renders from the side channel.
|
|
732
970
|
|
|
733
971
|
Recommended pairing for a "vehicle cards" / "tax breakdown" / "chart" widget on a public chat:
|
|
734
972
|
|
|
735
973
|
```tsx
|
|
736
974
|
<ChatRoot
|
|
737
975
|
transport={transport}
|
|
738
|
-
|
|
976
|
+
messages={{ renderAfter: (m) => <VehicleCardsForMessage messageId={m.id} /> }}
|
|
739
977
|
/>
|
|
740
978
|
```
|
|
741
979
|
|