@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,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { DIGITHINGS } from "@digithings-ai/hero-banner/definitions";
|
|
4
|
+
import { HeroBanner } from "@digithings-ai/hero-banner/web";
|
|
5
|
+
|
|
6
|
+
/** @deprecated Pages use `BrandWordmark` (the studio 4×4 mask). This still
|
|
7
|
+
* draws the hero-banner `DIGITHINGS` cells for anything that has not moved.
|
|
8
|
+
* Do not mount it on the digithings hero, nav, or footer.
|
|
9
|
+
*
|
|
10
|
+
* Pixel lockup, drawn from the shared hero-banner definition
|
|
11
|
+
* (`packages/hero-banner`, `DIGITHINGS`: build 1400 ms, tick 40 ms).
|
|
12
|
+
* The hero builds on every load (`replayLoading`); the footer is the same
|
|
13
|
+
* mark at page width and stays blank until `live` flips the first time it
|
|
14
|
+
* scrolls into view. Reduced motion paints the settled mark with no build
|
|
15
|
+
* (inside `HeroBanner`, not in CSS). The `.pixel-word` wrapper keeps the
|
|
16
|
+
* responsive widths and the band aspect the footer ruling pins. */
|
|
17
|
+
export function PixelWordmark({
|
|
18
|
+
variant = "hero",
|
|
19
|
+
live = false,
|
|
20
|
+
}: {
|
|
21
|
+
variant?: "hero" | "footer";
|
|
22
|
+
live?: boolean;
|
|
23
|
+
}) {
|
|
24
|
+
if (variant === "footer") {
|
|
25
|
+
return (
|
|
26
|
+
<div className="pixel-word pixel-word-footer">
|
|
27
|
+
<HeroBanner def={DIGITHINGS} live={live} armed={live} />
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return (
|
|
32
|
+
<div className="pixel-word pixel-word-hero is-live">
|
|
33
|
+
<HeroBanner def={DIGITHINGS} replayLoading />
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static registry of the vendor logos referenced by the module/subsystem
|
|
3
|
+
* manifests. Named imports (not `import * as`) keep this tree-shakeable so only
|
|
4
|
+
* these ~18 icons ship — not the whole simple-icons set. Any slug NOT in this
|
|
5
|
+
* map falls back to a monogram chip in StackLogo (the no-mark names like
|
|
6
|
+
* NautilusTrader, LiteLLM, LangSmith, Cheaper Inference,
|
|
7
|
+
* Assistant UI — those publish no single-path monochrome SVG, which is what
|
|
8
|
+
* StackLogo needs).
|
|
9
|
+
*/
|
|
10
|
+
import {
|
|
11
|
+
siDocker, siDrizzle, siFastapi, siLangchain, siLanggraph,
|
|
12
|
+
siModelcontextprotocol, siNextdotjs, siOpenai, siOpentelemetry, siOptuna,
|
|
13
|
+
siPolars, siPostgresql, siPrometheus, siPydantic, siReact, siRedis, siSqlite,
|
|
14
|
+
siSupabase, siVercel,
|
|
15
|
+
} from "simple-icons";
|
|
16
|
+
|
|
17
|
+
export interface SimpleIcon { hex: string; path: string }
|
|
18
|
+
|
|
19
|
+
export const ICONS: Record<string, SimpleIcon> = {
|
|
20
|
+
docker: siDocker,
|
|
21
|
+
drizzle: siDrizzle,
|
|
22
|
+
fastapi: siFastapi,
|
|
23
|
+
langchain: siLangchain,
|
|
24
|
+
langgraph: siLanggraph,
|
|
25
|
+
modelcontextprotocol: siModelcontextprotocol,
|
|
26
|
+
nextdotjs: siNextdotjs,
|
|
27
|
+
openai: siOpenai,
|
|
28
|
+
opentelemetry: siOpentelemetry,
|
|
29
|
+
optuna: siOptuna,
|
|
30
|
+
polars: siPolars,
|
|
31
|
+
postgresql: siPostgresql,
|
|
32
|
+
prometheus: siPrometheus,
|
|
33
|
+
pydantic: siPydantic,
|
|
34
|
+
react: siReact,
|
|
35
|
+
redis: siRedis,
|
|
36
|
+
sqlite: siSqlite,
|
|
37
|
+
supabase: siSupabase,
|
|
38
|
+
vercel: siVercel,
|
|
39
|
+
};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState, type CSSProperties, type PointerEvent, type ReactNode } from "react";
|
|
4
|
+
import { ICONS } from "../logos";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Marquee — the infinite "built on" strip promoted from the design reference
|
|
8
|
+
* (data/marquee-ticker), in our terminal register: a mono row drifting behind
|
|
9
|
+
* an edge-fade mask, looping seamlessly. Hover pins the computed transform so
|
|
10
|
+
* the strip freezes where it is. Reduced motion stops the drift and the strip
|
|
11
|
+
* reads statically.
|
|
12
|
+
*
|
|
13
|
+
* Seamless loop mechanics: the content renders twice as two `.mq-group` spans
|
|
14
|
+
* and the track translates exactly -50% per cycle. Each group carries the
|
|
15
|
+
* inter-item gap as its own padding-right (and the track itself has NO gap),
|
|
16
|
+
* so half the track width equals exactly one period and the wrap is invisible.
|
|
17
|
+
*
|
|
18
|
+
* Items may be plain strings (legacy dress: mono chip with a leading accent
|
|
19
|
+
* dot) or `{ name, icon }` objects — `icon` is a slug in the shared Simple
|
|
20
|
+
* Icons registry (components/logos.ts) and renders as a small glyph before the
|
|
21
|
+
* name; an object without a resolvable icon renders text-only. Arbitrary
|
|
22
|
+
* `children` are also duplicated into the two groups. The overflow + edge-fade
|
|
23
|
+
* mask, the loop keyframes, the pause control and the reduced-motion stop live in
|
|
24
|
+
* styles/marquee.css. Wiring (in the consuming app):
|
|
25
|
+
* globals.css @import "@digithings-ai/ui/styles/marquee.css";
|
|
26
|
+
* @source "<path-to>/packages/ui/src/components/marquee";
|
|
27
|
+
*/
|
|
28
|
+
export type MarqueeItem = {
|
|
29
|
+
/** Display name (mono lozenge). */
|
|
30
|
+
name: string;
|
|
31
|
+
/** Simple Icons slug in the shared registry (components/logos.ts). */
|
|
32
|
+
icon?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type MarqueeProps = {
|
|
36
|
+
/** Row contents — strings (accent-dot chips) or `{ name, icon }` lozenges. */
|
|
37
|
+
items?: (string | MarqueeItem)[];
|
|
38
|
+
/** Arbitrary content instead of `items` (duplicated for the seamless loop). */
|
|
39
|
+
children?: ReactNode;
|
|
40
|
+
/** Drift direction. */
|
|
41
|
+
direction?: "left" | "right";
|
|
42
|
+
/** Loop duration in seconds (one full -50% translate). */
|
|
43
|
+
speed?: number;
|
|
44
|
+
/** Item text tone — reference dress is "soft"; "mute" for quieter strips. */
|
|
45
|
+
tone?: "soft" | "mute";
|
|
46
|
+
/** Hold the drift still (a consumer's pause control). Hover pauses regardless. */
|
|
47
|
+
paused?: boolean;
|
|
48
|
+
/** Accessible label for the strip. */
|
|
49
|
+
"aria-label"?: string;
|
|
50
|
+
/** Extra classes on the masked row wrapper. */
|
|
51
|
+
className?: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Literal class strings (not a `text-ink-${tone}` template) so Tailwind's
|
|
55
|
+
// @source scan generates both variants.
|
|
56
|
+
const TONE: Record<"soft" | "mute", string> = {
|
|
57
|
+
soft: "text-ink-soft",
|
|
58
|
+
mute: "text-ink-mute",
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** Horizontal translation from a computed `matrix(...)` or our own `translateX(Npx)`. */
|
|
62
|
+
export function matrixTranslateX(transform: string): number | null {
|
|
63
|
+
const value = transform.trim();
|
|
64
|
+
if (value === "" || value === "none") return 0;
|
|
65
|
+
const translate = value.match(/^translateX\(([-\d.]+)px\)$/);
|
|
66
|
+
if (translate) {
|
|
67
|
+
const n = Number(translate[1]);
|
|
68
|
+
return Number.isFinite(n) ? n : null;
|
|
69
|
+
}
|
|
70
|
+
const matrix = value.match(/^matrix\(([^)]+)\)$/);
|
|
71
|
+
if (!matrix) return null;
|
|
72
|
+
const parts = matrix[1].split(",").map((part) => Number(part.trim()));
|
|
73
|
+
if (parts.length !== 6 || parts.some((n) => !Number.isFinite(n))) return null;
|
|
74
|
+
return parts[4];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Progress through one loop, 0 inclusive to 1 exclusive.
|
|
79
|
+
* Left drift runs from 0 to `-period`. Right drift runs from `-period` to 0.
|
|
80
|
+
*/
|
|
81
|
+
export function loopProgress(
|
|
82
|
+
translateX: number,
|
|
83
|
+
periodPx: number,
|
|
84
|
+
direction: "left" | "right",
|
|
85
|
+
): number {
|
|
86
|
+
if (!(periodPx > 0) || !Number.isFinite(translateX)) return 0;
|
|
87
|
+
const along = direction === "left" ? -translateX / periodPx : (translateX + periodPx) / periodPx;
|
|
88
|
+
let wrapped = along % 1;
|
|
89
|
+
if (wrapped < 0) wrapped += 1;
|
|
90
|
+
return wrapped || 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Negative delay that restarts a linear loop at `progress`. */
|
|
94
|
+
export function resumeDelaySeconds(progress: number, durationSeconds: number): string {
|
|
95
|
+
const p = Number.isFinite(progress) ? Math.min(1, Math.max(0, progress)) : 0;
|
|
96
|
+
const d = Number.isFinite(durationSeconds) && durationSeconds > 0 ? durationSeconds : 0;
|
|
97
|
+
return `-${p * d}s`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function Chip({ item, tone }: { item: string | MarqueeItem; tone: "soft" | "mute" }) {
|
|
101
|
+
const isPlain = typeof item === "string";
|
|
102
|
+
const name = isPlain ? item : item.name;
|
|
103
|
+
const icon = !isPlain && item.icon ? (ICONS[item.icon] ?? null) : null;
|
|
104
|
+
return (
|
|
105
|
+
<span
|
|
106
|
+
className={`inline-flex items-center gap-[0.55rem] font-mono text-[0.82rem] whitespace-nowrap ${TONE[tone]}`}
|
|
107
|
+
>
|
|
108
|
+
{isPlain ? <span className="h-1 w-1 rounded-full bg-accent" aria-hidden="true" /> : null}
|
|
109
|
+
{icon ? (
|
|
110
|
+
<svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true">
|
|
111
|
+
<path d={icon.path} fill="currentColor" />
|
|
112
|
+
</svg>
|
|
113
|
+
) : null}
|
|
114
|
+
{name}
|
|
115
|
+
</span>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function Marquee({
|
|
120
|
+
items,
|
|
121
|
+
children,
|
|
122
|
+
direction = "left",
|
|
123
|
+
speed = 34,
|
|
124
|
+
tone = "soft",
|
|
125
|
+
paused,
|
|
126
|
+
"aria-label": ariaLabel,
|
|
127
|
+
className,
|
|
128
|
+
}: MarqueeProps) {
|
|
129
|
+
const dirClass = direction === "right" ? "mq-track--right" : "mq-track--left";
|
|
130
|
+
const trackRef = useRef<HTMLDivElement>(null);
|
|
131
|
+
const hovering = useRef(false);
|
|
132
|
+
const [frozen, setFrozen] = useState<string | null>(null);
|
|
133
|
+
const [resumeDelay, setResumeDelay] = useState("0s");
|
|
134
|
+
|
|
135
|
+
const pin = () => {
|
|
136
|
+
const track = trackRef.current;
|
|
137
|
+
if (!track || frozen) return;
|
|
138
|
+
const tx = matrixTranslateX(getComputedStyle(track).transform);
|
|
139
|
+
if (tx === null) return;
|
|
140
|
+
setFrozen(`translateX(${tx}px)`);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const release = () => {
|
|
144
|
+
if (!frozen) return;
|
|
145
|
+
const track = trackRef.current;
|
|
146
|
+
const tx = matrixTranslateX(frozen) ?? 0;
|
|
147
|
+
const period = track ? track.scrollWidth / 2 : 0;
|
|
148
|
+
const progress = loopProgress(tx, period, direction);
|
|
149
|
+
setResumeDelay(resumeDelaySeconds(progress, speed));
|
|
150
|
+
setFrozen(null);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
if (paused || hovering.current || frozen === null) return;
|
|
155
|
+
const track = trackRef.current;
|
|
156
|
+
const tx = matrixTranslateX(frozen) ?? 0;
|
|
157
|
+
const period = track ? track.scrollWidth / 2 : 0;
|
|
158
|
+
setResumeDelay(resumeDelaySeconds(loopProgress(tx, period, direction), speed));
|
|
159
|
+
setFrozen(null);
|
|
160
|
+
}, [paused, frozen, direction, speed]);
|
|
161
|
+
|
|
162
|
+
const onPointerEnter = (event: PointerEvent<HTMLDivElement>) => {
|
|
163
|
+
hovering.current = true;
|
|
164
|
+
if (event.pointerType === "touch") return;
|
|
165
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
166
|
+
pin();
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const onPointerLeave = () => {
|
|
170
|
+
hovering.current = false;
|
|
171
|
+
if (!paused) release();
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const style = {
|
|
175
|
+
"--mq-speed": `${speed}s`,
|
|
176
|
+
animationDelay: frozen ? undefined : resumeDelay,
|
|
177
|
+
animation: frozen ? "none" : undefined,
|
|
178
|
+
transform: frozen ?? undefined,
|
|
179
|
+
} as CSSProperties;
|
|
180
|
+
const group = (hidden: boolean) => (
|
|
181
|
+
<span className="mq-group" aria-hidden={hidden || undefined}>
|
|
182
|
+
{items
|
|
183
|
+
? items.map((item, i) => (
|
|
184
|
+
<Chip key={`${typeof item === "string" ? item : item.name}-${i}`} item={item} tone={tone} />
|
|
185
|
+
))
|
|
186
|
+
: children}
|
|
187
|
+
</span>
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<div
|
|
192
|
+
className={`mq-row${className ? ` ${className}` : ""}`}
|
|
193
|
+
aria-label={ariaLabel}
|
|
194
|
+
data-paused={paused ? "true" : undefined}
|
|
195
|
+
onPointerEnter={onPointerEnter}
|
|
196
|
+
onPointerLeave={onPointerLeave}
|
|
197
|
+
>
|
|
198
|
+
<div ref={trackRef} className={`mq-track ${dirClass}`} style={style}>
|
|
199
|
+
{group(false)}
|
|
200
|
+
{group(true)}
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PerfMetrics — the tearsheet grade block promoted from the design reference
|
|
3
|
+
* (finance/perf-metrics): labels in mono micro-caps, values in large tabular
|
|
4
|
+
* numerals, laid out on an n-up hairline grid. Only reads that carry money
|
|
5
|
+
* meaning take `tone: "up" | "down"` (--up / --down phosphor semantics);
|
|
6
|
+
* everything else stays ink so the eye goes to what matters.
|
|
7
|
+
*
|
|
8
|
+
* The hairline cell grid is sibling-index CSS (styles/metrics.css) keyed off
|
|
9
|
+
* the container's `data-cols` — Tailwind can't express nth-child, so that
|
|
10
|
+
* grammar stays CSS. Below 720px the grid collapses to two-up and the
|
|
11
|
+
* borders re-derive. Server component — no state, no effects.
|
|
12
|
+
*
|
|
13
|
+
* Wiring (in the consuming app):
|
|
14
|
+
* globals.css @import "@digithings-ai/ui/styles/metrics.css";
|
|
15
|
+
* @source "<path-to>/packages/ui/src/components/metrics";
|
|
16
|
+
*/
|
|
17
|
+
export type PerfMetric = {
|
|
18
|
+
/** Mono micro-caps label — "CAGR", "Sharpe", "Max drawdown" … */
|
|
19
|
+
label: string;
|
|
20
|
+
/** Preformatted display value — "+44.9%", "1.82" … */
|
|
21
|
+
value: string;
|
|
22
|
+
/** Optional muted unit suffix rendered after the value — "%", "bps", "×". */
|
|
23
|
+
unit?: string;
|
|
24
|
+
/** Money semantics: only return / drawdown-class reads take a tone. */
|
|
25
|
+
tone?: "up" | "down";
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const COLS: Record<2 | 3 | 4, string> = {
|
|
29
|
+
2: "grid-cols-2",
|
|
30
|
+
3: "grid-cols-3",
|
|
31
|
+
4: "grid-cols-4",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export function PerfMetrics({
|
|
35
|
+
metrics,
|
|
36
|
+
columns = 4,
|
|
37
|
+
className,
|
|
38
|
+
}: {
|
|
39
|
+
metrics: PerfMetric[];
|
|
40
|
+
/** Desktop cells per row (collapses to 2 below 720px). */
|
|
41
|
+
columns?: 2 | 3 | 4;
|
|
42
|
+
className?: string;
|
|
43
|
+
}) {
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
data-cols={columns}
|
|
47
|
+
className={`perf-metrics grid ${COLS[columns]} overflow-hidden rounded-none border border-hair bg-surface max-[720px]:grid-cols-2${
|
|
48
|
+
className ? ` ${className}` : ""
|
|
49
|
+
}`}
|
|
50
|
+
>
|
|
51
|
+
{metrics.map((m) => (
|
|
52
|
+
<div key={m.label} className="perf-metric p-[1.2rem]">
|
|
53
|
+
<span className="block font-mono text-[0.6rem] uppercase tracking-[0.1em] text-ink-mute">
|
|
54
|
+
{m.label}
|
|
55
|
+
</span>
|
|
56
|
+
<span
|
|
57
|
+
className={`mt-[0.35rem] block font-mono text-[1.5rem] [font-variant-numeric:tabular-nums] ${
|
|
58
|
+
m.tone === "up" ? "text-up" : m.tone === "down" ? "text-down" : "text-ink"
|
|
59
|
+
}`}
|
|
60
|
+
>
|
|
61
|
+
{m.value}
|
|
62
|
+
{m.unit ? <span className="ms-[0.15em] text-[0.85rem] text-ink-mute">{m.unit}</span> : null}
|
|
63
|
+
</span>
|
|
64
|
+
</div>
|
|
65
|
+
))}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { animate, useInView, useReducedMotion } from "motion/react";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* StatCounter — the count-up metric strip promoted from the design reference
|
|
8
|
+
* (data/stat-counter): figures count up from zero the first time the strip
|
|
9
|
+
* scrolls into view, the one place a number earns a little motion. The running
|
|
10
|
+
* value is written straight to the DOM node (no per-frame re-render); mono,
|
|
11
|
+
* tabular numerals keep the digits from reflowing. Fires once; reduced motion
|
|
12
|
+
* and no-JS both show the final figure immediately (SSR ships the final value).
|
|
13
|
+
*
|
|
14
|
+
* Hairline dividers between stats are sibling-index CSS (styles/metrics.css):
|
|
15
|
+
* left hairlines on every stat but the first, re-derived for the 2-up collapse
|
|
16
|
+
* below 720px. Client component — in-view observer + imperative count.
|
|
17
|
+
*
|
|
18
|
+
* Wiring (in the consuming app):
|
|
19
|
+
* globals.css @import "@digithings-ai/ui/styles/metrics.css";
|
|
20
|
+
* @source "<path-to>/packages/ui/src/components/metrics";
|
|
21
|
+
*/
|
|
22
|
+
export type CounterStat = {
|
|
23
|
+
/** Final numeric value the counter lands on. */
|
|
24
|
+
value: number;
|
|
25
|
+
/** Fixed decimal places; omitted → rounded + locale-grouped. */
|
|
26
|
+
decimals?: number;
|
|
27
|
+
/** Literal prefix — "$", "+" … */
|
|
28
|
+
prefix?: string;
|
|
29
|
+
/** Literal suffix — "%", "×", "k" … */
|
|
30
|
+
suffix?: string;
|
|
31
|
+
/** Mono micro-caps label under the figure. */
|
|
32
|
+
label: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const COLS: Record<2 | 3 | 4, string> = {
|
|
36
|
+
2: "grid-cols-2",
|
|
37
|
+
3: "grid-cols-3",
|
|
38
|
+
4: "grid-cols-4",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function format(v: number, s: CounterStat) {
|
|
42
|
+
const n = s.decimals ? v.toFixed(s.decimals) : Math.round(v).toLocaleString();
|
|
43
|
+
return `${s.prefix ?? ""}${n}${s.suffix ?? ""}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function CountUp({ stat }: { stat: CounterStat }) {
|
|
47
|
+
const wrapRef = useRef<HTMLDivElement | null>(null);
|
|
48
|
+
const valueRef = useRef<HTMLSpanElement | null>(null);
|
|
49
|
+
const inView = useInView(wrapRef, { once: true, margin: "-15% 0px" });
|
|
50
|
+
const reduced = useReducedMotion();
|
|
51
|
+
|
|
52
|
+
// Write the running number straight to the DOM node — no per-frame React
|
|
53
|
+
// re-render. SSR ships the final value (correct with no JS); the count only
|
|
54
|
+
// runs once the strip scrolls into view.
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const node = valueRef.current;
|
|
57
|
+
if (!node || !inView || reduced) return;
|
|
58
|
+
const controls = animate(0, stat.value, {
|
|
59
|
+
duration: 1.1,
|
|
60
|
+
ease: [0.22, 1, 0.36, 1],
|
|
61
|
+
onUpdate: (v) => {
|
|
62
|
+
node.textContent = format(v, stat);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
return () => controls.stop();
|
|
66
|
+
}, [inView, reduced, stat]);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div ref={wrapRef} className="sc-stat px-[1.2rem] py-[1.4rem]">
|
|
70
|
+
<span
|
|
71
|
+
ref={valueRef}
|
|
72
|
+
className="block font-mono text-[clamp(1.6rem,4vw,2.4rem)] tabular-nums tracking-[-0.01em] text-accent"
|
|
73
|
+
>
|
|
74
|
+
{format(stat.value, stat)}
|
|
75
|
+
</span>
|
|
76
|
+
<span className="mt-[0.35rem] block font-mono text-[0.62rem] uppercase tracking-[0.1em] text-ink-mute">
|
|
77
|
+
{stat.label}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function StatCounter({
|
|
84
|
+
stats,
|
|
85
|
+
columns = 4,
|
|
86
|
+
className,
|
|
87
|
+
}: {
|
|
88
|
+
stats: CounterStat[];
|
|
89
|
+
/** Desktop stats per row (collapses to 2 below 720px). */
|
|
90
|
+
columns?: 2 | 3 | 4;
|
|
91
|
+
className?: string;
|
|
92
|
+
}) {
|
|
93
|
+
return (
|
|
94
|
+
<div
|
|
95
|
+
className={`stat-counter grid ${COLS[columns]} overflow-hidden rounded-none border border-hair bg-surface max-[720px]:grid-cols-2${
|
|
96
|
+
className ? ` ${className}` : ""
|
|
97
|
+
}`}
|
|
98
|
+
>
|
|
99
|
+
{stats.map((stat) => (
|
|
100
|
+
<CountUp key={stat.label} stat={stat} />
|
|
101
|
+
))}
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* LayoutLines — the persistent page geometry (D1, #4429).
|
|
7
|
+
*
|
|
8
|
+
* A fixed, full-viewport-height pair of dashed vertical hairlines at the
|
|
9
|
+
* container's edges, mounted once per page and `pointer-events-none`. Every
|
|
10
|
+
* section then sits between them, which is what makes a long page read as one
|
|
11
|
+
* built object rather than a stack of bordered boxes. Without it the only
|
|
12
|
+
* vertical structure on the page is whatever each section draws for itself.
|
|
13
|
+
*
|
|
14
|
+
* Technique read out of launch-ui's `components/ui/layout-lines.tsx` (MIT) and
|
|
15
|
+
* re-expressed in this kit's tokens and width register. The width comes from
|
|
16
|
+
* `--frame-w` plus a gutter on each side, the same register the document
|
|
17
|
+
* grammar uses.
|
|
18
|
+
*
|
|
19
|
+
* Renders nothing meaningful to AT and is `aria-hidden` by construction (an
|
|
20
|
+
* empty decorative element). `fixed` + `inset-0` means it does not scroll,
|
|
21
|
+
* which is the point: the rules are a property of the viewport, not the page.
|
|
22
|
+
*
|
|
23
|
+
* The rules used to land flush on the column, so text ran straight up to them.
|
|
24
|
+
* `--line-pad` pushes the pair *outward* from the content: the inner hairline
|
|
25
|
+
* grows by the gutter on each side while every section keeps its own
|
|
26
|
+
* `--page-pad`, which is what gives the page air inside its own edges. So the
|
|
27
|
+
* rails sit one gutter outside the content column rather than on it.
|
|
28
|
+
*
|
|
29
|
+
* They deliberately do NOT meet the section dividers. `line-t` / `line-b` on a
|
|
30
|
+
* section are full-bleed by design, and those full-width rules are part of the
|
|
31
|
+
* page's document feel; making them stop at the rails would turn every band
|
|
32
|
+
* into a framed box. Two roles, two weights: the dividers separate sections,
|
|
33
|
+
* the rails are a fixed vertical guide that the dividers cross. The rails draw
|
|
34
|
+
* at `--hair-2` because at `--hair` they were invisible — see the note beside
|
|
35
|
+
* `line-y` in web-theme.css.
|
|
36
|
+
*/
|
|
37
|
+
export function LayoutLines({ className }: { className?: string }) {
|
|
38
|
+
return (
|
|
39
|
+
<div
|
|
40
|
+
aria-hidden="true"
|
|
41
|
+
className={cn("pointer-events-none fixed inset-0 top-0 z-0 overflow-hidden", className)}
|
|
42
|
+
// Above a full-bleed chat canvas, under the site nav (z-index 210).
|
|
43
|
+
// Inline so a host that never scans the z-20 utility still stacks the rails.
|
|
44
|
+
style={{ zIndex: 20 }}
|
|
45
|
+
>
|
|
46
|
+
<div
|
|
47
|
+
className="mx-auto h-full w-full max-w-[calc(var(--frame-w)+2*var(--line-pad,0px))] line-y line-dashed"
|
|
48
|
+
style={{
|
|
49
|
+
["--line-pad" as string]: "var(--page-pad)",
|
|
50
|
+
// Same column as web-theme.css `line-y` + the max-w utility. Inline so a
|
|
51
|
+
// host that never scans that arbitrary class still insets the rails.
|
|
52
|
+
width: "100%",
|
|
53
|
+
maxWidth: "calc(var(--frame-w) + 2 * var(--line-pad, 0px))",
|
|
54
|
+
marginInline: "auto",
|
|
55
|
+
// Same 1px dashed --hair-2 rails as web-theme.css `line-y` + `line-dashed`.
|
|
56
|
+
borderLeft: "1px dashed var(--hair-2)",
|
|
57
|
+
borderRight: "1px dashed var(--hair-2)",
|
|
58
|
+
boxSizing: "border-box",
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* MockupFrame + Mockup — the two-layer product artefact (D1, #4429).
|
|
67
|
+
*
|
|
68
|
+
* A `glow`/gradient frame at one depth holding a surface at another, with the
|
|
69
|
+
* inner surface carrying the drop shadow. That layering is what gives a product
|
|
70
|
+
* shot depth; a bare screenshot inside a border reads flat. Pair with <Glow/>
|
|
71
|
+
* behind it and `fade-bottom` on the section.
|
|
72
|
+
*
|
|
73
|
+
* Technique read out of launch-ui's `components/ui/mockup.tsx` (MIT), ported to
|
|
74
|
+
* this kit's tokens (`--hair`, `--surface`) and to zero radius, matching the
|
|
75
|
+
* canon's flat-corner rule.
|
|
76
|
+
*/
|
|
77
|
+
export function MockupFrame({
|
|
78
|
+
size = "small",
|
|
79
|
+
className,
|
|
80
|
+
children,
|
|
81
|
+
}: {
|
|
82
|
+
/** Frame padding — the visible "bezel" around the mockup. */
|
|
83
|
+
size?: "small" | "large";
|
|
84
|
+
className?: string;
|
|
85
|
+
children: ReactNode;
|
|
86
|
+
}) {
|
|
87
|
+
return (
|
|
88
|
+
<div
|
|
89
|
+
className={cn(
|
|
90
|
+
"relative z-10 overflow-hidden bg-hair",
|
|
91
|
+
size === "small" ? "p-[0.5rem]" : "p-[1rem]",
|
|
92
|
+
className,
|
|
93
|
+
)}
|
|
94
|
+
>
|
|
95
|
+
{children}
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function Mockup({
|
|
101
|
+
className,
|
|
102
|
+
children,
|
|
103
|
+
}: {
|
|
104
|
+
className?: string;
|
|
105
|
+
children: ReactNode;
|
|
106
|
+
}) {
|
|
107
|
+
return (
|
|
108
|
+
<div
|
|
109
|
+
className={cn(
|
|
110
|
+
"relative z-10 overflow-hidden border border-hair bg-surface shadow-[0_20px_50px_-12px_rgba(0,0,0,0.45)]",
|
|
111
|
+
className,
|
|
112
|
+
)}
|
|
113
|
+
>
|
|
114
|
+
{children}
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Glow — the light source behind a hero artefact (D1, #4429).
|
|
121
|
+
*
|
|
122
|
+
* Two stacked radial ellipses at different scales and opacities, so the falloff
|
|
123
|
+
* is not a single flat wash. `absolute` and `pointer-events-none`; the parent
|
|
124
|
+
* must be `relative`. Accent-tinted, so each app's livery (or the umbrella's
|
|
125
|
+
* neutral ink) dresses it automatically.
|
|
126
|
+
*
|
|
127
|
+
* Technique read out of launch-ui's `components/ui/glow.tsx` (MIT). Their
|
|
128
|
+
* version hardcodes `--brand`; this one reads `--accent`, which digithings.ai
|
|
129
|
+
* already collapses to neutral ink in its own sheet — so the same component
|
|
130
|
+
* gives digiquant.io a green wash and us a white one.
|
|
131
|
+
*/
|
|
132
|
+
export function Glow({
|
|
133
|
+
variant = "top",
|
|
134
|
+
className,
|
|
135
|
+
}: {
|
|
136
|
+
variant?: "top" | "above" | "bottom" | "below" | "center";
|
|
137
|
+
className?: string;
|
|
138
|
+
}) {
|
|
139
|
+
const position =
|
|
140
|
+
variant === "above"
|
|
141
|
+
? "-top-[128px]"
|
|
142
|
+
: variant === "bottom"
|
|
143
|
+
? "bottom-0"
|
|
144
|
+
: variant === "below"
|
|
145
|
+
? "-bottom-[128px]"
|
|
146
|
+
: variant === "center"
|
|
147
|
+
? "top-[50%]"
|
|
148
|
+
: "top-0";
|
|
149
|
+
const centered = variant === "center" ? "-translate-y-1/2" : undefined;
|
|
150
|
+
return (
|
|
151
|
+
<div
|
|
152
|
+
aria-hidden="true"
|
|
153
|
+
className={cn("pointer-events-none absolute w-full", position, className)}
|
|
154
|
+
>
|
|
155
|
+
<div
|
|
156
|
+
className={cn(
|
|
157
|
+
"absolute left-1/2 h-[256px] w-[60%] -translate-x-1/2 scale-[2.5] rounded-[50%] opacity-20 sm:h-[512px]",
|
|
158
|
+
centered,
|
|
159
|
+
)}
|
|
160
|
+
style={{
|
|
161
|
+
backgroundImage:
|
|
162
|
+
"radial-gradient(ellipse at center, color-mix(in srgb, var(--accent) 50%, transparent) 10%, transparent 60%)",
|
|
163
|
+
}}
|
|
164
|
+
/>
|
|
165
|
+
<div
|
|
166
|
+
className={cn(
|
|
167
|
+
"absolute left-1/2 h-[128px] w-[40%] -translate-x-1/2 scale-[2] rounded-[50%] opacity-20 sm:h-[256px]",
|
|
168
|
+
centered,
|
|
169
|
+
)}
|
|
170
|
+
style={{
|
|
171
|
+
backgroundImage:
|
|
172
|
+
"radial-gradient(ellipse at center, color-mix(in srgb, var(--accent) 30%, transparent) 10%, transparent 60%)",
|
|
173
|
+
}}
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
177
|
+
}
|