@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,163 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
|
|
5
|
+
import { cn } from "../../../../lib/utils"
|
|
6
|
+
|
|
7
|
+
import { XIcon } from "lucide-react"
|
|
8
|
+
|
|
9
|
+
import { Button } from "./button"
|
|
10
|
+
import { stockPortalProps, useStockShape } from "../stock-shape"
|
|
11
|
+
|
|
12
|
+
function Dialog({ ...props }: DialogPrimitive.Root.Props) {
|
|
13
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
|
|
17
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
|
|
21
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
|
|
25
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function DialogOverlay({
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}: DialogPrimitive.Backdrop.Props) {
|
|
32
|
+
return (
|
|
33
|
+
<DialogPrimitive.Backdrop
|
|
34
|
+
data-slot="dialog-overlay"
|
|
35
|
+
className={cn(
|
|
36
|
+
"fixed inset-0 isolate z-50 bg-black/10 duration-(--dur-fast) supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function DialogContent({
|
|
45
|
+
className,
|
|
46
|
+
children,
|
|
47
|
+
showCloseButton = true,
|
|
48
|
+
...props
|
|
49
|
+
}: DialogPrimitive.Popup.Props & {
|
|
50
|
+
showCloseButton?: boolean
|
|
51
|
+
}) {
|
|
52
|
+
const stock = useStockShape()
|
|
53
|
+
return (
|
|
54
|
+
<DialogPortal>
|
|
55
|
+
<DialogOverlay />
|
|
56
|
+
<DialogPrimitive.Popup
|
|
57
|
+
data-slot="dialog-content"
|
|
58
|
+
{...stockPortalProps(stock)}
|
|
59
|
+
className={cn(
|
|
60
|
+
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-surface-2 p-4 text-sm text-ink ring-1 ring-foreground/10 duration-(--dur-fast) outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
61
|
+
className
|
|
62
|
+
)}
|
|
63
|
+
{...props}
|
|
64
|
+
>
|
|
65
|
+
{children}
|
|
66
|
+
{showCloseButton && (
|
|
67
|
+
<DialogPrimitive.Close
|
|
68
|
+
data-slot="dialog-close"
|
|
69
|
+
render={
|
|
70
|
+
<Button
|
|
71
|
+
variant="ghost"
|
|
72
|
+
className="absolute top-2 right-2"
|
|
73
|
+
size="icon-sm"
|
|
74
|
+
/>
|
|
75
|
+
}
|
|
76
|
+
>
|
|
77
|
+
<XIcon />
|
|
78
|
+
<span className="sr-only">Close</span>
|
|
79
|
+
</DialogPrimitive.Close>
|
|
80
|
+
)}
|
|
81
|
+
</DialogPrimitive.Popup>
|
|
82
|
+
</DialogPortal>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
data-slot="dialog-header"
|
|
90
|
+
className={cn("flex flex-col gap-2", className)}
|
|
91
|
+
{...props}
|
|
92
|
+
/>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function DialogFooter({
|
|
97
|
+
className,
|
|
98
|
+
showCloseButton = false,
|
|
99
|
+
children,
|
|
100
|
+
...props
|
|
101
|
+
}: React.ComponentProps<"div"> & {
|
|
102
|
+
showCloseButton?: boolean
|
|
103
|
+
}) {
|
|
104
|
+
return (
|
|
105
|
+
<div
|
|
106
|
+
data-slot="dialog-footer"
|
|
107
|
+
className={cn(
|
|
108
|
+
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-surface-2/50 p-4 sm:flex-row sm:justify-end",
|
|
109
|
+
className
|
|
110
|
+
)}
|
|
111
|
+
{...props}
|
|
112
|
+
>
|
|
113
|
+
{children}
|
|
114
|
+
{showCloseButton && (
|
|
115
|
+
<DialogPrimitive.Close render={<Button variant="outline" />}>
|
|
116
|
+
Close
|
|
117
|
+
</DialogPrimitive.Close>
|
|
118
|
+
)}
|
|
119
|
+
</div>
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
|
|
124
|
+
return (
|
|
125
|
+
<DialogPrimitive.Title
|
|
126
|
+
data-slot="dialog-title"
|
|
127
|
+
className={cn(
|
|
128
|
+
"cn-font-heading text-base leading-none font-medium",
|
|
129
|
+
className
|
|
130
|
+
)}
|
|
131
|
+
{...props}
|
|
132
|
+
/>
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function DialogDescription({
|
|
137
|
+
className,
|
|
138
|
+
...props
|
|
139
|
+
}: DialogPrimitive.Description.Props) {
|
|
140
|
+
return (
|
|
141
|
+
<DialogPrimitive.Description
|
|
142
|
+
data-slot="dialog-description"
|
|
143
|
+
className={cn(
|
|
144
|
+
"text-sm text-ink-mute *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-ink",
|
|
145
|
+
className
|
|
146
|
+
)}
|
|
147
|
+
{...props}
|
|
148
|
+
/>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export {
|
|
153
|
+
Dialog,
|
|
154
|
+
DialogClose,
|
|
155
|
+
DialogContent,
|
|
156
|
+
DialogDescription,
|
|
157
|
+
DialogFooter,
|
|
158
|
+
DialogHeader,
|
|
159
|
+
DialogOverlay,
|
|
160
|
+
DialogPortal,
|
|
161
|
+
DialogTitle,
|
|
162
|
+
DialogTrigger,
|
|
163
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cn } from "../../../../lib/utils"
|
|
2
|
+
|
|
3
|
+
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
|
4
|
+
return (
|
|
5
|
+
<div
|
|
6
|
+
data-slot="skeleton"
|
|
7
|
+
className={cn("animate-pulse rounded-md bg-surface-2", className)}
|
|
8
|
+
{...props}
|
|
9
|
+
/>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { Skeleton }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "../../../../lib/utils"
|
|
3
|
+
import { useStockShape } from "../stock-shape"
|
|
4
|
+
|
|
5
|
+
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|
6
|
+
const stock = useStockShape()
|
|
7
|
+
return (
|
|
8
|
+
<textarea
|
|
9
|
+
data-slot="textarea"
|
|
10
|
+
className={cn(
|
|
11
|
+
"flex field-sizing-content min-h-16 w-full rounded-none border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-ink-mute focus-visible:border-accent focus-visible:ring-3 focus-visible:ring-accent/50 disabled:cursor-not-allowed disabled:bg-surface-2/50 disabled:opacity-50 aria-invalid:border-danger aria-invalid:ring-3 aria-invalid:ring-danger/20 md:text-sm dark:bg-surface-2/30 dark:disabled:bg-surface-2/80 dark:aria-invalid:border-danger/50 dark:aria-invalid:ring-danger/40",
|
|
12
|
+
stock && "rounded-lg",
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { Textarea }
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
|
|
4
|
+
import { cn } from "../../../../lib/utils"
|
|
5
|
+
import { useStockShape } from "../stock-shape"
|
|
6
|
+
|
|
7
|
+
function TooltipProvider({
|
|
8
|
+
delay = 0,
|
|
9
|
+
...props
|
|
10
|
+
}: TooltipPrimitive.Provider.Props) {
|
|
11
|
+
return (
|
|
12
|
+
<TooltipPrimitive.Provider
|
|
13
|
+
data-slot="tooltip-provider"
|
|
14
|
+
delay={delay}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
|
|
21
|
+
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
|
|
25
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function TooltipContent({
|
|
29
|
+
className,
|
|
30
|
+
side = "top",
|
|
31
|
+
sideOffset = 4,
|
|
32
|
+
align = "center",
|
|
33
|
+
alignOffset = 0,
|
|
34
|
+
children,
|
|
35
|
+
...props
|
|
36
|
+
}: TooltipPrimitive.Popup.Props &
|
|
37
|
+
Pick<
|
|
38
|
+
TooltipPrimitive.Positioner.Props,
|
|
39
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
40
|
+
>) {
|
|
41
|
+
const stock = useStockShape()
|
|
42
|
+
return (
|
|
43
|
+
<TooltipPrimitive.Portal>
|
|
44
|
+
<TooltipPrimitive.Positioner
|
|
45
|
+
align={align}
|
|
46
|
+
alignOffset={alignOffset}
|
|
47
|
+
side={side}
|
|
48
|
+
sideOffset={sideOffset}
|
|
49
|
+
className="isolate z-50"
|
|
50
|
+
>
|
|
51
|
+
<TooltipPrimitive.Popup
|
|
52
|
+
data-slot="tooltip-content"
|
|
53
|
+
className={cn(
|
|
54
|
+
"z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-none bg-ink px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-none data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
55
|
+
stock && "rounded-md **:data-[slot=kbd]:rounded-sm",
|
|
56
|
+
className
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
>
|
|
60
|
+
{children}
|
|
61
|
+
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-ink fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
|
|
62
|
+
</TooltipPrimitive.Popup>
|
|
63
|
+
</TooltipPrimitive.Positioner>
|
|
64
|
+
</TooltipPrimitive.Portal>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { useAuiState } from "@assistant-ui/react";
|
|
5
|
+
import { useShallow } from "zustand/react/shallow";
|
|
6
|
+
|
|
7
|
+
const useFileSrc = (file: File | undefined) => {
|
|
8
|
+
const [entry, setEntry] = useState<{ file: File; url: string } | undefined>(
|
|
9
|
+
undefined,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!file) {
|
|
14
|
+
setEntry(undefined);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const objectUrl = URL.createObjectURL(file);
|
|
19
|
+
setEntry({ file, url: objectUrl });
|
|
20
|
+
|
|
21
|
+
return () => {
|
|
22
|
+
URL.revokeObjectURL(objectUrl);
|
|
23
|
+
};
|
|
24
|
+
}, [file]);
|
|
25
|
+
|
|
26
|
+
return entry !== undefined && entry.file === file ? entry.url : undefined;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const useAttachmentSrc = () => {
|
|
30
|
+
const { file, src } = useAuiState(
|
|
31
|
+
useShallow((s): { file?: File; src?: string } => {
|
|
32
|
+
if (s.attachment.type !== "image") return {};
|
|
33
|
+
if (s.attachment.file) return { file: s.attachment.file };
|
|
34
|
+
const src = s.attachment.content?.filter((c) => c.type === "image")[0]
|
|
35
|
+
?.image;
|
|
36
|
+
if (!src) return {};
|
|
37
|
+
return { src };
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return useFileSrc(file) ?? src;
|
|
42
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
|
|
5
|
+
export type UseCopyToClipboardOptions = {
|
|
6
|
+
copiedDuration?: number;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const useCopyToClipboard = ({
|
|
10
|
+
copiedDuration = 3000,
|
|
11
|
+
}: UseCopyToClipboardOptions = {}) => {
|
|
12
|
+
const [isCopied, setIsCopied] = useState<boolean>(false);
|
|
13
|
+
|
|
14
|
+
const copyToClipboard = (value: string) => {
|
|
15
|
+
if (!value || typeof navigator === "undefined" || !navigator.clipboard) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
navigator.clipboard.writeText(value).then(
|
|
20
|
+
() => {
|
|
21
|
+
setIsCopied(true);
|
|
22
|
+
setTimeout(() => setIsCopied(false), copiedDuration);
|
|
23
|
+
},
|
|
24
|
+
() => {},
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return { isCopied, copyToClipboard };
|
|
29
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek models sometimes echo their own search-template instruction into
|
|
3
|
+
* the visible answer ("Use the results below to formulate an answer to the
|
|
4
|
+
* user question unless additional information is needed"), often twice in a
|
|
5
|
+
* row. It is never content, so drop it from rendered markdown (DIG-1210).
|
|
6
|
+
*/
|
|
7
|
+
const PHRASE =
|
|
8
|
+
"Use the results below to formulate an answer to the user question unless additional information is needed";
|
|
9
|
+
|
|
10
|
+
const esc = (t: string) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11
|
+
// Words joined by flexible whitespace, so line-wrapped copies still match.
|
|
12
|
+
const FULL = PHRASE.split(" ").map(esc).join("\\s+");
|
|
13
|
+
/** One or more back-to-back full copies, with or without separators. */
|
|
14
|
+
const REPEATED = new RegExp(`\\s*(?:${FULL}\\s*[.,;:]?\\s*)+`, "gi");
|
|
15
|
+
|
|
16
|
+
/** Drop a copy cut off mid-phrase at the very end (streaming or truncated). */
|
|
17
|
+
function stripTrailingPartial(text: string): string {
|
|
18
|
+
const lower = text.toLowerCase();
|
|
19
|
+
const at = lower.lastIndexOf("use the");
|
|
20
|
+
if (at < 0) return text;
|
|
21
|
+
const tail = lower.slice(at).replace(/\s+/g, " ").trimEnd();
|
|
22
|
+
return PHRASE.toLowerCase().startsWith(tail) ? text.slice(0, at).trimEnd() : text;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function stripModelBoilerplate(text: string): string {
|
|
26
|
+
const out = text.replace(REPEATED, (_m, offset: number) => (offset === 0 ? "" : " "));
|
|
27
|
+
return stripTrailingPartial(out).replace(/ {2,}/g, " ");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type TextNode = { type: string; value?: string; children?: TextNode[] };
|
|
31
|
+
|
|
32
|
+
function walk(node: TextNode): void {
|
|
33
|
+
if (node.type === "text" && typeof node.value === "string") {
|
|
34
|
+
node.value = stripModelBoilerplate(node.value);
|
|
35
|
+
}
|
|
36
|
+
for (const child of node.children ?? []) walk(child);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** remark plugin: strip the boilerplate from every text node. */
|
|
40
|
+
export function remarkStripModelBoilerplate() {
|
|
41
|
+
return (tree: TextNode) => walk(tree);
|
|
42
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The empty-state starter chips, capped and cycled.
|
|
5
|
+
*
|
|
6
|
+
* A tenant defines its own starter list (eight for the house tenant, five for
|
|
7
|
+
* the trial) and the welcome used to print all of them, turning the empty state into
|
|
8
|
+
* a wall of prompts. Four is what fits on one line of reading; the rest are
|
|
9
|
+
* reached by cycling, so every defined starter is still seen.
|
|
10
|
+
*
|
|
11
|
+
* The cycle is deliberately polite:
|
|
12
|
+
* - it pauses while the pointer is over the chips or focus is inside them,
|
|
13
|
+
* because a list that changes under the cursor is a list you cannot click;
|
|
14
|
+
* - it stops for good once the conversation has started or the composer holds
|
|
15
|
+
* text, so the prompts never move under someone who is reading them while
|
|
16
|
+
* composing;
|
|
17
|
+
* - it never starts under `prefers-reduced-motion: reduce`, which pins the
|
|
18
|
+
* first group;
|
|
19
|
+
* - the timer is cleared on unmount and whenever those conditions stop it, so
|
|
20
|
+
* nothing ticks in the background.
|
|
21
|
+
*
|
|
22
|
+
* The group size is constant, so a swap never reflows the row: the wrapper is
|
|
23
|
+
* keyed by the group offset and the chips re-run their existing entrance fade
|
|
24
|
+
* (house duration tokens) instead of the row growing or shrinking.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
createContext,
|
|
29
|
+
useContext,
|
|
30
|
+
useEffect,
|
|
31
|
+
useMemo,
|
|
32
|
+
useState,
|
|
33
|
+
type FocusEventHandler,
|
|
34
|
+
type PointerEventHandler,
|
|
35
|
+
type ReactNode,
|
|
36
|
+
} from "react";
|
|
37
|
+
|
|
38
|
+
/** How many starter chips are on screen at once. */
|
|
39
|
+
export const VISIBLE_SUGGESTIONS = 4;
|
|
40
|
+
|
|
41
|
+
/** How long a group stays before the next one fades in. */
|
|
42
|
+
export const SUGGESTION_CYCLE_MS = 8_000;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The window of `all` starting at `offset`, wrapping. Returns the whole list
|
|
46
|
+
* when it already fits, so a tenant with 3 starters is never padded.
|
|
47
|
+
*/
|
|
48
|
+
export function suggestionWindow(
|
|
49
|
+
all: readonly string[],
|
|
50
|
+
offset: number,
|
|
51
|
+
): readonly string[] {
|
|
52
|
+
if (all.length <= VISIBLE_SUGGESTIONS) return all;
|
|
53
|
+
const start = ((offset % all.length) + all.length) % all.length;
|
|
54
|
+
return Array.from(
|
|
55
|
+
{ length: VISIBLE_SUGGESTIONS },
|
|
56
|
+
(_, index) => all[(start + index) % all.length]!,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Reduced-motion, read directly: the cycle is a JS timer, so CSS cannot stop it. */
|
|
61
|
+
function usePrefersReducedMotion(): boolean {
|
|
62
|
+
// Lazy read: the group on screen is the same either way (offset 0), so the
|
|
63
|
+
// client's real preference cannot desync the server markup.
|
|
64
|
+
const [reduced, setReduced] = useState(
|
|
65
|
+
() =>
|
|
66
|
+
typeof window !== "undefined" &&
|
|
67
|
+
Boolean(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches),
|
|
68
|
+
);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
71
|
+
const query = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
72
|
+
const onChange = (event: MediaQueryListEvent) => setReduced(event.matches);
|
|
73
|
+
query.addEventListener?.("change", onChange);
|
|
74
|
+
return () => query.removeEventListener?.("change", onChange);
|
|
75
|
+
}, []);
|
|
76
|
+
return reduced;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Handlers a chip row spreads so the cycle pauses while it is being read. */
|
|
80
|
+
export type SuggestionCycleHandlers = {
|
|
81
|
+
onPointerEnter: PointerEventHandler;
|
|
82
|
+
onPointerLeave: PointerEventHandler;
|
|
83
|
+
onFocus: FocusEventHandler;
|
|
84
|
+
onBlur: FocusEventHandler;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type SuggestionCycle = {
|
|
88
|
+
/** The group currently on screen. */
|
|
89
|
+
visible: readonly string[];
|
|
90
|
+
/** Index of the group's first chip — also the row's remount key. */
|
|
91
|
+
offset: number;
|
|
92
|
+
/** True while hovered or focused: the cycle is held, not stopped. */
|
|
93
|
+
paused: boolean;
|
|
94
|
+
handlers: SuggestionCycleHandlers;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Cap `all` to {@link VISIBLE_SUGGESTIONS} and advance through it every
|
|
99
|
+
* {@link SUGGESTION_CYCLE_MS}.
|
|
100
|
+
*
|
|
101
|
+
* `stopped` is the caller's "the user is no longer looking at a fresh empty
|
|
102
|
+
* state" signal (thread has messages, composer has text).
|
|
103
|
+
*/
|
|
104
|
+
export function useSuggestionCycle(
|
|
105
|
+
all: readonly string[],
|
|
106
|
+
{ stopped = false }: { stopped?: boolean } = {},
|
|
107
|
+
): SuggestionCycle {
|
|
108
|
+
const [offset, setOffset] = useState(0);
|
|
109
|
+
const [hovered, setHovered] = useState(false);
|
|
110
|
+
const [focused, setFocused] = useState(false);
|
|
111
|
+
const reduced = usePrefersReducedMotion();
|
|
112
|
+
|
|
113
|
+
const paused = hovered || focused;
|
|
114
|
+
const cycles = all.length > VISIBLE_SUGGESTIONS;
|
|
115
|
+
const active = cycles && !stopped && !paused && !reduced;
|
|
116
|
+
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (!active) return;
|
|
119
|
+
const timer = setInterval(
|
|
120
|
+
() => setOffset((current) => current + VISIBLE_SUGGESTIONS),
|
|
121
|
+
SUGGESTION_CYCLE_MS,
|
|
122
|
+
);
|
|
123
|
+
return () => clearInterval(timer);
|
|
124
|
+
}, [active]);
|
|
125
|
+
|
|
126
|
+
const handlers = useMemo<SuggestionCycleHandlers>(
|
|
127
|
+
() => ({
|
|
128
|
+
onPointerEnter: () => setHovered(true),
|
|
129
|
+
onPointerLeave: () => setHovered(false),
|
|
130
|
+
onFocus: () => setFocused(true),
|
|
131
|
+
onBlur: () => setFocused(false),
|
|
132
|
+
}),
|
|
133
|
+
[],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const visible = useMemo(
|
|
137
|
+
() => suggestionWindow(all, offset),
|
|
138
|
+
[all, offset],
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
return { visible, offset, paused, handlers };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const SuggestionCycleContext = createContext<SuggestionCycle | null>(null);
|
|
145
|
+
|
|
146
|
+
/** Mounts the cycle above the skins so every renderer shares one timer. */
|
|
147
|
+
export function SuggestionCycleProvider({
|
|
148
|
+
value,
|
|
149
|
+
children,
|
|
150
|
+
}: {
|
|
151
|
+
value: SuggestionCycle;
|
|
152
|
+
children: ReactNode;
|
|
153
|
+
}) {
|
|
154
|
+
return (
|
|
155
|
+
<SuggestionCycleContext.Provider value={value}>
|
|
156
|
+
{children}
|
|
157
|
+
</SuggestionCycleContext.Provider>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* What a skin's chip row needs: the remount key (so the group fades in) and
|
|
163
|
+
* the pause handlers. Surfaces mounted without the provider still render —
|
|
164
|
+
* they simply never cycle.
|
|
165
|
+
*/
|
|
166
|
+
export function useSuggestionGroup(): {
|
|
167
|
+
groupKey: number;
|
|
168
|
+
handlers: SuggestionCycleHandlers;
|
|
169
|
+
} {
|
|
170
|
+
const cycle = useContext(SuggestionCycleContext);
|
|
171
|
+
return useMemo(
|
|
172
|
+
() => ({
|
|
173
|
+
groupKey: cycle?.offset ?? 0,
|
|
174
|
+
handlers: cycle?.handlers ?? NO_HANDLERS,
|
|
175
|
+
}),
|
|
176
|
+
[cycle],
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const NO_HANDLERS: SuggestionCycleHandlers = {
|
|
181
|
+
onPointerEnter: () => {},
|
|
182
|
+
onPointerLeave: () => {},
|
|
183
|
+
onFocus: () => {},
|
|
184
|
+
onBlur: () => {},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Has the visitor left the fresh empty state? Reads the assistant-ui runtime
|
|
189
|
+
* directly so the shell that owns the starter list can stop the cycle without
|
|
190
|
+
* sitting inside the runtime provider. Defensive on purpose: a runtime double
|
|
191
|
+
* or an SSR pass without a live thread simply reports "not started".
|
|
192
|
+
*/
|
|
193
|
+
export function useThreadActivity(
|
|
194
|
+
runtime: { thread?: unknown } | null | undefined,
|
|
195
|
+
): { started: boolean; typed: boolean } {
|
|
196
|
+
const thread = runtime?.thread as
|
|
197
|
+
| {
|
|
198
|
+
getState?: () => { messages?: readonly unknown[] } | undefined;
|
|
199
|
+
subscribe?: (callback: () => void) => (() => void) | void;
|
|
200
|
+
composer?: { getState?: () => { text?: string } | undefined };
|
|
201
|
+
}
|
|
202
|
+
| undefined;
|
|
203
|
+
const [started, setStarted] = useState(false);
|
|
204
|
+
const [typed, setTyped] = useState(false);
|
|
205
|
+
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
if (typeof thread?.getState !== "function") return;
|
|
208
|
+
const read = () => {
|
|
209
|
+
let messages: readonly unknown[] | undefined;
|
|
210
|
+
let text: string | undefined;
|
|
211
|
+
try {
|
|
212
|
+
messages = thread.getState?.()?.messages;
|
|
213
|
+
text = thread.composer?.getState?.()?.text;
|
|
214
|
+
} catch {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
setStarted((messages?.length ?? 0) > 0);
|
|
218
|
+
setTyped((text?.trim().length ?? 0) > 0);
|
|
219
|
+
};
|
|
220
|
+
// Deferred out of the synchronous effect body —
|
|
221
|
+
// react-hooks/set-state-in-effect.
|
|
222
|
+
queueMicrotask(read);
|
|
223
|
+
const unsubscribe = thread.subscribe?.(read);
|
|
224
|
+
return () => {
|
|
225
|
+
if (typeof unsubscribe === "function") unsubscribe();
|
|
226
|
+
};
|
|
227
|
+
}, [thread]);
|
|
228
|
+
|
|
229
|
+
return useMemo(() => ({ started, typed }), [started, typed]);
|
|
230
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Horizontal row for example questions. Its scrollbar is thin and stays
|
|
5
|
+
* hidden until the row is hovered, focused or scrolled (styles in
|
|
6
|
+
* chat-aui.css, `.aui-suggestion-scroll`). Chrome does not repaint a
|
|
7
|
+
* `::-webkit-scrollbar-thumb` when only `:hover` changes, so the row sets
|
|
8
|
+
* `data-hover` / `data-focus` itself, and `data-scrolling` for a moment after
|
|
9
|
+
* each scroll so touch and trackpad users see where they are.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
forwardRef,
|
|
14
|
+
useCallback,
|
|
15
|
+
useEffect,
|
|
16
|
+
useRef,
|
|
17
|
+
type FocusEvent,
|
|
18
|
+
type HTMLAttributes,
|
|
19
|
+
type PointerEvent,
|
|
20
|
+
type UIEvent,
|
|
21
|
+
} from "react";
|
|
22
|
+
|
|
23
|
+
import { cn } from "../../lib/utils";
|
|
24
|
+
|
|
25
|
+
const REVEAL_MS = 900;
|
|
26
|
+
|
|
27
|
+
export const SuggestionScrollRow = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
|
28
|
+
function SuggestionScrollRow(
|
|
29
|
+
{ className, onScroll, onPointerEnter, onPointerLeave, onFocus, onBlur, ...props },
|
|
30
|
+
ref,
|
|
31
|
+
) {
|
|
32
|
+
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
33
|
+
|
|
34
|
+
useEffect(
|
|
35
|
+
() => () => {
|
|
36
|
+
if (timer.current) clearTimeout(timer.current);
|
|
37
|
+
},
|
|
38
|
+
[],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const handleScroll = useCallback(
|
|
42
|
+
(event: UIEvent<HTMLDivElement>) => {
|
|
43
|
+
const el = event.currentTarget;
|
|
44
|
+
el.dataset.scrolling = "true";
|
|
45
|
+
if (timer.current) clearTimeout(timer.current);
|
|
46
|
+
timer.current = setTimeout(() => {
|
|
47
|
+
delete el.dataset.scrolling;
|
|
48
|
+
}, REVEAL_MS);
|
|
49
|
+
onScroll?.(event);
|
|
50
|
+
},
|
|
51
|
+
[onScroll],
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
ref={ref}
|
|
57
|
+
className={cn("aui-suggestion-scroll overflow-x-auto", className)}
|
|
58
|
+
onScroll={handleScroll}
|
|
59
|
+
onPointerEnter={(event: PointerEvent<HTMLDivElement>) => {
|
|
60
|
+
if (event.pointerType === "mouse") event.currentTarget.dataset.hover = "true";
|
|
61
|
+
onPointerEnter?.(event);
|
|
62
|
+
}}
|
|
63
|
+
onPointerLeave={(event: PointerEvent<HTMLDivElement>) => {
|
|
64
|
+
delete event.currentTarget.dataset.hover;
|
|
65
|
+
onPointerLeave?.(event);
|
|
66
|
+
}}
|
|
67
|
+
onFocus={(event: FocusEvent<HTMLDivElement>) => {
|
|
68
|
+
event.currentTarget.dataset.focus = "true";
|
|
69
|
+
onFocus?.(event);
|
|
70
|
+
}}
|
|
71
|
+
onBlur={(event: FocusEvent<HTMLDivElement>) => {
|
|
72
|
+
if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {
|
|
73
|
+
delete event.currentTarget.dataset.focus;
|
|
74
|
+
}
|
|
75
|
+
onBlur?.(event);
|
|
76
|
+
}}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
);
|