@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,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place mermaid gets told what colour anything is.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `ChatMermaidBlock` so every mermaid surface in the kit — the
|
|
5
|
+
* chat fence and the architecture diagram — resolves the same palette the same
|
|
6
|
+
* way, and a token added for one is a token added for both.
|
|
7
|
+
*
|
|
8
|
+
* THE RULE: mermaid gets `theme: "base"` — the only built-in palette that lets
|
|
9
|
+
* every `themeVariable` through — and every variable is filled from the
|
|
10
|
+
* *computed* value of a design token read off the host element. Reading off the
|
|
11
|
+
* host rather than `:root` means a livery scope (`.accent-digichat`,
|
|
12
|
+
* `.accent-digiquant`) or a nested `[data-theme]` resolves exactly the colours
|
|
13
|
+
* the surrounding section resolves.
|
|
14
|
+
*
|
|
15
|
+
* Tokens that do not resolve (tokens.css not loaded, non-browser) are omitted,
|
|
16
|
+
* so mermaid falls back to its own value for that variable rather than being
|
|
17
|
+
* handed an empty string it cannot parse.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* mermaid themeVariable -> design token. Kept explicit (rather than letting
|
|
22
|
+
* mermaid derive everything from `primaryColor`) because its derivation runs
|
|
23
|
+
* khroma lighten/darken on whatever it is given, and the canon palette is not a
|
|
24
|
+
* ramp — `--ink-mute` is the line colour in both themes, not a shade of the
|
|
25
|
+
* node fill. Diagram kinds beyond flowchart borrow these too: the sequence,
|
|
26
|
+
* state, class, ER and architecture renderers all read actor/label/note/
|
|
27
|
+
* attribute/border vars.
|
|
28
|
+
*/
|
|
29
|
+
export const THEME_TOKENS: ReadonlyArray<readonly [string, string]> = [
|
|
30
|
+
// canvas + type
|
|
31
|
+
["background", "--surface"],
|
|
32
|
+
["textColor", "--ink-soft"],
|
|
33
|
+
["titleColor", "--ink"],
|
|
34
|
+
// nodes
|
|
35
|
+
["primaryColor", "--surface-2"],
|
|
36
|
+
["primaryTextColor", "--ink"],
|
|
37
|
+
["primaryBorderColor", "--accent"],
|
|
38
|
+
["secondaryColor", "--surface"],
|
|
39
|
+
["secondaryTextColor", "--ink"],
|
|
40
|
+
["secondaryBorderColor", "--hair-2"],
|
|
41
|
+
["tertiaryColor", "--surface"],
|
|
42
|
+
["tertiaryTextColor", "--ink-soft"],
|
|
43
|
+
["tertiaryBorderColor", "--hair-2"],
|
|
44
|
+
["mainBkg", "--surface-2"],
|
|
45
|
+
["nodeBorder", "--accent"],
|
|
46
|
+
["nodeTextColor", "--ink"],
|
|
47
|
+
// edges + clusters
|
|
48
|
+
["lineColor", "--ink-mute"],
|
|
49
|
+
["edgeLabelBackground", "--surface"],
|
|
50
|
+
["clusterBkg", "--surface"],
|
|
51
|
+
["clusterBorder", "--hair-2"],
|
|
52
|
+
// architecture (v11 architecture-beta)
|
|
53
|
+
["archEdgeColor", "--ink-mute"],
|
|
54
|
+
["archEdgeArrowColor", "--ink-mute"],
|
|
55
|
+
["archGroupBorderColor", "--hair-2"],
|
|
56
|
+
// sequence
|
|
57
|
+
["actorBkg", "--surface-2"],
|
|
58
|
+
["actorBorder", "--accent"],
|
|
59
|
+
["actorTextColor", "--ink"],
|
|
60
|
+
["actorLineColor", "--ink-mute"],
|
|
61
|
+
["signalColor", "--ink-mute"],
|
|
62
|
+
["signalTextColor", "--ink-soft"],
|
|
63
|
+
["activationBkgColor", "--surface-2"],
|
|
64
|
+
["activationBorderColor", "--accent"],
|
|
65
|
+
["sequenceNumberColor", "--surface"],
|
|
66
|
+
["labelBoxBkgColor", "--surface-2"],
|
|
67
|
+
["labelBoxBorderColor", "--hair-2"],
|
|
68
|
+
["labelTextColor", "--ink"],
|
|
69
|
+
["loopTextColor", "--ink-soft"],
|
|
70
|
+
["noteBkgColor", "--surface"],
|
|
71
|
+
["noteBorderColor", "--hair-2"],
|
|
72
|
+
["noteTextColor", "--ink-soft"],
|
|
73
|
+
["altBackground", "--surface-2"],
|
|
74
|
+
// class / ER / state
|
|
75
|
+
["classText", "--ink"],
|
|
76
|
+
["attributeBackgroundColorOdd", "--surface"],
|
|
77
|
+
["attributeBackgroundColorEven", "--surface-2"],
|
|
78
|
+
// pie / quadrant chrome (categorical slice hues stay mermaid's — the canon
|
|
79
|
+
// has no categorical ramp in the token layer)
|
|
80
|
+
["pieTitleTextColor", "--ink"],
|
|
81
|
+
["pieSectionTextColor", "--ink"],
|
|
82
|
+
["pieLegendTextColor", "--ink-soft"],
|
|
83
|
+
["pieStrokeColor", "--surface"],
|
|
84
|
+
["pieOuterStrokeColor", "--hair-2"],
|
|
85
|
+
// failure chrome — the negative-semantics token, so a bad node reads red in
|
|
86
|
+
// both themes without a literal
|
|
87
|
+
["errorBkgColor", "--surface"],
|
|
88
|
+
["errorTextColor", "--down"],
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Stroke widths are numbers, not colours, so they cannot come from a token —
|
|
93
|
+
* they are pinned here to the kit's hairline weight so an architecture diagram
|
|
94
|
+
* draws at the same weight as every other rule on the page.
|
|
95
|
+
*/
|
|
96
|
+
const THEME_LITERALS: ReadonlyArray<readonly [string, string]> = [
|
|
97
|
+
["archEdgeWidth", "1.4"],
|
|
98
|
+
["archGroupBorderWidth", "1"],
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
/** Resolve THEME_TOKENS against `host`'s computed style. */
|
|
102
|
+
export function tokenThemeVariables(host: Element): Record<string, string | boolean> {
|
|
103
|
+
const cs = window.getComputedStyle(host);
|
|
104
|
+
const vars: Record<string, string | boolean> = {};
|
|
105
|
+
for (const [key, token] of THEME_TOKENS) {
|
|
106
|
+
const value = cs.getPropertyValue(token).trim();
|
|
107
|
+
if (value) vars[key] = value;
|
|
108
|
+
}
|
|
109
|
+
for (const [key, value] of THEME_LITERALS) vars[key] = value;
|
|
110
|
+
const mono = cs.getPropertyValue("--font-mono").trim();
|
|
111
|
+
if (mono) vars.fontFamily = mono;
|
|
112
|
+
// mermaid's own lighten/darken direction for anything it still derives.
|
|
113
|
+
vars.darkMode = document.documentElement.getAttribute("data-theme") !== "light";
|
|
114
|
+
return vars;
|
|
115
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scroll pose for the architecture tour camera.
|
|
3
|
+
*
|
|
4
|
+
* The walk is one 0..1 progress value. Each step owns an equal slice. The
|
|
5
|
+
* camera rests on that step's boxes through the parked midpoint (where a
|
|
6
|
+
* single wheel gesture settles), then glides to the next step's boxes so it
|
|
7
|
+
* arrives as the narration crosses the boundary.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface CamFrame {
|
|
11
|
+
k: number;
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const CAM_IDENTITY: CamFrame = { k: 1, x: 0, y: 0 };
|
|
17
|
+
|
|
18
|
+
export interface Box {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
w: number;
|
|
22
|
+
h: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface SpotRect {
|
|
26
|
+
left: number;
|
|
27
|
+
top: number;
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const clamp01 = (n: number): number => Math.max(0, Math.min(1, n));
|
|
33
|
+
|
|
34
|
+
const smooth = (n: number): number => {
|
|
35
|
+
const t = clamp01(n);
|
|
36
|
+
return t * t * (3 - 2 * t);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Active step and the 0..1 position inside it. Progress 1 stays on the last step. */
|
|
40
|
+
export function walkCursor(progress: number, count: number): { index: number; frac: number } {
|
|
41
|
+
if (count <= 0) return { index: 0, frac: 0 };
|
|
42
|
+
const v = clamp01(progress) * count;
|
|
43
|
+
const index = Math.min(count - 1, Math.floor(v));
|
|
44
|
+
const frac = v - index;
|
|
45
|
+
return { index, frac: clamp01(frac) };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 0 while the reader is parked on this step's boxes, then 0→1 across the
|
|
50
|
+
* tail of the step so the camera is on the next boxes as the step index flips.
|
|
51
|
+
*/
|
|
52
|
+
export function glideAmount(frac: number): number {
|
|
53
|
+
return smooth((frac - 0.55) / 0.45);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function lerpCam(a: CamFrame, b: CamFrame, t: number): CamFrame {
|
|
57
|
+
const u = clamp01(t);
|
|
58
|
+
return {
|
|
59
|
+
k: a.k + (b.k - a.k) * u,
|
|
60
|
+
x: a.x + (b.x - a.x) * u,
|
|
61
|
+
y: a.y + (b.y - a.y) * u,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function camTransform(frame: CamFrame): string {
|
|
66
|
+
if (frame.k < 1.02 && Math.abs(frame.x) < 0.5 && Math.abs(frame.y) < 0.5) return "";
|
|
67
|
+
return `scale(${frame.k.toFixed(4)}) translate(${frame.x.toFixed(2)}px, ${frame.y.toFixed(2)}px)`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Frame the lit boxes. Zoom is capped so a small pair of boxes cannot crop
|
|
72
|
+
* the rest of the drawing out of the stage, and a step that already fills
|
|
73
|
+
* the frame (the whole-graph opener) stays at identity.
|
|
74
|
+
*/
|
|
75
|
+
export function fitCamera(opts: {
|
|
76
|
+
boxes: Box[];
|
|
77
|
+
stageW: number;
|
|
78
|
+
stageH: number;
|
|
79
|
+
contentW: number;
|
|
80
|
+
contentH: number;
|
|
81
|
+
/** Fraction of the stage the target should occupy. */
|
|
82
|
+
fill?: number;
|
|
83
|
+
maxScale?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Contain (default) fits the whole cluster in the stage, so a wide row
|
|
86
|
+
* never zooms. Cover zooms on the tighter axis, up to `maxScale`, so a
|
|
87
|
+
* wide short row still travels into view.
|
|
88
|
+
*/
|
|
89
|
+
cover?: boolean;
|
|
90
|
+
/** Stage pixels kept clear around the lit boxes, so the glow is never cut. */
|
|
91
|
+
pad?: number;
|
|
92
|
+
}): CamFrame {
|
|
93
|
+
const { boxes, stageW, stageH, contentW, contentH } = opts;
|
|
94
|
+
const fill = opts.fill ?? 0.34;
|
|
95
|
+
const maxScale = opts.maxScale ?? 1.45;
|
|
96
|
+
const pad = opts.pad ?? 24;
|
|
97
|
+
if (boxes.length === 0 || stageW <= 0 || stageH <= 0 || contentW <= 0 || contentH <= 0) {
|
|
98
|
+
return CAM_IDENTITY;
|
|
99
|
+
}
|
|
100
|
+
const minX = Math.min(...boxes.map((b) => b.x));
|
|
101
|
+
const minY = Math.min(...boxes.map((b) => b.y));
|
|
102
|
+
const maxX = Math.max(...boxes.map((b) => b.x + b.w));
|
|
103
|
+
const maxY = Math.max(...boxes.map((b) => b.y + b.h));
|
|
104
|
+
const width = Math.max(maxX - minX, 1);
|
|
105
|
+
const height = Math.max(maxY - minY, 1);
|
|
106
|
+
/* A cluster that already covers the drawing is the whole-graph beat.
|
|
107
|
+
Cover would otherwise zoom the letterboxed margin around it. */
|
|
108
|
+
if (width / contentW > 0.7 && height / contentH > 0.62) return CAM_IDENTITY;
|
|
109
|
+
const contain = Math.min(
|
|
110
|
+
Math.max(stageW - 2 * pad, 1) / width,
|
|
111
|
+
Math.max(stageH - 2 * pad, 1) / height,
|
|
112
|
+
);
|
|
113
|
+
const cover = Math.max(stageW / width, stageH / height);
|
|
114
|
+
// Cover never crops the lit boxes: it stops at the padded contain fit.
|
|
115
|
+
const zoom = opts.cover ? Math.min(fill * cover, contain) : fill * contain;
|
|
116
|
+
const k = Math.max(1, Math.min(maxScale, zoom));
|
|
117
|
+
if (k < 1.02) return CAM_IDENTITY;
|
|
118
|
+
const cx = (minX + maxX) / 2;
|
|
119
|
+
const cy = (minY + maxY) / 2;
|
|
120
|
+
const pan = (span: number, content: number, centre: number): number => {
|
|
121
|
+
const low = span / k - content;
|
|
122
|
+
if (low > 0) return low / 2;
|
|
123
|
+
return Math.min(0, Math.max(low, span / (2 * k) - centre));
|
|
124
|
+
};
|
|
125
|
+
return { k, x: pan(stageW, contentW, cx), y: pan(stageH, contentH, cy) };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Spotlight over the lit boxes. A union that already covers the drawing is the
|
|
129
|
+
whole-graph beat — no spotlight, the full graph stays lit. */
|
|
130
|
+
export function unionSpot(
|
|
131
|
+
boxes: Box[],
|
|
132
|
+
contentW: number,
|
|
133
|
+
contentH: number,
|
|
134
|
+
pad = 6,
|
|
135
|
+
): SpotRect | null {
|
|
136
|
+
if (boxes.length === 0 || contentW <= 0 || contentH <= 0) return null;
|
|
137
|
+
const minX = Math.min(...boxes.map((b) => b.x));
|
|
138
|
+
const minY = Math.min(...boxes.map((b) => b.y));
|
|
139
|
+
const maxX = Math.max(...boxes.map((b) => b.x + b.w));
|
|
140
|
+
const maxY = Math.max(...boxes.map((b) => b.y + b.h));
|
|
141
|
+
const width = Math.max(maxX - minX, 0);
|
|
142
|
+
const height = Math.max(maxY - minY, 0);
|
|
143
|
+
if ((width * height) / (contentW * contentH) > 0.78) return null;
|
|
144
|
+
return {
|
|
145
|
+
left: minX - pad,
|
|
146
|
+
top: minY - pad,
|
|
147
|
+
width: width + pad * 2,
|
|
148
|
+
height: height + pad * 2,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function mixSpot(
|
|
153
|
+
a: SpotRect | null,
|
|
154
|
+
b: SpotRect | null,
|
|
155
|
+
t: number,
|
|
156
|
+
): { rect: SpotRect; opacity: number } | null {
|
|
157
|
+
const u = clamp01(t);
|
|
158
|
+
if (!a && !b) return null;
|
|
159
|
+
if (!a && b) return { rect: b, opacity: u };
|
|
160
|
+
if (a && !b) return { rect: a, opacity: 1 - u };
|
|
161
|
+
if (!a || !b) return null;
|
|
162
|
+
return {
|
|
163
|
+
rect: {
|
|
164
|
+
left: a.left + (b.left - a.left) * u,
|
|
165
|
+
top: a.top + (b.top - a.top) * u,
|
|
166
|
+
width: a.width + (b.width - a.width) * u,
|
|
167
|
+
height: a.height + (b.height - a.height) * u,
|
|
168
|
+
},
|
|
169
|
+
opacity: 1,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Button } from "../../ui";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Docs code affordances: a hover-revealed copy button, a bare copyable code
|
|
7
|
+
* block, and a label-tabbed code switcher. Presentation-generic — samples
|
|
8
|
+
* arrive as `{ label, code }`, so language naming ("curl" vs "bash") is the
|
|
9
|
+
* caller's business. Token-backed utilities carry the look; the hover-reveal
|
|
10
|
+
* combinator for the copy button lives in styles/docs.css.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface CodeSample {
|
|
14
|
+
/** Tab label, e.g. "curl", "Python", "TypeScript". */
|
|
15
|
+
label: string;
|
|
16
|
+
code: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Tiny write-only "copy / copied" clipboard button (1.2s confirmation flip). */
|
|
20
|
+
function DocsCopyButton({ text, ariaLabel }: { text: string; ariaLabel: string }) {
|
|
21
|
+
const [copied, setCopied] = useState(false);
|
|
22
|
+
return (
|
|
23
|
+
<Button
|
|
24
|
+
type="button"
|
|
25
|
+
variant="ghost"
|
|
26
|
+
className="docs-code-copy h-auto cursor-pointer rounded-none border border-hair bg-surface/80 px-[0.4rem] py-[0.12rem] font-mono text-[0.7rem] font-normal text-ink-mute hover:bg-transparent hover:text-ink"
|
|
27
|
+
aria-label={ariaLabel}
|
|
28
|
+
onClick={() =>
|
|
29
|
+
navigator.clipboard?.writeText(text).then(
|
|
30
|
+
() => {
|
|
31
|
+
setCopied(true);
|
|
32
|
+
setTimeout(() => setCopied(false), 1200);
|
|
33
|
+
},
|
|
34
|
+
() => {},
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
>
|
|
38
|
+
{copied ? "copied" : "copy"}
|
|
39
|
+
</Button>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Pre-formatted code block with the hover-revealed copy affordance. */
|
|
44
|
+
export function DocsCodeBlock({
|
|
45
|
+
code,
|
|
46
|
+
copyLabel = "Copy code",
|
|
47
|
+
}: {
|
|
48
|
+
code: string;
|
|
49
|
+
copyLabel?: string;
|
|
50
|
+
}) {
|
|
51
|
+
return (
|
|
52
|
+
<div className="doc-code relative mt-[0.5rem]">
|
|
53
|
+
<DocsCopyButton text={code} ariaLabel={copyLabel} />
|
|
54
|
+
<pre className="m-0 overflow-x-auto rounded-none border border-hair bg-ink/6 px-[0.8rem] py-[0.7rem]">
|
|
55
|
+
<code className="whitespace-pre font-mono text-[0.8rem] text-ink">{code}</code>
|
|
56
|
+
</pre>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Label-tabbed code block — one sample visible at a time, copyable. */
|
|
62
|
+
export function CodeTabs({ samples }: { samples: CodeSample[] }) {
|
|
63
|
+
const [sel, setSel] = useState(0);
|
|
64
|
+
if (!samples.length) return null;
|
|
65
|
+
const cur = samples[Math.min(sel, samples.length - 1)];
|
|
66
|
+
return (
|
|
67
|
+
<div className="mt-[0.3rem] flex flex-col gap-[0.3rem]">
|
|
68
|
+
{samples.length > 1 && (
|
|
69
|
+
<div className="flex gap-[0.2rem]" role="tablist">
|
|
70
|
+
{samples.map((s, i) => (
|
|
71
|
+
<Button
|
|
72
|
+
key={s.label}
|
|
73
|
+
type="button"
|
|
74
|
+
role="tab"
|
|
75
|
+
aria-selected={i === sel}
|
|
76
|
+
variant="link"
|
|
77
|
+
className={`h-auto cursor-pointer rounded-none border border-transparent px-[0.6rem] py-[0.2rem] font-mono text-[0.72rem] font-normal transition-colors duration-150 ease-brand hover:no-underline ${
|
|
78
|
+
i === sel ? "bg-accent-weak text-ink" : "text-ink-mute hover:text-ink-soft"
|
|
79
|
+
}`}
|
|
80
|
+
onClick={() => setSel(i)}
|
|
81
|
+
>
|
|
82
|
+
{s.label}
|
|
83
|
+
</Button>
|
|
84
|
+
))}
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
<DocsCodeBlock code={cur.code} copyLabel={`Copy ${cur.label} example`} />
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { cn } from "../../lib/utils";
|
|
5
|
+
import type { CodeSample } from "./CodeTabs";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The install command, as the primary action. OpenCode-informed: a label tab
|
|
9
|
+
* strip sits on a hairline, and the command beneath it *is* the copy button —
|
|
10
|
+
* no separate "copy" chrome floating beside it. A click blurs the command and
|
|
11
|
+
* shows "copied" for a few seconds, then the command returns. The command is
|
|
12
|
+
* split into a muted `protocol` prefix and a medium-weight ink payload; there
|
|
13
|
+
* is no syntax colour. The inline form is the shared `copy-cmd` button the
|
|
14
|
+
* hero clone and the module compose line both use.
|
|
15
|
+
*/
|
|
16
|
+
export interface CopyCommandSample extends CodeSample {
|
|
17
|
+
/** Leading token rendered muted, e.g. `docker compose` or `git clone`. */
|
|
18
|
+
protocol?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface CopyCommandProps {
|
|
22
|
+
samples: CopyCommandSample[];
|
|
23
|
+
/** Accessible name for the tab strip. */
|
|
24
|
+
ariaLabel?: string;
|
|
25
|
+
className?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Single-command form: no tab strip, no copy affordance — the whole code box
|
|
28
|
+
* *is* the button ("click the box and it copies"). For a hero where the
|
|
29
|
+
* command is a one-liner and the tabs would be noise. `samples[0]` is shown;
|
|
30
|
+
* the rest are ignored.
|
|
31
|
+
*/
|
|
32
|
+
inline?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** How long the command stays blurred with the copied note. */
|
|
36
|
+
const COPIED_MS = 2500;
|
|
37
|
+
|
|
38
|
+
async function writeClipboard(text: string): Promise<boolean> {
|
|
39
|
+
const clipboard = typeof navigator === "undefined" ? undefined : navigator.clipboard;
|
|
40
|
+
if (clipboard?.writeText) {
|
|
41
|
+
try {
|
|
42
|
+
await clipboard.writeText(text);
|
|
43
|
+
return true;
|
|
44
|
+
} catch {
|
|
45
|
+
/* fall through */
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const el = document.createElement("textarea");
|
|
50
|
+
el.value = text;
|
|
51
|
+
el.setAttribute("readonly", "");
|
|
52
|
+
el.style.position = "fixed";
|
|
53
|
+
el.style.top = "-9999px";
|
|
54
|
+
document.body.appendChild(el);
|
|
55
|
+
el.select();
|
|
56
|
+
const ok = document.execCommand("copy");
|
|
57
|
+
document.body.removeChild(el);
|
|
58
|
+
return ok;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function splitCommand(sample: CopyCommandSample): [string, string] {
|
|
65
|
+
const { code, protocol } = sample;
|
|
66
|
+
if (!protocol || !code.startsWith(protocol)) return ["", code];
|
|
67
|
+
return [protocol, code.slice(protocol.length)];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function CopyCommand({ samples, ariaLabel = "Install command", className, inline = false }: CopyCommandProps) {
|
|
71
|
+
const [selected, setSelected] = useState(0);
|
|
72
|
+
const [copied, setCopied] = useState(false);
|
|
73
|
+
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
74
|
+
|
|
75
|
+
useEffect(
|
|
76
|
+
() => () => {
|
|
77
|
+
if (timer.current) clearTimeout(timer.current);
|
|
78
|
+
},
|
|
79
|
+
[],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const current = samples[Math.min(selected, samples.length - 1)];
|
|
83
|
+
if (!current) return null;
|
|
84
|
+
|
|
85
|
+
const [protocol, payload] = splitCommand(current);
|
|
86
|
+
|
|
87
|
+
function copy() {
|
|
88
|
+
void writeClipboard(current.code).then((ok) => {
|
|
89
|
+
if (!ok) return;
|
|
90
|
+
setCopied(true);
|
|
91
|
+
if (timer.current) clearTimeout(timer.current);
|
|
92
|
+
timer.current = setTimeout(() => setCopied(false), COPIED_MS);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className={cn(inline ? "w-fit max-w-full" : "w-full max-w-[var(--measure-prose)]", className)}>
|
|
98
|
+
{inline ? null : (
|
|
99
|
+
<div
|
|
100
|
+
role="tablist"
|
|
101
|
+
aria-label={ariaLabel}
|
|
102
|
+
className="flex flex-wrap items-end gap-[1.4rem] border-b border-hair"
|
|
103
|
+
>
|
|
104
|
+
{samples.map((sample, index) => (
|
|
105
|
+
<button
|
|
106
|
+
key={sample.label}
|
|
107
|
+
type="button"
|
|
108
|
+
role="tab"
|
|
109
|
+
aria-selected={index === selected}
|
|
110
|
+
onClick={() => setSelected(index)}
|
|
111
|
+
className={cn(
|
|
112
|
+
"cursor-pointer border-b-2 border-transparent bg-transparent pb-[0.55rem] font-mono text-[0.8rem] tracking-[0.02em] transition-colors duration-150 ease-brand",
|
|
113
|
+
index === selected ? "border-b-ink text-ink" : "text-ink-mute hover:text-ink-soft",
|
|
114
|
+
)}
|
|
115
|
+
>
|
|
116
|
+
{sample.label}
|
|
117
|
+
</button>
|
|
118
|
+
))}
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
<button
|
|
122
|
+
type="button"
|
|
123
|
+
data-copied={copied ? "true" : "false"}
|
|
124
|
+
onClick={copy}
|
|
125
|
+
aria-label={`Copy ${inline ? "command" : `${current.label} command`}`}
|
|
126
|
+
className={cn(
|
|
127
|
+
"copy-cmd group relative flex cursor-pointer items-center gap-[1rem] px-[1rem] py-[0.85rem] text-left font-mono text-[0.85rem] leading-[1.5] text-ink hover:bg-surface-2",
|
|
128
|
+
inline
|
|
129
|
+
? "w-auto max-w-full border border-hair bg-transparent"
|
|
130
|
+
: "w-full border-x border-b border-t-0 border-hair bg-surface",
|
|
131
|
+
)}
|
|
132
|
+
>
|
|
133
|
+
<span
|
|
134
|
+
className={cn(
|
|
135
|
+
"min-w-0 truncate whitespace-nowrap transition-[filter,opacity] duration-200 group-data-[copied=true]:blur-[3px] group-data-[copied=true]:opacity-40",
|
|
136
|
+
inline ? "" : "flex-1",
|
|
137
|
+
)}
|
|
138
|
+
>
|
|
139
|
+
{protocol ? <span className="text-ink-mute">{protocol}</span> : null}
|
|
140
|
+
<span className="font-medium">{payload}</span>
|
|
141
|
+
</span>
|
|
142
|
+
<span
|
|
143
|
+
aria-hidden="true"
|
|
144
|
+
className="pointer-events-none absolute inset-0 hidden items-center justify-center font-medium text-ink group-data-[copied=true]:flex"
|
|
145
|
+
>
|
|
146
|
+
copied
|
|
147
|
+
</span>
|
|
148
|
+
</button>
|
|
149
|
+
</div>
|
|
150
|
+
);
|
|
151
|
+
}
|