@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,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical spacing and type rhythm for the digichat thread.
|
|
3
|
+
*
|
|
4
|
+
* A short scale (`--trace-space-1` … `--trace-space-8`, kept apart from the design tokens' `--space-*`) is the only length source.
|
|
5
|
+
* `DENSITY_TOKENS` is the one density set: four stack gaps, body padding,
|
|
6
|
+
* two line heights and two letter-spacings. Every mode (compact / default /
|
|
7
|
+
* airy) reassigns it on `.digichat-thread[data-density=…]`.
|
|
8
|
+
* The semantic `--trace-*` names are aliases of that set, kept so the rules
|
|
9
|
+
* that already read them keep working. Rules in `chat-digichat.css` and
|
|
10
|
+
* `chat-aui.css` reference the tokens and do not carry their own lengths.
|
|
11
|
+
*
|
|
12
|
+
* `default` is the fixed rhythm: room under the Worked title, one gap
|
|
13
|
+
* between every sibling group, a tighter gap inside a group, and a short
|
|
14
|
+
* step from a reasoning header into its text.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export const TRACE_DENSITIES = ["compact", "default", "airy"] as const;
|
|
18
|
+
export type TraceDensity = (typeof TRACE_DENSITIES)[number];
|
|
19
|
+
|
|
20
|
+
export const DEFAULT_TRACE_DENSITY: TraceDensity = "default";
|
|
21
|
+
|
|
22
|
+
/** localStorage key. Digichat only — the dashboard never reads it. */
|
|
23
|
+
export const DENSITY_STORAGE_KEY = "dt-density";
|
|
24
|
+
|
|
25
|
+
/** The scale, in rem. Density never invents a length outside this list. */
|
|
26
|
+
export const TRACE_SPACE_SCALE = {
|
|
27
|
+
1: "0.125rem",
|
|
28
|
+
2: "0.25rem",
|
|
29
|
+
3: "0.375rem",
|
|
30
|
+
4: "0.5rem",
|
|
31
|
+
5: "0.625rem",
|
|
32
|
+
6: "0.75rem",
|
|
33
|
+
7: "1rem",
|
|
34
|
+
8: "1.25rem",
|
|
35
|
+
} as const;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* One density set, mirrored by the CSS custom properties in
|
|
39
|
+
* `styles/chat-aui.css` (`--density-*`) and pinned by
|
|
40
|
+
* `styles/density-tokens.contract.test.ts`. Every length is on
|
|
41
|
+
* `TRACE_SPACE_SCALE`; the line heights and trackings are unitless.
|
|
42
|
+
*/
|
|
43
|
+
export type DensityTokens = {
|
|
44
|
+
/** Tightest stack gap: items inside a group, paragraphs, list items. */
|
|
45
|
+
gap1: string;
|
|
46
|
+
/** Sibling gap: messages, chain groups, block separation. */
|
|
47
|
+
gap2: string;
|
|
48
|
+
/** Roomier step: title → first row, block → next block. */
|
|
49
|
+
gap3: string;
|
|
50
|
+
/** Largest step: message → message. */
|
|
51
|
+
gap4: string;
|
|
52
|
+
/** Inset inside a reasoning / tool body, and the composer's own padding. */
|
|
53
|
+
pad: string;
|
|
54
|
+
/** Body line height (answers, prose). */
|
|
55
|
+
leading: string;
|
|
56
|
+
/** Trace / mono line height (reasoning, tool dumps, code). */
|
|
57
|
+
leadingTight: string;
|
|
58
|
+
/** Body letter spacing. */
|
|
59
|
+
tracking: string;
|
|
60
|
+
/** Mono / trace letter spacing. */
|
|
61
|
+
trackingMono: string;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const DENSITY_TOKENS: Record<TraceDensity, DensityTokens> = {
|
|
65
|
+
compact: {
|
|
66
|
+
gap1: TRACE_SPACE_SCALE[1],
|
|
67
|
+
gap2: TRACE_SPACE_SCALE[2],
|
|
68
|
+
gap3: TRACE_SPACE_SCALE[3],
|
|
69
|
+
gap4: TRACE_SPACE_SCALE[4],
|
|
70
|
+
pad: "0rem",
|
|
71
|
+
leading: "1.45",
|
|
72
|
+
leadingTight: "1.3",
|
|
73
|
+
tracking: "-0.005em",
|
|
74
|
+
trackingMono: "0em",
|
|
75
|
+
},
|
|
76
|
+
default: {
|
|
77
|
+
gap1: TRACE_SPACE_SCALE[2],
|
|
78
|
+
gap2: TRACE_SPACE_SCALE[4],
|
|
79
|
+
gap3: TRACE_SPACE_SCALE[5],
|
|
80
|
+
gap4: TRACE_SPACE_SCALE[6],
|
|
81
|
+
pad: TRACE_SPACE_SCALE[1],
|
|
82
|
+
leading: "1.6",
|
|
83
|
+
leadingTight: "1.4",
|
|
84
|
+
tracking: "0em",
|
|
85
|
+
trackingMono: "0.01em",
|
|
86
|
+
},
|
|
87
|
+
airy: {
|
|
88
|
+
gap1: TRACE_SPACE_SCALE[3],
|
|
89
|
+
gap2: TRACE_SPACE_SCALE[6],
|
|
90
|
+
gap3: TRACE_SPACE_SCALE[7],
|
|
91
|
+
gap4: TRACE_SPACE_SCALE[8],
|
|
92
|
+
pad: TRACE_SPACE_SCALE[3],
|
|
93
|
+
leading: "1.75",
|
|
94
|
+
leadingTight: "1.55",
|
|
95
|
+
tracking: "0.005em",
|
|
96
|
+
trackingMono: "0.02em",
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type TraceSpacingTokens = {
|
|
101
|
+
/** Worked title → first row. Alias of `gap3`. */
|
|
102
|
+
titleGap: string;
|
|
103
|
+
/** Between sibling groups, whatever the group type. Alias of `gap2`. */
|
|
104
|
+
groupGap: string;
|
|
105
|
+
/** Between items inside one group, and between paragraphs in a body. Alias of `gap1`. */
|
|
106
|
+
withinGap: string;
|
|
107
|
+
/** Group header → its body. */
|
|
108
|
+
headerGap: string;
|
|
109
|
+
/** Block padding inside a reasoning / tool body. Alias of `pad`. */
|
|
110
|
+
bodyPad: string;
|
|
111
|
+
/** Inline indent of a reasoning body (the hairline rail). */
|
|
112
|
+
bodyIndent: string;
|
|
113
|
+
/** The chain block against the answer that follows it. Alias of `gap4`. */
|
|
114
|
+
chainGap: string;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Palette copy. The composer menu and `/density` print the same words.
|
|
119
|
+
* Kept here so the ids and the labels cannot drift from the tokens.
|
|
120
|
+
*/
|
|
121
|
+
export const TRACE_DENSITY_LABELS: Record<TraceDensity, string> = {
|
|
122
|
+
compact: "Compact · tighter",
|
|
123
|
+
default: "Default · balanced rhythm",
|
|
124
|
+
airy: "Airy · more room",
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The semantic chain tokens, derived from `DENSITY_TOKENS` so a mode cannot
|
|
129
|
+
* disagree with itself. `headerGap` and `bodyIndent` are the two that are not
|
|
130
|
+
* a plain alias: a compact header sits flush, and the rail indent steps with
|
|
131
|
+
* the mode.
|
|
132
|
+
*/
|
|
133
|
+
function traceSpacingFor(
|
|
134
|
+
tokens: DensityTokens,
|
|
135
|
+
headerGap: string,
|
|
136
|
+
bodyIndent: string,
|
|
137
|
+
): TraceSpacingTokens {
|
|
138
|
+
return {
|
|
139
|
+
titleGap: tokens.gap3,
|
|
140
|
+
groupGap: tokens.gap2,
|
|
141
|
+
withinGap: tokens.gap1,
|
|
142
|
+
headerGap,
|
|
143
|
+
bodyPad: tokens.pad,
|
|
144
|
+
bodyIndent,
|
|
145
|
+
chainGap: tokens.gap4,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const TRACE_SPACING: Record<TraceDensity, TraceSpacingTokens> = {
|
|
150
|
+
compact: traceSpacingFor(DENSITY_TOKENS.compact, "0rem", TRACE_SPACE_SCALE[5]),
|
|
151
|
+
default: traceSpacingFor(
|
|
152
|
+
DENSITY_TOKENS.default,
|
|
153
|
+
DENSITY_TOKENS.default.gap1,
|
|
154
|
+
TRACE_SPACE_SCALE[6],
|
|
155
|
+
),
|
|
156
|
+
airy: traceSpacingFor(DENSITY_TOKENS.airy, DENSITY_TOKENS.airy.gap2, TRACE_SPACE_SCALE[7]),
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export function isTraceDensity(value: string): value is TraceDensity {
|
|
160
|
+
return (TRACE_DENSITIES as readonly string[]).includes(value);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function normalizeTraceDensity(value: string | null | undefined): TraceDensity | null {
|
|
164
|
+
if (!value) return null;
|
|
165
|
+
const id = value.trim().toLowerCase();
|
|
166
|
+
return isTraceDensity(id) ? id : null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** A phone-width viewport has no room for the desktop rhythm either way. */
|
|
170
|
+
export const MOBILE_DENSITY_QUERY = "(max-width: 640px)";
|
|
171
|
+
|
|
172
|
+
/** What a phone gets when nobody has chosen a rhythm. */
|
|
173
|
+
export const MOBILE_DEFAULT_DENSITY: TraceDensity = "airy";
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Resolution order, highest first:
|
|
177
|
+
*
|
|
178
|
+
* 1. a stored user choice (`dt-density`) — always wins, desktop or phone
|
|
179
|
+
* 2. the mobile default (airy) — viewports <= 640px
|
|
180
|
+
* 3. an explicit deployment density
|
|
181
|
+
* 4. the builtin `default` rhythm
|
|
182
|
+
*
|
|
183
|
+
* The mobile default deliberately outranks a deployment default: a deployment
|
|
184
|
+
* picks a density for a desktop panel, and a phone still needs air. Passing no
|
|
185
|
+
* context (the old two-argument call) keeps the pre-mobile behaviour.
|
|
186
|
+
*/
|
|
187
|
+
export function resolveTraceDensity(
|
|
188
|
+
stored: string | null | undefined,
|
|
189
|
+
deployed: string | null | undefined,
|
|
190
|
+
context: { mobile?: boolean } = {},
|
|
191
|
+
): TraceDensity {
|
|
192
|
+
const chosen = normalizeTraceDensity(stored);
|
|
193
|
+
if (chosen) return chosen;
|
|
194
|
+
if (context.mobile) return MOBILE_DEFAULT_DENSITY;
|
|
195
|
+
return normalizeTraceDensity(deployed) ?? DEFAULT_TRACE_DENSITY;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
type DensityStorage = {
|
|
199
|
+
getItem(key: string): string | null;
|
|
200
|
+
setItem?(key: string, value: string): void;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
function safeStorage(): DensityStorage | null {
|
|
204
|
+
try {
|
|
205
|
+
if (typeof localStorage === "undefined") return null;
|
|
206
|
+
return localStorage;
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function readStoredDensity(storage?: DensityStorage | null): TraceDensity | null {
|
|
213
|
+
const store = storage === undefined ? safeStorage() : storage;
|
|
214
|
+
if (!store) return null;
|
|
215
|
+
try {
|
|
216
|
+
return normalizeTraceDensity(store.getItem(DENSITY_STORAGE_KEY));
|
|
217
|
+
} catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function writeStoredDensity(density: TraceDensity, storage?: DensityStorage | null): void {
|
|
223
|
+
const store = storage === undefined ? safeStorage() : storage;
|
|
224
|
+
if (!store?.setItem) return;
|
|
225
|
+
try {
|
|
226
|
+
store.setItem(DENSITY_STORAGE_KEY, density);
|
|
227
|
+
} catch {
|
|
228
|
+
/* private mode */
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
type DensityViewport = {
|
|
233
|
+
matchMedia?(query: string): { matches: boolean };
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* True when the viewport is phone-width. Never throws: a host without
|
|
238
|
+
* `matchMedia` (bare jsdom, a server render) simply is not mobile.
|
|
239
|
+
*/
|
|
240
|
+
export function matchesMobileDensityViewport(viewport?: DensityViewport | null): boolean {
|
|
241
|
+
const target =
|
|
242
|
+
viewport === null ? null : (viewport ?? (typeof window === "undefined" ? null : window));
|
|
243
|
+
if (!target || typeof target.matchMedia !== "function") return false;
|
|
244
|
+
try {
|
|
245
|
+
return target.matchMedia(MOBILE_DENSITY_QUERY).matches === true;
|
|
246
|
+
} catch {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity → canon projection (#1418 gap 6).
|
|
3
|
+
*
|
|
4
|
+
* `DigiChatActivity` is the wire vocabulary every backend provider emits (see
|
|
5
|
+
* digichat's `lib/chat-activity.ts`); the shared chat family in
|
|
6
|
+
* `@digithings-ai/ui` is the *rendering* vocabulary — collapsible `ChatToolCall`
|
|
7
|
+
* rows, a `ChatThinking` disclosure, `ChatWidgetFrame` cards. The two do not
|
|
8
|
+
* line up field-for-field, so this module is the boundary adapter between
|
|
9
|
+
* them: one pure function, no JSX, no DOM. Session UI in digichat 2.0 reads
|
|
10
|
+
* `source-*` parts directly; these helpers remain for export / legacy hydrate.
|
|
11
|
+
*
|
|
12
|
+
* Keeping the projection separate from the component is what makes the mapping
|
|
13
|
+
* testable in a plain node environment (no jsdom in either consumer's vitest
|
|
14
|
+
* setup) — and it is the seam to extend when the protocol grows a field, so
|
|
15
|
+
* neither the shared primitives nor the renderer has to fork.
|
|
16
|
+
*
|
|
17
|
+
* Fidelity notes — where the wire model carries less than the canon can show:
|
|
18
|
+
*
|
|
19
|
+
* - **No timings.** `ActivitySpan` has no duration field, so nothing can fill
|
|
20
|
+
* `ChatToolCall`'s `duration` slot with real milliseconds. It is the head
|
|
21
|
+
* row's right-aligned mono meta slot, so we spend it on the outcome count
|
|
22
|
+
* (`3 notes` / `no hits`) — the fact a reader actually wants folded, and
|
|
23
|
+
* the only way a collapsed result row stays honest about whether the search
|
|
24
|
+
* found anything.
|
|
25
|
+
* - **`status` rows without an outcome have already lost their structure.** By the time a
|
|
26
|
+
* withheld-documents outcome reaches here it is prose (`Found results for "x".`), with the tool
|
|
27
|
+
* name and query dissolved into the string upstream. Parsing them back out of prose would be
|
|
28
|
+
* guesswork, so these render as canon system asides rather than as fake tool rows. A `status`
|
|
29
|
+
* carrying an `outcome` keeps its structure and renders as a first-class tool row instead.
|
|
30
|
+
* - **Reasoning arrives as one blob**, not pre-segmented steps, so it maps to
|
|
31
|
+
* `ChatThinking`'s `children` disclosure rather than its railed `steps`
|
|
32
|
+
* list. See `ChatThinking`'s own docstring, which prescribes exactly this.
|
|
33
|
+
*/
|
|
34
|
+
import type { ChatToolCallStatus } from "@digithings-ai/ui";
|
|
35
|
+
import type { DigiChatActivity, VaultHitSummary } from "./types";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ephemeral Foundry ack labels — never chain rows. The session uses a bare
|
|
39
|
+
* flash caret for progress; these would duplicate it and linger after settle.
|
|
40
|
+
*/
|
|
41
|
+
export const WORKING_LABEL = "Working…";
|
|
42
|
+
|
|
43
|
+
/** Activities that belong in the tool/trace chain (excludes ephemeral Working…). */
|
|
44
|
+
export function chainActivities(
|
|
45
|
+
activities: readonly DigiChatActivity[],
|
|
46
|
+
): DigiChatActivity[] {
|
|
47
|
+
return activities.filter(
|
|
48
|
+
(a) => !(a.kind === "trace" && a.label === WORKING_LABEL),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Path only when it adds information — Foundry search hits set both to the
|
|
53
|
+
* same chunk id (`page__docs___chunk0`), which was rendering twice. */
|
|
54
|
+
export function distinctHitPath(title: string, path: string): string | null {
|
|
55
|
+
return path && path !== title ? path : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* One rendered row of the agent chain. Each variant names the shared primitive
|
|
60
|
+
* it becomes, and carries only what that primitive's props need.
|
|
61
|
+
*/
|
|
62
|
+
export type CanonActivityRow =
|
|
63
|
+
| {
|
|
64
|
+
/** → <ChatToolCall> */
|
|
65
|
+
kind: "tool";
|
|
66
|
+
key: string;
|
|
67
|
+
/** Head name, rendered in the accent. */
|
|
68
|
+
name: string;
|
|
69
|
+
/** Head `(args)` — the query, when the provider sent one. */
|
|
70
|
+
args?: string;
|
|
71
|
+
status: ChatToolCallStatus;
|
|
72
|
+
/** Right-aligned head meta — an outcome count, not a timing (see above). */
|
|
73
|
+
meta?: string;
|
|
74
|
+
/** Retrieved documents, rendered as the fold-out body of this tool row. */
|
|
75
|
+
sources?: VaultHitSummary[];
|
|
76
|
+
/**
|
|
77
|
+
* Running rows carry a body line so the head is expandable (caret).
|
|
78
|
+
* Locate tools use "Searching…"; other in-flight tools use "Working…".
|
|
79
|
+
*/
|
|
80
|
+
lines?: string[];
|
|
81
|
+
/** Start expanded when a body is attached. */
|
|
82
|
+
defaultOpen?: boolean;
|
|
83
|
+
}
|
|
84
|
+
| {
|
|
85
|
+
/** → <ChatThinking> */
|
|
86
|
+
kind: "thinking";
|
|
87
|
+
key: string;
|
|
88
|
+
label: string;
|
|
89
|
+
text: string;
|
|
90
|
+
}
|
|
91
|
+
| {
|
|
92
|
+
/** → <ChatWidgetFrame variant="card"> */
|
|
93
|
+
kind: "brief";
|
|
94
|
+
key: string;
|
|
95
|
+
themes: { label: string; summary: string }[];
|
|
96
|
+
questions?: string[];
|
|
97
|
+
}
|
|
98
|
+
| {
|
|
99
|
+
/** → <ChatMessage role="system"> — a `·` aside. */
|
|
100
|
+
kind: "aside";
|
|
101
|
+
key: string;
|
|
102
|
+
message: string;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/** `3 notes` / `1 note` / `no hits` — the folded outcome read. */
|
|
106
|
+
export function outcomeMeta(count: number): string {
|
|
107
|
+
if (count <= 0) return "no hits";
|
|
108
|
+
return `${count} note${count === 1 ? "" : "s"}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Wire ids whose in-flight fold should read "Searching…", not "Working…". */
|
|
112
|
+
const LOCATE_TOOL_NAMES = new Set([
|
|
113
|
+
"digisearch",
|
|
114
|
+
"azure_ai_search",
|
|
115
|
+
"rag_sources",
|
|
116
|
+
"digivault",
|
|
117
|
+
"digivault_search_notes",
|
|
118
|
+
"web_search",
|
|
119
|
+
]);
|
|
120
|
+
|
|
121
|
+
/** Raw backend tool id for the head — one-to-one with the backend. */
|
|
122
|
+
export function toolDisplayName(name: string): string {
|
|
123
|
+
return name;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** In-flight body so the head stays a disclosure; locate tools keep Searching…. */
|
|
127
|
+
function inFlightToolLines(toolName: string): string[] {
|
|
128
|
+
return LOCATE_TOOL_NAMES.has(toolName) ? ["Searching…"] : [WORKING_LABEL];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const QUERY_DISPLAY_MAX = 80;
|
|
132
|
+
|
|
133
|
+
/** Omit a query that already appeared on the previous row; truncate long ones. */
|
|
134
|
+
export function displayArgs(
|
|
135
|
+
query: string | undefined,
|
|
136
|
+
previousQuery: string | undefined,
|
|
137
|
+
): string | undefined {
|
|
138
|
+
const q = query?.trim();
|
|
139
|
+
if (!q) return undefined;
|
|
140
|
+
if (previousQuery?.trim() === q) return undefined;
|
|
141
|
+
if (q.length <= QUERY_DISPLAY_MAX) return q;
|
|
142
|
+
return `${q.slice(0, QUERY_DISPLAY_MAX - 1).trimEnd()}…`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Strip markdown chrome so a snippet reads as a sentence, not a wall. */
|
|
146
|
+
export function readableSnippet(raw: string, max = 220): string {
|
|
147
|
+
const s = raw
|
|
148
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
149
|
+
.replace(/!\[[^\]]*]\([^)]*\)/g, " ")
|
|
150
|
+
.replace(/\[([^\]]*)]\([^)]*\)/g, "$1")
|
|
151
|
+
.replace(/[#*_`>~]+/g, " ")
|
|
152
|
+
.replace(/\s+/g, " ")
|
|
153
|
+
.trim();
|
|
154
|
+
if (!s) return "";
|
|
155
|
+
if (s.length <= max) return s;
|
|
156
|
+
const cut = s.slice(0, max);
|
|
157
|
+
const lastStop = Math.max(
|
|
158
|
+
cut.lastIndexOf(". "),
|
|
159
|
+
cut.lastIndexOf("? "),
|
|
160
|
+
cut.lastIndexOf("! "),
|
|
161
|
+
);
|
|
162
|
+
const end =
|
|
163
|
+
lastStop > max * 0.45 ? cut.slice(0, lastStop + 1) : cut.replace(/\s+\S*$/, "");
|
|
164
|
+
return `${end.trimEnd()}…`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function previousQueryAt(
|
|
168
|
+
activities: readonly DigiChatActivity[],
|
|
169
|
+
i: number,
|
|
170
|
+
): string | undefined {
|
|
171
|
+
const prev = activities[i - 1];
|
|
172
|
+
if (!prev || !("query" in prev)) return undefined;
|
|
173
|
+
return prev.query;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function hitsForDisplay(hits: VaultHitSummary[] | undefined): VaultHitSummary[] | undefined {
|
|
177
|
+
if (!hits?.length) return undefined;
|
|
178
|
+
return hits.map((hit) =>
|
|
179
|
+
hit.snippet ? { ...hit, snippet: readableSnippet(hit.snippet) } : hit,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Projects the wire activities onto canon rows, in order.
|
|
185
|
+
*
|
|
186
|
+
* Keyed by IDENTITY, not position. Position is not stable: `toDigiChatActivity`
|
|
187
|
+
* appends the reasoning row last, after every tool row, so its index climbs each
|
|
188
|
+
* time a tool arrives; it also rewrites a row in place from `tool_call` to
|
|
189
|
+
* `tool_result` (kind is part of any composite key), and its final filter drops
|
|
190
|
+
* orphaned placeholders, shifting everything after them. Since `ChatThinking`
|
|
191
|
+
* and `ChatToolCall` are uncontrolled, a changed key is an unmount — so an
|
|
192
|
+
* index key collapsed a disclosure the reader had opened, mid-stream, and
|
|
193
|
+
* removed its text from the DOM.
|
|
194
|
+
*
|
|
195
|
+
* Reasoning is a singleton per turn, so it keys as itself. Tool rows key on
|
|
196
|
+
* name+query, which is the identity the producer already groups by internally.
|
|
197
|
+
* A trailing index still disambiguates genuine duplicates.
|
|
198
|
+
*/
|
|
199
|
+
/**
|
|
200
|
+
* A key that survives the row moving. `reasoning` is one per turn so it needs no
|
|
201
|
+
* suffix; tool rows carry name+query (the producer's own grouping key) and a
|
|
202
|
+
* trailing index only to separate true duplicates.
|
|
203
|
+
*/
|
|
204
|
+
function identityKey(activity: DigiChatActivity, i: number): string {
|
|
205
|
+
switch (activity.kind) {
|
|
206
|
+
case "reasoning":
|
|
207
|
+
return "reasoning";
|
|
208
|
+
case "tool_call":
|
|
209
|
+
case "tool_result":
|
|
210
|
+
return `tool:${activity.name}|${"query" in activity ? (activity.query ?? "") : ""}`;
|
|
211
|
+
default:
|
|
212
|
+
return `${activity.kind}-${i}`;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function toCanonRows(activities: readonly DigiChatActivity[]): CanonActivityRow[] {
|
|
217
|
+
return activities.map((activity, i): CanonActivityRow => {
|
|
218
|
+
const key = identityKey(activity, i);
|
|
219
|
+
const args =
|
|
220
|
+
"query" in activity ? displayArgs(activity.query, previousQueryAt(activities, i)) : undefined;
|
|
221
|
+
switch (activity.kind) {
|
|
222
|
+
case "tool_call":
|
|
223
|
+
return {
|
|
224
|
+
kind: "tool",
|
|
225
|
+
key,
|
|
226
|
+
name: toolDisplayName(activity.name),
|
|
227
|
+
...(args ? { args } : {}),
|
|
228
|
+
status: "running",
|
|
229
|
+
lines: inFlightToolLines(activity.name),
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
case "tool_result":
|
|
233
|
+
return {
|
|
234
|
+
kind: "tool",
|
|
235
|
+
key,
|
|
236
|
+
name: toolDisplayName(activity.name),
|
|
237
|
+
...(args ? { args } : {}),
|
|
238
|
+
status: "ok",
|
|
239
|
+
meta: outcomeMeta(activity.count),
|
|
240
|
+
...(activity.hits.length ? { sources: hitsForDisplay(activity.hits) } : {}),
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
case "trace":
|
|
244
|
+
// An opaque upstream step. It has a label but no arguments and no
|
|
245
|
+
// output, so it rebuilds as a bodyless tool row — the canon's own
|
|
246
|
+
// read for "a step that ran".
|
|
247
|
+
return {
|
|
248
|
+
kind: "tool",
|
|
249
|
+
key,
|
|
250
|
+
name: activity.label,
|
|
251
|
+
status: activity.done ? "ok" : "running",
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
case "reasoning":
|
|
255
|
+
return { kind: "thinking", key, label: "reasoning", text: activity.text };
|
|
256
|
+
|
|
257
|
+
case "brief":
|
|
258
|
+
return {
|
|
259
|
+
kind: "brief",
|
|
260
|
+
key,
|
|
261
|
+
themes: activity.themes,
|
|
262
|
+
...(activity.questions?.length ? { questions: activity.questions } : {}),
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
case "status": {
|
|
266
|
+
// Absent outcome: a quiet note (e.g. results exist but tenant detail
|
|
267
|
+
// withholds them). Present outcome: the emitter kept the structure,
|
|
268
|
+
// so this renders as a first-class tool row — "failed" as an error,
|
|
269
|
+
// "unreadable" as an honest unknown with NO count. A missing count
|
|
270
|
+
// stays missing: defaulting it to the zero string would read as
|
|
271
|
+
// "no hits", the exact lie this removes.
|
|
272
|
+
const outcome = activity.outcome;
|
|
273
|
+
if (!outcome) return { kind: "aside", key, message: activity.message };
|
|
274
|
+
const outcomeArgs = displayArgs(outcome.query, previousQueryAt(activities, i));
|
|
275
|
+
return {
|
|
276
|
+
kind: "tool",
|
|
277
|
+
key,
|
|
278
|
+
name: toolDisplayName(outcome.name),
|
|
279
|
+
...(outcomeArgs ? { args: outcomeArgs } : {}),
|
|
280
|
+
status: outcome.state === "failed" ? "error" : "ok",
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
default: {
|
|
285
|
+
const _exhaustive: never = activity;
|
|
286
|
+
void _exhaustive;
|
|
287
|
+
return { kind: "aside", key, message: "" };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* The step to show in the waiting caret: what the agent is doing *right now*,
|
|
295
|
+
* or nothing.
|
|
296
|
+
*
|
|
297
|
+
* The caret used to cycle a hard-coded script — "thinking", "routing through
|
|
298
|
+
* digigraph", "gathering context", "composing the answer" — on a ~10s loop
|
|
299
|
+
* regardless of what was happening. It read as a placeholder because it was
|
|
300
|
+
* one: none of those words were tied to any real step, and two of them named
|
|
301
|
+
* a backend this transcript may not even be talking to.
|
|
302
|
+
*
|
|
303
|
+
* So the label comes from the stream or there is no label. `undefined` means
|
|
304
|
+
* the caller shows a bare blinking cursor — an honest "something is happening"
|
|
305
|
+
* beats a confident sentence about the wrong thing.
|
|
306
|
+
*
|
|
307
|
+
* Scans from the end because the newest unfinished step is the current one.
|
|
308
|
+
* A tool row is "unfinished" until a tool_result for the same name arrives;
|
|
309
|
+
* matching on name alone (not name+query) is deliberate, since a call's query
|
|
310
|
+
* is often still empty while it is in flight — that is exactly the case this
|
|
311
|
+
* has to cover.
|
|
312
|
+
*/
|
|
313
|
+
export function liveActivityLabel(activities: DigiChatActivity[]): string | undefined {
|
|
314
|
+
const settledTools = new Set<string>();
|
|
315
|
+
for (let i = activities.length - 1; i >= 0; i -= 1) {
|
|
316
|
+
const activity = activities[i];
|
|
317
|
+
switch (activity.kind) {
|
|
318
|
+
case "tool_result":
|
|
319
|
+
settledTools.add(activity.name);
|
|
320
|
+
break;
|
|
321
|
+
case "status":
|
|
322
|
+
// A promoted status settles exactly the tool it names — keyed by
|
|
323
|
+
// name, the same key tool_result uses, so one tool's outcome never
|
|
324
|
+
// silences a different tool still running.
|
|
325
|
+
if (activity.outcome) settledTools.add(activity.outcome.name);
|
|
326
|
+
break;
|
|
327
|
+
case "tool_call": {
|
|
328
|
+
if (settledTools.has(activity.name)) break;
|
|
329
|
+
const query = activity.query?.trim();
|
|
330
|
+
return query ? `Searching for "${query}"` : "Searching…";
|
|
331
|
+
}
|
|
332
|
+
case "trace":
|
|
333
|
+
if (!activity.done) return activity.label;
|
|
334
|
+
break;
|
|
335
|
+
default:
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Deduped hit list across settled searches on a turn. Kept for callers that
|
|
344
|
+
* want a flat view; the session renders hits on each tool row instead.
|
|
345
|
+
*/
|
|
346
|
+
export function citationHits(activities: readonly DigiChatActivity[]): VaultHitSummary[] {
|
|
347
|
+
const out: VaultHitSummary[] = [];
|
|
348
|
+
const byPath = new Map<string, number>();
|
|
349
|
+
for (const activity of activities) {
|
|
350
|
+
if (activity.kind !== "tool_result") continue;
|
|
351
|
+
for (const hit of activity.hits) {
|
|
352
|
+
const idx = byPath.get(hit.path);
|
|
353
|
+
if (idx === undefined) {
|
|
354
|
+
byPath.set(hit.path, out.length);
|
|
355
|
+
out.push(hit);
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (!out[idx].body && hit.body) {
|
|
359
|
+
out[idx] = hit;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return out;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Foundry inline markers — `【9:0†source】` — stream as raw answer text.
|
|
367
|
+
* Strip so the prose stays human-readable; the chunks already sit on the
|
|
368
|
+
* search tool row that produced them. */
|
|
369
|
+
export function stripFoundryCitationMarkers(text: string): string {
|
|
370
|
+
return text
|
|
371
|
+
.replace(/\u3010[\d:]+\u2020source\u3011/g, "")
|
|
372
|
+
.replace(/[ \t]+\n/g, "\n")
|
|
373
|
+
.replace(/ {2,}/g, " ")
|
|
374
|
+
.trimEnd();
|
|
375
|
+
}
|