@takazudo/zudo-doc 3.2.0 → 4.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.
Files changed (439) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +2 -2
  3. package/bin/tag-cli-config.ts +126 -0
  4. package/bin/tags-audit-runner.ts +60 -76
  5. package/bin/tags-audit.mjs +17 -36
  6. package/bin/tags-suggest-runner.ts +429 -0
  7. package/bin/tags-suggest.mjs +30 -0
  8. package/bin/zudo-doc.mjs +78 -25
  9. package/dist/breadcrumb/breadcrumb.d.ts +4 -4
  10. package/dist/breadcrumb/find-path.d.ts +2 -2
  11. package/dist/breadcrumb/index.d.ts +1 -1
  12. package/dist/breadcrumb/types.d.ts +0 -5
  13. package/dist/chrome/derive.d.ts +16 -4
  14. package/dist/chrome/derive.js +64 -4
  15. package/dist/chrome/index.d.ts +13 -10
  16. package/dist/chrome/index.js +12 -6
  17. package/dist/chrome/primary-slots.d.ts +21 -0
  18. package/dist/chrome/primary-slots.js +59 -0
  19. package/dist/chrome-bindings.d.ts +227 -0
  20. package/dist/chrome-bindings.js +6 -0
  21. package/dist/code-syntax/code-block-enhancer-script.d.ts +4 -0
  22. package/dist/code-syntax/code-block-enhancer-script.js +7 -4
  23. package/dist/code-syntax/code-block-enhancer.d.ts +2 -2
  24. package/dist/code-syntax/code-block-enhancer.js +1 -3
  25. package/dist/code-syntax/index.d.ts +5 -5
  26. package/dist/code-syntax/index.js +4 -13
  27. package/dist/code-syntax/mermaid-init-script.d.ts +1 -22
  28. package/dist/code-syntax/mermaid-init-script.js +0 -2
  29. package/dist/code-syntax/mermaid-init.d.ts +0 -1
  30. package/dist/code-syntax/mermaid-init.js +3 -4
  31. package/dist/code-syntax/tabs-init.d.ts +0 -1
  32. package/dist/code-syntax/tabs-init.js +1 -3
  33. package/dist/code-syntax/tabs.d.ts +0 -1
  34. package/dist/code-syntax/tabs.js +1 -3
  35. package/dist/color-scheme-utils.d.ts +72 -4
  36. package/dist/color-scheme-utils.js +137 -0
  37. package/dist/color-schemes-defaults/index.d.ts +23 -0
  38. package/dist/color-schemes-defaults/index.js +134 -0
  39. package/dist/config.d.ts +459 -0
  40. package/dist/config.js +125 -0
  41. package/dist/content/component-map.d.ts +0 -16
  42. package/dist/content/component-map.js +2 -4
  43. package/dist/content/index.d.ts +1 -1
  44. package/dist/content/index.js +2 -3
  45. package/dist/content.css +1 -1
  46. package/dist/design-token-panel-bootstrap.d.ts +82 -27
  47. package/dist/design-token-panel-bootstrap.js +110 -21
  48. package/dist/design-token-panel-config/index.d.ts +49 -0
  49. package/dist/design-token-panel-config/index.js +211 -0
  50. package/dist/design-token-panel-config/manifest.d.ts +46 -0
  51. package/dist/design-token-panel-config/manifest.js +105 -0
  52. package/dist/directive-vocabulary-defaults/index.d.ts +16 -0
  53. package/dist/directive-vocabulary-defaults/index.js +12 -0
  54. package/dist/doc-body-end-islands/design-token-panel-island.d.ts +15 -0
  55. package/dist/doc-body-end-islands/design-token-panel-island.js +31 -0
  56. package/dist/doc-body-end-islands/index.d.ts +52 -7
  57. package/dist/doc-body-end-islands/index.js +19 -0
  58. package/dist/doc-body-end-islands/theme-pack-switcher-island.d.ts +37 -0
  59. package/dist/doc-body-end-islands/theme-pack-switcher-island.js +18 -0
  60. package/dist/doc-content-header/index.js +1 -1
  61. package/dist/doc-history-area/index.d.ts +10 -4
  62. package/dist/doc-history-area/index.js +3 -2
  63. package/dist/doc-metainfo-area/index.d.ts +1 -0
  64. package/dist/doc-page-props/index.d.ts +7 -23
  65. package/dist/doc-page-renderer/index.d.ts +1 -0
  66. package/dist/doc-page-renderer/index.js +1 -0
  67. package/dist/doc-page-shell/index.js +19 -12
  68. package/dist/doc-pager/index.js +1 -1
  69. package/dist/doc-route-entries/index.d.ts +2 -2
  70. package/dist/doclayout/doc-layout-with-defaults.js +4 -4
  71. package/dist/doclayout/doc-layout.d.ts +8 -0
  72. package/dist/doclayout/doc-layout.js +10 -1
  73. package/dist/docs-schema/index.d.ts +67 -0
  74. package/dist/docs-schema/index.js +43 -0
  75. package/dist/eject/index.d.ts +16 -0
  76. package/dist/eject/index.js +122 -28
  77. package/dist/extract-headings/index.d.ts +5 -8
  78. package/dist/extract-headings/index.js +2 -9
  79. package/dist/factory-context/index.d.ts +56 -4
  80. package/dist/features.css +65 -43
  81. package/dist/find-in-page/find-bar.d.ts +9 -0
  82. package/dist/find-in-page/find-bar.js +114 -0
  83. package/dist/find-in-page/find-in-page.d.ts +17 -0
  84. package/dist/find-in-page/find-in-page.js +131 -0
  85. package/dist/find-in-page/index.d.ts +11 -0
  86. package/dist/find-in-page/index.js +49 -0
  87. package/dist/footer/footer.js +1 -0
  88. package/dist/frontmatter-preview-defaults/index.d.ts +16 -0
  89. package/dist/frontmatter-preview-defaults/index.js +25 -0
  90. package/dist/head-with-defaults/index.d.ts +2 -0
  91. package/dist/head-with-defaults/index.js +16 -0
  92. package/dist/header/header.d.ts +10 -5
  93. package/dist/header/header.js +45 -5
  94. package/dist/header/index.d.ts +1 -1
  95. package/dist/header/types.d.ts +30 -1
  96. package/dist/header-with-defaults/index.js +1 -0
  97. package/dist/home-page/index.d.ts +2 -0
  98. package/dist/home-page/index.js +11 -5
  99. package/dist/home-page/prepare-home-data.js +1 -1
  100. package/dist/i18n-defaults/index.d.ts +22 -0
  101. package/dist/i18n-defaults/index.js +167 -0
  102. package/dist/locale-merge/index.d.ts +9 -14
  103. package/dist/locale-merge/index.js +6 -4
  104. package/dist/mdx-components/index.js +4 -4
  105. package/dist/plugins/claude-resources.d.ts +1 -0
  106. package/dist/plugins/claude-resources.js +4 -2
  107. package/dist/plugins/doc-history.js +1 -1
  108. package/dist/{integrations → plugins/internal}/claude-resources/index.d.ts +1 -16
  109. package/dist/{integrations → plugins/internal}/claude-resources/index.js +0 -9
  110. package/dist/{integrations → plugins/internal}/doc-history/index.d.ts +0 -17
  111. package/dist/{integrations → plugins/internal}/doc-history/index.js +0 -4
  112. package/dist/{integrations → plugins/internal}/doc-history/pre-build.d.ts +2 -4
  113. package/dist/plugins/internal/llms-txt/index.d.ts +7 -0
  114. package/dist/{integrations → plugins/internal}/llms-txt/load.d.ts +1 -1
  115. package/dist/{integrations → plugins/internal}/llms-txt/load.js +2 -2
  116. package/dist/{integrations → plugins/internal}/llms-txt/strip.d.ts +2 -3
  117. package/dist/{integrations → plugins/internal}/llms-txt/strip.js +1 -1
  118. package/dist/{integrations → plugins/internal}/llms-txt/types.d.ts +13 -22
  119. package/dist/{integrations → plugins/internal}/search-index/collect.js +1 -1
  120. package/dist/plugins/internal/theme-packs/copy.d.ts +32 -0
  121. package/dist/plugins/internal/theme-packs/copy.js +45 -0
  122. package/dist/plugins/internal/theme-packs/dev-middleware.d.ts +22 -0
  123. package/dist/plugins/internal/theme-packs/dev-middleware.js +80 -0
  124. package/dist/plugins/internal/theme-packs/index.d.ts +6 -0
  125. package/dist/plugins/internal/theme-packs/index.js +9 -0
  126. package/dist/plugins/internal/theme-packs/resolve.d.ts +17 -0
  127. package/dist/plugins/internal/theme-packs/resolve.js +17 -0
  128. package/dist/plugins/internal/theme-packs/types.d.ts +22 -0
  129. package/dist/plugins/internal/theme-packs/types.js +0 -0
  130. package/dist/plugins/llms-txt.js +1 -1
  131. package/dist/plugins/routes.js +55 -21
  132. package/dist/plugins/search-index.js +1 -1
  133. package/dist/plugins/theme-packs.d.ts +3 -0
  134. package/dist/plugins/theme-packs.js +40 -0
  135. package/dist/preset.d.ts +28 -14
  136. package/dist/preset.js +24 -9
  137. package/dist/route-context/index.js +4 -6
  138. package/dist/route-enumerators/index.d.ts +6 -18
  139. package/dist/route-enumerators/index.js +8 -6
  140. package/dist/routes/404.js +3 -1
  141. package/dist/routes/_chrome.js +3 -2
  142. package/dist/routes/_context.d.ts +7 -3
  143. package/dist/routes/_context.js +2 -0
  144. package/dist/routes/_docs-helpers.js +9 -21
  145. package/dist/safelist.css +1 -1
  146. package/dist/settings.d.ts +64 -12
  147. package/dist/sidebar-tree/build-tree.d.ts +3 -4
  148. package/dist/sidebar-tree/build-tree.js +4 -3
  149. package/dist/sidebar-tree/index.d.ts +2 -2
  150. package/dist/sidebar-tree/types.d.ts +6 -17
  151. package/dist/site-tree-nav-island/index.d.ts +3 -1
  152. package/dist/site-tree-nav-island/index.js +17 -5
  153. package/dist/site-tree-nav-island/state.d.ts +4 -0
  154. package/dist/site-tree-nav-island/state.js +10 -0
  155. package/dist/tag-helpers/index.d.ts +6 -13
  156. package/dist/tag-helpers/index.js +5 -23
  157. package/dist/tag-pages/index.d.ts +8 -21
  158. package/dist/tag-pages/index.js +14 -10
  159. package/dist/tags-audit.d.ts +16 -44
  160. package/dist/tags-audit.js +9 -172
  161. package/dist/theme/color-scheme-provider.d.ts +2 -3
  162. package/dist/theme/color-scheme-provider.js +2 -10
  163. package/dist/theme/index.d.ts +1 -4
  164. package/dist/theme/index.js +2 -30
  165. package/dist/theme/theme-pack-provider.d.ts +42 -0
  166. package/dist/theme/theme-pack-provider.js +75 -0
  167. package/dist/theme-cli/apply.d.ts +16 -0
  168. package/dist/theme-cli/apply.js +61 -0
  169. package/dist/theme-cli/config-io.d.ts +15 -0
  170. package/dist/theme-cli/config-io.js +51 -0
  171. package/dist/theme-cli/config-rewriter.d.ts +21 -0
  172. package/dist/theme-cli/config-rewriter.js +141 -0
  173. package/dist/theme-cli/config-scanner.d.ts +63 -0
  174. package/dist/theme-cli/config-scanner.js +288 -0
  175. package/dist/theme-cli/index.d.ts +7 -0
  176. package/dist/theme-cli/index.js +38 -0
  177. package/dist/theme-cli/list.d.ts +27 -0
  178. package/dist/theme-cli/list.js +64 -0
  179. package/dist/theme-cli/provenance.d.ts +17 -0
  180. package/dist/theme-cli/provenance.js +35 -0
  181. package/dist/theme-cli/registry.d.ts +14 -0
  182. package/dist/theme-cli/registry.js +13 -0
  183. package/dist/theme-pack-dialog/dialog-state.d.ts +25 -0
  184. package/dist/theme-pack-dialog/dialog-state.js +40 -0
  185. package/dist/theme-pack-dialog/index.d.ts +5 -0
  186. package/dist/theme-pack-dialog/index.js +152 -0
  187. package/dist/theme-pack-dialog/theme-pack-card.d.ts +25 -0
  188. package/dist/theme-pack-dialog/theme-pack-card.js +72 -0
  189. package/dist/theme-pack-switcher/index.d.ts +49 -0
  190. package/dist/theme-pack-switcher/index.js +205 -0
  191. package/dist/theme-pack-switcher/switcher-state.d.ts +56 -0
  192. package/dist/theme-pack-switcher/switcher-state.js +43 -0
  193. package/dist/theme-pack-switcher/theme-pack-sync.d.ts +85 -0
  194. package/dist/theme-pack-switcher/theme-pack-sync.js +121 -0
  195. package/dist/theme-packs/beacon/fonts/AtkinsonHyperlegible-400-latin.woff2 +0 -0
  196. package/dist/theme-packs/beacon/fonts/AtkinsonHyperlegible-700-latin.woff2 +0 -0
  197. package/dist/theme-packs/beacon/fonts/AtkinsonHyperlegible-italic-400-latin.woff2 +0 -0
  198. package/dist/theme-packs/beacon/fonts/AtkinsonHyperlegible-italic-700-latin.woff2 +0 -0
  199. package/dist/theme-packs/beacon/fonts/AtkinsonHyperlegibleMono-italic-latin.woff2 +0 -0
  200. package/dist/theme-packs/beacon/fonts/AtkinsonHyperlegibleMono-latin.woff2 +0 -0
  201. package/dist/theme-packs/beacon/fonts/OFL.txt +94 -0
  202. package/dist/theme-packs/beacon/meta.json +37 -0
  203. package/dist/theme-packs/beacon/pack.css +442 -0
  204. package/dist/theme-packs/broadsheet/fonts/CourierPrime-400-latin.woff2 +0 -0
  205. package/dist/theme-packs/broadsheet/fonts/CourierPrime-700-latin.woff2 +0 -0
  206. package/dist/theme-packs/broadsheet/fonts/OFL.txt +95 -0
  207. package/dist/theme-packs/broadsheet/fonts/PlayfairDisplay-italic-latin.woff2 +0 -0
  208. package/dist/theme-packs/broadsheet/fonts/PlayfairDisplay-latin.woff2 +0 -0
  209. package/dist/theme-packs/broadsheet/fonts/SourceSerif4-italic-latin.woff2 +0 -0
  210. package/dist/theme-packs/broadsheet/fonts/SourceSerif4-latin.woff2 +0 -0
  211. package/dist/theme-packs/broadsheet/meta.json +38 -0
  212. package/dist/theme-packs/broadsheet/pack.css +448 -0
  213. package/dist/theme-packs/brutalist/fonts/ArchivoBlack-latin.woff2 +0 -0
  214. package/dist/theme-packs/brutalist/fonts/IBMPlexMono-400-latin.woff2 +0 -0
  215. package/dist/theme-packs/brutalist/fonts/IBMPlexMono-500-latin.woff2 +0 -0
  216. package/dist/theme-packs/brutalist/fonts/IBMPlexMono-700-latin.woff2 +0 -0
  217. package/dist/theme-packs/brutalist/fonts/OFL.txt +95 -0
  218. package/dist/theme-packs/brutalist/fonts/SpaceGrotesk-latin.woff2 +0 -0
  219. package/dist/theme-packs/brutalist/meta.json +38 -0
  220. package/dist/theme-packs/brutalist/pack.css +546 -0
  221. package/dist/theme-packs/default/meta.json +37 -0
  222. package/dist/theme-packs/drift/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
  223. package/dist/theme-packs/drift/fonts/IBMPlexMono-400-latin.woff2 +0 -0
  224. package/dist/theme-packs/drift/fonts/IBMPlexMono-500-latin-ext.woff2 +0 -0
  225. package/dist/theme-packs/drift/fonts/IBMPlexMono-500-latin.woff2 +0 -0
  226. package/dist/theme-packs/drift/fonts/IBMPlexSans-italic-latin.woff2 +0 -0
  227. package/dist/theme-packs/drift/fonts/IBMPlexSans-latin-ext.woff2 +0 -0
  228. package/dist/theme-packs/drift/fonts/IBMPlexSans-latin.woff2 +0 -0
  229. package/dist/theme-packs/drift/fonts/OFL.txt +93 -0
  230. package/dist/theme-packs/drift/meta.json +37 -0
  231. package/dist/theme-packs/drift/pack.css +478 -0
  232. package/dist/theme-packs/fjord/fonts/Inter-latin-ext.woff2 +0 -0
  233. package/dist/theme-packs/fjord/fonts/Inter-latin.woff2 +0 -0
  234. package/dist/theme-packs/fjord/fonts/JetBrainsMono-latin-ext.woff2 +0 -0
  235. package/dist/theme-packs/fjord/fonts/JetBrainsMono-latin.woff2 +0 -0
  236. package/dist/theme-packs/fjord/fonts/OFL.txt +94 -0
  237. package/dist/theme-packs/fjord/meta.json +37 -0
  238. package/dist/theme-packs/fjord/pack.css +375 -0
  239. package/dist/theme-packs/foundry/fonts/Inter-latin-ext.woff2 +0 -0
  240. package/dist/theme-packs/foundry/fonts/Inter-latin.woff2 +0 -0
  241. package/dist/theme-packs/foundry/fonts/OFL.txt +93 -0
  242. package/dist/theme-packs/foundry/meta.json +37 -0
  243. package/dist/theme-packs/foundry/pack.css +202 -0
  244. package/dist/theme-packs/futura-editorial/fonts/Jost-latin.woff2 +0 -0
  245. package/dist/theme-packs/futura-editorial/fonts/NotoSans-italic-latin.woff2 +0 -0
  246. package/dist/theme-packs/futura-editorial/fonts/NotoSans-latin.woff2 +0 -0
  247. package/dist/theme-packs/futura-editorial/fonts/OFL.txt +95 -0
  248. package/dist/theme-packs/futura-editorial/fonts/SpaceMono-400-latin.woff2 +0 -0
  249. package/dist/theme-packs/futura-editorial/fonts/SpaceMono-700-latin.woff2 +0 -0
  250. package/dist/theme-packs/futura-editorial/meta.json +38 -0
  251. package/dist/theme-packs/futura-editorial/pack.css +313 -0
  252. package/dist/theme-packs/hearth/fonts/Fraunces-italic-latin.woff2 +0 -0
  253. package/dist/theme-packs/hearth/fonts/Fraunces-latin.woff2 +0 -0
  254. package/dist/theme-packs/hearth/fonts/Nunito-latin.woff2 +0 -0
  255. package/dist/theme-packs/hearth/fonts/OFL.txt +95 -0
  256. package/dist/theme-packs/hearth/fonts/SplineSansMono-latin.woff2 +0 -0
  257. package/dist/theme-packs/hearth/meta.json +38 -0
  258. package/dist/theme-packs/hearth/pack.css +385 -0
  259. package/dist/theme-packs/hollow/fonts/OFL.txt +94 -0
  260. package/dist/theme-packs/hollow/fonts/SpaceGrotesk-latin.woff2 +0 -0
  261. package/dist/theme-packs/hollow/fonts/SpaceMono-400-italic-latin.woff2 +0 -0
  262. package/dist/theme-packs/hollow/fonts/SpaceMono-400-latin.woff2 +0 -0
  263. package/dist/theme-packs/hollow/fonts/SpaceMono-700-latin.woff2 +0 -0
  264. package/dist/theme-packs/hollow/meta.json +37 -0
  265. package/dist/theme-packs/hollow/pack.css +428 -0
  266. package/dist/theme-packs/ledger/fonts/CrimsonPro-latin-ext-italic.woff2 +0 -0
  267. package/dist/theme-packs/ledger/fonts/CrimsonPro-latin-ext.woff2 +0 -0
  268. package/dist/theme-packs/ledger/fonts/CrimsonPro-latin-italic.woff2 +0 -0
  269. package/dist/theme-packs/ledger/fonts/CrimsonPro-latin.woff2 +0 -0
  270. package/dist/theme-packs/ledger/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
  271. package/dist/theme-packs/ledger/fonts/IBMPlexMono-400-latin.woff2 +0 -0
  272. package/dist/theme-packs/ledger/fonts/IBMPlexMono-500-latin-ext.woff2 +0 -0
  273. package/dist/theme-packs/ledger/fonts/IBMPlexMono-500-latin.woff2 +0 -0
  274. package/dist/theme-packs/ledger/fonts/OFL.txt +94 -0
  275. package/dist/theme-packs/ledger/meta.json +37 -0
  276. package/dist/theme-packs/ledger/pack.css +463 -0
  277. package/dist/theme-packs/manuscript/fonts/CourierPrime-400-latin-ext.woff2 +0 -0
  278. package/dist/theme-packs/manuscript/fonts/CourierPrime-400-latin.woff2 +0 -0
  279. package/dist/theme-packs/manuscript/fonts/CourierPrime-700-latin-ext.woff2 +0 -0
  280. package/dist/theme-packs/manuscript/fonts/CourierPrime-700-latin.woff2 +0 -0
  281. package/dist/theme-packs/manuscript/fonts/EBGaramond-latin-italic.woff2 +0 -0
  282. package/dist/theme-packs/manuscript/fonts/EBGaramond-latin.woff2 +0 -0
  283. package/dist/theme-packs/manuscript/fonts/OFL.txt +94 -0
  284. package/dist/theme-packs/manuscript/meta.json +37 -0
  285. package/dist/theme-packs/manuscript/pack.css +376 -0
  286. package/dist/theme-packs/matcha/fonts/MPLUS1Code-latin.woff2 +0 -0
  287. package/dist/theme-packs/matcha/fonts/OFL.txt +95 -0
  288. package/dist/theme-packs/matcha/fonts/ShipporiMincho-500-latin.woff2 +0 -0
  289. package/dist/theme-packs/matcha/fonts/ShipporiMincho-700-latin.woff2 +0 -0
  290. package/dist/theme-packs/matcha/fonts/ZenKakuGothicNew-400-latin.woff2 +0 -0
  291. package/dist/theme-packs/matcha/fonts/ZenKakuGothicNew-500-latin.woff2 +0 -0
  292. package/dist/theme-packs/matcha/meta.json +38 -0
  293. package/dist/theme-packs/matcha/pack.css +409 -0
  294. package/dist/theme-packs/nocturne/fonts/CormorantGaramond-italic-latin.woff2 +0 -0
  295. package/dist/theme-packs/nocturne/fonts/CormorantGaramond-latin.woff2 +0 -0
  296. package/dist/theme-packs/nocturne/fonts/IBMPlexMono-400-latin.woff2 +0 -0
  297. package/dist/theme-packs/nocturne/fonts/IBMPlexMono-500-latin.woff2 +0 -0
  298. package/dist/theme-packs/nocturne/fonts/IBMPlexMono-italic-latin.woff2 +0 -0
  299. package/dist/theme-packs/nocturne/fonts/Manrope-latin.woff2 +0 -0
  300. package/dist/theme-packs/nocturne/fonts/OFL.txt +95 -0
  301. package/dist/theme-packs/nocturne/meta.json +38 -0
  302. package/dist/theme-packs/nocturne/pack.css +502 -0
  303. package/dist/theme-packs/observatory/fonts/JosefinSans-latin.woff2 +0 -0
  304. package/dist/theme-packs/observatory/fonts/Jost-italic-latin.woff2 +0 -0
  305. package/dist/theme-packs/observatory/fonts/Jost-latin.woff2 +0 -0
  306. package/dist/theme-packs/observatory/fonts/OFL.txt +95 -0
  307. package/dist/theme-packs/observatory/fonts/SpaceMono-400-latin.woff2 +0 -0
  308. package/dist/theme-packs/observatory/fonts/SpaceMono-700-latin.woff2 +0 -0
  309. package/dist/theme-packs/observatory/fonts/SpaceMono-italic-latin.woff2 +0 -0
  310. package/dist/theme-packs/observatory/meta.json +38 -0
  311. package/dist/theme-packs/observatory/pack.css +728 -0
  312. package/dist/theme-packs/onyx/fonts/CormorantGaramond-italic-latin.woff2 +0 -0
  313. package/dist/theme-packs/onyx/fonts/CormorantGaramond-latin.woff2 +0 -0
  314. package/dist/theme-packs/onyx/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
  315. package/dist/theme-packs/onyx/fonts/IBMPlexMono-400-latin.woff2 +0 -0
  316. package/dist/theme-packs/onyx/fonts/IBMPlexMono-500-latin-ext.woff2 +0 -0
  317. package/dist/theme-packs/onyx/fonts/IBMPlexMono-500-latin.woff2 +0 -0
  318. package/dist/theme-packs/onyx/fonts/Jost-latin.woff2 +0 -0
  319. package/dist/theme-packs/onyx/fonts/OFL.txt +95 -0
  320. package/dist/theme-packs/onyx/meta.json +38 -0
  321. package/dist/theme-packs/onyx/pack.css +497 -0
  322. package/dist/theme-packs/phosphor/fonts/JetBrainsMono-latin-ext.woff2 +0 -0
  323. package/dist/theme-packs/phosphor/fonts/JetBrainsMono-latin.woff2 +0 -0
  324. package/dist/theme-packs/phosphor/fonts/OFL.txt +94 -0
  325. package/dist/theme-packs/phosphor/fonts/VT323-latin-ext.woff2 +0 -0
  326. package/dist/theme-packs/phosphor/fonts/VT323-latin.woff2 +0 -0
  327. package/dist/theme-packs/phosphor/meta.json +38 -0
  328. package/dist/theme-packs/phosphor/pack.css +493 -0
  329. package/dist/theme-packs/solar/fonts/OFL.txt +94 -0
  330. package/dist/theme-packs/solar/fonts/SourceCodePro-italic-latin.woff2 +0 -0
  331. package/dist/theme-packs/solar/fonts/SourceCodePro-latin.woff2 +0 -0
  332. package/dist/theme-packs/solar/fonts/SourceSans3-italic-latin.woff2 +0 -0
  333. package/dist/theme-packs/solar/fonts/SourceSans3-latin.woff2 +0 -0
  334. package/dist/theme-packs/solar/meta.json +37 -0
  335. package/dist/theme-packs/solar/pack.css +380 -0
  336. package/dist/theme-packs/sumi/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
  337. package/dist/theme-packs/sumi/fonts/IBMPlexMono-400-latin.woff2 +0 -0
  338. package/dist/theme-packs/sumi/fonts/IBMPlexMono-500-latin-ext.woff2 +0 -0
  339. package/dist/theme-packs/sumi/fonts/IBMPlexMono-500-latin.woff2 +0 -0
  340. package/dist/theme-packs/sumi/fonts/OFL.txt +95 -0
  341. package/dist/theme-packs/sumi/fonts/ShipporiMinchoB1-600-latin.woff2 +0 -0
  342. package/dist/theme-packs/sumi/fonts/ShipporiMinchoB1-700-latin.woff2 +0 -0
  343. package/dist/theme-packs/sumi/fonts/ShipporiMinchoB1-800-latin.woff2 +0 -0
  344. package/dist/theme-packs/sumi/fonts/ZenKakuGothicNew-400-latin.woff2 +0 -0
  345. package/dist/theme-packs/sumi/fonts/ZenKakuGothicNew-500-latin.woff2 +0 -0
  346. package/dist/theme-packs/sumi/meta.json +38 -0
  347. package/dist/theme-packs/sumi/pack.css +502 -0
  348. package/dist/theme-packs/swissgrid/fonts/IBMPlexMono-400-latin-ext.woff2 +0 -0
  349. package/dist/theme-packs/swissgrid/fonts/IBMPlexMono-400-latin.woff2 +0 -0
  350. package/dist/theme-packs/swissgrid/fonts/IBMPlexMono-600-latin.woff2 +0 -0
  351. package/dist/theme-packs/swissgrid/fonts/Inter-latin-ext.woff2 +0 -0
  352. package/dist/theme-packs/swissgrid/fonts/Inter-latin.woff2 +0 -0
  353. package/dist/theme-packs/swissgrid/fonts/InterTight-latin.woff2 +0 -0
  354. package/dist/theme-packs/swissgrid/fonts/OFL.txt +95 -0
  355. package/dist/theme-packs/swissgrid/meta.json +38 -0
  356. package/dist/theme-packs/swissgrid/pack.css +518 -0
  357. package/dist/theme-packs/washi/fonts/MPlus1Code-400-latin.woff2 +0 -0
  358. package/dist/theme-packs/washi/fonts/MPlus1Code-500-latin.woff2 +0 -0
  359. package/dist/theme-packs/washi/fonts/OFL.txt +95 -0
  360. package/dist/theme-packs/washi/fonts/ShipporiMincho-500-latin.woff2 +0 -0
  361. package/dist/theme-packs/washi/fonts/ShipporiMincho-600-latin.woff2 +0 -0
  362. package/dist/theme-packs/washi/fonts/ShipporiMincho-700-latin.woff2 +0 -0
  363. package/dist/theme-packs/washi/fonts/ZenKakuGothicNew-400-latin.woff2 +0 -0
  364. package/dist/theme-packs/washi/fonts/ZenKakuGothicNew-500-latin.woff2 +0 -0
  365. package/dist/theme-packs/washi/fonts/ZenKakuGothicNew-700-latin.woff2 +0 -0
  366. package/dist/theme-packs/washi/meta.json +38 -0
  367. package/dist/theme-packs/washi/pack.css +479 -0
  368. package/dist/theme-packs-registry/index.d.ts +5 -0
  369. package/dist/theme-packs-registry/index.js +30 -0
  370. package/dist/theme-packs-registry/load-registry.d.ts +23 -0
  371. package/dist/theme-packs-registry/load-registry.js +64 -0
  372. package/dist/theme-packs-registry/meta-schema.d.ts +70 -0
  373. package/dist/theme-packs-registry/meta-schema.js +38 -0
  374. package/dist/theme-packs-registry/resolve-enabled-packs.d.ts +25 -0
  375. package/dist/theme-packs-registry/resolve-enabled-packs.js +43 -0
  376. package/dist/theme-packs-registry/token-manifest.d.ts +19 -0
  377. package/dist/theme-packs-registry/token-manifest.js +57 -0
  378. package/dist/theme-packs-registry/validator.d.ts +35 -0
  379. package/dist/theme-packs-registry/validator.js +284 -0
  380. package/dist/theme-toggle/color-scheme-sync.d.ts +2 -4
  381. package/dist/theme.css +319 -0
  382. package/dist/versions-page/index.d.ts +2 -2
  383. package/dist/versions-page/index.js +8 -8
  384. package/dist/z-index-defaults/index.d.ts +45 -0
  385. package/dist/z-index-defaults/index.js +34 -0
  386. package/eject/breadcrumb/breadcrumb.tsx +4 -4
  387. package/eject/breadcrumb/find-path.ts +2 -2
  388. package/eject/breadcrumb/index.ts +1 -1
  389. package/eject/breadcrumb/types.ts +0 -6
  390. package/eject/code-group/index.tsx +2 -2
  391. package/eject/doc-history/index.tsx +1 -1
  392. package/eject/doc-pager/index.tsx +1 -1
  393. package/eject/footer/footer.tsx +1 -0
  394. package/eject/header/header.tsx +86 -21
  395. package/eject/header/index.ts +3 -0
  396. package/eject/header/types.ts +39 -1
  397. package/eject/site-tree-nav-island/index.tsx +15 -3
  398. package/eject/site-tree-nav-island/state.ts +9 -0
  399. package/eject/theme-toggle/color-scheme-sync.ts +2 -4
  400. package/package.json +82 -29
  401. package/routes-src/404.tsx +4 -1
  402. package/routes-src/_chrome.tsx +31 -8
  403. package/routes-src/_context.ts +4 -0
  404. package/routes-src/_docs-helpers.ts +9 -29
  405. package/routes-src/_virtual.d.ts +38 -2
  406. package/tsconfig.base.json +29 -0
  407. package/virtual-modules.d.ts +83 -0
  408. package/zfb-config-shim.d.ts +210 -0
  409. package/dist/integrations/llms-txt/index.d.ts +0 -33
  410. package/dist/theme/color-tweak-export-modal.d.ts +0 -16
  411. package/dist/theme/color-tweak-export-modal.js +0 -138
  412. package/dist/theme/design-token-serde.d.ts +0 -183
  413. package/dist/theme/design-token-serde.js +0 -346
  414. package/dist/theme/design-token-types.d.ts +0 -13
  415. package/dist/theme/design-token-types.js +0 -6
  416. package/dist/theme/iframe-bridge.d.ts +0 -42
  417. package/dist/theme/iframe-bridge.js +0 -72
  418. /package/dist/{integrations → plugins/internal}/claude-resources/escape-for-mdx.d.ts +0 -0
  419. /package/dist/{integrations → plugins/internal}/claude-resources/escape-for-mdx.js +0 -0
  420. /package/dist/{integrations → plugins/internal}/claude-resources/generate.d.ts +0 -0
  421. /package/dist/{integrations → plugins/internal}/claude-resources/generate.js +0 -0
  422. /package/dist/{integrations → plugins/internal}/doc-history/pre-build.js +0 -0
  423. /package/dist/{integrations → plugins/internal}/llms-txt/dev-middleware.d.ts +0 -0
  424. /package/dist/{integrations → plugins/internal}/llms-txt/dev-middleware.js +0 -0
  425. /package/dist/{integrations → plugins/internal}/llms-txt/emit.d.ts +0 -0
  426. /package/dist/{integrations → plugins/internal}/llms-txt/emit.js +0 -0
  427. /package/dist/{integrations → plugins/internal}/llms-txt/generate.d.ts +0 -0
  428. /package/dist/{integrations → plugins/internal}/llms-txt/generate.js +0 -0
  429. /package/dist/{integrations → plugins/internal}/llms-txt/index.js +0 -0
  430. /package/dist/{integrations → plugins/internal}/llms-txt/types.js +0 -0
  431. /package/dist/{integrations → plugins/internal}/search-index/build-emitter.d.ts +0 -0
  432. /package/dist/{integrations → plugins/internal}/search-index/build-emitter.js +0 -0
  433. /package/dist/{integrations → plugins/internal}/search-index/collect.d.ts +0 -0
  434. /package/dist/{integrations → plugins/internal}/search-index/dev-middleware.d.ts +0 -0
  435. /package/dist/{integrations → plugins/internal}/search-index/dev-middleware.js +0 -0
  436. /package/dist/{integrations → plugins/internal}/search-index/index.d.ts +0 -0
  437. /package/dist/{integrations → plugins/internal}/search-index/index.js +0 -0
  438. /package/dist/{integrations → plugins/internal}/search-index/types.d.ts +0 -0
  439. /package/dist/{integrations → plugins/internal}/search-index/types.js +0 -0
@@ -0,0 +1,429 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Package-owned tags:suggest runner — optional local-LLM tag suggester.
4
+ *
5
+ * Reads one or more doc files, asks a local Ollama instance for up to 3 tag
6
+ * ids from the project vocabulary, and either opens an interactive approval
7
+ * prompt (default) or appends suggestions to `.tag-suggestions.jsonl`
8
+ * (`--batch`, also used automatically when stdout is not a TTY).
9
+ *
10
+ * Entirely developer-opt-in. Never runs in CI; never wired into b4push.
11
+ */
12
+ import { appendFile, readFile, writeFile } from "node:fs/promises";
13
+ import { existsSync } from "node:fs";
14
+ import { isAbsolute, relative, resolve } from "node:path";
15
+ import { parseArgs } from "node:util";
16
+ import matter from "gray-matter";
17
+
18
+ import type { TagVocabularyEntry } from "@takazudo/zudo-doc/settings";
19
+ import {
20
+ loadTagCliConfig,
21
+ takeTagCliConfigInput,
22
+ } from "./tag-cli-config.ts";
23
+
24
+ const DEFAULT_HOST = "http://localhost:11434";
25
+ const DEFAULT_MODEL = "qwen2.5:7b";
26
+ const BODY_CHAR_LIMIT = 1500;
27
+ const REQUEST_TIMEOUT_MS = 60_000;
28
+ const BATCH_FILE = ".tag-suggestions.jsonl";
29
+
30
+ interface Args {
31
+ files: string[];
32
+ host: string;
33
+ model: string;
34
+ batch: boolean;
35
+ help: boolean;
36
+ }
37
+
38
+ interface ParsedArgs extends Args {
39
+ configInput: string | undefined;
40
+ }
41
+
42
+ interface Suggestion {
43
+ file: string;
44
+ current: string[];
45
+ suggested: string[];
46
+ }
47
+
48
+ function printHelp(): void {
49
+ process.stdout.write(`Usage: tags-suggest --config <path> [options] <file...>
50
+
51
+ Ask a local Ollama LLM to suggest up to 3 tag ids from the project
52
+ vocabulary for each doc file. Opt-in developer tool — never runs in CI.
53
+
54
+ Options:
55
+ --host <url> Ollama endpoint (default: ${DEFAULT_HOST})
56
+ --model <id> Ollama model id (default: ${DEFAULT_MODEL})
57
+ --batch Append suggestions to ${BATCH_FILE} instead of prompting.
58
+ Auto-enabled when stdout is not a TTY.
59
+ --help Show this help.
60
+
61
+ Ollama setup:
62
+ 1. Install from https://ollama.com/
63
+ 2. Pull a model: \`ollama pull ${DEFAULT_MODEL}\`
64
+ 3. Ensure the daemon is running at ${DEFAULT_HOST}
65
+
66
+ Model trade-offs:
67
+ - \`qwen2.5:7b\` (default) — balanced quality/speed; ~5 GB download.
68
+ - \`llama3.1:8b\` — similar size, stronger English reasoning.
69
+ - \`qwen2.5:3b\` / \`llama3.2:3b\` — smaller/faster, noisier output.
70
+ Pass \`--model\` to override.
71
+
72
+ Exit codes:
73
+ 0 success
74
+ 1 usage error
75
+ 2 Ollama unreachable or returned unusable output
76
+ `);
77
+ }
78
+
79
+ function parseCliArgs(argv: string[]): ParsedArgs {
80
+ const configArgs = takeTagCliConfigInput(argv);
81
+ let parsed;
82
+ try {
83
+ parsed = parseArgs({
84
+ args: configArgs.argv,
85
+ allowPositionals: true,
86
+ options: {
87
+ host: { type: "string", default: DEFAULT_HOST },
88
+ model: { type: "string", default: DEFAULT_MODEL },
89
+ batch: { type: "boolean", default: false },
90
+ help: { type: "boolean", default: false },
91
+ },
92
+ });
93
+ } catch (err) {
94
+ const msg = err instanceof Error ? err.message : String(err);
95
+ process.stderr.write(`tags:suggest: ${msg}\n`);
96
+ process.exit(1);
97
+ }
98
+ return {
99
+ configInput: configArgs.configInput,
100
+ files: parsed.positionals,
101
+ host: String(parsed.values.host ?? DEFAULT_HOST),
102
+ model: String(parsed.values.model ?? DEFAULT_MODEL),
103
+ batch: Boolean(parsed.values.batch),
104
+ help: Boolean(parsed.values.help),
105
+ };
106
+ }
107
+
108
+ function buildPrompt(
109
+ entries: TagVocabularyEntry[],
110
+ title: string,
111
+ body: string,
112
+ ): string {
113
+ const vocabLines = entries
114
+ .map((e) => {
115
+ const label = e.label ?? e.id;
116
+ const desc = e.description ?? "";
117
+ const group = e.group ? ` [${e.group}]` : "";
118
+ return `- ${e.id}${group} — ${label}: ${desc}`.trim();
119
+ })
120
+ .join("\n");
121
+ const snippet = body.slice(0, BODY_CHAR_LIMIT);
122
+ return `You are tagging a documentation page.
123
+
124
+ Vocabulary (pick ONLY from these ids):
125
+ ${vocabLines}
126
+
127
+ Document title: ${title}
128
+
129
+ Document excerpt:
130
+ """
131
+ ${snippet}
132
+ """
133
+
134
+ Return a JSON array of AT MOST 3 tag ids that best fit this page. The
135
+ array must contain only strings that exactly match ids from the
136
+ vocabulary above. Respond with the JSON array and nothing else.`;
137
+ }
138
+
139
+ interface OllamaGenerateResponse {
140
+ response?: string;
141
+ error?: string;
142
+ }
143
+
144
+ async function callOllama(
145
+ host: string,
146
+ model: string,
147
+ prompt: string,
148
+ ): Promise<string> {
149
+ const url = `${host.replace(/\/$/, "")}/api/generate`;
150
+ const controller = new AbortController();
151
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
152
+
153
+ let res: Response;
154
+ try {
155
+ res = await fetch(url, {
156
+ method: "POST",
157
+ headers: { "content-type": "application/json" },
158
+ body: JSON.stringify({
159
+ model,
160
+ prompt,
161
+ stream: false,
162
+ format: "json",
163
+ }),
164
+ signal: controller.signal,
165
+ });
166
+ } catch (err) {
167
+ clearTimeout(timer);
168
+ if ((err as Error)?.name === "AbortError") {
169
+ throw new OllamaError(
170
+ `Ollama request timed out after ${REQUEST_TIMEOUT_MS / 1000}s at ${host}. Is \`ollama serve\` running?`,
171
+ );
172
+ }
173
+ // Any other fetch rejection (ECONNREFUSED, UND_ERR_CONNECT_TIMEOUT,
174
+ // DNS failure, bad port, …) means we could not talk to Ollama. Collapse
175
+ // them into one actionable message — the full stack isn't useful to
176
+ // the doc author running this CLI.
177
+ throw new OllamaUnreachableError(host, model);
178
+ }
179
+ clearTimeout(timer);
180
+
181
+ if (!res.ok) {
182
+ let detail = "";
183
+ try {
184
+ const body = (await res.json()) as OllamaGenerateResponse;
185
+ if (body?.error) detail = ` — ${body.error}`;
186
+ } catch {
187
+ // ignore; keep status line only
188
+ }
189
+ if (res.status === 404) {
190
+ throw new OllamaError(
191
+ `Ollama responded 404 at ${host}. Pull the model first: \`ollama pull ${model}\`.${detail}`,
192
+ );
193
+ }
194
+ throw new OllamaError(
195
+ `Ollama responded HTTP ${res.status} at ${host}.${detail}`,
196
+ );
197
+ }
198
+
199
+ let json: OllamaGenerateResponse;
200
+ try {
201
+ json = (await res.json()) as OllamaGenerateResponse;
202
+ } catch {
203
+ throw new OllamaError(`Ollama returned non-JSON response at ${host}.`);
204
+ }
205
+ if (typeof json.response !== "string") {
206
+ throw new OllamaError(`Ollama response missing 'response' field.`);
207
+ }
208
+ return json.response;
209
+ }
210
+
211
+ class OllamaError extends Error {
212
+ readonly friendly = true;
213
+ }
214
+ class OllamaUnreachableError extends OllamaError {
215
+ constructor(host: string, model: string) {
216
+ super(
217
+ `Ollama not reachable at ${host}. Install from https://ollama.com/ and run \`ollama pull ${model}\`.`,
218
+ );
219
+ }
220
+ }
221
+
222
+ function parseSuggestions(
223
+ raw: string,
224
+ allowedIds: Set<string>,
225
+ ): string[] {
226
+ const trimmed = raw.trim();
227
+ // Accept either a bare JSON array or a JSON object that wraps one.
228
+ // Some models return `{"tags": [...]}` even when asked for an array.
229
+ let parsed: unknown;
230
+ try {
231
+ parsed = JSON.parse(trimmed);
232
+ } catch {
233
+ // Last-ditch: try to extract the first JSON array substring.
234
+ const match = trimmed.match(/\[[\s\S]*\]/);
235
+ if (!match) {
236
+ throw new OllamaError(
237
+ `Model output was not valid JSON. Raw: ${trimmed.slice(0, 200)}`,
238
+ );
239
+ }
240
+ parsed = JSON.parse(match[0]);
241
+ }
242
+ let arr: unknown[];
243
+ if (Array.isArray(parsed)) {
244
+ arr = parsed;
245
+ } else if (
246
+ parsed &&
247
+ typeof parsed === "object" &&
248
+ Array.isArray((parsed as { tags?: unknown }).tags)
249
+ ) {
250
+ arr = (parsed as { tags: unknown[] }).tags;
251
+ } else {
252
+ throw new OllamaError(`Model output is not a JSON array.`);
253
+ }
254
+ const ids = arr
255
+ .filter((v): v is string => typeof v === "string")
256
+ .map((v) => v.trim())
257
+ .filter((v) => v.length > 0 && allowedIds.has(v));
258
+ // Dedupe, cap at 3.
259
+ return Array.from(new Set(ids)).slice(0, 3);
260
+ }
261
+
262
+ function asStringArray(v: unknown): string[] {
263
+ if (!Array.isArray(v)) return [];
264
+ return v.filter((x): x is string => typeof x === "string");
265
+ }
266
+
267
+ async function writeFrontmatterTags(
268
+ filePath: string,
269
+ parsed: matter.GrayMatterFile<string>,
270
+ nextTags: string[],
271
+ ): Promise<void> {
272
+ const data = { ...parsed.data, tags: nextTags };
273
+ const rebuilt = matter.stringify(parsed.content, data);
274
+ await writeFile(filePath, rebuilt, "utf-8");
275
+ }
276
+
277
+ async function appendBatch(
278
+ repoRoot: string,
279
+ entry: Suggestion,
280
+ ): Promise<void> {
281
+ const outPath = resolve(repoRoot, BATCH_FILE);
282
+ await appendFile(outPath, JSON.stringify(entry) + "\n", "utf-8");
283
+ }
284
+
285
+ class UsageError extends Error {}
286
+
287
+ function resolveFiles(repoRoot: string, inputs: string[]): string[] {
288
+ const out: string[] = [];
289
+ for (const raw of inputs) {
290
+ const abs = isAbsolute(raw) ? raw : resolve(repoRoot, raw);
291
+ if (!existsSync(abs)) {
292
+ throw new UsageError(`file not found: ${raw}`);
293
+ }
294
+ out.push(abs);
295
+ }
296
+ return out;
297
+ }
298
+
299
+ async function main(): Promise<void> {
300
+ const args = parseCliArgs(process.argv.slice(2));
301
+ if (args.help) {
302
+ printHelp();
303
+ return;
304
+ }
305
+
306
+ const repoRoot = process.cwd();
307
+ const { config } = await loadTagCliConfig(args.configInput, repoRoot);
308
+
309
+ if (args.files.length === 0) {
310
+ process.stderr.write(
311
+ "tags:suggest: no input files. Pass one or more `.mdx` paths, or run with --help.\n",
312
+ );
313
+ process.exit(1);
314
+ }
315
+
316
+ const files = resolveFiles(repoRoot, args.files);
317
+ const vocab = [...config.vocabulary];
318
+ const allowedIds = new Set(vocab.map((e) => e.id));
319
+
320
+ const isTty = Boolean(process.stdout.isTTY);
321
+ const batchMode = args.batch || !isTty;
322
+
323
+ // Dynamic import so the script's `--help` and arg parsing work even if
324
+ // `@inquirer/prompts` is momentarily unavailable in an odd environment.
325
+ type CheckboxFn = <V extends string>(config: {
326
+ message: string;
327
+ choices: { name: string; value: V; checked?: boolean }[];
328
+ loop?: boolean;
329
+ }) => Promise<V[]>;
330
+ let checkbox: CheckboxFn | null = null;
331
+ if (!batchMode) {
332
+ const mod = (await import("@inquirer/prompts")) as {
333
+ checkbox: CheckboxFn;
334
+ };
335
+ checkbox = mod.checkbox;
336
+ }
337
+
338
+ for (const filePath of files) {
339
+ const rel = relative(repoRoot, filePath);
340
+ const source = await readFile(filePath, "utf-8");
341
+ const parsed = matter(source);
342
+ const title =
343
+ typeof parsed.data.title === "string" ? parsed.data.title : rel;
344
+ const current = asStringArray(parsed.data.tags);
345
+
346
+ let suggested: string[];
347
+ try {
348
+ const prompt = buildPrompt(vocab, title, parsed.content);
349
+ const raw = await callOllama(args.host, args.model, prompt);
350
+ suggested = parseSuggestions(raw, allowedIds);
351
+ } catch (err) {
352
+ if (err instanceof OllamaError) {
353
+ process.stderr.write(`tags:suggest: ${err.message}\n`);
354
+ process.exit(2);
355
+ }
356
+ throw err;
357
+ }
358
+
359
+ if (suggested.length === 0) {
360
+ process.stdout.write(`${rel}: no vocabulary-matching suggestions\n`);
361
+ continue;
362
+ }
363
+
364
+ if (batchMode) {
365
+ await appendBatch(repoRoot, {
366
+ file: rel,
367
+ current,
368
+ suggested,
369
+ });
370
+ process.stdout.write(
371
+ `${rel}: recorded ${suggested.length} suggestion(s) to ${BATCH_FILE}\n`,
372
+ );
373
+ continue;
374
+ }
375
+
376
+ // Interactive approval: pre-check suggestions, show current tags for context.
377
+ const combined = Array.from(new Set([...current, ...suggested]));
378
+ const choices = combined.map((id) => {
379
+ const inSuggestion = suggested.includes(id);
380
+ const inCurrent = current.includes(id);
381
+ const label =
382
+ inSuggestion && inCurrent
383
+ ? `${id} (current, also suggested)`
384
+ : inSuggestion
385
+ ? `${id} (suggested)`
386
+ : `${id} (current)`;
387
+ return {
388
+ name: label,
389
+ value: id,
390
+ checked: inSuggestion || inCurrent,
391
+ };
392
+ });
393
+ if (!checkbox) {
394
+ // Should not happen — we set it above when !batchMode.
395
+ throw new Error("interactive prompt loader missing");
396
+ }
397
+ const picked = await checkbox<string>({
398
+ message: `${rel} — pick tags to write:`,
399
+ choices,
400
+ loop: false,
401
+ });
402
+ const nextTags = picked.slice();
403
+ if (
404
+ nextTags.length === current.length &&
405
+ nextTags.every((t, i) => t === current[i])
406
+ ) {
407
+ process.stdout.write(`${rel}: unchanged\n`);
408
+ continue;
409
+ }
410
+ await writeFrontmatterTags(filePath, parsed, nextTags);
411
+ process.stdout.write(
412
+ `${rel}: wrote tags [${nextTags.join(", ")}]\n`,
413
+ );
414
+ }
415
+ }
416
+
417
+ main().catch((err) => {
418
+ if (err instanceof OllamaError) {
419
+ process.stderr.write(`tags:suggest: ${err.message}\n`);
420
+ process.exit(2);
421
+ }
422
+ if (err instanceof UsageError) {
423
+ process.stderr.write(`tags:suggest: ${err.message}\n`);
424
+ process.exit(1);
425
+ }
426
+ const msg = err instanceof Error ? err.message : String(err);
427
+ process.stderr.write(`tags:suggest: ${msg}\n`);
428
+ process.exit(1);
429
+ });
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ // Package bin: local-LLM suggestions from an explicit project tag config.
3
+
4
+ import { spawnSync } from "node:child_process";
5
+ import { dirname, resolve } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const runnerPath = resolve(__dirname, "tags-suggest-runner.ts");
10
+ const tsxCli = fileURLToPath(import.meta.resolve("tsx/cli"));
11
+
12
+ const result = spawnSync(
13
+ process.execPath,
14
+ [tsxCli, runnerPath, ...process.argv.slice(2)],
15
+ {
16
+ stdio: "inherit",
17
+ env: process.env,
18
+ cwd: process.cwd(),
19
+ },
20
+ );
21
+
22
+ if (result.error) {
23
+ process.stderr.write(
24
+ `\n[tags-suggest] ERROR: Could not start the package tag-suggest runner.\n` +
25
+ ` Reinstall @takazudo/zudo-doc and try again.\n\n`,
26
+ );
27
+ process.exit(1);
28
+ }
29
+
30
+ process.exit(result.status ?? 1);
package/bin/zudo-doc.mjs CHANGED
@@ -1,24 +1,31 @@
1
1
  #!/usr/bin/env node
2
2
  // @takazudo/zudo-doc/bin/zudo-doc.mjs
3
3
  //
4
- // Package bin: `zudo-doc eject <component>` swizzle CLI.
4
+ // Package bin: `zudo-doc eject <component>` swizzle CLI, plus
5
+ // `zudo-doc theme list|apply <slug>` (issue #2824; ADR
6
+ // docs/adr/theme-packs.md).
5
7
  //
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`:
8
+ // Self-contained ESM — runs on plain `node` with NO `tsx` requirement. Both
9
+ // the eject and theme-cli logic are imported from the package's COMPILED
10
+ // `../dist/eject/index.js` / `../dist/theme-cli/index.js`, and the only
11
+ // runtime deps are `minimist` + `picocolors` (declared deps of this package,
12
+ // so they are present transitively in any consumer's node_modules). This is
13
+ // the key difference from the tsx-runner pattern used by `bin/tags-audit.mjs`:
11
14
  // 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).
15
+ // tsx), whereas eject/theme-cli only copy files / rewrite text — no TS eval
16
+ // needed — so both work in a default generated project that never installed
17
+ // tsx (#2367).
14
18
  //
15
19
  // Usage:
16
20
  // zudo-doc eject <component> # eject a component's TS source into the project
21
+ // zudo-doc theme list # list installed theme packs + the active one
22
+ // zudo-doc theme apply <slug> # rewrite zfb.config.ts's themePack field
17
23
  // zudo-doc --help # show help
18
24
 
19
25
  import minimist from "minimist";
20
26
  import pc from "picocolors";
21
27
  import { eject, EJECTABLE } from "../dist/eject/index.js";
28
+ import { applyThemePack, formatThemeList, listThemePacks } from "../dist/theme-cli/index.js";
22
29
 
23
30
  const argv = minimist(process.argv.slice(2), {
24
31
  boolean: ["help"],
@@ -31,8 +38,10 @@ function printHelp() {
31
38
  ${pc.bold("Usage:")} zudo-doc <subcommand> [options]
32
39
 
33
40
  ${pc.bold("Subcommands:")}
34
- eject <component> Copy a component's TS source into your project and
35
- rewrite imports so it resolves locally.
41
+ eject <component> Copy a component's TS source into your project and
42
+ rewrite imports so it resolves locally.
43
+ theme list List the installed theme packs and which one is active.
44
+ theme apply <slug> Rewrite zfb.config.ts's themePack field to <slug>.
36
45
 
37
46
  ${pc.bold("Ejectable components:")}
38
47
  ${validNames}
@@ -46,35 +55,79 @@ ${pc.bold("Examples:")}
46
55
 
47
56
  ${pc.dim("# Eject the theme-toggle component")}
48
57
  zudo-doc eject theme-toggle
58
+
59
+ ${pc.dim("# List installed theme packs")}
60
+ zudo-doc theme list
61
+
62
+ ${pc.dim("# Switch to the foundry theme pack")}
63
+ zudo-doc theme apply foundry
49
64
  `);
50
65
  }
51
66
 
52
- async function main() {
53
- if (argv["help"] || argv._.length === 0) {
54
- printHelp();
55
- process.exit(0);
67
+ async function runEject(componentArg) {
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);
56
75
  }
57
76
 
58
- const [subcommand, componentArg] = argv._;
77
+ await eject(componentArg, { cwd: process.cwd() });
78
+ }
79
+
80
+ async function runThemeList() {
81
+ const result = await listThemePacks({ cwd: process.cwd() });
82
+ console.log(formatThemeList(result));
83
+ }
59
84
 
60
- if (subcommand !== "eject") {
85
+ async function runThemeApply(slugArg) {
86
+ if (!slugArg) {
61
87
  console.error(
62
- pc.red(`Unknown subcommand "${subcommand}".`) +
63
- `\nRun \`zudo-doc --help\` for usage.`,
88
+ pc.red(`Missing theme pack slug.`) + `\nUsage: zudo-doc theme apply <slug>`,
64
89
  );
65
90
  process.exit(1);
66
91
  }
67
92
 
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
- );
93
+ const result = await applyThemePack(slugArg, { cwd: process.cwd() });
94
+ if (!result.ok) {
95
+ console.error(pc.red(result.message));
74
96
  process.exit(1);
75
97
  }
98
+ console.log(pc.green(result.message));
99
+ }
76
100
 
77
- await eject(componentArg, { cwd: process.cwd() });
101
+ async function runTheme(themeArgs) {
102
+ const [action, arg] = themeArgs;
103
+
104
+ if (action === "list") return runThemeList();
105
+ if (action === "apply") return runThemeApply(arg);
106
+
107
+ console.error(
108
+ pc.red(`Unknown "theme" action "${action}".`) +
109
+ `\nUsage: zudo-doc theme list` +
110
+ `\n zudo-doc theme apply <slug>`,
111
+ );
112
+ process.exit(1);
113
+ }
114
+
115
+ async function main() {
116
+ if (argv["help"] || argv._.length === 0) {
117
+ printHelp();
118
+ process.exit(0);
119
+ }
120
+
121
+ const [subcommand, ...rest] = argv._;
122
+
123
+ if (subcommand === "eject") return runEject(rest[0]);
124
+ if (subcommand === "theme") return runTheme(rest);
125
+
126
+ console.error(
127
+ pc.red(`Unknown subcommand "${subcommand}".`) +
128
+ `\nRun \`zudo-doc --help\` for usage.`,
129
+ );
130
+ process.exit(1);
78
131
  }
79
132
 
80
133
  main().catch((err) => {
@@ -1,7 +1,7 @@
1
1
  /** @jsxRuntime automatic */
2
2
  /** @jsxImportSource preact */
3
3
  import type { ComponentChildren, VNode } from "preact";
4
- import type { BreadcrumbItem, SidebarNode } from "./types.js";
4
+ import type { BreadcrumbItem, BreadcrumbNode } from "./types.js";
5
5
  /**
6
6
  * Convert a sidebar tree + the current page's id into the ordered list
7
7
  * of crumbs the renderer walks. The first crumb is the home rung
@@ -13,13 +13,13 @@ import type { BreadcrumbItem, SidebarNode } from "./types.js";
13
13
  * src/utils/docs.ts so the rendered output stays parity-equivalent
14
14
  * with the original Astro breadcrumb component.
15
15
  */
16
- export declare function buildBreadcrumbItems(tree: SidebarNode[], currentId: string, homeHref: string): BreadcrumbItem[];
16
+ export declare function buildBreadcrumbItems(tree: BreadcrumbNode[], currentId: string, homeHref: string): BreadcrumbItem[];
17
17
  export interface BreadcrumbProps {
18
18
  /** Pre-built items, or omit and pass `tree` + `currentId` instead. */
19
19
  items?: BreadcrumbItem[];
20
20
  /** Sidebar tree to derive the trail from. Required when `items` is omitted. */
21
- tree?: SidebarNode[];
22
- /** Id of the current page (matches SidebarNode.id). Required when `items` is omitted. */
21
+ tree?: BreadcrumbNode[];
22
+ /** Id of the current page (matches BreadcrumbNode.id). Required when `items` is omitted. */
23
23
  currentId?: string;
24
24
  /** Href for the leading home rung. Defaults to "/". */
25
25
  homeHref?: string;
@@ -1,4 +1,4 @@
1
- import type { BreadcrumbNode, SidebarNode } from "./types.js";
1
+ import type { BreadcrumbNode } from "./types.js";
2
2
  /**
3
3
  * Walk the sidebar tree and return the chain of nodes from the root
4
4
  * level down to (and including) the node whose `id` matches `targetId`.
@@ -10,4 +10,4 @@ import type { BreadcrumbNode, SidebarNode } from "./types.js";
10
10
  * ancestry. The home/root crumb is the renderer's responsibility — this
11
11
  * helper deals strictly with the tree itself.
12
12
  */
13
- export declare function findPath<T extends BreadcrumbNode = SidebarNode>(nodes: readonly T[], targetId: string): T[];
13
+ export declare function findPath<T extends BreadcrumbNode = BreadcrumbNode>(nodes: readonly T[], targetId: string): T[];
@@ -1,4 +1,4 @@
1
1
  export { Breadcrumb, buildBreadcrumbItems } from "./breadcrumb.js";
2
2
  export type { BreadcrumbProps } from "./breadcrumb.js";
3
3
  export { findPath } from "./find-path.js";
4
- export type { BreadcrumbItem, SidebarNode } from "./types.js";
4
+ export type { BreadcrumbItem, BreadcrumbNode } from "./types.js";
@@ -15,11 +15,6 @@ export interface BreadcrumbNode {
15
15
  sidebar_position?: number;
16
16
  children?: readonly BreadcrumbNode[];
17
17
  }
18
- /**
19
- * Backward-compatible alias retained for callers that previously imported
20
- * `SidebarNode` from this subpath.
21
- */
22
- export type SidebarNode = BreadcrumbNode;
23
18
  /**
24
19
  * One rung of the breadcrumb trail. `href` is omitted on the final
25
20
  * (current-page) item so the renderer can present it as plain text.