@takazudo/zudo-doc 5.28.2 → 6.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 (456) hide show
  1. package/CHANGELOG.md +22 -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/category-nav/index.d.ts +1 -2
  43. package/dist/category-nav/index.js +11 -5
  44. package/dist/category-tree-nav/index.d.ts +1 -2
  45. package/dist/category-tree-nav/index.js +3 -3
  46. package/dist/chrome/derive.d.ts +18 -27
  47. package/dist/chrome/derive.js +47 -29
  48. package/dist/chrome/index.d.ts +5 -5
  49. package/dist/chrome/index.js +1 -1
  50. package/dist/chrome/island-data.d.ts +4 -0
  51. package/dist/chrome/island-data.js +52 -0
  52. package/dist/chrome/primary-slots.d.ts +2 -3
  53. package/dist/chrome-bindings.d.ts +28 -22
  54. package/dist/code-group/index.d.ts +3 -4
  55. package/dist/code-group/index.js +3 -7
  56. package/dist/code-syntax/code-block-enhancer.d.ts +1 -2
  57. package/dist/code-syntax/code-block-enhancer.js +2 -2
  58. package/dist/code-syntax/mermaid-init.d.ts +1 -2
  59. package/dist/code-syntax/mermaid-init.js +5 -2
  60. package/dist/code-syntax/tabs-init-script.js +1 -1
  61. package/dist/code-syntax/tabs-init.d.ts +1 -2
  62. package/dist/code-syntax/tabs-init.js +2 -2
  63. package/dist/code-syntax/tabs.d.ts +6 -6
  64. package/dist/code-syntax/tabs.js +8 -8
  65. package/dist/compiled.css +2933 -2943
  66. package/dist/config-assertions/index.d.ts +13 -0
  67. package/dist/config-assertions/index.js +14 -0
  68. package/dist/config.d.ts +36 -17
  69. package/dist/config.js +23 -95
  70. package/dist/content/content-blockquote.d.ts +2 -3
  71. package/dist/content/content-blockquote.js +3 -3
  72. package/dist/content/content-code.d.ts +3 -4
  73. package/dist/content/content-code.js +5 -5
  74. package/dist/content/content-link.d.ts +3 -4
  75. package/dist/content/content-link.js +10 -10
  76. package/dist/content/content-ol.d.ts +2 -3
  77. package/dist/content/content-ol.js +5 -4
  78. package/dist/content/content-paragraph.d.ts +2 -3
  79. package/dist/content/content-paragraph.js +1 -1
  80. package/dist/content/content-strong.d.ts +2 -3
  81. package/dist/content/content-strong.js +3 -3
  82. package/dist/content/content-table.d.ts +2 -3
  83. package/dist/content/content-table.js +4 -4
  84. package/dist/content/content-ul.d.ts +2 -3
  85. package/dist/content/content-ul.js +4 -4
  86. package/dist/content/heading-h2.d.ts +2 -3
  87. package/dist/content/heading-h2.js +4 -4
  88. package/dist/content/heading-h3.d.ts +2 -3
  89. package/dist/content/heading-h3.js +4 -4
  90. package/dist/content/heading-h4.d.ts +2 -3
  91. package/dist/content/heading-h4.js +4 -4
  92. package/dist/content-admonition/index.d.ts +3 -4
  93. package/dist/content-admonition/index.js +2 -2
  94. package/dist/content.css +35 -33
  95. package/dist/design-token-panel-bootstrap-controller.d.ts +178 -0
  96. package/dist/design-token-panel-bootstrap-controller.js +359 -0
  97. package/dist/design-token-panel-bootstrap-island.d.ts +5 -0
  98. package/dist/design-token-panel-bootstrap-island.js +19 -0
  99. package/dist/design-token-panel-bootstrap.d.ts +5 -195
  100. package/dist/design-token-panel-bootstrap.js +3 -288
  101. package/dist/desktop-sidebar-toggle-island/index.d.ts +2 -7
  102. package/dist/desktop-sidebar-toggle-island/index.js +2 -58
  103. package/dist/desktop-sidebar-toggle-island/island.d.ts +4 -0
  104. package/dist/desktop-sidebar-toggle-island/island.js +43 -0
  105. package/dist/desktop-sidebar-toggle-island/storage.d.ts +4 -0
  106. package/dist/desktop-sidebar-toggle-island/storage.js +29 -0
  107. package/dist/desktop-toc-toggle-island/index.d.ts +2 -7
  108. package/dist/desktop-toc-toggle-island/index.js +2 -58
  109. package/dist/desktop-toc-toggle-island/island.d.ts +4 -0
  110. package/dist/desktop-toc-toggle-island/island.js +43 -0
  111. package/dist/desktop-toc-toggle-island/storage.d.ts +4 -0
  112. package/dist/desktop-toc-toggle-island/storage.js +29 -0
  113. package/dist/details/details.d.ts +3 -4
  114. package/dist/details/details.js +1 -1
  115. package/dist/doc-body-end/index.d.ts +1 -2
  116. package/dist/doc-body-end/index.js +1 -1
  117. package/dist/doc-body-end-islands/design-token-panel-island.d.ts +2 -2
  118. package/dist/doc-body-end-islands/design-token-panel-island.js +3 -7
  119. package/dist/doc-body-end-islands/index.d.ts +4 -21
  120. package/dist/doc-body-end-islands/index.js +5 -3
  121. package/dist/doc-body-end-islands/theme-pack-switcher-island.d.ts +3 -13
  122. package/dist/doc-body-end-islands/theme-pack-switcher-island.js +11 -13
  123. package/dist/doc-content-header/index.d.ts +1 -2
  124. package/dist/doc-content-header/index.js +1 -1
  125. package/dist/doc-history/index.d.ts +1 -1
  126. package/dist/doc-history/index.js +166 -141
  127. package/dist/doc-history-area/index.d.ts +6 -4
  128. package/dist/doc-history-area/index.js +17 -16
  129. package/dist/doc-metainfo-area/index.d.ts +2 -3
  130. package/dist/doc-metainfo-area/index.js +1 -1
  131. package/dist/doc-page-renderer/index.d.ts +13 -7
  132. package/dist/doc-page-renderer/index.js +10 -1
  133. package/dist/doc-page-shell/index.d.ts +20 -7
  134. package/dist/doc-page-shell/index.js +17 -6
  135. package/dist/doc-pager/index.d.ts +1 -2
  136. package/dist/doc-pager/index.js +3 -3
  137. package/dist/doc-tags-area/index.d.ts +2 -3
  138. package/dist/doc-tags-area/index.js +1 -1
  139. package/dist/doclayout/doc-layout-with-defaults.d.ts +9 -9
  140. package/dist/doclayout/doc-layout-with-defaults.js +5 -15
  141. package/dist/doclayout/doc-layout.d.ts +26 -27
  142. package/dist/doclayout/doc-layout.js +23 -14
  143. package/dist/docs-schema/index.d.ts +4 -0
  144. package/dist/docs-schema/index.js +1 -0
  145. package/dist/extract-headings/index.js +43 -12
  146. package/dist/factory-context/index.d.ts +22 -19
  147. package/dist/features.css +70 -8
  148. package/dist/find-in-page/find-bar.d.ts +4 -2
  149. package/dist/find-in-page/find-bar.js +58 -69
  150. package/dist/find-in-page/index.d.ts +2 -8
  151. package/dist/find-in-page/index.js +33 -30
  152. package/dist/footer/footer.d.ts +3 -4
  153. package/dist/footer/footer.js +3 -3
  154. package/dist/footer-with-defaults/index.d.ts +2 -3
  155. package/dist/footer-with-defaults/index.js +1 -1
  156. package/dist/frontmatter-preview-defaults/index.js +1 -0
  157. package/dist/head/doc-head.d.ts +1 -1
  158. package/dist/head/doc-head.js +24 -32
  159. package/dist/head/og-tags.d.ts +1 -1
  160. package/dist/head/og-tags.js +8 -7
  161. package/dist/head/twitter-card.d.ts +1 -1
  162. package/dist/head/twitter-card.js +1 -1
  163. package/dist/head-with-defaults/index.d.ts +7 -2
  164. package/dist/head-with-defaults/index.js +46 -32
  165. package/dist/header/header.d.ts +8 -8
  166. package/dist/header/header.js +9 -11
  167. package/dist/header/types.d.ts +7 -7
  168. package/dist/header-with-defaults/index.d.ts +1 -2
  169. package/dist/header-with-defaults/index.js +18 -8
  170. package/dist/home-intro/index.d.ts +2 -5
  171. package/dist/home-intro/index.js +7 -5
  172. package/dist/home-intro/prepare.js +4 -3
  173. package/dist/home-page/index.d.ts +3 -3
  174. package/dist/home-page/index.js +19 -17
  175. package/dist/html-preview-wrapper/highlighted-code.d.ts +3 -4
  176. package/dist/html-preview-wrapper/highlighted-code.js +19 -17
  177. package/dist/html-preview-wrapper/html-preview-wrapper.d.ts +4 -25
  178. package/dist/html-preview-wrapper/html-preview-wrapper.js +2 -87
  179. package/dist/html-preview-wrapper/html-preview.d.ts +3 -10
  180. package/dist/html-preview-wrapper/html-preview.js +13 -37
  181. package/dist/html-preview-wrapper/index.d.ts +2 -1
  182. package/dist/html-preview-wrapper/index.js +1 -1
  183. package/dist/html-preview-wrapper/preview-base.d.ts +3 -5
  184. package/dist/html-preview-wrapper/preview-base.js +56 -59
  185. package/dist/html-preview-wrapper/wrapper.d.ts +20 -0
  186. package/dist/html-preview-wrapper/wrapper.js +37 -0
  187. package/dist/hydration-pending.d.ts +3 -5
  188. package/dist/hydration-pending.js +7 -5
  189. package/dist/i18n-version/language-switcher.d.ts +5 -32
  190. package/dist/i18n-version/language-switcher.js +5 -106
  191. package/dist/i18n-version/switcher-generated-scripts.d.ts +2 -0
  192. package/dist/i18n-version/switcher-generated-scripts.js +107 -0
  193. package/dist/i18n-version/switcher-url-state.d.ts +76 -0
  194. package/dist/i18n-version/switcher-url-state.js +103 -0
  195. package/dist/i18n-version/version-banner.d.ts +2 -3
  196. package/dist/i18n-version/version-banner.js +1 -1
  197. package/dist/i18n-version/version-switcher.d.ts +5 -50
  198. package/dist/i18n-version/version-switcher.js +5 -157
  199. package/dist/icons/index.d.ts +18 -19
  200. package/dist/icons/index.js +17 -33
  201. package/dist/image-enlarge/index.d.ts +1 -10
  202. package/dist/image-enlarge/index.js +80 -55
  203. package/dist/image-enlarge/public.d.ts +2 -0
  204. package/dist/image-enlarge/public.js +6 -0
  205. package/dist/image-enlarge/ssr-fallback.d.ts +2 -0
  206. package/dist/image-enlarge/ssr-fallback.js +8 -0
  207. package/dist/index.d.ts +11 -4
  208. package/dist/inline-version-switcher/index.d.ts +1 -2
  209. package/dist/inline-version-switcher/index.js +1 -1
  210. package/dist/island-types/index.d.ts +2 -2
  211. package/dist/island-types/index.js +1 -5
  212. package/dist/math-block/index.d.ts +4 -7
  213. package/dist/math-block/index.js +5 -5
  214. package/dist/mcp/handler.d.ts +8 -0
  215. package/dist/mcp/handler.js +129 -0
  216. package/dist/mcp/index.d.ts +4 -0
  217. package/dist/mcp/index.js +7 -0
  218. package/dist/mcp/loader.d.ts +19 -0
  219. package/dist/mcp/loader.js +202 -0
  220. package/dist/mdx-components/index.js +40 -108
  221. package/dist/mermaid-enlarge/index.d.ts +1 -8
  222. package/dist/mermaid-enlarge/index.js +174 -158
  223. package/dist/mermaid-enlarge/public.d.ts +2 -0
  224. package/dist/mermaid-enlarge/public.js +6 -0
  225. package/dist/mermaid-enlarge/ssr-fallback.d.ts +2 -0
  226. package/dist/mermaid-enlarge/ssr-fallback.js +8 -0
  227. package/dist/metainfo/doc-metainfo.d.ts +2 -3
  228. package/dist/metainfo/doc-metainfo.js +1 -4
  229. package/dist/metainfo/doc-tags.d.ts +2 -3
  230. package/dist/metainfo/doc-tags.js +1 -1
  231. package/dist/metainfo/frontmatter-preview.d.ts +3 -4
  232. package/dist/metainfo/frontmatter-preview.js +1 -1
  233. package/dist/nav-data-prep/index.js +6 -4
  234. package/dist/nav-indexing/category-nav.d.ts +2 -3
  235. package/dist/nav-indexing/category-nav.js +1 -2
  236. package/dist/nav-indexing/category-tree-nav.d.ts +2 -3
  237. package/dist/nav-indexing/category-tree-nav.js +1 -1
  238. package/dist/nav-indexing/doc-card-grid.d.ts +2 -3
  239. package/dist/nav-indexing/doc-card-grid.js +1 -2
  240. package/dist/nav-indexing/docs-sitemap.d.ts +2 -3
  241. package/dist/nav-indexing/docs-sitemap.js +2 -2
  242. package/dist/nav-indexing/index.d.ts +1 -1
  243. package/dist/nav-indexing/nav-card-grid.d.ts +2 -3
  244. package/dist/nav-indexing/nav-card-grid.js +1 -2
  245. package/dist/nav-indexing/note-tray-index-parts/card-list.d.ts +1 -2
  246. package/dist/nav-indexing/note-tray-index-parts/card-list.js +4 -4
  247. package/dist/nav-indexing/note-tray-index-parts/date-line.d.ts +2 -3
  248. package/dist/nav-indexing/note-tray-index-parts/date-line.js +1 -1
  249. package/dist/nav-indexing/note-tray-index-parts/index-list.d.ts +1 -2
  250. package/dist/nav-indexing/note-tray-index-parts/index-list.js +2 -2
  251. package/dist/nav-indexing/note-tray-index-parts/timeline.d.ts +1 -2
  252. package/dist/nav-indexing/note-tray-index-parts/timeline.js +2 -2
  253. package/dist/nav-indexing/note-tray-index.d.ts +1 -2
  254. package/dist/nav-indexing/note-tray-index.js +1 -1
  255. package/dist/nav-indexing/site-tree-nav-demo.d.ts +2 -3
  256. package/dist/nav-indexing/site-tree-nav-demo.js +2 -2
  257. package/dist/nav-indexing/tag-nav.d.ts +1 -2
  258. package/dist/nav-indexing/tag-nav.js +1 -1
  259. package/dist/nav-indexing/types.d.ts +2 -2
  260. package/dist/nav-indexing/versions-page-content.d.ts +2 -3
  261. package/dist/nav-indexing/versions-page-content.js +8 -8
  262. package/dist/note-tray-index/index.d.ts +1 -2
  263. package/dist/note-tray-index/index.js +1 -1
  264. package/dist/page-loading/page-loading-overlay.d.ts +1 -2
  265. package/dist/page-loading/page-loading-overlay.js +3 -5
  266. package/dist/plugins/agent-export.d.ts +3 -0
  267. package/dist/plugins/agent-export.js +34 -0
  268. package/dist/plugins/internal/agent-export/chunk.d.ts +5 -0
  269. package/dist/plugins/internal/agent-export/chunk.js +90 -0
  270. package/dist/plugins/internal/agent-export/index.d.ts +24 -0
  271. package/dist/plugins/internal/agent-export/index.js +123 -0
  272. package/dist/plugins/internal/agent-export/normalize.d.ts +5 -0
  273. package/dist/plugins/internal/agent-export/normalize.js +73 -0
  274. package/dist/plugins/internal/llms-txt/dev-middleware.d.ts +6 -0
  275. package/dist/plugins/internal/llms-txt/dev-middleware.js +8 -3
  276. package/dist/plugins/internal/llms-txt/emit.js +23 -6
  277. package/dist/plugins/internal/llms-txt/generate.d.ts +7 -1
  278. package/dist/plugins/internal/llms-txt/generate.js +9 -2
  279. package/dist/plugins/internal/llms-txt/load.d.ts +2 -0
  280. package/dist/plugins/internal/llms-txt/load.js +20 -4
  281. package/dist/plugins/internal/llms-txt/types.d.ts +10 -0
  282. package/dist/plugins/routes.js +4 -0
  283. package/dist/preset.d.ts +19 -9
  284. package/dist/preset.js +20 -1
  285. package/dist/route-context-payload/index.js +1 -1
  286. package/dist/routes/404.d.ts +2 -3
  287. package/dist/routes/404.js +1 -1
  288. package/dist/routes/_chrome.d.ts +5 -5
  289. package/dist/routes/_chrome.js +7 -2
  290. package/dist/routes/_context.d.ts +4 -4
  291. package/dist/routes/_design-token-panel-bootstrap.d.ts +1 -4
  292. package/dist/routes/_design-token-panel-bootstrap.js +9 -1
  293. package/dist/routes/docs-slug.d.ts +2 -3
  294. package/dist/routes/docs-tags-index.d.ts +2 -3
  295. package/dist/routes/docs-tags-index.js +1 -1
  296. package/dist/routes/docs-tags-tag.d.ts +2 -3
  297. package/dist/routes/docs-tags-tag.js +1 -1
  298. package/dist/routes/docs-versions.d.ts +2 -3
  299. package/dist/routes/docs-versions.js +1 -1
  300. package/dist/routes/files-path.d.ts +2 -3
  301. package/dist/routes/files-path.js +1 -1
  302. package/dist/routes/index.d.ts +2 -3
  303. package/dist/routes/index.js +1 -1
  304. package/dist/routes/locale-docs-slug.d.ts +2 -3
  305. package/dist/routes/locale-docs-tags-index.d.ts +2 -3
  306. package/dist/routes/locale-docs-tags-index.js +1 -1
  307. package/dist/routes/locale-docs-tags-tag.d.ts +2 -3
  308. package/dist/routes/locale-docs-tags-tag.js +1 -1
  309. package/dist/routes/locale-docs-versions.d.ts +2 -3
  310. package/dist/routes/locale-docs-versions.js +1 -1
  311. package/dist/routes/locale-files-path.d.ts +2 -3
  312. package/dist/routes/locale-files-path.js +1 -1
  313. package/dist/routes/locale-index.d.ts +2 -3
  314. package/dist/routes/locale-index.js +1 -1
  315. package/dist/routes/mcp.d.ts +5 -0
  316. package/dist/routes/mcp.js +25 -0
  317. package/dist/routes/v-docs-slug.d.ts +2 -3
  318. package/dist/routes/v-locale-docs-slug.d.ts +2 -3
  319. package/dist/search-widget/index.d.ts +1 -2
  320. package/dist/search-widget/index.js +4 -7
  321. package/dist/search-widget-script/generated-script.js +12 -5
  322. package/dist/settings-defaults.d.ts +2 -0
  323. package/dist/settings-defaults.js +93 -0
  324. package/dist/settings.d.ts +8 -2
  325. package/dist/sidebar/index.d.ts +1 -1
  326. package/dist/sidebar/sidebar.d.ts +12 -44
  327. package/dist/sidebar/sidebar.js +4 -3
  328. package/dist/sidebar/types.d.ts +14 -0
  329. package/dist/sidebar-prepaint/index.d.ts +1 -2
  330. package/dist/sidebar-prepaint/index.js +4 -6
  331. package/dist/sidebar-resizer/sidebar-resizer-init.d.ts +2 -3
  332. package/dist/sidebar-resizer/sidebar-resizer-init.js +3 -3
  333. package/dist/sidebar-toggle-island/index.d.ts +8 -11
  334. package/dist/sidebar-toggle-island/index.js +75 -62
  335. package/dist/sidebar-tree/build-tree.js +5 -6
  336. package/dist/sidebar-tree/types.d.ts +2 -0
  337. package/dist/sidebar-tree-island/index.d.ts +4 -19
  338. package/dist/sidebar-tree-island/index.js +782 -436
  339. package/dist/sidebar-tree-island/scope-state.d.ts +38 -0
  340. package/dist/sidebar-tree-island/scope-state.js +151 -0
  341. package/dist/sidebar-tree-island/sidebar-scroll-preserve.js +6 -2
  342. package/dist/sidebar-utils/index.d.ts +17 -19
  343. package/dist/sidebar-utils/index.js +100 -3
  344. package/dist/sidebar-utils/scope.d.ts +11 -0
  345. package/dist/sidebar-utils/scope.js +67 -0
  346. package/dist/sidebar-with-defaults/index.d.ts +1 -2
  347. package/dist/sidebar-with-defaults/index.js +19 -18
  348. package/dist/site-schema/nav-tree.js +15 -15
  349. package/dist/site-schema/types.d.ts +1 -0
  350. package/dist/site-tree-nav/index.d.ts +2 -3
  351. package/dist/site-tree-nav/index.js +11 -12
  352. package/dist/site-tree-nav/normalize-island-props.d.ts +16 -0
  353. package/dist/site-tree-nav/normalize-island-props.js +122 -0
  354. package/dist/site-tree-nav-island/index.d.ts +3 -3
  355. package/dist/site-tree-nav-island/index.js +243 -144
  356. package/dist/smart-break/index.d.ts +3 -4
  357. package/dist/smart-break/index.js +1 -1
  358. package/dist/tab-item/tab-item.d.ts +3 -4
  359. package/dist/tab-item/tab-item.js +1 -1
  360. package/dist/tag-pages/index.d.ts +3 -3
  361. package/dist/tag-pages/index.js +10 -10
  362. package/dist/theme/color-scheme-provider.d.ts +3 -3
  363. package/dist/theme/color-scheme-provider.js +3 -3
  364. package/dist/theme/theme-pack-provider.d.ts +1 -1
  365. package/dist/theme/theme-pack-provider.js +3 -3
  366. package/dist/theme/theme-toggle.d.ts +2 -3
  367. package/dist/theme/theme-toggle.js +1 -1
  368. package/dist/theme-pack-dialog/index.d.ts +1 -1
  369. package/dist/theme-pack-dialog/index.js +69 -75
  370. package/dist/theme-pack-dialog/theme-pack-card.d.ts +3 -4
  371. package/dist/theme-pack-dialog/theme-pack-card.js +19 -17
  372. package/dist/theme-pack-switcher/index.d.ts +4 -5
  373. package/dist/theme-pack-switcher/index.js +52 -49
  374. package/dist/theme-toggle/index.d.ts +1 -1
  375. package/dist/theme-toggle/index.js +154 -167
  376. package/dist/theme.css +47 -121
  377. package/dist/toc/mobile-toc.d.ts +3 -5
  378. package/dist/toc/mobile-toc.js +29 -27
  379. package/dist/toc/toc.d.ts +3 -4
  380. package/dist/toc/toc.js +18 -18
  381. package/dist/toc/use-active-heading.d.ts +5 -4
  382. package/dist/toc/use-active-heading.js +36 -30
  383. package/dist/toc-prepaint/index.d.ts +1 -2
  384. package/dist/toc-prepaint/index.js +4 -6
  385. package/dist/transitions/nested-island-props-refresh.d.ts +3 -16
  386. package/dist/transitions/nested-island-props-refresh.js +133 -151
  387. package/dist/transitions/page-events.d.ts +8 -13
  388. package/dist/tree-nav-shared/index.d.ts +4 -5
  389. package/dist/tree-nav-shared/index.js +6 -7
  390. package/dist/use-modal-dialog/index.d.ts +9 -63
  391. package/dist/use-modal-dialog/index.js +41 -59
  392. package/dist/versions-page/index.d.ts +3 -3
  393. package/dist/versions-page/index.js +5 -5
  394. package/dist/wind/index.d.ts +5 -0
  395. package/dist/wind/index.js +169 -0
  396. package/dist/wind.json +498 -0
  397. package/dist/z-index-defaults/index.d.ts +4 -4
  398. package/dist/zdtp-loader.d.ts +7 -0
  399. package/dist/zdtp-loader.js +8 -0
  400. package/eject/asset-components/asset-card.tsx +14 -16
  401. package/eject/asset-components/asset-code.tsx +10 -12
  402. package/eject/asset-components/shared.tsx +5 -7
  403. package/eject/breadcrumb/breadcrumb.tsx +15 -16
  404. package/eject/code-group/index.tsx +4 -10
  405. package/eject/content-admonition/index.tsx +5 -6
  406. package/eject/desktop-sidebar-toggle-island/index.ts +4 -0
  407. package/eject/desktop-sidebar-toggle-island/island.tsx +50 -0
  408. package/eject/desktop-sidebar-toggle-island/storage.ts +27 -0
  409. package/eject/details/details.tsx +3 -5
  410. package/eject/doc-history/index.tsx +193 -179
  411. package/eject/doc-pager/index.tsx +3 -4
  412. package/eject/footer/footer.tsx +7 -9
  413. package/eject/header/header.tsx +42 -54
  414. package/eject/header/nav-overflow-script.ts +1 -1
  415. package/eject/header/types.ts +12 -13
  416. package/eject/image-enlarge/index.tsx +84 -69
  417. package/eject/image-enlarge/public.ts +2 -0
  418. package/eject/image-enlarge/ssr-fallback.tsx +7 -0
  419. package/eject/page-loading/page-loading-overlay.tsx +8 -15
  420. package/eject/sidebar/index.ts +1 -0
  421. package/eject/sidebar/sidebar.tsx +16 -97
  422. package/eject/sidebar/types.ts +15 -0
  423. package/eject/sidebar-toggle-island/index.tsx +103 -102
  424. package/eject/sidebar-tree-island/index.tsx +827 -615
  425. package/eject/sidebar-tree-island/scope-state.ts +154 -0
  426. package/eject/sidebar-tree-island/sidebar-scroll-preserve.ts +8 -2
  427. package/eject/site-tree-nav-island/index.tsx +349 -222
  428. package/eject/tab-item/tab-item.tsx +2 -4
  429. package/eject/theme-toggle/hydration-pending.ts +6 -10
  430. package/eject/theme-toggle/index.tsx +133 -173
  431. package/eject/toc/mobile-toc.tsx +27 -31
  432. package/eject/toc/toc.tsx +21 -24
  433. package/eject/toc/use-active-heading.ts +43 -38
  434. package/package.json +40 -22
  435. package/routes-src/404.tsx +2 -3
  436. package/routes-src/_chrome.tsx +7 -2
  437. package/routes-src/_design-token-panel-bootstrap.tsx +1 -63
  438. package/routes-src/docs-slug.tsx +2 -3
  439. package/routes-src/docs-tags-index.tsx +2 -3
  440. package/routes-src/docs-tags-tag.tsx +2 -3
  441. package/routes-src/docs-versions.tsx +2 -3
  442. package/routes-src/files-path.tsx +2 -4
  443. package/routes-src/index.tsx +2 -3
  444. package/routes-src/locale-docs-slug.tsx +2 -3
  445. package/routes-src/locale-docs-tags-index.tsx +2 -3
  446. package/routes-src/locale-docs-tags-tag.tsx +2 -3
  447. package/routes-src/locale-docs-versions.tsx +2 -3
  448. package/routes-src/locale-files-path.tsx +2 -4
  449. package/routes-src/locale-index.tsx +2 -3
  450. package/routes-src/mcp.tsx +25 -0
  451. package/routes-src/v-docs-slug.tsx +2 -3
  452. package/routes-src/v-locale-docs-slug.tsx +2 -3
  453. package/tsconfig.base.json +1 -1
  454. package/dist/safelist.css +0 -2
  455. package/dist/theme-no-reset.css +0 -379
  456. package/eject/desktop-sidebar-toggle-island/index.tsx +0 -110
@@ -1,23 +1,22 @@
1
1
  "use client";
2
2
 
3
3
  /** @jsxRuntime automatic */
4
- /** @jsxImportSource preact */
5
-
6
4
  // DocHistory island — relocated from src/components/doc-history.tsx (epic #2344, S4).
7
- // Uses the shared hook and types shipped by S1a instead of re-inlining them:
8
- // - useModalDialog from @takazudo/zudo-doc/use-modal-dialog (open/close sync, focus management)
5
+ // Uses the shared modal helper and types instead of re-inlining them:
6
+ // - modalDialog from @takazudo/zudo-doc/use-modal-dialog (open/close sync, focus management)
9
7
  // - DocHistoryData / DocHistoryEntry from @takazudo/zudo-doc/island-types
10
8
  // - SmartBreak from @takazudo/zudo-doc/smart-break
11
9
  //
12
10
  // CSS: island-coupled .diff-* rules are now in packages/zudo-doc/src/features.css
13
11
  // (moved from src/styles/global.css in this same commit).
14
12
 
15
- import { useState, useEffect, useCallback, useMemo, useRef } from "preact/compat";
13
+ import { computed, For, getScope, Show, signal } from "@takazudo/zfb/zudo-react";
14
+ import type { Ref } from "@takazudo/zfb/zudo-react";
16
15
  import type { DocHistoryData, DocHistoryEntry } from "../island-types/index.js";
17
16
  import { SmartBreak } from "../smart-break/index.js";
18
17
  import { History, Close, ArrowLeft } from "../icons/index.js";
19
18
  import { AFTER_NAVIGATE_EVENT } from "../transitions/index.js";
20
- import { useModalDialog } from "../use-modal-dialog/index.js";
19
+ import { modalDialog } from "../use-modal-dialog/index.js";
21
20
  import { formatDate } from "../format-date/index.js";
22
21
  import type { ResolvedDateFormats } from "../settings.js";
23
22
 
@@ -55,14 +54,14 @@ interface DiffSelection {
55
54
 
56
55
  function Spinner() {
57
56
  return (
58
- <div className="flex items-center justify-center py-vsp-xl">
57
+ <div class="flex items-center justify-center py-vsp-xl">
59
58
  <span
60
- className="inline-block box-border rounded-full animate-spin"
59
+ class="inline-block box-border rounded-full page-loading-spinner"
61
60
  style={{
62
- width: 48,
63
- height: 48,
61
+ width: "48px",
62
+ height: "48px",
64
63
  border: "5px solid var(--color-fg, #fff)",
65
- borderBottomColor: "transparent",
64
+ "border-bottom-color": "transparent",
66
65
  }}
67
66
  />
68
67
  </div>
@@ -170,6 +169,7 @@ async function getCachedDiff(
170
169
  newerHash: string,
171
170
  olderContent: string,
172
171
  newerContent: string,
172
+ isCurrent: () => boolean,
173
173
  ): Promise<DiffChanges> {
174
174
  const key = `${olderHash}::${newerHash}`;
175
175
  const hit = diffCache.get(key);
@@ -190,6 +190,9 @@ async function getCachedDiff(
190
190
  throw new Error('Compare requires the optional peer "diff": install it to use docHistory');
191
191
  },
192
192
  );
193
+ // The module import cannot be aborted; a replaced comparison must not do
194
+ // work or populate the cache after its owning scope has been disposed.
195
+ if (!isCurrent()) throw new Error("Comparison cancelled");
193
196
  const changes = diffLines(olderContent, newerContent);
194
197
  diffCache.set(key, changes);
195
198
  if (diffCache.size > DIFF_CACHE_LIMIT) {
@@ -208,63 +211,67 @@ function DiffViewer({
208
211
  onBack: () => void;
209
212
  showBackButton: boolean;
210
213
  }) {
211
- const [changes, setChanges] = useState<DiffChanges | null>(null);
212
- const [diffError, setDiffError] = useState<string | null>(null);
213
-
214
- useEffect(() => {
214
+ const scope = getScope();
215
+ const changes = signal<DiffChanges | null>(null);
216
+ const diffError = signal<string | null>(null);
217
+ const tableRevision = signal(0);
218
+ scope.onActivate(() => {
215
219
  let cancelled = false;
216
- setDiffError(null);
217
220
  getCachedDiff(
218
221
  selection.older.hash,
219
222
  selection.newer.hash,
220
223
  selection.older.content,
221
224
  selection.newer.content,
225
+ () => !cancelled && !scope.abortSignal.aborted,
222
226
  ).then((result) => {
223
- if (!cancelled) setChanges(result);
227
+ if (!cancelled && !scope.abortSignal.aborted) {
228
+ changes.value = result;
229
+ tableRevision.value++;
230
+ }
224
231
  }).catch((e: unknown) => {
225
- if (!cancelled) {
226
- setDiffError(e instanceof Error ? e.message : "Failed to compute diff");
232
+ if (!cancelled && !scope.abortSignal.aborted) {
233
+ diffError.value = e instanceof Error ? e.message : "Failed to compute diff";
227
234
  }
228
235
  });
229
236
  return () => { cancelled = true; };
230
- }, [selection.older.hash, selection.newer.hash]);
231
-
232
- const rows = useMemo(
233
- () => (changes ? buildSideBySideRows(changes) : []),
234
- [changes],
235
- );
237
+ });
238
+ const tableSnapshots = computed(() => changes.value
239
+ ? [{ revision: tableRevision.value, rows: buildSideBySideRows(changes.value) }]
240
+ : []);
236
241
 
237
242
  return (
238
- <div className="flex flex-col h-full">
243
+ <div class="flex flex-col h-full">
239
244
  {/* Header */}
240
- <div className="flex items-center gap-hsp-sm px-hsp-lg py-vsp-xs border-b border-muted">
245
+ <div class="flex items-center gap-hsp-sm px-hsp-lg py-vsp-xs border-b border-muted">
241
246
  {showBackButton && (
242
247
  <button
243
248
  type="button"
244
- onClick={onBack}
245
- className="text-muted hover:text-fg lg:hidden"
249
+ on:click={onBack}
250
+ class="text-muted hover:text-fg lg:hidden"
246
251
  aria-label="Back to revisions"
247
252
  >
248
- <ArrowLeft className="h-icon-sm w-icon-sm" />
253
+ <ArrowLeft class="h-icon-sm w-icon-sm" />
249
254
  </button>
250
255
  )}
251
- <div className="flex-1 min-w-0 flex">
252
- <div className="w-1/2 text-small text-muted font-mono truncate pr-hsp-sm">
256
+ <div class="flex-1 min-w-0 flex">
257
+ <div class="w-1/2 text-small text-muted font-mono truncate pr-hsp-sm">
253
258
  {selection.older.hash.slice(0, 7)}
254
259
  </div>
255
- <div className="w-1/2 text-small text-muted font-mono truncate pl-hsp-sm">
260
+ <div class="w-1/2 text-small text-muted font-mono truncate pl-hsp-sm">
256
261
  {selection.newer.hash.slice(0, 7)}
257
262
  </div>
258
263
  </div>
259
264
  </div>
260
265
 
261
266
  {/* Side-by-side diff — shows a spinner while the diff module lazy-loads */}
262
- {diffError && (
263
- <div className="px-hsp-lg py-vsp-lg text-danger text-small">{diffError}</div>
264
- )}
265
- {!changes && !diffError && <Spinner />}
266
- <div className={`flex-1 overflow-auto${!changes ? " hidden" : ""}`}>
267
- <table className="w-full border-collapse" style={{ tableLayout: "fixed" }}>
267
+ <Show when={computed(() => diffError.value !== null)}>
268
+ {() => <div class="px-hsp-lg py-vsp-lg text-danger text-small">{diffError}</div>}
269
+ </Show>
270
+ <Show when={computed(() => !changes.value && !diffError.value)}>{() => <Spinner />}</Show>
271
+ <div class={computed(() => `flex-1 overflow-auto${!changes.value ? " hidden" : ""}`)}>
272
+ <For each={tableSnapshots} by={(snapshot) => snapshot.revision}>
273
+ {(snapshot) => (
274
+ <table class="w-full border-collapse" style={{ "table-layout": "fixed" }}>
268
275
  <colgroup>
269
276
  <col style={{ width: "2.5rem" }} />
270
277
  <col />
@@ -272,7 +279,7 @@ function DiffViewer({
272
279
  <col />
273
280
  </colgroup>
274
281
  <tbody>
275
- {rows.map((row, idx) => {
282
+ {snapshot.value.rows.map((row) => {
276
283
  const leftBg =
277
284
  row.type === "removed" || row.type === "changed"
278
285
  ? "diff-line-removed"
@@ -285,21 +292,21 @@ function DiffViewer({
285
292
  const rightEmpty = row.rightLine === null;
286
293
 
287
294
  return (
288
- <tr key={idx} className="diff-row">
295
+ <tr class="diff-row">
289
296
  {/* Left line number */}
290
- <td className={`diff-line-num ${leftBg}`}>
297
+ <td class={`diff-line-num ${leftBg}`}>
291
298
  {row.leftNum ?? ""}
292
299
  </td>
293
300
  {/* Left content */}
294
- <td className={`diff-line-content ${leftBg}${leftEmpty ? " diff-line-empty" : ""}`}>
301
+ <td class={`diff-line-content ${leftBg}${leftEmpty ? " diff-line-empty" : ""}`}>
295
302
  {row.leftLine ?? ""}
296
303
  </td>
297
304
  {/* Right line number */}
298
- <td className={`diff-line-num ${rightBg}`}>
305
+ <td class={`diff-line-num ${rightBg}`}>
299
306
  {row.rightNum ?? ""}
300
307
  </td>
301
308
  {/* Right content */}
302
- <td className={`diff-line-content ${rightBg}${rightEmpty ? " diff-line-empty" : ""}`}>
309
+ <td class={`diff-line-content ${rightBg}${rightEmpty ? " diff-line-empty" : ""}`}>
303
310
  {row.rightLine ?? ""}
304
311
  </td>
305
312
  </tr>
@@ -307,6 +314,8 @@ function DiffViewer({
307
314
  })}
308
315
  </tbody>
309
316
  </table>
317
+ )}
318
+ </For>
310
319
  </div>
311
320
  </div>
312
321
  );
@@ -327,28 +336,29 @@ function RevisionList({
327
336
  displayLocale?: string;
328
337
  dateFormats?: ResolvedDateFormats;
329
338
  }) {
330
- const [selectedA, setSelectedA] = useState<number>(1); // older (default: second entry)
331
- const [selectedB, setSelectedB] = useState<number>(0); // newer (default: first entry)
339
+ const revisionEntries = signal(entries);
340
+ const selectedA = signal(1); // older (default: second entry)
341
+ const selectedB = signal(0); // newer (default: first entry)
332
342
 
333
343
  if (entries.length === 0) {
334
344
  return (
335
- <div className="px-hsp-lg py-vsp-lg text-muted text-small">
345
+ <div class="px-hsp-lg py-vsp-lg text-muted text-small">
336
346
  No revision history available.
337
347
  </div>
338
348
  );
339
349
  }
340
350
 
341
- const canCompare =
342
- selectedA !== selectedB &&
343
- selectedA >= 0 &&
344
- selectedB >= 0 &&
345
- selectedA < entries.length &&
346
- selectedB < entries.length;
351
+ const canCompare = computed(() =>
352
+ selectedA.value !== selectedB.value &&
353
+ selectedA.value >= 0 &&
354
+ selectedB.value >= 0 &&
355
+ selectedA.value < entries.length &&
356
+ selectedB.value < entries.length);
347
357
 
348
358
  function handleCompare() {
349
- if (!canCompare) return;
350
- const idxOlder = Math.max(selectedA, selectedB);
351
- const idxNewer = Math.min(selectedA, selectedB);
359
+ if (!canCompare.value) return;
360
+ const idxOlder = Math.max(selectedA.value, selectedB.value);
361
+ const idxNewer = Math.min(selectedA.value, selectedB.value);
352
362
  const olderEntry = entries[idxOlder];
353
363
  const newerEntry = entries[idxNewer];
354
364
  if (!olderEntry || !newerEntry) return;
@@ -359,33 +369,33 @@ function RevisionList({
359
369
  }
360
370
 
361
371
  return (
362
- <div className="flex flex-col h-full">
372
+ <div class="flex flex-col h-full">
363
373
  {/* Compare bar */}
364
374
  {entries.length >= 2 && (
365
- <div className="px-hsp-lg py-vsp-xs border-b border-muted flex items-center gap-hsp-sm">
375
+ <div class="px-hsp-lg py-vsp-xs border-b border-muted flex items-center gap-hsp-sm">
366
376
  <button
367
377
  type="button"
368
- disabled={!canCompare}
369
- onClick={handleCompare}
370
- className={
371
- canCompare
378
+ disabled={computed(() => !canCompare.value)}
379
+ on:click={handleCompare}
380
+ class={computed(() =>
381
+ canCompare.value
372
382
  ? "px-hsp-md py-vsp-2xs text-small rounded bg-accent text-bg hover:bg-accent-hover"
373
383
  : "px-hsp-md py-vsp-2xs text-small rounded bg-surface text-muted cursor-not-allowed"
374
- }
384
+ )}
375
385
  >
376
386
  Compare
377
387
  </button>
378
- <span className="text-caption text-muted">
388
+ <span class="text-caption text-muted">
379
389
  Select two revisions (A / B)
380
390
  </span>
381
391
  </div>
382
392
  )}
383
393
 
384
394
  {/* Revision entries */}
385
- <div className="flex-1 overflow-auto">
386
- {entries.map((entry, idx) => {
387
- const isA = selectedA === idx;
388
- const isB = selectedB === idx;
395
+ <div class="flex-1 overflow-auto">
396
+ <For each={revisionEntries} by={(entry) => entry.hash}>{(entry, idx) => {
397
+ const isA = computed(() => selectedA.value === idx.value);
398
+ const isB = computed(() => selectedB.value === idx.value);
389
399
  // Renders in UTC (via the shared formatter) rather than the
390
400
  // visitor's local time zone — a deliberate correction over the
391
401
  // previous ambient-browser-locale-dependent formatting, not a
@@ -393,45 +403,44 @@ function RevisionList({
393
403
  // shift by one day versus the prior behavior; the wave-6
394
404
  // default-parity gate exempts doc-history on this basis (#4073).
395
405
  const dateStr = formatDate(
396
- entry.date,
406
+ entry.value.date,
397
407
  displayLocale ?? "en",
398
408
  dateFormats?.full,
399
409
  );
400
410
 
401
411
  return (
402
412
  <div
403
- key={entry.hash}
404
- className={
405
- isA || isB
413
+ class={computed(() =>
414
+ isA.value || isB.value
406
415
  ? "px-hsp-lg py-vsp-xs border-b border-muted bg-surface"
407
416
  : "px-hsp-lg py-vsp-xs border-b border-muted hover:bg-surface"
408
- }
417
+ )}
409
418
  >
410
- <div className="flex items-start gap-hsp-sm">
419
+ <div class="flex items-start gap-hsp-sm">
411
420
  {/* Selection badges */}
412
421
  {entries.length >= 2 && (
413
- <div className="flex flex-col gap-vsp-2xs pt-[2px] shrink-0">
422
+ <div class="flex flex-col gap-vsp-2xs pt-[2px] shrink-0">
414
423
  <button
415
424
  type="button"
416
- onClick={() => setSelectedA(idx)}
417
- className={
418
- isA
425
+ on:click={() => { selectedA.value = idx.value; }}
426
+ class={computed(() =>
427
+ isA.value
419
428
  ? "w-[1.5rem] h-[1.25rem] text-caption rounded flex items-center justify-center bg-accent text-bg"
420
429
  : "w-[1.5rem] h-[1.25rem] text-caption rounded flex items-center justify-center border border-muted text-muted hover:border-fg hover:text-fg"
421
- }
422
- aria-label={`Select revision ${entry.hash.slice(0, 7)} as A`}
430
+ )}
431
+ aria-label={`Select revision ${entry.value.hash.slice(0, 7)} as A`}
423
432
  >
424
433
  A
425
434
  </button>
426
435
  <button
427
436
  type="button"
428
- onClick={() => setSelectedB(idx)}
429
- className={
430
- isB
437
+ on:click={() => { selectedB.value = idx.value; }}
438
+ class={computed(() =>
439
+ isB.value
431
440
  ? "w-[1.5rem] h-[1.25rem] text-caption rounded flex items-center justify-center bg-accent text-bg"
432
441
  : "w-[1.5rem] h-[1.25rem] text-caption rounded flex items-center justify-center border border-muted text-muted hover:border-fg hover:text-fg"
433
- }
434
- aria-label={`Select revision ${entry.hash.slice(0, 7)} as B`}
442
+ )}
443
+ aria-label={`Select revision ${entry.value.hash.slice(0, 7)} as B`}
435
444
  >
436
445
  B
437
446
  </button>
@@ -439,22 +448,22 @@ function RevisionList({
439
448
  )}
440
449
 
441
450
  {/* Revision info */}
442
- <div className="min-w-0 flex-1">
443
- <div className="flex items-baseline gap-hsp-sm">
444
- <code className="text-caption text-accent font-mono">
445
- {entry.hash.slice(0, 7)}
451
+ <div class="min-w-0 flex-1">
452
+ <div class="flex items-baseline gap-hsp-sm">
453
+ <code class="text-caption text-accent font-mono">
454
+ {entry.value.hash.slice(0, 7)}
446
455
  </code>
447
- <span className="text-caption text-muted">{dateStr}</span>
456
+ <span class="text-caption text-muted">{dateStr}</span>
448
457
  </div>
449
- <div className="text-small text-fg mt-vsp-2xs truncate">
450
- <SmartBreak>{entry.message}</SmartBreak>
458
+ <div class="text-small text-fg mt-vsp-2xs truncate">
459
+ <SmartBreak>{entry.value.message}</SmartBreak>
451
460
  </div>
452
- <div className="text-caption text-muted">{entry.author}</div>
461
+ <div class="text-caption text-muted">{entry.value.author}</div>
453
462
  </div>
454
463
  </div>
455
464
  </div>
456
465
  );
457
- })}
466
+ }}</For>
458
467
  </div>
459
468
  </div>
460
469
  );
@@ -471,19 +480,16 @@ export function DocHistory({
471
480
  displayLocale,
472
481
  dateFormats,
473
482
  }: DocHistoryProps) {
474
- const [view, setView] = useState<PanelView>("closed");
475
- const [data, setData] = useState<DocHistoryData | null>(null);
476
- const [loading, setLoading] = useState(false);
477
- const [error, setError] = useState<string | null>(null);
478
- const [diffSelection, setDiffSelection] = useState<DiffSelection | null>(
479
- null,
480
- );
483
+ const scope = getScope();
484
+ const view = signal<PanelView>("closed");
485
+ const data = signal<DocHistoryData | null>(null);
486
+ const loading = signal(false);
487
+ const error = signal<string | null>(null);
488
+ const diffSelection = signal<DiffSelection | null>(null);
481
489
  // Holds the history trigger button element captured in handleOpen() so the
482
- // hook can restore focus to it when the dialog closes. We capture it there
483
- // (synchronously in the click handler, before React re-renders) rather than
484
- // inside the hook's effect because the button is unmounted when isOpen=true
485
- // (conditional render) — by the time the open-effect fires, it's gone (#2295).
486
- const returnFocusRef = useRef<HTMLElement | null>(null);
490
+ // helper can restore focus to it when the dialog closes. Capture it in the
491
+ // click handler so the connected trigger remains the return target (#2295).
492
+ const returnFocusRef: Ref<HTMLElement> = { current: null };
487
493
 
488
494
  const base = basePath.replace(/\/+$/, "");
489
495
  // Doc-history storage sentinel ("" -> "index"): a root index page has the
@@ -499,74 +505,85 @@ export function DocHistory({
499
505
  ? `${base}/doc-history/${locale}/${historySlug}.json`
500
506
  : `${base}/doc-history/${historySlug}.json`;
501
507
 
502
- const fetchHistory = useCallback(async () => {
503
- if (data) return; // already loaded
504
- setLoading(true);
505
- setError(null);
508
+ let fetchController: AbortController | null = null;
509
+ let fetchRun = 0;
510
+ const fetchHistory = async () => {
511
+ if (data.value || loading.value) return;
512
+ fetchController?.abort();
513
+ const controller = new AbortController();
514
+ fetchController = controller;
515
+ const run = ++fetchRun;
516
+ loading.value = true;
517
+ error.value = null;
506
518
  try {
507
- const res = await fetch(fetchPath);
519
+ const res = await fetch(fetchPath, { signal: controller.signal });
520
+ if (controller.signal.aborted || scope.abortSignal.aborted || run !== fetchRun) return;
508
521
  if (!res.ok) {
509
522
  throw new Error(`Failed to load history (${res.status})`);
510
523
  }
511
524
  const json: DocHistoryData = await res.json();
525
+ if (controller.signal.aborted || scope.abortSignal.aborted || run !== fetchRun) return;
512
526
  if (!json || !Array.isArray(json.entries)) {
513
527
  throw new Error("Malformed history response");
514
528
  }
515
- setData(json);
529
+ data.value = json;
516
530
  } catch (e) {
517
- setError(e instanceof Error ? e.message : "Failed to load history");
531
+ if (!controller.signal.aborted && !scope.abortSignal.aborted && run === fetchRun)
532
+ error.value = e instanceof Error ? e.message : "Failed to load history";
518
533
  } finally {
519
- setLoading(false);
534
+ if (!scope.abortSignal.aborted && run === fetchRun) loading.value = false;
520
535
  }
521
- }, [data, fetchPath]);
536
+ };
537
+ scope.onActivate(() => () => { fetchController?.abort(); fetchRun++; });
522
538
 
523
- function handleOpen(e: React.MouseEvent<HTMLButtonElement>) {
539
+ function handleOpen(e: Event) {
524
540
  // Capture the trigger so the dialog hook can restore focus to it on close
525
541
  // (a11y #2295). The button now stays mounted while the panel is open (see
526
542
  // the render below), so this ref stays a *connected* node — required for
527
543
  // .focus() to actually land on close (zudolab/zudo-doc#2303).
528
- returnFocusRef.current = e.currentTarget;
529
- setView("revisions");
530
- fetchHistory();
544
+ returnFocusRef.current = e.currentTarget as HTMLButtonElement;
545
+ view.value = "revisions";
546
+ void fetchHistory();
531
547
  }
532
548
 
533
- const handleClose = useCallback(() => {
534
- setView("closed");
535
- setDiffSelection(null);
536
- }, []);
549
+ const handleClose = () => {
550
+ view.value = "closed";
551
+ diffSelection.value = null;
552
+ fetchController?.abort();
553
+ fetchRun++;
554
+ loading.value = false;
555
+ };
537
556
 
538
557
  function handleSelectDiff(selection: DiffSelection) {
539
- setDiffSelection(selection);
540
- setView("diff");
558
+ diffSelection.value = selection;
559
+ view.value = "diff";
541
560
  }
542
561
 
543
562
  function handleBackToRevisions() {
544
- setDiffSelection(null);
545
- setView("revisions");
563
+ diffSelection.value = null;
564
+ view.value = "revisions";
546
565
  }
547
566
 
548
567
  // Lock body scroll when panel is open
549
- useEffect(() => {
550
- if (view !== "closed") {
551
- document.body.style.overflow = "hidden";
552
- } else {
553
- document.body.style.overflow = "";
554
- }
555
- return () => {
556
- document.body.style.overflow = "";
557
- };
558
- }, [view]);
568
+ scope.effect(() => {
569
+ if (view.value === "closed") return;
570
+ const previousOverflow = document.body.style.overflow;
571
+ document.body.style.overflow = "hidden";
572
+ return () => { document.body.style.overflow = previousOverflow; };
573
+ });
559
574
 
560
- const isOpen = view !== "closed";
561
- const hasDiff = view === "diff" && diffSelection;
575
+ const isOpen = computed(() => view.value !== "closed");
576
+ const hasDiff = computed(() => view.value === "diff" && diffSelection.value !== null);
577
+ const diffPairs = computed(() => diffSelection.value && view.value === "diff"
578
+ ? [{ key: `${diffSelection.value.older.hash}:${diffSelection.value.newer.hash}`, selection: diffSelection.value }]
579
+ : []);
562
580
 
563
581
  // Shared dialog lifecycle: showModal/close sync, native-close callback,
564
- // and navigation-close — delegated to useModalDialog.
582
+ // and navigation-close — delegated to modalDialog.
565
583
  // manageFocus: move focus to the close button on open, restore to the
566
584
  // history trigger button on close (a11y interaction #2295).
567
- // returnFocusRef: pre-captured trigger element (button is unmounted when
568
- // isOpen=true, so capture must happen in the click handler, not the effect).
569
- const { dialogRef } = useModalDialog({
585
+ // returnFocusRef: pre-captured, still-connected trigger element.
586
+ const { dialogRef } = modalDialog(scope, {
570
587
  isOpen,
571
588
  onClose: handleClose,
572
589
  navigateEvent: AFTER_NAVIGATE_EVENT,
@@ -585,15 +602,16 @@ export function DocHistory({
585
602
  (the old `{!isOpen && …}`) left the ref pointing at a detached node,
586
603
  so `.focus()` no-op'd and focus fell to <body> on close
587
604
  (zudolab/zudo-doc#2303). */}
588
- <div className="flex justify-end mt-vsp-xl">
605
+ <div class="flex justify-end mt-vsp-xl">
589
606
  <button
590
607
  type="button"
591
- onClick={handleOpen}
592
- className="doc-history-trigger flex items-center gap-hsp-xs px-hsp-md py-vsp-xs rounded-lg bg-surface border border-muted text-muted hover:text-accent hover:border-accent focus-visible:text-accent focus-visible:border-accent transition-colors"
608
+ on:click={handleOpen}
609
+ data-doc-history-trigger=""
610
+ class="flex items-center gap-hsp-xs px-hsp-md py-vsp-xs rounded-lg bg-surface border border-muted text-muted hover:text-accent hover:border-accent focus-visible:text-accent focus-visible:border-accent transition-colors duration-0"
593
611
  aria-label="View document history"
594
612
  >
595
- <History className="h-icon-md w-icon-md" />
596
- <span className="text-small">History</span>
613
+ <History class="h-icon-md w-icon-md" />
614
+ <span class="text-small">History</span>
597
615
  </button>
598
616
  </div>
599
617
 
@@ -609,47 +627,46 @@ export function DocHistory({
609
627
  <dialog
610
628
  ref={dialogRef}
611
629
  aria-label="Document revision history"
612
- className="doc-history-panel z-modal fixed inset-0 m-0 h-full w-full max-h-full max-w-full bg-bg border-none p-0 backdrop:z-modal-backdrop backdrop:bg-bg/30"
630
+ data-doc-history-panel=""
631
+ class="z-modal fixed inset-0 m-0 h-full w-full max-h-full max-w-full bg-bg border-none p-0 backdrop:z-modal-backdrop backdrop:bg-bg/30"
613
632
  style={{ color: "var(--color-fg)" }}
614
633
  >
615
634
  {/* Panel header */}
616
- <div className="flex items-center justify-between px-hsp-lg py-vsp-xs border-b border-muted">
617
- <h2 className="text-body font-semibold text-fg">
618
- {view === "diff" ? "Diff" : "Revision History"}
635
+ <div class="flex items-center justify-between px-hsp-lg py-vsp-xs border-b border-muted">
636
+ <h2 class="text-body font-semibold text-fg">
637
+ {computed(() => view.value === "diff" ? "Diff" : "Revision History")}
619
638
  </h2>
620
639
  <button
621
640
  type="button"
622
- onClick={handleClose}
623
- className="text-muted hover:text-fg"
641
+ on:click={handleClose}
642
+ class="text-muted hover:text-fg"
624
643
  aria-label="Close history panel"
625
644
  >
626
- <Close className="h-icon-md w-icon-md" />
645
+ <Close class="h-icon-md w-icon-md" />
627
646
  </button>
628
647
  </div>
629
648
 
630
649
  {/* Panel body */}
631
- <div className="h-[calc(100%-3rem)] overflow-hidden">
632
- {loading && <Spinner />}
650
+ <div class="h-[calc(100%_-_3rem)] overflow-hidden">
651
+ <Show when={loading}>{() => <Spinner />}</Show>
633
652
 
634
- {error && (
635
- <div className="px-hsp-lg py-vsp-lg text-danger text-small">
636
- {error}
637
- </div>
638
- )}
653
+ <Show when={computed(() => error.value !== null)}>
654
+ {() => <div class="px-hsp-lg py-vsp-lg text-danger text-small">{error}</div>}
655
+ </Show>
639
656
 
640
657
  {/* Difit-style LR split: revision sidebar | diff area */}
641
- {!loading && !error && data && (
642
- <div className="flex h-full">
658
+ <Show when={computed(() => !loading.value && !error.value && data.value !== null)}>{() => (
659
+ <div class="flex h-full">
643
660
  {/* Left sidebar: revision list — always visible on lg */}
644
661
  <div
645
- className={
646
- hasDiff
662
+ class={computed(() =>
663
+ hasDiff.value
647
664
  ? "hidden lg:flex lg:flex-col lg:w-[clamp(16rem,25%,22rem)] shrink-0 border-r border-muted h-full"
648
665
  : "flex flex-col w-full h-full"
649
- }
666
+ )}
650
667
  >
651
668
  <RevisionList
652
- entries={data.entries}
669
+ entries={data.value!.entries}
653
670
  onSelectDiff={handleSelectDiff}
654
671
  displayLocale={displayLocale}
655
672
  dateFormats={dateFormats}
@@ -657,22 +674,19 @@ export function DocHistory({
657
674
  </div>
658
675
 
659
676
  {/* Right: diff viewer (on mobile, replaces the sidebar) */}
660
- {hasDiff && (
661
- <div className="flex-1 min-w-0 h-full">
677
+ <Show when={hasDiff}>{() => (
678
+ <div class="flex-1 min-w-0 h-full">
662
679
  {/* Key on the compared pair forces a fresh mount whenever the
663
680
  selection changes, so the previous pair's diff rows can
664
681
  never render under the new header hashes while the lazy
665
682
  diff recompute is in flight (#2068). */}
666
- <DiffViewer
667
- key={`${diffSelection.older.hash}:${diffSelection.newer.hash}`}
668
- selection={diffSelection}
669
- onBack={handleBackToRevisions}
670
- showBackButton={true}
671
- />
683
+ <For each={diffPairs} by={(pair) => pair.key}>{(pair) => (
684
+ <DiffViewer selection={pair.value.selection} onBack={handleBackToRevisions} showBackButton={true} />
685
+ )}</For>
672
686
  </div>
673
- )}
687
+ )}</Show>
674
688
  </div>
675
- )}
689
+ )}</Show>
676
690
  </div>
677
691
  </dialog>
678
692
  </>