@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.
- package/.agents/skills/remotion-best-practices/SKILL.md +61 -0
- package/.agents/skills/remotion-best-practices/rules/3d.md +86 -0
- package/.agents/skills/remotion-best-practices/rules/animations.md +27 -0
- package/.agents/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +178 -0
- package/.agents/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/.agents/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/.agents/skills/remotion-best-practices/rules/assets.md +78 -0
- package/.agents/skills/remotion-best-practices/rules/audio-visualization.md +198 -0
- package/.agents/skills/remotion-best-practices/rules/audio.md +169 -0
- package/.agents/skills/remotion-best-practices/rules/calculate-metadata.md +134 -0
- package/.agents/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/.agents/skills/remotion-best-practices/rules/charts.md +120 -0
- package/.agents/skills/remotion-best-practices/rules/compositions.md +154 -0
- package/.agents/skills/remotion-best-practices/rules/display-captions.md +184 -0
- package/.agents/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/.agents/skills/remotion-best-practices/rules/ffmpeg.md +38 -0
- package/.agents/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/.agents/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/.agents/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/.agents/skills/remotion-best-practices/rules/get-video-duration.md +60 -0
- package/.agents/skills/remotion-best-practices/rules/gifs.md +141 -0
- package/.agents/skills/remotion-best-practices/rules/images.md +134 -0
- package/.agents/skills/remotion-best-practices/rules/import-srt-captions.md +69 -0
- package/.agents/skills/remotion-best-practices/rules/light-leaks.md +73 -0
- package/.agents/skills/remotion-best-practices/rules/lottie.md +70 -0
- package/.agents/skills/remotion-best-practices/rules/maps.md +412 -0
- package/.agents/skills/remotion-best-practices/rules/measuring-dom-nodes.md +34 -0
- package/.agents/skills/remotion-best-practices/rules/measuring-text.md +140 -0
- package/.agents/skills/remotion-best-practices/rules/parameters.md +109 -0
- package/.agents/skills/remotion-best-practices/rules/sequencing.md +118 -0
- package/.agents/skills/remotion-best-practices/rules/sfx.md +26 -0
- package/.agents/skills/remotion-best-practices/rules/subtitles.md +36 -0
- package/.agents/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/.agents/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/.agents/skills/remotion-best-practices/rules/timing.md +179 -0
- package/.agents/skills/remotion-best-practices/rules/transcribe-captions.md +70 -0
- package/.agents/skills/remotion-best-practices/rules/transitions.md +197 -0
- package/.agents/skills/remotion-best-practices/rules/transparent-videos.md +106 -0
- package/.agents/skills/remotion-best-practices/rules/trimming.md +51 -0
- package/.agents/skills/remotion-best-practices/rules/videos.md +171 -0
- package/.agents/skills/remotion-best-practices/rules/voiceover.md +99 -0
- package/.agents/skills/simple/SKILL.md +52 -0
- package/.agents/skills/vercel-react-best-practices/AGENTS.md +3254 -0
- package/.agents/skills/vercel-react-best-practices/README.md +123 -0
- package/.agents/skills/vercel-react-best-practices/SKILL.md +141 -0
- package/.agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.agents/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/.agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.agents/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/.agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +142 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/.agents/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.claude/CLAUDE.md +142 -0
- package/.claude/commands/review.md +120 -0
- package/.claude/commands/sync-docs.md +71 -0
- package/.claude/roadmap.md +98 -0
- package/.claude/rules/style-guide.md +830 -0
- package/.claude/settings.json +18 -0
- package/.claude/user-stories.md +248 -0
- package/AGENTS.md +64 -0
- package/README.md +7 -7
- package/biome.json +69 -0
- package/bun.lock +1124 -0
- package/docs/design.md +563 -0
- package/docs/plans/2026-03-13-client-mode-design.md +86 -0
- package/docs/plans/2026-03-13-client-mode-plan.md +605 -0
- package/docs/plans/2026-03-13-keyboard-shortcuts-design.md +129 -0
- package/docs/plans/2026-03-13-keyboard-shortcuts-plan.md +1471 -0
- package/docs/plans/2026-03-13-multi-document-design.md +183 -0
- package/docs/plans/2026-03-13-performance-benchmarks-design.md +121 -0
- package/e2e/comments.spec.ts +125 -0
- package/e2e/document-load.spec.ts +54 -0
- package/e2e/export.spec.ts +58 -0
- package/e2e/fixtures/sample.html +13 -0
- package/e2e/fixtures/sample.md +7 -0
- package/e2e/persistence-file.spec.ts +342 -0
- package/e2e/utils/cli.ts +84 -0
- package/e2e/utils/selection.ts +135 -0
- package/{dist/index.html → index.html} +8 -2
- package/lefthook.yml +8 -0
- package/package.json +17 -39
- package/playwright.config.ts +22 -0
- package/skills-lock.json +20 -0
- package/src/App.tsx +396 -0
- package/src/cli/index.ts +467 -0
- package/src/components/ActionsMenu.tsx +110 -0
- package/src/components/DocumentViewer/CodeBlock.tsx +83 -0
- package/src/components/DocumentViewer/DocumentViewer.tsx +257 -0
- package/src/components/DocumentViewer/IframeContainer.tsx +251 -0
- package/src/components/DocumentViewer/MermaidDiagram.tsx +137 -0
- package/src/components/DocumentViewer/index.ts +1 -0
- package/src/components/FloatingTOC.tsx +59 -0
- package/src/components/Header.tsx +63 -0
- package/src/components/InlineEditor.tsx +72 -0
- package/src/components/MarginNote.tsx +198 -0
- package/src/components/MarginNotes.tsx +50 -0
- package/src/components/RawModal.tsx +141 -0
- package/src/components/ReanchorConfirm.tsx +33 -0
- package/src/components/SettingsModal.tsx +221 -0
- package/src/components/ShortcutCapture.tsx +45 -0
- package/src/components/ShortcutList.tsx +157 -0
- package/src/components/TabBar.tsx +60 -0
- package/src/components/TableOfContents.tsx +108 -0
- package/src/components/comments/CommentBadge.tsx +43 -0
- package/src/components/comments/CommentInput.tsx +119 -0
- package/src/components/comments/CommentListItem.tsx +82 -0
- package/src/components/comments/CommentManager.tsx +106 -0
- package/src/components/comments/CommentMinimap.tsx +62 -0
- package/src/components/comments/CommentNav.tsx +104 -0
- package/src/components/ui/ActionBar.tsx +16 -0
- package/src/components/ui/ActionLink.tsx +32 -0
- package/src/components/ui/Button.tsx +55 -0
- package/src/components/ui/Dialog.tsx +156 -0
- package/src/components/ui/DropdownMenu.tsx +114 -0
- package/src/components/ui/SeparatorDot.tsx +9 -0
- package/src/components/ui/Text.tsx +54 -0
- package/src/contexts/CommentContext.tsx +222 -0
- package/src/contexts/LayoutContext.tsx +76 -0
- package/src/hooks/useClickOutside.ts +35 -0
- package/src/hooks/useClipboard.ts +79 -0
- package/src/hooks/useCommentNavigation.ts +130 -0
- package/src/hooks/useComments.ts +323 -0
- package/src/hooks/useDocument.ts +141 -0
- package/src/hooks/useFontPreference.ts +76 -0
- package/src/hooks/useHeadings.test.ts +159 -0
- package/src/hooks/useHeadings.ts +129 -0
- package/src/hooks/useKeybindings.ts +120 -0
- package/src/hooks/useKeyboardShortcuts.ts +63 -0
- package/src/hooks/useLayoutMode.ts +44 -0
- package/src/hooks/useReanchorMode.ts +33 -0
- package/src/hooks/useScrollMetrics.ts +56 -0
- package/src/hooks/useScrollSpy.ts +81 -0
- package/src/hooks/useTextSelection.ts +123 -0
- package/src/hooks/useThemePreference.ts +66 -0
- package/src/index.css +823 -0
- package/src/lib/__fixtures__/bench-data.ts +167 -0
- package/src/lib/anchor.bench.ts +112 -0
- package/src/lib/anchor.test.ts +531 -0
- package/src/lib/anchor.ts +465 -0
- package/src/lib/comment-storage.bench.ts +63 -0
- package/src/lib/comment-storage.test.ts +624 -0
- package/src/lib/comment-storage.ts +263 -0
- package/src/lib/context.bench.ts +41 -0
- package/src/lib/context.test.ts +224 -0
- package/src/lib/context.ts +193 -0
- package/src/lib/export.bench.ts +35 -0
- package/src/lib/export.ts +43 -0
- package/src/lib/highlight/colors.ts +37 -0
- package/src/lib/highlight/core.test.ts +98 -0
- package/src/lib/highlight/core.ts +54 -0
- package/src/lib/highlight/dom.ts +342 -0
- package/src/lib/highlight/highlighter.ts +427 -0
- package/src/lib/highlight/index.ts +23 -0
- package/src/lib/highlight/script-builder.ts +485 -0
- package/src/lib/highlight/types.ts +57 -0
- package/src/lib/html-processor.test.tsx +170 -0
- package/src/lib/html-processor.tsx +95 -0
- package/src/lib/layout-constants.ts +12 -0
- package/src/lib/margin-layout.bench.ts +28 -0
- package/src/lib/margin-layout.ts +100 -0
- package/src/lib/scroll.test.ts +118 -0
- package/src/lib/scroll.ts +47 -0
- package/src/lib/shortcut-registry.test.ts +173 -0
- package/src/lib/shortcut-registry.ts +209 -0
- package/src/lib/utils.test.ts +110 -0
- package/src/lib/utils.ts +61 -0
- package/src/main.tsx +10 -0
- package/src/server/index.ts +883 -0
- package/src/store/index.test.ts +220 -0
- package/src/store/index.ts +234 -0
- package/src/test-setup.ts +1 -0
- package/src/types/index.ts +115 -0
- package/test.md +74 -0
- package/tsconfig.cli.json +12 -0
- package/tsconfig.json +20 -0
- package/vite.config.ts +19 -0
- package/vitest.config.ts +15 -0
- package/dist/assets/_basePickBy-hOr-yGsE.js +0 -1
- package/dist/assets/_baseUniq-b7bzdUSn.js +0 -1
- package/dist/assets/arc-D65wG9gm.js +0 -1
- package/dist/assets/architecture-PBZL5I3N-DBa6CAv_.js +0 -1
- package/dist/assets/architectureDiagram-2XIMDMQ5-Djwpsh98.js +0 -36
- package/dist/assets/array-DOVTz2Mq.js +0 -1
- package/dist/assets/blockDiagram-WCTKOSBZ-BdW5TTxj.js +0 -132
- package/dist/assets/c4Diagram-IC4MRINW-DTmkHEXu.js +0 -10
- package/dist/assets/channel-B3MUFipN.js +0 -1
- package/dist/assets/chunk-4BX2VUAB-DEqzsvDc.js +0 -1
- package/dist/assets/chunk-55IACEB6-BzVuSUV8.js +0 -1
- package/dist/assets/chunk-7E7YKBS2-CZ8IcA4c.js +0 -1
- package/dist/assets/chunk-7R4GIKGN-CWVVC8HX.js +0 -79
- package/dist/assets/chunk-C72U2L5F-B1Tso5TH.js +0 -1
- package/dist/assets/chunk-EGIJ26TM-Cx_7CFik.js +0 -1
- package/dist/assets/chunk-FMBD7UC4-Cfk_iGhv.js +0 -15
- package/dist/assets/chunk-GEFDOKGD-C_5hRbJt.js +0 -2
- package/dist/assets/chunk-GLR3WWYH-CkY7IyBj.js +0 -2
- package/dist/assets/chunk-HHEYEP7N-B0I4X5cr.js +0 -1
- package/dist/assets/chunk-JSJVCQXG-CAjwlVLg.js +0 -1
- package/dist/assets/chunk-KX2RTZJC-DWqnZZ02.js +0 -1
- package/dist/assets/chunk-KYZI473N-gjRVhJgJ.js +0 -53
- package/dist/assets/chunk-L3YUKLVL-D7C9GuxL.js +0 -1
- package/dist/assets/chunk-MX3YWQON-i-77iuVj.js +0 -1
- package/dist/assets/chunk-NQ4KR5QH-B22Pvemm.js +0 -220
- package/dist/assets/chunk-O4XLMI2P-ZQd5L6ZD.js +0 -7
- package/dist/assets/chunk-OZEHJAEY-BaPKTELw.js +0 -1
- package/dist/assets/chunk-PQ6SQG4A-DqE1eupT.js +0 -1
- package/dist/assets/chunk-PU5JKC2W-BTqWqedh.js +0 -70
- package/dist/assets/chunk-QZHKN3VN-Nm9TvMss.js +0 -1
- package/dist/assets/chunk-R5LLSJPH-DkiNs1dN.js +0 -1
- package/dist/assets/chunk-WL4C6EOR-CioD2fv2.js +0 -189
- package/dist/assets/chunk-XIRO2GV7-B4GGQONY.js +0 -1
- package/dist/assets/chunk-XPW4576I-C0IbbQos.js +0 -32
- package/dist/assets/chunk-XZSTWKYB-DMOqFWmT.js +0 -94
- package/dist/assets/chunk-YBOYWFTD-CoeQgeVY.js +0 -1
- package/dist/assets/classDiagram-VBA2DB6C-DV9ltQ7h.js +0 -1
- package/dist/assets/classDiagram-v2-RAHNMMFH-C6nD9wmM.js +0 -1
- package/dist/assets/clone-DuY6BQEm.js +0 -1
- package/dist/assets/cose-bilkent-S5V4N54A-B6FexK6p.js +0 -1
- package/dist/assets/cytoscape.esm-DoTFyJaN.js +0 -321
- package/dist/assets/dagre-CCcocoCU.js +0 -1
- package/dist/assets/dagre-KLK3FWXG-DIELowj9.js +0 -4
- package/dist/assets/defaultLocale-Ck2Xxk-C.js +0 -1
- package/dist/assets/diagram-E7M64L7V-D1mm0PoO.js +0 -24
- package/dist/assets/diagram-IFDJBPK2-7DVjly8y.js +0 -43
- package/dist/assets/diagram-P4PSJMXO-jO7pfyMb.js +0 -24
- package/dist/assets/dist-BywRdrPx.js +0 -1
- package/dist/assets/erDiagram-INFDFZHY-DSRxlRFy.js +0 -70
- package/dist/assets/flowDiagram-PKNHOUZH-CgKzzNdR.js +0 -162
- package/dist/assets/ganttDiagram-A5KZAMGK-CtsE7Y4E.js +0 -292
- package/dist/assets/gitGraph-HDMCJU4V-BU9uhwtz.js +0 -1
- package/dist/assets/gitGraphDiagram-K3NZZRJ6-DOU8RGdw.js +0 -65
- package/dist/assets/graphlib-WkJoBgka.js +0 -1
- package/dist/assets/index-CKVArt9D.js +0 -562
- package/dist/assets/index-DzRKJazf.css +0 -2
- package/dist/assets/info-3K5VOQVL-CPpvM-SG.js +0 -1
- package/dist/assets/infoDiagram-LFFYTUFH-VKLs5DsF.js +0 -2
- package/dist/assets/init-Bft5Ffpj.js +0 -1
- package/dist/assets/isArrayLikeObject-icl0H0jo.js +0 -1
- package/dist/assets/isEmpty-Du8sNmkE.js +0 -1
- package/dist/assets/ishikawaDiagram-PHBUUO56-CsWvEjux.js +0 -70
- package/dist/assets/journeyDiagram-4ABVD52K-BzJGTdIT.js +0 -139
- package/dist/assets/kanban-definition-K7BYSVSG-B_9ClJ1A.js +0 -89
- package/dist/assets/katex-BJrMXEjr.js +0 -261
- package/dist/assets/line-CC_tDGId.js +0 -1
- package/dist/assets/linear-Cts_d04Y.js +0 -1
- package/dist/assets/math-CNhlSIO3.js +0 -1
- package/dist/assets/mermaid-parser.core-Vb9KKv1R.js +0 -4
- package/dist/assets/mermaid.core-C_7xsp3d.js +0 -11
- package/dist/assets/mindmap-definition-YRQLILUH-BWmfy5wB.js +0 -68
- package/dist/assets/ordinal-DIg8h6NI.js +0 -1
- package/dist/assets/packet-RMMSAZCW-Q-WG6o3b.js +0 -1
- package/dist/assets/path-DfRbCp9y.js +0 -1
- package/dist/assets/pie-UPGHQEXC-Cwi2tLlt.js +0 -1
- package/dist/assets/pieDiagram-SKSYHLDU-Dyf3X_in.js +0 -30
- package/dist/assets/quadrantDiagram-337W2JSQ-B5_5m61Q.js +0 -7
- package/dist/assets/radar-KQ55EAFF-Dtw2VzxY.js +0 -1
- package/dist/assets/requirementDiagram-Z7DCOOCP-BSERBnlW.js +0 -73
- package/dist/assets/rough.esm-KjoEK0it.js +0 -1
- package/dist/assets/sankeyDiagram-WA2Y5GQK-CMcEY8Cz.js +0 -10
- package/dist/assets/sequenceDiagram-2WXFIKYE-D28qcXwC.js +0 -145
- package/dist/assets/src-C8kkzlHX.js +0 -1
- package/dist/assets/stateDiagram-RAJIS63D-7oVrCmRl.js +0 -1
- package/dist/assets/stateDiagram-v2-FVOUBMTO-DtFptQAd.js +0 -1
- package/dist/assets/timeline-definition-YZTLITO2-rbCfBEvG.js +0 -61
- package/dist/assets/treemap-KZPCXAKY-BlRvF0um.js +0 -1
- package/dist/assets/vennDiagram-LZ73GAT5-DBit3zWa.js +0 -34
- package/dist/assets/xychartDiagram-JWTSCODW-BVYXv51y.js +0 -7
- 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
|
+
});
|