@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
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Chat — shared "light surface".
|
|
5
|
+
*
|
|
6
|
+
* The single source of truth for everything safe to load synchronously:
|
|
7
|
+
* types, runtime constants, pure `core` + transports, hooks (no UI),
|
|
8
|
+
* notifier, payload-dispatch, logger, utils, styles, context.
|
|
9
|
+
*
|
|
10
|
+
* Both root entry points re-export this module verbatim:
|
|
11
|
+
* - `index.ts` (`@djangocfg/ui-tools`) — `public` + heavy sync components
|
|
12
|
+
* - `lazy.tsx` (`@djangocfg/ui-tools/chat`) — `public` + Lazy* wrappers
|
|
13
|
+
*
|
|
14
|
+
* Heavy UI components must NOT be added here — they belong in `index.ts`
|
|
15
|
+
* (sync) and `lazy.tsx` (lazy) only.
|
|
16
|
+
*
|
|
17
|
+
* `./chat` is the only sanctioned Chat subpath — do not add new subpaths
|
|
18
|
+
* (avoids the dual-bundle / two-React-context trap).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// Types
|
|
22
|
+
export type {
|
|
23
|
+
ChatRole,
|
|
24
|
+
ChatMessage,
|
|
25
|
+
ChatPersona,
|
|
26
|
+
ChatToolCall,
|
|
27
|
+
ChatAttachment,
|
|
28
|
+
ChatSource,
|
|
29
|
+
ChatDisplayMode,
|
|
30
|
+
ChatUserContext,
|
|
31
|
+
ChatAssistantContext,
|
|
32
|
+
ChatPrefs,
|
|
33
|
+
ChatConfig,
|
|
34
|
+
ChatLabels,
|
|
35
|
+
ChatTransport,
|
|
36
|
+
ChatStreamEvent,
|
|
37
|
+
CreateSessionOptions,
|
|
38
|
+
SessionInfo,
|
|
39
|
+
HistoryPage,
|
|
40
|
+
StreamOptions,
|
|
41
|
+
SendOptions,
|
|
42
|
+
MessageBlock,
|
|
43
|
+
MessageBlockKind,
|
|
44
|
+
BlockAppearance,
|
|
45
|
+
TextBlock,
|
|
46
|
+
MarkdownBlock,
|
|
47
|
+
AudioBlock,
|
|
48
|
+
VideoBlock,
|
|
49
|
+
ImageBlock,
|
|
50
|
+
GalleryBlock,
|
|
51
|
+
MapBlock,
|
|
52
|
+
JsonBlock,
|
|
53
|
+
MermaidBlock,
|
|
54
|
+
CodeBlock,
|
|
55
|
+
LottieBlock,
|
|
56
|
+
CustomBlock,
|
|
57
|
+
} from './types';
|
|
58
|
+
export { DEFAULT_LABELS } from './types';
|
|
59
|
+
|
|
60
|
+
// Constants — plain runtime constants, no UI imports
|
|
61
|
+
export {
|
|
62
|
+
STORAGE_KEYS,
|
|
63
|
+
CSS_VARS,
|
|
64
|
+
DEFAULT_Z_INDEX,
|
|
65
|
+
LIMITS,
|
|
66
|
+
DEFAULT_SIDEBAR,
|
|
67
|
+
HOTKEYS,
|
|
68
|
+
CHAT_EVENT_NAME,
|
|
69
|
+
type ChatEventDetail,
|
|
70
|
+
} from './constants';
|
|
71
|
+
|
|
72
|
+
// Core (pure) — reducer / id / token buffer / persona / initials
|
|
73
|
+
export {
|
|
74
|
+
reducer,
|
|
75
|
+
initialState,
|
|
76
|
+
createId,
|
|
77
|
+
createTokenBuffer,
|
|
78
|
+
resolvePersona,
|
|
79
|
+
deriveInitials,
|
|
80
|
+
type ChatState,
|
|
81
|
+
type ChatAction,
|
|
82
|
+
type TokenBuffer,
|
|
83
|
+
} from './core';
|
|
84
|
+
|
|
85
|
+
// Transport — pure functions, no UI
|
|
86
|
+
export {
|
|
87
|
+
createHttpTransport,
|
|
88
|
+
createMockTransport,
|
|
89
|
+
parseSSE,
|
|
90
|
+
TransportError,
|
|
91
|
+
createPydanticAIChatTransport,
|
|
92
|
+
createToolIdQueue,
|
|
93
|
+
mapPydanticAIEvent,
|
|
94
|
+
createPydanticAISSEMap,
|
|
95
|
+
type HttpTransportConfig,
|
|
96
|
+
type MockTransportOptions,
|
|
97
|
+
type ParseSSEOptions,
|
|
98
|
+
type PydanticAIChatTransportOpts,
|
|
99
|
+
type PydanticAIEvent,
|
|
100
|
+
type ToolIdQueue,
|
|
101
|
+
} from './core/transport';
|
|
102
|
+
|
|
103
|
+
// Hooks — React state glue, no JSX / UI components
|
|
104
|
+
export {
|
|
105
|
+
useChat,
|
|
106
|
+
useChatComposer,
|
|
107
|
+
useChatHistory,
|
|
108
|
+
useChatLayout,
|
|
109
|
+
useChatAudio,
|
|
110
|
+
useAutoFocusOnStreamEnd,
|
|
111
|
+
useRegisterComposer,
|
|
112
|
+
useChatReset,
|
|
113
|
+
useVisitorFingerprint,
|
|
114
|
+
useChatDockPrefs,
|
|
115
|
+
DEFAULT_DOCK_PREFS,
|
|
116
|
+
useFocusOnEmptyClick,
|
|
117
|
+
useChatUnread,
|
|
118
|
+
useChatUnreadNotifier,
|
|
119
|
+
useChatLightbox,
|
|
120
|
+
type UseChatUnreadOptions,
|
|
121
|
+
type UseChatUnreadReturn,
|
|
122
|
+
type UseChatUnreadNotifierOptions,
|
|
123
|
+
type UseChatConfig,
|
|
124
|
+
type UseChatReturn,
|
|
125
|
+
type UseChatComposerOptions,
|
|
126
|
+
type UseChatComposerReturn,
|
|
127
|
+
type UseChatHistoryOptions,
|
|
128
|
+
type UseChatLayoutConfig,
|
|
129
|
+
type UseChatLayoutReturn,
|
|
130
|
+
type UseAutoFocusOnStreamEndOptions,
|
|
131
|
+
type Focusable,
|
|
132
|
+
type UseChatResetOptions,
|
|
133
|
+
type UseChatResetReturn,
|
|
134
|
+
type UseVisitorFingerprintOptions,
|
|
135
|
+
type ChatDockPrefs,
|
|
136
|
+
type UseChatDockPrefsOptions,
|
|
137
|
+
type UseChatDockPrefsReturn,
|
|
138
|
+
type UseFocusOnEmptyClickOptions,
|
|
139
|
+
type UseChatLightboxReturn,
|
|
140
|
+
type ChatLightboxState,
|
|
141
|
+
type ChatLightboxScope,
|
|
142
|
+
} from './hooks';
|
|
143
|
+
|
|
144
|
+
// Audio
|
|
145
|
+
export {
|
|
146
|
+
useChatAudioPrefs,
|
|
147
|
+
DEFAULT_CHAT_SOUNDS,
|
|
148
|
+
type ChatAudioEvent,
|
|
149
|
+
type ChatAudioSounds,
|
|
150
|
+
type ChatAudioConfig,
|
|
151
|
+
type UseChatAudioReturn,
|
|
152
|
+
} from './core/audio';
|
|
153
|
+
|
|
154
|
+
// Notifier — title rotation + favicon badge + page-visibility + cross-tab
|
|
155
|
+
export {
|
|
156
|
+
createBrowserNotifier,
|
|
157
|
+
createNoopNotifier,
|
|
158
|
+
createTitleRotator,
|
|
159
|
+
createFaviconBadge,
|
|
160
|
+
createCrossTabNotifier,
|
|
161
|
+
isPageHidden,
|
|
162
|
+
onVisibilityChange,
|
|
163
|
+
type ChatNotifier,
|
|
164
|
+
type BrowserNotifierOptions,
|
|
165
|
+
type TitleRotatorOptions,
|
|
166
|
+
type TitleMode,
|
|
167
|
+
type FaviconBadgeOptions,
|
|
168
|
+
type CrossTabNotifierOptions,
|
|
169
|
+
} from './notifier';
|
|
170
|
+
|
|
171
|
+
// Tool-call payload dispatcher — pure
|
|
172
|
+
export {
|
|
173
|
+
dispatchToolPayload,
|
|
174
|
+
isPlainObject,
|
|
175
|
+
isLatLng,
|
|
176
|
+
isGeoJSONFeatureCollection,
|
|
177
|
+
isStringValue,
|
|
178
|
+
type ToolPayloadMatcher,
|
|
179
|
+
type ToolPayloadFallback,
|
|
180
|
+
} from './core/payload-dispatch';
|
|
181
|
+
|
|
182
|
+
// Dev logger (consola-based, namespace "chat:*")
|
|
183
|
+
export { getChatLogger, type ChatLogger, type ChatLogScope } from './core/logger';
|
|
184
|
+
|
|
185
|
+
// Utils — pure helpers
|
|
186
|
+
export { sanitizeDraft, isSubmittableDraft, collectImageAttachments } from './utils';
|
|
187
|
+
|
|
188
|
+
// Styles — role-aware className tokens + hooks
|
|
189
|
+
export {
|
|
190
|
+
BUBBLE_SURFACE,
|
|
191
|
+
ANCHOR,
|
|
192
|
+
TOGGLE,
|
|
193
|
+
DESTRUCTIVE_SURFACE,
|
|
194
|
+
TOOL_CALL,
|
|
195
|
+
useChatBubbleStyles,
|
|
196
|
+
useChatRoleStyles,
|
|
197
|
+
useChatDestructiveStyles,
|
|
198
|
+
type ChatBubbleSurface,
|
|
199
|
+
type ChatBubbleStyles,
|
|
200
|
+
type ChatRoleStyles,
|
|
201
|
+
type ChatDestructiveStyles,
|
|
202
|
+
} from './styles';
|
|
203
|
+
|
|
204
|
+
// Context — provider + hooks for callers wiring custom chat shells
|
|
205
|
+
export {
|
|
206
|
+
ChatProvider,
|
|
207
|
+
useChatContext,
|
|
208
|
+
useChatContextOptional,
|
|
209
|
+
type ChatContextValue,
|
|
210
|
+
type ChatProviderProps,
|
|
211
|
+
type ComposerHandle,
|
|
212
|
+
} from './context';
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { type ReactNode, useMemo, useRef, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
6
|
+
|
|
7
|
+
import type { ChatAttachment, ChatConfig, ChatMessage, ChatTransport } from '../types';
|
|
8
|
+
import type { ChatAudioConfig } from '../core/audio/types';
|
|
9
|
+
import {
|
|
10
|
+
ChatProvider,
|
|
11
|
+
useChatContext,
|
|
12
|
+
useChatContextOptional,
|
|
13
|
+
type ChatContextValue,
|
|
14
|
+
} from '../context';
|
|
15
|
+
import { useChatComposer, type UseChatComposerReturn } from '../hooks/useChatComposer';
|
|
16
|
+
import { useFocusOnEmptyClick } from '../hooks/useFocusOnEmptyClick';
|
|
17
|
+
import { Composer } from '../composer/Composer';
|
|
18
|
+
import type {
|
|
19
|
+
ComposerAppearance,
|
|
20
|
+
ComposerFooterProps,
|
|
21
|
+
ComposerLayout,
|
|
22
|
+
ComposerSize,
|
|
23
|
+
ComposerSlots,
|
|
24
|
+
} from '../composer/types';
|
|
25
|
+
import { EmptyState } from './EmptyState';
|
|
26
|
+
import { ErrorBanner } from './ErrorBanner';
|
|
27
|
+
import { JumpToLatest } from '../messages/JumpToLatest';
|
|
28
|
+
import { MessageBubble } from '../messages/MessageBubble';
|
|
29
|
+
import { MessageList, type MessageListHandle } from '../messages/MessageList';
|
|
30
|
+
import type { AttachmentRendererMap } from '../messages/Attachments';
|
|
31
|
+
import type { BlockRegistry } from '../messages/blocks';
|
|
32
|
+
import type { ToolCallsProps } from '../messages/ToolCalls';
|
|
33
|
+
|
|
34
|
+
/** Empty-state render API — seeds + focuses the composer from prompt chips. */
|
|
35
|
+
export interface ChatEmptyApi {
|
|
36
|
+
setValue: (v: string) => void;
|
|
37
|
+
focus: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Full-replacement render API for the composer slot — the live composer
|
|
41
|
+
* hook plus the resolved {@link ChatComposerConfig} that produced it.
|
|
42
|
+
* One source of truth; no hand-mirrored field list. */
|
|
43
|
+
export interface ChatComposerRenderApi {
|
|
44
|
+
composer: UseChatComposerReturn;
|
|
45
|
+
config: ChatComposerConfig;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Composer-region configuration for `<ChatRoot composer={…}>`. Groups
|
|
49
|
+
* every knob that forwards into the built-in `<Composer>`. */
|
|
50
|
+
export interface ChatComposerConfig {
|
|
51
|
+
/** Visual size variant. Default `md`. */
|
|
52
|
+
size?: ComposerSize;
|
|
53
|
+
/** Input-surface layout. Default `stacked` (`sm` → `inline`). */
|
|
54
|
+
layout?: ComposerLayout;
|
|
55
|
+
/** Extra className on the `<Composer>` wrapper. */
|
|
56
|
+
className?: string;
|
|
57
|
+
/** Hide the composer entirely (e.g. while waiting for human approval). */
|
|
58
|
+
hidden?: boolean;
|
|
59
|
+
/** Show the paperclip "attach" button. */
|
|
60
|
+
showAttachmentButton?: boolean;
|
|
61
|
+
/** Called when the user clicks the attach button. */
|
|
62
|
+
onPickFiles?: () => void;
|
|
63
|
+
/** Tier A declarative slots — action clusters + raw-node escape hatches. */
|
|
64
|
+
slots?: ComposerSlots;
|
|
65
|
+
/** Footer toolbar below the input surface. `false` hides it. */
|
|
66
|
+
footer?: ComposerFooterProps | false;
|
|
67
|
+
/** Full replacement — receives the live composer hook + this config. */
|
|
68
|
+
render?: (api: ChatComposerRenderApi) => ReactNode;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Named ReactNode slots for `<ChatRoot slots={…}>`. The `header` / `empty`
|
|
72
|
+
* slots accept either a node or a render fn — no separate `renderX` prop. */
|
|
73
|
+
export interface ChatSlots {
|
|
74
|
+
/** Sticky banner above the message list (e.g. quota warning). */
|
|
75
|
+
banner?: ReactNode;
|
|
76
|
+
/** Header row below the banner — node, or a fn given the chat context. */
|
|
77
|
+
header?: ReactNode | ((ctx: ChatContextValue) => ReactNode);
|
|
78
|
+
/** Replaces the default `<EmptyState>` — node, or a fn given a seed API. */
|
|
79
|
+
empty?: ReactNode | ((api: ChatEmptyApi) => ReactNode);
|
|
80
|
+
/** Replaces the default `<JumpToLatest>` floating pill. */
|
|
81
|
+
jumpToLatest?: ReactNode;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Message-rendering configuration for `<ChatRoot messages={…}>`. */
|
|
85
|
+
export interface ChatMessagesConfig {
|
|
86
|
+
/** Replace `<MessageBubble>` per message. */
|
|
87
|
+
render?: (m: ChatMessage, i: number) => ReactNode;
|
|
88
|
+
/**
|
|
89
|
+
* Render arbitrary content beneath every default `<MessageBubble>`
|
|
90
|
+
* (not invoked when `render` is set — the host owns layout then).
|
|
91
|
+
*/
|
|
92
|
+
renderAfter?: (m: ChatMessage) => ReactNode;
|
|
93
|
+
/** Forwarded into `<MessageBubble toolCallsProps>` to swap payload renderers. */
|
|
94
|
+
toolCallsProps?: Omit<ToolCallsProps, 'calls'>;
|
|
95
|
+
/** Per-type attachment renderers — `{ image, audio, video, file, default }`. */
|
|
96
|
+
attachmentRenderers?: AttachmentRendererMap;
|
|
97
|
+
/** Called when an attachment tile is clicked (e.g. open lightbox). */
|
|
98
|
+
onAttachmentOpen?: (attachment: ChatAttachment) => void;
|
|
99
|
+
/**
|
|
100
|
+
* Registry of `kind` → renderer for `message.blocks`. Merge host
|
|
101
|
+
* overrides over the defaults with `createBlockRegistry({ … })`.
|
|
102
|
+
* Omit to use `BUILTIN_BLOCK_REGISTRY`.
|
|
103
|
+
*/
|
|
104
|
+
blockRegistry?: BlockRegistry;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface ChatRootProps {
|
|
108
|
+
// ---- core wiring -------------------------------------------------------
|
|
109
|
+
/**
|
|
110
|
+
* Transport. Required UNLESS `<ChatRoot>` is rendered inside an
|
|
111
|
+
* existing `<ChatProvider>` (e.g. mounted by `<ChatLauncher>`), in
|
|
112
|
+
* which case the ambient provider is reused and `transport` is
|
|
113
|
+
* ignored.
|
|
114
|
+
*/
|
|
115
|
+
transport?: ChatTransport;
|
|
116
|
+
config?: ChatConfig;
|
|
117
|
+
/** Session wiring — pre-existing id to attach, auto-create toggle. */
|
|
118
|
+
session?: { initialId?: string; autoCreate?: boolean };
|
|
119
|
+
streaming?: boolean;
|
|
120
|
+
/** Audio-trigger configuration. Off by default (no `sounds` map). */
|
|
121
|
+
audio?: ChatAudioConfig;
|
|
122
|
+
/**
|
|
123
|
+
* Verbose dev-mode logging via `consola` (namespace `chat:*`).
|
|
124
|
+
* Defaults to `isDev` from `@djangocfg/ui-core/lib`.
|
|
125
|
+
*/
|
|
126
|
+
debug?: boolean;
|
|
127
|
+
|
|
128
|
+
// ---- presentation ------------------------------------------------------
|
|
129
|
+
/** Spaciousness of the whole chat — scales bubbles + composer.
|
|
130
|
+
* `compact` (default) for embedded chat, `full` for full-page. */
|
|
131
|
+
appearance?: ComposerAppearance;
|
|
132
|
+
/** Extra className on the `<ChatRoot>` wrapper. */
|
|
133
|
+
className?: string;
|
|
134
|
+
/** Extra className forwarded to the `<MessageList>` scroll container. */
|
|
135
|
+
listClassName?: string;
|
|
136
|
+
|
|
137
|
+
// ---- composition (grouped) --------------------------------------------
|
|
138
|
+
/** Named ReactNode slots — banner, header, empty, jumpToLatest. */
|
|
139
|
+
slots?: ChatSlots;
|
|
140
|
+
/** Composer-region configuration. */
|
|
141
|
+
composer?: ChatComposerConfig;
|
|
142
|
+
/** Message-rendering configuration. */
|
|
143
|
+
messages?: ChatMessagesConfig;
|
|
144
|
+
|
|
145
|
+
// ---- behavior ----------------------------------------------------------
|
|
146
|
+
/**
|
|
147
|
+
* Click in the message area → focus the composer (Slack / ChatGPT).
|
|
148
|
+
* @default true
|
|
149
|
+
*/
|
|
150
|
+
focusOnEmptyClick?: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Contribute extra transport metadata, computed fresh per send.
|
|
153
|
+
* Forwarded to the `<ChatProvider>` this component creates — ignored
|
|
154
|
+
* when mounted under an ambient provider (set it on that provider
|
|
155
|
+
* instead). Used to attach the page-context snapshot.
|
|
156
|
+
*/
|
|
157
|
+
getDynamicMetadata?: () => Record<string, unknown> | undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function ChatRoot(props: ChatRootProps) {
|
|
161
|
+
const {
|
|
162
|
+
transport,
|
|
163
|
+
config,
|
|
164
|
+
session,
|
|
165
|
+
streaming,
|
|
166
|
+
audio,
|
|
167
|
+
debug,
|
|
168
|
+
className,
|
|
169
|
+
listClassName,
|
|
170
|
+
getDynamicMetadata,
|
|
171
|
+
...rest
|
|
172
|
+
} = props;
|
|
173
|
+
// When mounted under a launcher-owned `<ChatProvider>`, reuse that
|
|
174
|
+
// provider instead of wrapping in a second one. This lets host code
|
|
175
|
+
// freely nest `<ChatRoot>` inside `<ChatLauncher>` without losing
|
|
176
|
+
// `headerSlots` access to the same session / clearMessages / audio.
|
|
177
|
+
const ambient = useChatContextOptional();
|
|
178
|
+
if (ambient) {
|
|
179
|
+
return <ChatRootShell className={className} listClassName={listClassName} parts={rest} />;
|
|
180
|
+
}
|
|
181
|
+
if (!transport) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
'<ChatRoot> requires `transport` when mounted outside a <ChatProvider>.',
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return (
|
|
187
|
+
<ChatProvider
|
|
188
|
+
transport={transport}
|
|
189
|
+
config={config}
|
|
190
|
+
initialSessionId={session?.initialId}
|
|
191
|
+
autoCreateSession={session?.autoCreate}
|
|
192
|
+
streaming={streaming}
|
|
193
|
+
audio={audio}
|
|
194
|
+
debug={debug}
|
|
195
|
+
blockRegistry={rest.messages?.blockRegistry}
|
|
196
|
+
getDynamicMetadata={getDynamicMetadata}
|
|
197
|
+
>
|
|
198
|
+
<ChatRootShell className={className} listClassName={listClassName} parts={rest} />
|
|
199
|
+
</ChatProvider>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
type ChatRootParts = Pick<
|
|
204
|
+
ChatRootProps,
|
|
205
|
+
'appearance' | 'slots' | 'composer' | 'messages' | 'focusOnEmptyClick'
|
|
206
|
+
>;
|
|
207
|
+
|
|
208
|
+
interface ChatRootShellProps {
|
|
209
|
+
className?: string;
|
|
210
|
+
listClassName?: string;
|
|
211
|
+
parts: ChatRootParts;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function ChatRootShell({ className, listClassName, parts }: ChatRootShellProps) {
|
|
215
|
+
const { slots = {}, composer: composerConfig = {}, messages = {} } = parts;
|
|
216
|
+
const chat = useChatContext();
|
|
217
|
+
const composer = useChatComposer({
|
|
218
|
+
onSubmit: (content, attachments) => chat.sendMessage(content, attachments),
|
|
219
|
+
disabled: chat.isStreaming,
|
|
220
|
+
});
|
|
221
|
+
const onMessagesMouseUp = useFocusOnEmptyClick({
|
|
222
|
+
enabled: parts.focusOnEmptyClick !== false,
|
|
223
|
+
});
|
|
224
|
+
// Stream-end composer re-focus is handled by `<ChatProvider>` — it
|
|
225
|
+
// covers every usage pattern, not just `ChatRoot`.
|
|
226
|
+
// MessageList (virtuoso) owns the scroll viewport. We talk to it via
|
|
227
|
+
// the imperative handle (scrollToBottom) and the `onAtBottomChange`
|
|
228
|
+
// callback (drives the pill).
|
|
229
|
+
const listRef = useRef<MessageListHandle | null>(null);
|
|
230
|
+
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
231
|
+
// The id of the most-recent user-sent message — passed as
|
|
232
|
+
// `scrollAnchorId` so every send re-anchors the viewport.
|
|
233
|
+
const lastUserMessageId = useMemo(() => {
|
|
234
|
+
const msgs = chat.messages;
|
|
235
|
+
for (let i = msgs.length - 1; i >= 0; i -= 1) {
|
|
236
|
+
if (msgs[i].role === 'user') return msgs[i].id;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}, [chat.messages]);
|
|
240
|
+
const handleStartReached = chat.hasMore && !chat.isLoadingMore
|
|
241
|
+
? () => void chat.loadMore()
|
|
242
|
+
: undefined;
|
|
243
|
+
|
|
244
|
+
const greeting = chat.config.greeting ?? 'How can I help?';
|
|
245
|
+
const description = chat.config.description;
|
|
246
|
+
const suggestions = chat.config.suggestions;
|
|
247
|
+
|
|
248
|
+
const headerNode = typeof slots.header === 'function'
|
|
249
|
+
? slots.header(chat)
|
|
250
|
+
: slots.header;
|
|
251
|
+
|
|
252
|
+
const emptyNode = typeof slots.empty === 'function'
|
|
253
|
+
? slots.empty({ setValue: composer.setValue, focus: composer.focus })
|
|
254
|
+
: (slots.empty ?? (
|
|
255
|
+
<EmptyState
|
|
256
|
+
greeting={greeting}
|
|
257
|
+
description={description}
|
|
258
|
+
suggestions={suggestions}
|
|
259
|
+
onPickSuggestion={(prompt) => {
|
|
260
|
+
composer.setValue(prompt);
|
|
261
|
+
composer.focus();
|
|
262
|
+
}}
|
|
263
|
+
/>
|
|
264
|
+
));
|
|
265
|
+
|
|
266
|
+
// `appearance` scales the whole chat (bubbles + composer). Default
|
|
267
|
+
// `compact` keeps every embedded chat pixel-identical.
|
|
268
|
+
const appearance: ComposerAppearance = parts.appearance ?? 'compact';
|
|
269
|
+
|
|
270
|
+
const renderItem = messages.render
|
|
271
|
+
?? ((m: ChatMessage) => (
|
|
272
|
+
<MessageBubble
|
|
273
|
+
key={m.id}
|
|
274
|
+
message={m}
|
|
275
|
+
appearance={appearance}
|
|
276
|
+
toolCallsProps={messages.toolCallsProps}
|
|
277
|
+
attachmentRenderers={messages.attachmentRenderers}
|
|
278
|
+
blockRegistry={messages.blockRegistry}
|
|
279
|
+
onAttachmentOpen={messages.onAttachmentOpen}
|
|
280
|
+
renderAfterMessage={messages.renderAfter}
|
|
281
|
+
onCopy={() => copy(m.content)}
|
|
282
|
+
onRegenerate={() => void chat.regenerate(m.id)}
|
|
283
|
+
onDelete={() => chat.deleteMessage(m.id)}
|
|
284
|
+
/>
|
|
285
|
+
));
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
<div className={cn('relative flex h-full min-h-0 flex-col overflow-hidden', className)}>
|
|
289
|
+
{slots.banner ?? null}
|
|
290
|
+
{headerNode ?? null}
|
|
291
|
+
<div className="relative flex min-h-0 flex-1 flex-col" onMouseUp={onMessagesMouseUp}>
|
|
292
|
+
<ErrorBanner
|
|
293
|
+
error={chat.error}
|
|
294
|
+
onDismiss={chat.error ? () => chat.clearMessages() : undefined}
|
|
295
|
+
onRetry={chat.error ? () => void chat.regenerate() : undefined}
|
|
296
|
+
/>
|
|
297
|
+
<MessageList
|
|
298
|
+
ref={listRef}
|
|
299
|
+
renderItem={renderItem}
|
|
300
|
+
appearance={appearance}
|
|
301
|
+
renderEmpty={() => <>{emptyNode}</>}
|
|
302
|
+
className={listClassName}
|
|
303
|
+
onStartReached={handleStartReached}
|
|
304
|
+
onAtBottomChange={setIsAtBottom}
|
|
305
|
+
scrollAnchorId={lastUserMessageId}
|
|
306
|
+
/>
|
|
307
|
+
<div className="pointer-events-none absolute inset-x-0 bottom-2 flex justify-center">
|
|
308
|
+
{slots.jumpToLatest ?? (
|
|
309
|
+
<JumpToLatest
|
|
310
|
+
visible={!isAtBottom}
|
|
311
|
+
onClick={() => listRef.current?.scrollToBottom(true)}
|
|
312
|
+
/>
|
|
313
|
+
)}
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
{!composerConfig.hidden && (
|
|
317
|
+
composerConfig.render
|
|
318
|
+
? composerConfig.render({ composer, config: composerConfig })
|
|
319
|
+
: (
|
|
320
|
+
<Composer
|
|
321
|
+
composer={composer}
|
|
322
|
+
placeholder={chat.config.placeholder}
|
|
323
|
+
showAttachmentButton={composerConfig.showAttachmentButton}
|
|
324
|
+
onPickFiles={composerConfig.onPickFiles}
|
|
325
|
+
size={composerConfig.size}
|
|
326
|
+
appearance={appearance}
|
|
327
|
+
layout={composerConfig.layout}
|
|
328
|
+
className={composerConfig.className}
|
|
329
|
+
composerSlots={composerConfig.slots}
|
|
330
|
+
footer={composerConfig.footer ?? {}}
|
|
331
|
+
/>
|
|
332
|
+
)
|
|
333
|
+
)}
|
|
334
|
+
</div>
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function copy(text: string) {
|
|
339
|
+
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
340
|
+
void navigator.clipboard.writeText(text);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// re-export for convenience: composer hook return is a common slot dependency
|
|
345
|
+
export type { UseChatComposerReturn };
|
|
@@ -24,7 +24,9 @@ export function EmptyState({
|
|
|
24
24
|
<div className="grid size-10 place-items-center rounded-full bg-muted">
|
|
25
25
|
<Sparkles aria-hidden className="size-5 text-muted-foreground" />
|
|
26
26
|
</div>
|
|
27
|
-
{greeting ?
|
|
27
|
+
{greeting ? (
|
|
28
|
+
<h2 className="text-base font-semibold text-foreground">{greeting}</h2>
|
|
29
|
+
) : null}
|
|
28
30
|
{description ? (
|
|
29
31
|
<p className="max-w-md text-sm text-muted-foreground">{description}</p>
|
|
30
32
|
) : null}
|
|
@@ -35,7 +37,7 @@ export function EmptyState({
|
|
|
35
37
|
key={s.prompt}
|
|
36
38
|
type="button"
|
|
37
39
|
onClick={() => onPickSuggestion?.(s.prompt)}
|
|
38
|
-
className="rounded-lg border border-border bg-background/60 px-3 py-2 text-left text-xs hover:bg-accent"
|
|
40
|
+
className="rounded-lg border border-border bg-background/60 px-3 py-2 text-left text-xs text-foreground hover:bg-accent hover:text-accent-foreground"
|
|
39
41
|
>
|
|
40
42
|
{s.label}
|
|
41
43
|
</button>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Shell — composition-root pieces (ChatRoot preset, empty / error states). */
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
ChatRoot,
|
|
7
|
+
type ChatRootProps,
|
|
8
|
+
type ChatComposerConfig,
|
|
9
|
+
type ChatComposerRenderApi,
|
|
10
|
+
type ChatSlots,
|
|
11
|
+
type ChatMessagesConfig,
|
|
12
|
+
type ChatEmptyApi,
|
|
13
|
+
} from './ChatRoot';
|
|
14
|
+
export { EmptyState, type EmptyStateProps } from './EmptyState';
|
|
15
|
+
export { ErrorBanner, type ErrorBannerProps } from './ErrorBanner';
|