@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,131 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<MessageBlocks>` — walks `message.blocks`, dispatches each through the
|
|
5
|
+
* registry, and renders it under the bubble (text/markdown in a bubble
|
|
6
|
+
* wrapper, media full-width). Per-block error isolation + unknown-kind
|
|
7
|
+
* fallback keep one bad block from blanking the transcript.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Component, type ReactNode } from 'react';
|
|
11
|
+
|
|
12
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
13
|
+
|
|
14
|
+
import type { MessageBlock } from '../../types/block';
|
|
15
|
+
import { useChatBubbleStyles } from '../../styles';
|
|
16
|
+
import { BUILTIN_BLOCK_REGISTRY, type BlockRegistry } from './registry';
|
|
17
|
+
|
|
18
|
+
export interface MessageBlocksProps {
|
|
19
|
+
blocks?: MessageBlock[];
|
|
20
|
+
/** Registry of `kind` → renderer. Defaults to `BUILTIN_BLOCK_REGISTRY`. */
|
|
21
|
+
registry?: BlockRegistry;
|
|
22
|
+
/** Spaciousness forwarded to renderers. */
|
|
23
|
+
appearance?: 'compact' | 'full';
|
|
24
|
+
isUser?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* When true, an unknown `kind` renders a visible dev notice. When
|
|
27
|
+
* false (default) it is skipped silently — production-lenient.
|
|
28
|
+
*/
|
|
29
|
+
strict?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Block kinds rendered inside a chat-bubble surface, not full-width. */
|
|
33
|
+
const PROSE_KINDS = new Set<MessageBlock['kind']>(['text', 'markdown']);
|
|
34
|
+
|
|
35
|
+
interface BoundaryProps {
|
|
36
|
+
kind: string;
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
}
|
|
39
|
+
interface BoundaryState {
|
|
40
|
+
failed: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Minimal per-block error boundary. */
|
|
44
|
+
class BlockErrorBoundary extends Component<BoundaryProps, BoundaryState> {
|
|
45
|
+
state: BoundaryState = { failed: false };
|
|
46
|
+
|
|
47
|
+
static getDerivedStateFromError(): BoundaryState {
|
|
48
|
+
return { failed: true };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
render() {
|
|
52
|
+
if (this.state.failed) {
|
|
53
|
+
return (
|
|
54
|
+
<div className="rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-muted-foreground">
|
|
55
|
+
Failed to render a {this.props.kind} block.
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return this.props.children;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function UnknownBlock({ kind }: { kind: string }) {
|
|
64
|
+
return (
|
|
65
|
+
<div className="rounded-lg border border-dashed border-border px-3 py-2 text-xs text-muted-foreground">
|
|
66
|
+
Unknown block kind: <code className="font-mono">{kind}</code>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function MessageBlocks({
|
|
72
|
+
blocks,
|
|
73
|
+
registry = BUILTIN_BLOCK_REGISTRY,
|
|
74
|
+
appearance = 'compact',
|
|
75
|
+
isUser = false,
|
|
76
|
+
strict = false,
|
|
77
|
+
}: MessageBlocksProps) {
|
|
78
|
+
const { surface: bubbleSurface } = useChatBubbleStyles(
|
|
79
|
+
isUser ? 'user' : 'assistant',
|
|
80
|
+
false,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (!blocks?.length) return null;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div
|
|
87
|
+
className={cn('mt-1.5 flex w-full flex-col gap-2', isUser && 'items-end')}
|
|
88
|
+
>
|
|
89
|
+
{blocks.map((block) => {
|
|
90
|
+
const renderer = registry[block.kind] as
|
|
91
|
+
| ((b: MessageBlock, c: { appearance: 'compact' | 'full'; isUser: boolean }) => ReactNode)
|
|
92
|
+
| undefined;
|
|
93
|
+
|
|
94
|
+
let body: ReactNode;
|
|
95
|
+
if (renderer) {
|
|
96
|
+
body = renderer(block, { appearance, isUser });
|
|
97
|
+
} else if (strict) {
|
|
98
|
+
body = <UnknownBlock kind={block.kind} />;
|
|
99
|
+
} else {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const isProse = PROSE_KINDS.has(block.kind);
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div key={block.id} data-block-kind={block.kind} className="w-full">
|
|
107
|
+
{block.caption ? (
|
|
108
|
+
<div className="mb-1 text-xs font-medium text-muted-foreground">
|
|
109
|
+
{block.caption}
|
|
110
|
+
</div>
|
|
111
|
+
) : null}
|
|
112
|
+
<BlockErrorBoundary kind={block.kind}>
|
|
113
|
+
{isProse ? (
|
|
114
|
+
<div
|
|
115
|
+
className={cn(
|
|
116
|
+
'inline-block max-w-full rounded-2xl px-3.5 py-2 text-sm',
|
|
117
|
+
bubbleSurface,
|
|
118
|
+
)}
|
|
119
|
+
>
|
|
120
|
+
{body}
|
|
121
|
+
</div>
|
|
122
|
+
) : (
|
|
123
|
+
body
|
|
124
|
+
)}
|
|
125
|
+
</BlockErrorBoundary>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
})}
|
|
129
|
+
</div>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Built-in block registry.
|
|
5
|
+
*
|
|
6
|
+
* Each media renderer is reached through a `React.lazy` indirection, so a
|
|
7
|
+
* chat that never shows (say) a map block never downloads MapLibre. This
|
|
8
|
+
* module must NOT statically import any tool — only `React.lazy(() =>
|
|
9
|
+
* import('./renderers/…'))` thunks. The `text`/`markdown` kinds are the
|
|
10
|
+
* sole exception: `MarkdownMessage` is already a Chat dependency.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { lazy, Suspense, type ComponentType } from 'react';
|
|
14
|
+
|
|
15
|
+
import { MarkdownMessage } from '../../../MarkdownMessage';
|
|
16
|
+
import type {
|
|
17
|
+
AudioBlock,
|
|
18
|
+
CodeBlock,
|
|
19
|
+
GalleryBlock,
|
|
20
|
+
ImageBlock,
|
|
21
|
+
JsonBlock,
|
|
22
|
+
LottieBlock,
|
|
23
|
+
MapBlock,
|
|
24
|
+
MarkdownBlock,
|
|
25
|
+
MermaidBlock,
|
|
26
|
+
MessageBlock,
|
|
27
|
+
TextBlock,
|
|
28
|
+
VideoBlock,
|
|
29
|
+
} from '../../types/block';
|
|
30
|
+
import type { BlockRegistry } from './registry';
|
|
31
|
+
import type { BlockRendererProps } from './renderers/types';
|
|
32
|
+
|
|
33
|
+
/** Minimal inline placeholder while a renderer chunk streams in. */
|
|
34
|
+
function BlockFallback() {
|
|
35
|
+
return (
|
|
36
|
+
<div
|
|
37
|
+
role="status"
|
|
38
|
+
aria-live="polite"
|
|
39
|
+
className="h-16 w-full animate-pulse rounded-xl bg-muted/60"
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Wrap a lazily-loaded renderer in a Suspense boundary. */
|
|
45
|
+
function lazyBlock<B extends MessageBlock>(
|
|
46
|
+
loader: () => Promise<{ default: ComponentType<BlockRendererProps<B>> }>,
|
|
47
|
+
) {
|
|
48
|
+
const Lazy = lazy(loader);
|
|
49
|
+
return function LazyBlockRenderer(props: BlockRendererProps<B>) {
|
|
50
|
+
return (
|
|
51
|
+
<Suspense fallback={<BlockFallback />}>
|
|
52
|
+
<Lazy {...props} />
|
|
53
|
+
</Suspense>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const AudioBlockRenderer = lazyBlock<AudioBlock>(() => import('./renderers/AudioBlock'));
|
|
59
|
+
const VideoBlockRenderer = lazyBlock<VideoBlock>(() => import('./renderers/VideoBlock'));
|
|
60
|
+
const ImageBlockRenderer = lazyBlock<ImageBlock>(() => import('./renderers/ImageBlock'));
|
|
61
|
+
const GalleryBlockRenderer = lazyBlock<GalleryBlock>(() => import('./renderers/GalleryBlock'));
|
|
62
|
+
const MapBlockRenderer = lazyBlock<MapBlock>(() => import('./renderers/MapBlock'));
|
|
63
|
+
const JsonBlockRenderer = lazyBlock<JsonBlock>(() => import('./renderers/JsonBlock'));
|
|
64
|
+
const MermaidBlockRenderer = lazyBlock<MermaidBlock>(() => import('./renderers/MermaidBlock'));
|
|
65
|
+
const CodeBlockRenderer = lazyBlock<CodeBlock>(() => import('./renderers/CodeBlock'));
|
|
66
|
+
const LottieBlockRenderer = lazyBlock<LottieBlock>(() => import('./renderers/LottieBlock'));
|
|
67
|
+
|
|
68
|
+
/** Render a `text` / `markdown` block as bubble-styled prose. */
|
|
69
|
+
function renderProse(content: string, isUser: boolean) {
|
|
70
|
+
return <MarkdownMessage content={content} isUser={isUser} size="sm" />;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The default registry. Host registries are merged over this via
|
|
75
|
+
* `createBlockRegistry(overrides)`.
|
|
76
|
+
*/
|
|
77
|
+
export const BUILTIN_BLOCK_REGISTRY: BlockRegistry = {
|
|
78
|
+
text: (block: TextBlock, ctx) => renderProse(block.text, ctx.isUser),
|
|
79
|
+
markdown: (block: MarkdownBlock, ctx) => renderProse(block.markdown, ctx.isUser),
|
|
80
|
+
audio: (block, ctx) => <AudioBlockRenderer block={block} ctx={ctx} />,
|
|
81
|
+
video: (block, ctx) => <VideoBlockRenderer block={block} ctx={ctx} />,
|
|
82
|
+
image: (block, ctx) => <ImageBlockRenderer block={block} ctx={ctx} />,
|
|
83
|
+
gallery: (block, ctx) => <GalleryBlockRenderer block={block} ctx={ctx} />,
|
|
84
|
+
map: (block, ctx) => <MapBlockRenderer block={block} ctx={ctx} />,
|
|
85
|
+
json: (block, ctx) => <JsonBlockRenderer block={block} ctx={ctx} />,
|
|
86
|
+
mermaid: (block, ctx) => <MermaidBlockRenderer block={block} ctx={ctx} />,
|
|
87
|
+
code: (block, ctx) => <CodeBlockRenderer block={block} ctx={ctx} />,
|
|
88
|
+
lottie: (block, ctx) => <LottieBlockRenderer block={block} ctx={ctx} />,
|
|
89
|
+
// `custom` has no built-in renderer — it falls through to the
|
|
90
|
+
// unknown-kind fallback unless the host registers one.
|
|
91
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Message blocks — typed rich-content slots rendered under a bubble. */
|
|
4
|
+
|
|
5
|
+
export { MessageBlocks, type MessageBlocksProps } from './MessageBlocks';
|
|
6
|
+
export {
|
|
7
|
+
createBlockRegistry,
|
|
8
|
+
BUILTIN_BLOCK_REGISTRY,
|
|
9
|
+
type BlockRegistry,
|
|
10
|
+
type BlockRenderer,
|
|
11
|
+
type BlockRenderContext,
|
|
12
|
+
} from './registry';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Block registry — maps a `MessageBlock['kind']` to a renderer.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors `AttachmentRendererMap`, but keyed by a discriminant instead of
|
|
7
|
+
* a predicate: cheaper, exhaustively typed, host-extensible. The built-in
|
|
8
|
+
* registry (`BUILTIN_BLOCK_REGISTRY`) lives in `./builtin` so this module
|
|
9
|
+
* stays free of any tool import.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ReactNode } from 'react';
|
|
13
|
+
|
|
14
|
+
import type { MessageBlock, MessageBlockKind, BlockAppearance } from '../../types/block';
|
|
15
|
+
import { BUILTIN_BLOCK_REGISTRY } from './builtin';
|
|
16
|
+
|
|
17
|
+
/** Context passed to every renderer alongside the block. */
|
|
18
|
+
export interface BlockRenderContext {
|
|
19
|
+
appearance: BlockAppearance;
|
|
20
|
+
isUser: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** A renderer for one `kind`. The union is narrowed by the registry key. */
|
|
24
|
+
export type BlockRenderer<K extends MessageBlockKind = MessageBlockKind> = (
|
|
25
|
+
block: Extract<MessageBlock, { kind: K }>,
|
|
26
|
+
ctx: BlockRenderContext,
|
|
27
|
+
) => ReactNode;
|
|
28
|
+
|
|
29
|
+
/** Partial map of `kind` → renderer. Missing kinds fall back to unknown. */
|
|
30
|
+
export type BlockRegistry = {
|
|
31
|
+
[K in MessageBlockKind]?: BlockRenderer<K>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Merge host overrides over the built-in registry. Overrides win by key —
|
|
36
|
+
* `{ map: myMapRenderer }` swaps only the map renderer.
|
|
37
|
+
*/
|
|
38
|
+
export function createBlockRegistry(overrides?: BlockRegistry): BlockRegistry {
|
|
39
|
+
return { ...BUILTIN_BLOCK_REGISTRY, ...overrides };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { BUILTIN_BLOCK_REGISTRY };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Audio block — thin chat wrapper around `AudioPlayer`. */
|
|
4
|
+
|
|
5
|
+
import { LazyPlayer } from '../../../../AudioPlayer/lazy';
|
|
6
|
+
import type { AudioBlock } from '../../../types/block';
|
|
7
|
+
import type { BlockRendererProps } from './types';
|
|
8
|
+
|
|
9
|
+
export default function AudioBlockRenderer({ block, ctx }: BlockRendererProps<AudioBlock>) {
|
|
10
|
+
const variant = block.variant ?? (ctx.appearance === 'compact' ? 'compact' : 'default');
|
|
11
|
+
return (
|
|
12
|
+
<LazyPlayer
|
|
13
|
+
src={block.src}
|
|
14
|
+
title={block.title}
|
|
15
|
+
artist={block.artist}
|
|
16
|
+
cover={block.cover}
|
|
17
|
+
variant={variant}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Code block — read-only syntax highlighting via `PrettyCode`. */
|
|
4
|
+
|
|
5
|
+
import { LazyPrettyCode } from '../../../../PrettyCode/lazy';
|
|
6
|
+
import type { Language } from '../../../../PrettyCode/lazy';
|
|
7
|
+
import type { CodeBlock } from '../../../types/block';
|
|
8
|
+
import type { BlockRendererProps } from './types';
|
|
9
|
+
|
|
10
|
+
export default function CodeBlockRenderer({ block, ctx }: BlockRendererProps<CodeBlock>) {
|
|
11
|
+
const compact = ctx.appearance === 'compact';
|
|
12
|
+
return (
|
|
13
|
+
<LazyPrettyCode
|
|
14
|
+
data={block.code}
|
|
15
|
+
language={block.language as Language}
|
|
16
|
+
isCompact={compact}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Gallery block — thin chat wrapper around `Gallery`. */
|
|
4
|
+
|
|
5
|
+
import { Gallery } from '../../../../Gallery';
|
|
6
|
+
import type { GalleryMediaItem } from '../../../../Gallery';
|
|
7
|
+
import type { GalleryBlock } from '../../../types/block';
|
|
8
|
+
import type { BlockRendererProps } from './types';
|
|
9
|
+
|
|
10
|
+
export default function GalleryBlockRenderer({ block, ctx }: BlockRendererProps<GalleryBlock>) {
|
|
11
|
+
const images: GalleryMediaItem[] = block.items.map((item) => ({
|
|
12
|
+
id: item.id,
|
|
13
|
+
src: item.src,
|
|
14
|
+
thumbnail: item.thumbnail,
|
|
15
|
+
alt: item.alt,
|
|
16
|
+
}));
|
|
17
|
+
return (
|
|
18
|
+
<div className="overflow-hidden rounded-xl border border-border">
|
|
19
|
+
<Gallery
|
|
20
|
+
images={images}
|
|
21
|
+
showThumbnails={ctx.appearance !== 'compact'}
|
|
22
|
+
enableLightbox
|
|
23
|
+
/>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Image block — interactive → `ImageViewer`, otherwise a plain `<img>`. */
|
|
4
|
+
|
|
5
|
+
import { LazyImageViewer } from '../../../../ImageViewer/lazy';
|
|
6
|
+
import type { ImageBlock } from '../../../types/block';
|
|
7
|
+
import type { BlockRendererProps } from './types';
|
|
8
|
+
|
|
9
|
+
export default function ImageBlockRenderer({ block }: BlockRendererProps<ImageBlock>) {
|
|
10
|
+
if (block.interactive) {
|
|
11
|
+
return (
|
|
12
|
+
<div className="overflow-hidden rounded-xl border border-border">
|
|
13
|
+
<LazyImageViewer
|
|
14
|
+
images={[{ file: { name: block.alt ?? 'image', path: block.src }, src: block.src }]}
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return (
|
|
20
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
21
|
+
<img
|
|
22
|
+
src={block.src}
|
|
23
|
+
alt={block.alt ?? ''}
|
|
24
|
+
className="max-h-96 w-auto max-w-full rounded-xl border border-border object-contain"
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** JSON block — thin chat wrapper around `JsonTree`. */
|
|
4
|
+
|
|
5
|
+
import { LazyJsonTree } from '../../../../JsonTree/lazy';
|
|
6
|
+
import type { JsonBlock } from '../../../types/block';
|
|
7
|
+
import type { BlockRendererProps } from './types';
|
|
8
|
+
|
|
9
|
+
export default function JsonBlockRenderer({ block, ctx }: BlockRendererProps<JsonBlock>) {
|
|
10
|
+
const mode = block.mode ?? (ctx.appearance === 'compact' ? 'compact' : 'full');
|
|
11
|
+
return <LazyJsonTree data={block.data} mode={mode} />;
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Lottie block — thin chat wrapper around `LottiePlayer`. */
|
|
4
|
+
|
|
5
|
+
import { LazyLottiePlayer } from '../../../../LottiePlayer/lazy';
|
|
6
|
+
import type { LottieBlock } from '../../../types/block';
|
|
7
|
+
import type { BlockRendererProps } from './types';
|
|
8
|
+
|
|
9
|
+
export default function LottieBlockRenderer({ block, ctx }: BlockRendererProps<LottieBlock>) {
|
|
10
|
+
return <LazyLottiePlayer src={block.src} size={ctx.appearance === 'compact' ? 'sm' : 'md'} />;
|
|
11
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Map block — thin chat wrapper around `LazyMapContainer` + `MapMarker`. */
|
|
4
|
+
|
|
5
|
+
import { LazyMapContainer, MapMarker } from '../../../../Map/lazy';
|
|
6
|
+
import type { MarkerData } from '../../../../Map/lazy';
|
|
7
|
+
import type { MapBlock } from '../../../types/block';
|
|
8
|
+
import type { BlockRendererProps } from './types';
|
|
9
|
+
|
|
10
|
+
export default function MapBlockRenderer({ block, ctx }: BlockRendererProps<MapBlock>) {
|
|
11
|
+
const height = ctx.appearance === 'compact' ? 240 : 380;
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
className="overflow-hidden rounded-xl border border-border"
|
|
15
|
+
style={{ height }}
|
|
16
|
+
>
|
|
17
|
+
<LazyMapContainer
|
|
18
|
+
initialViewport={{
|
|
19
|
+
longitude: block.center.lng,
|
|
20
|
+
latitude: block.center.lat,
|
|
21
|
+
zoom: block.zoom ?? 11,
|
|
22
|
+
}}
|
|
23
|
+
>
|
|
24
|
+
{block.markers?.map((m) => {
|
|
25
|
+
const marker: MarkerData = {
|
|
26
|
+
id: m.id,
|
|
27
|
+
longitude: m.lng,
|
|
28
|
+
latitude: m.lat,
|
|
29
|
+
data: m.label ? { label: m.label } : undefined,
|
|
30
|
+
};
|
|
31
|
+
return <MapMarker key={m.id} marker={marker} ariaLabel={m.label} />;
|
|
32
|
+
})}
|
|
33
|
+
</LazyMapContainer>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Mermaid block — thin chat wrapper around `Mermaid`. */
|
|
4
|
+
|
|
5
|
+
import { LazyMermaid } from '../../../../Mermaid/lazy';
|
|
6
|
+
import type { MermaidBlock } from '../../../types/block';
|
|
7
|
+
import type { BlockRendererProps } from './types';
|
|
8
|
+
|
|
9
|
+
export default function MermaidBlockRenderer({ block }: BlockRendererProps<MermaidBlock>) {
|
|
10
|
+
return <LazyMermaid chart={block.chart} />;
|
|
11
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Video block — thin chat wrapper around `VideoPlayer`. */
|
|
4
|
+
|
|
5
|
+
import { VideoPlayer } from '../../../../VideoPlayer/VideoPlayer';
|
|
6
|
+
import type { UrlSource } from '../../../../VideoPlayer/types';
|
|
7
|
+
import type { VideoBlock } from '../../../types/block';
|
|
8
|
+
import type { BlockRendererProps } from './types';
|
|
9
|
+
|
|
10
|
+
export default function VideoBlockRenderer({ block }: BlockRendererProps<VideoBlock>) {
|
|
11
|
+
// `poster` lives on the structured source, not as a top-level prop. A
|
|
12
|
+
// raw URL string is auto-classified, but to carry a poster we wrap it
|
|
13
|
+
// in a `UrlSource`.
|
|
14
|
+
const source: UrlSource | string = block.poster
|
|
15
|
+
? { type: 'url', url: block.src, poster: block.poster, title: block.title }
|
|
16
|
+
: block.src;
|
|
17
|
+
return (
|
|
18
|
+
<VideoPlayer
|
|
19
|
+
source={source}
|
|
20
|
+
aspectRatio={block.aspectRatio ?? 16 / 9}
|
|
21
|
+
className="overflow-hidden rounded-xl"
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Shared prop shape for every per-kind renderer module. */
|
|
2
|
+
import type { MessageBlock } from '../../../types/block';
|
|
3
|
+
import type { BlockRenderContext } from '../registry';
|
|
4
|
+
|
|
5
|
+
export interface BlockRendererProps<B extends MessageBlock = MessageBlock> {
|
|
6
|
+
block: B;
|
|
7
|
+
ctx: BlockRenderContext;
|
|
8
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/** Messages — chat transcript rendering (list, bubble, tool calls, …). */
|
|
4
|
+
|
|
4
5
|
export {
|
|
5
6
|
MessageList,
|
|
6
7
|
type MessageListProps,
|
|
@@ -8,7 +9,6 @@ export {
|
|
|
8
9
|
} from './MessageList';
|
|
9
10
|
export { MessageBubble, type MessageBubbleProps } from './MessageBubble';
|
|
10
11
|
export { MessageActions, type MessageActionsProps } from './MessageActions';
|
|
11
|
-
export { Composer, type ComposerProps, type ComposerSize } from './Composer';
|
|
12
12
|
export { Sources, type SourcesProps } from './Sources';
|
|
13
13
|
export { ToolCalls, type ToolCallsProps, type ToolPayloadKind } from './ToolCalls';
|
|
14
14
|
export {
|
|
@@ -22,8 +22,14 @@ export {
|
|
|
22
22
|
type AttachmentRendererArgs,
|
|
23
23
|
type AttachmentRendererMap,
|
|
24
24
|
} from './Attachments';
|
|
25
|
-
export { EmptyState, type EmptyStateProps } from './EmptyState';
|
|
26
|
-
export { ErrorBanner, type ErrorBannerProps } from './ErrorBanner';
|
|
27
25
|
export { JumpToLatest, type JumpToLatestProps } from './JumpToLatest';
|
|
28
26
|
export { StreamingIndicator, type StreamingIndicatorProps } from './StreamingIndicator';
|
|
29
|
-
export {
|
|
27
|
+
export {
|
|
28
|
+
MessageBlocks,
|
|
29
|
+
type MessageBlocksProps,
|
|
30
|
+
createBlockRegistry,
|
|
31
|
+
BUILTIN_BLOCK_REGISTRY,
|
|
32
|
+
type BlockRegistry,
|
|
33
|
+
type BlockRenderer,
|
|
34
|
+
type BlockRenderContext,
|
|
35
|
+
} from './blocks';
|