@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,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strategy tear sheet model shared by the marketing band, the strategy page,
|
|
3
|
+
* and the quant TUI. Figures come only from the payload. A missing number
|
|
4
|
+
* stays null (the renderer draws an em dash). A chart series is omitted
|
|
5
|
+
* until it holds two finite values.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type StrategySheetMetric = {
|
|
9
|
+
label: string;
|
|
10
|
+
/** Preformatted figure. Null is unpublished. */
|
|
11
|
+
value: string | null;
|
|
12
|
+
tone?: "pos" | "neg";
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type StrategySheetPoint = { t: string; v: number };
|
|
16
|
+
|
|
17
|
+
export type StrategySheetBar = { t: string; o: number; h: number; l: number; c: number };
|
|
18
|
+
|
|
19
|
+
export type StrategySheetModel = {
|
|
20
|
+
title: string;
|
|
21
|
+
symbol: string;
|
|
22
|
+
metrics: StrategySheetMetric[];
|
|
23
|
+
position: StrategySheetMetric[];
|
|
24
|
+
equity: StrategySheetPoint[] | null;
|
|
25
|
+
drawdown: StrategySheetPoint[] | null;
|
|
26
|
+
price: StrategySheetBar[] | null;
|
|
27
|
+
trades: { columns: string[]; rows: string[][] } | null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const TRADE_COLUMNS = ["Direction", "Entry date", "Entry", "Exit date", "Exit", "Return"];
|
|
31
|
+
const BARS = "▁▂▃▄▅▆▇█";
|
|
32
|
+
|
|
33
|
+
function rec(value: unknown): Record<string, unknown> | null {
|
|
34
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
35
|
+
? (value as Record<string, unknown>)
|
|
36
|
+
: null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function finite(value: unknown): number | null {
|
|
40
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function text(value: unknown): string | null {
|
|
44
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function toneOf(value: number | null): "pos" | "neg" | undefined {
|
|
48
|
+
if (value === null || value === 0) return undefined;
|
|
49
|
+
return value > 0 ? "pos" : "neg";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function pct(value: number | null): string | null {
|
|
53
|
+
if (value === null) return null;
|
|
54
|
+
return `${value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}%`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function num(value: number | null, digits: number): string | null {
|
|
58
|
+
if (value === null) return null;
|
|
59
|
+
return value.toLocaleString("en-US", { minimumFractionDigits: digits, maximumFractionDigits: digits });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function metric(label: string, value: number | null, format: "pct" | "num" | "count"): StrategySheetMetric {
|
|
63
|
+
if (format === "pct") return { label, value: pct(value), tone: toneOf(value) };
|
|
64
|
+
if (format === "count") return { label, value: value === null ? null : String(Math.trunc(value)) };
|
|
65
|
+
return { label, value: num(value, 2), tone: toneOf(value) };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function yearsBetween(start: string, end: string): number | null {
|
|
69
|
+
const a = Date.parse(start);
|
|
70
|
+
const b = Date.parse(end);
|
|
71
|
+
if (!Number.isFinite(a) || !Number.isFinite(b) || b <= a) return null;
|
|
72
|
+
return (b - a) / (365.25 * 24 * 3600 * 1000);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** CAGR from real capital and dates. Missing inputs stay unpublished. */
|
|
76
|
+
export function sheetCagr(initial: number | null, final: number | null, start: string | null, end: string | null): number | null {
|
|
77
|
+
if (initial === null || final === null || initial <= 0 || final <= 0 || !start || !end) return null;
|
|
78
|
+
const years = yearsBetween(start, end);
|
|
79
|
+
if (years === null) return null;
|
|
80
|
+
return (Math.pow(final / initial, 1 / years) - 1) * 100;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Dated numeric series. Fewer than two finite points is not a chart. */
|
|
84
|
+
export function seriesOrNull(value: unknown): StrategySheetPoint[] | null {
|
|
85
|
+
if (!Array.isArray(value)) return null;
|
|
86
|
+
const points: StrategySheetPoint[] = [];
|
|
87
|
+
for (const item of value) {
|
|
88
|
+
const row = rec(item);
|
|
89
|
+
const t = text(row?.t);
|
|
90
|
+
const v = finite(row?.v);
|
|
91
|
+
if (!t || v === null) continue;
|
|
92
|
+
points.push({ t, v });
|
|
93
|
+
}
|
|
94
|
+
return points.length >= 2 ? points : null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function priceOrNull(value: unknown): StrategySheetBar[] | null {
|
|
98
|
+
if (!Array.isArray(value)) return null;
|
|
99
|
+
const bars: StrategySheetBar[] = [];
|
|
100
|
+
for (const item of value) {
|
|
101
|
+
const row = rec(item);
|
|
102
|
+
const t = text(row?.t);
|
|
103
|
+
const o = finite(row?.o);
|
|
104
|
+
const h = finite(row?.h);
|
|
105
|
+
const l = finite(row?.l);
|
|
106
|
+
const c = finite(row?.c);
|
|
107
|
+
if (!t || o === null || h === null || l === null || c === null) continue;
|
|
108
|
+
bars.push({ t, o, h, l, c });
|
|
109
|
+
}
|
|
110
|
+
return bars.length >= 2 ? bars : null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isDca(row: Record<string, unknown> | null): boolean {
|
|
114
|
+
if (!row) return false;
|
|
115
|
+
if (rec(row.dca)) return true;
|
|
116
|
+
if (row.kind === "dca") return true;
|
|
117
|
+
const strategy = text(row.strategy);
|
|
118
|
+
return strategy !== null && strategy.includes("sdca");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function dashCell(value: string | null): string {
|
|
122
|
+
return value ?? "—";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function tradeRows(value: unknown): string[][] | null {
|
|
126
|
+
if (!Array.isArray(value)) return null;
|
|
127
|
+
const rows: string[][] = [];
|
|
128
|
+
for (const item of value) {
|
|
129
|
+
const row = rec(item);
|
|
130
|
+
if (!row) continue;
|
|
131
|
+
const entryDate = text(row.entry_date);
|
|
132
|
+
const entry = finite(row.entry_price);
|
|
133
|
+
const exitDate = text(row.exit_date);
|
|
134
|
+
const exit = finite(row.exit_price);
|
|
135
|
+
const ret = finite(row.pnl_pct);
|
|
136
|
+
const direction = text(row.direction);
|
|
137
|
+
if (!entryDate && entry === null && !exitDate && exit === null) continue;
|
|
138
|
+
rows.push([
|
|
139
|
+
dashCell(direction),
|
|
140
|
+
dashCell(entryDate),
|
|
141
|
+
dashCell(num(entry, 2)),
|
|
142
|
+
dashCell(exitDate),
|
|
143
|
+
dashCell(num(exit, 2)),
|
|
144
|
+
dashCell(pct(ret)),
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
return rows.length > 0 ? rows : null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function positionOf(row: Record<string, unknown> | null): StrategySheetMetric[] {
|
|
151
|
+
const signal = rec(row?.current_signal);
|
|
152
|
+
const place = text(signal?.position);
|
|
153
|
+
const entry = text(signal?.entry_label);
|
|
154
|
+
const mark = finite(signal?.last_price);
|
|
155
|
+
const asOf = text(signal?.last_signal_date);
|
|
156
|
+
return [
|
|
157
|
+
{ label: "Position", value: place },
|
|
158
|
+
{ label: "Entry", value: entry },
|
|
159
|
+
{ label: "Mark", value: num(mark, 2) },
|
|
160
|
+
{ label: "As of", value: asOf },
|
|
161
|
+
];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function metricsOf(row: Record<string, unknown> | null): StrategySheetMetric[] {
|
|
165
|
+
const dca = rec(row?.dca);
|
|
166
|
+
if (isDca(row)) {
|
|
167
|
+
const total = finite(row?.net_profit_pct);
|
|
168
|
+
const dd = finite(row?.max_drawdown_pct);
|
|
169
|
+
const vs = finite(dca?.vs_lump_pct) ?? finite(row?.vs_lump_pct);
|
|
170
|
+
const allocated = finite(dca?.allocated_pct) ?? finite(row?.allocated_pct);
|
|
171
|
+
return [
|
|
172
|
+
metric("Total return", total, "pct"),
|
|
173
|
+
metric("Max DD", dd, "pct"),
|
|
174
|
+
metric("Vs lump", vs, "pct"),
|
|
175
|
+
metric("Allocated", allocated, "pct"),
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
const cagr = sheetCagr(
|
|
179
|
+
finite(row?.initial_capital),
|
|
180
|
+
finite(row?.final_equity),
|
|
181
|
+
text(row?.period_start),
|
|
182
|
+
text(row?.period_end),
|
|
183
|
+
);
|
|
184
|
+
return [
|
|
185
|
+
metric("CAGR", cagr, "pct"),
|
|
186
|
+
metric("Max DD", finite(row?.max_drawdown_pct), "pct"),
|
|
187
|
+
metric("Profit factor", finite(row?.profit_factor), "num"),
|
|
188
|
+
metric("Win rate", finite(row?.win_rate_pct), "pct"),
|
|
189
|
+
metric("Avg trade", finite(row?.avg_trade_pct), "pct"),
|
|
190
|
+
metric("Trades", finite(row?.total_trades), "count"),
|
|
191
|
+
];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** One strategy sheet. `data` null, or a payload that is not a tearsheet, publishes nothing. */
|
|
195
|
+
export function buildStrategyTearsheet(
|
|
196
|
+
data: unknown,
|
|
197
|
+
fallback: { title: string; symbol: string },
|
|
198
|
+
): StrategySheetModel {
|
|
199
|
+
const row = rec(data);
|
|
200
|
+
const trades = tradeRows(row?.trades);
|
|
201
|
+
return {
|
|
202
|
+
title: text(row?.label) ?? fallback.title,
|
|
203
|
+
symbol: text(row?.symbol) ?? fallback.symbol,
|
|
204
|
+
metrics: metricsOf(row),
|
|
205
|
+
position: positionOf(row),
|
|
206
|
+
equity: seriesOrNull(row?.equity_curve),
|
|
207
|
+
drawdown: seriesOrNull(row?.drawdown_curve),
|
|
208
|
+
price: priceOrNull(row?.ohlc_bars),
|
|
209
|
+
trades: trades ? { columns: TRADE_COLUMNS, rows: trades } : null,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Block-character chart. One value, or a non-finite value, is not a chart. */
|
|
214
|
+
export function seriesSparkline(values: readonly number[], width = 24): string | null {
|
|
215
|
+
const finiteValues = values.filter((value) => Number.isFinite(value));
|
|
216
|
+
if (finiteValues.length < 2) return null;
|
|
217
|
+
const sampled = finiteValues.length <= width ? finiteValues : sample(finiteValues, width);
|
|
218
|
+
const lo = Math.min(...sampled);
|
|
219
|
+
const hi = Math.max(...sampled);
|
|
220
|
+
const span = hi - lo;
|
|
221
|
+
return sampled
|
|
222
|
+
.map((n) => {
|
|
223
|
+
if (span === 0) return BARS[3] ?? BARS[0];
|
|
224
|
+
const index = Math.round(((n - lo) / span) * (BARS.length - 1));
|
|
225
|
+
return BARS[Math.min(BARS.length - 1, Math.max(0, index))] ?? BARS[0];
|
|
226
|
+
})
|
|
227
|
+
.join("");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function sample(values: number[], width: number): number[] {
|
|
231
|
+
const last = values.length - 1;
|
|
232
|
+
const out: number[] = [];
|
|
233
|
+
for (let i = 0; i < width; i += 1) {
|
|
234
|
+
const value = values[Math.round((i * last) / (width - 1))];
|
|
235
|
+
if (value !== undefined) out.push(value);
|
|
236
|
+
}
|
|
237
|
+
return out;
|
|
238
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* finance-tearsheet data shapes (#1463) — the chart-facing subset of the
|
|
3
|
+
* unified TearsheetData schema, promoted from
|
|
4
|
+
* apps/digiquant-web/components/tearsheet/{types,trades}.ts. Only what
|
|
5
|
+
* the family's render surfaces consume lives here; full tearsheet payload
|
|
6
|
+
* types and their derivation pipelines (series clipping, pivot stats, trade
|
|
7
|
+
* sorting) stay app-owned data wiring.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** One point of a dated series (equity, drawdown, cumulative P&L …). */
|
|
11
|
+
export interface TearsheetSeriesPoint {
|
|
12
|
+
t: string;
|
|
13
|
+
v: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** One daily OHLC bar for the price candlestick chart. */
|
|
17
|
+
export interface TearsheetOhlcBar {
|
|
18
|
+
t: string;
|
|
19
|
+
o: number;
|
|
20
|
+
h: number;
|
|
21
|
+
l: number;
|
|
22
|
+
c: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** One round-trip trade — drives markers, hover cards, and trade-log rows. */
|
|
26
|
+
export interface TearsheetTrade {
|
|
27
|
+
n: number;
|
|
28
|
+
direction: "long" | "short";
|
|
29
|
+
entry_label: string;
|
|
30
|
+
entry_date: string;
|
|
31
|
+
entry_price: number;
|
|
32
|
+
exit_date: string;
|
|
33
|
+
exit_price: number;
|
|
34
|
+
qty: number;
|
|
35
|
+
pnl: number;
|
|
36
|
+
pnl_pct: number;
|
|
37
|
+
equity_after: number;
|
|
38
|
+
exit_reason: string;
|
|
39
|
+
max_runup_pct: number;
|
|
40
|
+
max_drawdown_pct: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Open round-trip still marked to market (no realized P&L yet). */
|
|
44
|
+
export function isOpenTrade(t: TearsheetTrade): boolean {
|
|
45
|
+
return !t.exit_date || t.exit_reason === "open";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** One bar on the per-trade return chart. */
|
|
49
|
+
export interface TradeReturnBar {
|
|
50
|
+
/** X-axis date (exit for closed; period end for the open leg). */
|
|
51
|
+
t: string;
|
|
52
|
+
/** Realized or unrealized return (%). */
|
|
53
|
+
pct: number;
|
|
54
|
+
/** Unrealized open position — rendered highlighted at the end. */
|
|
55
|
+
open: boolean;
|
|
56
|
+
/** Source trade for hover tooltips. */
|
|
57
|
+
trade: TearsheetTrade;
|
|
58
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The footer as a hairline table, not a link cloud (D1, #4429). Every top-level
|
|
3
|
+
* destination gets one cell of equal weight, separated by the same 1px hairline
|
|
4
|
+
* the rest of the site is drawn with, and the legal strip sits underneath.
|
|
5
|
+
* This is the opencode.ai footer: one closed rectangle of centred cells, no
|
|
6
|
+
* column headings, no nested groups, no background change. The legal line
|
|
7
|
+
* (copyright, privacy, sibling site, profiles) sits outside the rectangle.
|
|
8
|
+
* The row is two cells wide until the viewport can hold five across.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ReactNode } from "react";
|
|
12
|
+
|
|
13
|
+
export interface FooterCell {
|
|
14
|
+
/** Sentence-case destination name. */
|
|
15
|
+
label: string;
|
|
16
|
+
href: string;
|
|
17
|
+
external?: boolean;
|
|
18
|
+
/** Optional muted footnote beside the label, e.g. a release tag or a count. */
|
|
19
|
+
note?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface FooterMetaLink {
|
|
23
|
+
label: string;
|
|
24
|
+
href: string;
|
|
25
|
+
external?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FooterCellsProps {
|
|
29
|
+
cells: FooterCell[];
|
|
30
|
+
/** One line of meta, e.g. "© 2026 digithings · open core". */
|
|
31
|
+
meta?: string;
|
|
32
|
+
/** Legal-strip links rendered after the meta line. */
|
|
33
|
+
metaLinks?: FooterMetaLink[];
|
|
34
|
+
/** Quiet profile row (typically <SocialRow/>) on the legal strip. */
|
|
35
|
+
profiles?: ReactNode;
|
|
36
|
+
/** How many equal cells sit on one row once the viewport is wide enough. */
|
|
37
|
+
columns?: 3 | 4 | 5;
|
|
38
|
+
/** Block between the boxes and the legal strip — the columnar sitemap. */
|
|
39
|
+
below?: ReactNode;
|
|
40
|
+
className?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const NAV_CLASS = {
|
|
44
|
+
3: "grid grid-cols-1 border-t border-l border-hair sm:grid-cols-3",
|
|
45
|
+
4: "grid grid-cols-2 border-t border-l border-hair sm:grid-cols-4",
|
|
46
|
+
5: "grid grid-cols-2 border-t border-l border-hair lg:grid-cols-5",
|
|
47
|
+
} as const;
|
|
48
|
+
|
|
49
|
+
const CELL =
|
|
50
|
+
"flex min-h-[4.5rem] items-center justify-center gap-[0.55rem] border-r border-b border-hair px-[0.75rem] py-[1.15rem] text-center font-mono text-[1rem] leading-[1.4] text-ink no-underline transition-colors duration-150 ease-brand hover:bg-surface-2 focus-visible:bg-surface-2 focus-visible:ring-[3px] focus-visible:ring-accent/30 focus-visible:outline-none lg:min-h-[5.5rem]";
|
|
51
|
+
|
|
52
|
+
export function FooterCells({
|
|
53
|
+
cells,
|
|
54
|
+
meta,
|
|
55
|
+
metaLinks,
|
|
56
|
+
profiles,
|
|
57
|
+
columns = 5,
|
|
58
|
+
below,
|
|
59
|
+
className,
|
|
60
|
+
}: FooterCellsProps) {
|
|
61
|
+
const legal = Boolean(meta || metaLinks?.length || profiles);
|
|
62
|
+
return (
|
|
63
|
+
<footer
|
|
64
|
+
className={[
|
|
65
|
+
"mx-auto w-full max-w-[calc(var(--frame-w)+2*var(--page-pad))]",
|
|
66
|
+
className,
|
|
67
|
+
]
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.join(" ")}
|
|
70
|
+
>
|
|
71
|
+
<nav aria-label="Footer" className={NAV_CLASS[columns]}>
|
|
72
|
+
{cells.map((cell) => (
|
|
73
|
+
<a
|
|
74
|
+
key={cell.href + cell.label}
|
|
75
|
+
href={cell.href}
|
|
76
|
+
target={cell.external ? "_blank" : undefined}
|
|
77
|
+
rel={cell.external ? "noopener noreferrer" : undefined}
|
|
78
|
+
className={CELL}
|
|
79
|
+
>
|
|
80
|
+
<span>{cell.label}</span>
|
|
81
|
+
{cell.note ? (
|
|
82
|
+
<span className="text-[var(--type-meta)] tracking-[0.02em] text-ink-mute">
|
|
83
|
+
[{cell.note}]
|
|
84
|
+
</span>
|
|
85
|
+
) : null}
|
|
86
|
+
</a>
|
|
87
|
+
))}
|
|
88
|
+
</nav>
|
|
89
|
+
{below}
|
|
90
|
+
{legal ? (
|
|
91
|
+
<div className="footer-legal flex flex-wrap items-center justify-center gap-x-[1.25rem] gap-y-[0.75rem] px-[var(--page-pad)] py-[1.35rem] font-mono text-[var(--type-meta)] tracking-[0.02em] text-ink-mute">
|
|
92
|
+
{meta ? <span>{meta}</span> : null}
|
|
93
|
+
{metaLinks?.map((link) => (
|
|
94
|
+
<a
|
|
95
|
+
key={link.href}
|
|
96
|
+
href={link.href}
|
|
97
|
+
target={link.external ? "_blank" : undefined}
|
|
98
|
+
rel={link.external ? "noopener noreferrer" : undefined}
|
|
99
|
+
className="underline-offset-[3px] hover:text-ink hover:underline"
|
|
100
|
+
>
|
|
101
|
+
{link.label}
|
|
102
|
+
</a>
|
|
103
|
+
))}
|
|
104
|
+
{profiles}
|
|
105
|
+
</div>
|
|
106
|
+
) : null}
|
|
107
|
+
</footer>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Scrollytelling module graph. Desktop: a pinned graph whose focus advances
|
|
4
|
+
* node-by-node with scroll, a side panel describing the focused module. Mobile
|
|
5
|
+
* or reduced-motion: a static vertical stepper of module cards. Reuses the
|
|
6
|
+
* v1 graph geometry + activation logic (active node + incident edges + neighbours).
|
|
7
|
+
*/
|
|
8
|
+
import { useEffect, useRef, useState } from "react";
|
|
9
|
+
import { useScroll, useMotionValueEvent } from "motion/react";
|
|
10
|
+
import { modules, edges, type ModuleNode } from "../data/modules";
|
|
11
|
+
import { Emblem } from "./emblems";
|
|
12
|
+
import { StackRow } from "./StackLogo";
|
|
13
|
+
|
|
14
|
+
const ordered = [...modules].sort((a, b) => a.graphOrder - b.graphOrder);
|
|
15
|
+
|
|
16
|
+
function neighboursOf(id: string): Set<string> {
|
|
17
|
+
const near = new Set<string>([id]);
|
|
18
|
+
for (const e of edges) {
|
|
19
|
+
if (e.a === id) near.add(e.b);
|
|
20
|
+
if (e.b === id) near.add(e.a);
|
|
21
|
+
}
|
|
22
|
+
return near;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function GraphSVG({ activeId, onPick }: { activeId: string | null; onPick: (id: string) => void }) {
|
|
26
|
+
const near = activeId ? neighboursOf(activeId) : null;
|
|
27
|
+
return (
|
|
28
|
+
<svg className="dg-graph" viewBox="0 0 920 560" role="img" aria-label="digithings module graph" preserveAspectRatio="xMidYMid meet">
|
|
29
|
+
<g>
|
|
30
|
+
{edges.map((e, i) => {
|
|
31
|
+
const a = modules.find((m) => m.id === e.a)!.graph;
|
|
32
|
+
const b = modules.find((m) => m.id === e.b)!.graph;
|
|
33
|
+
const active = activeId != null && (e.a === activeId || e.b === activeId);
|
|
34
|
+
return (
|
|
35
|
+
<line key={i} className={`dg-edge${active ? " on" : ""}${near ? " has-active" : ""}`}
|
|
36
|
+
x1={a.x} y1={a.y} x2={b.x} y2={b.y} />
|
|
37
|
+
);
|
|
38
|
+
})}
|
|
39
|
+
</g>
|
|
40
|
+
<g>
|
|
41
|
+
{modules.map((m) => {
|
|
42
|
+
const dim = near != null && !near.has(m.id);
|
|
43
|
+
const on = m.id === activeId;
|
|
44
|
+
return (
|
|
45
|
+
<g key={m.id} className={`dg-node${m.graph.hub ? " hub" : ""}${on ? " on" : ""}${dim ? " dim" : ""}`}
|
|
46
|
+
transform={`translate(${m.graph.x} ${m.graph.y})`} tabIndex={0} role="button"
|
|
47
|
+
aria-label={m.name} onClick={() => onPick(m.id)}
|
|
48
|
+
onKeyDown={(ev) => { if (ev.key === "Enter" || ev.key === " ") { ev.preventDefault(); onPick(m.id); } }}>
|
|
49
|
+
<circle className="halo" r={m.graph.r + 12} />
|
|
50
|
+
<circle className="node" r={m.graph.r} />
|
|
51
|
+
<text className="label" y={m.graph.r + 18}>{m.name}</text>
|
|
52
|
+
</g>
|
|
53
|
+
);
|
|
54
|
+
})}
|
|
55
|
+
</g>
|
|
56
|
+
</svg>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function ModulePanel({
|
|
61
|
+
m,
|
|
62
|
+
hrefForModule,
|
|
63
|
+
}: {
|
|
64
|
+
m: ModuleNode;
|
|
65
|
+
hrefForModule: (id: string) => string;
|
|
66
|
+
}) {
|
|
67
|
+
return (
|
|
68
|
+
<div className="dg-panel" key={m.id}>
|
|
69
|
+
<div className="dg-panel-head">
|
|
70
|
+
<Emblem id={m.emblem} size={40} />
|
|
71
|
+
<div>
|
|
72
|
+
<span className={`dg-tier t-${m.tier}`}>{m.tier}</span>
|
|
73
|
+
<h3>{m.name}</h3>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
<p className="dg-tag">{m.tagline}</p>
|
|
77
|
+
<StackRow items={m.stack} />
|
|
78
|
+
{m.dockerCmd && (
|
|
79
|
+
<div className="dg-docker"><span className="prompt">$</span> {m.dockerCmd}</div>
|
|
80
|
+
)}
|
|
81
|
+
<a className="dg-more" href={hrefForModule(m.id)}>man {m.id} →</a>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function ScrollyGraph({
|
|
87
|
+
hrefForModule = (id: string) => `/modules/${id}`,
|
|
88
|
+
}: {
|
|
89
|
+
/** Host-owned module URL. Defaults to the `/modules/[id]` host contract. */
|
|
90
|
+
hrefForModule?: (id: string) => string;
|
|
91
|
+
}) {
|
|
92
|
+
const trackRef = useRef<HTMLDivElement>(null);
|
|
93
|
+
const [idx, setIdx] = useState(0);
|
|
94
|
+
const [hover, setHover] = useState<string | null>(null);
|
|
95
|
+
const [stepper, setStepper] = useState(false);
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const mq = matchMedia("(max-width: 860px), (prefers-reduced-motion: reduce)");
|
|
99
|
+
const apply = () => setStepper(mq.matches);
|
|
100
|
+
apply();
|
|
101
|
+
mq.addEventListener("change", apply);
|
|
102
|
+
return () => mq.removeEventListener("change", apply);
|
|
103
|
+
}, []);
|
|
104
|
+
|
|
105
|
+
const { scrollYProgress } = useScroll({ target: trackRef, offset: ["start start", "end end"] });
|
|
106
|
+
useMotionValueEvent(scrollYProgress, "change", (p) => {
|
|
107
|
+
const n = ordered.length;
|
|
108
|
+
setIdx(Math.max(0, Math.min(n - 1, Math.floor(p * n))));
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (stepper) {
|
|
112
|
+
return (
|
|
113
|
+
<div className="dg-stepper">
|
|
114
|
+
{ordered.map((m) => (
|
|
115
|
+
<article className="dg-step" key={m.id}>
|
|
116
|
+
<GraphMini activeId={m.id} />
|
|
117
|
+
<ModulePanel m={m} hrefForModule={hrefForModule} />
|
|
118
|
+
</article>
|
|
119
|
+
))}
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const activeId = hover ?? ordered[idx].id;
|
|
125
|
+
const activeMod = modules.find((m) => m.id === activeId)!;
|
|
126
|
+
return (
|
|
127
|
+
<div ref={trackRef} className="dg-track" style={{ height: `${ordered.length * 90}vh` }}>
|
|
128
|
+
<div className="dg-stage">
|
|
129
|
+
<div className="dg-stage-grid">
|
|
130
|
+
<div className="dg-graph-wrap"
|
|
131
|
+
onMouseLeave={() => setHover(null)}
|
|
132
|
+
onMouseOver={(e) => {
|
|
133
|
+
const g = (e.target as Element).closest?.(".dg-node") as SVGGElement | null;
|
|
134
|
+
const lbl = g?.getAttribute("aria-label");
|
|
135
|
+
if (lbl) setHover(modules.find((m) => m.name === lbl)?.id ?? null);
|
|
136
|
+
}}>
|
|
137
|
+
<GraphSVG activeId={activeId} onPick={(id) => { location.href = hrefForModule(id); }} />
|
|
138
|
+
</div>
|
|
139
|
+
<ModulePanel m={activeMod} hrefForModule={hrefForModule} />
|
|
140
|
+
</div>
|
|
141
|
+
<div className="dg-rail" aria-hidden="true">
|
|
142
|
+
{ordered.map((m, i) => (
|
|
143
|
+
<span key={m.id} className={`dg-tick${i === idx ? " on" : ""}`} />
|
|
144
|
+
))}
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Small static graph used in the mobile stepper (one node highlighted). */
|
|
152
|
+
function GraphMini({ activeId }: { activeId: string }) {
|
|
153
|
+
return (
|
|
154
|
+
<div className="dg-graph-mini">
|
|
155
|
+
<GraphSVG activeId={activeId} onPick={() => {}} />
|
|
156
|
+
</div>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand/interface icon primitives shared across the marketing sites and the
|
|
3
|
+
* reference. Each is a pure className-passthrough SVG: it applies NO Tailwind
|
|
4
|
+
* utilities of its own, so it needs no `@source` line — the call-site
|
|
5
|
+
* className is scanned by the consuming app. Colours come from `currentColor`,
|
|
6
|
+
* so an icon inherits ink in chrome and accent inside a livery scope.
|
|
7
|
+
*/
|
|
8
|
+
import { type SVGProps } from "react";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* GitHubGlyph — the simplified GitHub octocat mark. Inlined byte-identically
|
|
12
|
+
* (viewBox 0 0 24 24, the same `d`) in digithings.ai's DtNav, digiquant.io's
|
|
13
|
+
* SiteNav and CloneRepoButton, and the nav-shell reference specimen until
|
|
14
|
+
* #1436 promoted it here. Defaults to 18×18 in `currentColor`; pass a
|
|
15
|
+
* `className` (or any svg attr — width/height override the defaults) at the
|
|
16
|
+
* call site. Distinct from the symbols catalog's `Glyph name="github"`, which
|
|
17
|
+
* draws the fuller official Simple Icons path.
|
|
18
|
+
*/
|
|
19
|
+
export function GitHubGlyph({ className, ...props }: SVGProps<SVGSVGElement>) {
|
|
20
|
+
return (
|
|
21
|
+
<svg
|
|
22
|
+
viewBox="0 0 24 24"
|
|
23
|
+
width="18"
|
|
24
|
+
height="18"
|
|
25
|
+
fill="currentColor"
|
|
26
|
+
aria-hidden="true"
|
|
27
|
+
className={className}
|
|
28
|
+
{...props}
|
|
29
|
+
>
|
|
30
|
+
<path d="M12 .5C5.7.5.5 5.7.5 12c0 5.1 3.3 9.4 7.9 10.9.6.1.8-.2.8-.6v-2c-3.2.7-3.9-1.5-3.9-1.5-.5-1.3-1.3-1.7-1.3-1.7-1.1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.7 1.3 3.4 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.8 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.3 1.2a11.5 11.5 0 0 1 6 0C17 4.7 18 5 18 5c.6 1.6.2 2.8.1 3.1.8.8 1.2 1.8 1.2 3.1 0 4.5-2.7 5.5-5.3 5.8.4.4.8 1.1.8 2.2v3.3c0 .4.2.7.8.6 4.6-1.5 7.9-5.8 7.9-10.9C23.5 5.7 18.3.5 12 .5z" />
|
|
31
|
+
</svg>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Google “G” — four brand fills so the mark reads at 18px. Decorative. */
|
|
36
|
+
export function GoogleGlyph({ className, ...props }: SVGProps<SVGSVGElement>) {
|
|
37
|
+
return (
|
|
38
|
+
<svg
|
|
39
|
+
viewBox="0 0 24 24"
|
|
40
|
+
width="18"
|
|
41
|
+
height="18"
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
className={className}
|
|
44
|
+
{...props}
|
|
45
|
+
>
|
|
46
|
+
<path
|
|
47
|
+
fill="#4285F4" // canon-allow: Google brand blue (official G mark)
|
|
48
|
+
d="M23.5 12.3c0-.8-.1-1.6-.2-2.3H12v4.4h6.5c-.3 1.5-1.1 2.7-2.4 3.5v2.9h3.8c2.3-2.1 3.6-5.2 3.6-8.5z"
|
|
49
|
+
/>
|
|
50
|
+
<path
|
|
51
|
+
fill="#34A853" // canon-allow: Google brand green (official G mark)
|
|
52
|
+
d="M12 24c3.2 0 5.9-1.1 7.9-2.9l-3.8-2.9c-1.1.7-2.5 1.2-4.1 1.2-3.1 0-5.8-2.1-6.7-5H1.3v3c2 4 6.1 6.6 10.7 6.6z"
|
|
53
|
+
/>
|
|
54
|
+
<path
|
|
55
|
+
fill="#FBBC05" // canon-allow: Google brand yellow (official G mark)
|
|
56
|
+
d="M5.3 14.4c-.2-.7-.4-1.5-.4-2.4s.1-1.7.4-2.4V6.6H1.3C.5 8.2 0 10 0 12s.5 3.8 1.3 5.4l4-3z"
|
|
57
|
+
/>
|
|
58
|
+
<path
|
|
59
|
+
fill="#EA4335" // canon-allow: Google brand red (official G mark)
|
|
60
|
+
d="M12 4.8c1.8 0 3.3.6 4.6 1.8l3.4-3.4C17.9 1.2 15.2 0 12 0 7.4 0 3.3 2.6 1.3 6.6l4 3c.9-2.9 3.6-4.8 6.7-4.8z"
|
|
61
|
+
/>
|
|
62
|
+
</svg>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** LinkedIn “in” mark. currentColor so it tracks ink. Decorative. */
|
|
67
|
+
export function LinkedInGlyph({ className, ...props }: SVGProps<SVGSVGElement>) {
|
|
68
|
+
return (
|
|
69
|
+
<svg
|
|
70
|
+
viewBox="0 0 24 24"
|
|
71
|
+
width="18"
|
|
72
|
+
height="18"
|
|
73
|
+
fill="currentColor"
|
|
74
|
+
aria-hidden="true"
|
|
75
|
+
className={className}
|
|
76
|
+
{...props}
|
|
77
|
+
>
|
|
78
|
+
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
|
|
79
|
+
</svg>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** X mark. currentColor so it tracks ink. Decorative. */
|
|
84
|
+
export function XGlyph({ className, ...props }: SVGProps<SVGSVGElement>) {
|
|
85
|
+
return (
|
|
86
|
+
<svg
|
|
87
|
+
viewBox="0 0 24 24"
|
|
88
|
+
width="16"
|
|
89
|
+
height="16"
|
|
90
|
+
fill="currentColor"
|
|
91
|
+
aria-hidden="true"
|
|
92
|
+
className={className}
|
|
93
|
+
{...props}
|
|
94
|
+
>
|
|
95
|
+
<path d="M18.2 2.3h3.3l-7.2 8.3 8.5 11.2h-6.6l-4.7-6.2-5.4 6.2H2.7l7.8-8.8L1.3 2.3h6.8l4.3 5.6 5.8-5.6zm-1.2 17.5h1.8L7.1 4.1H5.1l11.9 15.7z" />
|
|
96
|
+
</svg>
|
|
97
|
+
);
|
|
98
|
+
}
|