@djangocfg/ui-tools 2.1.404 → 2.1.408
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 -11
- 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 +13 -16
- 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/lazy.tsx +13 -27
- package/src/tools/AudioPlayer/parts/Meta/TimeDisplay.tsx +2 -5
- package/src/tools/Chat/README.md +267 -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 +326 -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 +106 -26
- 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/README.md +87 -230
- package/src/tools/VideoPlayer/VideoPlayer.tsx +82 -0
- package/src/tools/VideoPlayer/canvas/canvas-dispatcher.tsx +34 -0
- package/src/tools/VideoPlayer/canvas/hls-canvas.tsx +39 -0
- package/src/tools/VideoPlayer/canvas/iframe-canvas.tsx +33 -0
- package/src/tools/VideoPlayer/canvas/index.ts +12 -0
- package/src/tools/VideoPlayer/canvas/jsx-augmentation.ts +47 -0
- package/src/tools/VideoPlayer/canvas/native-canvas.tsx +38 -0
- package/src/tools/VideoPlayer/canvas/vimeo-canvas.tsx +40 -0
- package/src/tools/VideoPlayer/canvas/youtube-canvas.tsx +78 -0
- package/src/tools/VideoPlayer/index.ts +51 -65
- package/src/tools/VideoPlayer/lazy.tsx +11 -54
- package/src/tools/VideoPlayer/parts/controls-bar.tsx +35 -0
- package/src/tools/VideoPlayer/parts/fullscreen.tsx +19 -0
- package/src/tools/VideoPlayer/parts/index.ts +15 -0
- package/src/tools/VideoPlayer/parts/pip.tsx +19 -0
- package/src/tools/VideoPlayer/parts/play-button.tsx +19 -0
- package/src/tools/VideoPlayer/parts/playback-rate.tsx +31 -0
- package/src/tools/VideoPlayer/parts/poster.tsx +3 -0
- package/src/tools/VideoPlayer/parts/seek-bar.tsx +26 -0
- package/src/tools/VideoPlayer/parts/volume.tsx +32 -0
- package/src/tools/VideoPlayer/styles/video-player.css +141 -0
- package/src/tools/VideoPlayer/types.ts +82 -0
- package/src/tools/VideoPlayer/utils/parse-embed-url.ts +70 -0
- package/src/tools/VideoPlayer/utils/vimeo-id.ts +24 -0
- package/src/tools/VideoPlayer/utils/youtube-id.ts +64 -0
- package/src/tools/index.ts +37 -29
- 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/VideoPlayer/components/VideoControls.tsx +0 -138
- package/src/tools/VideoPlayer/components/VideoErrorFallback.tsx +0 -172
- package/src/tools/VideoPlayer/components/VideoPlayer.tsx +0 -201
- package/src/tools/VideoPlayer/components/index.ts +0 -14
- package/src/tools/VideoPlayer/context/VideoPlayerContext.tsx +0 -52
- package/src/tools/VideoPlayer/context/index.ts +0 -8
- package/src/tools/VideoPlayer/hooks/index.ts +0 -12
- package/src/tools/VideoPlayer/hooks/useVideoPlayerSettings.ts +0 -71
- package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +0 -117
- package/src/tools/VideoPlayer/providers/NativeProvider.tsx +0 -284
- package/src/tools/VideoPlayer/providers/StreamProvider.tsx +0 -505
- package/src/tools/VideoPlayer/providers/VidstackProvider.tsx +0 -397
- package/src/tools/VideoPlayer/providers/index.ts +0 -8
- package/src/tools/VideoPlayer/types/index.ts +0 -38
- package/src/tools/VideoPlayer/types/player.ts +0 -116
- package/src/tools/VideoPlayer/types/provider.ts +0 -93
- package/src/tools/VideoPlayer/types/sources.ts +0 -97
- package/src/tools/VideoPlayer/utils/debug.ts +0 -14
- package/src/tools/VideoPlayer/utils/fileSource.ts +0 -78
- package/src/tools/VideoPlayer/utils/index.ts +0 -12
- package/src/tools/VideoPlayer/utils/resolvers.ts +0 -75
- /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,64 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
4
|
+
|
|
5
|
+
import type { ComposerAction, ComposerSize } from './types';
|
|
6
|
+
|
|
7
|
+
/** Per-size geometry for action-bar buttons + their icons.
|
|
8
|
+
* ChatGPT/Gemini-style — fully round, slightly larger glyphs. */
|
|
9
|
+
export const BUTTON_SIZE: Record<ComposerSize, { button: string; icon: string }> = {
|
|
10
|
+
sm: { button: 'h-7 w-7 rounded-full', icon: '[&_svg]:size-4' },
|
|
11
|
+
md: { button: 'h-9 w-9 rounded-full', icon: '[&_svg]:size-[1.125rem]' },
|
|
12
|
+
lg: { button: 'h-11 w-11 rounded-full', icon: '[&_svg]:size-5' },
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const VARIANT_CLASSES: Record<NonNullable<ComposerAction['variant']>, string> = {
|
|
16
|
+
ghost: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground',
|
|
17
|
+
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
18
|
+
primary: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
19
|
+
// The send button is the composer's primary CTA — brand `primary`
|
|
20
|
+
// token, cohesive with accent surfaces (e.g. the user bubble).
|
|
21
|
+
// Disabled stays a dimmed `primary` (not neutral grey) so the button
|
|
22
|
+
// is still recognisable as send against the composer surface.
|
|
23
|
+
send: 'bg-primary text-primary-foreground hover:bg-primary/90 disabled:bg-primary/30 disabled:text-primary-foreground/60',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface ComposerButtonProps {
|
|
27
|
+
action: ComposerAction;
|
|
28
|
+
size: ComposerSize;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Renders a single declarative `ComposerAction` as an icon button.
|
|
33
|
+
* Centralises size, variant, aria-label, and `aria-pressed` so hosts
|
|
34
|
+
* cannot misalign or under-label an action.
|
|
35
|
+
*/
|
|
36
|
+
export function ComposerButton({ action, size }: ComposerButtonProps) {
|
|
37
|
+
const variant = action.variant ?? 'ghost';
|
|
38
|
+
const sz = BUTTON_SIZE[size];
|
|
39
|
+
const isToggle = action.pressed !== undefined;
|
|
40
|
+
return (
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
onClick={action.onClick}
|
|
44
|
+
disabled={action.disabled}
|
|
45
|
+
aria-label={action.label}
|
|
46
|
+
title={action.label}
|
|
47
|
+
aria-pressed={isToggle ? action.pressed : undefined}
|
|
48
|
+
className={cn(
|
|
49
|
+
'inline-flex shrink-0 cursor-pointer items-center justify-center',
|
|
50
|
+
'transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
|
|
51
|
+
'disabled:pointer-events-none [&_svg]:shrink-0',
|
|
52
|
+
// The send variant carries its own muted disabled fill, so it
|
|
53
|
+
// skips the generic opacity dim that the other variants use.
|
|
54
|
+
variant === 'send' ? 'disabled:cursor-default' : 'disabled:opacity-50',
|
|
55
|
+
sz.button,
|
|
56
|
+
sz.icon,
|
|
57
|
+
VARIANT_CLASSES[variant],
|
|
58
|
+
isToggle && action.pressed && variant === 'ghost' && 'bg-accent text-accent-foreground',
|
|
59
|
+
)}
|
|
60
|
+
>
|
|
61
|
+
{action.icon}
|
|
62
|
+
</button>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { isValidElement } from 'react';
|
|
4
|
+
|
|
5
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
6
|
+
|
|
7
|
+
import type { ComposerFooterProps } from './types';
|
|
8
|
+
|
|
9
|
+
/** Counter starts announcing once the draft passes this fraction of the cap. */
|
|
10
|
+
const COUNTER_THRESHOLD = 0.8;
|
|
11
|
+
|
|
12
|
+
const TEXT_SIZE = {
|
|
13
|
+
sm: 'text-[11px]',
|
|
14
|
+
md: 'text-xs',
|
|
15
|
+
lg: 'text-xs',
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
function shortcutHint(submitOn: 'enter' | 'cmd+enter'): string {
|
|
19
|
+
return submitOn === 'cmd+enter'
|
|
20
|
+
? '⌘⏎ to send'
|
|
21
|
+
: '⏎ to send · ⇧⏎ for newline';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The quiet strip below the input surface. Three zones: start (auto
|
|
26
|
+
* shortcut hint), center (host extras), end (auto char counter). Renders
|
|
27
|
+
* as a plain `div` unless it carries interactive controls.
|
|
28
|
+
*/
|
|
29
|
+
export function ComposerFooter({
|
|
30
|
+
start,
|
|
31
|
+
center,
|
|
32
|
+
end,
|
|
33
|
+
showCounter = true,
|
|
34
|
+
// Off by default — ChatGPT / Gemini don't surface a keyboard hint.
|
|
35
|
+
// Hosts can opt back in with `showHint`.
|
|
36
|
+
showHint = false,
|
|
37
|
+
value = '',
|
|
38
|
+
maxLength,
|
|
39
|
+
submitOn = 'enter',
|
|
40
|
+
size = 'md',
|
|
41
|
+
className,
|
|
42
|
+
}: ComposerFooterProps) {
|
|
43
|
+
const nearLimit =
|
|
44
|
+
maxLength !== undefined && value.length >= maxLength * COUNTER_THRESHOLD;
|
|
45
|
+
const counterNode =
|
|
46
|
+
showCounter && maxLength !== undefined && nearLimit ? (
|
|
47
|
+
<span
|
|
48
|
+
aria-live="polite"
|
|
49
|
+
className={cn(
|
|
50
|
+
'tabular-nums',
|
|
51
|
+
value.length >= maxLength ? 'text-destructive' : 'text-muted-foreground',
|
|
52
|
+
)}
|
|
53
|
+
>
|
|
54
|
+
{value.length} / {maxLength}
|
|
55
|
+
</span>
|
|
56
|
+
) : null;
|
|
57
|
+
|
|
58
|
+
const hintNode =
|
|
59
|
+
showHint && !start ? (
|
|
60
|
+
<span aria-hidden className="text-muted-foreground/70">
|
|
61
|
+
{shortcutHint(submitOn)}
|
|
62
|
+
</span>
|
|
63
|
+
) : null;
|
|
64
|
+
|
|
65
|
+
const startNode = start ?? hintNode;
|
|
66
|
+
const endNode = end ?? counterNode;
|
|
67
|
+
|
|
68
|
+
// Nothing to show — render nothing rather than an empty strip.
|
|
69
|
+
if (!startNode && !center && !endNode) return null;
|
|
70
|
+
|
|
71
|
+
// A toolbar role is only correct when the strip carries host-provided
|
|
72
|
+
// interactive controls. The auto hint/counter alone stay a plain div.
|
|
73
|
+
const hasInteractive = isValidElement(center) || isValidElement(start) || isValidElement(end);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<div
|
|
77
|
+
role={hasInteractive ? 'toolbar' : undefined}
|
|
78
|
+
aria-label={hasInteractive ? 'Composer options' : undefined}
|
|
79
|
+
className={cn(
|
|
80
|
+
'flex min-h-7 items-center justify-between gap-2 px-1 pt-1',
|
|
81
|
+
TEXT_SIZE[size],
|
|
82
|
+
className,
|
|
83
|
+
)}
|
|
84
|
+
>
|
|
85
|
+
<div className="flex min-w-0 items-center gap-2">{startNode}</div>
|
|
86
|
+
{center ? <div className="flex items-center gap-2">{center}</div> : null}
|
|
87
|
+
<div className="flex items-center gap-2">{endNode}</div>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Plus } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
import { MenuBuilder, type MenuItem } from '@djangocfg/ui-core/components';
|
|
6
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
7
|
+
|
|
8
|
+
import { BUTTON_SIZE } from './ComposerButton';
|
|
9
|
+
import { useResolvedComposerSize } from './size-context';
|
|
10
|
+
import type { ComposerSize } from './types';
|
|
11
|
+
|
|
12
|
+
export interface ComposerMenuButtonProps {
|
|
13
|
+
/** Declarative menu tree (see `MenuBuilder`). */
|
|
14
|
+
items: MenuItem[];
|
|
15
|
+
/** Omit to inherit the composer's size. */
|
|
16
|
+
size?: ComposerSize;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/** Accessible label for the trigger. */
|
|
19
|
+
label?: string;
|
|
20
|
+
/** Trigger icon. Defaults to a `+`. */
|
|
21
|
+
icon?: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The composer's leading menu button — a `+` trigger that opens a
|
|
26
|
+
* declarative `MenuBuilder` (ChatGPT-style: attach, tools, more).
|
|
27
|
+
*
|
|
28
|
+
* Drop it into `composerSlots.inlineStart` so it sits at the start of
|
|
29
|
+
* the action bar. Size inherits from the composer unless set explicitly.
|
|
30
|
+
*/
|
|
31
|
+
export function ComposerMenuButton({
|
|
32
|
+
items,
|
|
33
|
+
size,
|
|
34
|
+
disabled,
|
|
35
|
+
label = 'Add files and more',
|
|
36
|
+
icon,
|
|
37
|
+
}: ComposerMenuButtonProps) {
|
|
38
|
+
const sz = BUTTON_SIZE[useResolvedComposerSize(size)];
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<MenuBuilder items={items} side="top" align="start">
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
aria-label={label}
|
|
46
|
+
title={label}
|
|
47
|
+
className={cn(
|
|
48
|
+
'inline-flex shrink-0 cursor-pointer items-center justify-center',
|
|
49
|
+
'text-muted-foreground transition-colors',
|
|
50
|
+
'hover:bg-accent hover:text-accent-foreground',
|
|
51
|
+
'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
|
|
52
|
+
'disabled:pointer-events-none disabled:opacity-50 [&_svg]:shrink-0',
|
|
53
|
+
'data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
|
|
54
|
+
sz.button,
|
|
55
|
+
sz.icon,
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{icon ?? <Plus aria-hidden />}
|
|
59
|
+
</button>
|
|
60
|
+
</MenuBuilder>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import { ChevronDown } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
import { MenuBuilder, type MenuItem } from '@djangocfg/ui-core/components';
|
|
7
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
8
|
+
|
|
9
|
+
import { useResolvedComposerSize } from './size-context';
|
|
10
|
+
import type { ComposerSize } from './types';
|
|
11
|
+
|
|
12
|
+
/** One selectable model. */
|
|
13
|
+
export interface ComposerModelOption {
|
|
14
|
+
/** Stable id — also the radio value. */
|
|
15
|
+
id: string;
|
|
16
|
+
/** Pill text + menu row label. */
|
|
17
|
+
label: string;
|
|
18
|
+
/** Secondary muted line in the menu. */
|
|
19
|
+
description?: string;
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ComposerModelPickerProps {
|
|
24
|
+
/** Selected model id. */
|
|
25
|
+
value: string;
|
|
26
|
+
options: ComposerModelOption[];
|
|
27
|
+
onChange: (id: string) => void;
|
|
28
|
+
/** Omit to inherit the composer's size. */
|
|
29
|
+
size?: ComposerSize;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
/** Accessible label for the trigger. */
|
|
32
|
+
label?: string;
|
|
33
|
+
className?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Per-size geometry for the trigger pill. */
|
|
37
|
+
const PILL_SIZE: Record<ComposerSize, { pill: string; text: string }> = {
|
|
38
|
+
sm: { pill: 'h-7 gap-1 px-2', text: 'text-xs' },
|
|
39
|
+
md: { pill: 'h-9 gap-1 px-2.5', text: 'text-sm' },
|
|
40
|
+
lg: { pill: 'h-11 gap-1.5 px-3', text: 'text-sm' },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A "Flash-Lite ▾" pill that opens a radio-group model picker — Gemini's
|
|
45
|
+
* model switcher. Drop it into `composerSlots.inlineEnd` (trailing
|
|
46
|
+
* cluster, before mic/send).
|
|
47
|
+
*/
|
|
48
|
+
export function ComposerModelPicker({
|
|
49
|
+
value,
|
|
50
|
+
options,
|
|
51
|
+
onChange,
|
|
52
|
+
size,
|
|
53
|
+
disabled,
|
|
54
|
+
label = 'Select model',
|
|
55
|
+
className,
|
|
56
|
+
}: ComposerModelPickerProps) {
|
|
57
|
+
const sz = PILL_SIZE[useResolvedComposerSize(size)];
|
|
58
|
+
const current = options.find((o) => o.id === value);
|
|
59
|
+
|
|
60
|
+
const items: MenuItem[] = useMemo(
|
|
61
|
+
() => [
|
|
62
|
+
{
|
|
63
|
+
kind: 'radio-group',
|
|
64
|
+
id: 'model',
|
|
65
|
+
value,
|
|
66
|
+
onValueChange: onChange,
|
|
67
|
+
options: options.map((o) => ({
|
|
68
|
+
id: o.id,
|
|
69
|
+
value: o.id,
|
|
70
|
+
label: o.label,
|
|
71
|
+
description: o.description,
|
|
72
|
+
disabled: o.disabled,
|
|
73
|
+
})),
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
[value, options, onChange],
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<MenuBuilder items={items} side="top" align="end">
|
|
81
|
+
<button
|
|
82
|
+
type="button"
|
|
83
|
+
disabled={disabled}
|
|
84
|
+
aria-label={label}
|
|
85
|
+
title={label}
|
|
86
|
+
className={cn(
|
|
87
|
+
'inline-flex shrink-0 cursor-pointer items-center rounded-full',
|
|
88
|
+
'text-muted-foreground transition-colors',
|
|
89
|
+
'hover:bg-accent hover:text-accent-foreground',
|
|
90
|
+
'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
|
|
91
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
92
|
+
'data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
|
|
93
|
+
'[&_svg]:size-4 [&_svg]:shrink-0',
|
|
94
|
+
sz.pill,
|
|
95
|
+
sz.text,
|
|
96
|
+
className,
|
|
97
|
+
)}
|
|
98
|
+
>
|
|
99
|
+
<span className="font-medium">{current?.label ?? value}</span>
|
|
100
|
+
<ChevronDown aria-hidden className="opacity-70" />
|
|
101
|
+
</button>
|
|
102
|
+
</MenuBuilder>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback, useMemo, useRef } from 'react';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
LazyMarkdownEditor as MarkdownEditor,
|
|
7
|
+
type MarkdownEditorHandle,
|
|
8
|
+
type MentionConfig,
|
|
9
|
+
} from '@djangocfg/ui-tools/markdown-editor';
|
|
10
|
+
|
|
11
|
+
import { useRegisterComposer } from '../hooks/useAutoFocusOnStreamEnd';
|
|
12
|
+
import type { ComposerSize, ComposerTextareaProps } from './types';
|
|
13
|
+
|
|
14
|
+
/** Single-row baseline height per composer size (px). */
|
|
15
|
+
const MIN_HEIGHT: Record<ComposerSize, number> = {
|
|
16
|
+
sm: 20,
|
|
17
|
+
md: 24,
|
|
18
|
+
lg: 28,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export interface ComposerRichTextareaProps extends ComposerTextareaProps {
|
|
22
|
+
/** `@`-mention autocomplete config. Omit for a plain rich textarea. */
|
|
23
|
+
mentions?: MentionConfig;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Drop-in TipTap-backed textarea for the composer's Tier B `Textarea`
|
|
28
|
+
* slot. Pre-wired with chat defaults — `unstyled` (the composer surface
|
|
29
|
+
* draws the frame), no toolbar, size-matched single-row height, and
|
|
30
|
+
* Enter-to-send. The host passes only `mentions` if it needs `@`
|
|
31
|
+
* autocomplete; everything else flows from `ComposerTextareaProps`.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* <Composer
|
|
35
|
+
* slots={{ Textarea: (p) => <ComposerRichTextarea {...p} mentions={cfg} /> }}
|
|
36
|
+
* />
|
|
37
|
+
*/
|
|
38
|
+
export function ComposerRichTextarea({
|
|
39
|
+
composer,
|
|
40
|
+
placeholder,
|
|
41
|
+
disabled,
|
|
42
|
+
size,
|
|
43
|
+
className,
|
|
44
|
+
mentions,
|
|
45
|
+
}: ComposerRichTextareaProps) {
|
|
46
|
+
// Tiptap captures the mentions object once on first render — keep its
|
|
47
|
+
// identity stable so the suggestion plugin stays wired.
|
|
48
|
+
const stableMentions = useMemo(() => mentions, [mentions]);
|
|
49
|
+
|
|
50
|
+
// The TipTap editor has no `<textarea>`, so the composer's default
|
|
51
|
+
// `focus` handle (which targets `textareaRef`) is a no-op here.
|
|
52
|
+
// Register the editor's real imperative handle with the chat context
|
|
53
|
+
// so `useAutoFocusOnStreamEnd` (refocus after the assistant reply)
|
|
54
|
+
// and the voice slot actually drive *this* editor.
|
|
55
|
+
const editorRef = useRef<MarkdownEditorHandle>(null);
|
|
56
|
+
const focus = useCallback(() => editorRef.current?.focus(), []);
|
|
57
|
+
const moveCursorToEnd = useCallback(
|
|
58
|
+
() => editorRef.current?.moveCursorToEnd(),
|
|
59
|
+
[],
|
|
60
|
+
);
|
|
61
|
+
const getValue = useCallback(() => composer.value, [composer.value]);
|
|
62
|
+
useRegisterComposer({
|
|
63
|
+
focus,
|
|
64
|
+
moveCursorToEnd,
|
|
65
|
+
getValue,
|
|
66
|
+
setValue: composer.setValue,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className={className ?? 'min-w-0 flex-1'}>
|
|
71
|
+
<MarkdownEditor
|
|
72
|
+
ref={editorRef}
|
|
73
|
+
value={composer.value}
|
|
74
|
+
onChange={composer.setValue}
|
|
75
|
+
mentions={stableMentions}
|
|
76
|
+
placeholder={placeholder}
|
|
77
|
+
minHeight={MIN_HEIGHT[size]}
|
|
78
|
+
showToolbar={false}
|
|
79
|
+
unstyled
|
|
80
|
+
disabled={disabled}
|
|
81
|
+
onSubmit={() => {
|
|
82
|
+
if (composer.canSubmit) void composer.submit();
|
|
83
|
+
return true;
|
|
84
|
+
}}
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
import { X } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
7
|
+
|
|
8
|
+
import { useResolvedComposerSize } from './size-context';
|
|
9
|
+
import type { ComposerSize } from './types';
|
|
10
|
+
|
|
11
|
+
export interface ComposerToolPillProps {
|
|
12
|
+
/** Leading icon — a lucide-react element. */
|
|
13
|
+
icon?: ReactNode;
|
|
14
|
+
/** Pill text. */
|
|
15
|
+
label: ReactNode;
|
|
16
|
+
/** Accent fill when the tool/mode is active. */
|
|
17
|
+
active?: boolean;
|
|
18
|
+
/** Clicking the pill body (toggles the tool). */
|
|
19
|
+
onClick?: () => void;
|
|
20
|
+
/** When set, renders a trailing `×` that calls this instead. */
|
|
21
|
+
onRemove?: () => void;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
/** Omit to inherit the composer's size. */
|
|
24
|
+
size?: ComposerSize;
|
|
25
|
+
className?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Per-size geometry — round capsule heights matching the action bar. */
|
|
29
|
+
const PILL_SIZE: Record<ComposerSize, { pill: string; text: string; icon: string }> = {
|
|
30
|
+
sm: { pill: 'h-7 gap-1 px-2', text: 'text-xs', icon: '[&_svg]:size-3.5' },
|
|
31
|
+
md: { pill: 'h-9 gap-1.5 px-3', text: 'text-sm', icon: '[&_svg]:size-4' },
|
|
32
|
+
lg: { pill: 'h-11 gap-2 px-3.5', text: 'text-sm', icon: '[&_svg]:size-[1.125rem]' },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A rounded action-bar capsule for a selected tool or mode — Gemini's
|
|
37
|
+
* "🎨 Images" pill. Drop it into `composerSlots.inlineStart` (leading
|
|
38
|
+
* cluster). When `onRemove` is set the pill shows a trailing `×`.
|
|
39
|
+
*/
|
|
40
|
+
export function ComposerToolPill({
|
|
41
|
+
icon,
|
|
42
|
+
label,
|
|
43
|
+
active = false,
|
|
44
|
+
onClick,
|
|
45
|
+
onRemove,
|
|
46
|
+
disabled,
|
|
47
|
+
size,
|
|
48
|
+
className,
|
|
49
|
+
}: ComposerToolPillProps) {
|
|
50
|
+
const sz = PILL_SIZE[useResolvedComposerSize(size)];
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
className={cn(
|
|
54
|
+
'inline-flex shrink-0 items-center rounded-full border transition-colors',
|
|
55
|
+
sz.pill,
|
|
56
|
+
sz.text,
|
|
57
|
+
sz.icon,
|
|
58
|
+
active
|
|
59
|
+
? 'border-transparent bg-accent text-accent-foreground'
|
|
60
|
+
: 'border-border bg-transparent text-muted-foreground',
|
|
61
|
+
disabled && 'pointer-events-none opacity-50',
|
|
62
|
+
className,
|
|
63
|
+
)}
|
|
64
|
+
>
|
|
65
|
+
<button
|
|
66
|
+
type="button"
|
|
67
|
+
onClick={onClick}
|
|
68
|
+
disabled={disabled}
|
|
69
|
+
aria-pressed={onClick ? active : undefined}
|
|
70
|
+
className={cn(
|
|
71
|
+
'inline-flex items-center gap-1.5 outline-none',
|
|
72
|
+
onClick && !disabled && 'cursor-pointer hover:text-foreground',
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
{icon ? <span className="flex shrink-0 [&_svg]:shrink-0">{icon}</span> : null}
|
|
76
|
+
<span className="font-medium">{label}</span>
|
|
77
|
+
</button>
|
|
78
|
+
{onRemove ? (
|
|
79
|
+
<button
|
|
80
|
+
type="button"
|
|
81
|
+
onClick={onRemove}
|
|
82
|
+
disabled={disabled}
|
|
83
|
+
aria-label="Remove"
|
|
84
|
+
title="Remove"
|
|
85
|
+
className={cn(
|
|
86
|
+
'-mr-1 ml-0.5 inline-flex size-5 cursor-pointer items-center justify-center',
|
|
87
|
+
'rounded-full transition-colors hover:bg-foreground/10 [&_svg]:size-3.5',
|
|
88
|
+
)}
|
|
89
|
+
>
|
|
90
|
+
<X aria-hidden />
|
|
91
|
+
</button>
|
|
92
|
+
) : null}
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/** Composer — the `<Composer>` shell + its slot kit. */
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
Composer,
|
|
7
|
+
type ComposerProps,
|
|
8
|
+
type ComposerSize,
|
|
9
|
+
type ComposerAppearance,
|
|
10
|
+
} from './Composer';
|
|
11
|
+
export { ComposerButton, BUTTON_SIZE, type ComposerButtonProps } from './ComposerButton';
|
|
12
|
+
export { ComposerMenuButton, type ComposerMenuButtonProps } from './ComposerMenuButton';
|
|
13
|
+
export { ComposerRichTextarea, type ComposerRichTextareaProps } from './ComposerRichTextarea';
|
|
14
|
+
export { ComposerActionBar } from './ComposerActionBar';
|
|
15
|
+
export { ComposerToolPill, type ComposerToolPillProps } from './ComposerToolPill';
|
|
16
|
+
export {
|
|
17
|
+
ComposerModelPicker,
|
|
18
|
+
type ComposerModelPickerProps,
|
|
19
|
+
type ComposerModelOption,
|
|
20
|
+
} from './ComposerModelPicker';
|
|
21
|
+
export {
|
|
22
|
+
ComposerBanner,
|
|
23
|
+
type ComposerBannerProps,
|
|
24
|
+
type ComposerBannerAction,
|
|
25
|
+
} from './ComposerBanner';
|
|
26
|
+
export { ComposerFooter } from './ComposerFooter';
|
|
27
|
+
export { useResolvedComposerSize } from './size-context';
|
|
28
|
+
export {
|
|
29
|
+
useComposerActions,
|
|
30
|
+
type UseComposerActionsParams,
|
|
31
|
+
type ComposerActionClusters,
|
|
32
|
+
} from './useComposerActions';
|
|
33
|
+
export type {
|
|
34
|
+
ComposerAction,
|
|
35
|
+
ComposerActionVisibility,
|
|
36
|
+
ComposerLayout,
|
|
37
|
+
ComposerSlots,
|
|
38
|
+
ComposerSlotComponents,
|
|
39
|
+
ComposerSlotProps,
|
|
40
|
+
ComposerFooterProps,
|
|
41
|
+
SendButtonProps,
|
|
42
|
+
AttachButtonProps,
|
|
43
|
+
ComposerTextareaProps,
|
|
44
|
+
ActionBarProps,
|
|
45
|
+
} from './types';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext } from 'react';
|
|
4
|
+
|
|
5
|
+
import type { ComposerSize } from './types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Propagates the composer's `size` to slot components placed in
|
|
9
|
+
* `composerSlots.inlineStart` / `inlineEnd` (`ComposerMenuButton`,
|
|
10
|
+
* `ComposerModelPicker`, `ComposerToolPill`). Without this a host has
|
|
11
|
+
* to repeat `size` on every slotted control to keep the action bar
|
|
12
|
+
* visually aligned; with it, an explicit `size` prop on the slot
|
|
13
|
+
* component still wins, but omitting it inherits the composer's size.
|
|
14
|
+
*/
|
|
15
|
+
const ComposerSizeContext = createContext<ComposerSize | null>(null);
|
|
16
|
+
|
|
17
|
+
export const ComposerSizeProvider = ComposerSizeContext.Provider;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the effective size for a slotted control: an explicit prop
|
|
21
|
+
* always wins; otherwise inherit from the composer; final fallback `md`.
|
|
22
|
+
*/
|
|
23
|
+
export function useResolvedComposerSize(explicit?: ComposerSize): ComposerSize {
|
|
24
|
+
const inherited = useContext(ComposerSizeContext);
|
|
25
|
+
return explicit ?? inherited ?? 'md';
|
|
26
|
+
}
|