@ciderpress/ui 1.0.0-rc.1
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/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/0~142.js +2 -0
- package/dist/0~164.js +2 -0
- package/dist/0~2.js +2 -0
- package/dist/0~225.js +2 -0
- package/dist/0~288.js +2 -0
- package/dist/0~299.js +2 -0
- package/dist/0~3.js +2 -0
- package/dist/0~301.js +2 -0
- package/dist/0~311.js +2 -0
- package/dist/0~326.js +2 -0
- package/dist/0~40.js +2 -0
- package/dist/0~43.js +2 -0
- package/dist/0~433.js +2 -0
- package/dist/0~453.js +2 -0
- package/dist/0~457.js +2 -0
- package/dist/0~497.js +2 -0
- package/dist/0~511.js +2 -0
- package/dist/0~603.js +2 -0
- package/dist/0~616.js +2 -0
- package/dist/0~659.js +2 -0
- package/dist/0~670.js +2 -0
- package/dist/0~687.js +2 -0
- package/dist/0~706.js +2 -0
- package/dist/0~729.js +2 -0
- package/dist/0~768.js +2 -0
- package/dist/0~819.js +2 -0
- package/dist/0~830.js +2 -0
- package/dist/0~854.js +2 -0
- package/dist/0~863.js +2 -0
- package/dist/0~879.js +2 -0
- package/dist/0~96.js +2 -0
- package/dist/0~97.js +2 -0
- package/dist/components/FileTree/FileTree.css +90 -0
- package/dist/components/FileTree/FileTree.d.ts +7 -0
- package/dist/components/FileTree/FileTree.js +531 -0
- package/dist/components/FileTree/FileTreeItem.d.ts +8 -0
- package/dist/components/FileTree/RemoteSvgIcon.d.ts +11 -0
- package/dist/components/FileTree/index.d.ts +4 -0
- package/dist/components/folder-icons.d.ts +32 -0
- package/dist/components/languages.d.ts +7 -0
- package/dist/components/tree-parser/tree-parser.d.ts +12 -0
- package/dist/components/tree-parser/types.d.ts +11 -0
- package/dist/head/css/loader-apple.css +1 -0
- package/dist/head/css/loader-backdrop.css +1 -0
- package/dist/head/css/loader-dots.css +1 -0
- package/dist/head/css/themes/amber.css +1 -0
- package/dist/head/css/themes/arcade.css +1 -0
- package/dist/head/css/themes/grannysmith.css +1 -0
- package/dist/head/css/themes/honeycrisp.css +1 -0
- package/dist/head/css/themes/midnight.css +1 -0
- package/dist/head/css/themes/mulled.css +1 -0
- package/dist/head/js/loader-dots.js +1 -0
- package/dist/head/js/vscode-nav.js +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.mjs +40 -0
- package/dist/node.d.ts +81 -0
- package/dist/node.mjs +17762 -0
- package/dist/plugins/mermaid/MermaidRenderer.tsx +480 -0
- package/dist/plugins/mermaid/mermaid.css +199 -0
- package/dist/theme/components/announcement/announcement-bar.css +103 -0
- package/dist/theme/components/announcement/announcement-bar.tsx +173 -0
- package/dist/theme/components/ask-ai/ask-ai-button.css +52 -0
- package/dist/theme/components/ask-ai/ask-ai-button.tsx +56 -0
- package/dist/theme/components/content-footer/content-footer-portal.tsx +70 -0
- package/dist/theme/components/content-footer/feedback.css +47 -0
- package/dist/theme/components/content-footer/feedback.tsx +96 -0
- package/dist/theme/components/content-footer/meta-actions.css +25 -0
- package/dist/theme/components/content-footer/meta-actions.tsx +57 -0
- package/dist/theme/components/content-footer/page-pager.css +61 -0
- package/dist/theme/components/content-footer/page-pager.tsx +110 -0
- package/dist/theme/components/edit-source-button.tsx +97 -0
- package/dist/theme/components/footer/site-footer.css +179 -0
- package/dist/theme/components/footer/site-footer.tsx +115 -0
- package/dist/theme/components/home/cta.css +67 -0
- package/dist/theme/components/home/cta.tsx +77 -0
- package/dist/theme/components/home/feature-card.css +52 -0
- package/dist/theme/components/home/feature-card.tsx +131 -0
- package/dist/theme/components/home/feature.tsx +79 -0
- package/dist/theme/components/home/hero-demo.css +66 -0
- package/dist/theme/components/home/hero-demo.tsx +46 -0
- package/dist/theme/components/home/hero.css +96 -0
- package/dist/theme/components/home/hero.tsx +111 -0
- package/dist/theme/components/home/layout.tsx +183 -0
- package/dist/theme/components/home/page-rail.css +21 -0
- package/dist/theme/components/home/page-rail.tsx +19 -0
- package/dist/theme/components/home/split.css +148 -0
- package/dist/theme/components/home/split.tsx +120 -0
- package/dist/theme/components/home/trust-strip.css +33 -0
- package/dist/theme/components/home/trust-strip.tsx +43 -0
- package/dist/theme/components/home/workspaces.tsx +71 -0
- package/dist/theme/components/nav/branch-tag.css +54 -0
- package/dist/theme/components/nav/branch-tag.tsx +101 -0
- package/dist/theme/components/nav/ciderpress-doc-layout.tsx +247 -0
- package/dist/theme/components/nav/ciderpress-docs-bar.css +505 -0
- package/dist/theme/components/nav/ciderpress-docs-bar.tsx +426 -0
- package/dist/theme/components/nav/ciderpress-header.css +172 -0
- package/dist/theme/components/nav/ciderpress-header.tsx +113 -0
- package/dist/theme/components/nav/ciderpress-layout-context.tsx +49 -0
- package/dist/theme/components/nav/ciderpress-nav-hamburger.css +193 -0
- package/dist/theme/components/nav/ciderpress-nav-hamburger.tsx +112 -0
- package/dist/theme/components/nav/ciderpress-nav-menu.css +130 -0
- package/dist/theme/components/nav/ciderpress-nav-menu.tsx +372 -0
- package/dist/theme/components/nav/ciderpress-nav-search.css +73 -0
- package/dist/theme/components/nav/ciderpress-nav-search.tsx +55 -0
- package/dist/theme/components/nav/ciderpress-nav-social-links.css +35 -0
- package/dist/theme/components/nav/ciderpress-nav-social-links.tsx +87 -0
- package/dist/theme/components/nav/floating-branch-indicator.css +106 -0
- package/dist/theme/components/nav/floating-branch-indicator.tsx +109 -0
- package/dist/theme/components/nav/layout.tsx +268 -0
- package/dist/theme/components/nav/mobile-nav-cta.tsx +79 -0
- package/dist/theme/components/nav/nav-divider.css +15 -0
- package/dist/theme/components/nav/nav-divider.tsx +19 -0
- package/dist/theme/components/nav/nav-logo.css +42 -0
- package/dist/theme/components/nav/nav-logo.tsx +262 -0
- package/dist/theme/components/nav/theme-switcher.css +92 -0
- package/dist/theme/components/nav/theme-switcher.tsx +298 -0
- package/dist/theme/components/nav/topbar-cta.css +45 -0
- package/dist/theme/components/nav/topbar-cta.tsx +39 -0
- package/dist/theme/components/nav/variant-toggle.css +70 -0
- package/dist/theme/components/nav/variant-toggle.tsx +83 -0
- package/dist/theme/components/nav/version-chip.css +31 -0
- package/dist/theme/components/nav/version-chip.tsx +45 -0
- package/dist/theme/components/nav/vscode-tag.css +32 -0
- package/dist/theme/components/nav/vscode-tag.tsx +20 -0
- package/dist/theme/components/openapi/code-sample.tsx +341 -0
- package/dist/theme/components/openapi/copy-markdown-button.tsx +41 -0
- package/dist/theme/components/openapi/icons.tsx +45 -0
- package/dist/theme/components/openapi/index.ts +6 -0
- package/dist/theme/components/openapi/method-badge.tsx +24 -0
- package/dist/theme/components/openapi/openapi.css +665 -0
- package/dist/theme/components/openapi/operation.tsx +200 -0
- package/dist/theme/components/openapi/overview.tsx +207 -0
- package/dist/theme/components/openapi/parameters-table.tsx +149 -0
- package/dist/theme/components/openapi/request-body.tsx +90 -0
- package/dist/theme/components/openapi/response-list.tsx +112 -0
- package/dist/theme/components/openapi/schema-viewer.tsx +277 -0
- package/dist/theme/components/openapi/security-badges.tsx +63 -0
- package/dist/theme/components/openapi/spec-utils.ts +122 -0
- package/dist/theme/components/shared/accordion.css +118 -0
- package/dist/theme/components/shared/accordion.stories.tsx +96 -0
- package/dist/theme/components/shared/accordion.tsx +191 -0
- package/dist/theme/components/shared/brand-logo.stories.tsx +49 -0
- package/dist/theme/components/shared/brand-mark.stories.tsx +53 -0
- package/dist/theme/components/shared/browser-window.css +1 -0
- package/dist/theme/components/shared/browser-window.stories.tsx +100 -0
- package/dist/theme/components/shared/browser-window.tsx +2 -0
- package/dist/theme/components/shared/card.stories.tsx +50 -0
- package/dist/theme/components/shared/card.tsx +35 -0
- package/dist/theme/components/shared/ciderpress-logo.tsx +93 -0
- package/dist/theme/components/shared/ciderpress-mark.tsx +77 -0
- package/dist/theme/components/shared/color.css +56 -0
- package/dist/theme/components/shared/color.stories.tsx +47 -0
- package/dist/theme/components/shared/color.tsx +69 -0
- package/dist/theme/components/shared/columns.css +37 -0
- package/dist/theme/components/shared/columns.stories.tsx +91 -0
- package/dist/theme/components/shared/columns.tsx +50 -0
- package/dist/theme/components/shared/desktop-window.css +339 -0
- package/dist/theme/components/shared/desktop-window.tsx +535 -0
- package/dist/theme/components/shared/field.css +158 -0
- package/dist/theme/components/shared/field.stories.tsx +100 -0
- package/dist/theme/components/shared/field.tsx +186 -0
- package/dist/theme/components/shared/frame.css +39 -0
- package/dist/theme/components/shared/frame.stories.tsx +49 -0
- package/dist/theme/components/shared/frame.tsx +76 -0
- package/dist/theme/components/shared/icon.stories.tsx +90 -0
- package/dist/theme/components/shared/icon.tsx +41 -0
- package/dist/theme/components/shared/prompt.css +178 -0
- package/dist/theme/components/shared/prompt.stories.tsx +57 -0
- package/dist/theme/components/shared/prompt.tsx +369 -0
- package/dist/theme/components/shared/resolve-card-icon.ts +28 -0
- package/dist/theme/components/shared/section-card.stories.tsx +51 -0
- package/dist/theme/components/shared/section-card.tsx +44 -0
- package/dist/theme/components/shared/section-grid.stories.tsx +46 -0
- package/dist/theme/components/shared/section-grid.tsx +15 -0
- package/dist/theme/components/shared/status-badge.css +37 -0
- package/dist/theme/components/shared/status-badge.stories.tsx +61 -0
- package/dist/theme/components/shared/status-badge.tsx +74 -0
- package/dist/theme/components/shared/steps.css +88 -0
- package/dist/theme/components/shared/steps.stories.tsx +68 -0
- package/dist/theme/components/shared/steps.tsx +100 -0
- package/dist/theme/components/shared/tech-icon-table.stories.tsx +29 -0
- package/dist/theme/components/shared/tech-icon-table.tsx +71 -0
- package/dist/theme/components/shared/tech-tag.stories.tsx +40 -0
- package/dist/theme/components/shared/tech-tag.tsx +31 -0
- package/dist/theme/components/shared/theme-color-palette.css +80 -0
- package/dist/theme/components/shared/theme-color-palette.stories.tsx +52 -0
- package/dist/theme/components/shared/theme-color-palette.tsx +275 -0
- package/dist/theme/components/shared/tooltip.css +69 -0
- package/dist/theme/components/shared/tooltip.stories.tsx +72 -0
- package/dist/theme/components/shared/tooltip.tsx +77 -0
- package/dist/theme/components/sidebar/framework-picker.css +77 -0
- package/dist/theme/components/sidebar/framework-picker.tsx +78 -0
- package/dist/theme/components/sidebar/sidebar-filter.test.ts +140 -0
- package/dist/theme/components/sidebar/sidebar-filter.ts +79 -0
- package/dist/theme/components/sidebar/sidebar-links.css +102 -0
- package/dist/theme/components/sidebar/sidebar-links.tsx +122 -0
- package/dist/theme/components/sidebar/sidebar-promo.css +42 -0
- package/dist/theme/components/sidebar/sidebar-promo.tsx +52 -0
- package/dist/theme/components/sidebar/sidebar-scope.tsx +151 -0
- package/dist/theme/components/sidebar/sidebar-toggle.css +181 -0
- package/dist/theme/components/sidebar/sidebar-toggle.tsx +127 -0
- package/dist/theme/components/theme-provider.tsx +637 -0
- package/dist/theme/components/workspaces/card.css +151 -0
- package/dist/theme/components/workspaces/card.tsx +166 -0
- package/dist/theme/components/workspaces/grid.tsx +62 -0
- package/dist/theme/css.d.ts +1 -0
- package/dist/theme/env.d.ts +12 -0
- package/dist/theme/fonts/GeistMono-Variable.woff2 +0 -0
- package/dist/theme/fonts/GeistPixel-Square.woff2 +0 -0
- package/dist/theme/fonts/GeistSans-Variable.woff2 +0 -0
- package/dist/theme/hooks/use-ciderpress.ts +61 -0
- package/dist/theme/hooks/use-nav-items.ts +76 -0
- package/dist/theme/icons/index.ts +2 -0
- package/dist/theme/icons/tech-map.ts +228 -0
- package/dist/theme/index.tsx +180 -0
- package/dist/theme/lib/read-social-links.ts +27 -0
- package/dist/theme/lib/route-link.tsx +126 -0
- package/dist/theme/lib/safe-url.test.ts +0 -0
- package/dist/theme/lib/safe-url.ts +68 -0
- package/dist/theme/lib/theme-favicon.ts +125 -0
- package/dist/theme/lib/user-config-stub.ts +12 -0
- package/dist/theme/styles/layers.css +32 -0
- package/dist/theme/styles/overrides/badge.css +26 -0
- package/dist/theme/styles/overrides/content-footer.css +80 -0
- package/dist/theme/styles/overrides/details.css +61 -0
- package/dist/theme/styles/overrides/fonts.css +32 -0
- package/dist/theme/styles/overrides/home-card.css +133 -0
- package/dist/theme/styles/overrides/home.css +243 -0
- package/dist/theme/styles/overrides/rail.css +337 -0
- package/dist/theme/styles/overrides/rspress.css +62 -0
- package/dist/theme/styles/overrides/scrollbar.css +25 -0
- package/dist/theme/styles/overrides/section-card.css +114 -0
- package/dist/theme/styles/overrides/sidebar.css +144 -0
- package/dist/theme/styles/overrides/tokens.css +288 -0
- package/dist/theme/styles/overrides/vscode.css +88 -0
- package/dist/theme/styles/themes/amber.css +545 -0
- package/dist/theme/styles/themes/arcade-fx.css +366 -0
- package/dist/theme/styles/themes/arcade.css +273 -0
- package/dist/theme/styles/themes/grannysmith.css +545 -0
- package/dist/theme/styles/themes/honeycrisp.css +545 -0
- package/dist/theme/styles/themes/midnight.css +273 -0
- package/dist/theme/styles/themes/mulled.css +817 -0
- package/package.json +103 -0
- package/src/theme/components/announcement/announcement-bar.css +103 -0
- package/src/theme/components/announcement/announcement-bar.tsx +173 -0
- package/src/theme/components/ask-ai/ask-ai-button.css +52 -0
- package/src/theme/components/ask-ai/ask-ai-button.tsx +56 -0
- package/src/theme/components/content-footer/content-footer-portal.tsx +70 -0
- package/src/theme/components/content-footer/feedback.css +47 -0
- package/src/theme/components/content-footer/feedback.tsx +96 -0
- package/src/theme/components/content-footer/meta-actions.css +25 -0
- package/src/theme/components/content-footer/meta-actions.tsx +57 -0
- package/src/theme/components/content-footer/page-pager.css +61 -0
- package/src/theme/components/content-footer/page-pager.tsx +110 -0
- package/src/theme/components/edit-source-button.tsx +97 -0
- package/src/theme/components/footer/site-footer.css +179 -0
- package/src/theme/components/footer/site-footer.tsx +115 -0
- package/src/theme/components/home/cta.css +67 -0
- package/src/theme/components/home/cta.tsx +77 -0
- package/src/theme/components/home/feature-card.css +52 -0
- package/src/theme/components/home/feature-card.tsx +131 -0
- package/src/theme/components/home/feature.tsx +79 -0
- package/src/theme/components/home/hero-demo.css +66 -0
- package/src/theme/components/home/hero-demo.tsx +46 -0
- package/src/theme/components/home/hero.css +96 -0
- package/src/theme/components/home/hero.tsx +111 -0
- package/src/theme/components/home/layout.tsx +183 -0
- package/src/theme/components/home/page-rail.css +21 -0
- package/src/theme/components/home/page-rail.tsx +19 -0
- package/src/theme/components/home/split.css +148 -0
- package/src/theme/components/home/split.tsx +120 -0
- package/src/theme/components/home/trust-strip.css +33 -0
- package/src/theme/components/home/trust-strip.tsx +43 -0
- package/src/theme/components/home/workspaces.tsx +71 -0
- package/src/theme/components/nav/branch-tag.css +54 -0
- package/src/theme/components/nav/branch-tag.tsx +101 -0
- package/src/theme/components/nav/ciderpress-doc-layout.tsx +247 -0
- package/src/theme/components/nav/ciderpress-docs-bar.css +505 -0
- package/src/theme/components/nav/ciderpress-docs-bar.tsx +426 -0
- package/src/theme/components/nav/ciderpress-header.css +172 -0
- package/src/theme/components/nav/ciderpress-header.tsx +113 -0
- package/src/theme/components/nav/ciderpress-layout-context.tsx +49 -0
- package/src/theme/components/nav/ciderpress-nav-hamburger.css +193 -0
- package/src/theme/components/nav/ciderpress-nav-hamburger.tsx +112 -0
- package/src/theme/components/nav/ciderpress-nav-menu.css +130 -0
- package/src/theme/components/nav/ciderpress-nav-menu.tsx +372 -0
- package/src/theme/components/nav/ciderpress-nav-search.css +73 -0
- package/src/theme/components/nav/ciderpress-nav-search.tsx +55 -0
- package/src/theme/components/nav/ciderpress-nav-social-links.css +35 -0
- package/src/theme/components/nav/ciderpress-nav-social-links.tsx +87 -0
- package/src/theme/components/nav/floating-branch-indicator.css +106 -0
- package/src/theme/components/nav/floating-branch-indicator.tsx +109 -0
- package/src/theme/components/nav/layout.tsx +268 -0
- package/src/theme/components/nav/mobile-nav-cta.tsx +79 -0
- package/src/theme/components/nav/nav-divider.css +15 -0
- package/src/theme/components/nav/nav-divider.tsx +19 -0
- package/src/theme/components/nav/nav-logo.css +42 -0
- package/src/theme/components/nav/nav-logo.tsx +262 -0
- package/src/theme/components/nav/theme-switcher.css +92 -0
- package/src/theme/components/nav/theme-switcher.tsx +298 -0
- package/src/theme/components/nav/topbar-cta.css +45 -0
- package/src/theme/components/nav/topbar-cta.tsx +39 -0
- package/src/theme/components/nav/variant-toggle.css +70 -0
- package/src/theme/components/nav/variant-toggle.tsx +83 -0
- package/src/theme/components/nav/version-chip.css +31 -0
- package/src/theme/components/nav/version-chip.tsx +45 -0
- package/src/theme/components/nav/vscode-tag.css +32 -0
- package/src/theme/components/nav/vscode-tag.tsx +20 -0
- package/src/theme/components/openapi/code-sample.tsx +341 -0
- package/src/theme/components/openapi/copy-markdown-button.tsx +41 -0
- package/src/theme/components/openapi/icons.tsx +45 -0
- package/src/theme/components/openapi/index.ts +6 -0
- package/src/theme/components/openapi/method-badge.tsx +24 -0
- package/src/theme/components/openapi/openapi.css +665 -0
- package/src/theme/components/openapi/operation.tsx +200 -0
- package/src/theme/components/openapi/overview.tsx +207 -0
- package/src/theme/components/openapi/parameters-table.tsx +149 -0
- package/src/theme/components/openapi/request-body.tsx +90 -0
- package/src/theme/components/openapi/response-list.tsx +112 -0
- package/src/theme/components/openapi/schema-viewer.tsx +277 -0
- package/src/theme/components/openapi/security-badges.tsx +63 -0
- package/src/theme/components/openapi/spec-utils.ts +122 -0
- package/src/theme/components/shared/accordion.css +118 -0
- package/src/theme/components/shared/accordion.stories.tsx +96 -0
- package/src/theme/components/shared/accordion.tsx +191 -0
- package/src/theme/components/shared/brand-logo.stories.tsx +49 -0
- package/src/theme/components/shared/brand-mark.stories.tsx +53 -0
- package/src/theme/components/shared/browser-window.css +1 -0
- package/src/theme/components/shared/browser-window.stories.tsx +100 -0
- package/src/theme/components/shared/browser-window.tsx +2 -0
- package/src/theme/components/shared/card.stories.tsx +50 -0
- package/src/theme/components/shared/card.tsx +35 -0
- package/src/theme/components/shared/ciderpress-logo.tsx +93 -0
- package/src/theme/components/shared/ciderpress-mark.tsx +77 -0
- package/src/theme/components/shared/color.css +56 -0
- package/src/theme/components/shared/color.stories.tsx +47 -0
- package/src/theme/components/shared/color.tsx +69 -0
- package/src/theme/components/shared/columns.css +37 -0
- package/src/theme/components/shared/columns.stories.tsx +91 -0
- package/src/theme/components/shared/columns.tsx +50 -0
- package/src/theme/components/shared/desktop-window.css +339 -0
- package/src/theme/components/shared/desktop-window.tsx +535 -0
- package/src/theme/components/shared/field.css +158 -0
- package/src/theme/components/shared/field.stories.tsx +100 -0
- package/src/theme/components/shared/field.tsx +186 -0
- package/src/theme/components/shared/frame.css +39 -0
- package/src/theme/components/shared/frame.stories.tsx +49 -0
- package/src/theme/components/shared/frame.tsx +76 -0
- package/src/theme/components/shared/icon.stories.tsx +90 -0
- package/src/theme/components/shared/icon.tsx +41 -0
- package/src/theme/components/shared/prompt.css +178 -0
- package/src/theme/components/shared/prompt.stories.tsx +57 -0
- package/src/theme/components/shared/prompt.tsx +369 -0
- package/src/theme/components/shared/resolve-card-icon.ts +28 -0
- package/src/theme/components/shared/section-card.stories.tsx +51 -0
- package/src/theme/components/shared/section-card.tsx +44 -0
- package/src/theme/components/shared/section-grid.stories.tsx +46 -0
- package/src/theme/components/shared/section-grid.tsx +15 -0
- package/src/theme/components/shared/status-badge.css +37 -0
- package/src/theme/components/shared/status-badge.stories.tsx +61 -0
- package/src/theme/components/shared/status-badge.tsx +74 -0
- package/src/theme/components/shared/steps.css +88 -0
- package/src/theme/components/shared/steps.stories.tsx +68 -0
- package/src/theme/components/shared/steps.tsx +100 -0
- package/src/theme/components/shared/tech-icon-table.stories.tsx +29 -0
- package/src/theme/components/shared/tech-icon-table.tsx +71 -0
- package/src/theme/components/shared/tech-tag.stories.tsx +40 -0
- package/src/theme/components/shared/tech-tag.tsx +31 -0
- package/src/theme/components/shared/theme-color-palette.css +80 -0
- package/src/theme/components/shared/theme-color-palette.stories.tsx +52 -0
- package/src/theme/components/shared/theme-color-palette.tsx +275 -0
- package/src/theme/components/shared/tooltip.css +69 -0
- package/src/theme/components/shared/tooltip.stories.tsx +72 -0
- package/src/theme/components/shared/tooltip.tsx +77 -0
- package/src/theme/components/sidebar/framework-picker.css +77 -0
- package/src/theme/components/sidebar/framework-picker.tsx +78 -0
- package/src/theme/components/sidebar/sidebar-filter.test.ts +140 -0
- package/src/theme/components/sidebar/sidebar-filter.ts +79 -0
- package/src/theme/components/sidebar/sidebar-links.css +102 -0
- package/src/theme/components/sidebar/sidebar-links.tsx +122 -0
- package/src/theme/components/sidebar/sidebar-promo.css +42 -0
- package/src/theme/components/sidebar/sidebar-promo.tsx +52 -0
- package/src/theme/components/sidebar/sidebar-scope.tsx +151 -0
- package/src/theme/components/sidebar/sidebar-toggle.css +181 -0
- package/src/theme/components/sidebar/sidebar-toggle.tsx +127 -0
- package/src/theme/components/theme-provider.tsx +637 -0
- package/src/theme/components/workspaces/card.css +151 -0
- package/src/theme/components/workspaces/card.tsx +166 -0
- package/src/theme/components/workspaces/grid.tsx +62 -0
- package/src/theme/css.d.ts +1 -0
- package/src/theme/env.d.ts +12 -0
- package/src/theme/fonts/GeistMono-Variable.woff2 +0 -0
- package/src/theme/fonts/GeistPixel-Square.woff2 +0 -0
- package/src/theme/fonts/GeistSans-Variable.woff2 +0 -0
- package/src/theme/hooks/use-ciderpress.ts +61 -0
- package/src/theme/hooks/use-nav-items.ts +76 -0
- package/src/theme/icons/index.ts +2 -0
- package/src/theme/icons/tech-map.ts +228 -0
- package/src/theme/index.tsx +180 -0
- package/src/theme/lib/read-social-links.ts +27 -0
- package/src/theme/lib/route-link.tsx +126 -0
- package/src/theme/lib/safe-url.test.ts +0 -0
- package/src/theme/lib/safe-url.ts +68 -0
- package/src/theme/lib/theme-favicon.ts +125 -0
- package/src/theme/lib/user-config-stub.ts +12 -0
- package/src/theme/styles/layers.css +32 -0
- package/src/theme/styles/overrides/badge.css +26 -0
- package/src/theme/styles/overrides/content-footer.css +80 -0
- package/src/theme/styles/overrides/details.css +61 -0
- package/src/theme/styles/overrides/fonts.css +32 -0
- package/src/theme/styles/overrides/home-card.css +133 -0
- package/src/theme/styles/overrides/home.css +243 -0
- package/src/theme/styles/overrides/rail.css +337 -0
- package/src/theme/styles/overrides/rspress.css +62 -0
- package/src/theme/styles/overrides/scrollbar.css +25 -0
- package/src/theme/styles/overrides/section-card.css +114 -0
- package/src/theme/styles/overrides/sidebar.css +144 -0
- package/src/theme/styles/overrides/tokens.css +288 -0
- package/src/theme/styles/overrides/vscode.css +88 -0
- package/src/theme/styles/themes/amber.css +545 -0
- package/src/theme/styles/themes/arcade-fx.css +366 -0
- package/src/theme/styles/themes/arcade.css +273 -0
- package/src/theme/styles/themes/grannysmith.css +545 -0
- package/src/theme/styles/themes/honeycrisp.css +545 -0
- package/src/theme/styles/themes/midnight.css +273 -0
- package/src/theme/styles/themes/mulled.css +817 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
.cp-fwpicker {
|
|
2
|
+
padding: 14px 14px 12px;
|
|
3
|
+
border-bottom: 1px solid var(--cp-c-border);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.cp-fwpicker__head {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: space-between;
|
|
10
|
+
margin-bottom: 6px;
|
|
11
|
+
gap: 8px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.cp-fwpicker__head-right {
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
gap: 6px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.cp-fwpicker__label {
|
|
21
|
+
font-family: var(--cp-font-family-mono);
|
|
22
|
+
font-size: 10px;
|
|
23
|
+
text-transform: uppercase;
|
|
24
|
+
letter-spacing: 0.08em;
|
|
25
|
+
color: var(--cp-c-text-3);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.cp-fwpicker__version {
|
|
29
|
+
font-family: var(--cp-font-family-mono);
|
|
30
|
+
font-size: 10px;
|
|
31
|
+
color: var(--cp-c-text-3);
|
|
32
|
+
padding: 1px 6px;
|
|
33
|
+
border: 1px solid var(--cp-c-border);
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
white-space: nowrap;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.cp-fwpicker__control {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: 8px;
|
|
42
|
+
padding: 7px 10px;
|
|
43
|
+
background: var(--cp-c-bg-alt);
|
|
44
|
+
border: 1px solid var(--cp-c-border);
|
|
45
|
+
border-radius: var(--cp-radius-card);
|
|
46
|
+
font-size: 13px;
|
|
47
|
+
color: var(--cp-c-text-1);
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
transition: border-color 0.15s ease;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.cp-fwpicker__control:hover {
|
|
53
|
+
border-color: var(--cp-c-border);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.cp-fwpicker__mark {
|
|
57
|
+
width: 14px;
|
|
58
|
+
height: 14px;
|
|
59
|
+
border-radius: 3px;
|
|
60
|
+
background: linear-gradient(135deg, var(--cp-c-brand-1), var(--cp-c-brand-2));
|
|
61
|
+
display: grid;
|
|
62
|
+
place-items: center;
|
|
63
|
+
color: var(--cp-c-brand-fg);
|
|
64
|
+
font-size: 9px;
|
|
65
|
+
font-weight: 700;
|
|
66
|
+
font-family: var(--cp-font-family-mono);
|
|
67
|
+
flex-shrink: 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.cp-fwpicker__name {
|
|
71
|
+
flex: 1;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.cp-fwpicker__chev {
|
|
75
|
+
color: var(--cp-c-text-3);
|
|
76
|
+
flex-shrink: 0;
|
|
77
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { match } from 'massaman/match'
|
|
2
|
+
import type React from 'react'
|
|
3
|
+
|
|
4
|
+
import { SidebarToggle } from './sidebar-toggle'
|
|
5
|
+
|
|
6
|
+
import './framework-picker.css'
|
|
7
|
+
|
|
8
|
+
export interface FrameworkPickerProps {
|
|
9
|
+
/**
|
|
10
|
+
* Eyebrow label above the picker. Defaults to "Framework".
|
|
11
|
+
*/
|
|
12
|
+
readonly label?: string
|
|
13
|
+
/**
|
|
14
|
+
* Currently active framework name shown in the picker.
|
|
15
|
+
*/
|
|
16
|
+
readonly current: string
|
|
17
|
+
/**
|
|
18
|
+
* Optional small mark / icon shown to the left of the framework name.
|
|
19
|
+
*/
|
|
20
|
+
readonly mark?: React.ReactNode
|
|
21
|
+
/**
|
|
22
|
+
* Optional version chip shown next to the eyebrow label.
|
|
23
|
+
*/
|
|
24
|
+
readonly version?: string
|
|
25
|
+
/**
|
|
26
|
+
* When true, render a sidebar collapse toggle in the picker header.
|
|
27
|
+
*/
|
|
28
|
+
readonly showCollapseToggle?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* FrameworkPicker — top-of-sidebar selector showing the current framework
|
|
33
|
+
* + an optional version chip + an optional sidebar collapse toggle.
|
|
34
|
+
* Mirrors the mockup's framework switcher pattern.
|
|
35
|
+
*
|
|
36
|
+
* @param props - Picker configuration.
|
|
37
|
+
* @returns React element.
|
|
38
|
+
*/
|
|
39
|
+
export function FrameworkPicker(props: FrameworkPickerProps): React.ReactElement {
|
|
40
|
+
const { label = 'Framework', current, mark, version, showCollapseToggle } = props
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className="cp-fwpicker">
|
|
44
|
+
<div className="cp-fwpicker__head">
|
|
45
|
+
<span className="cp-fwpicker__label">{label}</span>
|
|
46
|
+
<div className="cp-fwpicker__head-right">
|
|
47
|
+
{match(version)
|
|
48
|
+
.with(undefined, () => null)
|
|
49
|
+
.otherwise((v) => (
|
|
50
|
+
<span className="cp-fwpicker__version">{v}</span>
|
|
51
|
+
))}
|
|
52
|
+
{match(showCollapseToggle)
|
|
53
|
+
.with(true, () => <SidebarToggle />)
|
|
54
|
+
.otherwise(() => null)}
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div className="cp-fwpicker__control">
|
|
58
|
+
{match(mark)
|
|
59
|
+
.with(undefined, () => null)
|
|
60
|
+
.otherwise((m) => (
|
|
61
|
+
<span className="cp-fwpicker__mark">{m}</span>
|
|
62
|
+
))}
|
|
63
|
+
<span className="cp-fwpicker__name">{current}</span>
|
|
64
|
+
<svg
|
|
65
|
+
className="cp-fwpicker__chev"
|
|
66
|
+
width="14"
|
|
67
|
+
height="14"
|
|
68
|
+
viewBox="0 0 24 24"
|
|
69
|
+
fill="none"
|
|
70
|
+
stroke="currentColor"
|
|
71
|
+
strokeWidth="2"
|
|
72
|
+
>
|
|
73
|
+
<polyline points="6 9 12 15 18 9" />
|
|
74
|
+
</svg>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { SidebarData } from '@rspress/core'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { belongsToScope, resolveScopedSidebar } from './sidebar-filter'
|
|
5
|
+
|
|
6
|
+
const gettingStarted: SidebarData[number] = {
|
|
7
|
+
text: 'Getting Started',
|
|
8
|
+
link: '/getting-started',
|
|
9
|
+
collapsible: true,
|
|
10
|
+
collapsed: true,
|
|
11
|
+
items: [
|
|
12
|
+
{ text: 'Introduction', link: '/getting-started/introduction' },
|
|
13
|
+
{ text: 'Quick Start', link: '/getting-started/quick-start' },
|
|
14
|
+
],
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const packages: SidebarData[number] = {
|
|
18
|
+
text: 'Packages',
|
|
19
|
+
link: '/packages',
|
|
20
|
+
collapsible: true,
|
|
21
|
+
collapsed: true,
|
|
22
|
+
items: [
|
|
23
|
+
{ text: 'CLI', link: '/packages/cli' },
|
|
24
|
+
{ text: 'Core', link: '/packages/core' },
|
|
25
|
+
],
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const contributing: SidebarData[number] = {
|
|
29
|
+
text: 'Contributing',
|
|
30
|
+
link: '/contributing',
|
|
31
|
+
collapsible: true,
|
|
32
|
+
collapsed: true,
|
|
33
|
+
items: [
|
|
34
|
+
{ text: 'Guides', link: '/contributing/guides' },
|
|
35
|
+
{ text: 'Standards', link: '/contributing/standards' },
|
|
36
|
+
],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const groupWithoutLink: SidebarData[number] = {
|
|
40
|
+
text: 'Nested Group',
|
|
41
|
+
collapsible: true,
|
|
42
|
+
collapsed: true,
|
|
43
|
+
items: [{ text: 'Deep Page', link: '/packages/deep/page' }],
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const fullSidebar: SidebarData = [gettingStarted, packages, contributing]
|
|
47
|
+
const scopes: readonly string[] = ['/packages', '/contributing']
|
|
48
|
+
|
|
49
|
+
describe('resolveScopedSidebar()', () => {
|
|
50
|
+
it('should return all items when no scopes are defined', () => {
|
|
51
|
+
const result = resolveScopedSidebar(fullSidebar, '/getting-started', [])
|
|
52
|
+
|
|
53
|
+
expect(result).toHaveLength(3)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should exclude standalone sections on a main page', () => {
|
|
57
|
+
const result = resolveScopedSidebar(fullSidebar, '/getting-started/introduction', scopes)
|
|
58
|
+
|
|
59
|
+
expect(result).toHaveLength(1)
|
|
60
|
+
expect(result[0]).toMatchObject({ text: 'Getting Started' })
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should show only the matching standalone section on an exact scope path', () => {
|
|
64
|
+
const result = resolveScopedSidebar(fullSidebar, '/packages', scopes)
|
|
65
|
+
|
|
66
|
+
expect(result).toHaveLength(1)
|
|
67
|
+
expect(result[0]).toMatchObject({ text: 'Packages' })
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should show only the matching standalone section on a nested scope path', () => {
|
|
71
|
+
const result = resolveScopedSidebar(fullSidebar, '/packages/cli', scopes)
|
|
72
|
+
|
|
73
|
+
expect(result).toHaveLength(1)
|
|
74
|
+
expect(result[0]).toMatchObject({ text: 'Packages' })
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should show only contributing items on the contributing scope', () => {
|
|
78
|
+
const result = resolveScopedSidebar(fullSidebar, '/contributing/guides', scopes)
|
|
79
|
+
|
|
80
|
+
expect(result).toHaveLength(1)
|
|
81
|
+
expect(result[0]).toMatchObject({ text: 'Contributing' })
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('should not mutate the original sidebar data', () => {
|
|
85
|
+
const original = [...fullSidebar]
|
|
86
|
+
|
|
87
|
+
resolveScopedSidebar(fullSidebar, '/packages', scopes)
|
|
88
|
+
|
|
89
|
+
expect(fullSidebar).toStrictEqual(original)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should not match a scope that is only a prefix of the pathname segment', () => {
|
|
93
|
+
const result = resolveScopedSidebar(fullSidebar, '/packages-extra/foo', scopes)
|
|
94
|
+
|
|
95
|
+
expect(result).toHaveLength(1)
|
|
96
|
+
expect(result[0]).toMatchObject({ text: 'Getting Started' })
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('belongsToScope()', () => {
|
|
101
|
+
it('should match an item whose link equals the scope', () => {
|
|
102
|
+
expect(belongsToScope(packages, '/packages')).toBe(true)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should match an item whose link starts with scope/', () => {
|
|
106
|
+
const item: SidebarData[number] = { text: 'CLI', link: '/packages/cli' }
|
|
107
|
+
|
|
108
|
+
expect(belongsToScope(item, '/packages')).toBe(true)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('should not match an item from a different scope', () => {
|
|
112
|
+
expect(belongsToScope(gettingStarted, '/packages')).toBe(false)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('should not match a partial path segment overlap', () => {
|
|
116
|
+
const item: SidebarData[number] = { text: 'Extra', link: '/packages-extra' }
|
|
117
|
+
|
|
118
|
+
expect(belongsToScope(item, '/packages')).toBe(false)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should match a group without a link by checking children', () => {
|
|
122
|
+
expect(belongsToScope(groupWithoutLink, '/packages')).toBe(true)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should not match a group without a link when children belong to a different scope', () => {
|
|
126
|
+
expect(belongsToScope(groupWithoutLink, '/contributing')).toBe(false)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should not match a divider (no link, no items)', () => {
|
|
130
|
+
const divider: SidebarData[number] = { dividerType: 'solid' }
|
|
131
|
+
|
|
132
|
+
expect(belongsToScope(divider, '/packages')).toBe(false)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should not match a section header (no link, no items)', () => {
|
|
136
|
+
const header: SidebarData[number] = { sectionHeaderText: 'Header' }
|
|
137
|
+
|
|
138
|
+
expect(belongsToScope(header, '/packages')).toBe(false)
|
|
139
|
+
})
|
|
140
|
+
})
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure sidebar filtering logic for multi-scope support.
|
|
3
|
+
*
|
|
4
|
+
* Extracts the runtime filtering from the Sidebar React component so the
|
|
5
|
+
* logic is independently testable without React rendering.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { SidebarData } from '@rspress/core'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Union element of `SidebarData` — sidebar items may be groups, leaf items,
|
|
12
|
+
* dividers, or section headers.
|
|
13
|
+
*
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
type SidebarDataItem = SidebarData[number]
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the sidebar items visible for the current scope.
|
|
20
|
+
*
|
|
21
|
+
* When the current pathname matches a standalone scope (e.g. `/packages`),
|
|
22
|
+
* only that section's sidebar items are returned. For all other paths,
|
|
23
|
+
* standalone sections are excluded and the remaining sections form a
|
|
24
|
+
* unified sidebar.
|
|
25
|
+
*
|
|
26
|
+
* @param items - Full unified sidebar items
|
|
27
|
+
* @param pathname - Current decoded URL pathname
|
|
28
|
+
* @param scopes - Standalone scope paths (e.g. `["/packages", "/contributing"]`)
|
|
29
|
+
* @returns Filtered sidebar items for the active scope
|
|
30
|
+
*/
|
|
31
|
+
export function resolveScopedSidebar(
|
|
32
|
+
items: SidebarData,
|
|
33
|
+
pathname: string,
|
|
34
|
+
scopes: readonly string[]
|
|
35
|
+
): SidebarData {
|
|
36
|
+
if (scopes.length === 0) {
|
|
37
|
+
return [...items]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const standaloneMatch = scopes.find(
|
|
41
|
+
(scope) => pathname === scope || pathname.startsWith(`${scope}/`)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
const all = [...items]
|
|
45
|
+
|
|
46
|
+
if (standaloneMatch) {
|
|
47
|
+
return all.filter((item) => belongsToScope(item, standaloneMatch))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return all.filter((item) => !scopes.some((scope) => belongsToScope(item, scope)))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check whether a sidebar item belongs to a given scope path.
|
|
55
|
+
*
|
|
56
|
+
* Matches when the item's link equals the scope or starts with scope + "/".
|
|
57
|
+
* For groups without a direct link, recursively checks child items.
|
|
58
|
+
* Dividers and section headers (no link, no items) are never matched.
|
|
59
|
+
*
|
|
60
|
+
* @private
|
|
61
|
+
* @param item - Sidebar item to check
|
|
62
|
+
* @param scope - Scope path (e.g. "/packages")
|
|
63
|
+
* @returns True when the item belongs to the scope
|
|
64
|
+
*/
|
|
65
|
+
export function belongsToScope(item: SidebarDataItem, scope: string): boolean {
|
|
66
|
+
if (Object.hasOwn(item, 'link')) {
|
|
67
|
+
const { link } = item as { readonly link?: string }
|
|
68
|
+
if (link && (link === scope || link.startsWith(`${scope}/`))) {
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Check children — auto-discovered groups from _meta.json may have no
|
|
73
|
+
// link on the group itself, only on their child items.
|
|
74
|
+
if (Object.hasOwn(item, 'items')) {
|
|
75
|
+
const { items } = item as { readonly items: readonly SidebarDataItem[] }
|
|
76
|
+
return items.some((child) => belongsToScope(child, scope))
|
|
77
|
+
}
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
.cp-sidebar-links {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: 6px;
|
|
5
|
+
padding: 8px 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* Position modifiers (`--above` / `--below`) used to own their own
|
|
9
|
+
divider hairlines + spacing, but the `.cp-sidebar-top` and
|
|
10
|
+
`.cp-sidebar-bottom` wrappers (added in v1) now provide the dividers
|
|
11
|
+
and edge-to-edge backgrounds. Keep the modifier classes as semantic
|
|
12
|
+
hooks for downstream consumers, but don't paint extra borders here. */
|
|
13
|
+
|
|
14
|
+
/* ── Base ───────────────────────────────────────────────── */
|
|
15
|
+
|
|
16
|
+
.cp-sidebar-link {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
gap: 8px;
|
|
20
|
+
padding: 6px 12px;
|
|
21
|
+
border-radius: 8px;
|
|
22
|
+
color: var(--rp-c-text-2);
|
|
23
|
+
font-size: 14px;
|
|
24
|
+
font-weight: 500;
|
|
25
|
+
line-height: 24px;
|
|
26
|
+
text-decoration: none;
|
|
27
|
+
transition:
|
|
28
|
+
color 0.15s,
|
|
29
|
+
background-color 0.15s,
|
|
30
|
+
border-color 0.15s,
|
|
31
|
+
opacity 0.15s;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* ── Ghost (default) — no background, subtle hover ───── */
|
|
35
|
+
|
|
36
|
+
.cp-sidebar-link--ghost {
|
|
37
|
+
background-color: transparent;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.cp-sidebar-link--ghost:hover {
|
|
41
|
+
color: var(--rp-c-text-1);
|
|
42
|
+
background-color: var(--rp-c-bg-mute);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* ── Brand — solid brand background ──────────────────── */
|
|
46
|
+
|
|
47
|
+
.cp-sidebar-link--brand {
|
|
48
|
+
color: var(--cp-c-on-brand);
|
|
49
|
+
background: var(--rp-c-brand-darker);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.cp-sidebar-link--brand:hover {
|
|
53
|
+
background: var(--rp-c-brand-dark);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ── Alt — outlined with border ──────────────────────── */
|
|
57
|
+
|
|
58
|
+
.cp-sidebar-link--alt {
|
|
59
|
+
border: 1px solid var(--rp-c-gray-light-3);
|
|
60
|
+
color: var(--rp-c-text-1);
|
|
61
|
+
background-color: var(--rp-c-bg);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
html.dark .cp-sidebar-link--alt {
|
|
65
|
+
background-color: var(--rp-c-bg-mute);
|
|
66
|
+
border-color: var(--cp-c-sidebar-alt-border-dark);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.cp-sidebar-link--alt:hover {
|
|
70
|
+
opacity: 0.8;
|
|
71
|
+
background-color: var(--rp-c-bg-mute);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* ── Shapes ──────────────────────────────────────────── */
|
|
75
|
+
|
|
76
|
+
.cp-sidebar-link--square {
|
|
77
|
+
border-radius: 8px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.cp-sidebar-link--rounded {
|
|
81
|
+
border-radius: 9999px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.cp-sidebar-link--circle {
|
|
85
|
+
border-radius: 50%;
|
|
86
|
+
justify-content: center;
|
|
87
|
+
width: 36px;
|
|
88
|
+
height: 36px;
|
|
89
|
+
padding: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.cp-sidebar-link-icon {
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
width: 16px;
|
|
95
|
+
height: 16px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.cp-sidebar-link-text {
|
|
99
|
+
overflow: hidden;
|
|
100
|
+
text-overflow: ellipsis;
|
|
101
|
+
white-space: nowrap;
|
|
102
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Link } from '@rspress/core/runtime'
|
|
2
|
+
import { match, P } from 'massaman/match'
|
|
3
|
+
import type React from 'react'
|
|
4
|
+
|
|
5
|
+
import { safeUrl } from '../../lib/safe-url.ts'
|
|
6
|
+
import { Icon } from '../shared/icon'
|
|
7
|
+
|
|
8
|
+
import './sidebar-links.css'
|
|
9
|
+
|
|
10
|
+
interface SidebarLinkItem {
|
|
11
|
+
readonly text: string
|
|
12
|
+
readonly link: string
|
|
13
|
+
readonly icon?: string | { readonly id: string; readonly color: string }
|
|
14
|
+
readonly style?: 'brand' | 'alt' | 'ghost'
|
|
15
|
+
readonly shape?: 'square' | 'rounded' | 'circle'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface SidebarLinksProps {
|
|
19
|
+
readonly items: readonly SidebarLinkItem[]
|
|
20
|
+
readonly position: 'above' | 'below'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Render a group of sidebar navigation links above or below the main sidebar.
|
|
25
|
+
*
|
|
26
|
+
* @param props - Sidebar links props with items and position
|
|
27
|
+
* @returns React element with sidebar links or null
|
|
28
|
+
*/
|
|
29
|
+
export function SidebarLinks(props: SidebarLinksProps): React.ReactElement | null {
|
|
30
|
+
return match(props.items.length > 0)
|
|
31
|
+
.with(true, () => (
|
|
32
|
+
<nav
|
|
33
|
+
className={`cp-sidebar-links cp-sidebar-links--${props.position}`}
|
|
34
|
+
aria-label={`Sidebar links ${props.position}`}
|
|
35
|
+
>
|
|
36
|
+
{props.items.map((item) => (
|
|
37
|
+
<SidebarLinkEntry key={item.link} item={item} />
|
|
38
|
+
))}
|
|
39
|
+
</nav>
|
|
40
|
+
))
|
|
41
|
+
.otherwise(() => null)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Render an icon from a sidebar link item's icon configuration.
|
|
46
|
+
*
|
|
47
|
+
* @private
|
|
48
|
+
* @param icon - Icon string or object with id/color
|
|
49
|
+
* @returns Icon element or null
|
|
50
|
+
*/
|
|
51
|
+
function renderIcon(icon: SidebarLinkItem['icon']): React.ReactElement | null {
|
|
52
|
+
return match(icon)
|
|
53
|
+
.with(P.string, (id) => <Icon icon={id} className="cp-sidebar-link-icon" />)
|
|
54
|
+
.with({ id: P.string, color: P.string }, (i) => (
|
|
55
|
+
<Icon icon={i.id} className="cp-sidebar-link-icon" style={{ color: i.color }} />
|
|
56
|
+
))
|
|
57
|
+
.otherwise(() => null)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check whether a URL points to an external origin.
|
|
62
|
+
*
|
|
63
|
+
* @private
|
|
64
|
+
* @param link - Link URL to check
|
|
65
|
+
* @returns True when the link is absolute http(s)
|
|
66
|
+
*/
|
|
67
|
+
function isExternal(link: string): boolean {
|
|
68
|
+
return link.startsWith('http://') || link.startsWith('https://')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Render a single sidebar link entry with optional icon.
|
|
73
|
+
* Uses client-side `Link` for internal routes, `<a>` for external URLs.
|
|
74
|
+
*
|
|
75
|
+
* @private
|
|
76
|
+
* @param props - Props with sidebar link item
|
|
77
|
+
* @returns Sidebar link element
|
|
78
|
+
*/
|
|
79
|
+
function SidebarLinkEntry({ item }: { readonly item: SidebarLinkItem }): React.ReactElement | null {
|
|
80
|
+
const isCircle = item.shape === 'circle'
|
|
81
|
+
const content = (
|
|
82
|
+
<>
|
|
83
|
+
{renderIcon(item.icon)}
|
|
84
|
+
{match(isCircle)
|
|
85
|
+
.with(true, () => null)
|
|
86
|
+
.otherwise(() => (
|
|
87
|
+
<span className="cp-sidebar-link-text">{item.text}</span>
|
|
88
|
+
))}
|
|
89
|
+
</>
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
const variant = item.style ?? 'ghost'
|
|
93
|
+
const shape = item.shape ?? 'square'
|
|
94
|
+
const cls = `cp-sidebar-link cp-sidebar-link--${variant} cp-sidebar-link--${shape}`
|
|
95
|
+
/* oxlint-disable unicorn/no-useless-undefined -- React aria-label expects undefined, not null */
|
|
96
|
+
const ariaLabel = match(isCircle)
|
|
97
|
+
.with(true, () => item.text)
|
|
98
|
+
.otherwise(() => undefined)
|
|
99
|
+
/* oxlint-enable unicorn/no-useless-undefined */
|
|
100
|
+
|
|
101
|
+
const safeLink = safeUrl(item.link)
|
|
102
|
+
if (safeLink === null) {
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
105
|
+
return match(isExternal(safeLink))
|
|
106
|
+
.with(true, () => (
|
|
107
|
+
<a
|
|
108
|
+
href={safeLink}
|
|
109
|
+
className={cls}
|
|
110
|
+
target="_blank"
|
|
111
|
+
rel="noopener noreferrer"
|
|
112
|
+
aria-label={ariaLabel}
|
|
113
|
+
>
|
|
114
|
+
{content}
|
|
115
|
+
</a>
|
|
116
|
+
))
|
|
117
|
+
.otherwise(() => (
|
|
118
|
+
<Link to={safeLink} className={cls} aria-label={ariaLabel}>
|
|
119
|
+
{content}
|
|
120
|
+
</Link>
|
|
121
|
+
))
|
|
122
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
.cp-sidebar-promo {
|
|
2
|
+
margin: 12px 8px;
|
|
3
|
+
border: 1px solid var(--cp-c-border);
|
|
4
|
+
border-radius: var(--cp-radius-card);
|
|
5
|
+
padding: 14px;
|
|
6
|
+
background: var(--cp-c-bg-soft);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.cp-sidebar-promo__title {
|
|
10
|
+
font-size: 13px;
|
|
11
|
+
font-weight: 500;
|
|
12
|
+
margin: 0 0 4px;
|
|
13
|
+
color: var(--cp-c-text-1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.cp-sidebar-promo__body {
|
|
17
|
+
font-size: 12px;
|
|
18
|
+
color: var(--cp-c-text-2);
|
|
19
|
+
line-height: 1.5;
|
|
20
|
+
margin: 0 0 10px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.cp-sidebar-promo__cta {
|
|
24
|
+
display: inline-flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
width: 100%;
|
|
28
|
+
padding: 6px 12px;
|
|
29
|
+
font-size: 12px;
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
border-radius: var(--cp-radius-pill);
|
|
32
|
+
background: var(--cp-c-brand-1);
|
|
33
|
+
color: var(--cp-c-brand-fg);
|
|
34
|
+
text-decoration: none;
|
|
35
|
+
transition: background 0.15s ease;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.cp-sidebar-promo__cta:hover {
|
|
39
|
+
background: var(--cp-c-brand-2);
|
|
40
|
+
color: var(--cp-c-brand-fg);
|
|
41
|
+
text-decoration: none;
|
|
42
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type React from 'react'
|
|
2
|
+
|
|
3
|
+
import { RouteLink } from '../../lib/route-link.tsx'
|
|
4
|
+
import { safeUrl } from '../../lib/safe-url.ts'
|
|
5
|
+
|
|
6
|
+
import './sidebar-promo.css'
|
|
7
|
+
|
|
8
|
+
export interface SidebarPromoProps {
|
|
9
|
+
/**
|
|
10
|
+
* Headline of the promo card.
|
|
11
|
+
*/
|
|
12
|
+
readonly title: string
|
|
13
|
+
/**
|
|
14
|
+
* Body copy beneath the headline.
|
|
15
|
+
*/
|
|
16
|
+
readonly body: string
|
|
17
|
+
/**
|
|
18
|
+
* CTA button label.
|
|
19
|
+
*/
|
|
20
|
+
readonly ctaText: string
|
|
21
|
+
/**
|
|
22
|
+
* CTA destination URL.
|
|
23
|
+
*/
|
|
24
|
+
readonly ctaHref: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* SidebarPromo — bordered promo card pinned at the bottom of the docs
|
|
29
|
+
* sidebar. Mirrors the mockup's "Ship docs that stay in sync" pattern.
|
|
30
|
+
*
|
|
31
|
+
* Renders `null` when `ctaHref` fails `safeUrl()` validation — the entire
|
|
32
|
+
* promo card disappears rather than rendering a card whose CTA goes
|
|
33
|
+
* nowhere safe.
|
|
34
|
+
*
|
|
35
|
+
* @param props - Promo card configuration.
|
|
36
|
+
* @returns React element, or `null` when the CTA href fails URL validation.
|
|
37
|
+
*/
|
|
38
|
+
export function SidebarPromo(props: SidebarPromoProps): React.ReactElement | null {
|
|
39
|
+
const ctaHref = safeUrl(props.ctaHref)
|
|
40
|
+
if (ctaHref === null) {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
return (
|
|
44
|
+
<div className="cp-sidebar-promo">
|
|
45
|
+
<p className="cp-sidebar-promo__title">{props.title}</p>
|
|
46
|
+
<p className="cp-sidebar-promo__body">{props.body}</p>
|
|
47
|
+
<RouteLink className="cp-sidebar-promo__cta" href={ctaHref}>
|
|
48
|
+
{props.ctaText}
|
|
49
|
+
</RouteLink>
|
|
50
|
+
</div>
|
|
51
|
+
)
|
|
52
|
+
}
|