@peaske7/readit 0.1.4 → 0.1.5

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 (320) hide show
  1. package/.agents/skills/remotion-best-practices/SKILL.md +61 -0
  2. package/.agents/skills/remotion-best-practices/rules/3d.md +86 -0
  3. package/.agents/skills/remotion-best-practices/rules/animations.md +27 -0
  4. package/.agents/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +178 -0
  5. package/.agents/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
  6. package/.agents/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
  7. package/.agents/skills/remotion-best-practices/rules/assets.md +78 -0
  8. package/.agents/skills/remotion-best-practices/rules/audio-visualization.md +198 -0
  9. package/.agents/skills/remotion-best-practices/rules/audio.md +169 -0
  10. package/.agents/skills/remotion-best-practices/rules/calculate-metadata.md +134 -0
  11. package/.agents/skills/remotion-best-practices/rules/can-decode.md +75 -0
  12. package/.agents/skills/remotion-best-practices/rules/charts.md +120 -0
  13. package/.agents/skills/remotion-best-practices/rules/compositions.md +154 -0
  14. package/.agents/skills/remotion-best-practices/rules/display-captions.md +184 -0
  15. package/.agents/skills/remotion-best-practices/rules/extract-frames.md +229 -0
  16. package/.agents/skills/remotion-best-practices/rules/ffmpeg.md +38 -0
  17. package/.agents/skills/remotion-best-practices/rules/fonts.md +152 -0
  18. package/.agents/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
  19. package/.agents/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
  20. package/.agents/skills/remotion-best-practices/rules/get-video-duration.md +60 -0
  21. package/.agents/skills/remotion-best-practices/rules/gifs.md +141 -0
  22. package/.agents/skills/remotion-best-practices/rules/images.md +134 -0
  23. package/.agents/skills/remotion-best-practices/rules/import-srt-captions.md +69 -0
  24. package/.agents/skills/remotion-best-practices/rules/light-leaks.md +73 -0
  25. package/.agents/skills/remotion-best-practices/rules/lottie.md +70 -0
  26. package/.agents/skills/remotion-best-practices/rules/maps.md +412 -0
  27. package/.agents/skills/remotion-best-practices/rules/measuring-dom-nodes.md +34 -0
  28. package/.agents/skills/remotion-best-practices/rules/measuring-text.md +140 -0
  29. package/.agents/skills/remotion-best-practices/rules/parameters.md +109 -0
  30. package/.agents/skills/remotion-best-practices/rules/sequencing.md +118 -0
  31. package/.agents/skills/remotion-best-practices/rules/sfx.md +26 -0
  32. package/.agents/skills/remotion-best-practices/rules/subtitles.md +36 -0
  33. package/.agents/skills/remotion-best-practices/rules/tailwind.md +11 -0
  34. package/.agents/skills/remotion-best-practices/rules/text-animations.md +20 -0
  35. package/.agents/skills/remotion-best-practices/rules/timing.md +179 -0
  36. package/.agents/skills/remotion-best-practices/rules/transcribe-captions.md +70 -0
  37. package/.agents/skills/remotion-best-practices/rules/transitions.md +197 -0
  38. package/.agents/skills/remotion-best-practices/rules/transparent-videos.md +106 -0
  39. package/.agents/skills/remotion-best-practices/rules/trimming.md +51 -0
  40. package/.agents/skills/remotion-best-practices/rules/videos.md +171 -0
  41. package/.agents/skills/remotion-best-practices/rules/voiceover.md +99 -0
  42. package/.agents/skills/simple/SKILL.md +52 -0
  43. package/.agents/skills/vercel-react-best-practices/AGENTS.md +3254 -0
  44. package/.agents/skills/vercel-react-best-practices/README.md +123 -0
  45. package/.agents/skills/vercel-react-best-practices/SKILL.md +141 -0
  46. package/.agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  47. package/.agents/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
  48. package/.agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
  49. package/.agents/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
  50. package/.agents/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
  51. package/.agents/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
  52. package/.agents/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
  53. package/.agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
  54. package/.agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
  55. package/.agents/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
  56. package/.agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
  57. package/.agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  58. package/.agents/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
  59. package/.agents/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
  60. package/.agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
  61. package/.agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
  62. package/.agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
  63. package/.agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
  64. package/.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
  65. package/.agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
  66. package/.agents/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
  67. package/.agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
  68. package/.agents/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
  69. package/.agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
  70. package/.agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
  71. package/.agents/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
  72. package/.agents/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
  73. package/.agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
  74. package/.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
  75. package/.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
  76. package/.agents/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
  77. package/.agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  78. package/.agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
  79. package/.agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
  80. package/.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  81. package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  82. package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  83. package/.agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
  84. package/.agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
  85. package/.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
  86. package/.agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  87. package/.agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
  88. package/.agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
  89. package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  90. package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
  91. package/.agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
  92. package/.agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  93. package/.agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  94. package/.agents/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
  95. package/.agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  96. package/.agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
  97. package/.agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  98. package/.agents/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
  99. package/.agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  100. package/.agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
  101. package/.agents/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
  102. package/.agents/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
  103. package/.agents/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
  104. package/.agents/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
  105. package/.agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +142 -0
  106. package/.agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
  107. package/.agents/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
  108. package/.claude/CLAUDE.md +142 -0
  109. package/.claude/commands/review.md +120 -0
  110. package/.claude/commands/sync-docs.md +71 -0
  111. package/.claude/roadmap.md +98 -0
  112. package/.claude/rules/style-guide.md +830 -0
  113. package/.claude/settings.json +18 -0
  114. package/.claude/user-stories.md +248 -0
  115. package/AGENTS.md +64 -0
  116. package/README.md +7 -7
  117. package/biome.json +69 -0
  118. package/bun.lock +1124 -0
  119. package/docs/design.md +563 -0
  120. package/docs/plans/2026-03-13-keyboard-shortcuts-design.md +129 -0
  121. package/docs/plans/2026-03-13-keyboard-shortcuts-plan.md +1471 -0
  122. package/docs/plans/2026-03-13-multi-document-design.md +183 -0
  123. package/docs/plans/2026-03-13-performance-benchmarks-design.md +121 -0
  124. package/e2e/comments.spec.ts +125 -0
  125. package/e2e/document-load.spec.ts +54 -0
  126. package/e2e/export.spec.ts +58 -0
  127. package/e2e/fixtures/sample.html +13 -0
  128. package/e2e/fixtures/sample.md +7 -0
  129. package/e2e/persistence-file.spec.ts +342 -0
  130. package/e2e/utils/cli.ts +84 -0
  131. package/e2e/utils/selection.ts +135 -0
  132. package/{dist/index.html → index.html} +8 -2
  133. package/lefthook.yml +8 -0
  134. package/package.json +17 -39
  135. package/playwright.config.ts +22 -0
  136. package/skills-lock.json +20 -0
  137. package/src/App.tsx +396 -0
  138. package/src/cli/index.ts +328 -0
  139. package/src/components/ActionsMenu.tsx +110 -0
  140. package/src/components/DocumentViewer/CodeBlock.tsx +83 -0
  141. package/src/components/DocumentViewer/DocumentViewer.tsx +257 -0
  142. package/src/components/DocumentViewer/IframeContainer.tsx +251 -0
  143. package/src/components/DocumentViewer/MermaidDiagram.tsx +137 -0
  144. package/src/components/DocumentViewer/index.ts +1 -0
  145. package/src/components/FloatingTOC.tsx +59 -0
  146. package/src/components/Header.tsx +63 -0
  147. package/src/components/InlineEditor.tsx +72 -0
  148. package/src/components/MarginNote.tsx +198 -0
  149. package/src/components/MarginNotes.tsx +50 -0
  150. package/src/components/RawModal.tsx +141 -0
  151. package/src/components/ReanchorConfirm.tsx +33 -0
  152. package/src/components/SettingsModal.tsx +221 -0
  153. package/src/components/ShortcutCapture.tsx +45 -0
  154. package/src/components/ShortcutList.tsx +157 -0
  155. package/src/components/TabBar.tsx +60 -0
  156. package/src/components/TableOfContents.tsx +108 -0
  157. package/src/components/comments/CommentBadge.tsx +43 -0
  158. package/src/components/comments/CommentInput.tsx +119 -0
  159. package/src/components/comments/CommentListItem.tsx +82 -0
  160. package/src/components/comments/CommentManager.tsx +106 -0
  161. package/src/components/comments/CommentMinimap.tsx +62 -0
  162. package/src/components/comments/CommentNav.tsx +104 -0
  163. package/src/components/ui/ActionBar.tsx +16 -0
  164. package/src/components/ui/ActionLink.tsx +32 -0
  165. package/src/components/ui/Button.tsx +55 -0
  166. package/src/components/ui/Dialog.tsx +156 -0
  167. package/src/components/ui/DropdownMenu.tsx +114 -0
  168. package/src/components/ui/SeparatorDot.tsx +9 -0
  169. package/src/components/ui/Text.tsx +54 -0
  170. package/src/contexts/CommentContext.tsx +222 -0
  171. package/src/contexts/LayoutContext.tsx +76 -0
  172. package/src/hooks/useClickOutside.ts +35 -0
  173. package/src/hooks/useClipboard.ts +79 -0
  174. package/src/hooks/useCommentNavigation.ts +130 -0
  175. package/src/hooks/useComments.ts +323 -0
  176. package/src/hooks/useDocument.ts +131 -0
  177. package/src/hooks/useFontPreference.ts +76 -0
  178. package/src/hooks/useHeadings.test.ts +159 -0
  179. package/src/hooks/useHeadings.ts +129 -0
  180. package/src/hooks/useKeybindings.ts +120 -0
  181. package/src/hooks/useKeyboardShortcuts.ts +63 -0
  182. package/src/hooks/useLayoutMode.ts +44 -0
  183. package/src/hooks/useReanchorMode.ts +33 -0
  184. package/src/hooks/useScrollMetrics.ts +56 -0
  185. package/src/hooks/useScrollSpy.ts +81 -0
  186. package/src/hooks/useTextSelection.ts +123 -0
  187. package/src/hooks/useThemePreference.ts +66 -0
  188. package/src/index.css +823 -0
  189. package/src/lib/__fixtures__/bench-data.ts +167 -0
  190. package/src/lib/anchor.bench.ts +112 -0
  191. package/src/lib/anchor.test.ts +531 -0
  192. package/src/lib/anchor.ts +465 -0
  193. package/src/lib/comment-storage.bench.ts +63 -0
  194. package/src/lib/comment-storage.test.ts +624 -0
  195. package/src/lib/comment-storage.ts +263 -0
  196. package/src/lib/context.bench.ts +41 -0
  197. package/src/lib/context.test.ts +224 -0
  198. package/src/lib/context.ts +193 -0
  199. package/src/lib/export.bench.ts +35 -0
  200. package/src/lib/export.ts +43 -0
  201. package/src/lib/highlight/colors.ts +37 -0
  202. package/src/lib/highlight/core.test.ts +98 -0
  203. package/src/lib/highlight/core.ts +54 -0
  204. package/src/lib/highlight/dom.ts +342 -0
  205. package/src/lib/highlight/highlighter.ts +427 -0
  206. package/src/lib/highlight/index.ts +23 -0
  207. package/src/lib/highlight/script-builder.ts +485 -0
  208. package/src/lib/highlight/types.ts +57 -0
  209. package/src/lib/html-processor.test.tsx +170 -0
  210. package/src/lib/html-processor.tsx +95 -0
  211. package/src/lib/layout-constants.ts +12 -0
  212. package/src/lib/margin-layout.bench.ts +28 -0
  213. package/src/lib/margin-layout.ts +100 -0
  214. package/src/lib/scroll.test.ts +118 -0
  215. package/src/lib/scroll.ts +47 -0
  216. package/src/lib/shortcut-registry.test.ts +173 -0
  217. package/src/lib/shortcut-registry.ts +209 -0
  218. package/src/lib/utils.test.ts +110 -0
  219. package/src/lib/utils.ts +50 -0
  220. package/src/main.tsx +10 -0
  221. package/src/server/index.ts +766 -0
  222. package/src/store/index.test.ts +220 -0
  223. package/src/store/index.ts +234 -0
  224. package/src/test-setup.ts +1 -0
  225. package/src/types/index.ts +115 -0
  226. package/test.md +74 -0
  227. package/tsconfig.cli.json +12 -0
  228. package/tsconfig.json +20 -0
  229. package/vite.config.ts +19 -0
  230. package/vitest.config.ts +15 -0
  231. package/dist/assets/_basePickBy-hOr-yGsE.js +0 -1
  232. package/dist/assets/_baseUniq-b7bzdUSn.js +0 -1
  233. package/dist/assets/arc-D65wG9gm.js +0 -1
  234. package/dist/assets/architecture-PBZL5I3N-DBa6CAv_.js +0 -1
  235. package/dist/assets/architectureDiagram-2XIMDMQ5-Djwpsh98.js +0 -36
  236. package/dist/assets/array-DOVTz2Mq.js +0 -1
  237. package/dist/assets/blockDiagram-WCTKOSBZ-BdW5TTxj.js +0 -132
  238. package/dist/assets/c4Diagram-IC4MRINW-DTmkHEXu.js +0 -10
  239. package/dist/assets/channel-B3MUFipN.js +0 -1
  240. package/dist/assets/chunk-4BX2VUAB-DEqzsvDc.js +0 -1
  241. package/dist/assets/chunk-55IACEB6-BzVuSUV8.js +0 -1
  242. package/dist/assets/chunk-7E7YKBS2-CZ8IcA4c.js +0 -1
  243. package/dist/assets/chunk-7R4GIKGN-CWVVC8HX.js +0 -79
  244. package/dist/assets/chunk-C72U2L5F-B1Tso5TH.js +0 -1
  245. package/dist/assets/chunk-EGIJ26TM-Cx_7CFik.js +0 -1
  246. package/dist/assets/chunk-FMBD7UC4-Cfk_iGhv.js +0 -15
  247. package/dist/assets/chunk-GEFDOKGD-C_5hRbJt.js +0 -2
  248. package/dist/assets/chunk-GLR3WWYH-CkY7IyBj.js +0 -2
  249. package/dist/assets/chunk-HHEYEP7N-B0I4X5cr.js +0 -1
  250. package/dist/assets/chunk-JSJVCQXG-CAjwlVLg.js +0 -1
  251. package/dist/assets/chunk-KX2RTZJC-DWqnZZ02.js +0 -1
  252. package/dist/assets/chunk-KYZI473N-gjRVhJgJ.js +0 -53
  253. package/dist/assets/chunk-L3YUKLVL-D7C9GuxL.js +0 -1
  254. package/dist/assets/chunk-MX3YWQON-i-77iuVj.js +0 -1
  255. package/dist/assets/chunk-NQ4KR5QH-B22Pvemm.js +0 -220
  256. package/dist/assets/chunk-O4XLMI2P-ZQd5L6ZD.js +0 -7
  257. package/dist/assets/chunk-OZEHJAEY-BaPKTELw.js +0 -1
  258. package/dist/assets/chunk-PQ6SQG4A-DqE1eupT.js +0 -1
  259. package/dist/assets/chunk-PU5JKC2W-BTqWqedh.js +0 -70
  260. package/dist/assets/chunk-QZHKN3VN-Nm9TvMss.js +0 -1
  261. package/dist/assets/chunk-R5LLSJPH-DkiNs1dN.js +0 -1
  262. package/dist/assets/chunk-WL4C6EOR-CioD2fv2.js +0 -189
  263. package/dist/assets/chunk-XIRO2GV7-B4GGQONY.js +0 -1
  264. package/dist/assets/chunk-XPW4576I-C0IbbQos.js +0 -32
  265. package/dist/assets/chunk-XZSTWKYB-DMOqFWmT.js +0 -94
  266. package/dist/assets/chunk-YBOYWFTD-CoeQgeVY.js +0 -1
  267. package/dist/assets/classDiagram-VBA2DB6C-DV9ltQ7h.js +0 -1
  268. package/dist/assets/classDiagram-v2-RAHNMMFH-C6nD9wmM.js +0 -1
  269. package/dist/assets/clone-DuY6BQEm.js +0 -1
  270. package/dist/assets/cose-bilkent-S5V4N54A-B6FexK6p.js +0 -1
  271. package/dist/assets/cytoscape.esm-DoTFyJaN.js +0 -321
  272. package/dist/assets/dagre-CCcocoCU.js +0 -1
  273. package/dist/assets/dagre-KLK3FWXG-DIELowj9.js +0 -4
  274. package/dist/assets/defaultLocale-Ck2Xxk-C.js +0 -1
  275. package/dist/assets/diagram-E7M64L7V-D1mm0PoO.js +0 -24
  276. package/dist/assets/diagram-IFDJBPK2-7DVjly8y.js +0 -43
  277. package/dist/assets/diagram-P4PSJMXO-jO7pfyMb.js +0 -24
  278. package/dist/assets/dist-BywRdrPx.js +0 -1
  279. package/dist/assets/erDiagram-INFDFZHY-DSRxlRFy.js +0 -70
  280. package/dist/assets/flowDiagram-PKNHOUZH-CgKzzNdR.js +0 -162
  281. package/dist/assets/ganttDiagram-A5KZAMGK-CtsE7Y4E.js +0 -292
  282. package/dist/assets/gitGraph-HDMCJU4V-BU9uhwtz.js +0 -1
  283. package/dist/assets/gitGraphDiagram-K3NZZRJ6-DOU8RGdw.js +0 -65
  284. package/dist/assets/graphlib-WkJoBgka.js +0 -1
  285. package/dist/assets/index-CKVArt9D.js +0 -562
  286. package/dist/assets/index-DzRKJazf.css +0 -2
  287. package/dist/assets/info-3K5VOQVL-CPpvM-SG.js +0 -1
  288. package/dist/assets/infoDiagram-LFFYTUFH-VKLs5DsF.js +0 -2
  289. package/dist/assets/init-Bft5Ffpj.js +0 -1
  290. package/dist/assets/isArrayLikeObject-icl0H0jo.js +0 -1
  291. package/dist/assets/isEmpty-Du8sNmkE.js +0 -1
  292. package/dist/assets/ishikawaDiagram-PHBUUO56-CsWvEjux.js +0 -70
  293. package/dist/assets/journeyDiagram-4ABVD52K-BzJGTdIT.js +0 -139
  294. package/dist/assets/kanban-definition-K7BYSVSG-B_9ClJ1A.js +0 -89
  295. package/dist/assets/katex-BJrMXEjr.js +0 -261
  296. package/dist/assets/line-CC_tDGId.js +0 -1
  297. package/dist/assets/linear-Cts_d04Y.js +0 -1
  298. package/dist/assets/math-CNhlSIO3.js +0 -1
  299. package/dist/assets/mermaid-parser.core-Vb9KKv1R.js +0 -4
  300. package/dist/assets/mermaid.core-C_7xsp3d.js +0 -11
  301. package/dist/assets/mindmap-definition-YRQLILUH-BWmfy5wB.js +0 -68
  302. package/dist/assets/ordinal-DIg8h6NI.js +0 -1
  303. package/dist/assets/packet-RMMSAZCW-Q-WG6o3b.js +0 -1
  304. package/dist/assets/path-DfRbCp9y.js +0 -1
  305. package/dist/assets/pie-UPGHQEXC-Cwi2tLlt.js +0 -1
  306. package/dist/assets/pieDiagram-SKSYHLDU-Dyf3X_in.js +0 -30
  307. package/dist/assets/quadrantDiagram-337W2JSQ-B5_5m61Q.js +0 -7
  308. package/dist/assets/radar-KQ55EAFF-Dtw2VzxY.js +0 -1
  309. package/dist/assets/requirementDiagram-Z7DCOOCP-BSERBnlW.js +0 -73
  310. package/dist/assets/rough.esm-KjoEK0it.js +0 -1
  311. package/dist/assets/sankeyDiagram-WA2Y5GQK-CMcEY8Cz.js +0 -10
  312. package/dist/assets/sequenceDiagram-2WXFIKYE-D28qcXwC.js +0 -145
  313. package/dist/assets/src-C8kkzlHX.js +0 -1
  314. package/dist/assets/stateDiagram-RAJIS63D-7oVrCmRl.js +0 -1
  315. package/dist/assets/stateDiagram-v2-FVOUBMTO-DtFptQAd.js +0 -1
  316. package/dist/assets/timeline-definition-YZTLITO2-rbCfBEvG.js +0 -61
  317. package/dist/assets/treemap-KZPCXAKY-BlRvF0um.js +0 -1
  318. package/dist/assets/vennDiagram-LZ73GAT5-DBit3zWa.js +0 -34
  319. package/dist/assets/xychartDiagram-JWTSCODW-BVYXv51y.js +0 -7
  320. package/dist/index.js +0 -1040
@@ -0,0 +1,79 @@
1
+ import { useCallback } from "react";
2
+ import { toast } from "sonner";
3
+ import { extractContext, formatForLLM } from "../lib/context";
4
+ import {
5
+ exportCommentsAsJson,
6
+ generatePrompt,
7
+ generateRawText,
8
+ } from "../lib/export";
9
+ import { truncate } from "../lib/utils";
10
+ import type { Comment, Document, Selection } from "../types";
11
+
12
+ interface UseClipboardParams {
13
+ comments: Comment[];
14
+ document: Document | undefined;
15
+ selection: Selection | undefined;
16
+ clearSelection: () => void;
17
+ }
18
+
19
+ export function useClipboard({
20
+ comments,
21
+ document,
22
+ selection,
23
+ clearSelection,
24
+ }: UseClipboardParams) {
25
+ // Export handlers
26
+ const copyAll = useCallback(() => {
27
+ if (!document) return;
28
+ const prompt = generatePrompt(comments, document.fileName);
29
+ navigator.clipboard.writeText(prompt);
30
+ toast.success("Copied all comments");
31
+ }, [comments, document]);
32
+
33
+ const copyAllRaw = useCallback(() => {
34
+ if (!document) return;
35
+ const raw = generateRawText(comments);
36
+ navigator.clipboard.writeText(raw);
37
+ toast.success("Copied all comments as raw text");
38
+ }, [comments, document]);
39
+
40
+ const exportJson = useCallback(() => {
41
+ if (!document) return;
42
+ exportCommentsAsJson(comments, document);
43
+ }, [comments, document]);
44
+
45
+ // Selection copy handlers
46
+ const copySelectionRaw = useCallback(() => {
47
+ if (!selection) return;
48
+
49
+ navigator.clipboard.writeText(selection.text);
50
+ toast.success(`Copied: "${truncate(selection.text)}"`);
51
+ clearSelection();
52
+ }, [selection, clearSelection]);
53
+
54
+ const copySelectionForLLM = useCallback(() => {
55
+ if (!selection || !document) return;
56
+
57
+ const context = extractContext({
58
+ content: document.content,
59
+ startOffset: selection.startOffset,
60
+ endOffset: selection.endOffset,
61
+ });
62
+ const formatted = formatForLLM({
63
+ context,
64
+ fileName: document.fileName,
65
+ });
66
+
67
+ navigator.clipboard.writeText(formatted);
68
+ toast.success(`Copied for LLM: "${truncate(selection.text)}"`);
69
+ clearSelection();
70
+ }, [selection, document, clearSelection]);
71
+
72
+ return {
73
+ copyAll,
74
+ copyAllRaw,
75
+ exportJson,
76
+ copySelectionRaw,
77
+ copySelectionForLLM,
78
+ };
79
+ }
@@ -0,0 +1,130 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { appStore, useAppStore } from "../store";
3
+ import type { Comment } from "../types";
4
+
5
+ interface UseCommentNavigationResult {
6
+ currentIndex: number;
7
+ hoveredCommentId: string | undefined;
8
+ setHoveredCommentId: (id: string | undefined) => void;
9
+ navigateToComment: (commentId: string) => void;
10
+ navigatePrevious: () => void;
11
+ navigateNext: () => void;
12
+ }
13
+
14
+ /**
15
+ * Manage comment navigation with cycling, keyboard shortcuts, and scroll-to-comment.
16
+ * Handles Alt+↑/↓ keyboard navigation.
17
+ */
18
+ export function useCommentNavigation(
19
+ sortedComments: Comment[],
20
+ ): UseCommentNavigationResult {
21
+ const [currentIndex, setCurrentIndex] = useState(0);
22
+ const hoveredCommentId = useAppStore(
23
+ (s) => s.getActiveDocumentState()?.hoveredCommentId,
24
+ );
25
+ const hoverTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(
26
+ undefined,
27
+ );
28
+
29
+ // Keep a ref to sortedComments so navigation callbacks stay stable
30
+ const sortedRef = useRef(sortedComments);
31
+ sortedRef.current = sortedComments;
32
+
33
+ // Cleanup hover timeout on unmount
34
+ useEffect(() => {
35
+ return () => clearTimeout(hoverTimeoutRef.current);
36
+ }, []);
37
+
38
+ // Clamp index when comments are removed (derived during render, no effect needed)
39
+ const clampedIndex =
40
+ sortedComments.length === 0
41
+ ? 0
42
+ : Math.min(currentIndex, sortedComments.length - 1);
43
+ if (clampedIndex !== currentIndex) {
44
+ setCurrentIndex(clampedIndex);
45
+ }
46
+
47
+ // Update DOM data-focused attributes imperatively
48
+ const updateFocusedMarks = useCallback((commentId: string | undefined) => {
49
+ const marks = window.document.querySelectorAll("mark[data-comment-id]");
50
+ for (const mark of marks) {
51
+ const id = mark.getAttribute("data-comment-id");
52
+ if (id === commentId) {
53
+ mark.setAttribute("data-focused", "true");
54
+ } else {
55
+ mark.removeAttribute("data-focused");
56
+ }
57
+ }
58
+ }, []);
59
+
60
+ const setHoveredCommentId = useCallback(
61
+ (id: string | undefined) => {
62
+ appStore.getState().setHoveredCommentId(id);
63
+ updateFocusedMarks(id);
64
+ },
65
+ [updateFocusedMarks],
66
+ );
67
+
68
+ // Navigate to a comment by scrolling its highlight into view
69
+ const navigateToComment = useCallback(
70
+ (commentId: string) => {
71
+ const selector = `mark[data-comment-id="${commentId}"]`;
72
+
73
+ const scrollAndHighlight = (element: Element) => {
74
+ element.scrollIntoView({ behavior: "smooth", block: "center" });
75
+ setHoveredCommentId(commentId);
76
+ clearTimeout(hoverTimeoutRef.current);
77
+ hoverTimeoutRef.current = setTimeout(
78
+ () => setHoveredCommentId(undefined),
79
+ 1500,
80
+ );
81
+ };
82
+
83
+ // Try main document first (for markdown)
84
+ const mainHighlight = document.querySelector(selector);
85
+ if (mainHighlight) {
86
+ scrollAndHighlight(mainHighlight);
87
+ return;
88
+ }
89
+
90
+ // Try inside iframe (for HTML content)
91
+ const iframe = document.querySelector("iframe");
92
+ const iframeHighlight = iframe?.contentDocument?.querySelector(selector);
93
+ if (iframeHighlight) {
94
+ scrollAndHighlight(iframeHighlight);
95
+ }
96
+ },
97
+ [setHoveredCommentId],
98
+ );
99
+
100
+ // Navigate to previous comment (cycles to last when at first)
101
+ const navigatePrevious = useCallback(() => {
102
+ const sc = sortedRef.current;
103
+ if (sc.length === 0) return;
104
+ setCurrentIndex((prev) => {
105
+ const newIndex = prev === 0 ? sc.length - 1 : prev - 1;
106
+ navigateToComment(sc[newIndex].id);
107
+ return newIndex;
108
+ });
109
+ }, [navigateToComment]);
110
+
111
+ // Navigate to next comment (cycles to first when at last)
112
+ const navigateNext = useCallback(() => {
113
+ const sc = sortedRef.current;
114
+ if (sc.length === 0) return;
115
+ setCurrentIndex((prev) => {
116
+ const newIndex = prev === sc.length - 1 ? 0 : prev + 1;
117
+ navigateToComment(sc[newIndex].id);
118
+ return newIndex;
119
+ });
120
+ }, [navigateToComment]);
121
+
122
+ return {
123
+ currentIndex: clampedIndex,
124
+ hoveredCommentId,
125
+ setHoveredCommentId,
126
+ navigateToComment,
127
+ navigatePrevious,
128
+ navigateNext,
129
+ };
130
+ }
@@ -0,0 +1,323 @@
1
+ import { useCallback, useEffect, useRef } from "react";
2
+ import { appStore, useAppStore } from "../store";
3
+ import { AnchorConfidences, type Comment } from "../types";
4
+
5
+ interface UseCommentsOptions {
6
+ clean?: boolean;
7
+ }
8
+
9
+ interface UseCommentsResult {
10
+ comments: Comment[];
11
+ error?: string;
12
+ addComment: (
13
+ selectedText: string,
14
+ comment: string,
15
+ startOffset: number,
16
+ endOffset: number,
17
+ ) => void;
18
+ deleteComment: (id: string) => void;
19
+ deleteAll: () => void;
20
+ editComment: (id: string, newText: string) => void;
21
+ reanchorComment: (
22
+ id: string,
23
+ selectedText: string,
24
+ startOffset: number,
25
+ endOffset: number,
26
+ ) => void;
27
+ }
28
+
29
+ /**
30
+ * Hook for managing comments with optimistic updates.
31
+ * State lives in the Zustand store; this hook coordinates API mutations.
32
+ */
33
+ export function useComments(
34
+ filePath: string | null,
35
+ options: UseCommentsOptions = {},
36
+ ): UseCommentsResult {
37
+ const { clean = false } = options;
38
+
39
+ // Read comments and error from the store
40
+ const comments = useAppStore(
41
+ (s) => s.documents.get(filePath ?? "")?.comments ?? [],
42
+ );
43
+ const error = useAppStore(
44
+ (s) => s.documents.get(filePath ?? "")?.commentsError ?? undefined,
45
+ );
46
+
47
+ // Track pending operations for rollback on error
48
+ const pendingOperations = useRef<Map<string, Comment[]>>(new Map());
49
+
50
+ // Capture filePath at call time for stable closures
51
+ const filePathRef = useRef(filePath);
52
+ filePathRef.current = filePath;
53
+
54
+ /**
55
+ * Execute an optimistic mutation with automatic rollback on error.
56
+ */
57
+ const executeMutation = useCallback(
58
+ async <T>({
59
+ operationId,
60
+ optimisticUpdate,
61
+ apiCall,
62
+ onSuccess,
63
+ errorMessage,
64
+ }: {
65
+ operationId: string;
66
+ optimisticUpdate: (prev: Comment[]) => Comment[];
67
+ apiCall: () => Promise<T>;
68
+ onSuccess?: (result: T, prev: Comment[]) => Comment[];
69
+ errorMessage: string;
70
+ }) => {
71
+ const fp = filePathRef.current;
72
+ if (!fp) return;
73
+
74
+ // Read current comments from store for rollback
75
+ const currentDocState = appStore.getState().documents.get(fp);
76
+ const previousComments = [...(currentDocState?.comments ?? [])];
77
+ pendingOperations.current.set(operationId, previousComments);
78
+
79
+ // Apply optimistic update
80
+ appStore.getState().setComments(optimisticUpdate(previousComments), fp);
81
+ appStore.getState().setCommentsError(null, fp);
82
+
83
+ try {
84
+ const result = await apiCall();
85
+
86
+ // Apply server response transformation if provided
87
+ if (onSuccess) {
88
+ const current = appStore.getState().documents.get(fp)?.comments ?? [];
89
+ appStore.getState().setComments(onSuccess(result, current), fp);
90
+ }
91
+ } catch (err) {
92
+ console.error(`${errorMessage}:`, err);
93
+ appStore
94
+ .getState()
95
+ .setCommentsError(
96
+ err instanceof Error ? err.message : errorMessage,
97
+ fp,
98
+ );
99
+
100
+ // Rollback on error
101
+ const rollback = pendingOperations.current.get(operationId);
102
+ if (rollback) {
103
+ appStore.getState().setComments(rollback, fp);
104
+ }
105
+ } finally {
106
+ pendingOperations.current.delete(operationId);
107
+ }
108
+ },
109
+ [],
110
+ );
111
+
112
+ // Build path-scoped API URL
113
+ const pathQuery = useCallback((base: string) => {
114
+ const fp = filePathRef.current;
115
+ if (!fp) return base;
116
+ return `${base}?path=${encodeURIComponent(fp)}`;
117
+ }, []);
118
+
119
+ // Load comments from API
120
+ useEffect(() => {
121
+ if (!filePath) return;
122
+
123
+ const loadComments = async () => {
124
+ appStore.getState().setCommentsError(null, filePath);
125
+ const query = `?path=${encodeURIComponent(filePath)}`;
126
+
127
+ try {
128
+ // If clean flag is set, clear comments first
129
+ if (clean) {
130
+ await fetch(`/api/comments${query}`, { method: "DELETE" });
131
+ appStore.getState().setComments([], filePath);
132
+ return;
133
+ }
134
+
135
+ const response = await fetch(`/api/comments${query}`);
136
+ if (!response.ok) {
137
+ throw new Error(`Failed to load comments: ${response.statusText}`);
138
+ }
139
+
140
+ const data = await response.json();
141
+ appStore.getState().setComments(data.comments || [], filePath);
142
+ } catch (err) {
143
+ console.error("Failed to load comments:", err);
144
+ appStore
145
+ .getState()
146
+ .setCommentsError(
147
+ err instanceof Error ? err.message : "Failed to load comments",
148
+ filePath,
149
+ );
150
+ appStore.getState().setComments([], filePath);
151
+ }
152
+ };
153
+
154
+ loadComments();
155
+ }, [filePath, clean]);
156
+
157
+ const addComment = useCallback(
158
+ (
159
+ selectedText: string,
160
+ commentText: string,
161
+ startOffset: number,
162
+ endOffset: number,
163
+ ) => {
164
+ const tempId = `temp-${crypto.randomUUID()}`;
165
+ const optimisticComment: Comment = {
166
+ id: tempId,
167
+ selectedText,
168
+ comment: commentText.trim(),
169
+ createdAt: new Date().toISOString(),
170
+ startOffset,
171
+ endOffset,
172
+ };
173
+
174
+ executeMutation({
175
+ operationId: tempId,
176
+ optimisticUpdate: (prev) => [...prev, optimisticComment],
177
+ apiCall: async () => {
178
+ const response = await fetch(pathQuery("/api/comments"), {
179
+ method: "POST",
180
+ headers: { "Content-Type": "application/json" },
181
+ body: JSON.stringify({
182
+ selectedText,
183
+ comment: commentText.trim(),
184
+ startOffset,
185
+ endOffset,
186
+ }),
187
+ });
188
+
189
+ if (!response.ok) {
190
+ throw new Error(`Failed to add comment: ${response.statusText}`);
191
+ }
192
+
193
+ return response.json();
194
+ },
195
+ onSuccess: (data, prev) =>
196
+ prev.map((c) => (c.id === tempId ? data.comment : c)),
197
+ errorMessage: "Failed to add comment",
198
+ });
199
+ },
200
+ [executeMutation, pathQuery],
201
+ );
202
+
203
+ const deleteComment = useCallback(
204
+ (id: string) => {
205
+ executeMutation({
206
+ operationId: `delete-${id}`,
207
+ optimisticUpdate: (prev) => prev.filter((c) => c.id !== id),
208
+ apiCall: async () => {
209
+ const response = await fetch(pathQuery(`/api/comments/${id}`), {
210
+ method: "DELETE",
211
+ });
212
+
213
+ if (!response.ok) {
214
+ throw new Error(`Failed to delete comment: ${response.statusText}`);
215
+ }
216
+ },
217
+ errorMessage: "Failed to delete comment",
218
+ });
219
+ },
220
+ [executeMutation, pathQuery],
221
+ );
222
+
223
+ const deleteAll = useCallback(() => {
224
+ executeMutation({
225
+ operationId: "delete-all",
226
+ optimisticUpdate: () => [],
227
+ apiCall: async () => {
228
+ const response = await fetch(pathQuery("/api/comments"), {
229
+ method: "DELETE",
230
+ });
231
+ if (!response.ok) {
232
+ throw new Error(
233
+ `Failed to delete all comments: ${response.statusText}`,
234
+ );
235
+ }
236
+ },
237
+ errorMessage: "Failed to delete all comments",
238
+ });
239
+ }, [executeMutation, pathQuery]);
240
+
241
+ const editComment = useCallback(
242
+ (id: string, newText: string) => {
243
+ const trimmed = newText.trim();
244
+ if (!trimmed) return;
245
+
246
+ executeMutation({
247
+ operationId: `edit-${id}`,
248
+ optimisticUpdate: (prev) =>
249
+ prev.map((c) => (c.id === id ? { ...c, comment: trimmed } : c)),
250
+ apiCall: async () => {
251
+ const response = await fetch(pathQuery(`/api/comments/${id}`), {
252
+ method: "PUT",
253
+ headers: { "Content-Type": "application/json" },
254
+ body: JSON.stringify({ comment: trimmed }),
255
+ });
256
+
257
+ if (!response.ok) {
258
+ throw new Error(`Failed to update comment: ${response.statusText}`);
259
+ }
260
+ },
261
+ errorMessage: "Failed to edit comment",
262
+ });
263
+ },
264
+ [executeMutation, pathQuery],
265
+ );
266
+
267
+ const reanchorComment = useCallback(
268
+ (
269
+ id: string,
270
+ selectedText: string,
271
+ startOffset: number,
272
+ endOffset: number,
273
+ ) => {
274
+ executeMutation({
275
+ operationId: `reanchor-${id}`,
276
+ optimisticUpdate: (prev) =>
277
+ prev.map((c) =>
278
+ c.id === id
279
+ ? {
280
+ ...c,
281
+ selectedText,
282
+ startOffset,
283
+ endOffset,
284
+ anchorConfidence: AnchorConfidences.EXACT,
285
+ }
286
+ : c,
287
+ ),
288
+ apiCall: async () => {
289
+ const response = await fetch(
290
+ pathQuery(`/api/comments/${id}/reanchor`),
291
+ {
292
+ method: "PUT",
293
+ headers: { "Content-Type": "application/json" },
294
+ body: JSON.stringify({ selectedText, startOffset, endOffset }),
295
+ },
296
+ );
297
+
298
+ if (!response.ok) {
299
+ throw new Error(
300
+ `Failed to re-anchor comment: ${response.statusText}`,
301
+ );
302
+ }
303
+
304
+ return response.json();
305
+ },
306
+ onSuccess: (data, prev) =>
307
+ prev.map((c) => (c.id === id ? data.comment : c)),
308
+ errorMessage: "Failed to re-anchor comment",
309
+ });
310
+ },
311
+ [executeMutation, pathQuery],
312
+ );
313
+
314
+ return {
315
+ comments,
316
+ error,
317
+ addComment,
318
+ deleteComment,
319
+ deleteAll,
320
+ editComment,
321
+ reanchorComment,
322
+ };
323
+ }
@@ -0,0 +1,131 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import { toast } from "sonner";
3
+ import { appStore, useAppStore } from "../store";
4
+ import type { Document } from "../types";
5
+
6
+ interface UseDocumentResult {
7
+ document: Document | null;
8
+ error: string | null;
9
+ isInitialized: boolean;
10
+ reload: () => Promise<void>;
11
+ }
12
+
13
+ /**
14
+ * Manage multi-document loading, lazy content fetching, and live reloading.
15
+ *
16
+ * On mount: fetches the document list from `/api/documents` and opens all
17
+ * files in the store. Content is loaded lazily when a tab becomes active.
18
+ * SSE events trigger content updates for already-loaded documents.
19
+ */
20
+ export function useDocument(): UseDocumentResult {
21
+ const [error, setError] = useState<string | null>(null);
22
+ const [isInitialized, setIsInitialized] = useState(false);
23
+
24
+ const activeDocumentPath = useAppStore((s) => s.activeDocumentPath);
25
+
26
+ // Active document — null until content is loaded
27
+ const document = useAppStore((s) => {
28
+ const ds = s.getActiveDocumentState();
29
+ if (!ds || !ds.document.content) return null;
30
+ return ds.document;
31
+ });
32
+
33
+ // Fetch document list on mount, populate store
34
+ useEffect(() => {
35
+ async function init() {
36
+ try {
37
+ const res = await fetch("/api/documents");
38
+ if (!res.ok) throw new Error(`Server error: ${res.status}`);
39
+ const data = await res.json();
40
+
41
+ const clean = data.clean || false;
42
+ for (const file of data.files) {
43
+ appStore.getState().openDocument({
44
+ content: "", // Content loaded lazily on tab activation
45
+ type: file.type,
46
+ filePath: file.path,
47
+ fileName: file.fileName,
48
+ clean,
49
+ });
50
+ }
51
+ } catch (err) {
52
+ setError(
53
+ err instanceof Error ? err.message : "Failed to load documents",
54
+ );
55
+ } finally {
56
+ setIsInitialized(true);
57
+ }
58
+ }
59
+ init();
60
+ }, []);
61
+
62
+ // Load content when active document changes and has no content yet
63
+ useEffect(() => {
64
+ if (!activeDocumentPath) return;
65
+ const state = appStore.getState().documents.get(activeDocumentPath);
66
+ if (!state || state.document.content) return;
67
+
68
+ async function loadContent() {
69
+ try {
70
+ const res = await fetch(
71
+ `/api/document?path=${encodeURIComponent(activeDocumentPath!)}`,
72
+ );
73
+ if (!res.ok) throw new Error(`Server error: ${res.status}`);
74
+ const data = await res.json();
75
+ appStore
76
+ .getState()
77
+ .updateDocumentContent(data.content, activeDocumentPath!);
78
+ } catch (err) {
79
+ setError(
80
+ err instanceof Error ? err.message : "Failed to load document",
81
+ );
82
+ }
83
+ }
84
+ loadContent();
85
+ }, [activeDocumentPath]);
86
+
87
+ // SSE: listen for file updates, reload content for already-loaded documents
88
+ useEffect(() => {
89
+ const eventSource = new EventSource("/api/document/stream");
90
+ eventSource.onmessage = async (e) => {
91
+ try {
92
+ const data = JSON.parse(e.data);
93
+ if (data.type === "update" && data.path) {
94
+ // Only reload if content was previously loaded
95
+ const state = appStore.getState().documents.get(data.path);
96
+ if (!state || !state.document.content) return;
97
+
98
+ const res = await fetch(
99
+ `/api/document?path=${encodeURIComponent(data.path)}`,
100
+ );
101
+ if (res.ok) {
102
+ const doc = await res.json();
103
+ appStore.getState().updateDocumentContent(doc.content, data.path);
104
+ }
105
+ }
106
+ } catch {
107
+ // Ignore non-JSON messages ("connected", "ping")
108
+ }
109
+ };
110
+ return () => eventSource.close();
111
+ }, []);
112
+
113
+ const reload = useCallback(async () => {
114
+ if (!activeDocumentPath) return;
115
+ try {
116
+ const res = await fetch(
117
+ `/api/document?path=${encodeURIComponent(activeDocumentPath)}`,
118
+ );
119
+ if (!res.ok) throw new Error(`Server error: ${res.status}`);
120
+ const data = await res.json();
121
+ appStore
122
+ .getState()
123
+ .updateDocumentContent(data.content, activeDocumentPath);
124
+ toast.success("Document reloaded");
125
+ } catch (err) {
126
+ toast.error(err instanceof Error ? err.message : "Failed to reload");
127
+ }
128
+ }, [activeDocumentPath]);
129
+
130
+ return { document, error, isInitialized, reload };
131
+ }