@digithings-ai/ui 0.0.0-stage → 0.1.0
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 +48 -2
- package/package.json +143 -4
- package/src/components/NavShell.tsx +776 -0
- package/src/components/SocialRow.tsx +87 -0
- package/src/components/Spinner.tsx +27 -0
- package/src/components/StackLogo.tsx +40 -0
- package/src/components/Terminal.tsx +110 -0
- package/src/components/TerminalManifest.tsx +212 -0
- package/src/components/ThemeProvider.tsx +271 -0
- package/src/components/account/AuthCard.tsx +309 -0
- package/src/components/assistant-ui/elements/mermaid-diagram.aui.tsx +63 -0
- package/src/components/assistant-ui/elements/mermaid-diagram.tsx +340 -0
- package/src/components/brand/BrandWordmark.tsx +32 -0
- package/src/components/changelog/ReleaseRail.tsx +81 -0
- package/src/components/changelog/index.ts +1 -0
- package/src/components/chat/ChatCodeBlock.tsx +67 -0
- package/src/components/chat/ChatMarkdown.tsx +66 -0
- package/src/components/chat/ChatMarkdownSource.tsx +203 -0
- package/src/components/chat/ChatMermaidBlock.tsx +202 -0
- package/src/components/chat/ChatMessage.tsx +77 -0
- package/src/components/chat/ChatPlayback.tsx +422 -0
- package/src/components/chat/ChatStreamCursor.tsx +25 -0
- package/src/components/chat/ChatThinking.tsx +113 -0
- package/src/components/chat/ChatToolCall.tsx +190 -0
- package/src/components/chat/ChatTranscript.tsx +60 -0
- package/src/components/chat/ChatWidgetFrame.tsx +107 -0
- package/src/components/chat/DigichatBootLoader.tsx +683 -0
- package/src/components/chat/DigichatLauncher.tsx +361 -0
- package/src/components/chat/DigichatThread.tsx +118 -0
- package/src/components/chat/DigichatThreadList.tsx +54 -0
- package/src/components/chat/DigichatWordmark.tsx +24 -0
- package/src/components/chat/DotMatrix.tsx +650 -0
- package/src/components/chat/boot-lab.tsx +766 -0
- package/src/components/chat/boot-signal.ts +144 -0
- package/src/components/chat/chat-surfaces.ts +52 -0
- package/src/components/chat/connection-status.ts +40 -0
- package/src/components/chat/digichat-wordmark.ts +355 -0
- package/src/components/chat/disclosure-chevron.tsx +49 -0
- package/src/components/chat/followable-href.ts +9 -0
- package/src/components/chat/gallery-thread/action-icons.tsx +25 -0
- package/src/components/chat/gallery-thread/attachment.aui.tsx +262 -0
- package/src/components/chat/gallery-thread/block-caret.tsx +439 -0
- package/src/components/chat/gallery-thread/cn.ts +1 -0
- package/src/components/chat/gallery-thread/composer-keyboard.ts +106 -0
- package/src/components/chat/gallery-thread/composer-trigger-popover.aui.tsx +235 -0
- package/src/components/chat/gallery-thread/css.d.ts +6 -0
- package/src/components/chat/gallery-thread/file.tsx +238 -0
- package/src/components/chat/gallery-thread/follow-up-suggestions.aui.tsx +92 -0
- package/src/components/chat/gallery-thread/format-json-dump.ts +112 -0
- package/src/components/chat/gallery-thread/hooks/use-attachment-src.ts +46 -0
- package/src/components/chat/gallery-thread/hooks/use-copy-to-clipboard.ts +9 -0
- package/src/components/chat/gallery-thread/image.tsx +519 -0
- package/src/components/chat/gallery-thread/markdown-text.tsx +278 -0
- package/src/components/chat/gallery-thread/message-error.aui.tsx +105 -0
- package/src/components/chat/gallery-thread/message-timing.aui.tsx +143 -0
- package/src/components/chat/gallery-thread/private-work-step.tsx +188 -0
- package/src/components/chat/gallery-thread/reasoning.tsx +332 -0
- package/src/components/chat/gallery-thread/scroll-mode.ts +174 -0
- package/src/components/chat/gallery-thread/source.tsx +76 -0
- package/src/components/chat/gallery-thread/thread.aui.tsx +1619 -0
- package/src/components/chat/gallery-thread/tooltip-icon-button.tsx +53 -0
- package/src/components/chat/gallery-thread/typed-welcome-copy.tsx +18 -0
- package/src/components/chat/gallery-thread/ui/tooltip.tsx +22 -0
- package/src/components/chat/gallery-thread/work-chain-summary.tsx +48 -0
- package/src/components/chat/gallery-thread/work-chain.tsx +110 -0
- package/src/components/chat/gallery-thread/work-status.ts +66 -0
- package/src/components/chat/overlay-scrollbar.tsx +86 -0
- package/src/components/chat/sanitize-mermaid-svg.ts +38 -0
- package/src/components/chat/skins/SOURCE.md +36 -0
- package/src/components/chat/skins/base/attachment.aui.tsx +287 -0
- package/src/components/chat/skins/base/aui-default/README.md +45 -0
- package/src/components/chat/skins/base/aui-default/collapsible-root.tsx +19 -0
- package/src/components/chat/skins/base/aui-default/collapsible.tsx +21 -0
- package/src/components/chat/skins/base/aui-default/reasoning.aui.tsx +120 -0
- package/src/components/chat/skins/base/aui-default/reasoning.tsx +331 -0
- package/src/components/chat/skins/base/aui-default/surfaces.ts +7 -0
- package/src/components/chat/skins/base/aui-default/tool-fallback.aui.tsx +772 -0
- package/src/components/chat/skins/base/aui-default/tool-group.aui.tsx +241 -0
- package/src/components/chat/skins/base/docs-model-options.ts +6 -0
- package/src/components/chat/skins/base/elements/assistant-trace.aui.tsx +169 -0
- package/src/components/chat/skins/base/elements/composer-trigger-popover.aui.tsx +271 -0
- package/src/components/chat/skins/base/elements/directive-text.aui.tsx +29 -0
- package/src/components/chat/skins/base/elements/directive-text.tsx +76 -0
- package/src/components/chat/skins/base/elements/message-timing.aui.tsx +104 -0
- package/src/components/chat/skins/base/elements/model-selector.aui.tsx +140 -0
- package/src/components/chat/skins/base/elements/model-selector.tsx +720 -0
- package/src/components/chat/skins/base/elements/quote.aui.tsx +290 -0
- package/src/components/chat/skins/base/elements/work-chain.tsx +100 -0
- package/src/components/chat/skins/base/markdown-text.tsx +294 -0
- package/src/components/chat/skins/base/model.ts +1 -0
- package/src/components/chat/skins/base/thread.tsx +1050 -0
- package/src/components/chat/skins/base/tooltip-icon-button.tsx +56 -0
- package/src/components/chat/skins/base/ui/avatar.tsx +115 -0
- package/src/components/chat/skins/base/ui/badge.tsx +51 -0
- package/src/components/chat/skins/base/ui/button.tsx +67 -0
- package/src/components/chat/skins/base/ui/command.tsx +185 -0
- package/src/components/chat/skins/base/ui/dialog.tsx +168 -0
- package/src/components/chat/skins/base/ui/dot-matrix.tsx +267 -0
- package/src/components/chat/skins/base/ui/popover.tsx +97 -0
- package/src/components/chat/skins/base/ui/skeleton.tsx +20 -0
- package/src/components/chat/skins/base/ui/textarea.tsx +24 -0
- package/src/components/chat/skins/base/ui/tooltip.tsx +73 -0
- package/src/components/chat/skins/base-assistant-ui/config-provider.tsx +90 -0
- package/src/components/chat/skins/base-assistant-ui/defaults.ts +419 -0
- package/src/components/chat/skins/base-assistant-ui/index.tsx +44 -0
- package/src/components/chat/skins/chatgpt.tsx +448 -0
- package/src/components/chat/skins/claude.tsx +380 -0
- package/src/components/chat/skins/digichat-composer-toolbar.tsx +151 -0
- package/src/components/chat/skins/digichat-context-meter-shares.ts +158 -0
- package/src/components/chat/skins/digichat-context-meter.tsx +243 -0
- package/src/components/chat/skins/digichat-meter-tone.ts +26 -0
- package/src/components/chat/skins/digichat-usage-meter.tsx +103 -0
- package/src/components/chat/skins/digichat.tsx +144 -0
- package/src/components/chat/skins/expo-react-native.tsx +29 -0
- package/src/components/chat/skins/gemini.tsx +322 -0
- package/src/components/chat/skins/grok-icon.tsx +34 -0
- package/src/components/chat/skins/grok.tsx +369 -0
- package/src/components/chat/skins/index.ts +46 -0
- package/src/components/chat/skins/perplexity.tsx +462 -0
- package/src/components/chat/skins/product-page-assistant/assistant-modal.tsx +73 -0
- package/src/components/chat/skins/product-page-assistant/index.tsx +19 -0
- package/src/components/chat/skins/product-page-assistant/product-dashboard.tsx +164 -0
- package/src/components/chat/skins/react-ink.tsx +97 -0
- package/src/components/chat/skins/thread-skin-host-contract.ts +102 -0
- package/src/components/chat/skins/thread-skin-view.tsx +156 -0
- package/src/components/chat/skins/thread-skins.ts +143 -0
- package/src/components/chat/skins/tooltip-icon-button.tsx +35 -0
- package/src/components/chat/skins/ui/dropdown-menu.tsx +272 -0
- package/src/components/chat/skins/webpage-assistant/assistant-config.ts +238 -0
- package/src/components/chat/skins/webpage-assistant/assistant-modal.tsx +78 -0
- package/src/components/chat/skins/webpage-assistant/config-provider.tsx +132 -0
- package/src/components/chat/skins/webpage-assistant/docs-app.tsx +106 -0
- package/src/components/chat/skins/webpage-assistant/docs-article.tsx +109 -0
- package/src/components/chat/skins/webpage-assistant/docs-assistant.tsx +73 -0
- package/src/components/chat/skins/webpage-assistant/docs-nav.tsx +81 -0
- package/src/components/chat/skins/webpage-assistant/host-ui.ts +79 -0
- package/src/components/chat/skins/webpage-assistant/index.tsx +12 -0
- package/src/components/chat/skins/webpage-assistant/markdown-page.tsx +11 -0
- package/src/components/chat/skins/webpage-assistant/pages.ts +9 -0
- package/src/components/chat/skins/webpage-assistant/types.ts +178 -0
- package/src/components/chat/skins/webpage-assistant/use-mobile.ts +19 -0
- package/src/components/chat/stock/SOURCE.md +90 -0
- package/src/components/chat/stock/artifacts-panel.tsx +305 -0
- package/src/components/chat/stock/attachment.aui.tsx +280 -0
- package/src/components/chat/stock/chat-notice.tsx +143 -0
- package/src/components/chat/stock/chat-private-mode.tsx +64 -0
- package/src/components/chat/stock/deploy-ui-context.tsx +89 -0
- package/src/components/chat/stock/embed-chat-prefs.tsx +471 -0
- package/src/components/chat/stock/file.tsx +265 -0
- package/src/components/chat/stock/follow-up-suggestions.aui.tsx +83 -0
- package/src/components/chat/stock/image.tsx +531 -0
- package/src/components/chat/stock/index.ts +129 -0
- package/src/components/chat/stock/json-view.tsx +140 -0
- package/src/components/chat/stock/markdown-text.tsx +319 -0
- package/src/components/chat/stock/message-error.aui.tsx +96 -0
- package/src/components/chat/stock/message-export-menu.tsx +156 -0
- package/src/components/chat/stock/palette-row-preview.tsx +26 -0
- package/src/components/chat/stock/reasoning.aui.tsx +120 -0
- package/src/components/chat/stock/reasoning.tsx +335 -0
- package/src/components/chat/stock/skin-chrome.tsx +81 -0
- package/src/components/chat/stock/skin-credit.tsx +63 -0
- package/src/components/chat/stock/skin-palette-bridge.tsx +235 -0
- package/src/components/chat/stock/skin-palette-context.ts +16 -0
- package/src/components/chat/stock/skin-runtime.tsx +52 -0
- package/src/components/chat/stock/skin-slash-palette.tsx +393 -0
- package/src/components/chat/stock/skin-types.ts +217 -0
- package/src/components/chat/stock/stock-chat-prefs-host.tsx +501 -0
- package/src/components/chat/stock/stock-send-gate.tsx +94 -0
- package/src/components/chat/stock/stock-shape.tsx +39 -0
- package/src/components/chat/stock/thread.aui.tsx +645 -0
- package/src/components/chat/stock/tool-display.ts +6 -0
- package/src/components/chat/stock/tool-fallback.aui.tsx +866 -0
- package/src/components/chat/stock/tool-group.aui.tsx +244 -0
- package/src/components/chat/stock/tool-surface.ts +120 -0
- package/src/components/chat/stock/tooltip-icon-button.tsx +49 -0
- package/src/components/chat/stock/ui/avatar.tsx +108 -0
- package/src/components/chat/stock/ui/button.tsx +81 -0
- package/src/components/chat/stock/ui/collapsible.tsx +21 -0
- package/src/components/chat/stock/ui/dialog.tsx +163 -0
- package/src/components/chat/stock/ui/skeleton.tsx +13 -0
- package/src/components/chat/stock/ui/textarea.tsx +20 -0
- package/src/components/chat/stock/ui/tooltip.tsx +68 -0
- package/src/components/chat/stock/use-attachment-src.ts +42 -0
- package/src/components/chat/stock/use-copy-to-clipboard.ts +29 -0
- package/src/components/chat/strip-model-boilerplate.ts +42 -0
- package/src/components/chat/suggestion-cycle.tsx +230 -0
- package/src/components/chat/suggestion-scroll-row.tsx +81 -0
- package/src/components/chat/trace-grouping/chain-group-by.ts +44 -0
- package/src/components/chat/trace-grouping/chain-state.ts +141 -0
- package/src/components/chat/trace-grouping/index.ts +32 -0
- package/src/components/chat/trace-grouping/tool-row.ts +74 -0
- package/src/components/chat/trace-grouping/use-deploy-work-disclosure.ts +35 -0
- package/src/components/chat/trace-grouping/use-work-chain.ts +103 -0
- package/src/components/chat/trace-i18n/core.ts +48 -0
- package/src/components/chat/trace-i18n/index.tsx +23 -0
- package/src/components/chat/trace-i18n/locales/en.ts +24 -0
- package/src/components/chat/trace-i18n/locales/fr.ts +28 -0
- package/src/components/chat/trace-i18n/types.ts +35 -0
- package/src/components/chat/trace-spacing.ts +249 -0
- package/src/components/chat/transcript/activity-view.ts +375 -0
- package/src/components/chat/transcript/chat-export.ts +133 -0
- package/src/components/chat/transcript/index.ts +10 -0
- package/src/components/chat/transcript/save-file.ts +209 -0
- package/src/components/chat/transcript/transcript-markdown.ts +428 -0
- package/src/components/chat/transcript/types.ts +81 -0
- package/src/components/chat/turn-outcome.ts +52 -0
- package/src/components/chat/use-warmup-phase.ts +18 -0
- package/src/components/chat/work-status-lead.tsx +49 -0
- package/src/components/chrome.tsx +264 -0
- package/src/components/command-palette/CommandPalette.tsx +404 -0
- package/src/components/command-palette/index.ts +6 -0
- package/src/components/contact/ContactMailto.tsx +112 -0
- package/src/components/contact/index.ts +5 -0
- package/src/components/conviction/ConvictionMeter.tsx +40 -0
- package/src/components/conviction/SignedConvictionBadge.tsx +26 -0
- package/src/components/conviction/index.ts +7 -0
- package/src/components/data-layout/BentoGrid.tsx +52 -0
- package/src/components/data-layout/CardRail.tsx +150 -0
- package/src/components/data-layout/DotMatrixStat.tsx +65 -0
- package/src/components/data-layout/FeatureCell.tsx +80 -0
- package/src/components/data-layout/MediaFrame.tsx +125 -0
- package/src/components/data-layout/ModuleGrid.tsx +735 -0
- package/src/components/data-layout/Odometer.tsx +149 -0
- package/src/components/data-layout/ProductFrame.tsx +79 -0
- package/src/components/data-layout/TestimonialWall.tsx +139 -0
- package/src/components/data-layout/index.ts +19 -0
- package/src/components/data-layout/treemap.ts +368 -0
- package/src/components/deck/DeckStack.tsx +243 -0
- package/src/components/desk/DataTable.tsx +373 -0
- package/src/components/desk/KpiGrid.tsx +80 -0
- package/src/components/desk/Sparkline.tsx +63 -0
- package/src/components/desk/StateBlock.tsx +132 -0
- package/src/components/desk/index.ts +18 -0
- package/src/components/diagrams/ArchitectureDiagram.tsx +350 -0
- package/src/components/diagrams/ArchitectureTour.tsx +912 -0
- package/src/components/diagrams/TerminalSchematic.tsx +192 -0
- package/src/components/diagrams/architecture-svg.tsx +384 -0
- package/src/components/diagrams/index.ts +30 -0
- package/src/components/diagrams/mermaid-theme.ts +115 -0
- package/src/components/diagrams/tour-camera.ts +171 -0
- package/src/components/docs/CodeTabs.tsx +90 -0
- package/src/components/docs/CopyCommand.tsx +151 -0
- package/src/components/docs/DocsLayout.tsx +197 -0
- package/src/components/docs/DocsSearch.tsx +97 -0
- package/src/components/docs/Endpoint.tsx +135 -0
- package/src/components/document/DocumentFrame.tsx +43 -0
- package/src/components/document/GlyphList.tsx +51 -0
- package/src/components/document/PageTitle.tsx +44 -0
- package/src/components/document/Prose.tsx +38 -0
- package/src/components/document/Section.tsx +55 -0
- package/src/components/document/index.ts +13 -0
- package/src/components/effects-chrome/AnnouncementBar.tsx +88 -0
- package/src/components/effects-chrome/HorizontalScrollTrack.tsx +461 -0
- package/src/components/effects-chrome/Pipeline.tsx +226 -0
- package/src/components/effects-chrome/RotatingPrompts.tsx +116 -0
- package/src/components/effects-chrome/StackingPanels.tsx +101 -0
- package/src/components/effects-chrome/TabStrip.tsx +232 -0
- package/src/components/effects-chrome/ToastStack.tsx +201 -0
- package/src/components/effects-chrome/horizontal-track-core.ts +70 -0
- package/src/components/effects-chrome/index.ts +35 -0
- package/src/components/emblems.tsx +188 -0
- package/src/components/faviconTheme.ts +23 -0
- package/src/components/figure/Figure.tsx +38 -0
- package/src/components/figure/index.ts +2 -0
- package/src/components/finance-charts/DrawdownPlot.tsx +76 -0
- package/src/components/finance-charts/EquityCurve.tsx +71 -0
- package/src/components/finance-charts/PriceChart.tsx +119 -0
- package/src/components/finance-charts/chart-host.tsx +236 -0
- package/src/components/finance-charts/chart-scale.ts +93 -0
- package/src/components/finance-charts/demo-data.ts +88 -0
- package/src/components/finance-charts/index.ts +46 -0
- package/src/components/finance-charts/lw-chart.tsx +292 -0
- package/src/components/finance-composites/MarketBar.tsx +243 -0
- package/src/components/finance-composites/OrderBook.tsx +133 -0
- package/src/components/finance-composites/PerformanceDashboard.tsx +185 -0
- package/src/components/finance-composites/SortableTable.tsx +144 -0
- package/src/components/finance-composites/StockTicker.tsx +65 -0
- package/src/components/finance-composites/SyncedTearsheet.tsx +226 -0
- package/src/components/finance-composites/index.ts +28 -0
- package/src/components/finance-tearsheet/KpiStrip.tsx +49 -0
- package/src/components/finance-tearsheet/LiveBadge.tsx +29 -0
- package/src/components/finance-tearsheet/ReturnsMatrix.tsx +321 -0
- package/src/components/finance-tearsheet/StrategyTearsheet.tsx +99 -0
- package/src/components/finance-tearsheet/TearsheetCard.tsx +54 -0
- package/src/components/finance-tearsheet/TradeLogTable.tsx +73 -0
- package/src/components/finance-tearsheet/charts.tsx +2624 -0
- package/src/components/finance-tearsheet/demo-data.ts +177 -0
- package/src/components/finance-tearsheet/format.ts +41 -0
- package/src/components/finance-tearsheet/index.ts +120 -0
- package/src/components/finance-tearsheet/print.ts +52 -0
- package/src/components/finance-tearsheet/risk-bands.ts +48 -0
- package/src/components/finance-tearsheet/stats.ts +35 -0
- package/src/components/finance-tearsheet/strategy-sheet-model.ts +238 -0
- package/src/components/finance-tearsheet/types.ts +58 -0
- package/src/components/footer/FooterCells.tsx +109 -0
- package/src/components/footer/index.ts +6 -0
- package/src/components/graph.tsx +158 -0
- package/src/components/icons.tsx +98 -0
- package/src/components/landing/PixelWordmark.tsx +36 -0
- package/src/components/logos.ts +39 -0
- package/src/components/marquee/Marquee.tsx +204 -0
- package/src/components/metrics/PerfMetrics.tsx +68 -0
- package/src/components/metrics/StatCounter.tsx +104 -0
- package/src/components/page-geometry.tsx +177 -0
- package/src/components/pricing/Pricing.tsx +174 -0
- package/src/components/pricing/PricingMatrix.tsx +219 -0
- package/src/components/prose/index.ts +2 -0
- package/src/components/prose/prose.tsx +86 -0
- package/src/components/repo-activity/RepoActivity.tsx +599 -0
- package/src/components/repo-activity/RepoHeatmap.tsx +225 -0
- package/src/components/repo-activity/demo.ts +201 -0
- package/src/components/repo-activity/fetch.ts +211 -0
- package/src/components/repo-activity/heatmap.ts +123 -0
- package/src/components/repo-activity/index.ts +28 -0
- package/src/components/repo-activity/types.ts +99 -0
- package/src/components/stages/NumberedStages.tsx +116 -0
- package/src/components/symbols/marks.tsx +116 -0
- package/src/components/symbols/terminal-lockup.tsx +136 -0
- package/src/components/symbols/terminal-marks.tsx +226 -0
- package/src/components/terminal/ChatResponseLoader.tsx +67 -0
- package/src/components/terminal/ContainerBootLoader.tsx +82 -0
- package/src/components/terminal/TerminalStepCaret.tsx +202 -0
- package/src/components/themeInit.ts +24 -0
- package/src/components/typography/MotionReveal.tsx +108 -0
- package/src/components/typography/TypewriterReveal.tsx +101 -0
- package/src/components/typography/WordReveal.tsx +143 -0
- package/src/data/modules.ts +378 -0
- package/src/data/subsystems.ts +101 -0
- package/src/index.ts +587 -0
- package/src/lib/base-path.ts +60 -0
- package/src/lib/cx.ts +25 -0
- package/src/lib/gloomberb.ts +69 -0
- package/src/lib/record.ts +19 -0
- package/src/lib/stub-payload.ts +183 -0
- package/src/lib/useBodyScrollLock.ts +44 -0
- package/src/lib/utils.ts +6 -0
- package/src/motion/primitives.tsx +105 -0
- package/src/motion/scroll-glide.ts +62 -0
- package/src/motion/scrolly-core.ts +38 -0
- package/src/motion/scrolly.tsx +95 -0
- package/src/navigation/HashScrollManager.tsx +167 -0
- package/src/navigation/hashScroll.ts +78 -0
- package/src/replay/browser.ts +280 -0
- package/src/replay/chat-export-run.ts +121 -0
- package/src/replay/export-format.ts +25 -0
- package/src/replay/export-mount.ts +32 -0
- package/src/replay/filename.ts +54 -0
- package/src/replay/index.ts +21 -0
- package/src/replay/redact-dom.ts +178 -0
- package/src/replay/redact.ts +113 -0
- package/src/replay/snapshot.ts +659 -0
- package/src/styles/account-auth.css +339 -0
- package/src/styles/brand-wordmark.css +11 -0
- package/src/styles/card-rail.css +120 -0
- package/src/styles/chat-aui.css +1143 -0
- package/src/styles/chat-core.css +469 -0
- package/src/styles/chat-digichat.css +2531 -0
- package/src/styles/chat-math.css +70 -0
- package/src/styles/chat-skin-palette.css +138 -0
- package/src/styles/chat-stock-menu.css +37 -0
- package/src/styles/chat-widgets.css +190 -0
- package/src/styles/command-palette.css +372 -0
- package/src/styles/controls-core.css +2261 -0
- package/src/styles/controls-overlay.css +603 -0
- package/src/styles/data-layout.css +288 -0
- package/src/styles/deck.css +141 -0
- package/src/styles/diagrams.css +1860 -0
- package/src/styles/digichat-app-theme.css +250 -0
- package/src/styles/digichat-boot-loader.css +487 -0
- package/src/styles/digichat-launcher.css +377 -0
- package/src/styles/docs.css +165 -0
- package/src/styles/effects-chrome.css +888 -0
- package/src/styles/finance-composites.css +214 -0
- package/src/styles/finance-tearsheet-overlay.css +90 -0
- package/src/styles/finance-tearsheet.css +945 -0
- package/src/styles/marquee.css +168 -0
- package/src/styles/metrics.css +82 -0
- package/src/styles/nav-shell.css +551 -0
- package/src/styles/pricing.css +79 -0
- package/src/styles/repo-activity.css +575 -0
- package/src/styles/stages.css +69 -0
- package/src/styles/terminal-loaders.css +122 -0
- package/src/styles/terminal-manifest.css +228 -0
- package/src/styles/web-theme.css +1007 -0
- package/src/styles/word-reveal.css +204 -0
- package/src/theme/apply-theme.ts +214 -0
- package/src/theme/index.ts +27 -0
- package/src/theme/palette-registry.ts +855 -0
- package/src/theme/tui-prefs.ts +64 -0
- package/src/ui/alert.tsx +78 -0
- package/src/ui/avatar.tsx +104 -0
- package/src/ui/badge.tsx +75 -0
- package/src/ui/breadcrumb.tsx +61 -0
- package/src/ui/button.tsx +67 -0
- package/src/ui/card.tsx +139 -0
- package/src/ui/checkbox.tsx +27 -0
- package/src/ui/collapsible.tsx +32 -0
- package/src/ui/date-pager.tsx +325 -0
- package/src/ui/dialog.tsx +157 -0
- package/src/ui/dropdown-menu.tsx +267 -0
- package/src/ui/empty-state.tsx +165 -0
- package/src/ui/field.tsx +104 -0
- package/src/ui/form.tsx +36 -0
- package/src/ui/icon-button.tsx +36 -0
- package/src/ui/index.ts +34 -0
- package/src/ui/input.tsx +33 -0
- package/src/ui/kbd.tsx +30 -0
- package/src/ui/label.tsx +32 -0
- package/src/ui/pager.tsx +119 -0
- package/src/ui/pagination.tsx +126 -0
- package/src/ui/radio-group.tsx +51 -0
- package/src/ui/search-bar.tsx +85 -0
- package/src/ui/segmented-control.tsx +76 -0
- package/src/ui/select.tsx +257 -0
- package/src/ui/separator.tsx +24 -0
- package/src/ui/sheet.tsx +141 -0
- package/src/ui/skeleton.tsx +76 -0
- package/src/ui/slider.tsx +76 -0
- package/src/ui/switch.tsx +31 -0
- package/src/ui/table.tsx +179 -0
- package/src/ui/tabs.tsx +89 -0
- package/src/ui/tags-input.tsx +168 -0
- package/src/ui/textarea.tsx +17 -0
- package/src/ui/tooltip.tsx +71 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { memo, useEffect, useRef, type ComponentPropsWithoutRef, type FC } from "react";
|
|
4
|
+
import {
|
|
5
|
+
ComposerPrimitive,
|
|
6
|
+
unstable_defaultDirectiveFormatter,
|
|
7
|
+
unstable_useTriggerPopoverScopeContext,
|
|
8
|
+
type Unstable_DirectiveFormatter,
|
|
9
|
+
type Unstable_TriggerItem,
|
|
10
|
+
} from "@assistant-ui/react";
|
|
11
|
+
import { cn } from "./cn";
|
|
12
|
+
import { OverlayScrollbar } from "../overlay-scrollbar";
|
|
13
|
+
import { PaletteRowPreview } from "../stock/palette-row-preview";
|
|
14
|
+
|
|
15
|
+
type IconComponent = FC<{ className?: string }>;
|
|
16
|
+
|
|
17
|
+
type DirectiveBehaviorProps = {
|
|
18
|
+
/** Formatter used to serialize the selected item into composer text. */
|
|
19
|
+
formatter?: Unstable_DirectiveFormatter | undefined;
|
|
20
|
+
/** Called after the directive text has been inserted into the composer. */
|
|
21
|
+
onInserted?: ((item: Unstable_TriggerItem) => void) | undefined;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type ActionBehaviorProps = {
|
|
25
|
+
/** Formatter used to serialize the audit-trail chip (when `removeOnExecute` is false). */
|
|
26
|
+
formatter?: Unstable_DirectiveFormatter | undefined;
|
|
27
|
+
/** Invoked with the selected item at the moment of selection. */
|
|
28
|
+
onExecute: (item: Unstable_TriggerItem) => void;
|
|
29
|
+
/** If `true`, strip the trigger text from the composer after executing. @default false */
|
|
30
|
+
removeOnExecute?: boolean | undefined;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type ComposerTriggerPopoverBaseProps = Omit<
|
|
34
|
+
ComponentPropsWithoutRef<typeof ComposerPrimitive.Unstable_TriggerPopover>,
|
|
35
|
+
"children"
|
|
36
|
+
> & {
|
|
37
|
+
/**
|
|
38
|
+
* Maps icon keys to components. Unused on the digichat skin (flat command
|
|
39
|
+
* + description rows). Kept so adapter bundles can still pass `iconMap`.
|
|
40
|
+
*/
|
|
41
|
+
iconMap?: Record<string, IconComponent>;
|
|
42
|
+
fallbackIcon?: IconComponent;
|
|
43
|
+
backLabel?: string;
|
|
44
|
+
emptyCategoriesLabel?: string;
|
|
45
|
+
/** Label shown when no items match. @default "No matching items" */
|
|
46
|
+
emptyItemsLabel?: string;
|
|
47
|
+
/** Label shown while an async adapter is resolving items. @default "Loading…" */
|
|
48
|
+
loadingLabel?: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type ComposerTriggerPopoverProps = ComposerTriggerPopoverBaseProps &
|
|
52
|
+
(
|
|
53
|
+
| {
|
|
54
|
+
directive: DirectiveBehaviorProps;
|
|
55
|
+
action?: never;
|
|
56
|
+
}
|
|
57
|
+
| {
|
|
58
|
+
action: ActionBehaviorProps;
|
|
59
|
+
directive?: never;
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
type ItemsProps = {
|
|
64
|
+
emptyLabel: string;
|
|
65
|
+
loadingLabel: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const Items: FC<ItemsProps> = ({ emptyLabel, loadingLabel }) => {
|
|
69
|
+
const { isLoading } = unstable_useTriggerPopoverScopeContext();
|
|
70
|
+
const listRef = useRef<HTMLDivElement | null>(null);
|
|
71
|
+
return (
|
|
72
|
+
<ComposerPrimitive.Unstable_TriggerPopoverItems>
|
|
73
|
+
{(items) => (
|
|
74
|
+
<div className="relative min-h-0">
|
|
75
|
+
<div
|
|
76
|
+
data-slot="composer-trigger-popover-items"
|
|
77
|
+
className="flex max-h-[min(50vh,22rem)] flex-col overflow-y-auto py-1"
|
|
78
|
+
ref={(node) => {
|
|
79
|
+
listRef.current = node;
|
|
80
|
+
node
|
|
81
|
+
?.querySelector<HTMLElement>("[data-highlighted]")
|
|
82
|
+
?.scrollIntoView({ block: "nearest" });
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
{items.map((item, index) => {
|
|
86
|
+
const direct = (item as unknown as { value?: unknown }).value;
|
|
87
|
+
const value =
|
|
88
|
+
typeof direct === "string"
|
|
89
|
+
? direct
|
|
90
|
+
: typeof item.metadata?.value === "string"
|
|
91
|
+
? item.metadata.value
|
|
92
|
+
: "";
|
|
93
|
+
const detailId = item.description ? `slash-desc-${item.id}` : undefined;
|
|
94
|
+
return (
|
|
95
|
+
<ComposerPrimitive.Unstable_TriggerPopoverItem
|
|
96
|
+
key={item.id}
|
|
97
|
+
item={item}
|
|
98
|
+
index={index}
|
|
99
|
+
aria-describedby={detailId}
|
|
100
|
+
className="group flex w-full cursor-pointer flex-col items-stretch px-2 py-1 text-start text-sm outline-none"
|
|
101
|
+
>
|
|
102
|
+
<span className="flex w-full items-center gap-2">
|
|
103
|
+
<span className="shrink-0 font-medium">{item.label}</span>
|
|
104
|
+
{value ? (
|
|
105
|
+
<span
|
|
106
|
+
data-slot="palette-value"
|
|
107
|
+
className="text-ink-mute ml-auto min-w-0 truncate text-end text-xs leading-tight"
|
|
108
|
+
>
|
|
109
|
+
{value}
|
|
110
|
+
</span>
|
|
111
|
+
) : null}
|
|
112
|
+
</span>
|
|
113
|
+
{item.description ? (
|
|
114
|
+
<span
|
|
115
|
+
id={detailId}
|
|
116
|
+
data-slot="palette-detail"
|
|
117
|
+
className="text-ink-mute hidden truncate text-xs leading-tight group-data-[highlighted]:block"
|
|
118
|
+
>
|
|
119
|
+
{item.description}
|
|
120
|
+
</span>
|
|
121
|
+
) : null}
|
|
122
|
+
</ComposerPrimitive.Unstable_TriggerPopoverItem>
|
|
123
|
+
);
|
|
124
|
+
})}
|
|
125
|
+
{items.length === 0 && (
|
|
126
|
+
<div className="text-ink-mute px-3 py-2 text-sm">
|
|
127
|
+
{isLoading ? loadingLabel : emptyLabel}
|
|
128
|
+
</div>
|
|
129
|
+
)}
|
|
130
|
+
</div>
|
|
131
|
+
<OverlayScrollbar targetRef={listRef} width={6} />
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
134
|
+
</ComposerPrimitive.Unstable_TriggerPopoverItems>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Arrow-key handling for the picker.
|
|
140
|
+
*
|
|
141
|
+
* The composer textarea keeps focus while the picker is open, so the keys are
|
|
142
|
+
* caught at document level (capture) rather than on the popover.
|
|
143
|
+
*
|
|
144
|
+
* It lives inside `Unstable_TriggerPopover` because the popover scope is the
|
|
145
|
+
* only place that exposes the picker's own `close()`. A synthetic Escape
|
|
146
|
+
* dispatched on `document` has `document` as its target, so it never reaches
|
|
147
|
+
* the composer's own keydown handler and the picker stayed open (DIG-4599).
|
|
148
|
+
*/
|
|
149
|
+
const TriggerPopoverArrowKeys: FC = () => {
|
|
150
|
+
const { open, close } = unstable_useTriggerPopoverScopeContext();
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
if (!open) return;
|
|
153
|
+
const onKey = (event: KeyboardEvent) => {
|
|
154
|
+
if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return;
|
|
155
|
+
const popover = document.querySelector<HTMLElement>(
|
|
156
|
+
'[data-slot="composer-trigger-popover"]',
|
|
157
|
+
);
|
|
158
|
+
if (!popover) return;
|
|
159
|
+
event.preventDefault();
|
|
160
|
+
event.stopPropagation();
|
|
161
|
+
if (event.key === "ArrowRight") {
|
|
162
|
+
// Same as Enter: submit the highlighted row.
|
|
163
|
+
popover.querySelector<HTMLElement>("[data-highlighted]")?.click();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
// Same as Escape: go back / close the picker, through the picker's own
|
|
167
|
+
// scope. Upstream's Escape handler closes the popover the same way.
|
|
168
|
+
close();
|
|
169
|
+
};
|
|
170
|
+
document.addEventListener("keydown", onKey, true);
|
|
171
|
+
return () => document.removeEventListener("keydown", onKey, true);
|
|
172
|
+
}, [open, close]);
|
|
173
|
+
return null;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Pre-built popover UI for a trigger-driven picker (mentions, slash commands).
|
|
178
|
+
* digichat: flat list, no category drill-down, no per-row icons, composer width.
|
|
179
|
+
*/
|
|
180
|
+
const ComposerTriggerPopoverImpl: FC<ComposerTriggerPopoverProps> = ({
|
|
181
|
+
emptyItemsLabel = "No matching items",
|
|
182
|
+
loadingLabel = "Loading…",
|
|
183
|
+
className,
|
|
184
|
+
directive,
|
|
185
|
+
action,
|
|
186
|
+
iconMap: _iconMap,
|
|
187
|
+
fallbackIcon: _fallbackIcon,
|
|
188
|
+
backLabel: _backLabel,
|
|
189
|
+
emptyCategoriesLabel: _emptyCategoriesLabel,
|
|
190
|
+
...props
|
|
191
|
+
}) => {
|
|
192
|
+
const warnedRef = useRef(false);
|
|
193
|
+
if (
|
|
194
|
+
process.env.NODE_ENV !== "production" &&
|
|
195
|
+
!warnedRef.current &&
|
|
196
|
+
Boolean(directive) === Boolean(action)
|
|
197
|
+
) {
|
|
198
|
+
warnedRef.current = true;
|
|
199
|
+
console.warn(
|
|
200
|
+
"[assistant-ui] ComposerTriggerPopover requires exactly one of `directive` or `action` props.",
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<ComposerPrimitive.Unstable_TriggerPopover
|
|
206
|
+
data-slot="composer-trigger-popover"
|
|
207
|
+
className={cn(
|
|
208
|
+
"aui-composer-trigger-popover bg-surface-2 text-ink absolute inset-x-0 bottom-full z-50 mb-1 w-full overflow-hidden rounded-none border",
|
|
209
|
+
className,
|
|
210
|
+
)}
|
|
211
|
+
{...props}
|
|
212
|
+
>
|
|
213
|
+
{directive ? (
|
|
214
|
+
<ComposerPrimitive.Unstable_TriggerPopover.Directive
|
|
215
|
+
formatter={directive.formatter ?? unstable_defaultDirectiveFormatter}
|
|
216
|
+
onInserted={directive.onInserted}
|
|
217
|
+
/>
|
|
218
|
+
) : action ? (
|
|
219
|
+
<ComposerPrimitive.Unstable_TriggerPopover.Action
|
|
220
|
+
formatter={action.formatter ?? unstable_defaultDirectiveFormatter}
|
|
221
|
+
onExecute={action.onExecute}
|
|
222
|
+
removeOnExecute={action.removeOnExecute}
|
|
223
|
+
/>
|
|
224
|
+
) : null}
|
|
225
|
+
<TriggerPopoverArrowKeys />
|
|
226
|
+
<PaletteRowPreview />
|
|
227
|
+
<Items emptyLabel={emptyItemsLabel} loadingLabel={loadingLabel} />
|
|
228
|
+
</ComposerPrimitive.Unstable_TriggerPopover>
|
|
229
|
+
);
|
|
230
|
+
};
|
|
231
|
+
ComposerTriggerPopoverImpl.displayName = "ComposerTriggerPopover";
|
|
232
|
+
|
|
233
|
+
export const ComposerTriggerPopover = memo(
|
|
234
|
+
ComposerTriggerPopoverImpl,
|
|
235
|
+
) as FC<ComposerTriggerPopoverProps>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
declare module "@assistant-ui/react-markdown/styles/dot.css";
|
|
2
|
+
|
|
3
|
+
// WS4: moved skins import the package stylesheets via relative side-effect
|
|
4
|
+
// imports. The app got its `*.css` declarations from Next's global types;
|
|
5
|
+
// the package needs its own.
|
|
6
|
+
declare module "*.css";
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { memo } from "react";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import type { FileMessagePartComponent } from "@assistant-ui/react";
|
|
6
|
+
import { DotMatrix } from "../DotMatrix";
|
|
7
|
+
import { cn } from "./cn";
|
|
8
|
+
|
|
9
|
+
const fileVariants = cva(
|
|
10
|
+
"aui-file-root inline-flex items-center gap-3 rounded-none transition-colors",
|
|
11
|
+
{
|
|
12
|
+
variants: {
|
|
13
|
+
variant: {
|
|
14
|
+
outline: "border-hair hover:bg-surface-2/50 border",
|
|
15
|
+
ghost: "hover:bg-surface-2/50",
|
|
16
|
+
muted: "bg-surface-2/50 hover:bg-surface-2",
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
sm: "px-2.5 py-1.5 text-xs",
|
|
20
|
+
default: "px-3 py-2 text-sm",
|
|
21
|
+
lg: "px-4 py-3 text-base",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "outline",
|
|
26
|
+
size: "default",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
export type FileDataKind = "data-uri" | "url" | "base64" | "id";
|
|
33
|
+
|
|
34
|
+
function getFileDataKind(
|
|
35
|
+
data: string,
|
|
36
|
+
sourceType?: "url" | "id",
|
|
37
|
+
): FileDataKind {
|
|
38
|
+
if (sourceType === "url" && /^data:/i.test(data)) return "data-uri";
|
|
39
|
+
if (sourceType) return sourceType;
|
|
40
|
+
if (/^data:/i.test(data)) return "data-uri";
|
|
41
|
+
if (/^https?:\/\//i.test(data)) return "url";
|
|
42
|
+
return "base64";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getBase64Size(base64: string): number {
|
|
46
|
+
const commaIndex = base64.indexOf(",");
|
|
47
|
+
const base64Data = commaIndex >= 0 ? base64.slice(commaIndex + 1) : base64;
|
|
48
|
+
const padding = (base64Data.match(/=/g) || []).length;
|
|
49
|
+
return Math.floor((base64Data.length * 3) / 4) - padding;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function formatFileSize(bytes: number): string {
|
|
53
|
+
if (bytes < 1024) {
|
|
54
|
+
return `${bytes} B`;
|
|
55
|
+
}
|
|
56
|
+
if (bytes < 1024 * 1024) {
|
|
57
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
58
|
+
}
|
|
59
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type FileRootProps = React.ComponentProps<"div"> &
|
|
63
|
+
VariantProps<typeof fileVariants>;
|
|
64
|
+
|
|
65
|
+
function FileRoot({
|
|
66
|
+
className,
|
|
67
|
+
variant,
|
|
68
|
+
size,
|
|
69
|
+
children,
|
|
70
|
+
...props
|
|
71
|
+
}: FileRootProps) {
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
data-slot="file-root"
|
|
75
|
+
data-variant={variant}
|
|
76
|
+
data-size={size}
|
|
77
|
+
className={cn(fileVariants({ variant, size, className }))}
|
|
78
|
+
{...props}
|
|
79
|
+
>
|
|
80
|
+
{children}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type FileIconDisplayProps = React.ComponentProps<"span"> & {
|
|
86
|
+
mimeType?: string;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
function FileIconDisplay({
|
|
90
|
+
mimeType,
|
|
91
|
+
className,
|
|
92
|
+
children,
|
|
93
|
+
...props
|
|
94
|
+
}: FileIconDisplayProps) {
|
|
95
|
+
return (
|
|
96
|
+
<span
|
|
97
|
+
data-slot="file-icon"
|
|
98
|
+
className={cn("text-ink-mute shrink-0", className)}
|
|
99
|
+
{...props}
|
|
100
|
+
>
|
|
101
|
+
{children ?? (
|
|
102
|
+
<DotMatrix
|
|
103
|
+
state="attach"
|
|
104
|
+
label={mimeType ? `${mimeType} file` : "File"}
|
|
105
|
+
className="size-3.5"
|
|
106
|
+
/>
|
|
107
|
+
)}
|
|
108
|
+
</span>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function FileName({
|
|
113
|
+
className,
|
|
114
|
+
children,
|
|
115
|
+
...props
|
|
116
|
+
}: React.ComponentProps<"span">) {
|
|
117
|
+
return (
|
|
118
|
+
<span
|
|
119
|
+
data-slot="file-name"
|
|
120
|
+
className={cn("min-w-0 flex-1 truncate font-medium", className)}
|
|
121
|
+
{...props}
|
|
122
|
+
>
|
|
123
|
+
{children || "Unnamed file"}
|
|
124
|
+
</span>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
type FileSizeProps = React.ComponentProps<"span"> & {
|
|
129
|
+
bytes: number;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
function FileSize({ bytes, className, ...props }: FileSizeProps) {
|
|
133
|
+
return (
|
|
134
|
+
<span
|
|
135
|
+
data-slot="file-size"
|
|
136
|
+
className={cn("text-ink-mute shrink-0", className)}
|
|
137
|
+
{...props}
|
|
138
|
+
>
|
|
139
|
+
{formatFileSize(bytes)}
|
|
140
|
+
</span>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
type FileDownloadProps = Omit<React.ComponentProps<"a">, "href"> & {
|
|
145
|
+
data: string;
|
|
146
|
+
mimeType: string;
|
|
147
|
+
filename?: string;
|
|
148
|
+
sourceType?: "url" | "id";
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
function FileDownload({
|
|
152
|
+
data,
|
|
153
|
+
mimeType,
|
|
154
|
+
filename,
|
|
155
|
+
sourceType,
|
|
156
|
+
className,
|
|
157
|
+
children,
|
|
158
|
+
...props
|
|
159
|
+
}: FileDownloadProps) {
|
|
160
|
+
if (typeof data !== "string") return null;
|
|
161
|
+
const kind = getFileDataKind(data, sourceType);
|
|
162
|
+
if (kind === "id") return null;
|
|
163
|
+
if (kind === "url" && !/^(https?:\/\/|blob:)/i.test(data)) return null;
|
|
164
|
+
const href = kind === "base64" ? `data:${mimeType};base64,${data}` : data;
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<a
|
|
168
|
+
data-slot="file-download"
|
|
169
|
+
href={href}
|
|
170
|
+
download={filename || "download"}
|
|
171
|
+
{...(kind === "url" && { target: "_blank", rel: "noopener noreferrer" })}
|
|
172
|
+
className={cn(
|
|
173
|
+
"text-ink-mute hover:bg-accent hover:text-accent-foreground shrink-0 rounded-none p-1 transition-colors",
|
|
174
|
+
className,
|
|
175
|
+
)}
|
|
176
|
+
{...props}
|
|
177
|
+
>
|
|
178
|
+
{children || <DotMatrix state="download" label="Download" className="size-3.5" />}
|
|
179
|
+
</a>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const FileImpl: FileMessagePartComponent = ({
|
|
184
|
+
filename,
|
|
185
|
+
data,
|
|
186
|
+
mimeType,
|
|
187
|
+
sourceType,
|
|
188
|
+
}) => {
|
|
189
|
+
const kind = getFileDataKind(data, sourceType);
|
|
190
|
+
const showSize =
|
|
191
|
+
typeof data === "string" && (kind === "base64" || kind === "data-uri");
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<FileRoot>
|
|
195
|
+
<FileIconDisplay mimeType={mimeType} />
|
|
196
|
+
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
197
|
+
<FileName>{filename}</FileName>
|
|
198
|
+
{showSize && (
|
|
199
|
+
<FileSize bytes={getBase64Size(data)} className="text-xs" />
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
<FileDownload
|
|
203
|
+
data={data}
|
|
204
|
+
mimeType={mimeType}
|
|
205
|
+
{...(filename !== undefined && { filename })}
|
|
206
|
+
{...(sourceType !== undefined && { sourceType })}
|
|
207
|
+
/>
|
|
208
|
+
</FileRoot>
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const File = memo(FileImpl) as unknown as FileMessagePartComponent & {
|
|
213
|
+
Root: typeof FileRoot;
|
|
214
|
+
Icon: typeof FileIconDisplay;
|
|
215
|
+
Name: typeof FileName;
|
|
216
|
+
Size: typeof FileSize;
|
|
217
|
+
Download: typeof FileDownload;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
File.displayName = "File";
|
|
221
|
+
File.Root = FileRoot;
|
|
222
|
+
File.Icon = FileIconDisplay;
|
|
223
|
+
File.Name = FileName;
|
|
224
|
+
File.Size = FileSize;
|
|
225
|
+
File.Download = FileDownload;
|
|
226
|
+
|
|
227
|
+
export {
|
|
228
|
+
File,
|
|
229
|
+
FileRoot,
|
|
230
|
+
FileIconDisplay,
|
|
231
|
+
FileName,
|
|
232
|
+
FileSize,
|
|
233
|
+
FileDownload,
|
|
234
|
+
fileVariants,
|
|
235
|
+
getFileDataKind,
|
|
236
|
+
getBase64Size,
|
|
237
|
+
formatFileSize,
|
|
238
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { AuiIf, useAuiState, ThreadPrimitive } from "@assistant-ui/react";
|
|
4
|
+
import { useCallback, useEffect, useRef, useState, type FC } from "react";
|
|
5
|
+
|
|
6
|
+
import { DotMatrix } from "../DotMatrix";
|
|
7
|
+
import { dismissKeyboardIfCoarse } from "./composer-keyboard";
|
|
8
|
+
import { SuggestionScrollRow } from "../suggestion-scroll-row";
|
|
9
|
+
|
|
10
|
+
const FollowupSuggestionsRow: FC = () => {
|
|
11
|
+
const suggestions = useAuiState((s) => s.thread.suggestions);
|
|
12
|
+
const scrollRef = useRef<HTMLDivElement>(null);
|
|
13
|
+
const rtlRef = useRef<boolean | null>(null);
|
|
14
|
+
const [fades, setFades] = useState({ left: false, right: false });
|
|
15
|
+
|
|
16
|
+
const updateFades = useCallback(() => {
|
|
17
|
+
const el = scrollRef.current;
|
|
18
|
+
if (!el) return;
|
|
19
|
+
const maxScroll = el.scrollWidth - el.clientWidth;
|
|
20
|
+
// scrollLeft runs 0..-max in RTL; normalize to hidden width per physical edge.
|
|
21
|
+
const fromStart = Math.abs(el.scrollLeft);
|
|
22
|
+
// getComputedStyle forces a style recalc per scroll event; direction is stable, read it once.
|
|
23
|
+
const rtl = (rtlRef.current ??= getComputedStyle(el).direction === "rtl");
|
|
24
|
+
const [left, right] = rtl
|
|
25
|
+
? [maxScroll - fromStart, fromStart]
|
|
26
|
+
: [fromStart, maxScroll - fromStart];
|
|
27
|
+
setFades((prev) => {
|
|
28
|
+
const next = { left: left > 1, right: right > 1 };
|
|
29
|
+
return prev.left === next.left && prev.right === next.right ? prev : next;
|
|
30
|
+
});
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
updateFades();
|
|
35
|
+
const el = scrollRef.current;
|
|
36
|
+
if (!el?.firstElementChild) return undefined;
|
|
37
|
+
const observer = new ResizeObserver(updateFades);
|
|
38
|
+
observer.observe(el);
|
|
39
|
+
observer.observe(el.firstElementChild);
|
|
40
|
+
return () => observer.disconnect();
|
|
41
|
+
}, [updateFades]);
|
|
42
|
+
|
|
43
|
+
const maskImage = `linear-gradient(to right, ${
|
|
44
|
+
fades.left ? "transparent, black 2rem" : "black"
|
|
45
|
+
}, ${fades.right ? "black calc(100% - 2rem), transparent" : "black"})`;
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<SuggestionScrollRow
|
|
49
|
+
ref={scrollRef}
|
|
50
|
+
onScroll={updateFades}
|
|
51
|
+
// overflow-x clips both axes; py-1/-my-1 gives focus rings vertical room without changing outer height.
|
|
52
|
+
className="aui-thread-followup-suggestions -my-1 w-full py-1"
|
|
53
|
+
style={{ maskImage, WebkitMaskImage: maskImage }}
|
|
54
|
+
>
|
|
55
|
+
<div className="mx-auto flex min-h-8 w-max items-center gap-2 px-0.5">
|
|
56
|
+
{suggestions.map((suggestion, idx) => (
|
|
57
|
+
<ThreadPrimitive.Suggestion
|
|
58
|
+
key={idx}
|
|
59
|
+
className="aui-thread-followup-suggestion bg-surface hover:bg-surface-2 rounded-none border px-3 py-1 text-sm whitespace-nowrap transition-colors ease-in"
|
|
60
|
+
prompt={suggestion.prompt}
|
|
61
|
+
send
|
|
62
|
+
onClick={() => dismissKeyboardIfCoarse()}
|
|
63
|
+
>
|
|
64
|
+
<DotMatrix
|
|
65
|
+
state="example"
|
|
66
|
+
decorative
|
|
67
|
+
className="aui-thread-followup-suggestion-mark size-3.5"
|
|
68
|
+
/>
|
|
69
|
+
{suggestion.title ?? suggestion.prompt}
|
|
70
|
+
{suggestion.label && (
|
|
71
|
+
<span className="aui-thread-followup-suggestion-label text-ink-mute ms-1">
|
|
72
|
+
{suggestion.label}
|
|
73
|
+
</span>
|
|
74
|
+
)}
|
|
75
|
+
</ThreadPrimitive.Suggestion>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
</SuggestionScrollRow>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const ThreadFollowupSuggestions: FC = () => (
|
|
83
|
+
<AuiIf
|
|
84
|
+
condition={(s) =>
|
|
85
|
+
!s.thread.isEmpty &&
|
|
86
|
+
!s.thread.isRunning &&
|
|
87
|
+
s.thread.suggestions.length > 0
|
|
88
|
+
}
|
|
89
|
+
>
|
|
90
|
+
<FollowupSuggestionsRow />
|
|
91
|
+
</AuiIf>
|
|
92
|
+
);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upper bound on the text repairPartialJson will scan. Tool arguments and
|
|
3
|
+
* results arrive from the model stream and nothing bounds their length, so
|
|
4
|
+
* the repair must never see an unbounded string: a long whitespace run used
|
|
5
|
+
* to cost a quadratic scan and froze the render thread (CodeQL
|
|
6
|
+
* js/polynomial-redos, alert 196, DIG-4818). Longer text is shown raw, never
|
|
7
|
+
* cut.
|
|
8
|
+
*
|
|
9
|
+
* The cap guards the repair path only. Valid JSON of any size still goes
|
|
10
|
+
* through JSON.parse and is pretty-printed as before; the one behaviour
|
|
11
|
+
* change above the cap is that partial (unparsable) JSON longer than the cap
|
|
12
|
+
* is shown raw instead of repaired and pretty-printed. The cap is defence in
|
|
13
|
+
* depth: the whitespace strip itself is linear now, but every regex in the
|
|
14
|
+
* repair stays bounded by this budget.
|
|
15
|
+
*/
|
|
16
|
+
export const MAX_REPAIR_INPUT_CHARS = 64 * 1024;
|
|
17
|
+
|
|
18
|
+
/** Pretty-print tool args the same way results already use JSON.stringify(..., null, 2). */
|
|
19
|
+
export function formatJsonDump(value: unknown): string {
|
|
20
|
+
if (value === undefined) return "";
|
|
21
|
+
if (typeof value === "string") {
|
|
22
|
+
const trimmed = value.trim();
|
|
23
|
+
if (!trimmed) return value;
|
|
24
|
+
try {
|
|
25
|
+
return JSON.stringify(JSON.parse(trimmed), null, 2);
|
|
26
|
+
} catch {
|
|
27
|
+
const repaired = repairPartialJson(trimmed);
|
|
28
|
+
if (repaired !== undefined) {
|
|
29
|
+
try {
|
|
30
|
+
return JSON.stringify(JSON.parse(repaired), null, 2);
|
|
31
|
+
} catch {
|
|
32
|
+
/* fall through to raw text */
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
return JSON.stringify(value, null, 2);
|
|
40
|
+
} catch {
|
|
41
|
+
return String(value);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function formatToolDurationMs(ms: number): string {
|
|
46
|
+
if (!Number.isFinite(ms) || ms < 0) return "0ms";
|
|
47
|
+
const rounded = Math.round(ms);
|
|
48
|
+
if (rounded < 100) return `${rounded}ms`;
|
|
49
|
+
const seconds = rounded / 1000;
|
|
50
|
+
if (seconds < 10) return `${(Math.round(seconds * 10) / 10).toFixed(1)}s`;
|
|
51
|
+
if (seconds < 60) return `${Math.floor(seconds)}s`;
|
|
52
|
+
return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Close a truncated JSON prefix (streaming tool args) so it can be parsed:
|
|
57
|
+
* closes an open string, drops a dangling key / trailing comma / colon, and
|
|
58
|
+
* closes open objects and arrays. Returns undefined when the text is not a
|
|
59
|
+
* JSON object/array prefix.
|
|
60
|
+
*/
|
|
61
|
+
export function repairPartialJson(text: string): string | undefined {
|
|
62
|
+
const src = text.trim();
|
|
63
|
+
if (!src || (src[0] !== "{" && src[0] !== "[")) return undefined;
|
|
64
|
+
// Cap before any scan of the text: the repair below runs regexes over it.
|
|
65
|
+
// Defence in depth for those regexes, not the fix itself: the quadratic
|
|
66
|
+
// whitespace strip is gone (trimEnd below).
|
|
67
|
+
if (src.length > MAX_REPAIR_INPUT_CHARS) return undefined;
|
|
68
|
+
const stack: string[] = [];
|
|
69
|
+
let inString = false;
|
|
70
|
+
let escaped = false;
|
|
71
|
+
for (const ch of src) {
|
|
72
|
+
if (inString) {
|
|
73
|
+
if (escaped) escaped = false;
|
|
74
|
+
else if (ch === "\\") escaped = true;
|
|
75
|
+
else if (ch === '"') inString = false;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (ch === '"') inString = true;
|
|
79
|
+
else if (ch === "{" || ch === "[") stack.push(ch);
|
|
80
|
+
else if (ch === "}" || ch === "]") {
|
|
81
|
+
if (stack.pop() !== (ch === "}" ? "{" : "[")) return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
let out = src;
|
|
85
|
+
if (inString) {
|
|
86
|
+
if (escaped) out = out.slice(0, -1);
|
|
87
|
+
out += '"';
|
|
88
|
+
}
|
|
89
|
+
for (let i = 0; i < 4; i++) {
|
|
90
|
+
const before = out;
|
|
91
|
+
// trimEnd, not out.replace(/\s+$/, ""): the regex re-tries the anchor at
|
|
92
|
+
// every position of a whitespace run and costs quadratic time on a long
|
|
93
|
+
// run, which froze the render thread (CodeQL js/polynomial-redos, alert
|
|
94
|
+
// 196). Same result, one linear pass.
|
|
95
|
+
out = out.trimEnd();
|
|
96
|
+
out = out.replace(/,$/, "");
|
|
97
|
+
out = out.replace(/:$/, "");
|
|
98
|
+
// dangling key with no value inside an object: {"a":1,"b"
|
|
99
|
+
if (stack[stack.length - 1] === "{") out = out.replace(/([{,])\s*"(?:[^"\\]|\\.)*"$/, "$1");
|
|
100
|
+
out = out.replace(/,$/, "");
|
|
101
|
+
// partial literal (tru, fals, nul) or number ending in '-' / '.' / 'e'
|
|
102
|
+
// No lookbehind here: a lookbehind literal is a parse-time SyntaxError on
|
|
103
|
+
// Safari below 16.4 and on older embedded WebViews, and this module is
|
|
104
|
+
// imported by the rendered thread — one unparsable regex would take the
|
|
105
|
+
// whole thread down. The prefix is captured and written back instead.
|
|
106
|
+
out = out.replace(/([:\[,]\s*)(t|tr|tru|f|fa|fal|fals|n|nu|nul)$/, "$1");
|
|
107
|
+
out = out.replace(/(\d)[.eE+-]+$/, "$1").replace(/([:\[,]\s*)-$/, "$1");
|
|
108
|
+
if (out === before) break;
|
|
109
|
+
}
|
|
110
|
+
while (stack.length) out += stack.pop() === "{" ? "}" : "]";
|
|
111
|
+
return out;
|
|
112
|
+
}
|