@wrikka/create-docs 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 +137 -0
- package/bin/cli.js +408 -0
- package/dist/index.cjs +20 -0
- package/dist/index.d.cts +475 -0
- package/dist/index.d.ts +475 -0
- package/dist/index.js +20 -0
- package/docs/_dir.yml +2 -0
- package/docs/config.md +75 -0
- package/docs/data-sources.md +88 -0
- package/docs/deploy.md +38 -0
- package/docs/features.md +51 -0
- package/docs/getting-started.md +42 -0
- package/docs/index.md +34 -0
- package/docs/markdown.md +154 -0
- package/docs/theming.md +53 -0
- package/package.json +142 -0
- package/scripts/generate-docs-index.ts +111 -0
- package/scripts/github-pull.ts +124 -0
- package/src/adapters/config/index.ts +1 -0
- package/src/adapters/config/node-config-loader.ts +36 -0
- package/src/adapters/fs/fs-docs-loader.ts +31 -0
- package/src/adapters/fs/index.ts +12 -0
- package/src/adapters/fs/memory-fs.ts +60 -0
- package/src/adapters/fs/node-fs.ts +116 -0
- package/src/adapters/git/index.ts +164 -0
- package/src/adapters/github/github-pull.ts +340 -0
- package/src/adapters/index.ts +6 -0
- package/src/adapters/velite.ts +112 -0
- package/src/adapters/vite/docs-watcher.ts +36 -0
- package/src/adapters/vite/index.ts +2 -0
- package/src/adapters/vite/polling-watcher.ts +89 -0
- package/src/code-block-enhancements/index.ts +3 -0
- package/src/config/index.ts +15 -0
- package/src/content/index.ts +11 -0
- package/src/content-validation/index.ts +5 -0
- package/src/index.ts +174 -0
- package/src/live-editor/index.ts +7 -0
- package/src/modules/analytics/application/plausible-adapter.ts +103 -0
- package/src/modules/analytics/application/umami-adapter.ts +109 -0
- package/src/modules/analytics/domain/events/index.ts +37 -0
- package/src/modules/analytics/domain/models/index.ts +35 -0
- package/src/modules/analytics/domain/operations/analytics-operations.ts +67 -0
- package/src/modules/analytics/domain/validators/index.ts +50 -0
- package/src/modules/analytics/index.ts +9 -0
- package/src/modules/analytics/ports/analytics-port.ts +17 -0
- package/src/modules/analytics/types/analytics.ts +16 -0
- package/src/modules/api-docs/application/type-docs-adapter.ts +34 -0
- package/src/modules/api-docs/domain/events/index.ts +42 -0
- package/src/modules/api-docs/domain/models/index.ts +40 -0
- package/src/modules/api-docs/domain/operations/api-docs-operations.ts +59 -0
- package/src/modules/api-docs/domain/validators/index.ts +56 -0
- package/src/modules/api-docs/index.ts +8 -0
- package/src/modules/api-docs/ports/api-docs-port.ts +21 -0
- package/src/modules/api-docs/types/api-docs.ts +15 -0
- package/src/modules/api-reference/openapi.ts +166 -0
- package/src/modules/api-reference/orpc.ts +94 -0
- package/src/modules/code-block-enhancements/application/usecases/index.ts +39 -0
- package/src/modules/code-block-enhancements/domain/code-operations.ts +72 -0
- package/src/modules/code-block-enhancements/domain/events/index.ts +58 -0
- package/src/modules/code-block-enhancements/domain/index.ts +11 -0
- package/src/modules/code-block-enhancements/domain/models/index.ts +36 -0
- package/src/modules/code-block-enhancements/domain/validators/index.ts +50 -0
- package/src/modules/code-block-enhancements/index.ts +10 -0
- package/src/modules/code-block-enhancements/ports/index.ts +18 -0
- package/src/modules/code-block-enhancements/types/index.ts +37 -0
- package/src/modules/code-execution/application/sandbox-adapter.ts +44 -0
- package/src/modules/code-execution/domain/events/index.ts +58 -0
- package/src/modules/code-execution/domain/models/index.ts +33 -0
- package/src/modules/code-execution/domain/operations/execution-operations.ts +60 -0
- package/src/modules/code-execution/domain/validators/index.ts +50 -0
- package/src/modules/code-execution/index.ts +8 -0
- package/src/modules/code-execution/ports/execution-port.ts +15 -0
- package/src/modules/code-execution/types/execution.ts +15 -0
- package/src/modules/comments/application/giscus-adapter.ts +55 -0
- package/src/modules/comments/domain/events/index.ts +55 -0
- package/src/modules/comments/domain/models/index.ts +30 -0
- package/src/modules/comments/domain/operations/comment-operations.ts +46 -0
- package/src/modules/comments/domain/validators/index.ts +50 -0
- package/src/modules/comments/index.ts +8 -0
- package/src/modules/comments/ports/comment-port.ts +18 -0
- package/src/modules/comments/types/comments.ts +19 -0
- package/src/modules/config/application/usecases/index.ts +1 -0
- package/src/modules/config/application/usecases/loadConfig.ts +38 -0
- package/src/modules/config/application/workflows/index.ts +1 -0
- package/src/modules/config/application/workflows/validateConfig.ts +14 -0
- package/src/modules/config/domain/events/index.ts +50 -0
- package/src/modules/config/domain/models/index.ts +9 -0
- package/src/modules/config/domain/operations/defaults.ts +74 -0
- package/src/modules/config/domain/operations/index.ts +2 -0
- package/src/modules/config/domain/operations/resolve.ts +40 -0
- package/src/modules/config/domain/validators/index.ts +152 -0
- package/src/modules/config/index.ts +23 -0
- package/src/modules/config/ports/index.ts +15 -0
- package/src/modules/config/types/index.ts +113 -0
- package/src/modules/config/types/public.ts +19 -0
- package/src/modules/content/application/cache/index.ts +6 -0
- package/src/modules/content/application/cache/parse-cache.ts +6 -0
- package/src/modules/content/application/cache/parseCache.ts +56 -0
- package/src/modules/content/application/usecases/index.ts +2 -0
- package/src/modules/content/application/usecases/load-doc-page.ts +1 -0
- package/src/modules/content/application/usecases/loadDocPage.ts +111 -0
- package/src/modules/content/application/usecases/scanDocsDir.ts +105 -0
- package/src/modules/content/application/workflows/index.ts +1 -0
- package/src/modules/content/application/workflows/processDocs.ts +51 -0
- package/src/modules/content/domain/events/index.ts +61 -0
- package/src/modules/content/domain/models/index.ts +13 -0
- package/src/modules/content/domain/operations/build-page.ts +146 -0
- package/src/modules/content/domain/operations/buildPage.ts +93 -0
- package/src/modules/content/domain/operations/index.ts +17 -0
- package/src/modules/content/domain/operations/parse.ts +241 -0
- package/src/modules/content/domain/validators/frontmatter.ts +82 -0
- package/src/modules/content/domain/validators/index.ts +6 -0
- package/src/modules/content/index.ts +27 -0
- package/src/modules/content/ports/index.ts +45 -0
- package/src/modules/content/ports/velite.ts +134 -0
- package/src/modules/content/types/index.ts +76 -0
- package/src/modules/content/types/public.ts +1 -0
- package/src/modules/content-linting/application/builtin-linting-adapter.ts +82 -0
- package/src/modules/content-linting/domain/events/index.ts +58 -0
- package/src/modules/content-linting/domain/models/index.ts +46 -0
- package/src/modules/content-linting/domain/operations/linting-operations.ts +82 -0
- package/src/modules/content-linting/domain/validators/index.ts +50 -0
- package/src/modules/content-linting/index.ts +8 -0
- package/src/modules/content-linting/ports/linting-port.ts +21 -0
- package/src/modules/content-linting/types/linting.ts +18 -0
- package/src/modules/content-validation/application/usecases/index.ts +18 -0
- package/src/modules/content-validation/domain/events/index.ts +7 -0
- package/src/modules/content-validation/domain/index.ts +16 -0
- package/src/modules/content-validation/domain/models/index.ts +33 -0
- package/src/modules/content-validation/domain/operations/index.ts +157 -0
- package/src/modules/content-validation/domain/types/index.ts +17 -0
- package/src/modules/content-validation/domain/validators/index.ts +8 -0
- package/src/modules/content-validation/index.ts +9 -0
- package/src/modules/content-validation/ports/index.ts +15 -0
- package/src/modules/content-validation/types/index.ts +5 -0
- package/src/modules/export/application/puppeteer-export-adapter.ts +63 -0
- package/src/modules/export/domain/events/index.ts +55 -0
- package/src/modules/export/domain/models/index.ts +38 -0
- package/src/modules/export/domain/operations/export-operations.ts +56 -0
- package/src/modules/export/domain/validators/index.ts +52 -0
- package/src/modules/export/index.ts +8 -0
- package/src/modules/export/ports/export-port.ts +15 -0
- package/src/modules/export/types/export.ts +15 -0
- package/src/modules/external-search/application/algolia-adapter.ts +182 -0
- package/src/modules/external-search/application/meilisearch-adapter.ts +161 -0
- package/src/modules/external-search/domain/events/index.ts +42 -0
- package/src/modules/external-search/domain/models/index.ts +42 -0
- package/src/modules/external-search/domain/operations/search-operations.ts +53 -0
- package/src/modules/external-search/domain/validators/index.ts +55 -0
- package/src/modules/external-search/index.ts +9 -0
- package/src/modules/external-search/ports/external-search-port.ts +23 -0
- package/src/modules/external-search/types/search.ts +19 -0
- package/src/modules/git-history/application/simple-git-adapter.ts +46 -0
- package/src/modules/git-history/domain/events/index.ts +34 -0
- package/src/modules/git-history/domain/models/index.ts +25 -0
- package/src/modules/git-history/domain/operations/git-history-operations.ts +56 -0
- package/src/modules/git-history/domain/validators/index.ts +52 -0
- package/src/modules/git-history/index.ts +8 -0
- package/src/modules/git-history/ports/git-history-port.ts +21 -0
- package/src/modules/git-history/types/git-history.ts +15 -0
- package/src/modules/i18n/application/fs-locale-loader.ts +39 -0
- package/src/modules/i18n/domain/events/index.ts +34 -0
- package/src/modules/i18n/domain/models/index.ts +34 -0
- package/src/modules/i18n/domain/operations/locale-operations.ts +74 -0
- package/src/modules/i18n/domain/validators/index.ts +52 -0
- package/src/modules/i18n/index.ts +8 -0
- package/src/modules/i18n/ports/locale-loader-port.ts +18 -0
- package/src/modules/i18n/types/locale.ts +17 -0
- package/src/modules/image-optimization/application/sharp-adapter.ts +58 -0
- package/src/modules/image-optimization/domain/events/index.ts +39 -0
- package/src/modules/image-optimization/domain/models/index.ts +27 -0
- package/src/modules/image-optimization/domain/operations/image-operations.ts +65 -0
- package/src/modules/image-optimization/domain/validators/index.ts +52 -0
- package/src/modules/image-optimization/index.ts +8 -0
- package/src/modules/image-optimization/ports/image-optimizer-port.ts +15 -0
- package/src/modules/image-optimization/types/image.ts +17 -0
- package/src/modules/live-editor/application/usecases/index.ts +60 -0
- package/src/modules/live-editor/domain/events/index.ts +37 -0
- package/src/modules/live-editor/domain/gitOperations.ts +110 -0
- package/src/modules/live-editor/domain/index.ts +15 -0
- package/src/modules/live-editor/domain/models/index.ts +21 -0
- package/src/modules/live-editor/domain/validators/index.ts +55 -0
- package/src/modules/live-editor/index.ts +14 -0
- package/src/modules/live-editor/ports/index.ts +49 -0
- package/src/modules/live-editor/types/index.ts +80 -0
- package/src/modules/live-preview/application/vite-preview-adapter.ts +20 -0
- package/src/modules/live-preview/domain/operations/preview-operations.ts +56 -0
- package/src/modules/live-preview/index.ts +8 -0
- package/src/modules/live-preview/ports/preview-port.ts +20 -0
- package/src/modules/live-preview/types/preview.ts +16 -0
- package/src/modules/mdx/application/mdx-js-adapter.ts +38 -0
- package/src/modules/mdx/domain/operations/parse-mdx.ts +43 -0
- package/src/modules/mdx/index.ts +8 -0
- package/src/modules/mdx/ports/mdx-parser-port.ts +15 -0
- package/src/modules/mdx/types/mdx.ts +14 -0
- package/src/modules/navigation/application/usecases/buildNav.ts +15 -0
- package/src/modules/navigation/application/usecases/buildSidebar.ts +45 -0
- package/src/modules/navigation/application/usecases/index.ts +2 -0
- package/src/modules/navigation/domain/events/index.ts +30 -0
- package/src/modules/navigation/domain/models/index.ts +7 -0
- package/src/modules/navigation/domain/operations/group.ts +79 -0
- package/src/modules/navigation/domain/operations/index.ts +10 -0
- package/src/modules/navigation/domain/operations/sort.ts +27 -0
- package/src/modules/navigation/domain/shared/utils/array.ts +39 -0
- package/src/modules/navigation/domain/validators/index.ts +88 -0
- package/src/modules/navigation/index.ts +15 -0
- package/src/modules/navigation/ports/index.ts +9 -0
- package/src/modules/navigation/types/index.ts +38 -0
- package/src/modules/navigation/types/public.ts +9 -0
- package/src/modules/openapi/application/swagger-parser-adapter.ts +178 -0
- package/src/modules/openapi/domain/operations/parse-openapi.ts +46 -0
- package/src/modules/openapi/index.ts +8 -0
- package/src/modules/openapi/ports/openapi-parser-port.ts +15 -0
- package/src/modules/openapi/types/openapi.ts +37 -0
- package/src/modules/plugin/application/usecases/build-virtual-modules.ts +53 -0
- package/src/modules/plugin/application/usecases/index.ts +1 -0
- package/src/modules/plugin/application/workflows/build-docs.ts +79 -0
- package/src/modules/plugin/application/workflows/index.ts +1 -0
- package/src/modules/plugin/domain/events/index.ts +62 -0
- package/src/modules/plugin/domain/models/index.ts +1 -0
- package/src/modules/plugin/domain/operations/build-virtual.ts +76 -0
- package/src/modules/plugin/domain/operations/index.ts +11 -0
- package/src/modules/plugin/domain/validators/index.ts +30 -0
- package/src/modules/plugin/index.ts +14 -0
- package/src/modules/plugin/ports/index.ts +21 -0
- package/src/modules/plugin/types/index.ts +27 -0
- package/src/modules/plugin/types/public.ts +1 -0
- package/src/modules/search/application/usecases/index.ts +1 -0
- package/src/modules/search/application/usecases/indexPages.ts +15 -0
- package/src/modules/search/domain/events/index.ts +32 -0
- package/src/modules/search/domain/models/index.ts +1 -0
- package/src/modules/search/domain/operations/build-index.ts +6 -0
- package/src/modules/search/domain/operations/buildIndex.ts +70 -0
- package/src/modules/search/domain/operations/index.ts +7 -0
- package/src/modules/search/domain/operations/query.ts +186 -0
- package/src/modules/search/domain/validators/index.ts +32 -0
- package/src/modules/search/index.ts +9 -0
- package/src/modules/search/types/index.ts +22 -0
- package/src/modules/search/types/public.ts +1 -0
- package/src/modules/search-analytics/application/local-storage-adapter.ts +68 -0
- package/src/modules/search-analytics/domain/operations/search-analytics-operations.ts +74 -0
- package/src/modules/search-analytics/index.ts +8 -0
- package/src/modules/search-analytics/ports/search-analytics-port.ts +20 -0
- package/src/modules/search-analytics/types/search-analytics.ts +16 -0
- package/src/modules/search-highlighting/application/dom-highlighting-adapter.ts +36 -0
- package/src/modules/search-highlighting/domain/operations/highlighting-operations.ts +72 -0
- package/src/modules/search-highlighting/index.ts +8 -0
- package/src/modules/search-highlighting/ports/highlighting-port.ts +14 -0
- package/src/modules/search-highlighting/types/highlighting.ts +15 -0
- package/src/modules/seo-optimization/application/usecases/index.ts +18 -0
- package/src/modules/seo-optimization/domain/index.ts +12 -0
- package/src/modules/seo-optimization/domain/seoOperations.ts +92 -0
- package/src/modules/seo-optimization/index.ts +8 -0
- package/src/modules/seo-optimization/ports/index.ts +18 -0
- package/src/modules/seo-optimization/types/index.ts +41 -0
- package/src/modules/theme/application/css-theme-adapter.ts +50 -0
- package/src/modules/theme/domain/operations/theme-operations.ts +68 -0
- package/src/modules/theme/index.ts +8 -0
- package/src/modules/theme/ports/theme-port.ts +8 -0
- package/src/modules/theme/types/theme.ts +12 -0
- package/src/modules/toc/application/markdown-toc-adapter.ts +106 -0
- package/src/modules/toc/domain/operations/toc-operations.ts +92 -0
- package/src/modules/toc/index.ts +8 -0
- package/src/modules/toc/ports/toc-port.ts +21 -0
- package/src/modules/toc/types/toc.ts +17 -0
- package/src/modules/toc-generator/application/usecases/index.ts +30 -0
- package/src/modules/toc-generator/domain/index.ts +12 -0
- package/src/modules/toc-generator/domain/tocOperations.ts +104 -0
- package/src/modules/toc-generator/index.ts +9 -0
- package/src/modules/toc-generator/ports/index.ts +20 -0
- package/src/modules/toc-generator/types/index.ts +50 -0
- package/src/modules/version-control/domain/events/index.ts +39 -0
- package/src/modules/version-control/domain/models/index.ts +25 -0
- package/src/modules/version-control/domain/validators/index.ts +50 -0
- package/src/modules/versioning/application/fs-version-loader.ts +44 -0
- package/src/modules/versioning/domain/operations/versioning-operations.ts +80 -0
- package/src/modules/versioning/index.ts +8 -0
- package/src/modules/versioning/ports/version-loader-port.ts +18 -0
- package/src/modules/versioning/types/versioning.ts +17 -0
- package/src/modules/workflow/domain/events/index.ts +55 -0
- package/src/modules/workflow/domain/models/index.ts +40 -0
- package/src/modules/workflow/domain/validators/index.ts +50 -0
- package/src/modules/yaml-parser/application/js-yaml-adapter.ts +34 -0
- package/src/modules/yaml-parser/domain/operations/parse-yaml.ts +58 -0
- package/src/modules/yaml-parser/index.ts +7 -0
- package/src/modules/yaml-parser/ports/yaml-parser-port.ts +18 -0
- package/src/navigation/index.ts +9 -0
- package/src/plugin/index.ts +4 -0
- package/src/presentation/index.ts +1 -0
- package/src/presentation/live-editor/index.ts +22 -0
- package/src/presentation/live-editor/live-editor-component.ts +219 -0
- package/src/presentation/live-editor/virtual-module.ts +16 -0
- package/src/presentation/solid/CodeBlock.tsx +120 -0
- package/src/presentation/solid/Toc.tsx +58 -0
- package/src/presentation/solid/index.ts +11 -0
- package/src/presentation/vite-plugin/docs-plugin.ts +212 -0
- package/src/presentation/vite-plugin/index.ts +9 -0
- package/src/presentation/vite-plugin/transform-markdown.ts +26 -0
- package/src/runtime/analytics.ts +59 -0
- package/src/runtime/api-diff.ts +115 -0
- package/src/runtime/app.tsx +32 -0
- package/src/runtime/components/AccentPicker.tsx +114 -0
- package/src/runtime/components/ApiPlayground.tsx +332 -0
- package/src/runtime/components/AskAiDialog.tsx +127 -0
- package/src/runtime/components/BackToTop.tsx +38 -0
- package/src/runtime/components/Banner.tsx +75 -0
- package/src/runtime/components/Breadcrumbs.tsx +42 -0
- package/src/runtime/components/CollectionDropdown.tsx +85 -0
- package/src/runtime/components/CustomizeDrawer.tsx +195 -0
- package/src/runtime/components/DocMarkdown.tsx +579 -0
- package/src/runtime/components/DocPrevNext.tsx +54 -0
- package/src/runtime/components/DocToc.tsx +147 -0
- package/src/runtime/components/DocsDropdown.tsx +184 -0
- package/src/runtime/components/Footer.tsx +52 -0
- package/src/runtime/components/GitHubAuth.tsx +82 -0
- package/src/runtime/components/GitHubStats.tsx +73 -0
- package/src/runtime/components/Head.tsx +225 -0
- package/src/runtime/components/LocaleDropdown.tsx +75 -0
- package/src/runtime/components/MobileBottomNav.tsx +111 -0
- package/src/runtime/components/NotFound.tsx +139 -0
- package/src/runtime/components/PageActions.tsx +144 -0
- package/src/runtime/components/RelatedDocs.tsx +50 -0
- package/src/runtime/components/ScrollProgress.tsx +32 -0
- package/src/runtime/components/SearchPalette.tsx +333 -0
- package/src/runtime/components/ShowcaseCard.tsx +87 -0
- package/src/runtime/components/SidebarNav.tsx +237 -0
- package/src/runtime/components/Skeleton.tsx +39 -0
- package/src/runtime/components/ThemeToggle.tsx +26 -0
- package/src/runtime/components/TopNav.tsx +270 -0
- package/src/runtime/components/VersionDropdown.tsx +189 -0
- package/src/runtime/components/markdown/components.ts +280 -0
- package/src/runtime/config.ts +225 -0
- package/src/runtime/content.ts +451 -0
- package/src/runtime/context.tsx +30 -0
- package/src/runtime/data.ts +93 -0
- package/src/runtime/github.ts +226 -0
- package/src/runtime/icons.ts +32 -0
- package/src/runtime/index.ts +153 -0
- package/src/runtime/layouts/DocsLayout.tsx +144 -0
- package/src/runtime/markdown-content.css +996 -0
- package/src/runtime/pages/AbTestPage.tsx +88 -0
- package/src/runtime/pages/AnalyticsPage.tsx +90 -0
- package/src/runtime/pages/ApiDiffPage.tsx +86 -0
- package/src/runtime/pages/ApiEndpointPage.tsx +248 -0
- package/src/runtime/pages/BuilderPage.tsx +150 -0
- package/src/runtime/pages/ChangelogPage.tsx +210 -0
- package/src/runtime/pages/CollectionPage.tsx +140 -0
- package/src/runtime/pages/CommunityPage.tsx +384 -0
- package/src/runtime/pages/CreatePage.tsx +448 -0
- package/src/runtime/pages/DocPage.tsx +193 -0
- package/src/runtime/pages/EditPage.tsx +201 -0
- package/src/runtime/pages/HomePage.tsx +187 -0
- package/src/runtime/pages/IssuesPage.tsx +140 -0
- package/src/runtime/pages/OAuthCallbackPage.tsx +80 -0
- package/src/runtime/pages/PluginsPage.tsx +249 -0
- package/src/runtime/pages/ShowcasePage.tsx +202 -0
- package/src/runtime/pwa.ts +39 -0
- package/src/runtime/router.tsx +192 -0
- package/src/runtime/seo.ts +192 -0
- package/src/runtime/theme.css +175 -0
- package/src/runtime/theme.ts +224 -0
- package/src/runtime/types.ts +217 -0
- package/src/search/index.ts +6 -0
- package/src/seo-optimization/index.ts +4 -0
- package/src/shared/constants/index.ts +26 -0
- package/src/shared/errors/index.ts +195 -0
- package/src/shared/index.ts +27 -0
- package/src/shared/types/brand.ts +14 -0
- package/src/shared/types/client.d.ts +45 -0
- package/src/shared/types/either.ts +38 -0
- package/src/shared/types/index.ts +42 -0
- package/src/shared/types/option.ts +52 -0
- package/src/shared/types/result.ts +81 -0
- package/src/shared/utils/array.ts +97 -0
- package/src/shared/utils/index.ts +46 -0
- package/src/shared/utils/object.ts +47 -0
- package/src/shared/utils/string.ts +74 -0
- package/src/toc-generator/index.ts +1 -0
- package/src/types/js-yaml.d.ts +42 -0
- package/src/worker.ts +462 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Imperative shell: assemble all the pieces into a Vite Plugin.
|
|
3
|
+
*
|
|
4
|
+
* The functional core (buildVirtualModules, etc.) lives in the plugin module.
|
|
5
|
+
* This file is the I/O boundary that talks to Vite's runtime API.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type DocsPluginOptions,
|
|
10
|
+
type ResolvedDocsConfig,
|
|
11
|
+
resolveConfig,
|
|
12
|
+
type UserConfig,
|
|
13
|
+
} from "@create-docs/modules/config";
|
|
14
|
+
import { type DocPage, ParseCache } from "@create-docs/modules/content";
|
|
15
|
+
import { buildDocs, isMarkdownPath } from "@create-docs/modules/plugin";
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_BASE_ROUTE,
|
|
18
|
+
DEFAULT_DOCS_DIR,
|
|
19
|
+
PLUGIN_NAME,
|
|
20
|
+
VIRTUAL_PREFIX,
|
|
21
|
+
} from "@create-docs/shared/constants";
|
|
22
|
+
import { isString } from "@create-docs/shared/utils/object";
|
|
23
|
+
import { toPosixPath } from "@create-docs/shared/utils/string";
|
|
24
|
+
import type { Plugin, ResolvedConfig, ViteDevServer } from "vite";
|
|
25
|
+
import { createFsDocsLoader } from "../../adapters/fs/fs-docs-loader";
|
|
26
|
+
import { buildVelite } from "../../adapters/velite";
|
|
27
|
+
import {
|
|
28
|
+
createNodeDocsWatcher,
|
|
29
|
+
createPollingWatcher,
|
|
30
|
+
} from "../../adapters/vite";
|
|
31
|
+
|
|
32
|
+
export type WatchMode = "native" | "polling";
|
|
33
|
+
|
|
34
|
+
export interface DocsPluginConfig extends Partial<UserConfig> {
|
|
35
|
+
/** Optional: explicit config object passed by the consumer. */
|
|
36
|
+
readonly userConfig?: UserConfig;
|
|
37
|
+
/** Optional: pre-resolved plugin options override. */
|
|
38
|
+
readonly options?: DocsPluginOptions;
|
|
39
|
+
/** Force a full browser reload on HMR (default: false, granular invalidation). */
|
|
40
|
+
readonly fullReload?: boolean;
|
|
41
|
+
/** Watcher mode. Default: 'native'. Use 'polling' for Linux subdir reliability. */
|
|
42
|
+
readonly watchMode?: WatchMode;
|
|
43
|
+
/** Polling interval in ms when watchMode='polling'. Default: 300. */
|
|
44
|
+
readonly pollingIntervalMs?: number;
|
|
45
|
+
/** Per-page transformation hook. Invoked after parse, before serialisation. */
|
|
46
|
+
readonly transformPage?: (page: DocPage) => DocPage | Promise<DocPage>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const docsPlugin = (cfg: DocsPluginConfig = {}): Plugin => {
|
|
50
|
+
const userConfig: UserConfig = cfg.userConfig ?? { ...cfg };
|
|
51
|
+
const options: DocsPluginOptions = cfg.options ?? {};
|
|
52
|
+
const fullReload = cfg.fullReload === true;
|
|
53
|
+
const watchMode: WatchMode = cfg.watchMode ?? "native";
|
|
54
|
+
const pollingIntervalMs = cfg.pollingIntervalMs ?? 300;
|
|
55
|
+
const transformPage = cfg.transformPage;
|
|
56
|
+
|
|
57
|
+
const docsDir = options.docsDir ?? DEFAULT_DOCS_DIR;
|
|
58
|
+
const baseRoute = options.baseRoute ?? DEFAULT_BASE_ROUTE;
|
|
59
|
+
|
|
60
|
+
let resolved: ResolvedDocsConfig | null = null;
|
|
61
|
+
let server: ViteDevServer | null = null;
|
|
62
|
+
let watcherHandle: { close: () => void } | null = null;
|
|
63
|
+
let lastModules: Readonly<Record<string, string>> = {};
|
|
64
|
+
const cache: ParseCache<DocPage> = new ParseCache<DocPage>();
|
|
65
|
+
|
|
66
|
+
const resolve = (): ResolvedDocsConfig => {
|
|
67
|
+
if (resolved) return resolved;
|
|
68
|
+
resolved = resolveConfig({ ...userConfig, docsDir, baseRoute });
|
|
69
|
+
return resolved;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const invalidateAndNotify = (now2: number = Date.now()): void => {
|
|
73
|
+
if (!server) return;
|
|
74
|
+
const invalidated: string[] = [];
|
|
75
|
+
for (const id of Object.keys(lastModules)) {
|
|
76
|
+
const mod = server.moduleGraph.getModuleById(id);
|
|
77
|
+
if (mod) {
|
|
78
|
+
server.moduleGraph.invalidateModule(mod);
|
|
79
|
+
invalidated.push(id);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (fullReload) {
|
|
83
|
+
server.ws.send({ type: "full-reload" });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// Granular HMR: send an update event referencing the virtual modules.
|
|
87
|
+
// Connected clients with `import.meta.hot.accept` on these virtuals
|
|
88
|
+
// will hot-swap; others will be re-evaluated via the standard
|
|
89
|
+
// module graph invalidation without a full page reload.
|
|
90
|
+
const now = now2;
|
|
91
|
+
server.ws.send({
|
|
92
|
+
type: "update",
|
|
93
|
+
updates: invalidated.map((id) => ({
|
|
94
|
+
type: "js-update" as const,
|
|
95
|
+
path: id,
|
|
96
|
+
acceptedPath: id,
|
|
97
|
+
timestamp: now,
|
|
98
|
+
})),
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const reload = async (): Promise<void> => {
|
|
103
|
+
const config = resolve();
|
|
104
|
+
|
|
105
|
+
// Build content with Velite
|
|
106
|
+
const veliteResult = await buildVelite({
|
|
107
|
+
configPath: config.docsDir
|
|
108
|
+
? `${config.docsDir}/velite.config.ts`
|
|
109
|
+
: "velite.config.ts",
|
|
110
|
+
watch: true,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (!veliteResult.success) {
|
|
114
|
+
console.error(
|
|
115
|
+
`[${PLUGIN_NAME}] Velite build failed:`,
|
|
116
|
+
veliteResult.error,
|
|
117
|
+
);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Build virtual modules from Velite output
|
|
122
|
+
const loader = createFsDocsLoader();
|
|
123
|
+
const result = await buildDocs({
|
|
124
|
+
config,
|
|
125
|
+
loader,
|
|
126
|
+
...(transformPage ? { transformPage } : {}),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (result.ok) {
|
|
130
|
+
lastModules = result.value as unknown as Record<string, string>;
|
|
131
|
+
invalidateAndNotify();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
name: PLUGIN_NAME,
|
|
137
|
+
|
|
138
|
+
config() {
|
|
139
|
+
return {
|
|
140
|
+
optimizeDeps: {
|
|
141
|
+
include: ["comark", "@comark/svelte"],
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
async configResolved(config: ResolvedConfig) {
|
|
147
|
+
resolve();
|
|
148
|
+
// Initial build so the virtual modules are ready for the first request.
|
|
149
|
+
await reload();
|
|
150
|
+
// Watch docsDir for changes
|
|
151
|
+
try {
|
|
152
|
+
const docsDirAbs = toPosixPath(config.root).replace(/\/$/, "");
|
|
153
|
+
if (watchMode === "polling") {
|
|
154
|
+
const watcher = createPollingWatcher({
|
|
155
|
+
intervalMs: pollingIntervalMs,
|
|
156
|
+
});
|
|
157
|
+
watcherHandle = watcher.watch(docsDirAbs, (_filename) => {
|
|
158
|
+
void reload();
|
|
159
|
+
});
|
|
160
|
+
} else {
|
|
161
|
+
const watcher = createNodeDocsWatcher();
|
|
162
|
+
watcherHandle = watcher.watch(docsDirAbs, (_filename) => {
|
|
163
|
+
void reload();
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
} catch (e) {
|
|
167
|
+
console.warn(`[${PLUGIN_NAME}] Failed to start watcher:`, e);
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
configureServer(s: ViteDevServer) {
|
|
172
|
+
server = s;
|
|
173
|
+
s.httpServer?.on("close", () => {
|
|
174
|
+
watcherHandle?.close();
|
|
175
|
+
cache.clear();
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
resolveId(id: string) {
|
|
180
|
+
if (id.startsWith(VIRTUAL_PREFIX)) return id;
|
|
181
|
+
return null;
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
async load(id: string) {
|
|
185
|
+
if (!id.startsWith(VIRTUAL_PREFIX)) return null;
|
|
186
|
+
const mod = (lastModules as Record<string, string>)[id];
|
|
187
|
+
if (mod !== undefined) return mod;
|
|
188
|
+
await reload();
|
|
189
|
+
const m = (lastModules as Record<string, string>)[id];
|
|
190
|
+
return m ?? null;
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
async transform(code: string, id: string) {
|
|
194
|
+
if (!isMarkdownPath(id)) return null;
|
|
195
|
+
if (!isString(code)) return null;
|
|
196
|
+
try {
|
|
197
|
+
// Velite handles markdown parsing, skip transformation here
|
|
198
|
+
// Content is processed by Velite build and loaded from virtual modules
|
|
199
|
+
return null;
|
|
200
|
+
} catch (e) {
|
|
201
|
+
console.error(`[${PLUGIN_NAME}] Failed to transform ${id}:`, e);
|
|
202
|
+
return { code, map: null };
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
closeBundle() {
|
|
207
|
+
watcherHandle?.close();
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export default docsPlugin;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin presentation entry point.
|
|
3
|
+
* This is the ONLY file in `presentation/` that should be imported by the
|
|
4
|
+
* public API — it's the imperative shell that wires adapters to use cases.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type { DocsPluginConfig } from "./docs-plugin";
|
|
8
|
+
export { default, docsPlugin } from "./docs-plugin";
|
|
9
|
+
export { transformMarkdown } from "./transform-markdown";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure transformation: turn a markdown file path → Svelte source code.
|
|
3
|
+
* No I/O, no exceptions. The `parse` from comark is injected so this
|
|
4
|
+
* function is fully testable.
|
|
5
|
+
*/
|
|
6
|
+
import { parseFrontmatter } from "@create-docs/modules/content";
|
|
7
|
+
|
|
8
|
+
export type MarkdownToSvelte = (
|
|
9
|
+
raw: string,
|
|
10
|
+
deps: {
|
|
11
|
+
readonly parse: (content: string) => Promise<unknown>;
|
|
12
|
+
},
|
|
13
|
+
) => Promise<string>;
|
|
14
|
+
|
|
15
|
+
export const transformMarkdown: MarkdownToSvelte = async (raw, { parse }) => {
|
|
16
|
+
const { frontmatter, content } = parseFrontmatter(raw);
|
|
17
|
+
const tree = await parse(content);
|
|
18
|
+
const fmJson = JSON.stringify(frontmatter);
|
|
19
|
+
const contentJson = JSON.stringify(content);
|
|
20
|
+
const treeJson = JSON.stringify(tree);
|
|
21
|
+
return `<script lang="ts">
|
|
22
|
+
export const frontmatter = ${fmJson};
|
|
23
|
+
export const content = ${contentJson};
|
|
24
|
+
export const tree = ${treeJson};
|
|
25
|
+
</script>`;
|
|
26
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight, privacy-friendly page-view analytics.
|
|
3
|
+
*
|
|
4
|
+
* Counts are stored in localStorage so the `/analytics` dashboard works
|
|
5
|
+
* with zero backend. When `config.analytics.endpoint` is set, each view is
|
|
6
|
+
* also beaconed to that URL for server-side aggregation.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface PageViewRecord {
|
|
10
|
+
path: string;
|
|
11
|
+
views: number;
|
|
12
|
+
lastVisit: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const STORAGE_KEY = "create-docs:analytics";
|
|
16
|
+
|
|
17
|
+
export function trackPageView(path: string, endpoint?: string): void {
|
|
18
|
+
try {
|
|
19
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
20
|
+
const data: PageViewRecord[] = raw ? JSON.parse(raw) : [];
|
|
21
|
+
const rec = data.find((r) => r.path === path);
|
|
22
|
+
if (rec) {
|
|
23
|
+
rec.views += 1;
|
|
24
|
+
rec.lastVisit = new Date().toISOString();
|
|
25
|
+
} else {
|
|
26
|
+
data.push({ path, views: 1, lastVisit: new Date().toISOString() });
|
|
27
|
+
}
|
|
28
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
29
|
+
} catch {
|
|
30
|
+
// Storage unavailable (private mode, quota) — skip local tracking.
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (endpoint) {
|
|
34
|
+
try {
|
|
35
|
+
navigator.sendBeacon(endpoint, JSON.stringify({ path, t: Date.now() }));
|
|
36
|
+
} catch {
|
|
37
|
+
// Beacon unsupported — skip remote tracking.
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getPageViews(): PageViewRecord[] {
|
|
43
|
+
try {
|
|
44
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
45
|
+
if (!raw) return [];
|
|
46
|
+
const data = JSON.parse(raw) as PageViewRecord[];
|
|
47
|
+
return Array.isArray(data) ? data : [];
|
|
48
|
+
} catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function resetPageViews(): void {
|
|
54
|
+
try {
|
|
55
|
+
localStorage.removeItem(STORAGE_KEY);
|
|
56
|
+
} catch {
|
|
57
|
+
// ignore
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { ApiCollection } from "./config";
|
|
2
|
+
import type { ApiEndpoint } from "./types";
|
|
3
|
+
|
|
4
|
+
export interface EndpointChange {
|
|
5
|
+
type: "added" | "removed" | "changed";
|
|
6
|
+
collection: string;
|
|
7
|
+
endpoint: ApiEndpoint;
|
|
8
|
+
/** Human-readable descriptions of what changed. */
|
|
9
|
+
changes: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const key = (c: string, e: ApiEndpoint) => `${c}:${e.method} ${e.path}`;
|
|
13
|
+
|
|
14
|
+
function describeChanges(prev: ApiEndpoint, next: ApiEndpoint): string[] {
|
|
15
|
+
const changes: string[] = [];
|
|
16
|
+
|
|
17
|
+
if (prev.summary !== next.summary) changes.push("Summary updated");
|
|
18
|
+
if (prev.description !== next.description)
|
|
19
|
+
changes.push("Description updated");
|
|
20
|
+
if (prev.tag !== next.tag) changes.push("Tag changed");
|
|
21
|
+
|
|
22
|
+
const prevParams = new Map(
|
|
23
|
+
(prev.parameters ?? []).map((p) => [`${p.in}:${p.name}`, p]),
|
|
24
|
+
);
|
|
25
|
+
const nextParams = new Map(
|
|
26
|
+
(next.parameters ?? []).map((p) => [`${p.in}:${p.name}`, p]),
|
|
27
|
+
);
|
|
28
|
+
for (const [k, p] of nextParams) {
|
|
29
|
+
const old = prevParams.get(k);
|
|
30
|
+
if (!old) changes.push(`Parameter added: ${p.name} (${p.in})`);
|
|
31
|
+
else if (old.required !== p.required)
|
|
32
|
+
changes.push(
|
|
33
|
+
`Parameter ${p.name} is now ${p.required ? "required" : "optional"}`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
for (const [k, p] of prevParams) {
|
|
37
|
+
if (!nextParams.has(k))
|
|
38
|
+
changes.push(`Parameter removed: ${p.name} (${p.in})`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (
|
|
42
|
+
(prev.requestBody?.required ?? false) !==
|
|
43
|
+
(next.requestBody?.required ?? false)
|
|
44
|
+
)
|
|
45
|
+
changes.push(
|
|
46
|
+
next.requestBody?.required
|
|
47
|
+
? "Request body is now required"
|
|
48
|
+
: "Request body is now optional",
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const prevRes = new Set(Object.keys(prev.responses ?? {}));
|
|
52
|
+
const nextRes = new Set(Object.keys(next.responses ?? {}));
|
|
53
|
+
for (const s of nextRes) {
|
|
54
|
+
if (!prevRes.has(s)) changes.push(`Response added: ${s}`);
|
|
55
|
+
}
|
|
56
|
+
for (const s of prevRes) {
|
|
57
|
+
if (!nextRes.has(s)) changes.push(`Response removed: ${s}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return changes;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Diff two API collection sets (previous vs current) by method+path.
|
|
65
|
+
* Works uniformly for OpenAPI, oRPC, Elysia, and Nitro-derived collections
|
|
66
|
+
* since all adapters normalise to {@link ApiCollection}.
|
|
67
|
+
*/
|
|
68
|
+
export function diffApiCollections(
|
|
69
|
+
previous: ApiCollection[],
|
|
70
|
+
current: ApiCollection[],
|
|
71
|
+
): EndpointChange[] {
|
|
72
|
+
const prev = new Map<string, ApiEndpoint>();
|
|
73
|
+
for (const c of previous) {
|
|
74
|
+
for (const e of c.endpoints) prev.set(key(c.id, e), e);
|
|
75
|
+
}
|
|
76
|
+
const next = new Map<string, ApiEndpoint>();
|
|
77
|
+
for (const c of current) {
|
|
78
|
+
for (const e of c.endpoints) next.set(key(c.id, e), e);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const changes: EndpointChange[] = [];
|
|
82
|
+
for (const [k, e] of next) {
|
|
83
|
+
const col = k.slice(0, k.indexOf(":"));
|
|
84
|
+
const old = prev.get(k);
|
|
85
|
+
if (!old) {
|
|
86
|
+
changes.push({
|
|
87
|
+
type: "added",
|
|
88
|
+
collection: col,
|
|
89
|
+
endpoint: e,
|
|
90
|
+
changes: [],
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const diffs = describeChanges(old, e);
|
|
95
|
+
if (diffs.length) {
|
|
96
|
+
changes.push({
|
|
97
|
+
type: "changed",
|
|
98
|
+
collection: col,
|
|
99
|
+
endpoint: e,
|
|
100
|
+
changes: diffs,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
for (const [k, e] of prev) {
|
|
105
|
+
if (!next.has(k)) {
|
|
106
|
+
changes.push({
|
|
107
|
+
type: "removed",
|
|
108
|
+
collection: k.slice(0, k.indexOf(":")),
|
|
109
|
+
endpoint: e,
|
|
110
|
+
changes: [],
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return changes;
|
|
115
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { RouterProvider } from "@tanstack/solid-router";
|
|
2
|
+
import type { JSX } from "solid-js";
|
|
3
|
+
import { render } from "solid-js/web";
|
|
4
|
+
import type { DocsAppConfig } from "./config";
|
|
5
|
+
import { DocsProvider } from "./context";
|
|
6
|
+
import { setupPwa } from "./pwa";
|
|
7
|
+
import { createDocsRouter } from "./router";
|
|
8
|
+
import { initTheme } from "./theme";
|
|
9
|
+
import "./theme.css";
|
|
10
|
+
import "./markdown-content.css";
|
|
11
|
+
|
|
12
|
+
/** App root component factory — mount with your own render call. */
|
|
13
|
+
export function createDocsApp(config: DocsAppConfig): () => JSX.Element {
|
|
14
|
+
if (typeof document !== "undefined") initTheme(config.theme?.defaultMode);
|
|
15
|
+
setupPwa(config);
|
|
16
|
+
const router = createDocsRouter(config);
|
|
17
|
+
return () => (
|
|
18
|
+
<DocsProvider config={config}>
|
|
19
|
+
<RouterProvider router={router} />
|
|
20
|
+
</DocsProvider>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** One-call mount. Looks up `#root` when no element is given. */
|
|
25
|
+
export function mountDocsApp(
|
|
26
|
+
config: DocsAppConfig,
|
|
27
|
+
el?: HTMLElement | null,
|
|
28
|
+
): void {
|
|
29
|
+
const root = el ?? document.getElementById("root");
|
|
30
|
+
if (!root) throw new Error("create-docs: root element not found");
|
|
31
|
+
render(createDocsApp(config), root);
|
|
32
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { createSignal, For, onCleanup, onMount, Show } from "solid-js";
|
|
2
|
+
import { useAccent } from "../theme";
|
|
3
|
+
|
|
4
|
+
export function AccentPicker() {
|
|
5
|
+
const { accent, accentIndex, accents, setAccent } = useAccent();
|
|
6
|
+
const [open, setOpen] = createSignal(false);
|
|
7
|
+
let rootEl: HTMLDivElement | undefined;
|
|
8
|
+
|
|
9
|
+
const onDocClick = (e: MouseEvent) => {
|
|
10
|
+
if (rootEl && !rootEl.contains(e.target as Node)) setOpen(false);
|
|
11
|
+
};
|
|
12
|
+
const onKey = (e: KeyboardEvent) => {
|
|
13
|
+
if (e.key === "Escape") setOpen(false);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
onMount(() => {
|
|
17
|
+
document.addEventListener("click", onDocClick);
|
|
18
|
+
document.addEventListener("keydown", onKey);
|
|
19
|
+
});
|
|
20
|
+
onCleanup(() => {
|
|
21
|
+
document.removeEventListener("click", onDocClick);
|
|
22
|
+
document.removeEventListener("keydown", onKey);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div ref={rootEl} class="relative">
|
|
27
|
+
<button
|
|
28
|
+
type="button"
|
|
29
|
+
onClick={() => setOpen(!open())}
|
|
30
|
+
aria-expanded={open()}
|
|
31
|
+
aria-haspopup="listbox"
|
|
32
|
+
aria-label="Accent color"
|
|
33
|
+
title={`Accent: ${accents[accentIndex()].name}`}
|
|
34
|
+
class="w-9 h-9 inline-flex items-center justify-center rounded-md border border-border text-muted hover:text-foreground hover:bg-surface transition-colors cursor-pointer"
|
|
35
|
+
>
|
|
36
|
+
<span
|
|
37
|
+
class="w-4 h-4 rounded-full border border-border"
|
|
38
|
+
style={{
|
|
39
|
+
"background-color": accents[accentIndex()].swatch,
|
|
40
|
+
}}
|
|
41
|
+
aria-hidden="true"
|
|
42
|
+
/>
|
|
43
|
+
</button>
|
|
44
|
+
<Show when={open()}>
|
|
45
|
+
<div
|
|
46
|
+
role="listbox"
|
|
47
|
+
aria-label="Accent color"
|
|
48
|
+
class="absolute right-0 top-full mt-2 w-56 rounded-lg border border-border bg-surface shadow-lg p-2 z-50"
|
|
49
|
+
>
|
|
50
|
+
<div class="px-2 pb-1.5 text-[10px] uppercase tracking-wider font-semibold text-muted">
|
|
51
|
+
Daily accent
|
|
52
|
+
</div>
|
|
53
|
+
<button
|
|
54
|
+
type="button"
|
|
55
|
+
role="option"
|
|
56
|
+
aria-selected={accent() === "auto"}
|
|
57
|
+
onClick={() => {
|
|
58
|
+
setAccent("auto");
|
|
59
|
+
setOpen(false);
|
|
60
|
+
}}
|
|
61
|
+
class={`w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors cursor-pointer border-none ${
|
|
62
|
+
accent() === "auto"
|
|
63
|
+
? "bg-primary/10 text-primary"
|
|
64
|
+
: "bg-transparent text-foreground hover:bg-background"
|
|
65
|
+
}`}
|
|
66
|
+
>
|
|
67
|
+
<span class="i-mdi:calendar-sync text-base" aria-hidden="true" />
|
|
68
|
+
<span class="flex-1 text-left">Auto — daily</span>
|
|
69
|
+
<Show when={accent() === "auto"}>
|
|
70
|
+
<span class="i-mdi:check text-primary" aria-hidden="true" />
|
|
71
|
+
</Show>
|
|
72
|
+
</button>
|
|
73
|
+
<div class="my-1.5 h-px bg-border" aria-hidden="true" />
|
|
74
|
+
<ul class="list-none m-0 p-0 grid grid-cols-1">
|
|
75
|
+
<For each={accents}>
|
|
76
|
+
{(a, i) => (
|
|
77
|
+
<li>
|
|
78
|
+
<button
|
|
79
|
+
type="button"
|
|
80
|
+
role="option"
|
|
81
|
+
aria-selected={accent() === i()}
|
|
82
|
+
onClick={() => {
|
|
83
|
+
setAccent(i());
|
|
84
|
+
setOpen(false);
|
|
85
|
+
}}
|
|
86
|
+
class={`w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors cursor-pointer border-none ${
|
|
87
|
+
accent() === i()
|
|
88
|
+
? "bg-primary/10 text-primary"
|
|
89
|
+
: "bg-transparent text-foreground hover:bg-background"
|
|
90
|
+
}`}
|
|
91
|
+
>
|
|
92
|
+
<span
|
|
93
|
+
class="w-4 h-4 rounded-full border border-border shrink-0"
|
|
94
|
+
style={{ "background-color": a.swatch }}
|
|
95
|
+
aria-hidden="true"
|
|
96
|
+
/>
|
|
97
|
+
<span class="flex-1 text-left truncate">{a.name}</span>
|
|
98
|
+
<span class="text-[10px] text-muted">{a.day}</span>
|
|
99
|
+
<Show when={accent() === i()}>
|
|
100
|
+
<span
|
|
101
|
+
class="i-mdi:check text-primary"
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
/>
|
|
104
|
+
</Show>
|
|
105
|
+
</button>
|
|
106
|
+
</li>
|
|
107
|
+
)}
|
|
108
|
+
</For>
|
|
109
|
+
</ul>
|
|
110
|
+
</div>
|
|
111
|
+
</Show>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|