@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,203 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* The parse half of <ChatMarkdown>: a markdown *string* → the `.chat-md`
|
|
4
|
+
* element grammar. <ChatMarkdown> stays a directive-free, server-safe frame
|
|
5
|
+
* around pre-built children; this file is where the renderer lives, split out
|
|
6
|
+
* purely for the RSC boundary — react-markdown imports React hooks, so it
|
|
7
|
+
* cannot be pulled into a server component.
|
|
8
|
+
*
|
|
9
|
+
* Pipeline (remark → rehype → React, no raw HTML anywhere — React escapes text
|
|
10
|
+
* nodes and no rehype-raw is in the chain):
|
|
11
|
+
* remark-gfm tables, task lists, strikethrough, autolinks
|
|
12
|
+
* remark-math `$inline$` and `$$block$$` → math nodes
|
|
13
|
+
* rehype-katex math nodes → KaTeX HTML + MathML, *at parse time*
|
|
14
|
+
*
|
|
15
|
+
* Math is deliberately NOT a client-side pass: rehype-katex runs inside the
|
|
16
|
+
* unified pipeline, so the server markup already contains laid-out KaTeX and
|
|
17
|
+
* math reads correctly with JS disabled. Only mermaid — which needs a live DOM
|
|
18
|
+
* to measure text — defers to the client, and <ChatMermaidBlock> ships the
|
|
19
|
+
* diagram source as its own fallback for that window.
|
|
20
|
+
*
|
|
21
|
+
* Emits bare tags wherever chat-core.css already has a combinator, and reaches
|
|
22
|
+
* for a component only where an affordance is needed: fenced code gets
|
|
23
|
+
* <ChatCodeBlock>'s copy caption, a ```mermaid fence gets <ChatMermaidBlock>.
|
|
24
|
+
*/
|
|
25
|
+
import { isValidElement, type ReactNode } from "react";
|
|
26
|
+
import type { Components, Options } from "react-markdown";
|
|
27
|
+
import ReactMarkdown from "react-markdown";
|
|
28
|
+
import rehypeKatex from "rehype-katex";
|
|
29
|
+
import remarkGfm from "remark-gfm";
|
|
30
|
+
import remarkMath from "remark-math";
|
|
31
|
+
|
|
32
|
+
import { ChatCodeBlock } from "./ChatCodeBlock";
|
|
33
|
+
import { ChatMermaidBlock } from "./ChatMermaidBlock";
|
|
34
|
+
|
|
35
|
+
/** Minimal shape of the mdast nodes remarkDisplayDollars touches. */
|
|
36
|
+
type MathNode = {
|
|
37
|
+
type: string;
|
|
38
|
+
children?: MathNode[];
|
|
39
|
+
position?: { start?: { offset?: number } };
|
|
40
|
+
data?: { hProperties?: { className?: string[] } };
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* remark-math's *flow* (display) construct needs the `$$` fences on lines of
|
|
45
|
+
* their own; `$$x$$` written on a single line therefore comes out as an
|
|
46
|
+
* `inlineMath` node and renders squeezed inline. Models write display math on
|
|
47
|
+
* one line constantly, so promote the double-dollar case: rehype-katex keys
|
|
48
|
+
* displayMode off the `math-display` class alone, so swapping the class is the
|
|
49
|
+
* whole fix. Single-dollar `$x$` is untouched and stays inline.
|
|
50
|
+
*/
|
|
51
|
+
function remarkDisplayDollars() {
|
|
52
|
+
return (tree: unknown, file: { value?: unknown }) => {
|
|
53
|
+
const src = String(file.value ?? "");
|
|
54
|
+
const walk = (node: MathNode): void => {
|
|
55
|
+
if (node.type === "inlineMath" && node.data?.hProperties) {
|
|
56
|
+
const at = node.position?.start?.offset;
|
|
57
|
+
if (typeof at === "number" && src.startsWith("$$", at)) {
|
|
58
|
+
node.data.hProperties.className = ["language-math", "math-display"];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const child of node.children ?? []) walk(child);
|
|
62
|
+
};
|
|
63
|
+
walk(tree as MathNode);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* `singleDollarTextMath: false` is load-bearing, not a preference. remark-math
|
|
69
|
+
* defaults it to true, which makes any paragraph containing two `$` signs parse
|
|
70
|
+
* as inline math: "the starter plan is $29 and the team plan is $99" re-sets the
|
|
71
|
+
* prose between them as italic math with the spaces collapsed, and eats the
|
|
72
|
+
* second `$`. Currency is far commoner than inline math in a docs answer — and
|
|
73
|
+
* this renders for a quant-finance stack — so `$…$` stays literal.
|
|
74
|
+
*
|
|
75
|
+
* The cost, stated plainly: there is then NO inline math syntax. remark-math
|
|
76
|
+
* understands only `$`/`$$`, not `\(…\)`, so turning single-dollar off leaves
|
|
77
|
+
* block math (`$$…$$`, plus fenced math promoted by remarkDisplayDollars below)
|
|
78
|
+
* as the only route. That is the right trade for a docs chat — a mangled price
|
|
79
|
+
* is a wrong answer, a formula that needs its own line is a formatting nit — but
|
|
80
|
+
* it is a trade, not a free win. Revisit if inline notation ever outweighs
|
|
81
|
+
* currency on these surfaces.
|
|
82
|
+
*/
|
|
83
|
+
const remarkPlugins: Options["remarkPlugins"] = [
|
|
84
|
+
remarkGfm,
|
|
85
|
+
[remarkMath, { singleDollarTextMath: false }],
|
|
86
|
+
remarkDisplayDollars,
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* KaTeX options. `strict: "ignore"` keeps half-typed model output from filling
|
|
91
|
+
* the console with parse complaints. rehype-katex forces `throwOnError` itself
|
|
92
|
+
* (first strict, then a lenient retry, then a `.katex-error` span), and that
|
|
93
|
+
* last-resort span is inline-styled from `errorColor` — so it is handed a
|
|
94
|
+
* var() and the failed expression reads on the token palette in both themes
|
|
95
|
+
* instead of KaTeX's hard-coded red.
|
|
96
|
+
*/
|
|
97
|
+
const rehypePlugins: Options["rehypePlugins"] = [
|
|
98
|
+
// maxSize bounds \rule and friends. KaTeX names maxExpand and maxSize as the two
|
|
99
|
+
// knobs for untrusted input; maxExpand defaults to 1000 and holds, but maxSize
|
|
100
|
+
// defaults to Infinity, so `$$\rule{99999em}{99999em}$$` in a model answer
|
|
101
|
+
// inserts a ~1.6-million-pixel box and blows out the page scroll — inline math
|
|
102
|
+
// has no overflow container at all.
|
|
103
|
+
[rehypeKatex, { strict: "ignore", errorColor: "var(--down)", maxSize: 10 }],
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
/** Only http(s) links survive; anything else (javascript:, data:) renders as text. */
|
|
107
|
+
function safeHref(href: string | undefined): string | undefined {
|
|
108
|
+
return href && (href.startsWith("https://") || href.startsWith("http://")) ? href : undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Per-fence-language block-code hook. Called with the fence's language tag
|
|
112
|
+
* (lowercased, no `language-` prefix — empty string for a bare fence) and its
|
|
113
|
+
* raw code. Return a node to render instead of the default fenced-code/mermaid
|
|
114
|
+
* handling below, or `undefined` to fall through to it. Not consulted for
|
|
115
|
+
* inline code (a single backtick span) — only ever for a block fence, since
|
|
116
|
+
* that is the only shape a caller has needed an override for so far. */
|
|
117
|
+
export type CodeBlockOverride = (lang: string, code: string) => ReactNode | undefined;
|
|
118
|
+
|
|
119
|
+
function buildComponents(renderCodeBlock?: CodeBlockOverride): Components {
|
|
120
|
+
return {
|
|
121
|
+
a: ({ href, children }) => {
|
|
122
|
+
const safe = safeHref(href);
|
|
123
|
+
if (!safe) return <span>{children}</span>;
|
|
124
|
+
return (
|
|
125
|
+
<a href={safe} target="_blank" rel="noopener noreferrer">
|
|
126
|
+
{children}
|
|
127
|
+
</a>
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
// Headings are downshifted two levels: a model that opens an answer with `#`
|
|
131
|
+
// must not take the page's <h1>. Neither digichat /embed nor digithings.ai/chat
|
|
132
|
+
// has an <h1> of its own on the transcript route, so an un-downshifted h1 from
|
|
133
|
+
// streamed text becomes the document's top-level heading — a real outline and
|
|
134
|
+
// screen-reader defect, not a cosmetic one. The renderer this replaced
|
|
135
|
+
// (digichat-ui's MiniMarkdown) mapped h1→h3, h2→h4, h3→h5, h4→h6; that
|
|
136
|
+
// behaviour is preserved here rather than dropped.
|
|
137
|
+
h1: ({ children }) => <h3>{children}</h3>,
|
|
138
|
+
h2: ({ children }) => <h4>{children}</h4>,
|
|
139
|
+
h3: ({ children }) => <h5>{children}</h5>,
|
|
140
|
+
h4: ({ children }) => <h6>{children}</h6>,
|
|
141
|
+
// Tables scroll inside their own box rather than widening the turn.
|
|
142
|
+
table: ({ children }) => (
|
|
143
|
+
<div className="chat-md-table">
|
|
144
|
+
<table>{children}</table>
|
|
145
|
+
</div>
|
|
146
|
+
),
|
|
147
|
+
// <ChatCodeBlock>/<ChatMermaidBlock> are <figure>s that bring their own
|
|
148
|
+
// <pre>, so the fence's wrapper is dropped and `code` decides the shape.
|
|
149
|
+
// Block code is decided HERE, at the <pre>, because that node is the only one
|
|
150
|
+
// that actually says "this is a block". Guessing from the child string instead
|
|
151
|
+
// — react-markdown >=9 dropped the `inline` prop — misread the commonest shape
|
|
152
|
+
// of model output: a bare one-line fence holding a single shell command has no
|
|
153
|
+
// language and no newline, so it rendered as inline <code>, losing its block
|
|
154
|
+
// styling and its copy button. Indented blocks lost their leading whitespace
|
|
155
|
+
// for the same reason.
|
|
156
|
+
pre: ({ children }) => {
|
|
157
|
+
const child = Array.isArray(children) ? children[0] : children;
|
|
158
|
+
if (isValidElement<{ className?: string; children?: ReactNode }>(child)) {
|
|
159
|
+
const raw = String(child.props.children ?? "").replace(/\n$/, "");
|
|
160
|
+
const lang = child.props.className?.replace(/^language-/, "").toLowerCase() ?? "";
|
|
161
|
+
if (renderCodeBlock) {
|
|
162
|
+
const overridden = renderCodeBlock(lang, raw);
|
|
163
|
+
if (overridden !== undefined) return overridden;
|
|
164
|
+
}
|
|
165
|
+
if (lang === "mermaid") return <ChatMermaidBlock code={raw} />;
|
|
166
|
+
return <ChatCodeBlock code={raw} lang={lang || undefined} />;
|
|
167
|
+
}
|
|
168
|
+
return <>{children}</>;
|
|
169
|
+
},
|
|
170
|
+
// Reached for inline code only; the `pre` branch above consumes block code
|
|
171
|
+
// without rendering its child. renderCodeBlock is deliberately NOT consulted
|
|
172
|
+
// here — every caller that has needed it so far (a fenced ```json chart
|
|
173
|
+
// envelope) only ever appears as a block, and applying it to inline spans
|
|
174
|
+
// too would risk misfiring on a short inline snippet that happens to share a
|
|
175
|
+
// language tag.
|
|
176
|
+
code: ({ className, children }) => {
|
|
177
|
+
const raw = String(children).replace(/\n$/, "");
|
|
178
|
+
const lang = className?.replace(/^language-/, "").toLowerCase() ?? "";
|
|
179
|
+
if (lang === "mermaid") return <ChatMermaidBlock code={raw} />;
|
|
180
|
+
if (lang) return <ChatCodeBlock code={raw} lang={lang} />;
|
|
181
|
+
return <code>{raw}</code>;
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export type ChatMarkdownSourceProps = {
|
|
187
|
+
/** Markdown source. */
|
|
188
|
+
source: string;
|
|
189
|
+
/** See {@link CodeBlockOverride}. */
|
|
190
|
+
renderCodeBlock?: CodeBlockOverride;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export function ChatMarkdownSource({ source, renderCodeBlock }: ChatMarkdownSourceProps) {
|
|
194
|
+
return (
|
|
195
|
+
<ReactMarkdown
|
|
196
|
+
remarkPlugins={remarkPlugins}
|
|
197
|
+
rehypePlugins={rehypePlugins}
|
|
198
|
+
components={buildComponents(renderCodeBlock)}
|
|
199
|
+
>
|
|
200
|
+
{source}
|
|
201
|
+
</ReactMarkdown>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Mermaid diagram block for <ChatMarkdown> — a ```mermaid fence in streamed
|
|
4
|
+
* model output, drawn as an SVG in the token palette. Promoted up from
|
|
5
|
+
* digichat-ui's MermaidBlock so both chat surfaces draw the same diagram;
|
|
6
|
+
* this is the canon copy.
|
|
7
|
+
*
|
|
8
|
+
* Four things it owes the canon:
|
|
9
|
+
*
|
|
10
|
+
* 1. TOKENS, NEVER MERMAID'S PALETTE. Every built-in mermaid theme hard-codes
|
|
11
|
+
* its own hues, so we run `theme: "base"` — the one palette that lets every
|
|
12
|
+
* themeVariable through — and fill it from the *computed* value of
|
|
13
|
+
* --ink / --surface / --hair / --accent read off this figure. Reading off
|
|
14
|
+
* the figure rather than :root means a livery scope (`.accent-digichat`)
|
|
15
|
+
* or a nested [data-theme] resolves exactly the colours the surrounding
|
|
16
|
+
* turn resolves.
|
|
17
|
+
* 2. BOTH THEMES, LIVE. A MutationObserver on <html data-theme> re-reads the
|
|
18
|
+
* tokens and re-renders, so flipping the theme toggle repaints the diagram
|
|
19
|
+
* instead of leaving a dark graph on a paper surface.
|
|
20
|
+
* 3. NO JS, NO PROBLEM. The source ships inside a <pre> in the server markup
|
|
21
|
+
* and is only replaced once mermaid has actually drawn something. A reader
|
|
22
|
+
* without JS (or before hydration) sees the diagram source, never an empty
|
|
23
|
+
* box; a parse failure keeps that same <pre> and never throws into the
|
|
24
|
+
* transcript.
|
|
25
|
+
* 4. LAZY. mermaid is ~2.9 MB raw (parser + d3 + dagre + cytoscape), so it is
|
|
26
|
+
* imported *inside* the effect — `await import("mermaid")` — and lands in
|
|
27
|
+
* its own async chunk that no page pays for until a diagram appears.
|
|
28
|
+
*
|
|
29
|
+
* Safety: the source is attacker-influenceable model text, so it runs under
|
|
30
|
+
* `securityLevel: "strict"` — mermaid disables HTML labels, drops click
|
|
31
|
+
* handlers/`script` directives, and passes its own output through the DOMPurify
|
|
32
|
+
* copy it bundles before returning it. `suppressErrorRendering` additionally
|
|
33
|
+
* stops a malformed graph injecting mermaid's error-bomb SVG into the
|
|
34
|
+
* transcript. `mermaid.render()` therefore hands back an already-sanitized SVG
|
|
35
|
+
* *string*, which is why this is the one justified innerHTML on the chat
|
|
36
|
+
* surfaces (`htmlLabels: false` is set again below so a config merge cannot
|
|
37
|
+
* quietly re-enable raw label HTML). Motion lives in styles/chat-core.css
|
|
38
|
+
* behind a prefers-reduced-motion guard.
|
|
39
|
+
*/
|
|
40
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
41
|
+
|
|
42
|
+
import { tokenThemeVariables } from "../diagrams/mermaid-theme";
|
|
43
|
+
import { ChatCopyButton } from "./ChatCodeBlock";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Quotes unquoted `[...]` flowchart node labels that contain characters the
|
|
47
|
+
* classic flowchart grammar cannot parse unquoted — `(`, `)`, `{`, `}` — so a
|
|
48
|
+
* model-written label like `C[List locations (Exchange, OneDrive, Blob)]`
|
|
49
|
+
* (a real observed failure: `Parse error ... got 'PS'` on the `(`) renders
|
|
50
|
+
* instead of falling back to source. Mermaid's own fix for this is quoting
|
|
51
|
+
* the label (`C["List locations (Exchange, OneDrive, Blob)"]`); this applies
|
|
52
|
+
* that mechanically rather than relying on the model to always remember to.
|
|
53
|
+
*
|
|
54
|
+
* Deliberately narrow: only touches `ID[label]` shapes (the commonest node
|
|
55
|
+
* form in model output), only when the label is not already quoted, and only
|
|
56
|
+
* when it contains one of the four unparseable characters — anything else
|
|
57
|
+
* (round `(...)`, diamond `{...}`, decorated shapes) is left alone rather than
|
|
58
|
+
* guessed at. The `[^\]"]*` capture excludes any label already containing a
|
|
59
|
+
* literal `"` from the match entirely — such a label is already ambiguous
|
|
60
|
+
* (unquoted-but-quoted), so this leaves it for mermaid's own parser to reject
|
|
61
|
+
* rather than guessing at an escape.
|
|
62
|
+
*
|
|
63
|
+
* A label starting with `[`, `(`, `/`, or `\` right after the node's opening
|
|
64
|
+
* `[` is bailed out on rather than sanitized: that is the four OTHER `[...]`
|
|
65
|
+
* bracket shapes mermaid overloads the same opening character for — subroutine
|
|
66
|
+
* `id[[text]]`, cylinder `id[(text)]`, parallelogram `id[/text/]`, trapezoid
|
|
67
|
+
* `id[\text\]`. The regex only looks for the FIRST `]`, so on a subroutine it
|
|
68
|
+
* stops at the shape's own inner `]]` instead of the outer one, and on a
|
|
69
|
+
* cylinder/parallelogram/trapezoid it captures the whole shape delimiter as
|
|
70
|
+
* part of the "label" and silently downgrades the node to a plain rectangle
|
|
71
|
+
* when it quotes it. Left untouched, these still fail the same way they did
|
|
72
|
+
* before this function existed (a genuinely paren-bearing cylinder label was
|
|
73
|
+
* already unparseable) — worse than not fixing it would be corrupting a shape
|
|
74
|
+
* that was rendering fine.
|
|
75
|
+
*
|
|
76
|
+
* Applied only to the copy handed to `mermaid.parse`/`render` — the `code`
|
|
77
|
+
* prop keeps the model's exact text for the copy button and the source
|
|
78
|
+
* fallback, so a reader never sees text the model didn't write.
|
|
79
|
+
*/
|
|
80
|
+
export function sanitizeMermaidLabels(code: string): string {
|
|
81
|
+
return code.replace(/(\b[\w-]+)\[([^\]"]*)\]/g, (whole, id: string, label: string) => {
|
|
82
|
+
if (/^[[(/\\]/.test(label)) return whole;
|
|
83
|
+
if (!/[(){}]/.test(label)) return whole;
|
|
84
|
+
return `${id}["${label}"]`;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The palette table moved to `../diagrams/mermaid-theme` when the
|
|
90
|
+
* architecture diagram became a second mermaid surface — one table, both
|
|
91
|
+
* diagrams, so a token added for one is a token added for both.
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
export type ChatMermaidBlockProps = {
|
|
95
|
+
/** Diagram source — rendered verbatim as the no-JS / parse-failure fallback. */
|
|
96
|
+
code: string;
|
|
97
|
+
/** Caption language label. */
|
|
98
|
+
label?: string;
|
|
99
|
+
className?: string;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export function ChatMermaidBlock({ code, label = "mermaid", className }: ChatMermaidBlockProps) {
|
|
103
|
+
const [svg, setSvg] = useState("");
|
|
104
|
+
const [failed, setFailed] = useState(false);
|
|
105
|
+
const [showSource, setShowSource] = useState(false);
|
|
106
|
+
// Bumped by the [data-theme] observer to re-run the render effect.
|
|
107
|
+
const [themeTick, setThemeTick] = useState(0);
|
|
108
|
+
const hostRef = useRef<HTMLElement>(null);
|
|
109
|
+
// React 19's useId is «r0»-shaped; mermaid uses this verbatim as an SVG id
|
|
110
|
+
// AND inside generated CSS selectors, so strip everything non-alphanumeric.
|
|
111
|
+
const id = `chat-mermaid-${useId().replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
112
|
+
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
const observer = new MutationObserver(() => setThemeTick((n) => n + 1));
|
|
115
|
+
observer.observe(document.documentElement, {
|
|
116
|
+
attributes: true,
|
|
117
|
+
attributeFilter: ["data-theme"],
|
|
118
|
+
});
|
|
119
|
+
return () => observer.disconnect();
|
|
120
|
+
}, []);
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
const host = hostRef.current;
|
|
124
|
+
if (!host) return;
|
|
125
|
+
let cancelled = false;
|
|
126
|
+
|
|
127
|
+
void (async () => {
|
|
128
|
+
try {
|
|
129
|
+
const mermaid = (await import("mermaid")).default;
|
|
130
|
+
mermaid.initialize({
|
|
131
|
+
startOnLoad: false,
|
|
132
|
+
securityLevel: "strict",
|
|
133
|
+
suppressErrorRendering: true,
|
|
134
|
+
theme: "base",
|
|
135
|
+
themeVariables: tokenThemeVariables(host),
|
|
136
|
+
flowchart: { htmlLabels: false, useMaxWidth: true },
|
|
137
|
+
sequence: { useMaxWidth: true },
|
|
138
|
+
});
|
|
139
|
+
// Validate first: parse throws on a malformed graph without touching
|
|
140
|
+
// the document, so the failure path never leaves stray nodes behind.
|
|
141
|
+
const sanitized = sanitizeMermaidLabels(code);
|
|
142
|
+
await mermaid.parse(sanitized);
|
|
143
|
+
const rendered = await mermaid.render(id, sanitized);
|
|
144
|
+
if (cancelled) return;
|
|
145
|
+
setSvg(rendered.svg);
|
|
146
|
+
setFailed(false);
|
|
147
|
+
} catch {
|
|
148
|
+
if (cancelled) return;
|
|
149
|
+
setSvg("");
|
|
150
|
+
setFailed(true);
|
|
151
|
+
}
|
|
152
|
+
})();
|
|
153
|
+
|
|
154
|
+
return () => {
|
|
155
|
+
cancelled = true;
|
|
156
|
+
};
|
|
157
|
+
}, [code, id, themeTick]);
|
|
158
|
+
|
|
159
|
+
const drawn = Boolean(svg) && !failed;
|
|
160
|
+
const cls = ["chat-md-mermaid", className ?? ""].filter(Boolean).join(" ");
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<figure
|
|
164
|
+
ref={hostRef}
|
|
165
|
+
className={cls}
|
|
166
|
+
data-state={failed ? "source" : drawn ? "diagram" : "pending"}
|
|
167
|
+
>
|
|
168
|
+
<figcaption>
|
|
169
|
+
<span>{failed ? `${label} · source` : label}</span>
|
|
170
|
+
<span className="chat-md-mermaid-actions">
|
|
171
|
+
{drawn ? (
|
|
172
|
+
<button
|
|
173
|
+
type="button"
|
|
174
|
+
className="chat-md-copy"
|
|
175
|
+
aria-expanded={showSource}
|
|
176
|
+
aria-controls={`${id}-source`}
|
|
177
|
+
onClick={() => setShowSource((v) => !v)}
|
|
178
|
+
>
|
|
179
|
+
{showSource ? "hide source" : "view source"}
|
|
180
|
+
</button>
|
|
181
|
+
) : null}
|
|
182
|
+
<ChatCopyButton text={code} ariaLabel="Copy diagram source" />
|
|
183
|
+
</span>
|
|
184
|
+
</figcaption>
|
|
185
|
+
{drawn ? (
|
|
186
|
+
<div
|
|
187
|
+
className="chat-md-mermaid-fig"
|
|
188
|
+
role="img"
|
|
189
|
+
aria-label={`${label} diagram`}
|
|
190
|
+
// mermaid.render returns a sanitized SVG string under
|
|
191
|
+
// securityLevel: "strict" — see the module docblock.
|
|
192
|
+
dangerouslySetInnerHTML={{ __html: svg }}
|
|
193
|
+
/>
|
|
194
|
+
) : null}
|
|
195
|
+
{!drawn || showSource ? (
|
|
196
|
+
<pre id={`${id}-source`}>
|
|
197
|
+
<code>{code}</code>
|
|
198
|
+
</pre>
|
|
199
|
+
) : null}
|
|
200
|
+
</figure>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat message — one turn of the digichat grammar (canon §16): a marker
|
|
3
|
+
* column and a body on a `1.25rem minmax(0,1fr)` grid — the digichat
|
|
4
|
+
* .dc-term-row geometry, and the convergence target for digichat-ui's
|
|
5
|
+
* .dc-msg (marker ⇢ .dc-who, body ⇢ .dc-body). `>` marks the user, `▸` the
|
|
6
|
+
* assistant, `·` system asides; markers carry the accent and stay out of
|
|
7
|
+
* the a11y tree. `tone` picks the body ink explicitly (user turns default
|
|
8
|
+
* to full ink, the rest to soft; an emphasized answer passes tone="ink") so
|
|
9
|
+
* consumers never fight utility order. `streaming` appends the blink caret.
|
|
10
|
+
* The row is position:relative (chat-core.css) so hover affordances — a
|
|
11
|
+
* per-turn <ChatCopyButton>, digichat-ui's .dc-msg-copy — can pin to it.
|
|
12
|
+
*/
|
|
13
|
+
import type { ReactNode } from "react";
|
|
14
|
+
import { ChatStreamCursor } from "./ChatStreamCursor";
|
|
15
|
+
|
|
16
|
+
export type ChatRole = "user" | "assistant" | "system";
|
|
17
|
+
|
|
18
|
+
export type ChatTone = "ink" | "soft" | "mute";
|
|
19
|
+
|
|
20
|
+
const MARKERS: Record<ChatRole, string> = { user: ">", assistant: "▸", system: "·" };
|
|
21
|
+
|
|
22
|
+
const TONES: Record<ChatTone, string> = {
|
|
23
|
+
ink: "text-ink",
|
|
24
|
+
soft: "text-ink-soft",
|
|
25
|
+
mute: "text-ink-mute",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type ChatMessageProps = {
|
|
29
|
+
role: ChatRole;
|
|
30
|
+
/** Marker glyph override (digichat-ui marks assistant turns `·`). */
|
|
31
|
+
marker?: ReactNode;
|
|
32
|
+
/** Body ink; defaults to `ink` for user turns, `soft` otherwise. */
|
|
33
|
+
tone?: ChatTone;
|
|
34
|
+
/** Append the blinking stream caret after the body. */
|
|
35
|
+
streaming?: boolean;
|
|
36
|
+
children?: ReactNode;
|
|
37
|
+
className?: string;
|
|
38
|
+
bodyClassName?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export function ChatMessage({
|
|
42
|
+
role,
|
|
43
|
+
marker,
|
|
44
|
+
tone,
|
|
45
|
+
streaming,
|
|
46
|
+
children,
|
|
47
|
+
className,
|
|
48
|
+
bodyClassName,
|
|
49
|
+
}: ChatMessageProps) {
|
|
50
|
+
const bodyTone = TONES[tone ?? (role === "user" ? "ink" : "soft")];
|
|
51
|
+
const cls = [
|
|
52
|
+
`chat-msg chat-msg--${role}`,
|
|
53
|
+
"grid grid-cols-[1.25rem_minmax(0,1fr)] items-baseline gap-[0.55rem]",
|
|
54
|
+
className ?? "",
|
|
55
|
+
]
|
|
56
|
+
.filter(Boolean)
|
|
57
|
+
.join(" ");
|
|
58
|
+
const bodyCls = [
|
|
59
|
+
"chat-msg-body min-w-0",
|
|
60
|
+
bodyTone,
|
|
61
|
+
role === "user" ? "whitespace-pre-wrap" : "",
|
|
62
|
+
bodyClassName ?? "",
|
|
63
|
+
]
|
|
64
|
+
.filter(Boolean)
|
|
65
|
+
.join(" ");
|
|
66
|
+
return (
|
|
67
|
+
<div className={cls}>
|
|
68
|
+
<span className="chat-msg-marker aui-msg-marker text-ink" aria-hidden="true">
|
|
69
|
+
{marker ?? MARKERS[role]}
|
|
70
|
+
</span>
|
|
71
|
+
<div className={bodyCls}>
|
|
72
|
+
{children}
|
|
73
|
+
{streaming ? <ChatStreamCursor /> : null}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|