@peaske7/readit 0.1.4 → 0.1.6

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 (322) 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-client-mode-design.md +86 -0
  121. package/docs/plans/2026-03-13-client-mode-plan.md +605 -0
  122. package/docs/plans/2026-03-13-keyboard-shortcuts-design.md +129 -0
  123. package/docs/plans/2026-03-13-keyboard-shortcuts-plan.md +1471 -0
  124. package/docs/plans/2026-03-13-multi-document-design.md +183 -0
  125. package/docs/plans/2026-03-13-performance-benchmarks-design.md +121 -0
  126. package/e2e/comments.spec.ts +125 -0
  127. package/e2e/document-load.spec.ts +54 -0
  128. package/e2e/export.spec.ts +58 -0
  129. package/e2e/fixtures/sample.html +13 -0
  130. package/e2e/fixtures/sample.md +7 -0
  131. package/e2e/persistence-file.spec.ts +342 -0
  132. package/e2e/utils/cli.ts +84 -0
  133. package/e2e/utils/selection.ts +135 -0
  134. package/{dist/index.html → index.html} +8 -2
  135. package/lefthook.yml +8 -0
  136. package/package.json +17 -39
  137. package/playwright.config.ts +22 -0
  138. package/skills-lock.json +20 -0
  139. package/src/App.tsx +396 -0
  140. package/src/cli/index.ts +467 -0
  141. package/src/components/ActionsMenu.tsx +110 -0
  142. package/src/components/DocumentViewer/CodeBlock.tsx +83 -0
  143. package/src/components/DocumentViewer/DocumentViewer.tsx +257 -0
  144. package/src/components/DocumentViewer/IframeContainer.tsx +251 -0
  145. package/src/components/DocumentViewer/MermaidDiagram.tsx +137 -0
  146. package/src/components/DocumentViewer/index.ts +1 -0
  147. package/src/components/FloatingTOC.tsx +59 -0
  148. package/src/components/Header.tsx +63 -0
  149. package/src/components/InlineEditor.tsx +72 -0
  150. package/src/components/MarginNote.tsx +198 -0
  151. package/src/components/MarginNotes.tsx +50 -0
  152. package/src/components/RawModal.tsx +141 -0
  153. package/src/components/ReanchorConfirm.tsx +33 -0
  154. package/src/components/SettingsModal.tsx +221 -0
  155. package/src/components/ShortcutCapture.tsx +45 -0
  156. package/src/components/ShortcutList.tsx +157 -0
  157. package/src/components/TabBar.tsx +60 -0
  158. package/src/components/TableOfContents.tsx +108 -0
  159. package/src/components/comments/CommentBadge.tsx +43 -0
  160. package/src/components/comments/CommentInput.tsx +119 -0
  161. package/src/components/comments/CommentListItem.tsx +82 -0
  162. package/src/components/comments/CommentManager.tsx +106 -0
  163. package/src/components/comments/CommentMinimap.tsx +62 -0
  164. package/src/components/comments/CommentNav.tsx +104 -0
  165. package/src/components/ui/ActionBar.tsx +16 -0
  166. package/src/components/ui/ActionLink.tsx +32 -0
  167. package/src/components/ui/Button.tsx +55 -0
  168. package/src/components/ui/Dialog.tsx +156 -0
  169. package/src/components/ui/DropdownMenu.tsx +114 -0
  170. package/src/components/ui/SeparatorDot.tsx +9 -0
  171. package/src/components/ui/Text.tsx +54 -0
  172. package/src/contexts/CommentContext.tsx +222 -0
  173. package/src/contexts/LayoutContext.tsx +76 -0
  174. package/src/hooks/useClickOutside.ts +35 -0
  175. package/src/hooks/useClipboard.ts +79 -0
  176. package/src/hooks/useCommentNavigation.ts +130 -0
  177. package/src/hooks/useComments.ts +323 -0
  178. package/src/hooks/useDocument.ts +141 -0
  179. package/src/hooks/useFontPreference.ts +76 -0
  180. package/src/hooks/useHeadings.test.ts +159 -0
  181. package/src/hooks/useHeadings.ts +129 -0
  182. package/src/hooks/useKeybindings.ts +120 -0
  183. package/src/hooks/useKeyboardShortcuts.ts +63 -0
  184. package/src/hooks/useLayoutMode.ts +44 -0
  185. package/src/hooks/useReanchorMode.ts +33 -0
  186. package/src/hooks/useScrollMetrics.ts +56 -0
  187. package/src/hooks/useScrollSpy.ts +81 -0
  188. package/src/hooks/useTextSelection.ts +123 -0
  189. package/src/hooks/useThemePreference.ts +66 -0
  190. package/src/index.css +823 -0
  191. package/src/lib/__fixtures__/bench-data.ts +167 -0
  192. package/src/lib/anchor.bench.ts +112 -0
  193. package/src/lib/anchor.test.ts +531 -0
  194. package/src/lib/anchor.ts +465 -0
  195. package/src/lib/comment-storage.bench.ts +63 -0
  196. package/src/lib/comment-storage.test.ts +624 -0
  197. package/src/lib/comment-storage.ts +263 -0
  198. package/src/lib/context.bench.ts +41 -0
  199. package/src/lib/context.test.ts +224 -0
  200. package/src/lib/context.ts +193 -0
  201. package/src/lib/export.bench.ts +35 -0
  202. package/src/lib/export.ts +43 -0
  203. package/src/lib/highlight/colors.ts +37 -0
  204. package/src/lib/highlight/core.test.ts +98 -0
  205. package/src/lib/highlight/core.ts +54 -0
  206. package/src/lib/highlight/dom.ts +342 -0
  207. package/src/lib/highlight/highlighter.ts +427 -0
  208. package/src/lib/highlight/index.ts +23 -0
  209. package/src/lib/highlight/script-builder.ts +485 -0
  210. package/src/lib/highlight/types.ts +57 -0
  211. package/src/lib/html-processor.test.tsx +170 -0
  212. package/src/lib/html-processor.tsx +95 -0
  213. package/src/lib/layout-constants.ts +12 -0
  214. package/src/lib/margin-layout.bench.ts +28 -0
  215. package/src/lib/margin-layout.ts +100 -0
  216. package/src/lib/scroll.test.ts +118 -0
  217. package/src/lib/scroll.ts +47 -0
  218. package/src/lib/shortcut-registry.test.ts +173 -0
  219. package/src/lib/shortcut-registry.ts +209 -0
  220. package/src/lib/utils.test.ts +110 -0
  221. package/src/lib/utils.ts +61 -0
  222. package/src/main.tsx +10 -0
  223. package/src/server/index.ts +883 -0
  224. package/src/store/index.test.ts +220 -0
  225. package/src/store/index.ts +234 -0
  226. package/src/test-setup.ts +1 -0
  227. package/src/types/index.ts +115 -0
  228. package/test.md +74 -0
  229. package/tsconfig.cli.json +12 -0
  230. package/tsconfig.json +20 -0
  231. package/vite.config.ts +19 -0
  232. package/vitest.config.ts +15 -0
  233. package/dist/assets/_basePickBy-hOr-yGsE.js +0 -1
  234. package/dist/assets/_baseUniq-b7bzdUSn.js +0 -1
  235. package/dist/assets/arc-D65wG9gm.js +0 -1
  236. package/dist/assets/architecture-PBZL5I3N-DBa6CAv_.js +0 -1
  237. package/dist/assets/architectureDiagram-2XIMDMQ5-Djwpsh98.js +0 -36
  238. package/dist/assets/array-DOVTz2Mq.js +0 -1
  239. package/dist/assets/blockDiagram-WCTKOSBZ-BdW5TTxj.js +0 -132
  240. package/dist/assets/c4Diagram-IC4MRINW-DTmkHEXu.js +0 -10
  241. package/dist/assets/channel-B3MUFipN.js +0 -1
  242. package/dist/assets/chunk-4BX2VUAB-DEqzsvDc.js +0 -1
  243. package/dist/assets/chunk-55IACEB6-BzVuSUV8.js +0 -1
  244. package/dist/assets/chunk-7E7YKBS2-CZ8IcA4c.js +0 -1
  245. package/dist/assets/chunk-7R4GIKGN-CWVVC8HX.js +0 -79
  246. package/dist/assets/chunk-C72U2L5F-B1Tso5TH.js +0 -1
  247. package/dist/assets/chunk-EGIJ26TM-Cx_7CFik.js +0 -1
  248. package/dist/assets/chunk-FMBD7UC4-Cfk_iGhv.js +0 -15
  249. package/dist/assets/chunk-GEFDOKGD-C_5hRbJt.js +0 -2
  250. package/dist/assets/chunk-GLR3WWYH-CkY7IyBj.js +0 -2
  251. package/dist/assets/chunk-HHEYEP7N-B0I4X5cr.js +0 -1
  252. package/dist/assets/chunk-JSJVCQXG-CAjwlVLg.js +0 -1
  253. package/dist/assets/chunk-KX2RTZJC-DWqnZZ02.js +0 -1
  254. package/dist/assets/chunk-KYZI473N-gjRVhJgJ.js +0 -53
  255. package/dist/assets/chunk-L3YUKLVL-D7C9GuxL.js +0 -1
  256. package/dist/assets/chunk-MX3YWQON-i-77iuVj.js +0 -1
  257. package/dist/assets/chunk-NQ4KR5QH-B22Pvemm.js +0 -220
  258. package/dist/assets/chunk-O4XLMI2P-ZQd5L6ZD.js +0 -7
  259. package/dist/assets/chunk-OZEHJAEY-BaPKTELw.js +0 -1
  260. package/dist/assets/chunk-PQ6SQG4A-DqE1eupT.js +0 -1
  261. package/dist/assets/chunk-PU5JKC2W-BTqWqedh.js +0 -70
  262. package/dist/assets/chunk-QZHKN3VN-Nm9TvMss.js +0 -1
  263. package/dist/assets/chunk-R5LLSJPH-DkiNs1dN.js +0 -1
  264. package/dist/assets/chunk-WL4C6EOR-CioD2fv2.js +0 -189
  265. package/dist/assets/chunk-XIRO2GV7-B4GGQONY.js +0 -1
  266. package/dist/assets/chunk-XPW4576I-C0IbbQos.js +0 -32
  267. package/dist/assets/chunk-XZSTWKYB-DMOqFWmT.js +0 -94
  268. package/dist/assets/chunk-YBOYWFTD-CoeQgeVY.js +0 -1
  269. package/dist/assets/classDiagram-VBA2DB6C-DV9ltQ7h.js +0 -1
  270. package/dist/assets/classDiagram-v2-RAHNMMFH-C6nD9wmM.js +0 -1
  271. package/dist/assets/clone-DuY6BQEm.js +0 -1
  272. package/dist/assets/cose-bilkent-S5V4N54A-B6FexK6p.js +0 -1
  273. package/dist/assets/cytoscape.esm-DoTFyJaN.js +0 -321
  274. package/dist/assets/dagre-CCcocoCU.js +0 -1
  275. package/dist/assets/dagre-KLK3FWXG-DIELowj9.js +0 -4
  276. package/dist/assets/defaultLocale-Ck2Xxk-C.js +0 -1
  277. package/dist/assets/diagram-E7M64L7V-D1mm0PoO.js +0 -24
  278. package/dist/assets/diagram-IFDJBPK2-7DVjly8y.js +0 -43
  279. package/dist/assets/diagram-P4PSJMXO-jO7pfyMb.js +0 -24
  280. package/dist/assets/dist-BywRdrPx.js +0 -1
  281. package/dist/assets/erDiagram-INFDFZHY-DSRxlRFy.js +0 -70
  282. package/dist/assets/flowDiagram-PKNHOUZH-CgKzzNdR.js +0 -162
  283. package/dist/assets/ganttDiagram-A5KZAMGK-CtsE7Y4E.js +0 -292
  284. package/dist/assets/gitGraph-HDMCJU4V-BU9uhwtz.js +0 -1
  285. package/dist/assets/gitGraphDiagram-K3NZZRJ6-DOU8RGdw.js +0 -65
  286. package/dist/assets/graphlib-WkJoBgka.js +0 -1
  287. package/dist/assets/index-CKVArt9D.js +0 -562
  288. package/dist/assets/index-DzRKJazf.css +0 -2
  289. package/dist/assets/info-3K5VOQVL-CPpvM-SG.js +0 -1
  290. package/dist/assets/infoDiagram-LFFYTUFH-VKLs5DsF.js +0 -2
  291. package/dist/assets/init-Bft5Ffpj.js +0 -1
  292. package/dist/assets/isArrayLikeObject-icl0H0jo.js +0 -1
  293. package/dist/assets/isEmpty-Du8sNmkE.js +0 -1
  294. package/dist/assets/ishikawaDiagram-PHBUUO56-CsWvEjux.js +0 -70
  295. package/dist/assets/journeyDiagram-4ABVD52K-BzJGTdIT.js +0 -139
  296. package/dist/assets/kanban-definition-K7BYSVSG-B_9ClJ1A.js +0 -89
  297. package/dist/assets/katex-BJrMXEjr.js +0 -261
  298. package/dist/assets/line-CC_tDGId.js +0 -1
  299. package/dist/assets/linear-Cts_d04Y.js +0 -1
  300. package/dist/assets/math-CNhlSIO3.js +0 -1
  301. package/dist/assets/mermaid-parser.core-Vb9KKv1R.js +0 -4
  302. package/dist/assets/mermaid.core-C_7xsp3d.js +0 -11
  303. package/dist/assets/mindmap-definition-YRQLILUH-BWmfy5wB.js +0 -68
  304. package/dist/assets/ordinal-DIg8h6NI.js +0 -1
  305. package/dist/assets/packet-RMMSAZCW-Q-WG6o3b.js +0 -1
  306. package/dist/assets/path-DfRbCp9y.js +0 -1
  307. package/dist/assets/pie-UPGHQEXC-Cwi2tLlt.js +0 -1
  308. package/dist/assets/pieDiagram-SKSYHLDU-Dyf3X_in.js +0 -30
  309. package/dist/assets/quadrantDiagram-337W2JSQ-B5_5m61Q.js +0 -7
  310. package/dist/assets/radar-KQ55EAFF-Dtw2VzxY.js +0 -1
  311. package/dist/assets/requirementDiagram-Z7DCOOCP-BSERBnlW.js +0 -73
  312. package/dist/assets/rough.esm-KjoEK0it.js +0 -1
  313. package/dist/assets/sankeyDiagram-WA2Y5GQK-CMcEY8Cz.js +0 -10
  314. package/dist/assets/sequenceDiagram-2WXFIKYE-D28qcXwC.js +0 -145
  315. package/dist/assets/src-C8kkzlHX.js +0 -1
  316. package/dist/assets/stateDiagram-RAJIS63D-7oVrCmRl.js +0 -1
  317. package/dist/assets/stateDiagram-v2-FVOUBMTO-DtFptQAd.js +0 -1
  318. package/dist/assets/timeline-definition-YZTLITO2-rbCfBEvG.js +0 -61
  319. package/dist/assets/treemap-KZPCXAKY-BlRvF0um.js +0 -1
  320. package/dist/assets/vennDiagram-LZ73GAT5-DBit3zWa.js +0 -34
  321. package/dist/assets/xychartDiagram-JWTSCODW-BVYXv51y.js +0 -7
  322. package/dist/index.js +0 -1040
@@ -0,0 +1,465 @@
1
+ import { type Anchor, AnchorConfidences } from "../types";
2
+ import { getLineNumber } from "./comment-storage";
3
+
4
+ // Anchor matching configuration
5
+ const DEFAULT_SEARCH_WINDOW = 500; // chars before/after line hint for exact match
6
+ const DEFAULT_FUZZY_THRESHOLD = 5; // max Levenshtein distance for fuzzy match
7
+ const MAX_FUZZY_TEXT_LENGTH = 200; // skip fuzzy matching for texts longer than this
8
+ const FUZZY_SEARCH_WINDOW = 2000; // larger window for fuzzy search near line hint
9
+
10
+ /**
11
+ * Common parameters for anchor finding functions.
12
+ * Using object destructuring per style guide §3.5 for multiple string parameters.
13
+ */
14
+ export interface FindAnchorParams {
15
+ source: string;
16
+ selectedText: string;
17
+ lineHint: string;
18
+ searchWindow?: number;
19
+ }
20
+
21
+ export interface FindAnchorFuzzyParams {
22
+ source: string;
23
+ selectedText: string;
24
+ lineHint?: string;
25
+ threshold?: number;
26
+ }
27
+
28
+ export interface FindAnchorWithFallbackParams {
29
+ source: string;
30
+ selectedText: string;
31
+ lineHint: string;
32
+ fuzzyThreshold?: number;
33
+ }
34
+
35
+ /**
36
+ * Normalize whitespace for comparison: collapse runs of whitespace to single space.
37
+ * This allows matching text that was reformatted (line breaks, indentation changes).
38
+ */
39
+ export function normalizeWhitespace(text: string): string {
40
+ return text.replace(/\s+/g, " ").trim();
41
+ }
42
+
43
+ /**
44
+ * Calculate Levenshtein distance between two strings.
45
+ * Uses Wagner-Fischer algorithm with O(min(m,n)) space.
46
+ *
47
+ * @param maxDistance Optional early exit threshold. If set, returns Infinity
48
+ * when distance is guaranteed to exceed this value.
49
+ */
50
+ export function levenshteinDistance(
51
+ a: string,
52
+ b: string,
53
+ maxDistance?: number,
54
+ ): number {
55
+ // Ensure a is the shorter string for space optimization
56
+ if (a.length > b.length) {
57
+ [a, b] = [b, a];
58
+ }
59
+
60
+ const m = a.length;
61
+ const n = b.length;
62
+
63
+ // Early termination for empty strings
64
+ if (m === 0) return n;
65
+ if (n === 0) return m;
66
+
67
+ // Early exit: length difference alone exceeds threshold
68
+ if (maxDistance !== undefined && Math.abs(m - n) > maxDistance) {
69
+ return Number.POSITIVE_INFINITY;
70
+ }
71
+
72
+ // Use single row for space optimization
73
+ let prevRow = new Array(m + 1);
74
+ let currRow = new Array(m + 1);
75
+
76
+ // Initialize first row
77
+ for (let i = 0; i <= m; i++) {
78
+ prevRow[i] = i;
79
+ }
80
+
81
+ for (let j = 1; j <= n; j++) {
82
+ currRow[0] = j;
83
+
84
+ // Track minimum value in this row for early exit
85
+ let rowMin = currRow[0];
86
+
87
+ for (let i = 1; i <= m; i++) {
88
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
89
+ currRow[i] = Math.min(
90
+ prevRow[i] + 1, // deletion
91
+ currRow[i - 1] + 1, // insertion
92
+ prevRow[i - 1] + cost, // substitution
93
+ );
94
+ rowMin = Math.min(rowMin, currRow[i]);
95
+ }
96
+
97
+ // Early exit: minimum possible distance exceeds threshold
98
+ if (maxDistance !== undefined && rowMin > maxDistance) {
99
+ return Number.POSITIVE_INFINITY;
100
+ }
101
+
102
+ [prevRow, currRow] = [currRow, prevRow];
103
+ }
104
+
105
+ return prevRow[m];
106
+ }
107
+
108
+ /**
109
+ * Get character offset for the start of a line number (1-indexed).
110
+ */
111
+ export function getLineOffset(content: string, lineNumber: number): number {
112
+ if (lineNumber <= 1) return 0;
113
+
114
+ let currentLine = 1;
115
+
116
+ for (let i = 0; i < content.length; i++) {
117
+ if (content[i] === "\n") {
118
+ currentLine++;
119
+ if (currentLine === lineNumber) {
120
+ return i + 1;
121
+ }
122
+ }
123
+ }
124
+
125
+ return content.length;
126
+ }
127
+
128
+ /**
129
+ * Parse line hint string to get line number(s).
130
+ * Supports "L42" and "L42-45" formats.
131
+ */
132
+ export function parseLineHint(lineHint: string): {
133
+ start: number;
134
+ end: number;
135
+ } {
136
+ const match = lineHint.match(/^L(\d+)(?:-(\d+))?$/);
137
+ if (!match) {
138
+ return { start: 1, end: 1 };
139
+ }
140
+
141
+ const start = Number.parseInt(match[1], 10);
142
+ const end = match[2] ? Number.parseInt(match[2], 10) : start;
143
+ return { start, end };
144
+ }
145
+
146
+ /**
147
+ * Find anchor position for selected text in source content.
148
+ * Uses line hint for fast lookup, falls back to global search.
149
+ */
150
+ export function findAnchor({
151
+ source,
152
+ selectedText,
153
+ lineHint,
154
+ searchWindow = DEFAULT_SEARCH_WINDOW,
155
+ }: FindAnchorParams): Anchor | undefined {
156
+ if (!selectedText || !source) {
157
+ return undefined;
158
+ }
159
+ const { start: hintLine } = parseLineHint(lineHint);
160
+
161
+ // Fast path: search near line hint
162
+ const lineOffset = getLineOffset(source, hintLine);
163
+ const windowStart = Math.max(0, lineOffset - searchWindow);
164
+ const windowEnd = Math.min(source.length, lineOffset + searchWindow);
165
+ const window = source.slice(windowStart, windowEnd);
166
+
167
+ const localIndex = window.indexOf(selectedText);
168
+ if (localIndex !== -1) {
169
+ const start = windowStart + localIndex;
170
+ const end = start + selectedText.length;
171
+ return {
172
+ start,
173
+ end,
174
+ line: getLineNumber(source, start),
175
+ confidence: AnchorConfidences.EXACT,
176
+ };
177
+ }
178
+
179
+ // Fallback: global search
180
+ const globalIndex = source.indexOf(selectedText);
181
+ if (globalIndex !== -1) {
182
+ return {
183
+ start: globalIndex,
184
+ end: globalIndex + selectedText.length,
185
+ line: getLineNumber(source, globalIndex),
186
+ confidence: AnchorConfidences.EXACT,
187
+ };
188
+ }
189
+
190
+ return undefined;
191
+ }
192
+
193
+ /**
194
+ * Build a position map from normalized string positions back to original positions.
195
+ * Returns array where normalizedToOriginal[i] = original position for normalized char i.
196
+ */
197
+ function buildNormalizedPositionMap(text: string): {
198
+ normalized: string;
199
+ toOriginal: number[];
200
+ } {
201
+ const toOriginal: number[] = [];
202
+ let normalized = "";
203
+ let inWhitespace = false;
204
+
205
+ for (let i = 0; i < text.length; i++) {
206
+ const char = text[i];
207
+ const isSpace = /\s/.test(char);
208
+
209
+ if (isSpace) {
210
+ if (!inWhitespace && normalized.length > 0) {
211
+ // First whitespace after content - emit single space
212
+ normalized += " ";
213
+ toOriginal.push(i);
214
+ }
215
+ inWhitespace = true;
216
+ } else {
217
+ normalized += char;
218
+ toOriginal.push(i);
219
+ inWhitespace = false;
220
+ }
221
+ }
222
+
223
+ // Trim trailing space
224
+ if (normalized.endsWith(" ")) {
225
+ normalized = normalized.slice(0, -1);
226
+ toOriginal.pop();
227
+ }
228
+
229
+ return { normalized, toOriginal };
230
+ }
231
+
232
+ /**
233
+ * Find anchor using whitespace-normalized matching.
234
+ * Useful when document was reformatted but content is unchanged.
235
+ * Returns "normalized" confidence level.
236
+ *
237
+ * Algorithm:
238
+ * 1. Normalize source and build position map
239
+ * 2. Find normalized text in normalized source (fast substring search)
240
+ * 3. Map positions back to original source
241
+ */
242
+ export function findAnchorNormalized({
243
+ source,
244
+ selectedText,
245
+ lineHint,
246
+ searchWindow = FUZZY_SEARCH_WINDOW,
247
+ }: FindAnchorParams): Anchor | undefined {
248
+ if (!selectedText || !source) {
249
+ return undefined;
250
+ }
251
+
252
+ const normalizedText = normalizeWhitespace(selectedText);
253
+ if (!normalizedText) {
254
+ return undefined;
255
+ }
256
+
257
+ // Skip if text has no collapsible whitespace (exact match would have worked)
258
+ if (normalizedText === selectedText) {
259
+ return undefined;
260
+ }
261
+ const { start: hintLine } = parseLineHint(lineHint);
262
+ const lineOffset = getLineOffset(source, hintLine);
263
+
264
+ // Define search window
265
+ const windowStart = Math.max(0, lineOffset - searchWindow);
266
+ const windowEnd = Math.min(source.length, lineOffset + searchWindow);
267
+ const window = source.slice(windowStart, windowEnd);
268
+
269
+ // Build normalized version with position mapping
270
+ const { normalized: normalizedWindow, toOriginal } =
271
+ buildNormalizedPositionMap(window);
272
+
273
+ // Fast substring search on normalized text
274
+ const normalizedIndex = normalizedWindow.indexOf(normalizedText);
275
+ if (normalizedIndex !== -1) {
276
+ // Map back to original positions
277
+ const originalStart = windowStart + toOriginal[normalizedIndex];
278
+ const endNormIndex = normalizedIndex + normalizedText.length - 1;
279
+ // Find original end: scan forward from mapped position to include trailing whitespace
280
+ let originalEnd = windowStart + toOriginal[endNormIndex] + 1;
281
+ // Extend to include any trailing whitespace that was collapsed
282
+ while (originalEnd < source.length && /\s/.test(source[originalEnd])) {
283
+ originalEnd++;
284
+ }
285
+
286
+ return {
287
+ start: originalStart,
288
+ end: originalEnd,
289
+ line: getLineNumber(source, originalStart),
290
+ confidence: AnchorConfidences.NORMALIZED,
291
+ };
292
+ }
293
+
294
+ // Global fallback (outside hint window)
295
+ const { normalized: fullNormalized, toOriginal: fullToOriginal } =
296
+ buildNormalizedPositionMap(source);
297
+ const globalIndex = fullNormalized.indexOf(normalizedText);
298
+ if (globalIndex !== -1) {
299
+ const originalStart = fullToOriginal[globalIndex];
300
+ const endNormIndex = globalIndex + normalizedText.length - 1;
301
+ let originalEnd = fullToOriginal[endNormIndex] + 1;
302
+ while (originalEnd < source.length && /\s/.test(source[originalEnd])) {
303
+ originalEnd++;
304
+ }
305
+
306
+ return {
307
+ start: originalStart,
308
+ end: originalEnd,
309
+ line: getLineNumber(source, originalStart),
310
+ confidence: AnchorConfidences.NORMALIZED,
311
+ };
312
+ }
313
+
314
+ return undefined;
315
+ }
316
+
317
+ /**
318
+ * Find anchor using fuzzy matching with Levenshtein distance.
319
+ * Scans the source for substrings similar to the selected text.
320
+ */
321
+ export function findAnchorFuzzy({
322
+ source,
323
+ selectedText,
324
+ lineHint,
325
+ threshold = DEFAULT_FUZZY_THRESHOLD,
326
+ }: FindAnchorFuzzyParams): Anchor | undefined {
327
+ if (!selectedText || !source) {
328
+ return undefined;
329
+ }
330
+
331
+ const textLen = selectedText.length;
332
+
333
+ // For very long texts, skip fuzzy matching (too expensive)
334
+ if (textLen > MAX_FUZZY_TEXT_LENGTH) {
335
+ return undefined;
336
+ }
337
+
338
+ let bestMatch: Anchor | undefined;
339
+ let bestDistance = threshold + 1;
340
+
341
+ // Determine search range based on line hint
342
+ let searchStart = 0;
343
+ let searchEnd = source.length;
344
+
345
+ if (lineHint) {
346
+ const { start: hintLine } = parseLineHint(lineHint);
347
+ const lineOffset = getLineOffset(source, hintLine);
348
+ // Search in a larger window for fuzzy matching
349
+ searchStart = Math.max(0, lineOffset - FUZZY_SEARCH_WINDOW);
350
+ searchEnd = Math.min(source.length, lineOffset + FUZZY_SEARCH_WINDOW);
351
+ }
352
+
353
+ // Slide window of similar length through the search range
354
+ const minLen = Math.max(1, textLen - threshold);
355
+ const maxLen = textLen + threshold;
356
+
357
+ for (let len = minLen; len <= maxLen; len++) {
358
+ for (let i = searchStart; i <= searchEnd - len; i++) {
359
+ const candidate = source.slice(i, i + len);
360
+ // Use early exit: only compute if distance could improve on current best
361
+ const distance = levenshteinDistance(
362
+ selectedText,
363
+ candidate,
364
+ bestDistance - 1,
365
+ );
366
+
367
+ if (distance < bestDistance) {
368
+ bestDistance = distance;
369
+ bestMatch = {
370
+ start: i,
371
+ end: i + len,
372
+ line: getLineNumber(source, i),
373
+ confidence: AnchorConfidences.FUZZY,
374
+ distance,
375
+ };
376
+
377
+ // Early exit if we found an exact match
378
+ if (distance === 0) {
379
+ return bestMatch;
380
+ }
381
+ }
382
+ }
383
+ }
384
+
385
+ return bestMatch;
386
+ }
387
+
388
+ /**
389
+ * Find anchor with fallback chain: exact → normalized → fuzzy.
390
+ *
391
+ * Matching strategies in order of preference:
392
+ * 1. Exact: Fast substring match (O(n))
393
+ * 2. Normalized: Whitespace-collapsed match for reformatted text (O(n))
394
+ * 3. Fuzzy: Levenshtein distance for small edits (O(n × m × threshold))
395
+ */
396
+ export function findAnchorWithFallback({
397
+ source,
398
+ selectedText,
399
+ lineHint,
400
+ fuzzyThreshold,
401
+ }: FindAnchorWithFallbackParams): Anchor | undefined {
402
+ // Try exact match first (fastest)
403
+ const exactMatch = findAnchor({ source, selectedText, lineHint });
404
+ if (exactMatch) {
405
+ return exactMatch;
406
+ }
407
+
408
+ // Try normalized match (handles reformatting)
409
+ const normalizedMatch = findAnchorNormalized({
410
+ source,
411
+ selectedText,
412
+ lineHint,
413
+ });
414
+ if (normalizedMatch) {
415
+ return normalizedMatch;
416
+ }
417
+
418
+ // Fall back to fuzzy matching (handles small edits)
419
+ return findAnchorFuzzy({
420
+ source,
421
+ selectedText,
422
+ lineHint,
423
+ threshold: fuzzyThreshold,
424
+ });
425
+ }
426
+
427
+ /**
428
+ * Find the closest match when multiple occurrences exist.
429
+ */
430
+ export function findClosestOccurrence({
431
+ source,
432
+ selectedText,
433
+ lineHint,
434
+ }: Omit<FindAnchorParams, "searchWindow">): Anchor | undefined {
435
+ if (!selectedText || !source) {
436
+ return undefined;
437
+ }
438
+
439
+ const { start: hintLine } = parseLineHint(lineHint);
440
+ const targetOffset = getLineOffset(source, hintLine);
441
+
442
+ let bestMatch: Anchor | undefined;
443
+ let bestDistance = Number.POSITIVE_INFINITY;
444
+ let index = 0;
445
+
446
+ while (true) {
447
+ const foundIndex = source.indexOf(selectedText, index);
448
+ if (foundIndex === -1) break;
449
+
450
+ const distance = Math.abs(foundIndex - targetOffset);
451
+ if (distance < bestDistance) {
452
+ bestDistance = distance;
453
+ bestMatch = {
454
+ start: foundIndex,
455
+ end: foundIndex + selectedText.length,
456
+ line: getLineNumber(source, foundIndex),
457
+ confidence: AnchorConfidences.EXACT,
458
+ };
459
+ }
460
+
461
+ index = foundIndex + 1;
462
+ }
463
+
464
+ return bestMatch;
465
+ }
@@ -0,0 +1,63 @@
1
+ import { bench, describe } from "vitest";
2
+ import {
3
+ COMMENT_FILE_LARGE,
4
+ COMMENT_FILE_MEDIUM,
5
+ COMMENT_FILE_OBJ_LARGE,
6
+ COMMENT_FILE_OBJ_MEDIUM,
7
+ COMMENT_FILE_SMALL,
8
+ LARGE_DOC,
9
+ } from "./__fixtures__/bench-data";
10
+ import {
11
+ computeHash,
12
+ createComment,
13
+ parseCommentFile,
14
+ serializeComments,
15
+ } from "./comment-storage";
16
+
17
+ describe("parseCommentFile", () => {
18
+ bench("1 comment", () => {
19
+ parseCommentFile(COMMENT_FILE_SMALL);
20
+ });
21
+
22
+ bench("10 comments", () => {
23
+ parseCommentFile(COMMENT_FILE_MEDIUM);
24
+ });
25
+
26
+ bench("50 comments", () => {
27
+ parseCommentFile(COMMENT_FILE_LARGE);
28
+ });
29
+ });
30
+
31
+ describe("serializeComments", () => {
32
+ bench("10 comments", () => {
33
+ serializeComments(COMMENT_FILE_OBJ_MEDIUM);
34
+ });
35
+
36
+ bench("50 comments", () => {
37
+ serializeComments(COMMENT_FILE_OBJ_LARGE);
38
+ });
39
+ });
40
+
41
+ describe("computeHash", () => {
42
+ const shortString = "x".repeat(100);
43
+
44
+ bench("short string (100 chars)", () => {
45
+ computeHash(shortString);
46
+ });
47
+
48
+ bench("large doc (~10k chars)", () => {
49
+ computeHash(LARGE_DOC);
50
+ });
51
+ });
52
+
53
+ describe("createComment", () => {
54
+ bench("short selection", () => {
55
+ createComment("selected text here", "my comment", 100, 118, LARGE_DOC);
56
+ });
57
+
58
+ const longSelection = "a".repeat(2000);
59
+
60
+ bench("long selection (triggers truncation)", () => {
61
+ createComment(longSelection, "my comment", 0, 2000, LARGE_DOC);
62
+ });
63
+ });