wiki-viewer 1.14.1 → 2.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 (559) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  4. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  5. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  12. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  13. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  14. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  16. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  17. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  18. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  19. package/.next/standalone/.next/server/app/api/agent/activity/route.js +6 -6
  20. package/.next/standalone/.next/server/app/api/agent/activity/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js +8 -8
  22. package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js +9 -9
  24. package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js +8 -8
  26. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js +8 -8
  28. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js +8 -8
  30. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js +9 -9
  32. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/agent/internal/span/route.js +8 -8
  34. package/.next/standalone/.next/server/app/api/agent/internal/span/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/agent/settings/route.js +7 -7
  36. package/.next/standalone/.next/server/app/api/agent/settings/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js +6 -6
  38. package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/assets/[...path]/route.js +5 -5
  41. package/.next/standalone/.next/server/app/api/assets/[...path]/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/share/[token]/route.js +6 -5
  43. package/.next/standalone/.next/server/app/api/share/[token]/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/share/route.js +4 -4
  45. package/.next/standalone/.next/server/app/api/share/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/system/browse/route.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/api/system/clear-root/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/system/reveal/route.js +6 -6
  49. package/.next/standalone/.next/server/app/api/system/reveal/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/api/system/root-status/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/api/system/set-root/route.js +6 -5
  52. package/.next/standalone/.next/server/app/api/system/set-root/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js +5 -4
  54. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/refresh/route.js +6 -5
  56. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/refresh/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js +5 -4
  58. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/api/system/workspaces/route.js +7 -6
  60. package/.next/standalone/.next/server/app/api/system/workspaces/route.js.nft.json +1 -1
  61. package/.next/standalone/.next/server/app/api/upload/[...path]/route.js +5 -5
  62. package/.next/standalone/.next/server/app/api/upload/[...path]/route.js.nft.json +1 -1
  63. package/.next/standalone/.next/server/app/api/wiki/app/route.js +7 -7
  64. package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
  65. package/.next/standalone/.next/server/app/api/wiki/backlinks/route.js +7 -7
  66. package/.next/standalone/.next/server/app/api/wiki/backlinks/route.js.nft.json +1 -1
  67. package/.next/standalone/.next/server/app/api/wiki/content/route.js +7 -7
  68. package/.next/standalone/.next/server/app/api/wiki/content/route.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/api/wiki/download/route.js +6 -6
  70. package/.next/standalone/.next/server/app/api/wiki/download/route.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/api/wiki/file/route.js +5 -5
  72. package/.next/standalone/.next/server/app/api/wiki/file/route.js.nft.json +1 -1
  73. package/.next/standalone/.next/server/app/api/wiki/folder/route.js +5 -5
  74. package/.next/standalone/.next/server/app/api/wiki/folder/route.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/api/wiki/git-branches/route.js +4 -4
  76. package/.next/standalone/.next/server/app/api/wiki/git-branches/route.js.nft.json +1 -1
  77. package/.next/standalone/.next/server/app/api/wiki/git-checkout/route.js +6 -6
  78. package/.next/standalone/.next/server/app/api/wiki/git-checkout/route.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/api/wiki/git-diff/route.js +4 -4
  80. package/.next/standalone/.next/server/app/api/wiki/git-diff/route.js.nft.json +1 -1
  81. package/.next/standalone/.next/server/app/api/wiki/git-file-info/route.js +4 -4
  82. package/.next/standalone/.next/server/app/api/wiki/git-file-info/route.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/api/wiki/git-history/route.js +4 -4
  84. package/.next/standalone/.next/server/app/api/wiki/git-history/route.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/api/wiki/git-pull/route.js +8 -8
  86. package/.next/standalone/.next/server/app/api/wiki/git-pull/route.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/api/wiki/move/route.js +6 -6
  88. package/.next/standalone/.next/server/app/api/wiki/move/route.js.nft.json +1 -1
  89. package/.next/standalone/.next/server/app/api/wiki/new-file/route.js +5 -5
  90. package/.next/standalone/.next/server/app/api/wiki/new-file/route.js.nft.json +1 -1
  91. package/.next/standalone/.next/server/app/api/wiki/outlinks/route.js +7 -7
  92. package/.next/standalone/.next/server/app/api/wiki/outlinks/route.js.nft.json +1 -1
  93. package/.next/standalone/.next/server/app/api/wiki/page/route.js +5 -5
  94. package/.next/standalone/.next/server/app/api/wiki/page/route.js.nft.json +1 -1
  95. package/.next/standalone/.next/server/app/api/wiki/presence/route.js +6 -6
  96. package/.next/standalone/.next/server/app/api/wiki/presence/route.js.nft.json +1 -1
  97. package/.next/standalone/.next/server/app/api/wiki/route.js +5 -5
  98. package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
  99. package/.next/standalone/.next/server/app/api/wiki/search/route.js +5 -5
  100. package/.next/standalone/.next/server/app/api/wiki/search/route.js.nft.json +1 -1
  101. package/.next/standalone/.next/server/app/api/wiki/slugs/route.js +5 -5
  102. package/.next/standalone/.next/server/app/api/wiki/slugs/route.js.nft.json +1 -1
  103. package/.next/standalone/.next/server/app/api/wiki/upload/route.js +5 -5
  104. package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
  105. package/.next/standalone/.next/server/app/api/wiki/watch/route.js +5 -5
  106. package/.next/standalone/.next/server/app/api/wiki/watch/route.js.nft.json +1 -1
  107. package/.next/standalone/.next/server/app/index.html +1 -1
  108. package/.next/standalone/.next/server/app/index.rsc +3 -3
  109. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  110. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
  111. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  113. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  114. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  115. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  116. package/.next/standalone/.next/server/app/s/[token]/page_client-reference-manifest.js +1 -1
  117. package/.next/standalone/.next/server/app/signin/page_client-reference-manifest.js +1 -1
  118. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-jzw2_._.js +3 -0
  119. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0.-oq-7._.js +3 -0
  120. package/.next/standalone/.next/server/chunks/[root-of-the-server]__000spch._.js +3 -0
  121. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01oin-a._.js +3 -0
  122. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01qcera._.js +16 -0
  123. package/.next/standalone/.next/server/chunks/[root-of-the-server]__02.uul~._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/[root-of-the-server]__024967n._.js +3 -0
  125. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06booxp._.js +3 -0
  126. package/.next/standalone/.next/server/chunks/[root-of-the-server]__071_pmz._.js +3 -0
  127. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_0bqpg._.js +3 -0
  128. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_1f2cw._.js +3 -0
  129. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0a6j6r4._.js +3 -0
  130. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0aoqat-._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0as-u6z._.js +3 -0
  132. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bvtbax._.js +3 -0
  133. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0c-ee48._.js +5 -0
  134. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e_9pc5._.js +3 -0
  135. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0esmskc._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0fshk._._.js +20 -0
  137. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0fu72o7._.js +3 -0
  138. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0k9mili._.js +3 -0
  139. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l-wu5u._.js +3 -0
  140. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lvz22t._.js +20 -0
  141. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0n-chxk._.js +16 -0
  142. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0n9zdf0._.js +3 -0
  143. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nmx3k2._.js +12 -0
  144. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nzlmkb._.js +3 -0
  145. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o0v6hf._.js +3 -0
  146. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s38pid._.js +3 -0
  147. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s_8poz._.js +3 -0
  148. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0sfrx~.._.js +3 -0
  149. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0sggstu._.js +3 -0
  150. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0u81k~2._.js +3 -0
  151. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uibxf_._.js +3 -0
  152. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wfv_by._.js +3 -0
  153. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wlj-yw._.js +3 -0
  154. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w~__xz._.js +3 -0
  155. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0yat~ul._.js +3 -0
  156. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0yxauu6._.js +3 -0
  157. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zbkgnr._.js +3 -0
  158. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zv_j05._.js +3 -0
  159. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0~rntxj._.js +3 -0
  160. package/.next/standalone/.next/server/chunks/src_lib_search_0d-_j3u._.js +4 -4
  161. package/.next/standalone/.next/server/chunks/src_lib_search_0ymmp0c._.js +3 -3
  162. package/.next/standalone/.next/server/chunks/src_lib_workspaces_ts_0qon40u._.js +4 -0
  163. package/.next/standalone/.next/server/chunks/ssr/{0~w0_katex_dist_katex_mjs_029cdbg._.js → 0~w0_katex_dist_katex_mjs_0s4yb1t._.js} +1 -1
  164. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0z41nrr._.js → [root-of-the-server]__10te1ye._.js} +2 -2
  165. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_13p348f._.js → node_modules__pnpm_0-a_zjq._.js} +1 -1
  166. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0gos._c._.js → node_modules__pnpm_02l_1_8._.js} +1 -1
  167. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0ru1i2-._.js → node_modules__pnpm_03rzttw._.js} +2 -2
  168. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_08xpp3w._.js → node_modules__pnpm_04frxp9._.js} +1 -1
  169. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0b8odbc._.js → node_modules__pnpm_057-t6m._.js} +1 -1
  170. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0f_n_zp._.js → node_modules__pnpm_05_oahi._.js} +1 -1
  171. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0~i3a.i._.js → node_modules__pnpm_05ans~m._.js} +1 -1
  172. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0i2m0b.._.js → node_modules__pnpm_06-2ia.._.js} +2 -2
  173. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0aha1~u._.js → node_modules__pnpm_086.ow4._.js} +1 -1
  174. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0w.kwue._.js → node_modules__pnpm_0cahog3._.js} +1 -1
  175. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0e0r_qk._.js → node_modules__pnpm_0cxanql._.js} +2 -2
  176. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0ajqqlq._.js → node_modules__pnpm_0e09gmf._.js} +1 -1
  177. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0hzzx9d._.js → node_modules__pnpm_0eqjo2f._.js} +1 -1
  178. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_02bje_g._.js → node_modules__pnpm_0f-yazg._.js} +1 -1
  179. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0xk042~._.js → node_modules__pnpm_0iu44jv._.js} +1 -1
  180. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_11ede0p._.js → node_modules__pnpm_0jh8q66._.js} +2 -2
  181. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0k-7uzm._.js → node_modules__pnpm_0m39f~t._.js} +1 -1
  182. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0xr.ztu._.js → node_modules__pnpm_0ma-8l.._.js} +1 -1
  183. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0i6_kfe._.js → node_modules__pnpm_0met6sx._.js} +2 -2
  184. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_04v1_ty._.js → node_modules__pnpm_0mtrsi0._.js} +2 -2
  185. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0607~kg._.js → node_modules__pnpm_0n_swwn._.js} +2 -2
  186. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0t4l89_._.js → node_modules__pnpm_0ndyy93._.js} +1 -1
  187. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0-_58b8._.js → node_modules__pnpm_0s-cj7c._.js} +1 -1
  188. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0y2h_cz._.js → node_modules__pnpm_0tr41-t._.js} +1 -1
  189. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_08edl1n._.js → node_modules__pnpm_0u.uija._.js} +1 -1
  190. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0026mgr._.js → node_modules__pnpm_0wiyn6d._.js} +1 -1
  191. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_09o_spj._.js → node_modules__pnpm_0ygzt82._.js} +1 -1
  192. package/.next/standalone/.next/server/chunks/ssr/src_app_page_tsx_0ss2.w7._.js +2 -2
  193. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  194. package/.next/standalone/.next/server/middleware-manifest.json +1 -1
  195. package/.next/standalone/.next/server/pages/404.html +1 -1
  196. package/.next/standalone/.next/server/pages/500.html +1 -1
  197. package/.next/standalone/.next/static/chunks/0ci7khnv7i~7_.js +17 -0
  198. package/.next/standalone/.next/static/chunks/0sx~65~lg4mgl.css +5 -0
  199. package/.next/standalone/package.json +1 -1
  200. package/README.md +88 -17
  201. package/bin/postbuild.js +21 -0
  202. package/bin/wiki-viewer.js +198 -6
  203. package/package.json +1 -1
  204. package/.next/standalone/.github/workflows/publish.yml +0 -37
  205. package/.next/standalone/.handoffs/archive/handoff-20260612-113332.md +0 -98
  206. package/.next/standalone/.handoffs/archive/handoff-20260612-141032.md +0 -73
  207. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-.y6im._.js +0 -3
  208. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-3_t2y._.js +0 -3
  209. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-kjkzt._.js +0 -3
  210. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-puax4._.js +0 -4
  211. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01h996g._.js +0 -3
  212. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0329ktu._.js +0 -3
  213. package/.next/standalone/.next/server/chunks/[root-of-the-server]__03_xj4r._.js +0 -3
  214. package/.next/standalone/.next/server/chunks/[root-of-the-server]__05398dt._.js +0 -4
  215. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06lfvxh._.js +0 -3
  216. package/.next/standalone/.next/server/chunks/[root-of-the-server]__06w0aet._.js +0 -3
  217. package/.next/standalone/.next/server/chunks/[root-of-the-server]__070_p2a._.js +0 -4
  218. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0905hi1._.js +0 -21
  219. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0aa63~j._.js +0 -5
  220. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0avcplk._.js +0 -4
  221. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0b8_kyo._.js +0 -16
  222. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bticq1._.js +0 -3
  223. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bwevvu._.js +0 -3
  224. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0c__k3q._.js +0 -4
  225. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cp~z4x._.js +0 -3
  226. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0fmdv9h._.js +0 -20
  227. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0h0qbwr._.js +0 -3
  228. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jk~oyi._.js +0 -3
  229. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ndq77b._.js +0 -3
  230. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nvgpuk._.js +0 -3
  231. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ocvng2._.js +0 -3
  232. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ps4h~.._.js +0 -3
  233. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0q7jqwp._.js +0 -12
  234. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qew~6p._.js +0 -3
  235. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s_9f-a._.js +0 -3
  236. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uv7b8u._.js +0 -3
  237. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uv8.9i._.js +0 -3
  238. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vfe2io._.js +0 -4
  239. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w11kjy._.js +0 -3
  240. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wc-.e4._.js +0 -3
  241. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ymovmp._.js +0 -3
  242. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0~c6ouv._.js +0 -3
  243. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0~rhs0u._.js +0 -16
  244. package/.next/standalone/.next/server/chunks/[root-of-the-server]__10h9tid._.js +0 -3
  245. package/.next/standalone/.next/server/chunks/[root-of-the-server]__10yk-u1._.js +0 -3
  246. package/.next/standalone/.next/server/chunks/[root-of-the-server]__11105ew._.js +0 -3
  247. package/.next/standalone/.next/server/chunks/[root-of-the-server]__11a3dhw._.js +0 -3
  248. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13420tt._.js +0 -3
  249. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13h~pdf._.js +0 -3
  250. package/.next/standalone/.next/static/chunks/02kg.lbkfi1yc.css +0 -5
  251. package/.next/standalone/.next/static/chunks/0rcwd6-.iu~nd.js +0 -17
  252. package/.next/standalone/.pi/ownership.json +0 -3
  253. package/.next/standalone/AGENTS.md +0 -114
  254. package/.next/standalone/DESIGN.md +0 -521
  255. package/.next/standalone/README.md +0 -821
  256. package/.next/standalone/TODO.md +0 -20
  257. package/.next/standalone/agents/bootstrap-prompt.md +0 -14
  258. package/.next/standalone/agents/wiki-viewer-skill/SKILL.md +0 -456
  259. package/.next/standalone/bin/postbuild.js +0 -78
  260. package/.next/standalone/bin/wiki-viewer.js +0 -922
  261. package/.next/standalone/certificates/localhost-key.pem +0 -28
  262. package/.next/standalone/certificates/localhost.pem +0 -24
  263. package/.next/standalone/docs/agent-collab-plan.md +0 -1615
  264. package/.next/standalone/docs/agent-collab-v2-plan.md +0 -771
  265. package/.next/standalone/docs/agent-fs-plan.md +0 -232
  266. package/.next/standalone/docs/editorial-skin-spec.md +0 -242
  267. package/.next/standalone/docs/file-vs-collab-authority.md +0 -124
  268. package/.next/standalone/docs/mobile-readiness-spec.md +0 -229
  269. package/.next/standalone/docs/workspacing-plan.md +0 -364
  270. package/.next/standalone/packages/wiki-viewer-mcp/README.md +0 -195
  271. package/.next/standalone/packages/wiki-viewer-mcp/bench-e2e.mjs +0 -166
  272. package/.next/standalone/packages/wiki-viewer-mcp/bench-latency.mjs +0 -87
  273. package/.next/standalone/packages/wiki-viewer-mcp/bench-payload.mjs +0 -45
  274. package/.next/standalone/packages/wiki-viewer-mcp/dist/http-client.js +0 -331
  275. package/.next/standalone/packages/wiki-viewer-mcp/dist/index.js +0 -531
  276. package/.next/standalone/packages/wiki-viewer-mcp/dist/register.js +0 -94
  277. package/.next/standalone/packages/wiki-viewer-mcp/dist/state-cache.js +0 -28
  278. package/.next/standalone/packages/wiki-viewer-mcp/package-lock.json +0 -1716
  279. package/.next/standalone/packages/wiki-viewer-mcp/package.json +0 -41
  280. package/.next/standalone/packages/wiki-viewer-mcp/src/http-client.ts +0 -459
  281. package/.next/standalone/packages/wiki-viewer-mcp/src/index.ts +0 -638
  282. package/.next/standalone/packages/wiki-viewer-mcp/src/register.ts +0 -144
  283. package/.next/standalone/packages/wiki-viewer-mcp/src/state-cache.ts +0 -55
  284. package/.next/standalone/packages/wiki-viewer-mcp/tsconfig.json +0 -19
  285. package/.next/standalone/pnpm-lock.yaml +0 -6697
  286. package/.next/standalone/pnpm-workspace.yaml +0 -8
  287. package/.next/standalone/postcss.config.mjs +0 -2
  288. package/.next/standalone/src/app/api/agent/activity/route.ts +0 -59
  289. package/.next/standalone/src/app/api/agent/admin/agents/[agentId]/revoke/route.ts +0 -40
  290. package/.next/standalone/src/app/api/agent/admin/agents/route.ts +0 -33
  291. package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/approve/route.ts +0 -83
  292. package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/deny/route.ts +0 -36
  293. package/.next/standalone/src/app/api/agent/admin/registrations/route.ts +0 -27
  294. package/.next/standalone/src/app/api/agent/events/[...path]/route.ts +0 -148
  295. package/.next/standalone/src/app/api/agent/files/[...path]/route.ts +0 -219
  296. package/.next/standalone/src/app/api/agent/fs/file/[...path]/route.ts +0 -446
  297. package/.next/standalone/src/app/api/agent/fs/ls/[[...path]]/route.ts +0 -152
  298. package/.next/standalone/src/app/api/agent/fs/move/route.ts +0 -130
  299. package/.next/standalone/src/app/api/agent/fs/search/route.ts +0 -272
  300. package/.next/standalone/src/app/api/agent/internal/span/route.ts +0 -121
  301. package/.next/standalone/src/app/api/agent/register/[regId]/route.ts +0 -50
  302. package/.next/standalone/src/app/api/agent/register/route.ts +0 -118
  303. package/.next/standalone/src/app/api/agent/settings/route.ts +0 -34
  304. package/.next/standalone/src/app/api/agent/settings/token/regenerate/route.ts +0 -20
  305. package/.next/standalone/src/app/api/agent/sidecar/[...path]/route.ts +0 -53
  306. package/.next/standalone/src/app/api/agents/install/route.ts +0 -143
  307. package/.next/standalone/src/app/api/agents/skill/route.ts +0 -20
  308. package/.next/standalone/src/app/api/agents/skill.tar.gz/route.ts +0 -87
  309. package/.next/standalone/src/app/api/app-proxy/[...path]/route.ts +0 -247
  310. package/.next/standalone/src/app/api/assets/[...path]/route.ts +0 -75
  311. package/.next/standalone/src/app/api/auth/[...all]/route.ts +0 -7
  312. package/.next/standalone/src/app/api/owner/init/route.ts +0 -14
  313. package/.next/standalone/src/app/api/share/[token]/route.ts +0 -269
  314. package/.next/standalone/src/app/api/share/route.ts +0 -127
  315. package/.next/standalone/src/app/api/system/admins/route.ts +0 -103
  316. package/.next/standalone/src/app/api/system/auth-config/route.ts +0 -22
  317. package/.next/standalone/src/app/api/system/auth-settings/route.ts +0 -86
  318. package/.next/standalone/src/app/api/system/browse/route.ts +0 -82
  319. package/.next/standalone/src/app/api/system/clear-root/route.ts +0 -16
  320. package/.next/standalone/src/app/api/system/config/route.ts +0 -14
  321. package/.next/standalone/src/app/api/system/pins/route.ts +0 -38
  322. package/.next/standalone/src/app/api/system/reveal/route.ts +0 -36
  323. package/.next/standalone/src/app/api/system/root-status/route.ts +0 -13
  324. package/.next/standalone/src/app/api/system/set-root/route.ts +0 -53
  325. package/.next/standalone/src/app/api/system/users/route.ts +0 -120
  326. package/.next/standalone/src/app/api/system/workspaces/[id]/open/route.ts +0 -34
  327. package/.next/standalone/src/app/api/system/workspaces/[id]/refresh/route.ts +0 -32
  328. package/.next/standalone/src/app/api/system/workspaces/[id]/route.ts +0 -78
  329. package/.next/standalone/src/app/api/system/workspaces/route.ts +0 -128
  330. package/.next/standalone/src/app/api/upload/[...path]/route.ts +0 -85
  331. package/.next/standalone/src/app/api/wiki/app/route.ts +0 -60
  332. package/.next/standalone/src/app/api/wiki/backlinks/route.ts +0 -29
  333. package/.next/standalone/src/app/api/wiki/content/route.ts +0 -159
  334. package/.next/standalone/src/app/api/wiki/download/route.ts +0 -100
  335. package/.next/standalone/src/app/api/wiki/file/route.ts +0 -57
  336. package/.next/standalone/src/app/api/wiki/folder/route.ts +0 -34
  337. package/.next/standalone/src/app/api/wiki/git-branches/route.ts +0 -32
  338. package/.next/standalone/src/app/api/wiki/git-checkout/route.ts +0 -45
  339. package/.next/standalone/src/app/api/wiki/git-diff/route.ts +0 -33
  340. package/.next/standalone/src/app/api/wiki/git-file-info/route.ts +0 -27
  341. package/.next/standalone/src/app/api/wiki/git-history/route.ts +0 -27
  342. package/.next/standalone/src/app/api/wiki/git-pull/route.ts +0 -48
  343. package/.next/standalone/src/app/api/wiki/move/route.ts +0 -61
  344. package/.next/standalone/src/app/api/wiki/new-file/route.ts +0 -56
  345. package/.next/standalone/src/app/api/wiki/outlinks/route.ts +0 -76
  346. package/.next/standalone/src/app/api/wiki/page/route.ts +0 -92
  347. package/.next/standalone/src/app/api/wiki/presence/route.ts +0 -56
  348. package/.next/standalone/src/app/api/wiki/route.ts +0 -116
  349. package/.next/standalone/src/app/api/wiki/search/route.ts +0 -44
  350. package/.next/standalone/src/app/api/wiki/slugs/route.ts +0 -54
  351. package/.next/standalone/src/app/api/wiki/upload/route.ts +0 -95
  352. package/.next/standalone/src/app/api/wiki/watch/route.ts +0 -67
  353. package/.next/standalone/src/app/globals.css +0 -1249
  354. package/.next/standalone/src/app/icon.svg +0 -11
  355. package/.next/standalone/src/app/layout.tsx +0 -78
  356. package/.next/standalone/src/app/manifest.ts +0 -30
  357. package/.next/standalone/src/app/page.tsx +0 -3260
  358. package/.next/standalone/src/app/s/[token]/page.tsx +0 -357
  359. package/.next/standalone/src/app/signin/page.tsx +0 -16
  360. package/.next/standalone/src/app/signin/signin-form.tsx +0 -238
  361. package/.next/standalone/src/components/ai-panel/activity-row.tsx +0 -284
  362. package/.next/standalone/src/components/ai-panel/ai-panel.tsx +0 -311
  363. package/.next/standalone/src/components/ai-panel/token-section.tsx +0 -313
  364. package/.next/standalone/src/components/auth-settings-sheet.tsx +0 -474
  365. package/.next/standalone/src/components/confirm-dialog.tsx +0 -56
  366. package/.next/standalone/src/components/dir-picker.tsx +0 -606
  367. package/.next/standalone/src/components/editor/backlinks-panel.tsx +0 -139
  368. package/.next/standalone/src/components/editor/bubble-menu.tsx +0 -346
  369. package/.next/standalone/src/components/editor/callout-extension.ts +0 -36
  370. package/.next/standalone/src/components/editor/comment-pip.tsx +0 -56
  371. package/.next/standalone/src/components/editor/comment-thread.tsx +0 -254
  372. package/.next/standalone/src/components/editor/csv-viewer.tsx +0 -379
  373. package/.next/standalone/src/components/editor/document-outline.tsx +0 -230
  374. package/.next/standalone/src/components/editor/editor-toolbar.tsx +0 -564
  375. package/.next/standalone/src/components/editor/editor.tsx +0 -1327
  376. package/.next/standalone/src/components/editor/extensions/drag-handle.ts +0 -325
  377. package/.next/standalone/src/components/editor/extensions/heading-anchors.ts +0 -55
  378. package/.next/standalone/src/components/editor/extensions/proof-span.ts +0 -60
  379. package/.next/standalone/src/components/editor/extensions/resizable-image.tsx +0 -159
  380. package/.next/standalone/src/components/editor/extensions.ts +0 -150
  381. package/.next/standalone/src/components/editor/file-fallback-viewer.tsx +0 -75
  382. package/.next/standalone/src/components/editor/folder-index.tsx +0 -236
  383. package/.next/standalone/src/components/editor/google-doc-viewer.tsx +0 -73
  384. package/.next/standalone/src/components/editor/image-viewer.tsx +0 -57
  385. package/.next/standalone/src/components/editor/large-file-gate.tsx +0 -108
  386. package/.next/standalone/src/components/editor/link-popover.tsx +0 -111
  387. package/.next/standalone/src/components/editor/media-popover.tsx +0 -212
  388. package/.next/standalone/src/components/editor/media-viewer.tsx +0 -70
  389. package/.next/standalone/src/components/editor/mermaid-viewer.tsx +0 -286
  390. package/.next/standalone/src/components/editor/node-app-viewer.tsx +0 -328
  391. package/.next/standalone/src/components/editor/notebook-viewer.tsx +0 -371
  392. package/.next/standalone/src/components/editor/office/docx-viewer.tsx +0 -93
  393. package/.next/standalone/src/components/editor/office/office-chrome.tsx +0 -83
  394. package/.next/standalone/src/components/editor/office/pptx-viewer.tsx +0 -104
  395. package/.next/standalone/src/components/editor/office/xlsx-viewer.tsx +0 -192
  396. package/.next/standalone/src/components/editor/pdf-viewer.tsx +0 -32
  397. package/.next/standalone/src/components/editor/proof-span-popover.tsx +0 -162
  398. package/.next/standalone/src/components/editor/slash-commands.tsx +0 -629
  399. package/.next/standalone/src/components/editor/source-viewer.tsx +0 -289
  400. package/.next/standalone/src/components/editor/suggest-edit-popover.tsx +0 -229
  401. package/.next/standalone/src/components/editor/suggestion-card.tsx +0 -206
  402. package/.next/standalone/src/components/editor/table-menu.tsx +0 -232
  403. package/.next/standalone/src/components/editor/view-mode-comment-button.tsx +0 -77
  404. package/.next/standalone/src/components/editor/website-viewer.tsx +0 -75
  405. package/.next/standalone/src/components/editor/wiki-link-create-dialog.tsx +0 -189
  406. package/.next/standalone/src/components/editor/wiki-link-decorator.ts +0 -79
  407. package/.next/standalone/src/components/editor/wiki-link-extension.ts +0 -120
  408. package/.next/standalone/src/components/editor/wiki-link-picker.tsx +0 -250
  409. package/.next/standalone/src/components/layout/viewer-toolbar.tsx +0 -54
  410. package/.next/standalone/src/components/search/search-command-dialog.tsx +0 -246
  411. package/.next/standalone/src/components/search/sidebar-search-box.tsx +0 -121
  412. package/.next/standalone/src/components/search/snippet-text.tsx +0 -48
  413. package/.next/standalone/src/components/share-dialog.tsx +0 -307
  414. package/.next/standalone/src/components/theme-provider.tsx +0 -16
  415. package/.next/standalone/src/components/theme-toggle.tsx +0 -24
  416. package/.next/standalone/src/components/ui/badge.tsx +0 -35
  417. package/.next/standalone/src/components/ui/button.tsx +0 -56
  418. package/.next/standalone/src/components/ui/card.tsx +0 -63
  419. package/.next/standalone/src/components/ui/collapsible.tsx +0 -9
  420. package/.next/standalone/src/components/ui/command.tsx +0 -151
  421. package/.next/standalone/src/components/ui/context-menu.tsx +0 -191
  422. package/.next/standalone/src/components/ui/dialog.tsx +0 -119
  423. package/.next/standalone/src/components/ui/dropdown-menu.tsx +0 -194
  424. package/.next/standalone/src/components/ui/input.tsx +0 -22
  425. package/.next/standalone/src/components/ui/label.tsx +0 -25
  426. package/.next/standalone/src/components/ui/popover.tsx +0 -33
  427. package/.next/standalone/src/components/ui/scroll-area.tsx +0 -47
  428. package/.next/standalone/src/components/ui/select.tsx +0 -156
  429. package/.next/standalone/src/components/ui/separator.tsx +0 -30
  430. package/.next/standalone/src/components/ui/sheet.tsx +0 -140
  431. package/.next/standalone/src/components/ui/skeleton.tsx +0 -15
  432. package/.next/standalone/src/components/ui/switch.tsx +0 -28
  433. package/.next/standalone/src/components/ui/tabs.tsx +0 -54
  434. package/.next/standalone/src/components/ui/textarea.tsx +0 -21
  435. package/.next/standalone/src/components/ui/tip.tsx +0 -23
  436. package/.next/standalone/src/components/ui/toggle-group.tsx +0 -41
  437. package/.next/standalone/src/components/ui/tooltip.tsx +0 -27
  438. package/.next/standalone/src/components/view-width-toggle.tsx +0 -70
  439. package/.next/standalone/src/components/wiki/frontmatter-header.tsx +0 -164
  440. package/.next/standalone/src/hooks/use-is-mobile.ts +0 -14
  441. package/.next/standalone/src/lib/app-runner.ts +0 -272
  442. package/.next/standalone/src/lib/auth/admin.ts +0 -120
  443. package/.next/standalone/src/lib/auth/allowlist.ts +0 -50
  444. package/.next/standalone/src/lib/auth/client.ts +0 -4
  445. package/.next/standalone/src/lib/auth/csrf.ts +0 -68
  446. package/.next/standalone/src/lib/auth/server.ts +0 -215
  447. package/.next/standalone/src/lib/cabinets/tree.ts +0 -75
  448. package/.next/standalone/src/lib/config.ts +0 -114
  449. package/.next/standalone/src/lib/embeds/detect.ts +0 -142
  450. package/.next/standalone/src/lib/git-secrets.ts +0 -65
  451. package/.next/standalone/src/lib/git.ts +0 -503
  452. package/.next/standalone/src/lib/google/detect.ts +0 -107
  453. package/.next/standalone/src/lib/markdown/parse-frontmatter.ts +0 -104
  454. package/.next/standalone/src/lib/markdown/remark-wikilinks.ts +0 -100
  455. package/.next/standalone/src/lib/markdown/sanitize-schema.ts +0 -106
  456. package/.next/standalone/src/lib/markdown/to-html.ts +0 -282
  457. package/.next/standalone/src/lib/markdown/to-markdown.ts +0 -252
  458. package/.next/standalone/src/lib/proof/activity-shared.ts +0 -31
  459. package/.next/standalone/src/lib/proof/activity.ts +0 -74
  460. package/.next/standalone/src/lib/proof/audit.ts +0 -88
  461. package/.next/standalone/src/lib/proof/auth.ts +0 -143
  462. package/.next/standalone/src/lib/proof/block-refs.ts +0 -133
  463. package/.next/standalone/src/lib/proof/blocks.ts +0 -73
  464. package/.next/standalone/src/lib/proof/client-auth.ts +0 -23
  465. package/.next/standalone/src/lib/proof/collab-state.ts +0 -81
  466. package/.next/standalone/src/lib/proof/event-bus.ts +0 -47
  467. package/.next/standalone/src/lib/proof/file-lock.ts +0 -43
  468. package/.next/standalone/src/lib/proof/glob.ts +0 -51
  469. package/.next/standalone/src/lib/proof/idempotency.ts +0 -37
  470. package/.next/standalone/src/lib/proof/lease.ts +0 -117
  471. package/.next/standalone/src/lib/proof/mutex.ts +0 -36
  472. package/.next/standalone/src/lib/proof/ops-applier.ts +0 -727
  473. package/.next/standalone/src/lib/proof/owner-auth.ts +0 -2
  474. package/.next/standalone/src/lib/proof/pending.ts +0 -97
  475. package/.next/standalone/src/lib/proof/proof-span.ts +0 -141
  476. package/.next/standalone/src/lib/proof/rate-limit.ts +0 -53
  477. package/.next/standalone/src/lib/proof/raw-fs.ts +0 -197
  478. package/.next/standalone/src/lib/proof/register-rate-limit.ts +0 -53
  479. package/.next/standalone/src/lib/proof/registry.ts +0 -195
  480. package/.next/standalone/src/lib/proof/sidecar.ts +0 -103
  481. package/.next/standalone/src/lib/proof/suggest-capture.ts +0 -69
  482. package/.next/standalone/src/lib/proof/types.ts +0 -182
  483. package/.next/standalone/src/lib/proof-config.ts +0 -14
  484. package/.next/standalone/src/lib/root-dir.ts +0 -35
  485. package/.next/standalone/src/lib/search/indexable-exts.ts +0 -25
  486. package/.next/standalone/src/lib/search/indexer.ts +0 -634
  487. package/.next/standalone/src/lib/search/sanitize.ts +0 -35
  488. package/.next/standalone/src/lib/search/search-db.ts +0 -57
  489. package/.next/standalone/src/lib/search/watcher-pool.ts +0 -79
  490. package/.next/standalone/src/lib/shared-docs/db.ts +0 -177
  491. package/.next/standalone/src/lib/toast.ts +0 -9
  492. package/.next/standalone/src/lib/utils.ts +0 -6
  493. package/.next/standalone/src/lib/wiki-helpers.ts +0 -36
  494. package/.next/standalone/src/lib/workspace-client.ts +0 -82
  495. package/.next/standalone/src/lib/workspace-context.ts +0 -173
  496. package/.next/standalone/src/lib/workspaces.ts +0 -400
  497. package/.next/standalone/src/middleware.ts +0 -44
  498. package/.next/standalone/src/stores/ai-panel-store.ts +0 -79
  499. package/.next/standalone/src/stores/editor-store.ts +0 -439
  500. package/.next/standalone/src/stores/pin-store.ts +0 -56
  501. package/.next/standalone/src/stores/proof-store.ts +0 -124
  502. package/.next/standalone/src/stores/recent-store.ts +0 -51
  503. package/.next/standalone/src/stores/search-store.ts +0 -104
  504. package/.next/standalone/src/stores/show-hidden-store.ts +0 -26
  505. package/.next/standalone/src/stores/sidebar-width-store.ts +0 -39
  506. package/.next/standalone/src/stores/skin-store.ts +0 -42
  507. package/.next/standalone/src/stores/tree-store.ts +0 -52
  508. package/.next/standalone/src/stores/view-width-store.ts +0 -101
  509. package/.next/standalone/src/stores/wiki-slugs-store.ts +0 -75
  510. package/.next/standalone/src/tests/proof/activity-aggregator.test.ts +0 -146
  511. package/.next/standalone/src/tests/proof/admin-create-user.test.ts +0 -43
  512. package/.next/standalone/src/tests/proof/agent-fs.test.ts +0 -973
  513. package/.next/standalone/src/tests/proof/agent-ownership.test.ts +0 -140
  514. package/.next/standalone/src/tests/proof/agent-workspace-scope.test.ts +0 -134
  515. package/.next/standalone/src/tests/proof/agents-install.test.ts +0 -110
  516. package/.next/standalone/src/tests/proof/bench-write.ts +0 -179
  517. package/.next/standalone/src/tests/proof/better-auth.test.ts +0 -68
  518. package/.next/standalone/src/tests/proof/block-refs.test.ts +0 -108
  519. package/.next/standalone/src/tests/proof/blocks.test.ts +0 -92
  520. package/.next/standalone/src/tests/proof/collab-state.test.ts +0 -501
  521. package/.next/standalone/src/tests/proof/comments-ops.test.ts +0 -177
  522. package/.next/standalone/src/tests/proof/editor-roundtrip.test.ts +0 -85
  523. package/.next/standalone/src/tests/proof/external-edit.test.ts +0 -58
  524. package/.next/standalone/src/tests/proof/file-lock.test.ts +0 -80
  525. package/.next/standalone/src/tests/proof/git-branches-checkout.test.ts +0 -136
  526. package/.next/standalone/src/tests/proof/git-read-features.test.ts +0 -473
  527. package/.next/standalone/src/tests/proof/git-secrets.test.ts +0 -138
  528. package/.next/standalone/src/tests/proof/git-sparse.test.ts +0 -257
  529. package/.next/standalone/src/tests/proof/git-workspace.test.ts +0 -135
  530. package/.next/standalone/src/tests/proof/git.test.ts +0 -243
  531. package/.next/standalone/src/tests/proof/glob.test.ts +0 -82
  532. package/.next/standalone/src/tests/proof/helpers/auth-session.ts +0 -27
  533. package/.next/standalone/src/tests/proof/markdown-to-html.test.ts +0 -46
  534. package/.next/standalone/src/tests/proof/ops-applier.test.ts +0 -368
  535. package/.next/standalone/src/tests/proof/owner-init.test.ts +0 -2
  536. package/.next/standalone/src/tests/proof/parse-frontmatter.test.ts +0 -60
  537. package/.next/standalone/src/tests/proof/preload.ts +0 -21
  538. package/.next/standalone/src/tests/proof/proof-span.test.ts +0 -98
  539. package/.next/standalone/src/tests/proof/rate-limit.test.ts +0 -54
  540. package/.next/standalone/src/tests/proof/readonly-enforcement.test.ts +0 -161
  541. package/.next/standalone/src/tests/proof/reconcile-sidecar.test.ts +0 -195
  542. package/.next/standalone/src/tests/proof/registration-flow.test.ts +0 -330
  543. package/.next/standalone/src/tests/proof/registry.test.ts +0 -169
  544. package/.next/standalone/src/tests/proof/routes.test.ts +0 -516
  545. package/.next/standalone/src/tests/proof/search-index.test.ts +0 -376
  546. package/.next/standalone/src/tests/proof/sidecar-lifecycle.test.ts +0 -177
  547. package/.next/standalone/src/tests/proof/sidecar-trim.test.ts +0 -58
  548. package/.next/standalone/src/tests/proof/suggestion-ops.test.ts +0 -280
  549. package/.next/standalone/src/tests/proof/wiki-content-put.test.ts +0 -140
  550. package/.next/standalone/src/tests/proof/wiki-routes-auth.test.ts +0 -208
  551. package/.next/standalone/src/tests/proof/workspace-isolation.test.ts +0 -87
  552. package/.next/standalone/src/tests/proof/workspaces-api.test.ts +0 -227
  553. package/.next/standalone/src/types/index.ts +0 -47
  554. package/.next/standalone/tailwind.config.ts +0 -191
  555. package/.next/standalone/tsconfig.json +0 -41
  556. package/.next/standalone/tsconfig.tsbuildinfo +0 -1
  557. /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → ecpoWt_SP3WkUTh0NsnuT}/_buildManifest.js +0 -0
  558. /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → ecpoWt_SP3WkUTh0NsnuT}/_clientMiddlewareManifest.js +0 -0
  559. /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → ecpoWt_SP3WkUTh0NsnuT}/_ssgManifest.js +0 -0
@@ -1,1327 +0,0 @@
1
- "use client";
2
-
3
- import { cellAround, isInTable } from "@tiptap/pm/tables";
4
- import { EditorContent, useEditor } from "@tiptap/react";
5
- import { AlertCircle, Check, Code2, FilePlus, Loader2, Sparkles } from "lucide-react";
6
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
- import { findNodeByPath } from "@/lib/cabinets/tree";
8
- import { markdownToHtml } from "@/lib/markdown/to-html";
9
- import { htmlToMarkdown } from "@/lib/markdown/to-markdown";
10
- import { parseFrontmatter } from "@/lib/markdown/parse-frontmatter";
11
- import { useAIPanelStore } from "@/stores/ai-panel-store";
12
- import { useEditorStore } from "@/stores/editor-store";
13
- import { useTreeStore } from "@/stores/tree-store";
14
- import {
15
- useViewWidthStore,
16
- VIEW_WIDTH_CSS,
17
- VIEW_ALIGN_ML,
18
- } from "@/stores/view-width-store";
19
- import { useWikiSlugsStore } from "@/stores/wiki-slugs-store";
20
- import type { TreeNode } from "@/types";
21
- import { useProofStore } from "@/stores/proof-store";
22
- import { captureSuggestion } from "@/lib/proof/suggest-capture";
23
- import { wsFetch, withWs } from "@/lib/workspace-client";
24
- import { showError } from "@/lib/toast";
25
- import { EditorBubbleMenu } from "./bubble-menu";
26
- import { EditorToolbar } from "./editor-toolbar";
27
- import { editorExtensions } from "./extensions";
28
- import { FolderIndex } from "./folder-index";
29
- import { CommentPip } from "./comment-pip";
30
- import { CommentThread } from "./comment-thread";
31
- import { ProofSpanPopover } from "./proof-span-popover";
32
- import { SuggestionCard } from "./suggestion-card";
33
- import { SuggestEditPopover } from "./suggest-edit-popover";
34
- import { SlashCommands } from "./slash-commands";
35
- import { DocumentOutline } from "./document-outline";
36
- import { BacklinksPanel } from "./backlinks-panel";
37
- import { TableMenu } from "./table-menu";
38
- import {
39
- useWikiLinkCreate,
40
- type WikiCreateResult,
41
- } from "./wiki-link-create-dialog";
42
- import { WikiLinkPicker } from "./wiki-link-picker";
43
- import { FrontmatterHeader } from "@/components/wiki/frontmatter-header";
44
- import { ViewModeCommentButton } from "./view-mode-comment-button";
45
-
46
- async function uploadFile(
47
- pagePath: string,
48
- file: File,
49
- ): Promise<string | null> {
50
- const formData = new FormData();
51
- formData.append("file", file);
52
- try {
53
- const res = await wsFetch(`/api/upload/${pagePath}`, {
54
- method: "POST",
55
- body: formData,
56
- });
57
- if (!res.ok) {
58
- showError(`Upload failed: ${file.name}`);
59
- return null;
60
- }
61
- const data = await res.json();
62
- return data.url;
63
- } catch {
64
- showError(`Upload failed: ${file.name}`);
65
- return null;
66
- }
67
- }
68
-
69
- function flattenTree(nodes: TreeNode[]): { path: string; name: string }[] {
70
- const result: { path: string; name: string }[] = [];
71
- for (const node of nodes) {
72
- result.push({ path: node.path, name: node.name });
73
- if (node.children) result.push(...flattenTree(node.children));
74
- }
75
- return result;
76
- }
77
-
78
- function findPageBySlug(
79
- slug: string,
80
- currentPath: string | null,
81
- nodes: TreeNode[],
82
- ): string | null {
83
- const allPages = flattenTree(nodes);
84
- // The slug matches the last segment of the path
85
- const matches = allPages.filter(
86
- (p) => p.name === slug || p.path.endsWith(`/${slug}`),
87
- );
88
- if (matches.length === 0) return null;
89
- if (matches.length === 1) return matches[0].path;
90
-
91
- // Prefer sibling pages (same parent directory as current page)
92
- if (currentPath) {
93
- const parentDir = currentPath.includes("/")
94
- ? currentPath.substring(0, currentPath.lastIndexOf("/"))
95
- : "";
96
- const sibling = matches.find(
97
- (m) => m.path === (parentDir ? `${parentDir}/${slug}` : slug),
98
- );
99
- if (sibling) return sibling.path;
100
- }
101
- return matches[0].path;
102
- }
103
-
104
- function navigateToPage(
105
- targetPath: string,
106
- selectPage: (path: string) => void,
107
- expandPath: (path: string) => void,
108
- ) {
109
- const parts = targetPath.split("/");
110
- for (let i = 1; i < parts.length; i++) {
111
- expandPath(parts.slice(0, i).join("/"));
112
- }
113
- selectPage(targetPath);
114
- useEditorStore.getState().loadPage(targetPath);
115
- // Scroll editor container to top
116
- setTimeout(() => {
117
- document.querySelector("[data-editor-scroll]")?.scrollTo(0, 0);
118
- }, 0);
119
- }
120
-
121
- function resolveInternalLink(
122
- href: string,
123
- currentPath: string | null,
124
- nodes: TreeNode[],
125
- ): string | null {
126
- const allPages = flattenTree(nodes);
127
-
128
- // Clean up the href: strip .md extension, leading ./ or /
129
- const linkPath = href
130
- .replace(/\.md$/, "")
131
- .replace(/^\.\//, "")
132
- .replace(/^\//, "");
133
-
134
- // 1. Try as absolute path (exact match in tree)
135
- const exactMatch = allPages.find((p) => p.path === linkPath);
136
- if (exactMatch) return exactMatch.path;
137
-
138
- // 2. Try relative to current page's directory
139
- if (currentPath) {
140
- const parentDir = currentPath.includes("/")
141
- ? currentPath.substring(0, currentPath.lastIndexOf("/"))
142
- : "";
143
- const relativePath = parentDir ? `${parentDir}/${linkPath}` : linkPath;
144
- const relMatch = allPages.find((p) => p.path === relativePath);
145
- if (relMatch) return relMatch.path;
146
- }
147
-
148
- // 3. Try matching by last segment (slug-style lookup)
149
- const slug = linkPath.includes("/") ? linkPath.split("/").pop()! : linkPath;
150
- return findPageBySlug(slug, currentPath, nodes);
151
- }
152
-
153
- type KBEditorMode = "viewing" | "editing" | "suggesting";
154
-
155
- interface KBEditorProps {
156
- mode?: KBEditorMode;
157
- }
158
-
159
- export function KBEditor({ mode }: KBEditorProps = {}) {
160
- const {
161
- currentPath,
162
- content,
163
- saveStatus,
164
- frontmatter,
165
- isLoading,
166
- loadStatus,
167
- createMissingPage,
168
- editMode,
169
- setEditMode,
170
- } = useEditorStore();
171
- const effectiveMode = mode ?? editMode;
172
- const isViewing = effectiveMode === "viewing";
173
- const isSuggesting = effectiveMode === "suggesting";
174
- const parsedViewingContent = useMemo(
175
- () => (isViewing ? parseFrontmatter(content) : { data: {}, body: content }),
176
- [content, isViewing],
177
- );
178
- const nodes = useTreeStore((s) => s.nodes);
179
- const editorMaxW = useViewWidthStore((s) => VIEW_WIDTH_CSS[s.width]);
180
- const editorMl = useViewWidthStore((s) => VIEW_ALIGN_ML[s.align]);
181
- const isRtl = isViewing
182
- ? parsedViewingContent.data.dir === "rtl"
183
- : frontmatter?.dir === "rtl";
184
- const { open: openAI, clearMessages } = useAIPanelStore();
185
- const { open: openWikiCreate, Dialog: WikiCreateDialog } =
186
- useWikiLinkCreate();
187
- // Keep a stable ref so the click handler closure can call the latest version
188
- // without being re-created on every render.
189
- const openWikiCreateRef =
190
- useRef<(slug: string) => Promise<WikiCreateResult>>(openWikiCreate);
191
- openWikiCreateRef.current = openWikiCreate;
192
-
193
- const isLoadingRef = useRef(false);
194
- const isViewingRef = useRef(isViewing);
195
- isViewingRef.current = isViewing;
196
- const [sourceMode, setSourceMode] = useState(false);
197
- const [sourceText, setSourceText] = useState("");
198
- // Reset the tab to "page" whenever the path changes — opening a new folder
199
- // shouldn't skip its index.md if the previous folder was on Files. Has to
200
- // be an effect (not state-during-render) because Tiptap's EditorContent
201
- // calls flushSync internally; setState during the parent render explodes
202
- // when EditorContent renders in the same pass.
203
- const [folderTab, setFolderTab] = useState<"page" | "files">("page");
204
- useEffect(() => {
205
- setFolderTab("page");
206
- }, []);
207
-
208
- // Prime the slug index once on mount so wiki-link broken-state and
209
- // the autocomplete picker both have data immediately.
210
- useEffect(() => {
211
- void useWikiSlugsStore.getState().load();
212
- }, []);
213
-
214
- // Load sidecar when the current path changes. Debounced: rapid navigation
215
- // otherwise fires a sidecar fetch per pass-through file, flooding the
216
- // connection pool. The cleanup clears the timer, so only the settled path loads.
217
- useEffect(() => {
218
- if (!currentPath) return;
219
- const id = setTimeout(() => {
220
- void useProofStore.getState().loadSidecar(currentPath);
221
- }, 200);
222
- return () => clearTimeout(id);
223
- }, [currentPath]);
224
-
225
- // Human edit-lease heartbeat: tell the server a human has this markdown doc
226
- // open so computeCollabState() reports "active" even before the first
227
- // suggestion/comment. This is what makes raw-fs writes from agents defer to
228
- // the collaborative (Tier-2) path while a human is editing. Only markdown
229
- // files participate in the collab-state machine.
230
- useEffect(() => {
231
- if (typeof window === "undefined") return;
232
- if (!currentPath) return;
233
- if (isViewing) return;
234
- if (!/\.(md|markdown)$/i.test(currentPath)) return;
235
-
236
- const path = currentPath;
237
- const ping = (action: "open" | "heartbeat" | "close") => {
238
- // keepalive lets the "close" beacon survive page unload.
239
- void wsFetch("/api/wiki/presence", {
240
- method: "POST",
241
- headers: { "Content-Type": "application/json" },
242
- body: JSON.stringify({ path, action }),
243
- keepalive: action === "close",
244
- }).catch(() => {
245
- /* presence is best-effort; ignore failures */
246
- });
247
- };
248
-
249
- // Debounce the "open" ping: rapid navigation through files otherwise emits
250
- // an open+close pair per pass-through. Only claim the lease once the user
251
- // settles on a file for >200ms; if we never opened, we never close.
252
- let opened = false;
253
- let id: ReturnType<typeof setInterval> | null = null;
254
- const onHidden = () => {
255
- if (opened && document.visibilityState === "hidden") ping("heartbeat");
256
- };
257
- const openTimer = setTimeout(() => {
258
- opened = true;
259
- ping("open");
260
- // Refresh well within the 90s server lease TTL.
261
- id = setInterval(() => ping("heartbeat"), 30_000);
262
- document.addEventListener("visibilitychange", onHidden);
263
- }, 200);
264
-
265
- return () => {
266
- clearTimeout(openTimer);
267
- if (id) clearInterval(id);
268
- document.removeEventListener("visibilitychange", onHidden);
269
- if (opened) ping("close");
270
- };
271
- }, [currentPath, isViewing]);
272
-
273
- // Subscribe to chokidar SSE: when current file changes on disk, reload sidecar.
274
- useEffect(() => {
275
- if (typeof window === "undefined") return;
276
- const es = new EventSource(withWs("/api/wiki/watch"));
277
- es.onmessage = (evt: MessageEvent<string>) => {
278
- try {
279
- const data = JSON.parse(evt.data) as { type: string; path: string };
280
- const activePath = useEditorStore.getState().currentPath;
281
- if (
282
- (data.type === "change" || data.type === "add") &&
283
- activePath &&
284
- data.path === activePath
285
- ) {
286
- // loadSnapshot first so server-side readSnapshot detects
287
- // fingerprint mismatch, emits file.externallyEdited, and persists
288
- // the sidecar. Then loadSidecar to refresh comments/suggestions.
289
- void useProofStore
290
- .getState()
291
- .loadSnapshot(activePath)
292
- .then(() => useProofStore.getState().loadSidecar(activePath));
293
- if (isViewingRef.current) {
294
- void useEditorStore.getState().loadPage(activePath);
295
- }
296
- }
297
- } catch {
298
- // ignore malformed events
299
- }
300
- };
301
- return () => {
302
- es.close();
303
- };
304
- }, []);
305
-
306
- // Proof-span popover state.
307
- const [proofTarget, setProofTarget] = useState<HTMLElement | null>(null);
308
-
309
- /**
310
- * Ref to the editor scroll container. Used to compute block positions
311
- * relative to the scrollable area for suggestion cards and comment pips.
312
- *
313
- * Phase D coordination: comment-pip positioning uses this same ref and the
314
- * same blockRefPositions map computed below.
315
- */
316
- const scrollContainerRef = useRef<HTMLDivElement>(null);
317
-
318
- /** Map of block ref → position relative to scroll container */
319
- const [blockRefPositions, setBlockRefPositions] = useState<
320
- Map<string, { top: number; left: number; width: number; bottom: number }>
321
- >(new Map());
322
-
323
- // Subscribe to snapshot data for suggestion cards.
324
- // NOTE: select the RAW stored references here — returning a freshly built
325
- // array (e.g. `?? []` or `.filter(...)`) on every call makes
326
- // useSyncExternalStore think the snapshot changed each render, which spins
327
- // into a "Maximum update depth exceeded" loop. Derive defaults/filters below.
328
- const snapshotBlocksRaw = useProofStore((s) =>
329
- currentPath ? s.byPath[currentPath]?.snapshotBlocks : undefined
330
- );
331
- const suggestionsRaw = useProofStore((s) =>
332
- currentPath ? s.byPath[currentPath]?.sidecar?.suggestions : undefined
333
- );
334
- const snapshotRevision = useProofStore((s) =>
335
- currentPath ? (s.byPath[currentPath]?.snapshotRevision ?? 0) : 0
336
- );
337
- const commentsRaw = useProofStore((s) =>
338
- currentPath ? s.byPath[currentPath]?.sidecar?.comments : undefined
339
- );
340
-
341
- const snapshotBlocks = useMemo(() => snapshotBlocksRaw ?? [], [snapshotBlocksRaw]);
342
- const comments = useMemo(() => commentsRaw ?? [], [commentsRaw]);
343
- const pendingSuggestions = useMemo(
344
- () => suggestionsRaw?.filter((sg) => sg.status === "pending") ?? [],
345
- [suggestionsRaw],
346
- );
347
-
348
- /** Group comments by block ref for pip rendering. */
349
- const commentsByRef = useMemo(() => {
350
- const map: Record<string, typeof comments> = {};
351
- for (const c of comments) {
352
- (map[c.ref] ??= []).push(c);
353
- }
354
- return map;
355
- }, [comments]);
356
-
357
- /** Tracks which block's comment thread is open and its anchor element. */
358
- const [threadTarget, setThreadTarget] = useState<{ blockRef: string; el: HTMLElement } | null>(null);
359
-
360
- /** Tracks the open human "suggest edit" popover (block + anchor + content). */
361
- const [suggestTarget, setSuggestTarget] = useState<
362
- { blockRef: string; markdown: string; anchor: { top: number; left: number } } | null
363
- >(null);
364
-
365
- /**
366
- * Resolve the current editor selection to a top-level block.
367
- *
368
- * Primary strategy: map the selection to its top-level ProseMirror child
369
- * INDEX, then look up snapshotBlocks[index] — the same index-based mapping
370
- * used by the position-tracker effect. This is robust even when the DOM
371
- * `data-block-ref` annotation has not been applied yet (e.g. snapshot still
372
- * loading), which previously made the suggest/comment buttons silently
373
- * no-op. Falls back to walking the DOM for an existing [data-block-ref].
374
- */
375
- const resolveSelectionBlock = useCallback((): {
376
- blockRef: string;
377
- blockEl: HTMLElement;
378
- markdown: string;
379
- } | null => {
380
- if (!editorRef.current) return null;
381
- const view = editorRef.current.view;
382
- const { from } = view.state.selection;
383
- const path = useEditorStore.getState().currentPath ?? "";
384
- const blocks = useProofStore.getState().byPath[path]?.snapshotBlocks ?? [];
385
-
386
- // Find the top-level child index containing the selection head.
387
- const $pos = view.state.doc.resolve(from);
388
- const topIndex = $pos.depth > 0 ? $pos.index(0) : 0;
389
-
390
- const proseMirror = scrollContainerRef.current?.querySelector(".ProseMirror");
391
- const children = proseMirror
392
- ? (Array.from(proseMirror.children) as HTMLElement[])
393
- : [];
394
- const blockEl = children[topIndex] ?? null;
395
-
396
- // Prefer the index-aligned snapshot block; fall back to the DOM attr.
397
- const block = blocks[topIndex];
398
- let blockRef: string | null =
399
- block?.ref ?? blockEl?.getAttribute("data-block-ref") ?? null;
400
- const markdown = block?.markdown ?? "";
401
-
402
- if (!blockRef && blockEl) {
403
- // Last-resort: DOM walk from selection anchor.
404
- const domAt = view.domAtPos(from);
405
- const node: HTMLElement | null =
406
- domAt.node.nodeType === Node.ELEMENT_NODE
407
- ? (domAt.node as HTMLElement)
408
- : domAt.node.parentElement;
409
- const found = node?.closest<HTMLElement>("[data-block-ref]") ?? null;
410
- blockRef = found?.getAttribute("data-block-ref") ?? null;
411
- }
412
-
413
- if (!blockRef || !blockEl) return null;
414
- return { blockRef, blockEl, markdown };
415
- }, []);
416
-
417
- const openSuggestForSelection = useCallback(() => {
418
- const resolved = resolveSelectionBlock();
419
- if (!resolved) return;
420
- const rect = resolved.blockEl.getBoundingClientRect();
421
- setSuggestTarget({
422
- blockRef: resolved.blockRef,
423
- markdown: resolved.markdown,
424
- anchor: { top: rect.bottom + 4, left: rect.left },
425
- });
426
- }, [resolveSelectionBlock]);
427
-
428
- const openCommentForSelection = useCallback(() => {
429
- const resolved = resolveSelectionBlock();
430
- if (!resolved) return;
431
- setThreadTarget({ blockRef: resolved.blockRef, el: resolved.blockEl });
432
- }, [resolveSelectionBlock]);
433
-
434
- // ── Suggesting mode: capture human block edits as suggestions ──────────────
435
- //
436
- // In suggesting mode the editor stays editable but edits never touch the
437
- // file. On flush (leaving a block or blurring the editor) we diff each
438
- // top-level block against the snapshot, emit a human `suggestion.add` for
439
- // every changed/added/removed block, then revert the editor to the snapshot
440
- // so the pending suggestion cards render over the original content.
441
-
442
- /** Set true whenever the user edits while in suggesting mode. */
443
- const suggestDirtyRef = useRef(false);
444
- /** Guards against re-entrant flushes (capture is async). */
445
- const flushingRef = useRef(false);
446
- /** Top-level block index that currently holds the selection. */
447
- const activeBlockIndexRef = useRef<number | null>(null);
448
-
449
- const normalizeMd = (s: string): string => s.replace(/\s+$/g, "").trimStart();
450
-
451
- const flushSuggestions = useCallback(async () => {
452
- if (flushingRef.current) return;
453
- if (isViewingRef.current) return;
454
- if (useEditorStore.getState().editMode !== "suggesting") return;
455
- if (!suggestDirtyRef.current) return;
456
- const ed = editorRef.current;
457
- const path = useEditorStore.getState().currentPath;
458
- if (!ed || !path) return;
459
-
460
- const proseMirror = scrollContainerRef.current?.querySelector(".ProseMirror");
461
- if (!proseMirror) return;
462
- const children = Array.from(proseMirror.children) as HTMLElement[];
463
- const snapBlocks =
464
- useProofStore.getState().byPath[path]?.snapshotBlocks ?? [];
465
- if (snapBlocks.length === 0) return;
466
-
467
- flushingRef.current = true;
468
- suggestDirtyRef.current = false;
469
- try {
470
- const getRevision = () =>
471
- useProofStore.getState().byPath[path]?.snapshotRevision ?? 0;
472
- const refresh = async () => {
473
- await useProofStore.getState().loadSnapshot(path);
474
- await useProofStore.getState().loadSidecar(path);
475
- };
476
-
477
- const count = Math.max(children.length, snapBlocks.length);
478
- let captured = false;
479
- for (let i = 0; i < count; i++) {
480
- const el = children[i];
481
- const snap = snapBlocks[i];
482
- const curMd = el ? htmlToMarkdown(el.outerHTML).trim() : null;
483
-
484
- if (snap && curMd !== null) {
485
- if (normalizeMd(curMd) !== normalizeMd(snap.markdown)) {
486
- const ok = await captureSuggestion({
487
- path,
488
- ref: snap.ref,
489
- kind: "replace",
490
- markdown: curMd,
491
- getRevision,
492
- refresh,
493
- });
494
- captured = captured || ok;
495
- }
496
- } else if (snap && curMd === null) {
497
- const ok = await captureSuggestion({
498
- path,
499
- ref: snap.ref,
500
- kind: "delete",
501
- getRevision,
502
- refresh,
503
- });
504
- captured = captured || ok;
505
- } else if (!snap && curMd !== null && curMd.length > 0) {
506
- // New trailing block: suggest inserting after the last known block.
507
- const lastRef = snapBlocks[snapBlocks.length - 1]?.ref;
508
- if (lastRef) {
509
- const ok = await captureSuggestion({
510
- path,
511
- ref: lastRef,
512
- kind: "insertAfter",
513
- markdown: curMd,
514
- getRevision,
515
- refresh,
516
- });
517
- captured = captured || ok;
518
- }
519
- }
520
- }
521
-
522
- if (captured) {
523
- // Reload sidecar so the new pending suggestion cards appear, then
524
- // revert the editor to the snapshot (file unchanged).
525
- await refresh();
526
- const freshSnap =
527
- useProofStore.getState().byPath[path]?.snapshotBlocks ?? snapBlocks;
528
- const snapshotMarkdown = freshSnap.map((b) => b.markdown).join("\n\n");
529
- isLoadingRef.current = true;
530
- const html = await markdownToHtml(snapshotMarkdown, path);
531
- ed.commands.setContent(html);
532
- setTimeout(() => {
533
- isLoadingRef.current = false;
534
- }, 50);
535
- }
536
- } finally {
537
- flushingRef.current = false;
538
- }
539
- }, []);
540
-
541
- // Load snapshot (ordered block list) when path changes so suggestion cards
542
- // can look up block content by ref.
543
- useEffect(() => {
544
- if (!currentPath) return;
545
- const id = setTimeout(() => {
546
- void useProofStore.getState().loadSnapshot(currentPath);
547
- }, 200);
548
- return () => clearTimeout(id);
549
- }, [currentPath]);
550
-
551
- /**
552
- * After content renders, walk `.ProseMirror > *` to build ref→position map.
553
- * Matches by index: the i-th ProseMirror child = snapshotBlocks[i].
554
- *
555
- * Phase D coordination: this effect also annotates each child element with
556
- * data-block-ref for any consumer that needs CSS/query-based lookup.
557
- */
558
- useEffect(() => {
559
- if (!currentPath || snapshotBlocks.length === 0 || !scrollContainerRef.current) return;
560
- const container = scrollContainerRef.current;
561
- const proseMirror = container.querySelector(".ProseMirror");
562
- if (!proseMirror) return;
563
- const children = Array.from(proseMirror.children) as HTMLElement[];
564
- const containerRect = container.getBoundingClientRect();
565
- const next = new Map<string, { top: number; left: number; width: number; bottom: number }>();
566
- for (let i = 0; i < Math.min(children.length, snapshotBlocks.length); i++) {
567
- const el = children[i];
568
- const block = snapshotBlocks[i];
569
- // Annotate DOM element — Phase D comment-pip and other consumers read this
570
- el.setAttribute("data-block-ref", block.ref);
571
- const rect = el.getBoundingClientRect();
572
- next.set(block.ref, {
573
- top: rect.top - containerRect.top + container.scrollTop,
574
- left: rect.left - containerRect.left,
575
- width: rect.width,
576
- bottom: rect.bottom - containerRect.top + container.scrollTop,
577
- });
578
- }
579
- setBlockRefPositions(next);
580
- }, [currentPath, snapshotBlocks]);
581
-
582
- const handleUpdate = useCallback(
583
- ({ editor }: { editor: ReturnType<typeof useEditor> }) => {
584
- if (isLoadingRef.current || isViewingRef.current || !editor) return;
585
- // In suggesting mode, mark the edit dirty so the next block-change or
586
- // blur flushes it into suggestions. Still push content to the store so
587
- // the store guard (no autosave in suggesting mode) keeps it in sync.
588
- if (useEditorStore.getState().editMode === "suggesting") {
589
- suggestDirtyRef.current = true;
590
- }
591
- const html = editor.getHTML();
592
- const md = htmlToMarkdown(html);
593
- useEditorStore.getState().updateContent(md);
594
- },
595
- [],
596
- );
597
-
598
- const editor = useEditor({
599
- extensions: editorExtensions,
600
- content: "",
601
- editable: !isViewing,
602
- onUpdate: handleUpdate,
603
- onBlur: () => {
604
- void flushSuggestions();
605
- },
606
- onSelectionUpdate: ({ editor: ed }) => {
607
- if (useEditorStore.getState().editMode !== "suggesting") return;
608
- const { from } = ed.state.selection;
609
- const $pos = ed.state.doc.resolve(from);
610
- const idx = $pos.depth > 0 ? $pos.index(0) : 0;
611
- const prev = activeBlockIndexRef.current;
612
- activeBlockIndexRef.current = idx;
613
- // Moved to a different top-level block — flush edits to the prior one.
614
- if (prev !== null && prev !== idx && suggestDirtyRef.current) {
615
- void flushSuggestions();
616
- }
617
- },
618
- editorProps: {
619
- attributes: {
620
- class:
621
- "focus:outline-none min-h-[calc(100vh-12rem)] px-4 sm:px-8 py-6 max-w-[var(--editor-max-w,48rem)] ml-[var(--editor-ml,auto)] mr-auto",
622
- },
623
- handleKeyDown: (view, event) => {
624
- if (
625
- (event.metaKey || event.ctrlKey) &&
626
- event.key.toLowerCase() === "a" &&
627
- isInTable(view.state)
628
- ) {
629
- const $cell = cellAround(view.state.selection.$from);
630
- const cell = $cell?.nodeAfter;
631
- if (!$cell || !cell) return false;
632
-
633
- const from = $cell.pos + 1;
634
- const to = $cell.pos + cell.nodeSize - 1;
635
- if (
636
- view.state.selection.from === from &&
637
- view.state.selection.to === to
638
- ) {
639
- return false;
640
- }
641
-
642
- event.preventDefault();
643
- editor?.chain().focus().setTextSelection({ from, to }).run();
644
- return true;
645
- }
646
-
647
- return false;
648
- },
649
- handleClick: (_view, _pos, event) => {
650
- const target = event.target as HTMLElement;
651
- const link = target.closest("a") as HTMLAnchorElement | null;
652
- if (!link) return false;
653
-
654
- const href = link.getAttribute("href");
655
- if (!href) return false;
656
-
657
- // Wiki-links inserted by the WikiLink mark
658
- if (link.dataset.wikiLink === "true") {
659
- event.preventDefault();
660
- event.stopPropagation();
661
- const slug = link.dataset.slug ?? "";
662
- const anchor = link.dataset.anchor ?? null;
663
- if (!slug) return true;
664
-
665
- const slugStore = useWikiSlugsStore.getState();
666
- if (slugStore.has(slug)) {
667
- const dir = slugStore.getDir(slug);
668
- const pagePath =
669
- dir === null || dir === "root"
670
- ? `${slug}.md`
671
- : `${dir}/${slug}.md`;
672
- void useEditorStore.getState().loadPage(pagePath);
673
- if (anchor) {
674
- setTimeout(() => {
675
- document
676
- .querySelector(`[id="${anchor}"]`)
677
- ?.scrollIntoView({ behavior: "smooth" });
678
- }, 200);
679
- }
680
- } else if (isViewingRef.current) {
681
- return true;
682
- } else {
683
- void openWikiCreateRef.current(slug).then((result) => {
684
- if (result.ok) {
685
- const dir = result.dir ?? "entities";
686
- const pagePath = `${dir}/${result.slug}.md`;
687
- void useEditorStore.getState().loadPage(pagePath);
688
- }
689
- });
690
- }
691
- return true;
692
- }
693
-
694
- // Wiki-links: #page:slug
695
- if (href.startsWith("#page:")) {
696
- event.preventDefault();
697
- event.stopPropagation();
698
- const slug = href.replace("#page:", "");
699
- const { nodes, selectPage, expandPath } = useTreeStore.getState();
700
- const activePath = useEditorStore.getState().currentPath;
701
- const targetPath = findPageBySlug(slug, activePath, nodes);
702
- if (targetPath) {
703
- navigateToPage(targetPath, selectPage, expandPath);
704
- }
705
- return true;
706
- }
707
-
708
- // Internal links: relative paths to .md files or other KB pages
709
- // Skip external URLs and API asset links (PDFs, images)
710
- if (/^https?:\/\//.test(href) || href.startsWith("/api/")) return false;
711
- if (href.startsWith("mailto:") || href.startsWith("tel:")) return false;
712
-
713
- event.preventDefault();
714
- event.stopPropagation();
715
-
716
- const { nodes, selectPage, expandPath } = useTreeStore.getState();
717
- const activePath = useEditorStore.getState().currentPath;
718
-
719
- // Resolve the link target to a KB page path
720
- const targetPath = resolveInternalLink(href, activePath, nodes);
721
- if (targetPath) {
722
- navigateToPage(targetPath, selectPage, expandPath);
723
- }
724
- return true;
725
- },
726
- handlePaste: (_view, event) => {
727
- if (isViewingRef.current) return false;
728
- const files = event.clipboardData?.files;
729
- const pagePath = useEditorStore.getState().currentPath;
730
-
731
- // 1. File paste → upload then insert appropriate node
732
- if (files && files.length > 0 && pagePath) {
733
- for (const file of Array.from(files)) {
734
- uploadFile(pagePath, file).then((url) => {
735
- if (!url || !editor) return;
736
- if (file.type.startsWith("image/")) {
737
- editor
738
- .chain()
739
- .focus()
740
- .setImage({ src: url, alt: file.name })
741
- .run();
742
- } else {
743
- editor
744
- .chain()
745
- .focus()
746
- .insertContent(`<a href="${url}">${file.name}</a>`)
747
- .run();
748
- }
749
- });
750
- }
751
- return true;
752
- }
753
-
754
- return false;
755
- },
756
- handleDrop: (_view, event) => {
757
- if (isViewingRef.current) return false;
758
- const files = event.dataTransfer?.files;
759
- if (!files || files.length === 0) return false;
760
-
761
- const pagePath = useEditorStore.getState().currentPath;
762
- if (!pagePath) return false;
763
-
764
- event.preventDefault();
765
- for (const file of Array.from(files)) {
766
- uploadFile(pagePath, file).then((url) => {
767
- if (!url || !editor) return;
768
- if (file.type.startsWith("image/")) {
769
- editor
770
- .chain()
771
- .focus()
772
- .setImage({ src: url, alt: file.name })
773
- .run();
774
- } else {
775
- editor
776
- .chain()
777
- .focus()
778
- .insertContent(`<a href="${url}">${file.name}</a>`)
779
- .run();
780
- }
781
- });
782
- }
783
- return true;
784
- },
785
- },
786
- immediatelyRender: false,
787
- });
788
-
789
- // Stable ref to the editor so callbacks with empty deps reach the live instance.
790
- const editorRef = useRef<typeof editor>(editor);
791
- editorRef.current = editor;
792
-
793
- useEffect(() => {
794
- editor?.setEditable(!isViewing);
795
- if (isViewing) setSourceMode(false);
796
- }, [editor, isViewing]);
797
-
798
- // When content updates from store (after loadPage), set it in editor
799
- const prevPathRef = useRef<string | null>(null);
800
- const renderedKeyRef = useRef<string | null>(null);
801
- const [renderedPath, setRenderedPath] = useState<string | null>(null);
802
- useEffect(() => {
803
- if (!editor || currentPath === null) return;
804
- // Skip if content hasn't actually changed (same path, dirty edit)
805
- if (
806
- useEditorStore.getState().isDirty &&
807
- currentPath === prevPathRef.current
808
- )
809
- return;
810
- // During page navigation the store briefly holds content="" while the
811
- // fetch is in flight. Rendering that empty string into ProseMirror is
812
- // pure waste — every extension runs a full schema pass twice per
813
- // navigation. Skip until the real content arrives.
814
- if (isLoading && content === "") return;
815
- // Dedupe identical (path, content) renders — e.g. cached paint followed
816
- // by a fresh fetch that returned the same markdown.
817
- const renderMarkdown = parsedViewingContent.body;
818
- const key = `${currentPath} ${renderMarkdown}`;
819
- if (renderedKeyRef.current === key) {
820
- if (renderedPath !== currentPath) setRenderedPath(currentPath);
821
- return;
822
- }
823
- prevPathRef.current = currentPath;
824
-
825
- let cancelled = false;
826
- const setContent = async () => {
827
- isLoadingRef.current = true;
828
- const html = await markdownToHtml(
829
- renderMarkdown,
830
- isViewing ? { pagePath: currentPath, sanitize: true } : currentPath,
831
- );
832
- // Rapid navigation: a newer page may have superseded this render while
833
- // markdownToHtml was awaiting. Don't stamp stale HTML into the editor.
834
- if (cancelled || useEditorStore.getState().currentPath !== currentPath) {
835
- isLoadingRef.current = false;
836
- return;
837
- }
838
- editor.commands.setContent(html);
839
- renderedKeyRef.current = key;
840
- setRenderedPath(currentPath);
841
- setTimeout(() => {
842
- isLoadingRef.current = false;
843
- }, 50);
844
- };
845
-
846
- setContent();
847
- return () => {
848
- cancelled = true;
849
- };
850
- }, [editor, content, currentPath, isLoading, renderedPath, parsedViewingContent.body, isViewing]);
851
-
852
- useEffect(() => {
853
- if (!isViewing || !renderedPath) return;
854
- const container = scrollContainerRef.current;
855
- if (!container) return;
856
- container
857
- .querySelectorAll<HTMLInputElement>('input[type="checkbox"]')
858
- .forEach((input) => {
859
- input.disabled = true;
860
- });
861
- }, [isViewing, renderedPath, parsedViewingContent.body]);
862
-
863
- const isLoadingState =
864
- currentPath !== null && (isLoading || renderedPath !== currentPath);
865
- // Don't flash a spinner for fast/cached opens: only reveal the overlay if the
866
- // load is still pending after a grace period. Instant (prefetched/cached) opens
867
- // resolve well within it and never show a loader — the world-class default.
868
- const [showLoadingOverlay, setShowLoadingOverlay] = useState(false);
869
- useEffect(() => {
870
- if (!isLoadingState) {
871
- setShowLoadingOverlay(false);
872
- return;
873
- }
874
- const id = setTimeout(() => setShowLoadingOverlay(true), 150);
875
- return () => clearTimeout(id);
876
- }, [isLoadingState]);
877
-
878
- const handleOpenAI = () => {
879
- clearMessages();
880
- openAI();
881
- };
882
-
883
- if (currentPath === null) {
884
- return (
885
- <div className="flex-1 flex items-center justify-center text-muted-foreground">
886
- <div className="text-center space-y-3">
887
- <p className="text-lg font-medium tracking-[-0.02em]">
888
- No page selected
889
- </p>
890
- <p className="text-sm text-muted-foreground/70">
891
- Select a page from the sidebar or create a new one
892
- </p>
893
- </div>
894
- </div>
895
- );
896
- }
897
-
898
- // Path resolved to a folder (or otherwise-missing target) without an
899
- // index.md. Render an explicit placeholder + Create CTA instead of
900
- // dropping the user into an empty editor that pretends to be the page.
901
- if (loadStatus === "missing") {
902
- const slug = currentPath.split("/").pop() || currentPath;
903
- const inferredTitle = slug
904
- .replace(/[-_]+/g, " ")
905
- .replace(/\b\w/g, (c) => c.toUpperCase());
906
- const folderNode = findNodeByPath(nodes, currentPath);
907
- const folderChildren = folderNode?.children ?? [];
908
- const hasChildren = folderChildren.length > 0;
909
- return (
910
- <div className="flex-1 overflow-y-auto">
911
- <div className="max-w-3xl mx-auto px-6 py-10 space-y-6">
912
- <div className="space-y-3">
913
- <p className="text-lg font-medium tracking-[-0.02em] text-foreground">
914
- {inferredTitle}
915
- </p>
916
- <p className="text-sm text-muted-foreground/80">
917
- This folder doesn&apos;t have an{" "}
918
- <code className="px-1 py-0.5 rounded bg-muted text-[12px]">
919
- index.md
920
- </code>
921
- {hasChildren ? " yet — its contents are listed below." : " yet."}
922
- </p>
923
- <button
924
- onClick={() => void createMissingPage(inferredTitle)}
925
- className="inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-[13px] font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
926
- >
927
- <FilePlus className="h-3.5 w-3.5" />
928
- Create page
929
- </button>
930
- </div>
931
- {hasChildren && (
932
- <FolderIndex
933
- key={currentPath}
934
- folderPath={currentPath}
935
- entries={folderChildren}
936
- />
937
- )}
938
- </div>
939
- </div>
940
- );
941
- }
942
-
943
- const toggleSourceMode = async () => {
944
- if (!sourceMode) {
945
- // Switching TO source mode — grab current markdown
946
- setSourceText(useEditorStore.getState().content);
947
- setSourceMode(true);
948
- } else {
949
- // Switching FROM source mode — apply changes
950
- useEditorStore.getState().updateContent(sourceText);
951
- if (editor) {
952
- isLoadingRef.current = true;
953
- const html = await markdownToHtml(sourceText, currentPath ?? undefined);
954
- editor.commands.setContent(html);
955
- setTimeout(() => {
956
- isLoadingRef.current = false;
957
- }, 50);
958
- }
959
- setSourceMode(false);
960
- }
961
- };
962
-
963
- // Folder pages with both an index.md (loadStatus === "ok") AND children
964
- // get a Page / Files tab strip so users can switch between the page body
965
- // and the directory listing without leaving the route.
966
- const renderedFolderNode = findNodeByPath(nodes, currentPath);
967
- const renderedFolderChildren =
968
- renderedFolderNode?.type === "directory" ||
969
- renderedFolderNode?.type === "cabinet"
970
- ? (renderedFolderNode.children ?? [])
971
- : [];
972
- const showFolderTabs = renderedFolderChildren.length > 0;
973
- const onFilesTab = showFolderTabs && folderTab === "files";
974
-
975
- return (
976
- <>
977
- <div className="flex-1 flex flex-col overflow-hidden">
978
- {showFolderTabs && (
979
- <div className="flex items-center gap-1 px-3 pt-2 border-b border-border">
980
- <button
981
- onClick={() => setFolderTab("page")}
982
- className={`px-3 py-1.5 text-[12px] rounded-t-md border-b-2 -mb-px transition-colors ${
983
- folderTab === "page"
984
- ? "border-primary text-foreground"
985
- : "border-transparent text-muted-foreground hover:text-foreground"
986
- }`}
987
- aria-pressed={folderTab === "page"}
988
- >
989
- Page
990
- </button>
991
- <button
992
- onClick={() => setFolderTab("files")}
993
- className={`px-3 py-1.5 text-[12px] rounded-t-md border-b-2 -mb-px transition-colors ${
994
- folderTab === "files"
995
- ? "border-primary text-foreground"
996
- : "border-transparent text-muted-foreground hover:text-foreground"
997
- }`}
998
- aria-pressed={folderTab === "files"}
999
- >
1000
- Files
1001
- <span className="ml-1.5 text-muted-foreground/60">
1002
- {renderedFolderChildren.length}
1003
- </span>
1004
- </button>
1005
- </div>
1006
- )}
1007
- {onFilesTab ? (
1008
- <div className="flex-1 overflow-y-auto">
1009
- <div className="max-w-3xl mx-auto px-6 py-6">
1010
- <FolderIndex
1011
- key={currentPath}
1012
- folderPath={currentPath}
1013
- entries={renderedFolderChildren}
1014
- />
1015
- </div>
1016
- </div>
1017
- ) : (
1018
- <>
1019
- {!isViewing && (
1020
- <div className="flex items-center min-w-0">
1021
- <div className="flex-1 min-w-0">
1022
- {!sourceMode && <EditorToolbar editor={editor} />}
1023
- </div>
1024
- <button
1025
- onClick={toggleSourceMode}
1026
- className={`flex items-center gap-1.5 px-3 py-1 mr-2 text-[11px] rounded-md transition-colors border border-border ${
1027
- sourceMode
1028
- ? "bg-primary text-primary-foreground"
1029
- : "text-muted-foreground hover:bg-accent"
1030
- }`}
1031
- >
1032
- <Code2 className="h-3 w-3" />
1033
- {sourceMode ? "Preview" : "Markdown"}
1034
- </button>
1035
- </div>
1036
- )}
1037
-
1038
- {sourceMode ? (
1039
- <div
1040
- className="flex-1 overflow-y-auto p-4"
1041
- dir={isRtl ? "rtl" : undefined}
1042
- >
1043
- <textarea
1044
- value={sourceText}
1045
- onChange={(e) => setSourceText(e.target.value)}
1046
- className="w-full h-full min-h-[calc(100vh-12rem)] bg-transparent font-mono text-[13px] leading-relaxed resize-none focus:outline-none"
1047
- spellCheck={false}
1048
- />
1049
- </div>
1050
- ) : (
1051
- <div className="flex-1 relative" dir={isRtl ? "rtl" : undefined}>
1052
- {isSuggesting && (
1053
- <div className="absolute top-0 inset-x-0 z-20 flex items-center justify-center gap-2 px-3 py-1 bg-primary/10 border-b border-primary/20 text-[11px] text-primary pointer-events-none">
1054
- Suggesting mode · your edits become suggestions for review
1055
- </div>
1056
- )}
1057
- <DocumentOutline editor={editor} scrollContainerRef={scrollContainerRef} />
1058
- <div
1059
- ref={scrollContainerRef}
1060
- className={`absolute inset-0 overflow-y-auto ${
1061
- isSuggesting ? "pt-7" : ""
1062
- }`}
1063
- style={{
1064
- ["--editor-max-w" as string]: editorMaxW,
1065
- ["--editor-ml" as string]: editorMl,
1066
- }}
1067
- data-editor-scroll
1068
- >
1069
- {/* Absolutely-positioned overlay for comment pips and suggestion cards.
1070
- height:0 so it doesn't push content; children overflow freely.
1071
- Positions from blockRefPositions are relative to scroll container top. */}
1072
- <div
1073
- aria-hidden="true"
1074
- className="relative pointer-events-none"
1075
- style={{ height: 0 }}
1076
- >
1077
- {/* Comment pips — one per block with at least one comment */}
1078
- {Object.entries(commentsByRef).map(([blockRef, blockComments]) => {
1079
- const pos = blockRefPositions.get(blockRef);
1080
- if (!pos) return null;
1081
- return (
1082
- <div key={`pip-${blockRef}`} style={{ pointerEvents: "auto" }}>
1083
- <CommentPip
1084
- blockRef={blockRef}
1085
- comments={blockComments}
1086
- top={pos.top + 4}
1087
- left={Math.max(0, pos.left - 20)}
1088
- onClick={() => {
1089
- const el = scrollContainerRef.current?.querySelector(
1090
- `[data-block-ref="${blockRef}"]`,
1091
- ) as HTMLElement | null;
1092
- if (el) setThreadTarget({ blockRef, el });
1093
- }}
1094
- />
1095
- </div>
1096
- );
1097
- })}
1098
-
1099
- {/* Suggestion cards — one per pending suggestion */}
1100
- {currentPath && pendingSuggestions.map((sg) => {
1101
- const pos = blockRefPositions.get(sg.ref);
1102
- const currentBlock = snapshotBlocks.find((b) => b.ref === sg.ref);
1103
- if (!pos || !currentBlock) return null;
1104
- const cardTop =
1105
- sg.kind === "insertAfter" ? pos.bottom + 4 :
1106
- sg.kind === "insertBefore" ? Math.max(0, pos.top - 80) :
1107
- pos.top;
1108
- return (
1109
- <div key={`sug-${sg.id}`} style={{ pointerEvents: "auto" }}>
1110
- <SuggestionCard
1111
- path={currentPath}
1112
- suggestion={sg}
1113
- currentMarkdown={currentBlock.markdown}
1114
- baseRevision={snapshotRevision}
1115
- getLatestRevision={() =>
1116
- useProofStore.getState().byPath[currentPath]?.snapshotRevision ?? 0
1117
- }
1118
- top={cardTop}
1119
- left={pos.left}
1120
- width={pos.width}
1121
- onSettled={() => {
1122
- void useProofStore.getState().loadSidecar(currentPath);
1123
- void useProofStore.getState().loadSnapshot(currentPath);
1124
- }}
1125
- readOnly={isViewing}
1126
- />
1127
- </div>
1128
- );
1129
- })}
1130
- </div>
1131
-
1132
- {/* Comment thread — Portal-rendered, driven by threadTarget */}
1133
- {threadTarget && currentPath && (
1134
- <CommentThread
1135
- path={currentPath}
1136
- blockRef={threadTarget.blockRef}
1137
- comments={commentsByRef[threadTarget.blockRef] ?? []}
1138
- anchorEl={threadTarget.el}
1139
- onClose={() => setThreadTarget(null)}
1140
- readOnly={isViewing}
1141
- />
1142
- )}
1143
-
1144
- {/* Human suggest-edit popover — driven by suggestTarget */}
1145
- {suggestTarget && currentPath && (
1146
- <SuggestEditPopover
1147
- path={currentPath}
1148
- blockRef={suggestTarget.blockRef}
1149
- currentMarkdown={suggestTarget.markdown}
1150
- anchor={suggestTarget.anchor}
1151
- onClose={() => setSuggestTarget(null)}
1152
- />
1153
- )}
1154
-
1155
- {isViewing && Object.keys(parsedViewingContent.data).length > 0 && (
1156
- <div className="max-w-[var(--editor-max-w,48rem)] ml-[var(--editor-ml,auto)] mr-auto px-4 sm:px-8 pt-6">
1157
- <FrontmatterHeader
1158
- data={parsedViewingContent.data as Record<string, never>}
1159
- />
1160
- </div>
1161
- )}
1162
- <EditorContent editor={editor} />
1163
- {currentPath && /\.(md|markdown)$/i.test(currentPath) && (
1164
- <BacklinksPanel currentPath={currentPath} />
1165
- )}
1166
- {/* Proof-span hover delegation */}
1167
- <div
1168
- aria-hidden="true"
1169
- className="contents"
1170
- onMouseOver={(e) => {
1171
- const span = (e.target as HTMLElement).closest<HTMLElement>(".proof-span");
1172
- if (span && span !== proofTarget) setProofTarget(span);
1173
- }}
1174
- onMouseOut={(e) => {
1175
- const related = e.relatedTarget as HTMLElement | null;
1176
- if (!related?.closest(".proof-span")) setProofTarget(null);
1177
- }}
1178
- />
1179
- {currentPath && (
1180
- <ProofSpanPopover
1181
- targetEl={proofTarget}
1182
- path={currentPath}
1183
- onClose={() => setProofTarget(null)}
1184
- onComment={() => {
1185
- if (!proofTarget) return;
1186
- const blockEl = proofTarget.closest<HTMLElement>("[data-block-ref]");
1187
- if (!blockEl) return;
1188
- const blockRef = blockEl.getAttribute("data-block-ref");
1189
- if (blockRef) setThreadTarget({ blockRef, el: blockEl });
1190
- }}
1191
- />
1192
- )}
1193
- {!isViewing && (
1194
- <>
1195
- <EditorBubbleMenu
1196
- editor={editor}
1197
- onSuggestEdit={openSuggestForSelection}
1198
- onComment={openCommentForSelection}
1199
- />
1200
- <TableMenu editor={editor} />
1201
- <SlashCommands editor={editor} />
1202
- <WikiLinkPicker
1203
- editor={editor}
1204
- onCreateRequest={openWikiCreateRef.current}
1205
- />
1206
- </>
1207
- )}
1208
- {isViewing && (
1209
- <ViewModeCommentButton
1210
- containerRef={scrollContainerRef}
1211
- onComment={openCommentForSelection}
1212
- />
1213
- )}
1214
- {/* AI Edit Prompt + slash hint */}
1215
- <div className="max-w-[var(--editor-max-w,48rem)] ml-[var(--editor-ml,auto)] mr-auto px-8 pb-8 flex items-center gap-4">
1216
- <button
1217
- onClick={handleOpenAI}
1218
- className="group flex items-center gap-2 text-[13px] text-muted-foreground/50 hover:text-muted-foreground transition-colors cursor-pointer"
1219
- >
1220
- <Sparkles className="h-3.5 w-3.5 group-hover:text-primary transition-colors" />
1221
- <span>Edit with AI</span>
1222
- </button>
1223
- {!isViewing && (
1224
- <span className="text-[11px] text-muted-foreground/30 select-none">
1225
- <kbd className="rounded px-1 py-0.5 font-mono text-[10px] ring-1 ring-foreground/10">
1226
- /
1227
- </kbd>{" "}
1228
- for commands
1229
- </span>
1230
- )}
1231
- </div>
1232
- </div>
1233
-
1234
- {showLoadingOverlay && (
1235
- <div
1236
- className="absolute inset-0 flex items-center justify-center bg-background/80 backdrop-blur-md z-20 pointer-events-none"
1237
- aria-hidden="true"
1238
- >
1239
- <Loader2 className="h-5 w-5 animate-spin text-muted-foreground/70" />
1240
- </div>
1241
- )}
1242
- </div>
1243
- )}
1244
-
1245
- {!isViewing && (
1246
- /* Status bar */
1247
- <div className="flex items-center justify-between px-4 py-1 border-t border-border text-xs text-muted-foreground/60">
1248
- <span className="text-[10.5px] text-muted-foreground/30 select-none hidden sm:block">
1249
- <kbd className="rounded px-1 font-mono text-[9.5px] ring-1 ring-foreground/10">
1250
- ⌘S
1251
- </kbd>{" "}
1252
- save
1253
- <span className="mx-1.5 opacity-40">·</span>
1254
- <kbd className="rounded px-1 font-mono text-[9.5px] ring-1 ring-foreground/10">
1255
- /
1256
- </kbd>{" "}
1257
- commands
1258
- </span>
1259
- <div className="flex items-center gap-3">
1260
- {/* Mode toggle */}
1261
- <div
1262
- className="flex items-center rounded-md border border-border overflow-hidden text-[10.5px]"
1263
- role="radiogroup"
1264
- aria-label="Edit mode"
1265
- >
1266
- <button
1267
- type="button"
1268
- role="radio"
1269
- aria-checked={editMode === "editing"}
1270
- onClick={() => setEditMode("editing")}
1271
- className={`px-2 py-0.5 transition-colors ${
1272
- editMode === "editing"
1273
- ? "bg-primary text-primary-foreground"
1274
- : "text-muted-foreground hover:bg-accent hover:text-accent-foreground"
1275
- }`}
1276
- >
1277
- Editing
1278
- </button>
1279
- <button
1280
- type="button"
1281
- role="radio"
1282
- aria-checked={editMode === "suggesting"}
1283
- onClick={() => setEditMode("suggesting")}
1284
- className={`px-2 py-0.5 transition-colors ${
1285
- editMode === "suggesting"
1286
- ? "bg-primary text-primary-foreground"
1287
- : "text-muted-foreground hover:bg-accent hover:text-accent-foreground"
1288
- }`}
1289
- >
1290
- Suggesting
1291
- </button>
1292
- </div>
1293
- <span
1294
- className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10.5px] transition-all duration-300 ${
1295
- saveStatus === "idle"
1296
- ? "opacity-0 pointer-events-none"
1297
- : "opacity-100"
1298
- } ${
1299
- saveStatus === "saving"
1300
- ? "bg-muted text-muted-foreground"
1301
- : saveStatus === "saved"
1302
- ? "bg-success/10 text-success"
1303
- : saveStatus === "error"
1304
- ? "bg-destructive/10 text-destructive"
1305
- : ""
1306
- }`}
1307
- >
1308
- {saveStatus === "saving" && (
1309
- <><Loader2 className="h-2.5 w-2.5 animate-spin" />Saving…</>
1310
- )}
1311
- {saveStatus === "saved" && (
1312
- <><Check className="h-2.5 w-2.5" />Saved</>
1313
- )}
1314
- {saveStatus === "error" && (
1315
- <><AlertCircle className="h-2.5 w-2.5" />Save failed</>
1316
- )}
1317
- </span>
1318
- </div>
1319
- </div>
1320
- )}
1321
- </>
1322
- )}
1323
- </div>
1324
- {WikiCreateDialog}
1325
- </>
1326
- );
1327
- }