wiki-viewer 1.9.1 → 1.11.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 (282) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-path-routes-manifest.json +3 -0
  3. package/.next/standalone/.next/build-manifest.json +5 -5
  4. package/.next/standalone/.next/routes-manifest.json +22 -0
  5. package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +2 -2
  6. package/.next/standalone/.next/server/app/_global-error/page.js +1 -1
  7. package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +2 -2
  16. package/.next/standalone/.next/server/app/_not-found/page/next-font-manifest.json +7 -1
  17. package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
  18. package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  21. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  22. package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  23. package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  24. package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  25. package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  26. package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  27. package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  28. package/.next/standalone/.next/server/app/api/agent/activity/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/agent/admin/agents/[agentId]/revoke/route.js +1 -1
  30. package/.next/standalone/.next/server/app/api/agent/admin/agents/[agentId]/revoke/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/agent/admin/agents/route.js +1 -1
  32. package/.next/standalone/.next/server/app/api/agent/admin/agents/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js +3 -4
  34. package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js +10 -11
  36. package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js +4 -5
  38. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js +3 -4
  41. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js +1 -1
  43. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/agent/internal/span/route.js +3 -4
  45. package/.next/standalone/.next/server/app/api/agent/internal/span/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/agent/settings/route.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js +3 -4
  48. package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js +1 -1
  50. package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/api/assets/[...path]/route.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/api/auth/[...all]/route.js +1 -1
  53. package/.next/standalone/.next/server/app/api/auth/[...all]/route.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/api/share/[token]/route/app-paths-manifest.json +3 -0
  55. package/.next/standalone/.next/server/app/api/share/[token]/route/build-manifest.json +9 -0
  56. package/.next/standalone/.next/server/app/api/share/[token]/route/server-reference-manifest.json +4 -0
  57. package/.next/standalone/.next/server/app/api/share/[token]/route.js +14 -0
  58. package/.next/standalone/.next/server/app/api/share/[token]/route.js.map +5 -0
  59. package/.next/standalone/.next/server/app/api/share/[token]/route.js.nft.json +1 -0
  60. package/.next/standalone/.next/server/app/api/share/[token]/route_client-reference-manifest.js +3 -0
  61. package/.next/standalone/.next/server/app/api/share/route/app-paths-manifest.json +3 -0
  62. package/.next/standalone/.next/server/app/api/share/route/build-manifest.json +9 -0
  63. package/.next/standalone/.next/server/app/api/share/route/server-reference-manifest.json +4 -0
  64. package/.next/standalone/.next/server/app/api/share/route.js +14 -0
  65. package/.next/standalone/.next/server/app/api/share/route.js.map +5 -0
  66. package/.next/standalone/.next/server/app/api/share/route.js.nft.json +1 -0
  67. package/.next/standalone/.next/server/app/api/share/route_client-reference-manifest.js +3 -0
  68. package/.next/standalone/.next/server/app/api/system/browse/route.js.nft.json +1 -1
  69. package/.next/standalone/.next/server/app/api/system/clear-root/route.js.nft.json +1 -1
  70. package/.next/standalone/.next/server/app/api/system/reveal/route.js.nft.json +1 -1
  71. package/.next/standalone/.next/server/app/api/system/root-status/route.js.nft.json +1 -1
  72. package/.next/standalone/.next/server/app/api/system/set-root/route.js +2 -2
  73. package/.next/standalone/.next/server/app/api/system/set-root/route.js.nft.json +1 -1
  74. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js.nft.json +1 -1
  75. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js.nft.json +1 -1
  76. package/.next/standalone/.next/server/app/api/system/workspaces/route.js.nft.json +1 -1
  77. package/.next/standalone/.next/server/app/api/upload/[...path]/route.js.nft.json +1 -1
  78. package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
  79. package/.next/standalone/.next/server/app/api/wiki/content/route.js.nft.json +1 -1
  80. package/.next/standalone/.next/server/app/api/wiki/download/route.js.nft.json +1 -1
  81. package/.next/standalone/.next/server/app/api/wiki/file/route.js.nft.json +1 -1
  82. package/.next/standalone/.next/server/app/api/wiki/folder/route.js.nft.json +1 -1
  83. package/.next/standalone/.next/server/app/api/wiki/move/route.js.nft.json +1 -1
  84. package/.next/standalone/.next/server/app/api/wiki/new-file/route.js.nft.json +1 -1
  85. package/.next/standalone/.next/server/app/api/wiki/page/route.js.nft.json +1 -1
  86. package/.next/standalone/.next/server/app/api/wiki/presence/route.js.nft.json +1 -1
  87. package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
  88. package/.next/standalone/.next/server/app/api/wiki/search/route.js.nft.json +1 -1
  89. package/.next/standalone/.next/server/app/api/wiki/slugs/route.js.nft.json +1 -1
  90. package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
  91. package/.next/standalone/.next/server/app/api/wiki/watch/route.js +1 -1
  92. package/.next/standalone/.next/server/app/api/wiki/watch/route.js.nft.json +1 -1
  93. package/.next/standalone/.next/server/app/index.html +1 -1
  94. package/.next/standalone/.next/server/app/index.rsc +9 -3
  95. package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  96. package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +9 -3
  97. package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  98. package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  99. package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +8 -2
  100. package/.next/standalone/.next/server/app/page/build-manifest.json +2 -2
  101. package/.next/standalone/.next/server/app/page/next-font-manifest.json +7 -1
  102. package/.next/standalone/.next/server/app/page.js +2 -2
  103. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  104. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  105. package/.next/standalone/.next/server/app/s/[token]/page/app-paths-manifest.json +3 -0
  106. package/.next/standalone/.next/server/app/s/[token]/page/build-manifest.json +16 -0
  107. package/.next/standalone/.next/server/app/s/[token]/page/next-font-manifest.json +16 -0
  108. package/.next/standalone/.next/server/app/s/[token]/page/react-loadable-manifest.json +1 -0
  109. package/.next/standalone/.next/server/app/s/[token]/page/server-reference-manifest.json +4 -0
  110. package/.next/standalone/.next/server/app/s/[token]/page.js +14 -0
  111. package/.next/standalone/.next/server/app/s/[token]/page.js.map +5 -0
  112. package/.next/standalone/.next/server/app/s/[token]/page.js.nft.json +1 -0
  113. package/.next/standalone/.next/server/app/s/[token]/page_client-reference-manifest.js +3 -0
  114. package/.next/standalone/.next/server/app/signin/page/build-manifest.json +2 -2
  115. package/.next/standalone/.next/server/app/signin/page/next-font-manifest.json +7 -1
  116. package/.next/standalone/.next/server/app/signin/page.js +3 -3
  117. package/.next/standalone/.next/server/app/signin/page.js.nft.json +1 -1
  118. package/.next/standalone/.next/server/app/signin/page_client-reference-manifest.js +1 -1
  119. package/.next/standalone/.next/server/app-paths-manifest.json +3 -0
  120. package/.next/standalone/.next/server/chunks/0dti_kysely_dist_esm_0u.fq2l._.js +1 -1
  121. package/.next/standalone/.next/server/chunks/0p91_next_dist_esm_build_templates_app-route_0d2jbo5.js +1 -1
  122. package/.next/standalone/.next/server/chunks/[root-of-the-server]__01hp8ax._.js +1 -1
  123. package/.next/standalone/.next/server/chunks/[root-of-the-server]__02scxiu._.js +3 -0
  124. package/.next/standalone/.next/server/chunks/[root-of-the-server]__07nnfjx._.js +16 -0
  125. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__12q8ch8._.js → [root-of-the-server]__08n~vj1._.js} +2 -2
  126. package/.next/standalone/.next/server/chunks/[root-of-the-server]__09s9qkd._.js +1 -1
  127. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__12ee_s6._.js → [root-of-the-server]__0bfl4v7._.js} +2 -2
  128. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l-8clq._.js +1 -1
  129. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0n3i3vu._.js +16 -0
  130. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ncjv3e._.js +3 -0
  131. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0s8ovh5._.js +3 -0
  132. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0~.-3yk._.js → [root-of-the-server]__0x3m4up._.js} +2 -2
  133. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0yzle76._.js +20 -0
  134. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0z.pwwj._.js +20 -0
  135. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zgb.q2._.js +3 -0
  136. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__00qu86p._.js → [root-of-the-server]__0zq0rlw._.js} +2 -2
  137. package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0-inabx._.js → [root-of-the-server]__12y5z5j._.js} +2 -2
  138. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_share_[token]_route_actions_0~m-o26.js +3 -0
  139. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_share_route_actions_0gz4h.j.js +3 -0
  140. package/.next/standalone/.next/server/chunks/node_modules__pnpm_0aj5g-w._.js +1 -1
  141. package/.next/standalone/.next/server/chunks/src_lib_auth_admin_ts_0g4i6v_._.js +3 -0
  142. package/.next/standalone/.next/server/chunks/ssr/08s9_dagre-d3-es_src_dagre_index_0iwjukf.js +1 -1
  143. package/.next/standalone/.next/server/chunks/ssr/0p91_next_dist_esm_build_templates_app-page_0p1dt3f.js +4 -0
  144. package/.next/standalone/.next/server/chunks/ssr/{0~w0_katex_dist_katex_mjs_0hyln.b._.js → 0~w0_katex_dist_katex_mjs_03n1kec._.js} +1 -1
  145. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_chunk-5ZQYHXKU_mjs_0z1.978._.js +1 -1
  146. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_chunk-KSCS5N6A_mjs_0v4oeem._.js +1 -1
  147. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_diagram-OG6HWLK6_mjs_0t5sus6._.js +1 -1
  148. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_erDiagram-TEJ5UH35_mjs_0v6rnqy._.js +1 -1
  149. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_sankeyDiagram-5OEKKPKP_mjs_11ifrnu._.js +1 -1
  150. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_mermaid_core_mjs_0hj_n-~._.js +1 -1
  151. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__02twzph._.js → [root-of-the-server]__00_67m6._.js} +2 -2
  152. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00lk0r~._.js → [root-of-the-server]__06mzg2t._.js} +2 -2
  153. package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0du58sw._.js → [root-of-the-server]__0lh.8ub._.js} +2 -2
  154. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oh...2._.js +3 -0
  155. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0osa2xb._.js +3 -0
  156. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0txwwn-._.js +3 -0
  157. package/.next/standalone/.next/server/chunks/ssr/_0wmhtx3._.js +3 -0
  158. package/.next/standalone/.next/server/chunks/ssr/_0yevh-d._.js +115 -0
  159. package/.next/standalone/.next/server/chunks/ssr/_0~a9dyk._.js +115 -0
  160. package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_s_[token]_page_actions_0bubzp7.js +3 -0
  161. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0iikbcy._.js → node_modules__pnpm_0-ljbo6._.js} +1 -1
  162. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_03z3na-._.js → node_modules__pnpm_0.b51bz._.js} +1 -1
  163. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0qd2ovj._.js → node_modules__pnpm_02z1q6x._.js} +2 -2
  164. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0elpfsb._.js → node_modules__pnpm_06rujwe._.js} +2 -2
  165. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0b5fla7._.js → node_modules__pnpm_07~ezvd._.js} +2 -2
  166. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_03.1jli._.js → node_modules__pnpm_0annbkv._.js} +1 -1
  167. package/.next/standalone/.next/server/chunks/ssr/node_modules__pnpm_0aq-myi._.js +1 -1
  168. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_00y7foi._.js → node_modules__pnpm_0db-578._.js} +2 -2
  169. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0s4yn43._.js → node_modules__pnpm_0e-h_5v._.js} +1 -1
  170. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0arbjru._.js → node_modules__pnpm_0efhg__._.js} +1 -1
  171. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_09eo8jh._.js → node_modules__pnpm_0fdgk8l._.js} +2 -2
  172. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0d~ida~._.js → node_modules__pnpm_0fy2khf._.js} +1 -1
  173. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0s8fvbn._.js → node_modules__pnpm_0ge9-p_._.js} +1 -1
  174. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0dbr7c2._.js → node_modules__pnpm_0h0n.h9._.js} +1 -1
  175. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0fh.6ir._.js → node_modules__pnpm_0k48w96._.js} +2 -2
  176. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0.~goq~._.js → node_modules__pnpm_0k8t~gn._.js} +2 -2
  177. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0mvih7b._.js → node_modules__pnpm_0kx2gj~._.js} +2 -2
  178. package/.next/standalone/.next/server/chunks/ssr/node_modules__pnpm_0lzb-n~._.js +1 -1
  179. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0fm6wcx._.js → node_modules__pnpm_0m2aya9._.js} +1 -1
  180. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_05~w-ok._.js → node_modules__pnpm_0rhh2ug._.js} +1 -1
  181. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0beormu._.js → node_modules__pnpm_0s7.cqo._.js} +2 -2
  182. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0adm8h6._.js → node_modules__pnpm_0u2s0ie._.js} +1 -1
  183. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_09d5yyu._.js → node_modules__pnpm_0uhtpu4._.js} +1 -1
  184. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0tq8zs6._.js → node_modules__pnpm_0vn52ck._.js} +1 -1
  185. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0c7u76a._.js → node_modules__pnpm_0z_-o7y._.js} +2 -2
  186. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0xa.ko1._.js → node_modules__pnpm_11li7t8._.js} +1 -1
  187. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0.kyqfq._.js → node_modules__pnpm_12ggywb._.js} +1 -1
  188. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_0vyv.lz._.js → node_modules__pnpm_12ohbxv._.js} +1 -1
  189. package/.next/standalone/.next/server/chunks/ssr/{node_modules__pnpm_12.ymhl._.js → node_modules__pnpm_12yj7m8._.js} +1 -1
  190. package/.next/standalone/.next/server/chunks/ssr/src_app_page_tsx_0ss2.w7._.js +19 -0
  191. package/.next/standalone/.next/server/edge/chunks/0p91_next_dist_0nh.6nc._.js +1 -1
  192. package/.next/standalone/.next/server/middleware-build-manifest.js +5 -5
  193. package/.next/standalone/.next/server/middleware-manifest.json +1 -1
  194. package/.next/standalone/.next/server/next-font-manifest.js +1 -1
  195. package/.next/standalone/.next/server/next-font-manifest.json +30 -3
  196. package/.next/standalone/.next/server/pages/404.html +1 -1
  197. package/.next/standalone/.next/server/pages/500.html +1 -1
  198. package/.next/standalone/.next/static/chunks/02c9264uigqjd.js +1 -0
  199. package/.next/standalone/.next/static/chunks/03zepg.10g9nk.js +21 -0
  200. package/.next/standalone/.next/static/chunks/08~wl1f9eyv70.css +5 -0
  201. package/.next/standalone/.next/static/chunks/0cre2g95gqnc_.js +1 -0
  202. package/.next/standalone/.next/static/chunks/0dhnx18v._gaq.js +113 -0
  203. package/.next/standalone/.next/static/chunks/{144e01xuvhx~x.js → 0g5dv59skfo1z.js} +1 -1
  204. package/.next/standalone/.next/static/chunks/{14n5cp8rr-k_b.js → 0qiz7kkyaxa93.js} +1 -1
  205. package/.next/standalone/.next/static/chunks/0vhw1us0pjfdy.js +1 -0
  206. package/.next/standalone/.next/static/chunks/{06iw_iy5e90d4.js → 0vp-ahhagxptc.js} +1 -1
  207. package/.next/standalone/.next/static/chunks/{0b84.439zffwm.js → 0yhis._5rr6tc.js} +1 -1
  208. package/.next/standalone/.next/static/chunks/0z1s6bzchqtx3.js +17 -0
  209. package/.next/standalone/.next/static/chunks/139g2hzko-flp.js +1 -0
  210. package/.next/standalone/.next/static/chunks/141sj6_6_3ryx.js +113 -0
  211. package/.next/standalone/.next/static/chunks/{turbopack-0lf4~b-ofzbm6.js → turbopack-12~z-mr7b9b50.js} +1 -1
  212. package/.next/standalone/.next/static/media/02263ebadd758ea4-s.0qg7j5o.yrclm.woff2 +0 -0
  213. package/.next/standalone/.next/static/media/0ada71b763703d9d-s.03kpp5fbi.88j.woff2 +0 -0
  214. package/.next/standalone/.next/static/media/167cd0713aa75522-s.0j1f760ks3~7e.woff2 +0 -0
  215. package/.next/standalone/.next/static/media/1a69062cfe6f77f8-s.07r2ragk8ph~z.woff2 +0 -0
  216. package/.next/standalone/.next/static/media/23b7a97ae3b5c134-s.p.0o14nraug8u5s.woff2 +0 -0
  217. package/.next/standalone/.next/static/media/2b7d3311b69a4dca-s.p.0g6adshk-wxa_.woff2 +0 -0
  218. package/.next/standalone/.next/static/media/2e95a7d252b9825a-s.0dgq8lqrj3gc_.woff2 +0 -0
  219. package/.next/standalone/.next/static/media/2fe89d53234c61d4-s.0k_.i63k_i_qa.woff2 +0 -0
  220. package/.next/standalone/.next/static/media/59b15b4bcd7b1eb5-s.0x08jh8vondwt.woff2 +0 -0
  221. package/.next/standalone/.next/static/media/5e05ae5b48faa55e-s.0m9hhkgdlzukj.woff2 +0 -0
  222. package/.next/standalone/.next/static/media/6632a144c419935f-s.02msn089-soa4.woff2 +0 -0
  223. package/.next/standalone/.next/static/media/68757d6cddeff913-s.0hkrq11n-dhos.woff2 +0 -0
  224. package/.next/standalone/.next/static/media/6ff74e33ebd7bca3-s.0_n_qwbzcushi.woff2 +0 -0
  225. package/.next/standalone/.next/static/media/791bf8c4bb753ed6-s.p.0524igg32t-c1.woff2 +0 -0
  226. package/.next/standalone/.next/static/media/7b27d9904b50e540-s.13-bl75dp544c.woff2 +0 -0
  227. package/.next/standalone/.next/static/media/99e609270109b47d-s.p.16-z~2sp29ex6.woff2 +0 -0
  228. package/.next/standalone/.next/static/media/9a4838fcda0d1bca-s.0787d67~10694.woff2 +0 -0
  229. package/.next/standalone/.next/static/media/a7afbb44bec2bb18-s.0juuwbcf7uz~t.woff2 +0 -0
  230. package/.next/standalone/.next/static/media/ca8956476e4b15e1-s.08wnys.spqmz-.woff2 +0 -0
  231. package/.next/standalone/.next/static/media/d38f3bca7db33566-s.p.07n94j49quulv.woff2 +0 -0
  232. package/.next/standalone/.next/static/media/e390973e931a41c5-s.08hd0g.321csk.woff2 +0 -0
  233. package/.next/standalone/.next/static/media/e455bcff747f0d5e-s.0lukgm0wyhr0a.woff2 +0 -0
  234. package/.next/standalone/.next/static/media/e532fa1b9921e1cd-s.0~wrfy7sm-h4g.woff2 +0 -0
  235. package/.next/standalone/.next/static/media/effe91970fc4db64-s.p.07ig5l8azj~hy.woff2 +0 -0
  236. package/.next/standalone/docs/editorial-skin-spec.md +242 -0
  237. package/.next/standalone/package.json +1 -1
  238. package/.next/standalone/src/app/api/share/[token]/route.ts +269 -0
  239. package/.next/standalone/src/app/api/share/route.ts +127 -0
  240. package/.next/standalone/src/app/api/system/workspaces/route.ts +9 -0
  241. package/.next/standalone/src/app/globals.css +470 -56
  242. package/.next/standalone/src/app/layout.tsx +29 -2
  243. package/.next/standalone/src/app/page.tsx +162 -19
  244. package/.next/standalone/src/app/s/[token]/page.tsx +357 -0
  245. package/.next/standalone/src/components/ai-panel/ai-panel.tsx +1 -1
  246. package/.next/standalone/src/components/auth-settings-sheet.tsx +38 -1
  247. package/.next/standalone/src/components/editor/csv-viewer.tsx +46 -3
  248. package/.next/standalone/src/components/editor/editor.tsx +14 -6
  249. package/.next/standalone/src/components/editor/large-file-gate.tsx +108 -0
  250. package/.next/standalone/src/components/editor/office/xlsx-viewer.tsx +83 -8
  251. package/.next/standalone/src/components/editor/slash-commands.tsx +2 -2
  252. package/.next/standalone/src/components/editor/source-viewer.tsx +67 -18
  253. package/.next/standalone/src/components/share-dialog.tsx +307 -0
  254. package/.next/standalone/src/components/ui/dialog.tsx +1 -1
  255. package/.next/standalone/src/components/ui/sheet.tsx +1 -1
  256. package/.next/standalone/src/components/ui/switch.tsx +1 -1
  257. package/.next/standalone/src/components/view-width-toggle.tsx +23 -0
  258. package/.next/standalone/src/lib/shared-docs/db.ts +177 -0
  259. package/.next/standalone/src/middleware.ts +1 -0
  260. package/.next/standalone/src/stores/sidebar-width-store.ts +39 -0
  261. package/.next/standalone/src/stores/skin-store.ts +42 -0
  262. package/.next/standalone/src/stores/view-width-store.ts +43 -4
  263. package/.next/standalone/tailwind.config.ts +11 -21
  264. package/.next/standalone/tsconfig.tsbuildinfo +1 -1
  265. package/package.json +1 -1
  266. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-244bv._.js +0 -16
  267. package/.next/standalone/.next/server/chunks/[root-of-the-server]__012753j._.js +0 -3
  268. package/.next/standalone/.next/server/chunks/[root-of-the-server]__03p0ob1._.js +0 -16
  269. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0gda_iu._.js +0 -3
  270. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0jk1uf4._.js +0 -3
  271. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0nd.b_c._.js +0 -3
  272. package/.next/standalone/.next/server/chunks/[root-of-the-server]__13aid91._.js +0 -3
  273. package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0hvvps3._.js +0 -3
  274. package/.next/standalone/.next/server/chunks/ssr/_0pqaawz._.js +0 -131
  275. package/.next/standalone/.next/static/chunks/09-jtayuhrq~b.js +0 -1
  276. package/.next/standalone/.next/static/chunks/0vp2gi~fq70je.js +0 -21
  277. package/.next/standalone/.next/static/chunks/125_k3o0s4cid.js +0 -129
  278. package/.next/standalone/.next/static/chunks/135c~rq_ux73w.js +0 -1
  279. package/.next/standalone/.next/static/chunks/167ph4xws_-61.css +0 -2
  280. /package/.next/standalone/.next/static/{NN8w2BNeE9aJeGzUsWJ8G → GCGec_I7i0LnI4YpnaeMc}/_buildManifest.js +0 -0
  281. /package/.next/standalone/.next/static/{NN8w2BNeE9aJeGzUsWJ8G → GCGec_I7i0LnI4YpnaeMc}/_clientMiddlewareManifest.js +0 -0
  282. /package/.next/standalone/.next/static/{NN8w2BNeE9aJeGzUsWJ8G → GCGec_I7i0LnI4YpnaeMc}/_ssgManifest.js +0 -0
@@ -0,0 +1,108 @@
1
+ "use client";
2
+
3
+ import { Download, ExternalLink, FileWarning, FolderOpen } from "lucide-react";
4
+ import { ViewerToolbar } from "@/components/layout/viewer-toolbar";
5
+ import { Button } from "@/components/ui/button";
6
+ import { withWs, wsFetch } from "@/lib/workspace-client";
7
+
8
+ interface LargeFileGateProps {
9
+ path: string;
10
+ size: number;
11
+ /** Mount the real viewer. */
12
+ onOpen: () => void;
13
+ }
14
+
15
+ function formatSize(bytes: number): string {
16
+ if (bytes >= 1024 * 1024 * 1024)
17
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
18
+ if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
19
+ if (bytes >= 1024) return `${Math.round(bytes / 1024)} KB`;
20
+ return `${bytes} B`;
21
+ }
22
+
23
+ /**
24
+ * Confirmation shown before mounting a viewer that loads and renders a whole
25
+ * large file. Sits above the viewer dispatch, so it guards every unsafe viewer
26
+ * in one place. The user can open it anyway, download it, or open the raw bytes.
27
+ */
28
+ export function LargeFileGate({ path, size, onOpen }: LargeFileGateProps) {
29
+ const assetUrl = withWs(`/api/assets/${path}`);
30
+ const filename = path.split("/").pop() || path;
31
+ const ext = filename.includes(".")
32
+ ? filename.split(".").pop()?.toUpperCase()
33
+ : "";
34
+
35
+ const revealInFinder = async () => {
36
+ try {
37
+ await wsFetch("/api/system/reveal", {
38
+ method: "POST",
39
+ headers: { "Content-Type": "application/json" },
40
+ body: JSON.stringify({ path }),
41
+ });
42
+ } catch {
43
+ /* ignore */
44
+ }
45
+ };
46
+
47
+ return (
48
+ <div className="flex-1 flex flex-col overflow-hidden">
49
+ <ViewerToolbar path={path} badge={ext || undefined} />
50
+ <div className="flex-1 flex items-center justify-center">
51
+ <div className="flex flex-col items-center gap-4 text-center max-w-sm">
52
+ <div className="flex size-16 items-center justify-center rounded-2xl bg-amber-500/10">
53
+ <FileWarning className="size-8 text-amber-500" />
54
+ </div>
55
+ <div className="space-y-1">
56
+ <p className="text-sm font-medium">{filename}</p>
57
+ <p className="text-xs text-muted-foreground">
58
+ This file is {formatSize(size)}. Opening it in the viewer may
59
+ freeze the tab while it loads and renders.
60
+ </p>
61
+ </div>
62
+ <div className="flex flex-wrap items-center justify-center gap-2">
63
+ <Button
64
+ variant="default"
65
+ size="sm"
66
+ className="gap-2"
67
+ onClick={onOpen}
68
+ >
69
+ Open anyway
70
+ </Button>
71
+ <Button
72
+ variant="outline"
73
+ size="sm"
74
+ className="gap-2"
75
+ onClick={() => window.open(assetUrl, "_blank")}
76
+ >
77
+ <ExternalLink className="h-4 w-4" />
78
+ Raw
79
+ </Button>
80
+ <Button
81
+ variant="outline"
82
+ size="sm"
83
+ className="gap-2"
84
+ onClick={() => {
85
+ const a = document.createElement("a");
86
+ a.href = assetUrl;
87
+ a.download = filename;
88
+ a.click();
89
+ }}
90
+ >
91
+ <Download className="h-4 w-4" />
92
+ Download
93
+ </Button>
94
+ <Button
95
+ variant="outline"
96
+ size="sm"
97
+ className="gap-2"
98
+ onClick={revealInFinder}
99
+ >
100
+ <FolderOpen className="h-4 w-4" />
101
+ Open in Finder
102
+ </Button>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ );
108
+ }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { Loader2 } from "lucide-react";
4
4
  import { wsFetch } from "@/lib/workspace-client";
5
- import { useEffect, useMemo, useState } from "react";
5
+ import { useEffect, useMemo, useRef, useState } from "react";
6
6
  import { cn } from "@/lib/utils";
7
7
  import { OfficeChrome } from "./office-chrome";
8
8
 
@@ -13,14 +13,48 @@ interface Props {
13
13
 
14
14
  interface Sheet {
15
15
  name: string;
16
- html: string;
16
+ // Built lazily on first view; sheet_to_html over a huge sheet freezes the tab.
17
+ html: string | null;
18
+ rows: number; // total rows, for the truncation banner
19
+ truncated: boolean;
17
20
  }
18
21
 
22
+ // Sheets longer than this are clamped before conversion to cap DOM size.
23
+ const MAX_ROWS = 2000;
24
+
19
25
  export function XlsxViewer({ path, title }: Props) {
20
26
  const [sheets, setSheets] = useState<Sheet[] | null>(null);
21
27
  const [active, setActive] = useState(0);
22
28
  const [loading, setLoading] = useState(true);
23
29
  const [error, setError] = useState<string | null>(null);
30
+ const wbRef = useRef<{
31
+ wb: import("xlsx").WorkBook;
32
+ toHtml: (ws: import("xlsx").WorkSheet) => {
33
+ html: string;
34
+ rows: number;
35
+ truncated: boolean;
36
+ };
37
+ } | null>(null);
38
+
39
+ // Build the active sheet's HTML the first time it is viewed.
40
+ useEffect(() => {
41
+ if (!sheets || !wbRef.current) return;
42
+ const s = sheets[active];
43
+ if (!s || s.html !== null) return;
44
+ const { wb, toHtml } = wbRef.current;
45
+ const r = toHtml(wb.Sheets[s.name]);
46
+ setSheets((prev) => {
47
+ if (!prev) return prev;
48
+ const next = [...prev];
49
+ next[active] = {
50
+ ...next[active],
51
+ html: r.html,
52
+ rows: r.rows,
53
+ truncated: r.truncated,
54
+ };
55
+ return next;
56
+ });
57
+ }, [sheets, active]);
24
58
 
25
59
  useEffect(() => {
26
60
  let cancelled = false;
@@ -42,12 +76,46 @@ export function XlsxViewer({ path, title }: Props) {
42
76
  cellDates: true,
43
77
  cellStyles: true,
44
78
  });
45
- const result: Sheet[] = wb.SheetNames.map((name) => ({
46
- name,
47
- html: XLSX.utils.sheet_to_html(wb.Sheets[name], { editable: false }),
48
- }));
79
+ // Convert one worksheet to HTML, clamping its row range to MAX_ROWS.
80
+ // Returns the HTML plus whether it was truncated and the true row
81
+ // count. Kept here so it closes over the dynamically-imported XLSX.
82
+ // sheet_to_html has no range option; it honors the sheet's own !ref.
83
+ // For oversized sheets we shrink a copied !ref so only MAX_ROWS emit.
84
+ const toHtml = (ws: import("xlsx").WorkSheet) => {
85
+ const ref = ws["!ref"];
86
+ const range = ref ? XLSX.utils.decode_range(ref) : null;
87
+ const totalRows = range ? range.e.r - range.s.r + 1 : 0;
88
+ if (!range || totalRows <= MAX_ROWS) {
89
+ return {
90
+ html: XLSX.utils.sheet_to_html(ws, { editable: false }),
91
+ rows: totalRows,
92
+ truncated: false,
93
+ };
94
+ }
95
+ const clampedWs = {
96
+ ...ws,
97
+ "!ref": XLSX.utils.encode_range({
98
+ s: range.s,
99
+ e: { r: range.s.r + MAX_ROWS - 1, c: range.e.c },
100
+ }),
101
+ };
102
+ return {
103
+ html: XLSX.utils.sheet_to_html(clampedWs, { editable: false }),
104
+ rows: totalRows,
105
+ truncated: true,
106
+ };
107
+ };
49
108
  if (cancelled) return;
50
- setSheets(result);
109
+ // Sheets render lazily via the active-sheet effect, so start them empty.
110
+ wbRef.current = { wb, toHtml };
111
+ setSheets(
112
+ wb.SheetNames.map((name) => ({
113
+ name,
114
+ html: null,
115
+ rows: 0,
116
+ truncated: false,
117
+ })),
118
+ );
51
119
  setActive(0);
52
120
  setLoading(false);
53
121
  } catch (err) {
@@ -105,7 +173,14 @@ export function XlsxViewer({ path, title }: Props) {
105
173
  </div>
106
174
  </div>
107
175
  )}
108
- {current && (
176
+ {current?.truncated && (
177
+ <div className="px-3 py-2 text-[11px] text-amber-600 dark:text-amber-400 border-b border-border">
178
+ Large sheet ({current.rows.toLocaleString()} rows). Showing first{" "}
179
+ {MAX_ROWS.toLocaleString()} for performance. Download the file to
180
+ see everything.
181
+ </div>
182
+ )}
183
+ {current?.html != null && (
109
184
  <div
110
185
  className="xlsx-sheet p-3 text-[12px]"
111
186
  dangerouslySetInnerHTML={{ __html: current.html }}
@@ -344,8 +344,8 @@ function CommandPreview({ cmd }: { cmd: SlashCommand }) {
344
344
  );
345
345
  case "Warning":
346
346
  return (
347
- <div className="flex items-start gap-1.5 bg-yellow-500/10 border border-yellow-500/20 rounded px-2 py-1">
348
- <AlertTriangle className="h-3 w-3 text-yellow-500 shrink-0 mt-0.5" />
347
+ <div className="flex items-start gap-1.5 bg-warning/10 border border-warning/20 rounded px-2 py-1">
348
+ <AlertTriangle className="h-3 w-3 text-warning shrink-0 mt-0.5" />
349
349
  <span className="text-[10px] text-foreground/60">Warning callout</span>
350
350
  </div>
351
351
  );
@@ -32,6 +32,19 @@ interface SourceViewerProps {
32
32
 
33
33
  const lowlight = createLowlight(common);
34
34
 
35
+ // Large files skip syntax highlighting (lowlight is synchronous and blocks the
36
+ // main thread) and render in chunks (one <tr> per line freezes the tab).
37
+ const LARGE_BYTES = 2 * 1024 * 1024; // 2 MB
38
+ const LARGE_LINES = 5000;
39
+ const RENDER_CHUNK = 2000; // lines added per "Show more"
40
+
41
+ function escapeHtml(s: string): string {
42
+ return s
43
+ .replace(/&/g, "&amp;")
44
+ .replace(/</g, "&lt;")
45
+ .replace(/>/g, "&gt;");
46
+ }
47
+
35
48
  const EXT_TO_LANG: Record<string, string> = {
36
49
  ".js": "javascript",
37
50
  ".cjs": "javascript",
@@ -90,10 +103,12 @@ function formatBadge(filename: string): string {
90
103
 
91
104
  export function SourceViewer({ path }: SourceViewerProps) {
92
105
  const [content, setContent] = useState<string | null>(null);
106
+ const [byteSize, setByteSize] = useState(0);
93
107
  const [binary, setBinary] = useState(false);
94
108
  const [loading, setLoading] = useState(true);
95
109
  const [wrap, setWrap] = useState(false);
96
110
  const [copied, setCopied] = useState(false);
111
+ const [visibleCount, setVisibleCount] = useState(RENDER_CHUNK);
97
112
 
98
113
  const assetUrl = withWs(`/api/assets/${path}`);
99
114
  const filename = path.split("/").pop() || path;
@@ -108,6 +123,7 @@ export function SourceViewer({ path }: SourceViewerProps) {
108
123
  if (looksBinary(bytes)) {
109
124
  setBinary(true);
110
125
  } else {
126
+ setByteSize(bytes.length);
111
127
  setContent(new TextDecoder("utf-8").decode(bytes));
112
128
  }
113
129
  }
@@ -122,27 +138,31 @@ export function SourceViewer({ path }: SourceViewerProps) {
122
138
  void fetchContent();
123
139
  }, [fetchContent]);
124
140
 
141
+ const lineCount = useMemo(
142
+ () => (content ? content.split("\n").length : 0),
143
+ [content],
144
+ );
145
+
146
+ const isLarge = byteSize > LARGE_BYTES || lineCount > LARGE_LINES;
147
+
125
148
  const highlightedLines = useMemo(() => {
126
149
  if (!content) return [];
150
+ if (isLarge) return content.split("\n").map(escapeHtml);
127
151
  try {
128
152
  const tree = language
129
153
  ? lowlight.highlight(language, content)
130
154
  : lowlight.highlightAuto(content);
131
- const html = toHtml(tree);
132
- // Split by newlines while preserving HTML tags that span lines
133
- return html.split("\n");
155
+ // Split on newlines, preserving tags that span lines.
156
+ return toHtml(tree).split("\n");
134
157
  } catch {
135
- // Fallback: no highlighting
136
- return content
137
- .split("\n")
138
- .map((line) =>
139
- line
140
- .replace(/&/g, "&amp;")
141
- .replace(/</g, "&lt;")
142
- .replace(/>/g, "&gt;"),
143
- );
158
+ return content.split("\n").map(escapeHtml);
144
159
  }
145
- }, [content, language]);
160
+ }, [content, language, isLarge]);
161
+
162
+ const shownLines = isLarge
163
+ ? highlightedLines.slice(0, visibleCount)
164
+ : highlightedLines;
165
+ const hasMore = isLarge && visibleCount < highlightedLines.length;
146
166
 
147
167
  const copyToClipboard = () => {
148
168
  if (!content) return;
@@ -212,27 +232,56 @@ export function SourceViewer({ path }: SourceViewerProps) {
212
232
  Raw
213
233
  </Button>
214
234
  </ViewerToolbar>
215
- <div className="flex-1 overflow-auto source-viewer-code bg-[#1e1e1e]">
235
+ <div className="flex-1 overflow-auto source-viewer-code bg-muted">
216
236
  {loading ? (
217
237
  <div className="flex items-center justify-center h-full text-muted-foreground text-sm">
218
238
  Loading...
219
239
  </div>
220
240
  ) : (
241
+ <>
242
+ {isLarge && (
243
+ <div className="px-4 py-2 text-[11px] text-amber-700 dark:text-amber-300 bg-amber-500/15 border-b border-amber-500/30 font-sans">
244
+ Large file ({(byteSize / (1024 * 1024)).toFixed(1)} MB,{" "}
245
+ {highlightedLines.length.toLocaleString()} lines). Syntax
246
+ highlighting disabled for performance. Use Raw or Download for
247
+ the full file.
248
+ </div>
249
+ )}
221
250
  <table className="w-full border-collapse text-[13px] leading-relaxed font-mono">
222
251
  <tbody>
223
- {highlightedLines.map((lineHtml, i) => (
224
- <tr key={i} className="hover:bg-white/5">
225
- <td className="w-12 pr-4 text-right text-[#858585] select-none align-top sticky left-0 bg-[#1e1e1e]">
252
+ {shownLines.map((lineHtml, i) => (
253
+ <tr key={i} className="hover:bg-foreground/5">
254
+ <td className="w-12 pr-4 text-right text-muted-foreground select-none align-top sticky left-0 bg-muted">
226
255
  {i + 1}
227
256
  </td>
228
257
  <td
229
- className={`text-[#d4d4d4] pl-2 ${wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"}`}
258
+ className={`text-foreground pl-2 ${wrap ? "whitespace-pre-wrap break-all" : "whitespace-pre"}`}
230
259
  dangerouslySetInnerHTML={{ __html: lineHtml || " " }}
231
260
  />
232
261
  </tr>
233
262
  ))}
234
263
  </tbody>
235
264
  </table>
265
+ {hasMore && (
266
+ <div className="flex items-center gap-3 px-4 py-3 border-t border-border font-sans">
267
+ <button
268
+ onClick={() => setVisibleCount((v) => v + RENDER_CHUNK)}
269
+ className="text-[11px] text-foreground hover:text-foreground px-2.5 py-1 rounded bg-foreground/10 hover:bg-foreground/20 transition-colors"
270
+ >
271
+ Show{" "}
272
+ {Math.min(
273
+ RENDER_CHUNK,
274
+ highlightedLines.length - visibleCount,
275
+ ).toLocaleString()}{" "}
276
+ more
277
+ </button>
278
+ <span className="text-[11px] text-muted-foreground">
279
+ {visibleCount.toLocaleString()} /{" "}
280
+ {highlightedLines.length.toLocaleString()} lines
281
+ </span>
282
+ </div>
283
+ )}
284
+ </>
236
285
  )}
237
286
  </div>
238
287
  </div>
@@ -0,0 +1,307 @@
1
+ "use client";
2
+
3
+ import {
4
+ Copy,
5
+ Check,
6
+ Globe,
7
+ Loader2,
8
+ Lock,
9
+ Trash2,
10
+ Clock,
11
+ } from "lucide-react";
12
+ import { useCallback, useEffect, useState } from "react";
13
+ import { Button } from "@/components/ui/button";
14
+ import { Input } from "@/components/ui/input";
15
+ import { Switch } from "@/components/ui/switch";
16
+ import { Label } from "@/components/ui/label";
17
+ import {
18
+ Dialog,
19
+ DialogContent,
20
+ DialogHeader,
21
+ DialogTitle,
22
+ DialogDescription,
23
+ } from "@/components/ui/dialog";
24
+ import { showSuccess, showError } from "@/lib/toast";
25
+ import { wsFetch } from "@/lib/workspace-client";
26
+
27
+ interface Share {
28
+ id: string;
29
+ token: string;
30
+ url: string;
31
+ hasPassword: boolean;
32
+ expiresAt: string | null;
33
+ createdAt: string;
34
+ viewCount: number;
35
+ isExpired: boolean;
36
+ }
37
+
38
+ interface ShareDialogProps {
39
+ open: boolean;
40
+ onOpenChange: (open: boolean) => void;
41
+ filePath: string;
42
+ }
43
+
44
+ export function ShareDialog({
45
+ open,
46
+ onOpenChange,
47
+ filePath,
48
+ }: ShareDialogProps) {
49
+ const [shares, setShares] = useState<Share[]>([]);
50
+ const [loading, setLoading] = useState(true);
51
+ const [creating, setCreating] = useState(false);
52
+
53
+ // Form fields
54
+ const [password, setPassword] = useState("");
55
+ const [enablePassword, setEnablePassword] = useState(false);
56
+ const [enableExpiry, setEnableExpiry] = useState(false);
57
+ const [expiryDays, setExpiryDays] = useState("7");
58
+
59
+ // Copied state per share token
60
+ const [copiedToken, setCopiedToken] = useState<string | null>(null);
61
+
62
+ const loadShares = useCallback(async () => {
63
+ setLoading(true);
64
+ try {
65
+ const res = await wsFetch(
66
+ `/api/share?path=${encodeURIComponent(filePath)}`,
67
+ );
68
+ if (res.ok) {
69
+ const data = await res.json();
70
+ setShares(data.shares ?? []);
71
+ }
72
+ } catch {
73
+ // silently fail
74
+ }
75
+ setLoading(false);
76
+ }, [filePath]);
77
+
78
+ useEffect(() => {
79
+ if (open) void loadShares();
80
+ }, [open, loadShares]);
81
+
82
+ const handleCreate = async () => {
83
+ setCreating(true);
84
+ try {
85
+ const body: Record<string, unknown> = { path: filePath };
86
+ if (enablePassword && password.trim()) {
87
+ body.password = password.trim();
88
+ }
89
+ if (enableExpiry && expiryDays) {
90
+ const d = new Date();
91
+ d.setDate(d.getDate() + Number(expiryDays));
92
+ body.expiresAt = d.toISOString();
93
+ }
94
+ const res = await wsFetch("/api/share", {
95
+ method: "POST",
96
+ headers: { "Content-Type": "application/json" },
97
+ body: JSON.stringify(body),
98
+ });
99
+ if (!res.ok) {
100
+ const e = await res.json();
101
+ showError(e.error ?? "Failed to create share link");
102
+ return;
103
+ }
104
+ showSuccess("Share link created");
105
+ setPassword("");
106
+ setEnablePassword(false);
107
+ setEnableExpiry(false);
108
+ setExpiryDays("7");
109
+ await loadShares();
110
+ } catch {
111
+ showError("Failed to create share link");
112
+ } finally {
113
+ setCreating(false);
114
+ }
115
+ };
116
+
117
+ const handleRevoke = async (token: string) => {
118
+ try {
119
+ const res = await fetch(`/api/share/${token}`, { method: "DELETE" });
120
+ if (!res.ok) {
121
+ showError("Failed to revoke share link");
122
+ return;
123
+ }
124
+ showSuccess("Share link revoked");
125
+ await loadShares();
126
+ } catch {
127
+ showError("Failed to revoke share link");
128
+ }
129
+ };
130
+
131
+ const copyLink = (url: string, token: string) => {
132
+ const fullUrl = `${window.location.origin}${url}`;
133
+ void navigator.clipboard.writeText(fullUrl);
134
+ setCopiedToken(token);
135
+ setTimeout(() => setCopiedToken(null), 2000);
136
+ };
137
+
138
+ return (
139
+ <Dialog open={open} onOpenChange={onOpenChange}>
140
+ <DialogContent className="sm:max-w-md">
141
+ <DialogHeader>
142
+ <DialogTitle>Share document</DialogTitle>
143
+ <DialogDescription>
144
+ Create a public link to share this document as read-only.
145
+ </DialogDescription>
146
+ </DialogHeader>
147
+
148
+ <div className="space-y-4">
149
+ {/* Create new share */}
150
+ <div className="rounded-sm border border-border p-3 space-y-3">
151
+ <h3 className="text-sm font-medium">New share link</h3>
152
+
153
+ {/* Password toggle */}
154
+ <div className="flex items-center justify-between">
155
+ <Label htmlFor="share-pwd" className="text-xs gap-1.5 flex items-center">
156
+ <Lock className="h-3 w-3 text-muted-foreground" />
157
+ Password protect
158
+ </Label>
159
+ <Switch
160
+ id="share-pwd"
161
+ checked={enablePassword}
162
+ onCheckedChange={setEnablePassword}
163
+ />
164
+ </div>
165
+ {enablePassword && (
166
+ <Input
167
+ type="password"
168
+ placeholder="Set a password"
169
+ value={password}
170
+ onChange={(e) => setPassword(e.target.value)}
171
+ />
172
+ )}
173
+
174
+ {/* Expiry toggle */}
175
+ <div className="flex items-center justify-between">
176
+ <Label htmlFor="share-expiry" className="text-xs gap-1.5 flex items-center">
177
+ <Clock className="h-3 w-3 text-muted-foreground" />
178
+ Set expiration
179
+ </Label>
180
+ <Switch
181
+ id="share-expiry"
182
+ checked={enableExpiry}
183
+ onCheckedChange={setEnableExpiry}
184
+ />
185
+ </div>
186
+ {enableExpiry && (
187
+ <div className="flex items-center gap-2">
188
+ <Input
189
+ type="number"
190
+ min={1}
191
+ max={365}
192
+ value={expiryDays}
193
+ onChange={(e) => setExpiryDays(e.target.value)}
194
+ className="w-20"
195
+ />
196
+ <span className="text-xs text-muted-foreground">days</span>
197
+ </div>
198
+ )}
199
+
200
+ <Button
201
+ size="sm"
202
+ className="w-full gap-1.5"
203
+ onClick={handleCreate}
204
+ disabled={creating}
205
+ >
206
+ {creating ? (
207
+ <Loader2 className="h-3.5 w-3.5 animate-spin" />
208
+ ) : (
209
+ <Globe className="h-3.5 w-3.5" />
210
+ )}
211
+ Create link
212
+ </Button>
213
+ </div>
214
+
215
+ {/* Existing shares */}
216
+ <div className="space-y-2">
217
+ <h3 className="text-xs font-medium text-muted-foreground">
218
+ {shares.length > 0
219
+ ? `Active links (${shares.length})`
220
+ : loading
221
+ ? "Loading..."
222
+ : "No active links"}
223
+ </h3>
224
+ {loading && (
225
+ <div className="flex justify-center py-2">
226
+ <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
227
+ </div>
228
+ )}
229
+ {shares.map((share) => (
230
+ <div
231
+ key={share.id}
232
+ className="flex items-center gap-2 rounded-sm border border-border p-2"
233
+ >
234
+ <div className="flex-1 min-w-0">
235
+ <div className="flex items-center gap-1.5">
236
+ <span className="text-xs font-mono truncate">
237
+ {window.location.origin}/s/{share.token}
238
+ </span>
239
+ </div>
240
+ <div className="flex items-center gap-2 mt-0.5">
241
+ {share.hasPassword && (
242
+ <span className="flex items-center gap-0.5 text-[10px] text-muted-foreground">
243
+ <Lock className="h-2.5 w-2.5" />
244
+ pwd
245
+ </span>
246
+ )}
247
+ {share.expiresAt && (
248
+ <span className="flex items-center gap-0.5 text-[10px] text-muted-foreground">
249
+ <Clock className="h-2.5 w-2.5" />
250
+ {formatExpiry(share.expiresAt)}
251
+ </span>
252
+ )}
253
+ <span className="text-[10px] text-muted-foreground">
254
+ {share.viewCount} view
255
+ {share.viewCount !== 1 ? "s" : ""}
256
+ </span>
257
+ {share.isExpired && (
258
+ <span className="text-[10px] text-destructive">
259
+ expired
260
+ </span>
261
+ )}
262
+ </div>
263
+ </div>
264
+ <div className="flex items-center gap-1 shrink-0">
265
+ <Button
266
+ size="sm"
267
+ variant="ghost"
268
+ className="h-7 w-7 p-0"
269
+ title="Copy link"
270
+ onClick={() => copyLink(share.url, share.token)}
271
+ >
272
+ {copiedToken === share.token ? (
273
+ <Check className="h-3.5 w-3.5 text-success" />
274
+ ) : (
275
+ <Copy className="h-3.5 w-3.5" />
276
+ )}
277
+ </Button>
278
+ <Button
279
+ size="sm"
280
+ variant="ghost"
281
+ className="h-7 w-7 p-0 text-destructive hover:text-destructive"
282
+ title="Revoke link"
283
+ onClick={() => handleRevoke(share.token)}
284
+ >
285
+ <Trash2 className="h-3.5 w-3.5" />
286
+ </Button>
287
+ </div>
288
+ </div>
289
+ ))}
290
+ </div>
291
+ </div>
292
+ </DialogContent>
293
+ </Dialog>
294
+ );
295
+ }
296
+
297
+ function formatExpiry(iso: string): string {
298
+ const d = new Date(iso);
299
+ const now = new Date();
300
+ const ms = d.getTime() - now.getTime();
301
+ if (ms <= 0) return "expired";
302
+ const days = Math.floor(ms / 86400000);
303
+ const hours = Math.floor((ms % 86400000) / 3600000);
304
+ if (days > 0) return `${days}d`;
305
+ if (hours > 0) return `${hours}h`;
306
+ return "<1h";
307
+ }