@takazudo/zudo-doc 0.2.21 → 1.0.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/bin/gen-z-index.mjs +164 -0
- package/bin/tags-audit-runner.ts +126 -0
- package/bin/tags-audit.mjs +64 -0
- package/bin/zudo-doc.mjs +83 -0
- package/dist/ai-chat-modal/index.d.ts +8 -0
- package/dist/ai-chat-modal/index.js +243 -0
- package/dist/body-foot-util/body-foot-util-area.d.ts +4 -8
- package/dist/body-foot-util/edit-link.d.ts +4 -8
- package/dist/body-foot-util/index.d.ts +4 -3
- package/dist/breadcrumb/breadcrumb.d.ts +5 -9
- package/dist/breadcrumb/find-path.d.ts +2 -5
- package/dist/breadcrumb/index.d.ts +4 -4
- package/dist/breadcrumb/types.d.ts +3 -5
- package/dist/category-nav/index.d.ts +86 -0
- package/dist/category-nav/index.js +44 -0
- package/dist/category-tree-nav/index.d.ts +65 -0
- package/dist/category-tree-nav/index.js +24 -0
- package/dist/code-group/index.d.ts +10 -0
- package/dist/code-group/index.js +37 -0
- package/dist/code-syntax/code-block-enhancer-script.d.ts +1 -3
- package/dist/code-syntax/code-block-enhancer.d.ts +3 -6
- package/dist/code-syntax/index.d.ts +9 -8
- package/dist/code-syntax/mermaid-init-script.d.ts +3 -5
- package/dist/code-syntax/mermaid-init.d.ts +4 -7
- package/dist/code-syntax/tabs-init-script.d.ts +1 -3
- package/dist/code-syntax/tabs-init.d.ts +3 -6
- package/dist/code-syntax/tabs.d.ts +4 -7
- package/dist/color-scheme-utils.d.ts +125 -0
- package/dist/color-scheme-utils.js +151 -0
- package/dist/compose-meta-title/index.d.ts +7 -0
- package/dist/compose-meta-title/index.js +10 -0
- package/dist/content/component-map.d.ts +13 -17
- package/dist/content/content-blockquote.d.ts +3 -6
- package/dist/content/content-code.d.ts +3 -6
- package/dist/content/content-code.js +1 -1
- package/dist/content/content-link.d.ts +3 -6
- package/dist/content/content-link.js +1 -1
- package/dist/content/content-ol.d.ts +3 -6
- package/dist/content/content-paragraph.d.ts +3 -6
- package/dist/content/content-strong.d.ts +3 -6
- package/dist/content/content-table.d.ts +3 -6
- package/dist/content/content-ul.d.ts +3 -6
- package/dist/content/heading-h2.d.ts +3 -6
- package/dist/content/heading-h3.d.ts +3 -6
- package/dist/content/heading-h4.d.ts +3 -6
- package/dist/content/index.d.ts +29 -13
- package/dist/content-admonition/index.d.ts +15 -0
- package/dist/content-admonition/index.js +14 -0
- package/dist/design-token-panel-bootstrap.d.ts +38 -0
- package/dist/design-token-panel-bootstrap.js +29 -0
- package/dist/desktop-sidebar-toggle-island/index.d.ts +5 -0
- package/dist/desktop-sidebar-toggle-island/index.js +62 -0
- package/dist/details/details.d.ts +3 -7
- package/dist/details/index.d.ts +2 -2
- package/dist/doc-body-end/index.d.ts +21 -0
- package/dist/doc-body-end/index.js +15 -0
- package/dist/doc-content-header/index.d.ts +69 -0
- package/dist/doc-content-header/index.js +49 -0
- package/dist/doc-history/index.d.ts +10 -0
- package/dist/doc-history/index.js +407 -0
- package/dist/doc-history-area/index.d.ts +84 -0
- package/dist/doc-history-area/index.js +76 -0
- package/dist/doc-metainfo-area/index.d.ts +46 -0
- package/dist/doc-metainfo-area/index.js +41 -0
- package/dist/doc-page-props/index.d.ts +93 -0
- package/dist/doc-page-props/index.js +0 -0
- package/dist/doc-page-renderer/index.d.ts +128 -0
- package/dist/doc-page-renderer/index.js +103 -0
- package/dist/doc-page-shell/index.d.ts +136 -0
- package/dist/doc-page-shell/index.js +127 -0
- package/dist/doc-pager/index.d.ts +25 -0
- package/dist/doc-pager/index.js +41 -0
- package/dist/doc-route-entries/index.d.ts +125 -0
- package/dist/doc-route-entries/index.js +76 -0
- package/dist/doc-route-paths/index.d.ts +54 -0
- package/dist/doc-route-paths/index.js +61 -0
- package/dist/doc-tags-area/index.d.ts +38 -0
- package/dist/doc-tags-area/index.js +30 -0
- package/dist/doclayout/anchors.d.ts +7 -9
- package/dist/doclayout/doc-layout-with-defaults.d.ts +16 -12
- package/dist/doclayout/doc-layout.d.ts +5 -9
- package/dist/doclayout/index.d.ts +6 -6
- package/dist/eject/index.d.ts +33 -0
- package/dist/eject/index.js +229 -0
- package/dist/extract-headings/index.d.ts +43 -0
- package/dist/extract-headings/index.js +111 -0
- package/dist/factory-context/index.d.ts +80 -0
- package/dist/factory-context/index.js +0 -0
- package/dist/features.css +882 -0
- package/dist/footer/footer.d.ts +4 -8
- package/dist/footer/index.d.ts +13 -3
- package/dist/footer/types.d.ts +4 -6
- package/dist/footer-with-defaults/index.d.ts +63 -0
- package/dist/footer-with-defaults/index.js +128 -0
- package/dist/frontmatter-preview-data/index.d.ts +26 -0
- package/dist/frontmatter-preview-data/index.js +23 -0
- package/dist/github-helpers/index.d.ts +17 -0
- package/dist/github-helpers/index.js +16 -0
- package/dist/head/doc-head.d.ts +3 -6
- package/dist/head/index.d.ts +9 -5
- package/dist/head/og-tags.d.ts +4 -7
- package/dist/head/twitter-card.d.ts +3 -6
- package/dist/head/types.d.ts +1 -3
- package/dist/head-with-defaults/index.d.ts +49 -0
- package/dist/head-with-defaults/index.js +62 -0
- package/dist/header/header.d.ts +6 -10
- package/dist/header/index.d.ts +16 -6
- package/dist/header/nav-active.d.ts +5 -7
- package/dist/header/nav-overflow-script.d.ts +1 -3
- package/dist/header/right-items.d.ts +3 -6
- package/dist/header/types.d.ts +10 -12
- package/dist/header-with-defaults/index.d.ts +77 -0
- package/dist/header-with-defaults/index.js +163 -0
- package/dist/html-preview-wrapper/dedent.d.ts +1 -3
- package/dist/html-preview-wrapper/highlighted-code.d.ts +3 -7
- package/dist/html-preview-wrapper/html-preview-wrapper.d.ts +6 -10
- package/dist/html-preview-wrapper/html-preview.d.ts +5 -9
- package/dist/html-preview-wrapper/index.d.ts +8 -5
- package/dist/html-preview-wrapper/preflight.d.ts +1 -3
- package/dist/html-preview-wrapper/preview-base.d.ts +4 -8
- package/dist/i18n-version/index.d.ts +7 -5
- package/dist/i18n-version/language-switcher.d.ts +5 -8
- package/dist/i18n-version/types.d.ts +3 -5
- package/dist/i18n-version/version-banner.d.ts +4 -8
- package/dist/i18n-version/version-switcher.d.ts +7 -10
- package/dist/icons/index.d.ts +9 -13
- package/dist/image-enlarge/index.d.ts +13 -0
- package/dist/image-enlarge/index.js +176 -0
- package/dist/index.d.ts +26 -2
- package/dist/inline-version-switcher/index.d.ts +31 -0
- package/dist/inline-version-switcher/index.js +44 -0
- package/dist/integrations/claude-resources/escape-for-mdx.d.ts +1 -3
- package/dist/integrations/claude-resources/generate.d.ts +2 -4
- package/dist/integrations/claude-resources/index.d.ts +7 -10
- package/dist/integrations/doc-history/index.d.ts +21 -24
- package/dist/integrations/doc-history/pre-build.d.ts +7 -9
- package/dist/integrations/llms-txt/dev-middleware.d.ts +7 -11
- package/dist/integrations/llms-txt/emit.d.ts +2 -7
- package/dist/integrations/llms-txt/generate.d.ts +3 -8
- package/dist/integrations/llms-txt/index.d.ts +33 -8
- package/dist/integrations/llms-txt/load.d.ts +3 -7
- package/dist/integrations/llms-txt/strip.d.ts +2 -6
- package/dist/integrations/llms-txt/types.d.ts +9 -13
- package/dist/integrations/search-index/build-emitter.d.ts +4 -7
- package/dist/integrations/search-index/collect.d.ts +2 -5
- package/dist/integrations/search-index/dev-middleware.d.ts +5 -8
- package/dist/integrations/search-index/index.d.ts +7 -5
- package/dist/integrations/search-index/types.d.ts +5 -7
- package/dist/island-types/index.d.ts +44 -0
- package/dist/island-types/index.js +12 -0
- package/dist/locale-merge/index.d.ts +114 -0
- package/dist/locale-merge/index.js +27 -0
- package/dist/math-block/index.d.ts +18 -0
- package/dist/math-block/index.js +29 -0
- package/dist/md-utils/index.d.ts +6 -8
- package/dist/md-utils/index.js +2 -1
- package/dist/mdx-components/index.d.ts +46 -0
- package/dist/mdx-components/index.js +161 -0
- package/dist/mermaid-enlarge/index.d.ts +11 -0
- package/dist/mermaid-enlarge/index.js +290 -0
- package/dist/metainfo/doc-metainfo.d.ts +5 -9
- package/dist/metainfo/doc-tags.d.ts +7 -11
- package/dist/metainfo/frontmatter-preview.d.ts +8 -12
- package/dist/metainfo/index.d.ts +6 -4
- package/dist/nav-data-prep/index.d.ts +72 -0
- package/dist/nav-data-prep/index.js +34 -0
- package/dist/nav-indexing/category-nav.d.ts +4 -8
- package/dist/nav-indexing/category-tree-nav.d.ts +4 -8
- package/dist/nav-indexing/doc-card-grid.d.ts +4 -8
- package/dist/nav-indexing/docs-sitemap.d.ts +4 -8
- package/dist/nav-indexing/index.d.ts +37 -10
- package/dist/nav-indexing/nav-card-grid.d.ts +4 -8
- package/dist/nav-indexing/site-tree-nav-demo.d.ts +4 -8
- package/dist/nav-indexing/tag-nav.d.ts +6 -10
- package/dist/nav-indexing/types.d.ts +6 -8
- package/dist/nav-indexing/versions-page-content.d.ts +4 -8
- package/dist/nav-scope/index.d.ts +41 -0
- package/dist/nav-scope/index.js +41 -0
- package/dist/nav-source-cache/index.d.ts +13 -0
- package/dist/nav-source-cache/index.js +28 -0
- package/dist/nav-source-docs/index.d.ts +169 -0
- package/dist/nav-source-docs/index.js +123 -0
- package/dist/page-loading/index.d.ts +1 -2
- package/dist/page-loading/page-loading-overlay.d.ts +4 -8
- package/dist/plugins/claude-resources.d.ts +3 -0
- package/dist/plugins/claude-resources.js +27 -0
- package/dist/plugins/connect-adapter.d.ts +31 -0
- package/dist/plugins/connect-adapter.js +78 -0
- package/dist/plugins/doc-history.d.ts +3 -0
- package/dist/plugins/doc-history.js +54 -0
- package/dist/plugins/llms-txt.d.ts +3 -0
- package/dist/plugins/llms-txt.js +48 -0
- package/dist/plugins/plugin-utils.d.ts +17 -0
- package/dist/plugins/plugin-utils.js +11 -0
- package/dist/plugins/routes.d.ts +2 -0
- package/dist/plugins/routes.js +100 -0
- package/dist/plugins/search-index.d.ts +3 -0
- package/dist/plugins/search-index.js +24 -0
- package/dist/preset.d.ts +198 -0
- package/dist/preset.js +185 -0
- package/dist/render-markdown/index.d.ts +5 -0
- package/dist/render-markdown/index.js +115 -0
- package/dist/robots.d.ts +36 -0
- package/dist/robots.js +17 -0
- package/dist/route-enumerators/index.d.ts +204 -0
- package/dist/route-enumerators/index.js +156 -0
- package/dist/routes/404.d.ts +7 -0
- package/dist/routes/404.js +47 -0
- package/dist/routes/_chrome.d.ts +32 -0
- package/dist/routes/_chrome.js +365 -0
- package/dist/routes/_context.d.ts +56 -0
- package/dist/routes/_context.js +222 -0
- package/dist/routes/_docs-helpers.d.ts +43 -0
- package/dist/routes/_docs-helpers.js +173 -0
- package/dist/routes/_virtual.d.js +0 -0
- package/dist/routes/api-ai-chat.d.ts +5 -0
- package/dist/routes/api-ai-chat.js +24 -0
- package/dist/routes/docs-slug.d.ts +21 -0
- package/dist/routes/docs-slug.js +26 -0
- package/dist/routes/docs-tags-index.d.ts +7 -0
- package/dist/routes/docs-tags-index.js +11 -0
- package/dist/routes/docs-tags-tag.d.ts +23 -0
- package/dist/routes/docs-tags-tag.js +19 -0
- package/dist/routes/docs-versions.d.ts +7 -0
- package/dist/routes/docs-versions.js +11 -0
- package/dist/routes/index.d.ts +7 -0
- package/dist/routes/index.js +117 -0
- package/dist/routes/locale-docs-slug.d.ts +27 -0
- package/dist/routes/locale-docs-slug.js +40 -0
- package/dist/routes/locale-docs-tags-index.d.ts +18 -0
- package/dist/routes/locale-docs-tags-index.js +15 -0
- package/dist/routes/locale-docs-tags-tag.d.ts +25 -0
- package/dist/routes/locale-docs-tags-tag.js +22 -0
- package/dist/routes/locale-docs-versions.d.ts +18 -0
- package/dist/routes/locale-docs-versions.js +15 -0
- package/dist/routes/locale-index.d.ts +24 -0
- package/dist/routes/locale-index.js +132 -0
- package/dist/routes/robots.txt.d.ts +3 -0
- package/dist/routes/robots.txt.js +8 -0
- package/dist/routes/sitemap.xml.d.ts +5 -0
- package/dist/routes/sitemap.xml.js +31 -0
- package/dist/routes/v-docs-slug.d.ts +27 -0
- package/dist/routes/v-docs-slug.js +34 -0
- package/dist/routes/v-locale-docs-slug.d.ts +31 -0
- package/dist/routes/v-locale-docs-slug.js +50 -0
- package/dist/safelist.css +1 -1
- package/dist/search-widget/index.d.ts +38 -0
- package/dist/search-widget/index.js +169 -0
- package/dist/search-widget-script/index.d.ts +18 -0
- package/dist/search-widget-script/index.js +465 -0
- package/dist/settings.d.ts +270 -0
- package/dist/settings.js +0 -0
- package/dist/sidebar/index.d.ts +21 -3
- package/dist/sidebar/sidebar.d.ts +5 -9
- package/dist/sidebar/types.d.ts +4 -6
- package/dist/sidebar-prepaint/index.d.ts +21 -0
- package/dist/sidebar-prepaint/index.js +23 -0
- package/dist/sidebar-resizer/index.d.ts +2 -6
- package/dist/sidebar-resizer/sidebar-resizer-init.d.ts +6 -10
- package/dist/sidebar-toggle-island/index.d.ts +13 -0
- package/dist/sidebar-toggle-island/index.js +122 -0
- package/dist/sidebar-tree/build-tree.d.ts +4 -8
- package/dist/sidebar-tree/category-meta.d.ts +3 -6
- package/dist/sidebar-tree/index.d.ts +18 -3
- package/dist/sidebar-tree/types.d.ts +6 -8
- package/dist/sidebar-tree-island/index.d.ts +13 -0
- package/dist/sidebar-tree-island/index.js +454 -0
- package/dist/sidebar-utils/index.d.ts +64 -0
- package/dist/sidebar-utils/index.js +88 -0
- package/dist/sidebar-with-defaults/index.d.ts +38 -0
- package/dist/sidebar-with-defaults/index.js +45 -0
- package/dist/site-tree-nav/index.d.ts +71 -0
- package/dist/site-tree-nav/index.js +36 -0
- package/dist/site-tree-nav-island/index.d.ts +11 -0
- package/dist/site-tree-nav-island/index.js +176 -0
- package/dist/slug/index.d.ts +4 -0
- package/dist/slug/index.js +19 -0
- package/dist/smart-break/index.d.ts +55 -0
- package/dist/{toc/smart-break.js → smart-break/index.js} +30 -3
- package/dist/tab-item/index.d.ts +2 -2
- package/dist/tab-item/tab-item.d.ts +6 -7
- package/dist/tag-helpers/index.d.ts +40 -0
- package/dist/tag-helpers/index.js +54 -0
- package/dist/tag-pages/index.d.ts +114 -0
- package/dist/tag-pages/index.js +146 -0
- package/dist/tags-audit.d.ts +107 -0
- package/dist/tags-audit.js +321 -0
- package/dist/theme/color-scheme-provider.d.ts +4 -8
- package/dist/theme/color-tweak-export-modal.d.ts +4 -7
- package/dist/theme/design-token-serde.d.ts +14 -17
- package/dist/theme/design-token-types.d.ts +4 -6
- package/dist/theme/iframe-bridge.d.ts +12 -14
- package/dist/theme/index.d.ts +7 -9
- package/dist/theme/theme-toggle.d.ts +3 -8
- package/dist/theme-toggle/color-scheme-sync.d.ts +5 -7
- package/dist/theme-toggle/index.d.ts +4 -8
- package/dist/toc/cx.d.ts +2 -3
- package/dist/toc/index.d.ts +8 -6
- package/dist/toc/mobile-toc.d.ts +5 -9
- package/dist/toc/mobile-toc.js +1 -1
- package/dist/toc/toc-title.d.ts +1 -3
- package/dist/toc/toc.d.ts +5 -9
- package/dist/toc/toc.js +1 -1
- package/dist/toc/types.d.ts +1 -3
- package/dist/toc/use-active-heading.d.ts +4 -7
- package/dist/transitions/index.d.ts +1 -1
- package/dist/transitions/page-events.d.ts +4 -6
- package/dist/tree-nav-shared/index.d.ts +22 -0
- package/dist/tree-nav-shared/index.js +66 -0
- package/dist/url-helpers/index.d.ts +37 -0
- package/dist/url-helpers/index.js +106 -0
- package/dist/url-normalizer/index.d.ts +39 -1
- package/dist/url-normalizer/normalize.d.ts +3 -5
- package/dist/use-modal-dialog/index.d.ts +70 -0
- package/dist/use-modal-dialog/index.js +73 -0
- package/dist/versions-page/index.d.ts +55 -0
- package/dist/versions-page/index.js +73 -0
- package/eject/breadcrumb/breadcrumb.tsx +194 -0
- package/eject/breadcrumb/find-path.ts +26 -0
- package/eject/breadcrumb/index.ts +4 -0
- package/eject/breadcrumb/types.ts +32 -0
- package/eject/code-group/index.tsx +78 -0
- package/eject/content-admonition/index.tsx +50 -0
- package/eject/details/details.tsx +34 -0
- package/eject/details/index.ts +4 -0
- package/eject/doc-pager/index.tsx +91 -0
- package/eject/footer/footer.tsx +167 -0
- package/eject/footer/index.ts +19 -0
- package/eject/footer/types.ts +60 -0
- package/eject/header/header.tsx +725 -0
- package/eject/header/index.ts +36 -0
- package/eject/header/nav-active.ts +122 -0
- package/eject/header/nav-overflow-script.ts +302 -0
- package/eject/header/right-items.ts +78 -0
- package/eject/header/types.ts +69 -0
- package/eject/page-loading/index.ts +10 -0
- package/eject/page-loading/page-loading-overlay.tsx +103 -0
- package/eject/sidebar/index.ts +27 -0
- package/eject/sidebar/sidebar.tsx +137 -0
- package/eject/sidebar/types.ts +73 -0
- package/eject/tab-item/index.ts +7 -0
- package/eject/tab-item/tab-item.tsx +70 -0
- package/eject/theme-toggle/color-scheme-sync.ts +73 -0
- package/eject/theme-toggle/index.tsx +120 -0
- package/eject/toc/cx.ts +38 -0
- package/eject/toc/index.ts +11 -0
- package/eject/toc/mobile-toc.tsx +133 -0
- package/eject/toc/toc-title.ts +33 -0
- package/eject/toc/toc.tsx +119 -0
- package/eject/toc/types.ts +14 -0
- package/eject/toc/use-active-heading.ts +162 -0
- package/package.json +371 -11
- package/routes-src/404.tsx +49 -0
- package/routes-src/_chrome.tsx +470 -0
- package/routes-src/_context.ts +314 -0
- package/routes-src/_docs-helpers.ts +284 -0
- package/routes-src/_virtual.d.ts +15 -0
- package/routes-src/api-ai-chat.tsx +46 -0
- package/routes-src/docs-slug.tsx +43 -0
- package/routes-src/docs-tags-index.tsx +14 -0
- package/routes-src/docs-tags-tag.tsx +29 -0
- package/routes-src/docs-versions.tsx +15 -0
- package/routes-src/index.tsx +134 -0
- package/routes-src/locale-docs-slug.tsx +69 -0
- package/routes-src/locale-docs-tags-index.tsx +23 -0
- package/routes-src/locale-docs-tags-tag.tsx +39 -0
- package/routes-src/locale-docs-versions.tsx +23 -0
- package/routes-src/locale-index.tsx +150 -0
- package/routes-src/robots.txt.tsx +13 -0
- package/routes-src/sitemap.xml.tsx +48 -0
- package/routes-src/v-docs-slug.tsx +64 -0
- package/routes-src/v-locale-docs-slug.tsx +85 -0
- package/dist/toc/smart-break.d.ts +0 -27
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @takazudo/zudo-doc/bin/gen-z-index.mjs
|
|
3
|
+
//
|
|
4
|
+
// Package bin: regenerate (or check) the GENERATED:Z_INDEX marker block inside
|
|
5
|
+
// a project's src/styles/global.css from the single source of truth in
|
|
6
|
+
// src/config/z-index-tokens.ts.
|
|
7
|
+
//
|
|
8
|
+
// Reads from the project root (process.cwd()). Conventional paths:
|
|
9
|
+
// - Tokens: src/config/z-index-tokens.ts
|
|
10
|
+
// - CSS: src/styles/global.css
|
|
11
|
+
//
|
|
12
|
+
// Usage (after pnpm install, via scripts in package.json):
|
|
13
|
+
// gen-z-index # rewrite the @theme block in global.css
|
|
14
|
+
// gen-z-index --check # verify committed block is up to date (exit 1 on drift)
|
|
15
|
+
//
|
|
16
|
+
// MUST be run with the project root as cwd (it resolves source paths against
|
|
17
|
+
// process.cwd(), NOT against this file's location). The package.json scripts and
|
|
18
|
+
// b4push satisfy this; CI's no-install z-index job invokes the file by path
|
|
19
|
+
// from the repo root: `node packages/zudo-doc/bin/gen-z-index.mjs --check`.
|
|
20
|
+
//
|
|
21
|
+
// The block is a Tailwind v4 `@theme { --z-index-<name>: <value>; }` for every
|
|
22
|
+
// tier, so Tailwind generates `z-<name>` utilities and raw CSS can reference
|
|
23
|
+
// `z-index: var(--z-index-<name>)`.
|
|
24
|
+
//
|
|
25
|
+
// Pure Node (fs only — NO npm deps). Idempotent: running twice produces no diff.
|
|
26
|
+
//
|
|
27
|
+
// MAINTENANCE: edit src/config/z-index-tokens.ts (the source of truth), then run
|
|
28
|
+
// `pnpm gen:z-index` and commit the regenerated global.css. Never hand-edit the
|
|
29
|
+
// block between the BEGIN/END markers.
|
|
30
|
+
|
|
31
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
32
|
+
import { resolve } from "node:path";
|
|
33
|
+
|
|
34
|
+
// Project root is always the current working directory — this is a project bin,
|
|
35
|
+
// not a monorepo script.
|
|
36
|
+
const ROOT = process.cwd();
|
|
37
|
+
|
|
38
|
+
const TOKENS_PATH = resolve(ROOT, "src/config/z-index-tokens.ts");
|
|
39
|
+
const CSS_PATH = resolve(ROOT, "src/styles/global.css");
|
|
40
|
+
|
|
41
|
+
const BEGIN_MARKER = "GENERATED:Z_INDEX_BEGIN";
|
|
42
|
+
const END_MARKER = "GENERATED:Z_INDEX_END";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Parse the Z_INDEX_TIERS array out of z-index-tokens.ts WITHOUT importing it
|
|
46
|
+
* (this bin is a dependency-free .mjs and cannot resolve TypeScript). Reads
|
|
47
|
+
* each `{ name: "...", value: <n>, ... }` object literal. Throws on a malformed
|
|
48
|
+
* source so drift between the parser and the file surfaces loudly.
|
|
49
|
+
*/
|
|
50
|
+
function parseTiers(src) {
|
|
51
|
+
const arrayMatch = src.match(
|
|
52
|
+
/export const Z_INDEX_TIERS[^=]*=\s*\[([\s\S]*?)\];/,
|
|
53
|
+
);
|
|
54
|
+
if (!arrayMatch) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Could not locate "export const Z_INDEX_TIERS = [ ... ]" in ${TOKENS_PATH}`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const body = arrayMatch[1];
|
|
60
|
+
const tiers = [];
|
|
61
|
+
// Each tier is a `{ ... }` object literal; iterate top-level braces.
|
|
62
|
+
const objectRe = /\{([\s\S]*?)\}/g;
|
|
63
|
+
let m;
|
|
64
|
+
while ((m = objectRe.exec(body)) !== null) {
|
|
65
|
+
const obj = m[1];
|
|
66
|
+
const nameMatch = obj.match(/name:\s*"([^"]+)"/);
|
|
67
|
+
const valueMatch = obj.match(/value:\s*(-?\d+)/);
|
|
68
|
+
if (!nameMatch || !valueMatch) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Malformed tier object in Z_INDEX_TIERS (missing name/value): ${obj.trim()}`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
tiers.push({ name: nameMatch[1], value: Number(valueMatch[1]) });
|
|
74
|
+
}
|
|
75
|
+
if (tiers.length === 0) {
|
|
76
|
+
throw new Error(`Z_INDEX_TIERS in ${TOKENS_PATH} parsed to an empty list`);
|
|
77
|
+
}
|
|
78
|
+
return tiers;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Build the full generated block (markers included). Two leading spaces of
|
|
83
|
+
* indentation match the surrounding `@theme` style in global.css.
|
|
84
|
+
*/
|
|
85
|
+
function buildBlock(tiers) {
|
|
86
|
+
const lines = [];
|
|
87
|
+
lines.push(` /* ${BEGIN_MARKER}`);
|
|
88
|
+
lines.push(
|
|
89
|
+
` * GENERATED:Z_INDEX — do not hand-edit; run pnpm gen:z-index.`,
|
|
90
|
+
);
|
|
91
|
+
lines.push(
|
|
92
|
+
` * Source of truth: src/config/z-index-tokens.ts. Tailwind v4 reads the`,
|
|
93
|
+
);
|
|
94
|
+
lines.push(
|
|
95
|
+
` * --z-index-<name> theme key and generates a z-<name> utility. */`,
|
|
96
|
+
);
|
|
97
|
+
lines.push(` @theme {`);
|
|
98
|
+
for (const tier of tiers) {
|
|
99
|
+
lines.push(` --z-index-${tier.name}: ${tier.value};`);
|
|
100
|
+
}
|
|
101
|
+
lines.push(` }`);
|
|
102
|
+
lines.push(` /* ${END_MARKER} */`);
|
|
103
|
+
return lines.join("\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Replace the existing BEGIN…END block in `css` with `block`. Throws if the
|
|
108
|
+
* markers are missing (the block must be seeded once by hand — see global.css).
|
|
109
|
+
*/
|
|
110
|
+
function replaceBlock(css, block) {
|
|
111
|
+
const beginIdx = css.indexOf(BEGIN_MARKER);
|
|
112
|
+
const endIdx = css.indexOf(END_MARKER);
|
|
113
|
+
if (beginIdx === -1 || endIdx === -1) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Could not find ${BEGIN_MARKER} … ${END_MARKER} markers in ${CSS_PATH}.\n` +
|
|
116
|
+
`Seed the marker block once by hand, then re-run the generator.`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
// Expand to the full comment line that opens the block (" /* GENERATED:...")
|
|
120
|
+
// and to the end of the closing "*/ " line so the whole region is replaced.
|
|
121
|
+
const lineStart = css.lastIndexOf("\n", beginIdx) + 1;
|
|
122
|
+
const afterEnd = css.indexOf("\n", endIdx);
|
|
123
|
+
const lineEnd = afterEnd === -1 ? css.length : afterEnd;
|
|
124
|
+
return css.slice(0, lineStart) + block + css.slice(lineEnd);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function main() {
|
|
128
|
+
const check = process.argv.includes("--check");
|
|
129
|
+
|
|
130
|
+
const tokensSrc = readFileSync(TOKENS_PATH, "utf8");
|
|
131
|
+
const css = readFileSync(CSS_PATH, "utf8");
|
|
132
|
+
|
|
133
|
+
const tiers = parseTiers(tokensSrc);
|
|
134
|
+
const block = buildBlock(tiers);
|
|
135
|
+
const next = replaceBlock(css, block);
|
|
136
|
+
|
|
137
|
+
if (check) {
|
|
138
|
+
if (next !== css) {
|
|
139
|
+
console.error(
|
|
140
|
+
"z-index codegen drift detected: src/styles/global.css is out of date.",
|
|
141
|
+
);
|
|
142
|
+
console.error("Run `pnpm gen:z-index` and commit the result.");
|
|
143
|
+
return 1;
|
|
144
|
+
}
|
|
145
|
+
console.log(
|
|
146
|
+
`OK — z-index @theme block is up to date (${tiers.length} tiers).`,
|
|
147
|
+
);
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (next === css) {
|
|
152
|
+
console.log(
|
|
153
|
+
`z-index @theme block already up to date (${tiers.length} tiers); no change.`,
|
|
154
|
+
);
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
writeFileSync(CSS_PATH, next);
|
|
158
|
+
console.log(
|
|
159
|
+
`Wrote z-index @theme block to src/styles/global.css (${tiers.length} tiers).`,
|
|
160
|
+
);
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
process.exit(main());
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env -S tsx
|
|
2
|
+
/**
|
|
3
|
+
* @takazudo/zudo-doc/bin/tags-audit-runner.ts
|
|
4
|
+
*
|
|
5
|
+
* TypeScript runner for the tags-audit package bin. Spawned by
|
|
6
|
+
* bin/tags-audit.mjs via tsx so it can:
|
|
7
|
+
* 1. Import the compiled core logic from this package
|
|
8
|
+
* (@takazudo/zudo-doc/tags-audit).
|
|
9
|
+
* 2. Dynamically import the project's TypeScript config files
|
|
10
|
+
* (src/config/settings.ts, src/config/tag-vocabulary.ts)
|
|
11
|
+
* via tsx's TypeScript-aware loader.
|
|
12
|
+
*
|
|
13
|
+
* This file is published as-is in the package's `bin/` directory.
|
|
14
|
+
* Do NOT compile it — it is only ever run through tsx.
|
|
15
|
+
*
|
|
16
|
+
* Flags:
|
|
17
|
+
* --fix rewrite alias tags to canonical ids, byte-stable
|
|
18
|
+
* --ci force non-zero exit on any hard issue regardless of governance
|
|
19
|
+
* --json emit the report as JSON instead of colorized text
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { join, resolve } from "node:path";
|
|
23
|
+
|
|
24
|
+
import pc from "picocolors";
|
|
25
|
+
import pluralize from "pluralize";
|
|
26
|
+
import stringSimilarity from "string-similarity";
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
audit,
|
|
30
|
+
applyFixes,
|
|
31
|
+
computeRewrites,
|
|
32
|
+
formatTextReport,
|
|
33
|
+
hasHardIssues,
|
|
34
|
+
type AuditOptions,
|
|
35
|
+
} from "@takazudo/zudo-doc/tags-audit";
|
|
36
|
+
|
|
37
|
+
// ── Project root ───────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
const ROOT_DIR = process.cwd();
|
|
40
|
+
|
|
41
|
+
// ── Dynamic import of project-specific TypeScript config ───────────────────
|
|
42
|
+
// tsx (which runs this file) handles the TypeScript imports below.
|
|
43
|
+
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
const settingsModule = await import(resolve(ROOT_DIR, "src/config/settings.ts")) as any;
|
|
46
|
+
const settings = settingsModule.settings as {
|
|
47
|
+
docsDir: string;
|
|
48
|
+
tagGovernance: "off" | "warn" | "strict";
|
|
49
|
+
tagVocabulary: boolean;
|
|
50
|
+
locales?: Record<string, { label?: string; dir: string }>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
const vocabModule = await import(resolve(ROOT_DIR, "src/config/tag-vocabulary.ts")) as any;
|
|
55
|
+
const tagVocabulary: readonly { id: string; [k: string]: unknown }[] =
|
|
56
|
+
vocabModule.tagVocabulary ?? [];
|
|
57
|
+
|
|
58
|
+
// ── CLI flags ──────────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
const argv = process.argv.slice(2);
|
|
61
|
+
const flags = {
|
|
62
|
+
fix: argv.includes("--fix"),
|
|
63
|
+
ci: argv.includes("--ci"),
|
|
64
|
+
json: argv.includes("--json"),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ── Resolve content directories from settings ──────────────────────────────
|
|
68
|
+
|
|
69
|
+
const docsDir = join(ROOT_DIR, settings.docsDir);
|
|
70
|
+
const localeDirs = Object.values(settings.locales ?? {}).map((l) =>
|
|
71
|
+
join(ROOT_DIR, l.dir),
|
|
72
|
+
);
|
|
73
|
+
const contentDirs = [docsDir, ...localeDirs];
|
|
74
|
+
|
|
75
|
+
const vocabularyActive =
|
|
76
|
+
Boolean(settings.tagVocabulary) && settings.tagGovernance !== "off";
|
|
77
|
+
|
|
78
|
+
// ── Fix mode ───────────────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
if (flags.fix) {
|
|
81
|
+
const rewrites = computeRewrites(tagVocabulary);
|
|
82
|
+
const touched = await applyFixes(contentDirs, rewrites, ROOT_DIR);
|
|
83
|
+
if (flags.json) {
|
|
84
|
+
process.stdout.write(JSON.stringify({ fixed: touched }, null, 2) + "\n");
|
|
85
|
+
} else if (touched.length === 0) {
|
|
86
|
+
console.log(pc.green("✓ No alias rewrites needed"));
|
|
87
|
+
} else {
|
|
88
|
+
console.log(pc.green(`✓ Rewrote aliases in ${touched.length} file(s):`));
|
|
89
|
+
for (const f of touched) console.log(` ${f}`);
|
|
90
|
+
}
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Audit mode ─────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
const opts: AuditOptions = {
|
|
97
|
+
rootDir: ROOT_DIR,
|
|
98
|
+
contentDirs,
|
|
99
|
+
vocabulary: tagVocabulary,
|
|
100
|
+
governance: settings.tagGovernance,
|
|
101
|
+
vocabularyActive,
|
|
102
|
+
nearDupHelpers: {
|
|
103
|
+
singular: pluralize.singular,
|
|
104
|
+
compareTwoStrings: stringSimilarity.compareTwoStrings,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const report = await audit(opts);
|
|
109
|
+
|
|
110
|
+
if (flags.json) {
|
|
111
|
+
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
112
|
+
} else {
|
|
113
|
+
console.log(formatTextReport(report, settings.tagGovernance, vocabularyActive, pc));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const hardIssues = hasHardIssues(report);
|
|
117
|
+
if (hardIssues && (flags.ci || settings.tagGovernance === "strict")) {
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
if (hardIssues) {
|
|
121
|
+
console.error(
|
|
122
|
+
pc.yellow(
|
|
123
|
+
"Note: tag issues found but running in non-strict mode (exit 0). Use --ci to fail.",
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @takazudo/zudo-doc/bin/tags-audit.mjs
|
|
3
|
+
//
|
|
4
|
+
// Package bin: vocabulary-aware audit of frontmatter tags.
|
|
5
|
+
//
|
|
6
|
+
// Spawns tsx (from the project's node_modules) to run the TypeScript runner
|
|
7
|
+
// (bin/tags-audit-runner.ts) which imports the compiled core logic from this
|
|
8
|
+
// package plus the project's TypeScript config files (settings.ts,
|
|
9
|
+
// tag-vocabulary.ts) via dynamic import.
|
|
10
|
+
//
|
|
11
|
+
// Requires tsx in the project's devDependencies. pnpm puts ./node_modules/.bin
|
|
12
|
+
// in PATH for npm scripts, so `pnpm run tags:audit` always finds tsx.
|
|
13
|
+
//
|
|
14
|
+
// Usage (via package.json scripts):
|
|
15
|
+
// tags-audit # report only (exit 0 in warn mode)
|
|
16
|
+
// tags-audit --fix # rewrite alias tags in place
|
|
17
|
+
// tags-audit --ci # force exit 1 on any hard issue
|
|
18
|
+
// tags-audit --json # emit JSON report
|
|
19
|
+
|
|
20
|
+
import { spawnSync } from "node:child_process";
|
|
21
|
+
import { resolve, dirname } from "node:path";
|
|
22
|
+
import { fileURLToPath } from "node:url";
|
|
23
|
+
import { existsSync } from "node:fs";
|
|
24
|
+
|
|
25
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
|
|
27
|
+
// The TypeScript runner lives alongside this bin, published in the package.
|
|
28
|
+
// tsx treats it as a TypeScript file and handles the import graph.
|
|
29
|
+
const RUNNER_PATH = resolve(__dirname, "tags-audit-runner.ts");
|
|
30
|
+
|
|
31
|
+
// Locate tsx in the project's node_modules/.bin.
|
|
32
|
+
// Walk up from cwd to handle monorepo setups where node_modules is hoisted.
|
|
33
|
+
function findTsxBin() {
|
|
34
|
+
let dir = process.cwd();
|
|
35
|
+
for (let i = 0; i < 10; i++) {
|
|
36
|
+
const candidate = resolve(dir, "node_modules/.bin/tsx");
|
|
37
|
+
if (existsSync(candidate)) return candidate;
|
|
38
|
+
const parent = dirname(dir);
|
|
39
|
+
if (parent === dir) break;
|
|
40
|
+
dir = parent;
|
|
41
|
+
}
|
|
42
|
+
// Last resort: tsx on PATH (e.g. globally installed or nix-shell)
|
|
43
|
+
return "tsx";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const tsxBin = findTsxBin();
|
|
47
|
+
|
|
48
|
+
const result = spawnSync(tsxBin, [RUNNER_PATH, ...process.argv.slice(2)], {
|
|
49
|
+
stdio: "inherit",
|
|
50
|
+
env: process.env,
|
|
51
|
+
cwd: process.cwd(),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (result.error) {
|
|
55
|
+
// tsx binary not found or failed to spawn
|
|
56
|
+
process.stderr.write(
|
|
57
|
+
`\n[tags-audit] ERROR: Could not run tsx to execute the tags-audit runner.\n` +
|
|
58
|
+
` Ensure tsx is in your project's devDependencies and run \`pnpm install\`.\n` +
|
|
59
|
+
` tsx searched for: ${tsxBin}\n\n`,
|
|
60
|
+
);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
process.exit(result.status ?? 1);
|
package/bin/zudo-doc.mjs
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @takazudo/zudo-doc/bin/zudo-doc.mjs
|
|
3
|
+
//
|
|
4
|
+
// Package bin: `zudo-doc eject <component>` swizzle CLI.
|
|
5
|
+
//
|
|
6
|
+
// Self-contained ESM — runs on plain `node` with NO `tsx` requirement. The eject
|
|
7
|
+
// logic is imported from the package's COMPILED `../dist/eject/index.js`, and the
|
|
8
|
+
// only runtime deps are `minimist` + `picocolors` (declared deps of this package,
|
|
9
|
+
// so they are present transitively in any consumer's node_modules). This is the
|
|
10
|
+
// key difference from the tsx-runner pattern used by `bin/tags-audit.mjs`:
|
|
11
|
+
// tags-audit must load the *project's* TypeScript config files at runtime (hence
|
|
12
|
+
// tsx), whereas eject only copies files + rewrites imports — no TS eval needed —
|
|
13
|
+
// so it works in a default generated project that never installed tsx (#2367).
|
|
14
|
+
//
|
|
15
|
+
// Usage:
|
|
16
|
+
// zudo-doc eject <component> # eject a component's TS source into the project
|
|
17
|
+
// zudo-doc --help # show help
|
|
18
|
+
|
|
19
|
+
import minimist from "minimist";
|
|
20
|
+
import pc from "picocolors";
|
|
21
|
+
import { eject, EJECTABLE } from "../dist/eject/index.js";
|
|
22
|
+
|
|
23
|
+
const argv = minimist(process.argv.slice(2), {
|
|
24
|
+
boolean: ["help"],
|
|
25
|
+
alias: { h: "help" },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function printHelp() {
|
|
29
|
+
const validNames = Object.keys(EJECTABLE).sort().join(", ");
|
|
30
|
+
console.log(`
|
|
31
|
+
${pc.bold("Usage:")} zudo-doc <subcommand> [options]
|
|
32
|
+
|
|
33
|
+
${pc.bold("Subcommands:")}
|
|
34
|
+
eject <component> Copy a component's TS source into your project and
|
|
35
|
+
rewrite imports so it resolves locally.
|
|
36
|
+
|
|
37
|
+
${pc.bold("Ejectable components:")}
|
|
38
|
+
${validNames}
|
|
39
|
+
|
|
40
|
+
${pc.bold("Options:")}
|
|
41
|
+
-h, --help Show this help message
|
|
42
|
+
|
|
43
|
+
${pc.bold("Examples:")}
|
|
44
|
+
${pc.dim("# Eject the header component")}
|
|
45
|
+
zudo-doc eject header
|
|
46
|
+
|
|
47
|
+
${pc.dim("# Eject the theme-toggle component")}
|
|
48
|
+
zudo-doc eject theme-toggle
|
|
49
|
+
`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function main() {
|
|
53
|
+
if (argv["help"] || argv._.length === 0) {
|
|
54
|
+
printHelp();
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const [subcommand, componentArg] = argv._;
|
|
59
|
+
|
|
60
|
+
if (subcommand !== "eject") {
|
|
61
|
+
console.error(
|
|
62
|
+
pc.red(`Unknown subcommand "${subcommand}".`) +
|
|
63
|
+
`\nRun \`zudo-doc --help\` for usage.`,
|
|
64
|
+
);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!componentArg) {
|
|
69
|
+
console.error(
|
|
70
|
+
pc.red(`Missing component name.`) +
|
|
71
|
+
`\nUsage: zudo-doc eject <component>` +
|
|
72
|
+
`\nRun \`zudo-doc --help\` for the list of ejectable components.`,
|
|
73
|
+
);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
await eject(componentArg, { cwd: process.cwd() });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
main().catch((err) => {
|
|
81
|
+
console.error(pc.red(String(err)));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
});
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
3
|
+
import { useState, useEffect, useRef, useCallback, memo } from "preact/compat";
|
|
4
|
+
import { renderMarkdown } from "../render-markdown/index.js";
|
|
5
|
+
import { SmartBreak } from "../smart-break/index.js";
|
|
6
|
+
import { BEFORE_NAVIGATE_EVENT } from "../transitions/index.js";
|
|
7
|
+
import { useModalDialog } from "../use-modal-dialog/index.js";
|
|
8
|
+
function isAiChatResponse(data) {
|
|
9
|
+
return typeof data === "object" && data !== null;
|
|
10
|
+
}
|
|
11
|
+
const ChatMessageRow = memo(function ChatMessageRow2({ msg }) {
|
|
12
|
+
return /* @__PURE__ */ jsxs(
|
|
13
|
+
"div",
|
|
14
|
+
{
|
|
15
|
+
className: `mb-vsp-xs flex ${msg.role === "user" ? "justify-end" : "justify-start"}`,
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: msg.role === "user" ? "You: " : "Assistant: " }),
|
|
18
|
+
msg.role === "user" ? /* @__PURE__ */ jsx("div", { className: "max-w-[85%] rounded-t-[1rem] rounded-bl-[1rem] rounded-br-[0.25rem] bg-chat-user-bg px-hsp-md py-vsp-2xs text-small leading-relaxed text-chat-user-text", children: /* @__PURE__ */ jsx(SmartBreak, { children: msg.content }) }) : /* @__PURE__ */ jsx(
|
|
19
|
+
"div",
|
|
20
|
+
{
|
|
21
|
+
className: "ai-chat-md max-w-[85%] rounded-t-[1rem] rounded-br-[1rem] rounded-bl-[0.25rem] bg-chat-assistant-bg px-hsp-md py-vsp-2xs text-small leading-relaxed text-chat-assistant-text",
|
|
22
|
+
dangerouslySetInnerHTML: { __html: renderMarkdown(msg.content) }
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
function AiChatModal({ basePath }) {
|
|
30
|
+
const messagesEndRef = useRef(null);
|
|
31
|
+
const inputRef = useRef(null);
|
|
32
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
33
|
+
const [messages, setMessages] = useState([]);
|
|
34
|
+
const [input, setInput] = useState("");
|
|
35
|
+
const [loading, setLoading] = useState(false);
|
|
36
|
+
const [error, setError] = useState(null);
|
|
37
|
+
const resetState = useCallback(() => {
|
|
38
|
+
setIsOpen(false);
|
|
39
|
+
setMessages([]);
|
|
40
|
+
setInput("");
|
|
41
|
+
setError(null);
|
|
42
|
+
setLoading(false);
|
|
43
|
+
}, []);
|
|
44
|
+
const { dialogRef, handleBackdropClick } = useModalDialog({
|
|
45
|
+
isOpen,
|
|
46
|
+
onClose: resetState,
|
|
47
|
+
navigateEvent: BEFORE_NAVIGATE_EVENT,
|
|
48
|
+
backdropClickClose: true,
|
|
49
|
+
restoreFocusOnly: true
|
|
50
|
+
});
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
function handleToggle() {
|
|
53
|
+
const dialog = dialogRef.current;
|
|
54
|
+
if (!dialog || !dialog.isConnected) return;
|
|
55
|
+
if (dialog.open) {
|
|
56
|
+
dialog.close();
|
|
57
|
+
} else {
|
|
58
|
+
setIsOpen(true);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
window.addEventListener("toggle-ai-chat", handleToggle);
|
|
62
|
+
return () => window.removeEventListener("toggle-ai-chat", handleToggle);
|
|
63
|
+
}, [dialogRef]);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (isOpen) {
|
|
66
|
+
inputRef.current?.focus();
|
|
67
|
+
}
|
|
68
|
+
}, [isOpen]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
71
|
+
}, [messages, loading]);
|
|
72
|
+
const sendMessage = useCallback(async () => {
|
|
73
|
+
const trimmed = input.trim();
|
|
74
|
+
if (!trimmed || loading) return;
|
|
75
|
+
const userMessage = { role: "user", content: trimmed };
|
|
76
|
+
const updatedMessages = [...messages, userMessage];
|
|
77
|
+
setMessages(updatedMessages);
|
|
78
|
+
setInput("");
|
|
79
|
+
setError(null);
|
|
80
|
+
setLoading(true);
|
|
81
|
+
try {
|
|
82
|
+
const base = basePath.replace(/\/+$/, "");
|
|
83
|
+
const res = await fetch(`${base}/api/ai-chat`, {
|
|
84
|
+
method: "POST",
|
|
85
|
+
headers: { "Content-Type": "application/json" },
|
|
86
|
+
body: JSON.stringify({
|
|
87
|
+
message: trimmed,
|
|
88
|
+
history: messages
|
|
89
|
+
})
|
|
90
|
+
});
|
|
91
|
+
const raw = await res.json();
|
|
92
|
+
const data = isAiChatResponse(raw) ? raw : {};
|
|
93
|
+
if (!res.ok) {
|
|
94
|
+
setError(("error" in data && typeof data.error === "string" ? data.error : null) || "Something went wrong");
|
|
95
|
+
} else if ("response" in data && typeof data.response === "string" && data.response) {
|
|
96
|
+
setMessages((prev) => [
|
|
97
|
+
...prev,
|
|
98
|
+
{ role: "assistant", content: data.response }
|
|
99
|
+
]);
|
|
100
|
+
} else {
|
|
101
|
+
setError("Received an empty or invalid response");
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
setError("Failed to connect to the AI assistant");
|
|
105
|
+
} finally {
|
|
106
|
+
setLoading(false);
|
|
107
|
+
}
|
|
108
|
+
}, [input, loading, messages, basePath]);
|
|
109
|
+
function handleKeyDown(e) {
|
|
110
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
sendMessage();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return /* @__PURE__ */ jsx(
|
|
116
|
+
"dialog",
|
|
117
|
+
{
|
|
118
|
+
ref: dialogRef,
|
|
119
|
+
onClick: handleBackdropClick,
|
|
120
|
+
className: "z-modal m-0 h-dvh max-h-none w-dvw max-w-none border-none bg-surface p-0 text-fg backdrop:z-modal-backdrop backdrop:bg-bg/80 lg:m-auto lg:h-[90vh] lg:max-h-[90vh] lg:w-[90vw] lg:max-w-[52.5rem] lg:border-solid lg:border lg:border-fg",
|
|
121
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col", children: [
|
|
122
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-between border-b border-muted px-hsp-lg py-vsp-xs", children: [
|
|
123
|
+
/* @__PURE__ */ jsx("h2", { className: "text-title font-bold text-fg", children: "AI Assistant" }),
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
"button",
|
|
126
|
+
{
|
|
127
|
+
type: "button",
|
|
128
|
+
onClick: () => dialogRef.current?.close(),
|
|
129
|
+
className: "flex items-center justify-center text-muted transition-colors hover:text-fg",
|
|
130
|
+
"aria-label": "Close",
|
|
131
|
+
children: /* @__PURE__ */ jsxs(
|
|
132
|
+
"svg",
|
|
133
|
+
{
|
|
134
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
135
|
+
width: "20",
|
|
136
|
+
height: "20",
|
|
137
|
+
viewBox: "0 0 24 24",
|
|
138
|
+
fill: "none",
|
|
139
|
+
stroke: "currentColor",
|
|
140
|
+
strokeWidth: "2",
|
|
141
|
+
strokeLinecap: "round",
|
|
142
|
+
strokeLinejoin: "round",
|
|
143
|
+
"aria-hidden": "true",
|
|
144
|
+
children: [
|
|
145
|
+
/* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
146
|
+
/* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
)
|
|
152
|
+
] }),
|
|
153
|
+
/* @__PURE__ */ jsxs("div", { role: "log", "aria-label": "Chat messages", className: "flex-1 overflow-y-auto px-hsp-lg py-vsp-sm", children: [
|
|
154
|
+
messages.length === 0 && !loading && /* @__PURE__ */ jsx("p", { className: "py-vsp-xl text-center text-small text-muted", children: "Ask a question about the documentation." }),
|
|
155
|
+
messages.map((msg, i) => /* @__PURE__ */ jsx(ChatMessageRow, { msg }, i)),
|
|
156
|
+
/* @__PURE__ */ jsx(
|
|
157
|
+
"div",
|
|
158
|
+
{
|
|
159
|
+
"aria-live": "polite",
|
|
160
|
+
"aria-atomic": "true",
|
|
161
|
+
className: "sr-only",
|
|
162
|
+
children: (() => {
|
|
163
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
164
|
+
const msg = messages[i];
|
|
165
|
+
if (msg && msg.role === "assistant") return msg.content;
|
|
166
|
+
}
|
|
167
|
+
return "";
|
|
168
|
+
})()
|
|
169
|
+
}
|
|
170
|
+
),
|
|
171
|
+
loading && /* @__PURE__ */ jsx("div", { className: "mb-vsp-xs flex justify-start", children: /* @__PURE__ */ jsx(
|
|
172
|
+
"div",
|
|
173
|
+
{
|
|
174
|
+
role: "status",
|
|
175
|
+
className: "rounded-t-[1rem] rounded-br-[1rem] rounded-bl-[0.25rem] bg-chat-assistant-bg px-hsp-md py-vsp-2xs text-small text-muted",
|
|
176
|
+
children: "Thinking..."
|
|
177
|
+
}
|
|
178
|
+
) }),
|
|
179
|
+
error && /* @__PURE__ */ jsx(
|
|
180
|
+
"div",
|
|
181
|
+
{
|
|
182
|
+
role: "alert",
|
|
183
|
+
className: "mb-vsp-xs rounded-[0.75rem] border border-danger bg-bg px-hsp-md py-vsp-2xs text-small text-danger",
|
|
184
|
+
children: error
|
|
185
|
+
}
|
|
186
|
+
),
|
|
187
|
+
/* @__PURE__ */ jsx("div", { ref: messagesEndRef })
|
|
188
|
+
] }),
|
|
189
|
+
/* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-muted px-hsp-lg py-vsp-xs", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-hsp-sm", children: [
|
|
190
|
+
/* @__PURE__ */ jsx(
|
|
191
|
+
"input",
|
|
192
|
+
{
|
|
193
|
+
ref: inputRef,
|
|
194
|
+
type: "text",
|
|
195
|
+
value: input,
|
|
196
|
+
onChange: (e) => setInput(e.currentTarget.value),
|
|
197
|
+
onKeyDown: handleKeyDown,
|
|
198
|
+
disabled: loading,
|
|
199
|
+
"aria-label": "Type your message",
|
|
200
|
+
"aria-busy": loading,
|
|
201
|
+
placeholder: "Type your message...",
|
|
202
|
+
className: "flex-1 rounded-full border border-muted bg-bg px-hsp-lg py-vsp-2xs text-small text-fg placeholder:text-muted focus:border-accent focus:outline-none disabled:opacity-50"
|
|
203
|
+
}
|
|
204
|
+
),
|
|
205
|
+
/* @__PURE__ */ jsx(
|
|
206
|
+
"button",
|
|
207
|
+
{
|
|
208
|
+
type: "button",
|
|
209
|
+
onClick: sendMessage,
|
|
210
|
+
disabled: loading || !input.trim(),
|
|
211
|
+
"aria-busy": loading,
|
|
212
|
+
className: "flex h-[2rem] w-[2rem] shrink-0 items-center justify-center rounded-full bg-accent text-bg transition-colors hover:bg-accent-hover disabled:opacity-50",
|
|
213
|
+
"aria-label": "Send message",
|
|
214
|
+
children: /* @__PURE__ */ jsxs(
|
|
215
|
+
"svg",
|
|
216
|
+
{
|
|
217
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
218
|
+
width: "16",
|
|
219
|
+
height: "16",
|
|
220
|
+
viewBox: "0 0 24 24",
|
|
221
|
+
fill: "none",
|
|
222
|
+
stroke: "currentColor",
|
|
223
|
+
strokeWidth: "2",
|
|
224
|
+
strokeLinecap: "round",
|
|
225
|
+
strokeLinejoin: "round",
|
|
226
|
+
"aria-hidden": "true",
|
|
227
|
+
children: [
|
|
228
|
+
/* @__PURE__ */ jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
229
|
+
/* @__PURE__ */ jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
)
|
|
235
|
+
] }) })
|
|
236
|
+
] })
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
AiChatModal.displayName = "AiChatModal";
|
|
241
|
+
export {
|
|
242
|
+
AiChatModal
|
|
243
|
+
};
|