@takazudo/zudo-doc 5.28.2 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (511) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +49 -16
  3. package/bin/gen-z-index.mjs +18 -18
  4. package/dist/agent-docs/identity.d.ts +4 -0
  5. package/dist/agent-docs/identity.js +24 -0
  6. package/dist/agent-docs/index.d.ts +5 -0
  7. package/dist/agent-docs/index.js +5 -0
  8. package/dist/agent-docs/limits.d.ts +11 -0
  9. package/dist/agent-docs/limits.js +24 -0
  10. package/dist/agent-docs/search.d.ts +14 -0
  11. package/dist/agent-docs/search.js +82 -0
  12. package/dist/agent-docs/tokenize.d.ts +2 -0
  13. package/dist/agent-docs/tokenize.js +22 -0
  14. package/dist/agent-docs/types.d.ts +68 -0
  15. package/dist/agent-docs/types.js +0 -0
  16. package/dist/ai-chat-modal/index.d.ts +1 -4
  17. package/dist/ai-chat-modal/index.js +183 -123
  18. package/dist/asset-components/asset-card.d.ts +1 -2
  19. package/dist/asset-components/asset-card.js +12 -12
  20. package/dist/asset-components/asset-code.d.ts +1 -2
  21. package/dist/asset-components/asset-code.js +10 -10
  22. package/dist/asset-components/shared.d.ts +3 -4
  23. package/dist/asset-components/shared.js +4 -4
  24. package/dist/asset-index-page/body.d.ts +5 -6
  25. package/dist/asset-index-page/body.js +6 -6
  26. package/dist/asset-index-page/index.d.ts +1 -2
  27. package/dist/asset-index-page/index.js +1 -1
  28. package/dist/asset-page/body.d.ts +5 -9
  29. package/dist/asset-page/body.js +3 -3
  30. package/dist/asset-page/components.d.ts +16 -17
  31. package/dist/asset-page/components.js +10 -9
  32. package/dist/asset-page/index.d.ts +1 -2
  33. package/dist/asset-page/index.js +1 -1
  34. package/dist/auto-logo/index.d.ts +1 -2
  35. package/dist/auto-logo/index.js +2 -2
  36. package/dist/body-foot-util/body-foot-util-area.d.ts +3 -4
  37. package/dist/body-foot-util/body-foot-util-area.js +1 -2
  38. package/dist/body-foot-util/edit-link.d.ts +2 -3
  39. package/dist/body-foot-util/edit-link.js +1 -2
  40. package/dist/breadcrumb/breadcrumb.d.ts +6 -6
  41. package/dist/breadcrumb/breadcrumb.js +3 -4
  42. package/dist/catalog.js +1 -1
  43. package/dist/category-nav/index.d.ts +1 -2
  44. package/dist/category-nav/index.js +11 -5
  45. package/dist/category-tree-nav/index.d.ts +1 -2
  46. package/dist/category-tree-nav/index.js +3 -3
  47. package/dist/chrome/derive.d.ts +18 -27
  48. package/dist/chrome/derive.js +47 -29
  49. package/dist/chrome/index.d.ts +5 -5
  50. package/dist/chrome/index.js +1 -1
  51. package/dist/chrome/island-data.d.ts +4 -0
  52. package/dist/chrome/island-data.js +52 -0
  53. package/dist/chrome/primary-slots.d.ts +2 -3
  54. package/dist/chrome-bindings.d.ts +30 -22
  55. package/dist/code-group/index.d.ts +3 -4
  56. package/dist/code-group/index.js +3 -7
  57. package/dist/code-syntax/code-block-enhancer.d.ts +1 -2
  58. package/dist/code-syntax/code-block-enhancer.js +2 -2
  59. package/dist/code-syntax/mermaid-init.d.ts +1 -2
  60. package/dist/code-syntax/mermaid-init.js +5 -2
  61. package/dist/code-syntax/tabs-init-script.js +1 -1
  62. package/dist/code-syntax/tabs-init.d.ts +1 -2
  63. package/dist/code-syntax/tabs-init.js +2 -2
  64. package/dist/code-syntax/tabs.d.ts +6 -6
  65. package/dist/code-syntax/tabs.js +8 -8
  66. package/dist/compiled.css +2937 -2942
  67. package/dist/config/component-tokens.d.ts +1 -1
  68. package/dist/config/component-tokens.js +22 -0
  69. package/dist/config-assertions/index.d.ts +13 -0
  70. package/dist/config-assertions/index.js +14 -0
  71. package/dist/config.d.ts +36 -17
  72. package/dist/config.js +23 -95
  73. package/dist/content/content-blockquote.d.ts +2 -3
  74. package/dist/content/content-blockquote.js +3 -3
  75. package/dist/content/content-code.d.ts +3 -4
  76. package/dist/content/content-code.js +5 -5
  77. package/dist/content/content-link.d.ts +3 -4
  78. package/dist/content/content-link.js +10 -10
  79. package/dist/content/content-ol.d.ts +2 -3
  80. package/dist/content/content-ol.js +5 -4
  81. package/dist/content/content-paragraph.d.ts +2 -3
  82. package/dist/content/content-paragraph.js +1 -1
  83. package/dist/content/content-strong.d.ts +2 -3
  84. package/dist/content/content-strong.js +3 -3
  85. package/dist/content/content-table.d.ts +2 -3
  86. package/dist/content/content-table.js +4 -4
  87. package/dist/content/content-ul.d.ts +2 -3
  88. package/dist/content/content-ul.js +4 -4
  89. package/dist/content/heading-h2.d.ts +2 -3
  90. package/dist/content/heading-h2.js +4 -4
  91. package/dist/content/heading-h3.d.ts +2 -3
  92. package/dist/content/heading-h3.js +4 -4
  93. package/dist/content/heading-h4.d.ts +2 -3
  94. package/dist/content/heading-h4.js +4 -4
  95. package/dist/content-admonition/index.d.ts +3 -4
  96. package/dist/content-admonition/index.js +2 -2
  97. package/dist/content.css +35 -33
  98. package/dist/design-token-panel-bootstrap-controller.d.ts +178 -0
  99. package/dist/design-token-panel-bootstrap-controller.js +359 -0
  100. package/dist/design-token-panel-bootstrap-island.d.ts +5 -0
  101. package/dist/design-token-panel-bootstrap-island.js +19 -0
  102. package/dist/design-token-panel-bootstrap.d.ts +5 -195
  103. package/dist/design-token-panel-bootstrap.js +3 -288
  104. package/dist/desktop-sidebar-toggle-island/index.d.ts +2 -7
  105. package/dist/desktop-sidebar-toggle-island/index.js +2 -58
  106. package/dist/desktop-sidebar-toggle-island/island.d.ts +4 -0
  107. package/dist/desktop-sidebar-toggle-island/island.js +43 -0
  108. package/dist/desktop-sidebar-toggle-island/storage.d.ts +4 -0
  109. package/dist/desktop-sidebar-toggle-island/storage.js +29 -0
  110. package/dist/desktop-toc-toggle-island/index.d.ts +2 -7
  111. package/dist/desktop-toc-toggle-island/index.js +2 -58
  112. package/dist/desktop-toc-toggle-island/island.d.ts +4 -0
  113. package/dist/desktop-toc-toggle-island/island.js +43 -0
  114. package/dist/desktop-toc-toggle-island/storage.d.ts +4 -0
  115. package/dist/desktop-toc-toggle-island/storage.js +29 -0
  116. package/dist/details/details.d.ts +3 -4
  117. package/dist/details/details.js +1 -1
  118. package/dist/doc-body-end/index.d.ts +1 -2
  119. package/dist/doc-body-end/index.js +1 -1
  120. package/dist/doc-body-end-islands/design-token-panel-island.d.ts +2 -2
  121. package/dist/doc-body-end-islands/design-token-panel-island.js +3 -7
  122. package/dist/doc-body-end-islands/index.d.ts +4 -21
  123. package/dist/doc-body-end-islands/index.js +5 -3
  124. package/dist/doc-body-end-islands/theme-pack-switcher-island.d.ts +3 -13
  125. package/dist/doc-body-end-islands/theme-pack-switcher-island.js +11 -13
  126. package/dist/doc-content-header/index.d.ts +1 -2
  127. package/dist/doc-content-header/index.js +1 -1
  128. package/dist/doc-history/index.d.ts +1 -1
  129. package/dist/doc-history/index.js +166 -141
  130. package/dist/doc-history-area/index.d.ts +6 -4
  131. package/dist/doc-history-area/index.js +17 -16
  132. package/dist/doc-metainfo-area/index.d.ts +2 -3
  133. package/dist/doc-metainfo-area/index.js +1 -1
  134. package/dist/doc-page-renderer/index.d.ts +13 -7
  135. package/dist/doc-page-renderer/index.js +10 -1
  136. package/dist/doc-page-shell/index.d.ts +20 -7
  137. package/dist/doc-page-shell/index.js +17 -6
  138. package/dist/doc-pager/index.d.ts +1 -2
  139. package/dist/doc-pager/index.js +3 -3
  140. package/dist/doc-tags-area/index.d.ts +2 -3
  141. package/dist/doc-tags-area/index.js +1 -1
  142. package/dist/doclayout/doc-layout-with-defaults.d.ts +9 -9
  143. package/dist/doclayout/doc-layout-with-defaults.js +5 -15
  144. package/dist/doclayout/doc-layout.d.ts +26 -27
  145. package/dist/doclayout/doc-layout.js +23 -14
  146. package/dist/docs-schema/index.d.ts +4 -0
  147. package/dist/docs-schema/index.js +1 -0
  148. package/dist/extract-headings/index.js +43 -12
  149. package/dist/factory-context/index.d.ts +22 -19
  150. package/dist/features.css +75 -8
  151. package/dist/find-in-page/find-bar.d.ts +4 -2
  152. package/dist/find-in-page/find-bar.js +58 -69
  153. package/dist/find-in-page/index.d.ts +2 -8
  154. package/dist/find-in-page/index.js +33 -30
  155. package/dist/footer/footer.d.ts +3 -4
  156. package/dist/footer/footer.js +3 -3
  157. package/dist/footer-with-defaults/index.d.ts +2 -3
  158. package/dist/footer-with-defaults/index.js +1 -1
  159. package/dist/frontmatter-preview-defaults/index.js +1 -0
  160. package/dist/head/doc-head.d.ts +1 -1
  161. package/dist/head/doc-head.js +24 -32
  162. package/dist/head/og-tags.d.ts +1 -1
  163. package/dist/head/og-tags.js +8 -7
  164. package/dist/head/twitter-card.d.ts +1 -1
  165. package/dist/head/twitter-card.js +1 -1
  166. package/dist/head-with-defaults/index.d.ts +7 -2
  167. package/dist/head-with-defaults/index.js +46 -32
  168. package/dist/header/header.d.ts +8 -8
  169. package/dist/header/header.js +9 -11
  170. package/dist/header/types.d.ts +7 -7
  171. package/dist/header-with-defaults/index.d.ts +3 -4
  172. package/dist/header-with-defaults/index.js +27 -14
  173. package/dist/home-intro/index.d.ts +2 -5
  174. package/dist/home-intro/index.js +7 -5
  175. package/dist/home-intro/prepare.js +4 -3
  176. package/dist/home-page/index.d.ts +3 -3
  177. package/dist/home-page/index.js +19 -17
  178. package/dist/html-preview-wrapper/highlighted-code.d.ts +3 -4
  179. package/dist/html-preview-wrapper/highlighted-code.js +19 -17
  180. package/dist/html-preview-wrapper/html-preview-wrapper.d.ts +4 -25
  181. package/dist/html-preview-wrapper/html-preview-wrapper.js +2 -87
  182. package/dist/html-preview-wrapper/html-preview.d.ts +3 -10
  183. package/dist/html-preview-wrapper/html-preview.js +13 -37
  184. package/dist/html-preview-wrapper/index.d.ts +2 -1
  185. package/dist/html-preview-wrapper/index.js +1 -1
  186. package/dist/html-preview-wrapper/preview-base.d.ts +3 -5
  187. package/dist/html-preview-wrapper/preview-base.js +56 -59
  188. package/dist/html-preview-wrapper/wrapper.d.ts +20 -0
  189. package/dist/html-preview-wrapper/wrapper.js +37 -0
  190. package/dist/hydration-pending.d.ts +3 -5
  191. package/dist/hydration-pending.js +7 -5
  192. package/dist/i18n-version/language-switcher.d.ts +5 -32
  193. package/dist/i18n-version/language-switcher.js +5 -106
  194. package/dist/i18n-version/switcher-generated-scripts.d.ts +2 -0
  195. package/dist/i18n-version/switcher-generated-scripts.js +107 -0
  196. package/dist/i18n-version/switcher-url-state.d.ts +76 -0
  197. package/dist/i18n-version/switcher-url-state.js +103 -0
  198. package/dist/i18n-version/version-banner.d.ts +2 -3
  199. package/dist/i18n-version/version-banner.js +1 -1
  200. package/dist/i18n-version/version-switcher.d.ts +5 -50
  201. package/dist/i18n-version/version-switcher.js +5 -157
  202. package/dist/icons/index.d.ts +18 -19
  203. package/dist/icons/index.js +17 -33
  204. package/dist/image-enlarge/index.d.ts +1 -10
  205. package/dist/image-enlarge/index.js +80 -55
  206. package/dist/image-enlarge/public.d.ts +2 -0
  207. package/dist/image-enlarge/public.js +6 -0
  208. package/dist/image-enlarge/ssr-fallback.d.ts +2 -0
  209. package/dist/image-enlarge/ssr-fallback.js +8 -0
  210. package/dist/index.d.ts +11 -4
  211. package/dist/inline-version-switcher/index.d.ts +1 -2
  212. package/dist/inline-version-switcher/index.js +1 -1
  213. package/dist/island-types/index.d.ts +2 -2
  214. package/dist/island-types/index.js +1 -5
  215. package/dist/math-block/index.d.ts +4 -7
  216. package/dist/math-block/index.js +5 -5
  217. package/dist/mcp/handler.d.ts +8 -0
  218. package/dist/mcp/handler.js +129 -0
  219. package/dist/mcp/index.d.ts +4 -0
  220. package/dist/mcp/index.js +7 -0
  221. package/dist/mcp/loader.d.ts +19 -0
  222. package/dist/mcp/loader.js +202 -0
  223. package/dist/mdx-components/index.js +40 -108
  224. package/dist/mermaid-enlarge/index.d.ts +1 -8
  225. package/dist/mermaid-enlarge/index.js +174 -158
  226. package/dist/mermaid-enlarge/public.d.ts +2 -0
  227. package/dist/mermaid-enlarge/public.js +6 -0
  228. package/dist/mermaid-enlarge/ssr-fallback.d.ts +2 -0
  229. package/dist/mermaid-enlarge/ssr-fallback.js +8 -0
  230. package/dist/metainfo/doc-metainfo.d.ts +2 -3
  231. package/dist/metainfo/doc-metainfo.js +1 -4
  232. package/dist/metainfo/doc-tags.d.ts +2 -3
  233. package/dist/metainfo/doc-tags.js +1 -1
  234. package/dist/metainfo/frontmatter-preview.d.ts +3 -4
  235. package/dist/metainfo/frontmatter-preview.js +1 -1
  236. package/dist/nav-data-prep/index.js +6 -4
  237. package/dist/nav-indexing/category-nav.d.ts +2 -3
  238. package/dist/nav-indexing/category-nav.js +1 -2
  239. package/dist/nav-indexing/category-tree-nav.d.ts +2 -3
  240. package/dist/nav-indexing/category-tree-nav.js +1 -1
  241. package/dist/nav-indexing/doc-card-grid.d.ts +2 -3
  242. package/dist/nav-indexing/doc-card-grid.js +1 -2
  243. package/dist/nav-indexing/docs-sitemap.d.ts +2 -3
  244. package/dist/nav-indexing/docs-sitemap.js +2 -2
  245. package/dist/nav-indexing/index.d.ts +1 -1
  246. package/dist/nav-indexing/nav-card-grid.d.ts +2 -3
  247. package/dist/nav-indexing/nav-card-grid.js +1 -2
  248. package/dist/nav-indexing/note-tray-index-parts/card-list.d.ts +1 -2
  249. package/dist/nav-indexing/note-tray-index-parts/card-list.js +4 -4
  250. package/dist/nav-indexing/note-tray-index-parts/date-line.d.ts +2 -3
  251. package/dist/nav-indexing/note-tray-index-parts/date-line.js +1 -1
  252. package/dist/nav-indexing/note-tray-index-parts/index-list.d.ts +1 -2
  253. package/dist/nav-indexing/note-tray-index-parts/index-list.js +2 -2
  254. package/dist/nav-indexing/note-tray-index-parts/timeline.d.ts +1 -2
  255. package/dist/nav-indexing/note-tray-index-parts/timeline.js +2 -2
  256. package/dist/nav-indexing/note-tray-index.d.ts +1 -2
  257. package/dist/nav-indexing/note-tray-index.js +1 -1
  258. package/dist/nav-indexing/site-tree-nav-demo.d.ts +2 -3
  259. package/dist/nav-indexing/site-tree-nav-demo.js +2 -2
  260. package/dist/nav-indexing/tag-nav.d.ts +1 -2
  261. package/dist/nav-indexing/tag-nav.js +1 -1
  262. package/dist/nav-indexing/types.d.ts +2 -2
  263. package/dist/nav-indexing/versions-page-content.d.ts +2 -3
  264. package/dist/nav-indexing/versions-page-content.js +8 -8
  265. package/dist/note-tray-index/index.d.ts +1 -2
  266. package/dist/note-tray-index/index.js +1 -1
  267. package/dist/page-loading/page-loading-overlay.d.ts +1 -2
  268. package/dist/page-loading/page-loading-overlay.js +3 -5
  269. package/dist/plugins/agent-export.d.ts +3 -0
  270. package/dist/plugins/agent-export.js +34 -0
  271. package/dist/plugins/internal/agent-export/chunk.d.ts +5 -0
  272. package/dist/plugins/internal/agent-export/chunk.js +90 -0
  273. package/dist/plugins/internal/agent-export/index.d.ts +24 -0
  274. package/dist/plugins/internal/agent-export/index.js +123 -0
  275. package/dist/plugins/internal/agent-export/normalize.d.ts +5 -0
  276. package/dist/plugins/internal/agent-export/normalize.js +73 -0
  277. package/dist/plugins/internal/llms-txt/dev-middleware.d.ts +6 -0
  278. package/dist/plugins/internal/llms-txt/dev-middleware.js +8 -3
  279. package/dist/plugins/internal/llms-txt/emit.js +23 -6
  280. package/dist/plugins/internal/llms-txt/generate.d.ts +7 -1
  281. package/dist/plugins/internal/llms-txt/generate.js +9 -2
  282. package/dist/plugins/internal/llms-txt/load.d.ts +2 -0
  283. package/dist/plugins/internal/llms-txt/load.js +20 -4
  284. package/dist/plugins/internal/llms-txt/types.d.ts +10 -0
  285. package/dist/plugins/routes.js +4 -0
  286. package/dist/preset.d.ts +19 -9
  287. package/dist/preset.js +20 -1
  288. package/dist/route-context-payload/index.js +1 -1
  289. package/dist/routes/404.d.ts +2 -3
  290. package/dist/routes/404.js +1 -1
  291. package/dist/routes/_chrome.d.ts +5 -5
  292. package/dist/routes/_chrome.js +7 -2
  293. package/dist/routes/_context.d.ts +4 -4
  294. package/dist/routes/_design-token-panel-bootstrap.d.ts +1 -4
  295. package/dist/routes/_design-token-panel-bootstrap.js +9 -1
  296. package/dist/routes/docs-slug.d.ts +2 -3
  297. package/dist/routes/docs-tags-index.d.ts +2 -3
  298. package/dist/routes/docs-tags-index.js +1 -1
  299. package/dist/routes/docs-tags-tag.d.ts +2 -3
  300. package/dist/routes/docs-tags-tag.js +1 -1
  301. package/dist/routes/docs-versions.d.ts +2 -3
  302. package/dist/routes/docs-versions.js +1 -1
  303. package/dist/routes/files-path.d.ts +2 -3
  304. package/dist/routes/files-path.js +1 -1
  305. package/dist/routes/index.d.ts +2 -3
  306. package/dist/routes/index.js +1 -1
  307. package/dist/routes/locale-docs-slug.d.ts +2 -3
  308. package/dist/routes/locale-docs-tags-index.d.ts +2 -3
  309. package/dist/routes/locale-docs-tags-index.js +1 -1
  310. package/dist/routes/locale-docs-tags-tag.d.ts +2 -3
  311. package/dist/routes/locale-docs-tags-tag.js +1 -1
  312. package/dist/routes/locale-docs-versions.d.ts +2 -3
  313. package/dist/routes/locale-docs-versions.js +1 -1
  314. package/dist/routes/locale-files-path.d.ts +2 -3
  315. package/dist/routes/locale-files-path.js +1 -1
  316. package/dist/routes/locale-index.d.ts +2 -3
  317. package/dist/routes/locale-index.js +1 -1
  318. package/dist/routes/mcp.d.ts +5 -0
  319. package/dist/routes/mcp.js +25 -0
  320. package/dist/routes/v-docs-slug.d.ts +2 -3
  321. package/dist/routes/v-locale-docs-slug.d.ts +2 -3
  322. package/dist/search-widget/index.d.ts +1 -2
  323. package/dist/search-widget/index.js +4 -7
  324. package/dist/search-widget-script/generated-script.js +12 -5
  325. package/dist/settings-defaults.d.ts +2 -0
  326. package/dist/settings-defaults.js +93 -0
  327. package/dist/settings.d.ts +8 -2
  328. package/dist/sidebar/client-data.d.ts +18 -0
  329. package/dist/sidebar/client-data.js +47 -0
  330. package/dist/sidebar/index.d.ts +3 -1
  331. package/dist/sidebar/index.js +3 -1
  332. package/dist/sidebar/sidebar.d.ts +12 -44
  333. package/dist/sidebar/sidebar.js +4 -3
  334. package/dist/sidebar/types.d.ts +14 -0
  335. package/dist/sidebar-prepaint/index.d.ts +1 -2
  336. package/dist/sidebar-prepaint/index.js +4 -6
  337. package/dist/sidebar-resizer/sidebar-resizer-init.d.ts +2 -3
  338. package/dist/sidebar-resizer/sidebar-resizer-init.js +3 -3
  339. package/dist/sidebar-toggle-island/index.d.ts +8 -11
  340. package/dist/sidebar-toggle-island/index.js +75 -62
  341. package/dist/sidebar-tree/build-tree.js +5 -6
  342. package/dist/sidebar-tree/types.d.ts +2 -0
  343. package/dist/sidebar-tree-island/index.d.ts +4 -19
  344. package/dist/sidebar-tree-island/index.js +782 -436
  345. package/dist/sidebar-tree-island/scope-state.d.ts +38 -0
  346. package/dist/sidebar-tree-island/scope-state.js +151 -0
  347. package/dist/sidebar-tree-island/sidebar-scroll-preserve.js +6 -2
  348. package/dist/sidebar-utils/index.d.ts +17 -19
  349. package/dist/sidebar-utils/index.js +100 -3
  350. package/dist/sidebar-utils/scope.d.ts +11 -0
  351. package/dist/sidebar-utils/scope.js +67 -0
  352. package/dist/sidebar-with-defaults/index.d.ts +1 -2
  353. package/dist/sidebar-with-defaults/index.js +19 -18
  354. package/dist/site-schema/nav-tree.js +15 -15
  355. package/dist/site-schema/types.d.ts +1 -0
  356. package/dist/site-tree-nav/index.d.ts +2 -3
  357. package/dist/site-tree-nav/index.js +11 -12
  358. package/dist/site-tree-nav/normalize-island-props.d.ts +16 -0
  359. package/dist/site-tree-nav/normalize-island-props.js +122 -0
  360. package/dist/site-tree-nav-island/index.d.ts +3 -3
  361. package/dist/site-tree-nav-island/index.js +243 -144
  362. package/dist/smart-break/index.d.ts +3 -4
  363. package/dist/smart-break/index.js +1 -1
  364. package/dist/tab-item/tab-item.d.ts +3 -4
  365. package/dist/tab-item/tab-item.js +1 -1
  366. package/dist/tag-pages/index.d.ts +3 -3
  367. package/dist/tag-pages/index.js +10 -10
  368. package/dist/theme/color-scheme-provider.d.ts +3 -3
  369. package/dist/theme/color-scheme-provider.js +3 -3
  370. package/dist/theme/theme-pack-provider.d.ts +1 -1
  371. package/dist/theme/theme-pack-provider.js +3 -3
  372. package/dist/theme/theme-toggle.d.ts +2 -3
  373. package/dist/theme/theme-toggle.js +1 -1
  374. package/dist/theme-pack-dialog/index.d.ts +1 -1
  375. package/dist/theme-pack-dialog/index.js +69 -75
  376. package/dist/theme-pack-dialog/theme-pack-card.d.ts +3 -4
  377. package/dist/theme-pack-dialog/theme-pack-card.js +19 -17
  378. package/dist/theme-pack-switcher/index.d.ts +4 -5
  379. package/dist/theme-pack-switcher/index.js +52 -49
  380. package/dist/theme-packs/academia/meta.json +1 -1
  381. package/dist/theme-packs/academia/pack.css +1 -1
  382. package/dist/theme-packs/bauhaus/meta.json +1 -1
  383. package/dist/theme-packs/bauhaus/pack.css +2 -2
  384. package/dist/theme-packs/blueprint/meta.json +1 -1
  385. package/dist/theme-packs/blueprint/pack.css +2 -2
  386. package/dist/theme-packs/botanica/meta.json +1 -1
  387. package/dist/theme-packs/botanica/pack.css +2 -2
  388. package/dist/theme-packs/broadsheet/meta.json +1 -1
  389. package/dist/theme-packs/broadsheet/pack.css +2 -2
  390. package/dist/theme-packs/brutalist/meta.json +1 -1
  391. package/dist/theme-packs/brutalist/pack.css +2 -2
  392. package/dist/theme-packs/eink/meta.json +1 -1
  393. package/dist/theme-packs/eink/pack.css +1 -1
  394. package/dist/theme-packs/futura-editorial/meta.json +1 -1
  395. package/dist/theme-packs/futura-editorial/pack.css +2 -2
  396. package/dist/theme-packs/hearth/meta.json +1 -1
  397. package/dist/theme-packs/hearth/pack.css +2 -2
  398. package/dist/theme-packs/ledger/meta.json +1 -1
  399. package/dist/theme-packs/ledger/pack.css +1 -1
  400. package/dist/theme-packs/manuscript/meta.json +1 -1
  401. package/dist/theme-packs/manuscript/pack.css +1 -1
  402. package/dist/theme-packs/matcha/meta.json +1 -1
  403. package/dist/theme-packs/matcha/pack.css +2 -2
  404. package/dist/theme-packs/nocturne/meta.json +1 -1
  405. package/dist/theme-packs/nocturne/pack.css +2 -2
  406. package/dist/theme-packs/observatory/meta.json +1 -1
  407. package/dist/theme-packs/observatory/pack.css +2 -2
  408. package/dist/theme-packs/onyx/meta.json +1 -1
  409. package/dist/theme-packs/onyx/pack.css +2 -2
  410. package/dist/theme-packs/phosphor/meta.json +1 -1
  411. package/dist/theme-packs/phosphor/pack.css +2 -2
  412. package/dist/theme-packs/riso/meta.json +1 -1
  413. package/dist/theme-packs/riso/pack.css +2 -2
  414. package/dist/theme-packs/sakura/meta.json +1 -1
  415. package/dist/theme-packs/sakura/pack.css +2 -2
  416. package/dist/theme-packs/scandi/meta.json +1 -1
  417. package/dist/theme-packs/scandi/pack.css +1 -1
  418. package/dist/theme-packs/solar/meta.json +1 -1
  419. package/dist/theme-packs/solar/pack.css +1 -1
  420. package/dist/theme-packs/sumi/meta.json +1 -1
  421. package/dist/theme-packs/sumi/pack.css +2 -2
  422. package/dist/theme-packs/swissgrid/meta.json +1 -1
  423. package/dist/theme-packs/swissgrid/pack.css +1 -1
  424. package/dist/theme-packs/timberline/meta.json +1 -1
  425. package/dist/theme-packs/timberline/pack.css +2 -2
  426. package/dist/theme-packs/washi/meta.json +1 -1
  427. package/dist/theme-packs/washi/pack.css +2 -2
  428. package/dist/theme-toggle/index.d.ts +1 -1
  429. package/dist/theme-toggle/index.js +154 -167
  430. package/dist/theme.css +47 -121
  431. package/dist/toc/mobile-toc.d.ts +3 -5
  432. package/dist/toc/mobile-toc.js +29 -27
  433. package/dist/toc/toc.d.ts +3 -4
  434. package/dist/toc/toc.js +18 -18
  435. package/dist/toc/use-active-heading.d.ts +5 -4
  436. package/dist/toc/use-active-heading.js +36 -30
  437. package/dist/toc-prepaint/index.d.ts +1 -2
  438. package/dist/toc-prepaint/index.js +4 -6
  439. package/dist/transitions/nested-island-props-refresh.d.ts +3 -16
  440. package/dist/transitions/nested-island-props-refresh.js +133 -151
  441. package/dist/transitions/page-events.d.ts +8 -13
  442. package/dist/tree-nav-shared/index.d.ts +4 -5
  443. package/dist/tree-nav-shared/index.js +6 -7
  444. package/dist/use-modal-dialog/index.d.ts +9 -63
  445. package/dist/use-modal-dialog/index.js +41 -59
  446. package/dist/versions-page/index.d.ts +3 -3
  447. package/dist/versions-page/index.js +5 -5
  448. package/dist/wind/index.d.ts +5 -0
  449. package/dist/wind/index.js +169 -0
  450. package/dist/wind.json +498 -0
  451. package/dist/z-index-defaults/index.d.ts +4 -4
  452. package/dist/zdtp-loader.d.ts +7 -0
  453. package/dist/zdtp-loader.js +8 -0
  454. package/eject/asset-components/asset-card.tsx +14 -16
  455. package/eject/asset-components/asset-code.tsx +10 -12
  456. package/eject/asset-components/shared.tsx +5 -7
  457. package/eject/breadcrumb/breadcrumb.tsx +15 -16
  458. package/eject/code-group/index.tsx +4 -10
  459. package/eject/content-admonition/index.tsx +5 -6
  460. package/eject/desktop-sidebar-toggle-island/index.ts +4 -0
  461. package/eject/desktop-sidebar-toggle-island/island.tsx +50 -0
  462. package/eject/desktop-sidebar-toggle-island/storage.ts +27 -0
  463. package/eject/details/details.tsx +3 -5
  464. package/eject/doc-history/index.tsx +193 -179
  465. package/eject/doc-pager/index.tsx +3 -4
  466. package/eject/footer/footer.tsx +7 -9
  467. package/eject/header/header.tsx +42 -54
  468. package/eject/header/nav-overflow-script.ts +1 -1
  469. package/eject/header/types.ts +12 -13
  470. package/eject/image-enlarge/index.tsx +84 -69
  471. package/eject/image-enlarge/public.ts +2 -0
  472. package/eject/image-enlarge/ssr-fallback.tsx +7 -0
  473. package/eject/page-loading/page-loading-overlay.tsx +8 -15
  474. package/eject/sidebar/client-data.ts +68 -0
  475. package/eject/sidebar/index.ts +3 -0
  476. package/eject/sidebar/sidebar.tsx +16 -97
  477. package/eject/sidebar/types.ts +15 -0
  478. package/eject/sidebar-toggle-island/index.tsx +103 -102
  479. package/eject/sidebar-tree-island/index.tsx +827 -615
  480. package/eject/sidebar-tree-island/scope-state.ts +154 -0
  481. package/eject/sidebar-tree-island/sidebar-scroll-preserve.ts +8 -2
  482. package/eject/site-tree-nav-island/index.tsx +349 -222
  483. package/eject/tab-item/tab-item.tsx +2 -4
  484. package/eject/theme-toggle/hydration-pending.ts +6 -10
  485. package/eject/theme-toggle/index.tsx +133 -173
  486. package/eject/toc/mobile-toc.tsx +27 -31
  487. package/eject/toc/toc.tsx +21 -24
  488. package/eject/toc/use-active-heading.ts +43 -38
  489. package/package.json +40 -22
  490. package/routes-src/404.tsx +2 -3
  491. package/routes-src/_chrome.tsx +7 -2
  492. package/routes-src/_design-token-panel-bootstrap.tsx +1 -63
  493. package/routes-src/docs-slug.tsx +2 -3
  494. package/routes-src/docs-tags-index.tsx +2 -3
  495. package/routes-src/docs-tags-tag.tsx +2 -3
  496. package/routes-src/docs-versions.tsx +2 -3
  497. package/routes-src/files-path.tsx +2 -4
  498. package/routes-src/index.tsx +2 -3
  499. package/routes-src/locale-docs-slug.tsx +2 -3
  500. package/routes-src/locale-docs-tags-index.tsx +2 -3
  501. package/routes-src/locale-docs-tags-tag.tsx +2 -3
  502. package/routes-src/locale-docs-versions.tsx +2 -3
  503. package/routes-src/locale-files-path.tsx +2 -4
  504. package/routes-src/locale-index.tsx +2 -3
  505. package/routes-src/mcp.tsx +25 -0
  506. package/routes-src/v-docs-slug.tsx +2 -3
  507. package/routes-src/v-locale-docs-slug.tsx +2 -3
  508. package/tsconfig.base.json +1 -1
  509. package/dist/safelist.css +0 -2
  510. package/dist/theme-no-reset.css +0 -379
  511. package/eject/desktop-sidebar-toggle-island/index.tsx +0 -110
@@ -1,6 +1,4 @@
1
1
  /** @jsxRuntime automatic */
2
- /** @jsxImportSource preact */
3
-
4
2
  // JSX port of the legacy `tab-item` component.
5
3
  //
6
4
  // Visual wrapper for one tab panel inside a Tabs group. Server-rendered,
@@ -8,7 +6,7 @@
8
6
  // `data-tab-label` / `data-tab-default` to wire interactivity, so the
9
7
  // attribute shape here must mirror the Astro original byte-for-byte.
10
8
 
11
- import type { ComponentChildren } from "preact";
9
+ import type { Child } from "@takazudo/zfb/zudo-react";
12
10
 
13
11
  export interface TabItemProps {
14
12
  /** Human-readable label rendered by the parent Tabs trigger row. */
@@ -25,7 +23,7 @@ export interface TabItemProps {
25
23
  */
26
24
  default?: boolean;
27
25
  /** Panel body. */
28
- children?: ComponentChildren;
26
+ children?: Child;
29
27
  }
30
28
 
31
29
  /**
@@ -1,13 +1,9 @@
1
- import { useEffect, useState } from "preact/hooks";
2
-
3
- /**
4
- * Keep an interactive control pending until its first successful client mount.
5
- * Initializing from `enabled` preserves server/client-first-render parity.
6
- */
7
- export function useHydrationPending(enabled: boolean): boolean {
8
- const [pending, setPending] = useState(enabled);
9
-
10
- useEffect(() => setPending(false), []);
1
+ import { signal } from "@takazudo/zfb/zudo-react";
2
+ import type { ReadonlySignal, Scope } from "@takazudo/zfb/zudo-react";
11
3
 
4
+ /** Keep SSR and the first client render pending, then release on activation. */
5
+ export function hydrationPending(scope: Scope, enabled: boolean): ReadonlySignal<boolean> {
6
+ const pending = signal(enabled);
7
+ scope.onActivate(() => { pending.value = false; });
12
8
  return pending;
13
9
  }
@@ -1,10 +1,7 @@
1
1
  "use client";
2
2
 
3
- /** @jsxRuntime automatic */
4
- /** @jsxImportSource preact */
5
- import { useState, useEffect, useRef } from "preact/hooks";
6
- import { createPortal } from "preact/compat";
7
- import { useHydrationPending } from "./hydration-pending.js";
3
+ import { computed, getScope, Show, signal, type ReadonlySignal, type Ref } from "@takazudo/zfb/zudo-react";
4
+ import { hydrationPending } from "./hydration-pending.js";
8
5
  import { AFTER_NAVIGATE_EVENT } from "../transitions/index.js";
9
6
  import {
10
7
  applyThemePreference,
@@ -21,16 +18,12 @@ let menuSequence = 0;
21
18
 
22
19
  function PreferenceIcon({ preference }: { preference: ThemePreference }) {
23
20
  return (
24
- <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="20" height="20"
25
- viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2"
26
- strokeLinecap="square" strokeLinejoin="miter">
27
- {preference === "light" ? (
28
- <><circle cx="12" cy="12" r="3.6" /><path d="M12 2.5V6M12 18V21.5M2.5 12H6M18 12H21.5M5.3 5.3L7.8 7.8M16.2 16.2L18.7 18.7M5.3 18.7L7.8 16.2M16.2 7.8L18.7 5.3" /></>
29
- ) : preference === "dark" ? (
30
- <path d="M10.2 2.9C5.7 3.9 2.6 7.9 3 12.6C3.4 17.7 7.9 21.5 13 21.1C16.9 20.7 20.1 17.9 21 14.1C18.6 15.7 15.6 15.8 13.2 14.3C9.3 12 8 6.8 10.2 2.9Z" />
31
- ) : (
32
- <path d="M2.5 3.5H21.5V16.5H2.5ZM12 16.5V20.5M7.5 20.5H16.5" />
33
- )}
21
+ <svg aria-hidden="true" width="20" height="20"
22
+ viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.2"
23
+ stroke-linecap="square" stroke-linejoin="miter">
24
+ <Show when={computed(() => preference === "light")}>{() => <><circle cx="12" cy="12" r="3.6" /><path d="M12 2.5V6M12 18V21.5M2.5 12H6M18 12H21.5M5.3 5.3L7.8 7.8M16.2 16.2L18.7 18.7M5.3 18.7L7.8 16.2M16.2 7.8L18.7 5.3" /></>}</Show>
25
+ <Show when={computed(() => preference === "dark")}>{() => <path d="M10.2 2.9C5.7 3.9 2.6 7.9 3 12.6C3.4 17.7 7.9 21.5 13 21.1C16.9 20.7 20.1 17.9 21 14.1C18.6 15.7 15.6 15.8 13.2 14.3C9.3 12 8 6.8 10.2 2.9Z" />}</Show>
26
+ <Show when={computed(() => preference === "system")}>{() => <path d="M2.5 3.5H21.5V16.5H2.5ZM12 16.5V20.5M7.5 20.5H16.5" />}</Show>
34
27
  </svg>
35
28
  );
36
29
  }
@@ -59,198 +52,165 @@ export interface ThemeToggleProps {
59
52
  pendingUntilHydrated?: boolean;
60
53
  }
61
54
 
62
- // Keep the named export: zfb's island scanner keys on the ThemeToggle name.
63
- export function ThemeToggle({
64
- defaultMode = "dark",
65
- respectPrefersColorScheme = true,
66
- labels = englishLabels,
67
- pendingUntilHydrated = true,
68
- }: ThemeToggleProps) {
69
- const pending = useHydrationPending(pendingUntilHydrated);
70
- // The menu exists only after a client interaction; allocate across island roots.
71
- const menuId = useRef("");
72
- const ensureMenuId = () => {
73
- if (!menuId.current) menuId.current = `zd-appearance-${++menuSequence}`;
74
- };
75
- const rootRef = useRef<HTMLDivElement>(null);
76
- const menuRef = useRef<HTMLDivElement>(null);
77
- const triggerRef = useRef<HTMLButtonElement>(null);
78
- const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);
79
- const restoreFocusRef = useRef(false);
80
- const [preference, setPreference] = useState<ThemePreference>(
81
- respectPrefersColorScheme ? "system" : defaultMode,
82
- );
83
- const [resolved, setResolved] = useState<ColorSchemeMode>(defaultMode);
84
- const [open, setOpen] = useState(false);
85
- const [activeIndex, setActiveIndex] = useState(0);
86
- const [placement, setPlacement] = useState<{ left: number; top: number; width: number; maxHeight: number } | null>(null);
55
+ interface AppearanceMenuProps {
56
+ id: string;
57
+ labels: ThemeToggleLabels;
58
+ preference: ReadonlySignal<ThemePreference>;
59
+ resolved: ReadonlySignal<ColorSchemeMode>;
60
+ activeIndex: ReadonlySignal<number>;
61
+ triggerRef: Ref<HTMLButtonElement>;
62
+ rootRef: Ref<HTMLDivElement>;
63
+ close: (restoreFocus?: boolean) => void;
64
+ select: (next: ThemePreference) => void;
65
+ move: (index: number) => void;
66
+ }
87
67
 
88
- useEffect(() => {
89
- const sync = () => {
90
- setPreference(readThemePreference(defaultMode, respectPrefersColorScheme));
91
- setResolved(readColorSchemeFromDom(defaultMode));
92
- };
93
- sync();
94
- const unsubscribePreference = subscribeThemePreferenceChanged(() => {
95
- sync();
96
- setOpen(false);
97
- });
98
- const unsubscribeScheme = subscribeColorSchemeChanged(sync);
99
- return () => {
100
- unsubscribePreference();
101
- unsubscribeScheme();
102
- };
103
- }, [defaultMode, respectPrefersColorScheme]);
68
+ function AppearanceMenu({ id, labels, preference, resolved, activeIndex, triggerRef, rootRef, close, select, move }: AppearanceMenuProps) {
69
+ const scope = getScope();
70
+ const menuRef: Ref<HTMLDivElement> = { current: null };
71
+ const itemRefs: Ref<HTMLButtonElement>[] = preferences.map(() => ({ current: null }));
72
+ const placement = signal({ visibility: "hidden", left: "0px", top: "0px", right: "auto", bottom: "auto", margin: "0", width: "260px", "max-height": "none" });
104
73
 
105
- useEffect(() => {
106
- if (!open) return;
107
- // A portaled menu still sits below the third-party token panel's very high
108
- // stacking tier. The native popover top layer keeps it actionable while
109
- // that panel is open, without competing with the host's z-index scale.
74
+ scope.onActivate(() => {
110
75
  const menu = menuRef.current;
111
- menu?.showPopover?.();
76
+ if (!menu) return;
77
+ menu.showPopover?.();
112
78
  const position = () => {
113
79
  const rect = triggerRef.current?.getBoundingClientRect();
114
80
  if (!rect) return;
115
81
  const gap = 8;
116
- const width = Math.min(260, window.innerWidth - gap * 2);
117
- // Measure after the popover enters the top layer. A fixed guess clips
118
- // the System helper once the option rows meet the 44px touch target.
119
- const desiredHeight = menu?.scrollHeight ?? 280;
82
+ const width = Math.max(0, Math.min(260, window.innerWidth - gap * 2));
83
+ const desiredHeight = menu.scrollHeight || 280;
120
84
  const roomBelow = window.innerHeight - rect.bottom - gap * 2;
121
85
  const roomAbove = rect.top - gap * 2;
122
86
  const above = roomBelow < desiredHeight && roomAbove > roomBelow;
123
87
  const maxHeight = Math.max(80, Math.min(desiredHeight, above ? roomAbove : roomBelow));
124
- setPlacement({
125
- left: Math.max(gap, Math.min(rect.right - width, window.innerWidth - width - gap)),
126
- top: above ? Math.max(gap, rect.top - gap - maxHeight) : rect.bottom + gap,
127
- width,
128
- maxHeight,
129
- });
88
+ const left = Math.max(gap, Math.min(rect.right - width, window.innerWidth - width - gap));
89
+ const top = above ? Math.max(gap, rect.top - gap - maxHeight) : rect.bottom + gap;
90
+ placement.value = { visibility: "visible", left: `${left}px`, top: `${top}px`, right: "auto", bottom: "auto", margin: "0", width: `${width}px`, "max-height": `${maxHeight}px` };
130
91
  };
131
92
  const onPointerDown = (event: PointerEvent) => {
132
- if (
133
- !rootRef.current?.contains(event.target as Node) &&
134
- !menuRef.current?.contains(event.target as Node)
135
- ) setOpen(false);
93
+ if (!rootRef.current?.contains(event.target as Node) && !menu.contains(event.target as Node)) close();
136
94
  };
137
- const onNavigate = () => setOpen(false);
95
+ const onNavigate = () => close();
138
96
  position();
97
+ // The placement signal commits visibility after this activation callback.
98
+ // Focus only after the visible popover has reached the browser layout.
99
+ const focusFrame = requestAnimationFrame(() => {
100
+ if (!scope.abortSignal.aborted) itemRefs[activeIndex.value]?.current?.focus();
101
+ });
139
102
  document.addEventListener("pointerdown", onPointerDown);
140
103
  window.addEventListener("resize", position);
141
104
  window.addEventListener("scroll", position, true);
142
105
  document.addEventListener(AFTER_NAVIGATE_EVENT, onNavigate);
143
106
  return () => {
144
- if (menu?.hidePopover && menu.matches(":popover-open")) menu.hidePopover();
107
+ cancelAnimationFrame(focusFrame);
108
+ if (menu.hidePopover && menu.matches(":popover-open")) menu.hidePopover();
145
109
  document.removeEventListener("pointerdown", onPointerDown);
146
110
  window.removeEventListener("resize", position);
147
111
  window.removeEventListener("scroll", position, true);
148
112
  document.removeEventListener(AFTER_NAVIGATE_EVENT, onNavigate);
149
113
  };
150
- }, [open]); // activeIndex is set before opening; arrow movement focuses directly.
114
+ });
115
+
116
+ const onMenuKeyDown = (event: Event) => {
117
+ const keyboard = event as KeyboardEvent;
118
+ if (keyboard.isComposing) return;
119
+ if (keyboard.key === "Escape") {
120
+ keyboard.preventDefault();
121
+ keyboard.stopPropagation();
122
+ close(true);
123
+ } else if (keyboard.key === "Tab") {
124
+ window.setTimeout(() => close(), 0);
125
+ } else if (keyboard.key === "ArrowDown" || keyboard.key === "ArrowUp" || keyboard.key === "Home" || keyboard.key === "End") {
126
+ keyboard.preventDefault();
127
+ const next = keyboard.key === "Home" ? 0 : keyboard.key === "End" ? 2 : activeIndex.value + (keyboard.key === "ArrowDown" ? 1 : -1);
128
+ move(next);
129
+ itemRefs[(next + preferences.length) % preferences.length]?.current?.focus();
130
+ }
131
+ };
151
132
 
152
- useEffect(() => {
153
- if (open && placement) itemRefs.current[activeIndex]?.focus();
154
- }, [open, placement]); // Focus once the portaled menu is visible.
133
+ return <div ref={menuRef} id={id} role="menu" aria-label={labels.appearance}
134
+ on:keydown={onMenuKeyDown}
135
+ class="fixed z-tooltip overflow-y-auto rounded-lg border border-muted bg-surface p-hsp-xs text-fg shadow-lg"
136
+ style={placement} popover="manual">
137
+ <div class="px-hsp-sm py-vsp-xs text-small font-semibold" aria-hidden="true">{labels.appearance}</div>
138
+ {preferences.map((option, index) => <button ref={itemRefs[index]} type="button"
139
+ role="menuitemradio" aria-checked={computed(() => preference.value === option)}
140
+ on:focus={() => move(index)} on:click={() => select(option)}
141
+ class={computed(() => `flex min-h-[44px] w-full items-center gap-hsp-sm rounded px-hsp-sm text-left text-small ${preference.value === option ? "bg-accent/10" : ""} hover:bg-accent/10 focus-visible:bg-accent/10 focus-visible:outline-2 focus-visible:outline-accent`)}>
142
+ <PreferenceIcon preference={option} />
143
+ <span class="flex-1">{labels[option]}</span>
144
+ <span aria-hidden="true" class="text-accent">{computed(() => preference.value === option ? "✓" : "")}</span>
145
+ </button>)}
146
+ <div class="px-hsp-sm py-vsp-xs text-small text-muted">
147
+ {computed(() => labels.systemHelper.replace("{mode}", labels[resolved.value]))}
148
+ </div>
149
+ </div>;
150
+ }
155
151
 
156
- useEffect(() => {
157
- if (open || !restoreFocusRef.current) return;
158
- restoreFocusRef.current = false;
159
- requestAnimationFrame(() => triggerRef.current?.focus());
160
- }, [open]);
152
+ // Keep the named export: zfb's island scanner keys on the ThemeToggle name.
153
+ export function ThemeToggle({ defaultMode = "dark", respectPrefersColorScheme = true, labels = englishLabels, pendingUntilHydrated = true }: ThemeToggleProps) {
154
+ const scope = getScope();
155
+ const pending = hydrationPending(scope, pendingUntilHydrated);
156
+ const rootRef: Ref<HTMLDivElement> = { current: null };
157
+ const triggerRef: Ref<HTMLButtonElement> = { current: null };
158
+ const preference = signal<ThemePreference>(respectPrefersColorScheme ? "system" : defaultMode);
159
+ const resolved = signal<ColorSchemeMode>(defaultMode);
160
+ const open = signal(false);
161
+ const activeIndex = signal(0);
162
+ let menuId = "";
163
+ const ensureMenuId = () => { if (!menuId) menuId = `zd-appearance-${++menuSequence}`; };
164
+
165
+ scope.onActivate(() => {
166
+ const sync = () => {
167
+ preference.value = readThemePreference(defaultMode, respectPrefersColorScheme);
168
+ resolved.value = readColorSchemeFromDom(defaultMode);
169
+ };
170
+ sync();
171
+ const unsubscribePreference = subscribeThemePreferenceChanged(() => { sync(); open.value = false; });
172
+ const unsubscribeScheme = subscribeColorSchemeChanged(sync);
173
+ return () => { unsubscribePreference(); unsubscribeScheme(); };
174
+ });
161
175
 
162
176
  const close = (restoreFocus = false) => {
163
- restoreFocusRef.current = restoreFocus;
164
- setOpen(false);
177
+ open.value = false;
178
+ if (restoreFocus) requestAnimationFrame(() => {
179
+ if (!scope.abortSignal.aborted && !open.value) triggerRef.current?.focus();
180
+ });
165
181
  };
166
182
  const select = (next: ThemePreference) => {
167
183
  close(true);
168
184
  applyThemePreference(next);
169
- setPreference(next);
170
- setResolved(readColorSchemeFromDom(defaultMode));
171
- };
172
- const move = (index: number) => {
173
- const next = (index + preferences.length) % preferences.length;
174
- setActiveIndex(next);
175
- itemRefs.current[next]?.focus();
176
- };
177
- const onMenuKeyDown = (event: KeyboardEvent) => {
178
- if (event.key === "Escape") {
179
- event.preventDefault();
180
- // The menu is portaled to document.body (see createPortal below), so this
181
- // keydown bubbles all the way to `document` — where the mobile drawer's own
182
- // Escape-to-close listener lives (sidebar-toggle-island/index.tsx). Without
183
- // stopPropagation, closing just the menu would also close the drawer
184
- // (zudolab/zudo-doc#4393).
185
- event.stopPropagation();
186
- close(true);
187
- } else if (event.key === "Tab") {
188
- // Let the browser move focus before unmounting the focused menu item.
189
- window.setTimeout(() => close(), 0);
190
- } else if (event.key === "ArrowDown") {
191
- event.preventDefault();
192
- move(activeIndex + 1);
193
- } else if (event.key === "ArrowUp") {
194
- event.preventDefault();
195
- move(activeIndex - 1);
196
- } else if (event.key === "Home") {
197
- event.preventDefault();
198
- move(0);
199
- } else if (event.key === "End") {
200
- event.preventDefault();
201
- move(2);
202
- }
185
+ preference.value = next;
186
+ resolved.value = readColorSchemeFromDom(defaultMode);
203
187
  };
188
+ const move = (index: number) => { activeIndex.value = (index + preferences.length) % preferences.length; };
189
+ const show = (index: number) => { ensureMenuId(); move(index); open.value = true; };
204
190
 
205
- return (
206
- <div ref={rootRef} className="relative inline-flex" data-zd-theme-menu="">
207
- <button ref={triggerRef} type="button" aria-haspopup="menu" aria-expanded={open}
208
- aria-controls={open ? menuId.current : undefined}
209
- aria-label={`${labels.appearance}: ${labels[preference]}`}
210
- aria-disabled={pending ? "true" : undefined}
211
- data-zd-pending={pending ? "" : undefined}
212
- onClick={() => {
213
- if (pending) return;
214
- if (open) close(true);
215
- else { setPlacement(null); ensureMenuId(); setActiveIndex(preferences.indexOf(preference)); setOpen(true); }
216
- }}
217
- onKeyDown={(event) => {
218
- if (pending) { if (event.key === "Enter" || event.key === " ") event.preventDefault(); return; }
219
- if (event.key === "ArrowDown" || event.key === "ArrowUp") {
220
- event.preventDefault(); setPlacement(null); ensureMenuId(); setActiveIndex(event.key === "ArrowDown" ? 0 : 2); setOpen(true);
221
- } else if (event.key === "Escape" && open) {
222
- // Same layered-ownership fix as onMenuKeyDown above: the trigger sits
223
- // inside the mobile drawer (unlike the portaled menu), so its keydown
224
- // bubbles straight to the drawer's document-level Escape listener.
225
- event.preventDefault();
226
- event.stopPropagation();
227
- close(true);
228
- }
229
- }}
230
- className="inline-flex h-[40px] w-[40px] shrink-0 items-center justify-center text-muted hover:text-fg focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2"
231
- ><PreferenceIcon preference={preference} /></button>
232
- {open && createPortal(<div ref={menuRef} id={menuId.current} role="menu" aria-label={labels.appearance}
233
- popover={typeof HTMLElement !== "undefined" && "showPopover" in HTMLElement.prototype ? "manual" : undefined}
234
- onKeyDown={onMenuKeyDown}
235
- className="fixed z-tooltip overflow-y-auto rounded-lg border border-muted bg-surface p-hsp-xs text-fg shadow-lg"
236
- style={placement ? { left: placement.left, top: placement.top, right: "auto", bottom: "auto", margin: 0, width: placement.width, maxHeight: placement.maxHeight } : { visibility: "hidden", left: 0, top: 0, right: "auto", bottom: "auto", margin: 0, width: Math.min(260, window.innerWidth - 16) }}>
237
- <div className="px-hsp-sm py-vsp-xs text-small font-semibold" aria-hidden="true">{labels.appearance}</div>
238
- {preferences.map((option, index) => (
239
- <button key={option} ref={(node) => { itemRefs.current[index] = node; }} type="button"
240
- role="menuitemradio" aria-checked={preference === option}
241
- onFocus={() => setActiveIndex(index)} onClick={() => select(option)}
242
- className={`flex min-h-[44px] w-full items-center gap-hsp-sm rounded px-hsp-sm text-left text-small ${preference === option ? "bg-accent/10" : ""} hover:bg-accent/10 focus-visible:bg-accent/10 focus-visible:outline-2 focus-visible:outline-accent`}
243
- >
244
- <PreferenceIcon preference={option} />
245
- <span className="flex-1">{labels[option]}</span>
246
- <span aria-hidden="true" className="text-accent">{preference === option ? "✓" : ""}</span>
247
- </button>
248
- ))}
249
- <div className="px-hsp-sm py-vsp-xs text-small text-muted">
250
- {labels.systemHelper.replace("{mode}", labels[resolved])}
251
- </div>
252
- </div>, document.body)}
253
- </div>
254
- );
191
+ return <div ref={rootRef} class="relative inline-flex" data-zd-theme-menu="">
192
+ <button ref={triggerRef} type="button" aria-haspopup="menu" aria-expanded={open}
193
+ aria-controls={computed(() => open.value ? menuId : undefined)}
194
+ aria-label={computed(() => `${labels.appearance}: ${labels[preference.value]}`)}
195
+ aria-disabled={computed(() => pending.value ? "true" : undefined)}
196
+ data-zd-pending={computed(() => pending.value ? "" : undefined)}
197
+ on:click={() => { if (pending.value) return; if (open.value) close(true); else show(preferences.indexOf(preference.value)); }}
198
+ on:keydown={(event: Event) => {
199
+ const keyboard = event as KeyboardEvent;
200
+ if (keyboard.isComposing) return;
201
+ if (pending.value) { if (keyboard.key === "Enter" || keyboard.key === " ") keyboard.preventDefault(); return; }
202
+ if (keyboard.key === "ArrowDown" || keyboard.key === "ArrowUp") {
203
+ keyboard.preventDefault(); show(keyboard.key === "ArrowDown" ? 0 : 2);
204
+ } else if (keyboard.key === "Escape" && open.value) {
205
+ keyboard.preventDefault(); keyboard.stopPropagation(); close(true);
206
+ }
207
+ }}
208
+ class="inline-flex h-[40px] w-[40px] shrink-0 items-center justify-center text-muted hover:text-fg focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-2">
209
+ <Show when={computed(() => preference.value === "light")}>{() => <PreferenceIcon preference="light" />}</Show>
210
+ <Show when={computed(() => preference.value === "dark")}>{() => <PreferenceIcon preference="dark" />}</Show>
211
+ <Show when={computed(() => preference.value === "system")}>{() => <PreferenceIcon preference="system" />}</Show>
212
+ </button>
213
+ <Show when={open}>{() => <AppearanceMenu id={menuId} labels={labels} preference={preference} resolved={resolved} activeIndex={activeIndex} triggerRef={triggerRef} rootRef={rootRef} close={close} select={select} move={move} />}</Show>
214
+ </div>;
255
215
  }
256
216
  ThemeToggle.displayName = "ThemeToggle";
@@ -1,10 +1,8 @@
1
1
  "use client";
2
2
 
3
3
  /** @jsxRuntime automatic */
4
- /** @jsxImportSource preact */
5
-
6
- import type { VNode } from "preact";
7
- import { useMemo, useState } from "preact/hooks";
4
+ import { computed, signal } from "@takazudo/zfb/zudo-react";
5
+ import type { Child } from "@takazudo/zfb/zudo-react";
8
6
 
9
7
  import type { HeadingItem } from "./types.js";
10
8
  import { SmartBreak } from "../smart-break/index.js";
@@ -16,7 +14,7 @@ export interface MobileTocProps {
16
14
  }
17
15
 
18
16
  /**
19
- * Collapsible TOC for narrow viewports (`xl:hidden`) — a Preact island
17
+ * Collapsible TOC for narrow viewports (`xl:hidden`) — a zudo-react island
20
18
  * component. Closed by default; tapping the header toggles, and tapping
21
19
  * any entry closes the panel after navigation.
22
20
  *
@@ -48,12 +46,17 @@ export interface MobileTocProps {
48
46
  export function MobileToc({
49
47
  headings,
50
48
  title = "On this page",
51
- }: MobileTocProps): VNode {
52
- const filtered = useMemo(
53
- () => headings.filter((h) => h.depth >= 2 && h.depth <= 4),
54
- [headings],
55
- );
56
- const [open, setOpen] = useState(false);
49
+ }: MobileTocProps): Child {
50
+ const filtered = headings.filter((h) => h.depth >= 2 && h.depth <= 4);
51
+ const open = signal(false);
52
+ const chevronClass = computed(() => cx(
53
+ "h-icon-sm w-icon-sm text-muted transition-transform duration-150",
54
+ open.value && "rotate-180",
55
+ ));
56
+ const itemsClass = computed(() => cx(
57
+ "border-t border-muted px-hsp-lg py-vsp-xs space-y-vsp-2xs",
58
+ !open.value && "hidden",
59
+ ));
57
60
 
58
61
  // No qualifying headings: emit a CSS-hidden container that still carries
59
62
  // the locale title text. The `hidden` class sets display:none visually,
@@ -62,7 +65,7 @@ export function MobileToc({
62
65
  // aria-hidden prevents screen readers from announcing the invisible label.
63
66
  if (filtered.length === 0) {
64
67
  return (
65
- <div className="hidden" aria-hidden="true">
68
+ <div class="hidden" aria-hidden="true">
66
69
  {title}
67
70
  </div>
68
71
  );
@@ -77,29 +80,25 @@ export function MobileToc({
77
80
  // branch is a `display:none` placeholder carrying only the locale label, so
78
81
  // there is nothing for a pack to style.
79
82
  return (
80
- <div data-zd-mobile-toc className="xl:hidden border border-muted mb-vsp-lg">
83
+ <div data-zd-mobile-toc class="xl:hidden border border-muted mb-vsp-lg">
81
84
  <button
82
85
  type="button"
83
- onClick={() => setOpen((prev) => !prev)}
86
+ on:click={() => { open.value = !open.value; }}
84
87
  aria-expanded={open}
85
- className="flex w-full items-center justify-between px-hsp-lg py-vsp-xs text-small font-medium text-fg"
88
+ class="flex w-full items-center justify-between px-hsp-lg py-vsp-xs text-small font-medium text-fg"
86
89
  >
87
90
  <span>{title}</span>
88
91
  <svg
89
- xmlns="http://www.w3.org/2000/svg"
90
92
  aria-hidden="true"
91
- className={cx(
92
- "h-icon-sm w-icon-sm text-muted transition-transform duration-150",
93
- open && "rotate-180",
94
- )}
93
+ class={chevronClass}
95
94
  fill="none"
96
95
  viewBox="0 0 24 24"
97
96
  stroke="currentColor"
98
- strokeWidth={2}
97
+ stroke-width={2}
99
98
  >
100
99
  <path
101
- strokeLinecap="round"
102
- strokeLinejoin="round"
100
+ stroke-linecap="round"
101
+ stroke-linejoin="round"
103
102
  d="M19 9l-7 7-7-7"
104
103
  />
105
104
  </svg>
@@ -110,24 +109,21 @@ export function MobileToc({
110
109
  elements remain in the static markup, satisfying the a11y requirement
111
110
  for keyboard accessibility after hydration. */}
112
111
  <ul
113
- className={cx(
114
- "border-t border-muted px-hsp-lg py-vsp-xs space-y-vsp-2xs",
115
- !open && "hidden",
116
- )}
117
- aria-hidden={!open}
112
+ class={itemsClass}
113
+ aria-hidden={computed(() => !open.value)}
118
114
  >
119
115
  {filtered.map((heading, index) => (
120
116
  <li
121
117
  key={`${heading.slug}-${index}`}
122
- className={cx(
118
+ class={cx(
123
119
  heading.depth === 3 && "ml-hsp-lg",
124
120
  heading.depth === 4 && "ml-hsp-2xl",
125
121
  )}
126
122
  >
127
123
  <a
128
124
  href={`#${heading.slug}`}
129
- onClick={() => setOpen(false)}
130
- className="block py-vsp-2xs text-small text-muted hover:text-fg hover:underline focus-visible:underline"
125
+ on:click={() => { open.value = false; }}
126
+ class="block py-vsp-2xs text-small text-muted hover:text-fg hover:underline focus-visible:underline"
131
127
  >
132
128
  <SmartBreak>{heading.text}</SmartBreak>
133
129
  </a>
package/eject/toc/toc.tsx CHANGED
@@ -1,10 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  /** @jsxRuntime automatic */
4
- /** @jsxImportSource preact */
5
-
6
- import type { VNode } from "preact";
7
- import { useMemo } from "preact/hooks";
4
+ import { computed, getScope, type Child } from "@takazudo/zfb/zudo-react";
8
5
 
9
6
  import { useActiveHeading } from "./use-active-heading.js";
10
7
  import type { HeadingItem } from "./types.js";
@@ -23,7 +20,7 @@ export interface TocProps {
23
20
  }
24
21
 
25
22
  /**
26
- * Desktop right-rail Table of Contents — a Preact island component.
23
+ * Desktop right-rail Table of Contents — a zudo-react island component.
27
24
  *
28
25
  * Renders the `<nav aria-label="Table of contents">` directly. **The
29
26
  * caller is responsible for wrapping this in `<Island when="load">`**
@@ -60,47 +57,47 @@ export interface TocProps {
60
57
  * `<nav aria-label="Table of contents">` landmark provides the section
61
58
  * semantics for screen readers. Issue #1655 / T1.
62
59
  */
63
- export function Toc({ headings, title = "On this page" }: TocProps): VNode {
64
- const filtered = useMemo(
65
- () => headings.filter((h) => h.depth >= 2 && h.depth <= 4),
66
- [headings],
67
- );
68
- const { activeId, activate } = useActiveHeading(filtered);
60
+ export function Toc({ headings, title = "On this page" }: TocProps): Child {
61
+ const filtered = headings.filter((h) => h.depth >= 2 && h.depth <= 4);
62
+ const { activeId, activate } = useActiveHeading(getScope(), filtered);
69
63
 
70
64
  return (
71
65
  <nav
72
66
  aria-label="Table of contents"
73
67
  data-zd-toc
74
- className={cx(
68
+ class={cx(
75
69
  "hidden xl:flex flex-col",
76
70
  "w-[280px] shrink-0",
77
71
  "sticky top-[3.5rem] self-start z-sidebar",
78
72
  "pt-vsp-xl lg:pt-vsp-2xl",
79
- "h-[calc(100vh-3.5rem)]",
73
+ "h-[calc(100vh_-_3.5rem)]",
80
74
  )}
81
75
  >
82
76
  {filtered.length > 0 && (
83
- <ul className="border-l border-muted pl-hsp-lg overflow-y-auto">
77
+ <ul class="border-l border-muted pl-hsp-lg overflow-y-auto">
84
78
  {filtered.map((heading, index) => {
85
- const isActive = heading.slug === activeId;
79
+ const isActive = computed(() => heading.slug === activeId.value);
80
+ const linkClass = computed(() =>
81
+ cx(
82
+ "block py-vsp-2xs text-small leading-snug transition-colors duration-0",
83
+ isActive.value
84
+ ? "bg-fg text-bg font-medium"
85
+ : "text-muted hover:underline focus:underline",
86
+ ),
87
+ );
86
88
  return (
87
89
  <li
88
90
  key={`${heading.slug}-${index}`}
89
- className={cx(
91
+ class={cx(
90
92
  heading.depth === 3 && "ml-hsp-lg",
91
93
  heading.depth === 4 && "ml-hsp-2xl",
92
94
  )}
93
95
  >
94
96
  <a
95
97
  href={`#${heading.slug}`}
96
- onClick={() => activate(heading.slug)}
97
- aria-current={isActive ? "true" : undefined}
98
- className={cx(
99
- "block py-vsp-2xs text-small leading-snug transition-colors",
100
- isActive
101
- ? "bg-fg text-bg font-medium"
102
- : "text-muted hover:underline focus:underline",
103
- )}
98
+ on:click={() => activate(heading.slug)}
99
+ aria-current={computed(() => isActive.value ? "true" : undefined)}
100
+ class={linkClass}
104
101
  >
105
102
  <SmartBreak>{heading.text}</SmartBreak>
106
103
  </a>