@peaske7/readit 0.3.0-rc.0 → 0.3.0-rc.1
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/dist/.vite/manifest.json +1111 -0
- package/dist/assets/_basePickBy-BMMA4Tou.js +1 -0
- package/dist/assets/_baseUniq-D40qku1I.js +1 -0
- package/dist/assets/arc-Ckg65iy8.js +1 -0
- package/dist/assets/architecture-YZFGNWBL-Dv3EY0zV.js +1 -0
- package/dist/assets/architectureDiagram-Q4EWVU46-ClRss4cm.js +36 -0
- package/dist/assets/array-Bjz-wYpJ.js +1 -0
- package/dist/assets/blockDiagram-DXYQGD6D-CBcFvoK1.js +132 -0
- package/dist/assets/c4Diagram-AHTNJAMY-D4d3ZLam.js +10 -0
- package/dist/assets/channel-D9EJxDy_.js +1 -0
- package/dist/assets/chunk-2KRD3SAO-DaFfaCGO.js +1 -0
- package/dist/assets/chunk-336JU56O-yLEQoF0v.js +2 -0
- package/dist/assets/chunk-426QAEUC-Uyzd4wAA.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-DRuTD7x5.js +1 -0
- package/dist/assets/chunk-4TB4RGXK-3xbpIi_o.js +206 -0
- package/dist/assets/chunk-55IACEB6-BExiaAoD.js +1 -0
- package/dist/assets/chunk-5FUZZQ4R-DatVvHnF.js +62 -0
- package/dist/assets/chunk-5PVQY5BW-BKgvrGh8.js +2 -0
- package/dist/assets/chunk-67CJDMHE-DMt8LNEX.js +1 -0
- package/dist/assets/chunk-7N4EOEYR-CzLGefVf.js +1 -0
- package/dist/assets/chunk-AA7GKIK3-B6GFAk4U.js +1 -0
- package/dist/assets/chunk-BSJP7CBP-BK29yehL.js +1 -0
- package/dist/assets/chunk-CIAEETIT-D7hBXImP.js +1 -0
- package/dist/assets/chunk-Dlc7tRH4.js +1 -0
- package/dist/assets/chunk-EDXVE4YY-PYJdlmyH.js +1 -0
- package/dist/assets/chunk-ENJZ2VHE-DUHKBv6x.js +10 -0
- package/dist/assets/chunk-FMBD7UC4-2FWyCCAV.js +15 -0
- package/dist/assets/chunk-FOC6F5B3-DKFHrt4K.js +1 -0
- package/dist/assets/chunk-ICPOFSXX-Bh__D0ec.js +122 -0
- package/dist/assets/chunk-K5T4RW27-D51O7IkG.js +94 -0
- package/dist/assets/chunk-KGLVRYIC-DMHSCH4T.js +1 -0
- package/dist/assets/chunk-LIHQZDEY-C2aANxt9.js +1 -0
- package/dist/assets/chunk-ORNJ4GCN-Db_37NRX.js +1 -0
- package/dist/assets/chunk-OYMX7WX6-BltXOJLJ.js +231 -0
- package/dist/assets/chunk-QZHKN3VN-8Lcg9gti.js +1 -0
- package/dist/assets/chunk-U2HBQHQK-ByS6tilY.js +70 -0
- package/dist/assets/chunk-X2U36JSP-Bm-4Gqg_.js +1 -0
- package/dist/assets/chunk-XPW4576I-Bqbompq4.js +32 -0
- package/dist/assets/chunk-YZCP3GAM-CsC0imPb.js +1 -0
- package/dist/assets/chunk-ZZ45TVLE-CG-CqfPC.js +1 -0
- package/dist/assets/classDiagram-6PBFFD2Q-Jy1uFUk4.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-ChiLl3rR.js +1 -0
- package/dist/assets/clone-BBjvuERA.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-q90QeGKv.js +1 -0
- package/dist/assets/cytoscape.esm-BfXff3fb.js +321 -0
- package/dist/assets/dagre-KV5264BT-BQWiLFJB.js +4 -0
- package/dist/assets/dagre-nn_aIZ2E.js +1 -0
- package/dist/assets/defaultLocale-BwmRmqJp.js +1 -0
- package/dist/assets/diagram-5BDNPKRD-CJa7Y97H.js +10 -0
- package/dist/assets/diagram-G4DWMVQ6-tVQGBWfY.js +24 -0
- package/dist/assets/diagram-MMDJMWI5-CpimFldm.js +43 -0
- package/dist/assets/diagram-TYMM5635-D11WQVgy.js +24 -0
- package/dist/assets/dist-BNz65Ibc.js +1 -0
- package/dist/assets/erDiagram-SMLLAGMA-C2bLd0jS.js +85 -0
- package/dist/assets/flowDiagram-DWJPFMVM-Kw3fOOLT.js +162 -0
- package/dist/assets/ganttDiagram-T4ZO3ILL-fyMhyE2X.js +292 -0
- package/dist/assets/gitGraph-7Q5UKJZL-BGFRt2qs.js +1 -0
- package/dist/assets/gitGraphDiagram-UUTBAWPF-D4JoiOvg.js +106 -0
- package/dist/assets/graphlib-DGcD9J2L.js +1 -0
- package/dist/assets/index-Cow3qpoq.css +2 -0
- package/dist/assets/index-DUf7okYi.js +14 -0
- package/dist/assets/info-OMHHGYJF-DI6-Z9vh.js +1 -0
- package/dist/assets/infoDiagram-42DDH7IO-D1ZkeMBy.js +2 -0
- package/dist/assets/init-TPm5RB77.js +1 -0
- package/dist/assets/isArrayLikeObject-69BLnVNM.js +1 -0
- package/dist/assets/isEmpty-DUS28g5f.js +1 -0
- package/dist/assets/ishikawaDiagram-UXIWVN3A-Dv8hzjZB.js +70 -0
- package/dist/assets/journeyDiagram-VCZTEJTY-COeB7F5r.js +139 -0
- package/dist/assets/kanban-definition-6JOO6SKY-BbYmxCYU.js +89 -0
- package/dist/assets/katex-5SGEXwpi.js +261 -0
- package/dist/assets/line-_v2NGEdn.js +1 -0
- package/dist/assets/linear-CXMqTN8N.js +1 -0
- package/dist/assets/mermaid-config-C8a4L22x.js +1 -0
- package/dist/assets/mermaid-parser.core-CFmphzPP.js +4 -0
- package/dist/assets/mermaid.core-DnHAupTp.js +11 -0
- package/dist/assets/mindmap-definition-QFDTVHPH-D7_lIep7.js +96 -0
- package/dist/assets/ordinal-D7l-8DAO.js +1 -0
- package/dist/assets/packet-4T2RLAQJ-DidW3JFc.js +1 -0
- package/dist/assets/path-BVpCanzE.js +1 -0
- package/dist/assets/pie-ZZUOXDRM-Bff2e5hg.js +1 -0
- package/dist/assets/pieDiagram-DEJITSTG-DDvYHCT_.js +30 -0
- package/dist/assets/quadrantDiagram-34T5L4WZ-DcLcIrdi.js +7 -0
- package/dist/assets/radar-PYXPWWZC-CsdZBH3M.js +1 -0
- package/dist/assets/requirementDiagram-MS252O5E-DLX6ld7D.js +84 -0
- package/dist/assets/rough.esm-BoTisKeL.js +1 -0
- package/dist/assets/sankeyDiagram-XADWPNL6-D-1GtsHM.js +10 -0
- package/dist/assets/sequenceDiagram-FGHM5R23-Bwxs0YQg.js +157 -0
- package/dist/assets/src-CrmkjRpa.js +1 -0
- package/dist/assets/stateDiagram-FHFEXIEX-DW7rOcnQ.js +1 -0
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-Jm-24vQ2.js +1 -0
- package/dist/assets/timeline-definition-GMOUNBTQ-DVdHyzxS.js +120 -0
- package/dist/assets/treeView-SZITEDCU-DPKseaET.js +1 -0
- package/dist/assets/treemap-W4RFUUIX-DH-7GZe_.js +1 -0
- package/dist/assets/vennDiagram-DHZGUBPP-DJaC6xmI.js +34 -0
- package/dist/assets/wardley-RL74JXVD-AgyXyBN5.js +1 -0
- package/dist/assets/wardleyDiagram-NUSXRM2D-CTKERPKv.js +20 -0
- package/dist/assets/xychartDiagram-5P7HB3ND-BuExiLXc.js +7 -0
- package/{index.html → dist/index.html} +2 -1
- package/dist/index.js +2539 -0
- package/package.json +11 -1
- package/.agents/skills/remotion-best-practices/SKILL.md +0 -61
- package/.agents/skills/remotion-best-practices/rules/3d.md +0 -86
- package/.agents/skills/remotion-best-practices/rules/animations.md +0 -27
- package/.agents/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +0 -178
- package/.agents/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +0 -100
- package/.agents/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +0 -108
- package/.agents/skills/remotion-best-practices/rules/assets.md +0 -78
- package/.agents/skills/remotion-best-practices/rules/audio-visualization.md +0 -198
- package/.agents/skills/remotion-best-practices/rules/audio.md +0 -169
- package/.agents/skills/remotion-best-practices/rules/calculate-metadata.md +0 -134
- package/.agents/skills/remotion-best-practices/rules/can-decode.md +0 -75
- package/.agents/skills/remotion-best-practices/rules/charts.md +0 -120
- package/.agents/skills/remotion-best-practices/rules/compositions.md +0 -154
- package/.agents/skills/remotion-best-practices/rules/display-captions.md +0 -184
- package/.agents/skills/remotion-best-practices/rules/extract-frames.md +0 -229
- package/.agents/skills/remotion-best-practices/rules/ffmpeg.md +0 -38
- package/.agents/skills/remotion-best-practices/rules/fonts.md +0 -152
- package/.agents/skills/remotion-best-practices/rules/get-audio-duration.md +0 -58
- package/.agents/skills/remotion-best-practices/rules/get-video-dimensions.md +0 -68
- package/.agents/skills/remotion-best-practices/rules/get-video-duration.md +0 -60
- package/.agents/skills/remotion-best-practices/rules/gifs.md +0 -141
- package/.agents/skills/remotion-best-practices/rules/images.md +0 -134
- package/.agents/skills/remotion-best-practices/rules/import-srt-captions.md +0 -69
- package/.agents/skills/remotion-best-practices/rules/light-leaks.md +0 -73
- package/.agents/skills/remotion-best-practices/rules/lottie.md +0 -70
- package/.agents/skills/remotion-best-practices/rules/maps.md +0 -412
- package/.agents/skills/remotion-best-practices/rules/measuring-dom-nodes.md +0 -34
- package/.agents/skills/remotion-best-practices/rules/measuring-text.md +0 -140
- package/.agents/skills/remotion-best-practices/rules/parameters.md +0 -109
- package/.agents/skills/remotion-best-practices/rules/sequencing.md +0 -118
- package/.agents/skills/remotion-best-practices/rules/sfx.md +0 -26
- package/.agents/skills/remotion-best-practices/rules/subtitles.md +0 -36
- package/.agents/skills/remotion-best-practices/rules/tailwind.md +0 -11
- package/.agents/skills/remotion-best-practices/rules/text-animations.md +0 -20
- package/.agents/skills/remotion-best-practices/rules/timing.md +0 -179
- package/.agents/skills/remotion-best-practices/rules/transcribe-captions.md +0 -70
- package/.agents/skills/remotion-best-practices/rules/transitions.md +0 -197
- package/.agents/skills/remotion-best-practices/rules/transparent-videos.md +0 -106
- package/.agents/skills/remotion-best-practices/rules/trimming.md +0 -51
- package/.agents/skills/remotion-best-practices/rules/videos.md +0 -171
- package/.agents/skills/remotion-best-practices/rules/voiceover.md +0 -99
- package/.agents/skills/simple/SKILL.md +0 -52
- package/.agents/skills/vercel-react-best-practices/AGENTS.md +0 -3254
- package/.agents/skills/vercel-react-best-practices/README.md +0 -123
- package/.agents/skills/vercel-react-best-practices/SKILL.md +0 -141
- package/.agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
- package/.agents/skills/vercel-react-best-practices/rules/advanced-init-once.md +0 -42
- package/.agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -39
- package/.agents/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -38
- package/.agents/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -80
- package/.agents/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -51
- package/.agents/skills/vercel-react-best-practices/rules/async-parallel.md +0 -28
- package/.agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
- package/.agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -59
- package/.agents/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
- package/.agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
- package/.agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
- package/.agents/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -50
- package/.agents/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
- package/.agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
- package/.agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
- package/.agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
- package/.agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -107
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
- package/.agents/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
- package/.agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
- package/.agents/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -50
- package/.agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +0 -60
- package/.agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
- package/.agents/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -37
- package/.agents/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -49
- package/.agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
- package/.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
- package/.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
- package/.agents/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -26
- package/.agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
- package/.agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
- package/.agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
- package/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +0 -30
- package/.agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +0 -85
- package/.agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +0 -68
- package/.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
- package/.agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +0 -75
- package/.agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
- package/.agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
- package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +0 -40
- package/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
- package/.agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
- package/.agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
- package/.agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +0 -38
- package/.agents/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -44
- package/.agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +0 -45
- package/.agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +0 -82
- package/.agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +0 -35
- package/.agents/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
- package/.agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +0 -73
- package/.agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
- package/.agents/skills/vercel-react-best-practices/rules/server-auth-actions.md +0 -96
- package/.agents/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
- package/.agents/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -76
- package/.agents/skills/vercel-react-best-practices/rules/server-dedup-props.md +0 -65
- package/.agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +0 -142
- package/.agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
- package/.agents/skills/vercel-react-best-practices/rules/server-serialization.md +0 -38
- package/.claude/CLAUDE.md +0 -184
- package/.claude/commands/review.md +0 -120
- package/.claude/commands/sync-docs.md +0 -71
- package/.claude/roadmap.md +0 -121
- package/.claude/rules/style-guide.md +0 -830
- package/.claude/settings.json +0 -18
- package/.claude/user-stories.md +0 -333
- package/AGENTS.md +0 -68
- package/Makefile +0 -32
- package/biome.json +0 -79
- package/bun.lock +0 -854
- package/bunfig.toml +0 -2
- package/docs/design.md +0 -563
- package/docs/perf-baseline.md +0 -130
- package/docs/plans/2026-03-13-client-mode-design.md +0 -86
- package/docs/plans/2026-03-13-client-mode-plan.md +0 -605
- package/docs/plans/2026-03-13-keyboard-shortcuts-design.md +0 -129
- package/docs/plans/2026-03-13-keyboard-shortcuts-plan.md +0 -1471
- package/docs/plans/2026-03-13-multi-document-design.md +0 -183
- package/docs/plans/2026-03-13-performance-benchmarks-design.md +0 -121
- package/docs/superpowers/plans/2026-03-26-surgical-pruning.md +0 -1176
- package/docs/superpowers/specs/2026-03-27-go-server-rewrite-design.md +0 -284
- package/e2e/comments.spec.ts +0 -81
- package/e2e/document-load.spec.ts +0 -32
- package/e2e/export.spec.ts +0 -58
- package/e2e/fixtures/sample.md +0 -7
- package/e2e/perf/add-comment.spec.ts +0 -116
- package/e2e/perf/fixtures/generate.ts +0 -327
- package/e2e/perf/initial-load.spec.ts +0 -49
- package/e2e/perf/perf.setup.ts +0 -23
- package/e2e/perf/perf.teardown.ts +0 -9
- package/e2e/perf/screenshot-final.png +0 -0
- package/e2e/perf/scroll.spec.ts +0 -39
- package/e2e/perf/tab-switch.spec.ts +0 -69
- package/e2e/perf/text-selection.spec.ts +0 -119
- package/e2e/perf/utils/metrics.ts +0 -350
- package/e2e/perf/utils/perf-cli.ts +0 -86
- package/e2e/persistence-file.spec.ts +0 -357
- package/e2e/utils/cli.ts +0 -84
- package/e2e/utils/selection.ts +0 -79
- package/go/cmd/readit/main.go +0 -416
- package/go/go.mod +0 -20
- package/go/go.sum +0 -41
- package/go/internal/server/anchor.go +0 -302
- package/go/internal/server/anchor_test.go +0 -111
- package/go/internal/server/comments.go +0 -390
- package/go/internal/server/documents.go +0 -113
- package/go/internal/server/embed.go +0 -17
- package/go/internal/server/headings.go +0 -33
- package/go/internal/server/headings_test.go +0 -75
- package/go/internal/server/htmltext.go +0 -123
- package/go/internal/server/markdown.go +0 -157
- package/go/internal/server/markdown_bench_test.go +0 -42
- package/go/internal/server/markdown_test.go +0 -79
- package/go/internal/server/server.go +0 -453
- package/go/internal/server/server_bench_test.go +0 -122
- package/go/internal/server/settings.go +0 -110
- package/go/internal/server/sse.go +0 -140
- package/go/internal/server/storage.go +0 -275
- package/go/internal/server/storage_test.go +0 -152
- package/go/internal/server/template.go +0 -66
- package/go/internal/server/types.go +0 -101
- package/go/internal/server/watcher.go +0 -74
- package/lefthook.yml +0 -8
- package/nvim-readit/lua/readit/health.lua +0 -64
- package/nvim-readit/lua/readit/init.lua +0 -463
- package/nvim-readit/plugin/readit.lua +0 -19
- package/playwright.config.ts +0 -34
- package/skills-lock.json +0 -20
- package/src/App.svelte +0 -890
- package/src/cli.ts +0 -881
- package/src/components/ActionsMenu.svelte +0 -95
- package/src/components/CommentBadge.svelte +0 -67
- package/src/components/CommentErrorBanner.svelte +0 -33
- package/src/components/CommentInput.svelte +0 -75
- package/src/components/CommentListItem.svelte +0 -95
- package/src/components/CommentManager.svelte +0 -129
- package/src/components/CommentNav.svelte +0 -109
- package/src/components/DocumentViewer.svelte +0 -233
- package/src/components/FloatingComment.svelte +0 -107
- package/src/components/Header.svelte +0 -76
- package/src/components/InlineEditor.svelte +0 -72
- package/src/components/MarginNote.svelte +0 -167
- package/src/components/MarginNotesContainer.svelte +0 -33
- package/src/components/MermaidEnhancer.svelte +0 -218
- package/src/components/MermaidModal.svelte +0 -67
- package/src/components/RawModal.svelte +0 -126
- package/src/components/ReanchorConfirm.svelte +0 -30
- package/src/components/SettingsModal.svelte +0 -220
- package/src/components/ShortcutCapture.svelte +0 -82
- package/src/components/ShortcutList.svelte +0 -145
- package/src/components/TabBar.svelte +0 -52
- package/src/components/TableOfContents.svelte +0 -125
- package/src/components/ui/ActionLink.svelte +0 -40
- package/src/components/ui/Button.svelte +0 -53
- package/src/components/ui/Dialog.svelte +0 -97
- package/src/components/ui/DropdownMenu.svelte +0 -85
- package/src/components/ui/DropdownMenuItem.svelte +0 -38
- package/src/components/ui/DropdownMenuSeparator.svelte +0 -11
- package/src/components/ui/Text.svelte +0 -42
- package/src/env.d.ts +0 -6
- package/src/index.css +0 -859
- package/src/lib/__fixtures__/bench-data.ts +0 -114
- package/src/lib/anchor.bench.ts +0 -91
- package/src/lib/anchor.test.ts +0 -527
- package/src/lib/anchor.ts +0 -381
- package/src/lib/comment-storage.bench.ts +0 -49
- package/src/lib/comment-storage.test.ts +0 -694
- package/src/lib/comment-storage.ts +0 -226
- package/src/lib/export.bench.ts +0 -21
- package/src/lib/export.ts +0 -36
- package/src/lib/fetch-or-throw.test.ts +0 -59
- package/src/lib/fetch-or-throw.ts +0 -12
- package/src/lib/headings.test.ts +0 -103
- package/src/lib/headings.ts +0 -44
- package/src/lib/highlight/core.test.ts +0 -93
- package/src/lib/highlight/dom.ts +0 -187
- package/src/lib/highlight/highlight-registry.ts +0 -221
- package/src/lib/highlight/highlight.bench.ts +0 -92
- package/src/lib/highlight/highlighter.ts +0 -247
- package/src/lib/highlight/resolver.ts +0 -38
- package/src/lib/highlight/types.ts +0 -17
- package/src/lib/html-text.test.ts +0 -162
- package/src/lib/html-text.ts +0 -161
- package/src/lib/i18n/en.ts +0 -124
- package/src/lib/i18n/index.ts +0 -3
- package/src/lib/i18n/ja.ts +0 -126
- package/src/lib/i18n/translations.ts +0 -27
- package/src/lib/i18n/types.ts +0 -130
- package/src/lib/key-lock.test.ts +0 -104
- package/src/lib/key-lock.ts +0 -23
- package/src/lib/margin-layout.bench.ts +0 -61
- package/src/lib/margin-layout.ts +0 -71
- package/src/lib/markdown-renderer.test.ts +0 -154
- package/src/lib/markdown-renderer.ts +0 -178
- package/src/lib/mermaid-config.ts +0 -38
- package/src/lib/mermaid-renderer.ts +0 -162
- package/src/lib/mermaid-worker.ts +0 -60
- package/src/lib/positions.ts +0 -157
- package/src/lib/shortcut-registry.ts +0 -244
- package/src/lib/utils.ts +0 -15
- package/src/main.ts +0 -16
- package/src/schema.ts +0 -92
- package/src/server.ts +0 -1216
- package/src/stores/app.svelte.ts +0 -231
- package/src/stores/locale.svelte.ts +0 -46
- package/src/stores/settings.svelte.ts +0 -90
- package/src/stores/shortcuts.svelte.ts +0 -104
- package/src/stores/ui.svelte.ts +0 -12
- package/src/template.ts +0 -104
- package/src/test-setup.ts +0 -48
- package/svelte.config.js +0 -5
- package/test.md +0 -74
- package/tsconfig.cli.json +0 -12
- package/tsconfig.json +0 -20
- package/vite.config.ts +0 -47
- package/vitest.config.ts +0 -15
- package/vscode-readit/.mcp.json +0 -7
- package/vscode-readit/.vscodeignore +0 -7
- package/vscode-readit/bun.lock +0 -78
- package/vscode-readit/icon.svg +0 -10
- package/vscode-readit/package.json +0 -110
- package/vscode-readit/src/extension.ts +0 -117
- package/vscode-readit/src/server-manager.ts +0 -272
- package/vscode-readit/src/webview-provider.ts +0 -204
- package/vscode-readit/tsconfig.json +0 -20
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
existsSync,
|
|
3
|
-
mkdirSync,
|
|
4
|
-
readFileSync,
|
|
5
|
-
rmSync,
|
|
6
|
-
writeFileSync,
|
|
7
|
-
} from "node:fs";
|
|
8
|
-
import * as os from "node:os";
|
|
9
|
-
import { dirname, join, resolve } from "node:path";
|
|
10
|
-
import { expect, test } from "@playwright/test";
|
|
11
|
-
import { spawnCli } from "./utils/cli";
|
|
12
|
-
import { addComment, selectTextInArticle } from "./utils/selection";
|
|
13
|
-
|
|
14
|
-
const FIXTURES_DIR = resolve(import.meta.dirname, "fixtures");
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Get the expected comment file path for a source file.
|
|
18
|
-
*/
|
|
19
|
-
function getCommentPath(sourcePath: string): string {
|
|
20
|
-
const absolute = resolve(sourcePath);
|
|
21
|
-
const normalized = absolute.replace(/^\//, "").replace(/^[A-Z]:[\\/]/, "");
|
|
22
|
-
const ext = normalized.lastIndexOf(".");
|
|
23
|
-
const withoutExt = ext > 0 ? normalized.slice(0, ext) : normalized;
|
|
24
|
-
return join(os.homedir(), ".readit", "comments", `${withoutExt}.comments.md`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Clean up comment file for a source file.
|
|
29
|
-
*/
|
|
30
|
-
function cleanupCommentFile(sourcePath: string): void {
|
|
31
|
-
const commentPath = getCommentPath(sourcePath);
|
|
32
|
-
if (existsSync(commentPath)) {
|
|
33
|
-
rmSync(commentPath);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
test.describe("File-Based Comment Persistence", () => {
|
|
38
|
-
const sampleMdPath = resolve(FIXTURES_DIR, "sample.md");
|
|
39
|
-
|
|
40
|
-
test.beforeEach(() => {
|
|
41
|
-
// Clean up any existing comment file before each test
|
|
42
|
-
cleanupCommentFile(sampleMdPath);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test.afterEach(() => {
|
|
46
|
-
// Clean up after each test
|
|
47
|
-
cleanupCommentFile(sampleMdPath);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("comments are saved to markdown file", async ({ page }) => {
|
|
51
|
-
const { url, cleanup } = await spawnCli(sampleMdPath, {
|
|
52
|
-
port: 4590,
|
|
53
|
-
clean: false,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
await page.goto(url);
|
|
58
|
-
|
|
59
|
-
// Wait for document to load
|
|
60
|
-
const article = page.locator("article#document-content");
|
|
61
|
-
await expect(article).toBeVisible();
|
|
62
|
-
|
|
63
|
-
// Add a comment
|
|
64
|
-
const textToSelect = "testing text selection";
|
|
65
|
-
await selectTextInArticle(page, textToSelect);
|
|
66
|
-
|
|
67
|
-
const commentText = "This is a test comment for file persistence";
|
|
68
|
-
await addComment(page, commentText);
|
|
69
|
-
|
|
70
|
-
// Wait for comment to be visible in UI
|
|
71
|
-
await expect(page.locator("body")).toContainText(commentText);
|
|
72
|
-
|
|
73
|
-
// Give time for the file to be written
|
|
74
|
-
await page.waitForTimeout(500);
|
|
75
|
-
|
|
76
|
-
// Verify the comment file was created
|
|
77
|
-
const commentPath = getCommentPath(sampleMdPath);
|
|
78
|
-
expect(existsSync(commentPath)).toBe(true);
|
|
79
|
-
|
|
80
|
-
// Verify the file contains the comment
|
|
81
|
-
const fileContent = readFileSync(commentPath, "utf-8");
|
|
82
|
-
expect(fileContent).toContain(textToSelect);
|
|
83
|
-
expect(fileContent).toContain(commentText);
|
|
84
|
-
expect(fileContent).toContain("version: 1");
|
|
85
|
-
} finally {
|
|
86
|
-
await cleanup();
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("comments persist across page reload", async ({ page }) => {
|
|
91
|
-
const { url, cleanup } = await spawnCli(sampleMdPath, {
|
|
92
|
-
port: 4591,
|
|
93
|
-
clean: false,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
await page.goto(url);
|
|
98
|
-
|
|
99
|
-
const article = page.locator("article#document-content");
|
|
100
|
-
await expect(article).toBeVisible();
|
|
101
|
-
|
|
102
|
-
// Add a comment
|
|
103
|
-
const textToSelect = "testing text selection";
|
|
104
|
-
await selectTextInArticle(page, textToSelect);
|
|
105
|
-
|
|
106
|
-
const commentText = "Persistent comment test";
|
|
107
|
-
await addComment(page, commentText);
|
|
108
|
-
|
|
109
|
-
// Wait for comment to appear
|
|
110
|
-
await expect(page.locator("body")).toContainText(commentText);
|
|
111
|
-
|
|
112
|
-
// Reload the page
|
|
113
|
-
await page.reload();
|
|
114
|
-
|
|
115
|
-
// Wait for document to reload
|
|
116
|
-
await expect(article).toBeVisible();
|
|
117
|
-
|
|
118
|
-
// Verify comment still exists after reload
|
|
119
|
-
await expect(page.locator("body")).toContainText(commentText);
|
|
120
|
-
|
|
121
|
-
// Verify highlight still exists
|
|
122
|
-
await page.waitForFunction(
|
|
123
|
-
() => {
|
|
124
|
-
const h = (window as unknown as Record<string, unknown>)
|
|
125
|
-
.__readitHighlights as { commentIds: string[] } | undefined;
|
|
126
|
-
return h && h.commentIds.length > 0;
|
|
127
|
-
},
|
|
128
|
-
{ timeout: 10_000 },
|
|
129
|
-
);
|
|
130
|
-
} finally {
|
|
131
|
-
await cleanup();
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test("comments persist across server restart", async ({ page }) => {
|
|
136
|
-
const PORT = 4592;
|
|
137
|
-
|
|
138
|
-
// First session: add a comment
|
|
139
|
-
const { url: url1, cleanup: cleanup1 } = await spawnCli(sampleMdPath, {
|
|
140
|
-
port: PORT,
|
|
141
|
-
clean: false,
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
const commentText = "Comment that survives restart";
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
await page.goto(url1);
|
|
148
|
-
|
|
149
|
-
const article = page.locator("article#document-content");
|
|
150
|
-
await expect(article).toBeVisible();
|
|
151
|
-
|
|
152
|
-
await selectTextInArticle(page, "testing text selection");
|
|
153
|
-
await addComment(page, commentText);
|
|
154
|
-
|
|
155
|
-
// Wait for comment to appear
|
|
156
|
-
await expect(page.locator("body")).toContainText(commentText);
|
|
157
|
-
} finally {
|
|
158
|
-
await cleanup1();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Wait for server to fully shut down
|
|
162
|
-
await page.waitForTimeout(1000);
|
|
163
|
-
|
|
164
|
-
// Second session: verify comment persists
|
|
165
|
-
const { url: url2, cleanup: cleanup2 } = await spawnCli(sampleMdPath, {
|
|
166
|
-
port: PORT,
|
|
167
|
-
clean: false,
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
await page.goto(url2);
|
|
172
|
-
|
|
173
|
-
const article = page.locator("article#document-content");
|
|
174
|
-
await expect(article).toBeVisible();
|
|
175
|
-
|
|
176
|
-
// Comment should still exist from previous session
|
|
177
|
-
await expect(page.locator("body")).toContainText(commentText);
|
|
178
|
-
|
|
179
|
-
// Highlight should still exist
|
|
180
|
-
await page.waitForFunction(
|
|
181
|
-
() => {
|
|
182
|
-
const h = (window as unknown as Record<string, unknown>)
|
|
183
|
-
.__readitHighlights as { commentIds: string[] } | undefined;
|
|
184
|
-
return h && h.commentIds.length > 0;
|
|
185
|
-
},
|
|
186
|
-
{ timeout: 10_000 },
|
|
187
|
-
);
|
|
188
|
-
} finally {
|
|
189
|
-
await cleanup2();
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test("--clean flag deletes comment file", async ({ page }) => {
|
|
194
|
-
const PORT = 4593;
|
|
195
|
-
|
|
196
|
-
// First, create a comment file manually
|
|
197
|
-
const commentPath = getCommentPath(sampleMdPath);
|
|
198
|
-
const commentDir = dirname(commentPath);
|
|
199
|
-
mkdirSync(commentDir, { recursive: true });
|
|
200
|
-
writeFileSync(
|
|
201
|
-
commentPath,
|
|
202
|
-
`---
|
|
203
|
-
source: ${sampleMdPath}
|
|
204
|
-
hash: abc123
|
|
205
|
-
version: 1
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
<!-- c:12345678|L5|2024-12-24T10:00:00Z -->
|
|
209
|
-
> testing text selection
|
|
210
|
-
|
|
211
|
-
Pre-existing comment to be cleared.
|
|
212
|
-
|
|
213
|
-
---
|
|
214
|
-
`,
|
|
215
|
-
"utf-8",
|
|
216
|
-
);
|
|
217
|
-
|
|
218
|
-
expect(existsSync(commentPath)).toBe(true);
|
|
219
|
-
|
|
220
|
-
// Start server with --clean flag
|
|
221
|
-
const { url, cleanup } = await spawnCli(sampleMdPath, {
|
|
222
|
-
port: PORT,
|
|
223
|
-
clean: true,
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
try {
|
|
227
|
-
await page.goto(url);
|
|
228
|
-
|
|
229
|
-
const article = page.locator("article#document-content");
|
|
230
|
-
await expect(article).toBeVisible();
|
|
231
|
-
|
|
232
|
-
// Wait for clean operation to complete
|
|
233
|
-
await page.waitForTimeout(500);
|
|
234
|
-
|
|
235
|
-
// Verify no highlights via observability hook
|
|
236
|
-
const highlightCount = await page.evaluate(() => {
|
|
237
|
-
const h = (window as unknown as Record<string, unknown>)
|
|
238
|
-
.__readitHighlights as { commentIds: string[] } | undefined;
|
|
239
|
-
return h?.commentIds?.length ?? 0;
|
|
240
|
-
});
|
|
241
|
-
expect(highlightCount).toBe(0);
|
|
242
|
-
} finally {
|
|
243
|
-
await cleanup();
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
test("edit updates the comment file", async ({ page }) => {
|
|
248
|
-
const { url, cleanup } = await spawnCli(sampleMdPath, {
|
|
249
|
-
port: 4594,
|
|
250
|
-
clean: false,
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
try {
|
|
254
|
-
await page.goto(url);
|
|
255
|
-
|
|
256
|
-
const article = page.locator("article#document-content");
|
|
257
|
-
await expect(article).toBeVisible();
|
|
258
|
-
|
|
259
|
-
// Add initial comment
|
|
260
|
-
const textToSelect = "testing text selection";
|
|
261
|
-
await selectTextInArticle(page, textToSelect);
|
|
262
|
-
const initialComment = "Initial comment text";
|
|
263
|
-
await addComment(page, initialComment);
|
|
264
|
-
|
|
265
|
-
await expect(page.locator("body")).toContainText(initialComment);
|
|
266
|
-
|
|
267
|
-
// Wait for file to be written
|
|
268
|
-
await page.waitForTimeout(500);
|
|
269
|
-
|
|
270
|
-
// Verify initial comment in file
|
|
271
|
-
const commentPath = getCommentPath(sampleMdPath);
|
|
272
|
-
let fileContent = readFileSync(commentPath, "utf-8");
|
|
273
|
-
expect(fileContent).toContain(initialComment);
|
|
274
|
-
|
|
275
|
-
// Find the margin note by data-comment-id (first one)
|
|
276
|
-
const marginNote = page.locator("[data-comment-id]").first();
|
|
277
|
-
|
|
278
|
-
// Force-click edit button (may be hidden behind opacity-0 group-hover)
|
|
279
|
-
const editButton = marginNote.getByText("Edit").first();
|
|
280
|
-
await editButton.click({ force: true });
|
|
281
|
-
|
|
282
|
-
// Wait for edit mode — find textarea globally since hasText filter breaks in edit mode
|
|
283
|
-
const textarea = page.locator("[data-comment-id] textarea");
|
|
284
|
-
await expect(textarea).toBeVisible();
|
|
285
|
-
|
|
286
|
-
// Clear and type new text
|
|
287
|
-
await textarea.fill("Updated comment text");
|
|
288
|
-
|
|
289
|
-
// Save edit
|
|
290
|
-
const saveButton = page
|
|
291
|
-
.locator("[data-comment-id]")
|
|
292
|
-
.getByText("Save")
|
|
293
|
-
.first();
|
|
294
|
-
await saveButton.click();
|
|
295
|
-
|
|
296
|
-
// Wait for file to be updated
|
|
297
|
-
await page.waitForTimeout(500);
|
|
298
|
-
|
|
299
|
-
// Verify updated comment in file
|
|
300
|
-
fileContent = readFileSync(commentPath, "utf-8");
|
|
301
|
-
expect(fileContent).toContain("Updated comment text");
|
|
302
|
-
} finally {
|
|
303
|
-
await cleanup();
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
test("delete removes comment from file", async ({ page }) => {
|
|
308
|
-
const { url, cleanup } = await spawnCli(sampleMdPath, {
|
|
309
|
-
port: 4595,
|
|
310
|
-
clean: false,
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
try {
|
|
314
|
-
await page.goto(url);
|
|
315
|
-
|
|
316
|
-
const article = page.locator("article#document-content");
|
|
317
|
-
await expect(article).toBeVisible();
|
|
318
|
-
|
|
319
|
-
// Add a comment
|
|
320
|
-
await selectTextInArticle(page, "testing text selection");
|
|
321
|
-
const commentText = "Comment to be deleted";
|
|
322
|
-
await addComment(page, commentText);
|
|
323
|
-
|
|
324
|
-
await expect(page.locator("body")).toContainText(commentText);
|
|
325
|
-
|
|
326
|
-
// Wait for file to be written
|
|
327
|
-
await page.waitForTimeout(500);
|
|
328
|
-
|
|
329
|
-
// Verify comment in file
|
|
330
|
-
const commentPath = getCommentPath(sampleMdPath);
|
|
331
|
-
let fileContent = readFileSync(commentPath, "utf-8");
|
|
332
|
-
expect(fileContent).toContain(commentText);
|
|
333
|
-
|
|
334
|
-
// Find the margin note containing the comment
|
|
335
|
-
const marginNote = page
|
|
336
|
-
.locator(".group")
|
|
337
|
-
.filter({ hasText: commentText })
|
|
338
|
-
.first();
|
|
339
|
-
await marginNote.hover();
|
|
340
|
-
|
|
341
|
-
// Click delete button
|
|
342
|
-
const deleteButton = marginNote.locator('button:has-text("Delete")');
|
|
343
|
-
await deleteButton.click();
|
|
344
|
-
|
|
345
|
-
// Wait for file to be updated
|
|
346
|
-
await page.waitForTimeout(500);
|
|
347
|
-
|
|
348
|
-
// Verify comment removed from file (file may be deleted if no comments left)
|
|
349
|
-
if (existsSync(commentPath)) {
|
|
350
|
-
fileContent = readFileSync(commentPath, "utf-8");
|
|
351
|
-
expect(fileContent).not.toContain(commentText);
|
|
352
|
-
}
|
|
353
|
-
} finally {
|
|
354
|
-
await cleanup();
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
});
|
package/e2e/utils/cli.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import type { ChildProcess } from "node:child_process";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
-
|
|
5
|
-
interface SpawnCliResult {
|
|
6
|
-
url: string;
|
|
7
|
-
process: ChildProcess;
|
|
8
|
-
cleanup: () => Promise<void>;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface SpawnCliOptions {
|
|
12
|
-
port?: number;
|
|
13
|
-
clean?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const CLI_PATH = resolve(import.meta.dirname, "../../dist/index.js");
|
|
17
|
-
|
|
18
|
-
export async function spawnCli(
|
|
19
|
-
fixturePath: string,
|
|
20
|
-
options: SpawnCliOptions = {},
|
|
21
|
-
): Promise<SpawnCliResult> {
|
|
22
|
-
const { port = 4567, clean = true } = options;
|
|
23
|
-
|
|
24
|
-
const args = [CLI_PATH, fixturePath, "--no-open", "--port", String(port)];
|
|
25
|
-
if (clean) {
|
|
26
|
-
args.push("--clean");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const cliProcess = spawn("bun", args, {
|
|
30
|
-
cwd: resolve(import.meta.dirname, "../.."),
|
|
31
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const url = await waitForServerReady(cliProcess);
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
url,
|
|
38
|
-
process: cliProcess,
|
|
39
|
-
cleanup: async () => {
|
|
40
|
-
cliProcess.kill("SIGTERM");
|
|
41
|
-
await new Promise<void>((resolve) => {
|
|
42
|
-
cliProcess.once("exit", () => resolve());
|
|
43
|
-
setTimeout(resolve, 1000); // Fallback timeout
|
|
44
|
-
});
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function waitForServerReady(cliProcess: ChildProcess): Promise<string> {
|
|
50
|
-
return new Promise((resolve, reject) => {
|
|
51
|
-
let output = "";
|
|
52
|
-
|
|
53
|
-
const timeout = setTimeout(() => {
|
|
54
|
-
reject(new Error("Server did not start within timeout"));
|
|
55
|
-
}, 10_000);
|
|
56
|
-
|
|
57
|
-
cliProcess.stdout?.on("data", (data: Buffer) => {
|
|
58
|
-
output += data.toString();
|
|
59
|
-
|
|
60
|
-
// Look for "URL: http://..." in output
|
|
61
|
-
const urlMatch = output.match(/URL:\s+(http:\/\/[^\s]+)/);
|
|
62
|
-
if (urlMatch) {
|
|
63
|
-
clearTimeout(timeout);
|
|
64
|
-
resolve(urlMatch[1]);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
cliProcess.stderr?.on("data", (data: Buffer) => {
|
|
69
|
-
console.error("[CLI stderr]", data.toString());
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
cliProcess.on("error", (err) => {
|
|
73
|
-
clearTimeout(timeout);
|
|
74
|
-
reject(err);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
cliProcess.on("exit", (code) => {
|
|
78
|
-
if (code !== 0 && code !== null) {
|
|
79
|
-
clearTimeout(timeout);
|
|
80
|
-
reject(new Error(`CLI exited with code ${code}: ${output}`));
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
}
|
package/e2e/utils/selection.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { Page } from "@playwright/test";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Wait for the Svelte app to be fully mounted and interactive.
|
|
5
|
-
* The app sets data-readit-ready="true" on <html> after onMount.
|
|
6
|
-
*/
|
|
7
|
-
export async function waitForAppReady(page: Page): Promise<void> {
|
|
8
|
-
await page.waitForFunction(
|
|
9
|
-
() => document.documentElement.dataset.readitReady === "true",
|
|
10
|
-
{ timeout: 10_000 },
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Select text within an article element (for markdown documents)
|
|
16
|
-
* Uses custom event to trigger selection handler (workaround for Playwright mouse issues)
|
|
17
|
-
*/
|
|
18
|
-
export async function selectTextInArticle(
|
|
19
|
-
page: Page,
|
|
20
|
-
textToSelect: string,
|
|
21
|
-
): Promise<void> {
|
|
22
|
-
// Ensure the Svelte app is fully mounted before dispatching events
|
|
23
|
-
await waitForAppReady(page);
|
|
24
|
-
|
|
25
|
-
// Find text and calculate offsets, then dispatch custom event
|
|
26
|
-
await page.evaluate((text) => {
|
|
27
|
-
const article = document.querySelector("article#document-content");
|
|
28
|
-
if (!article) throw new Error("Article element not found");
|
|
29
|
-
|
|
30
|
-
const walker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
|
|
31
|
-
let currentOffset = 0;
|
|
32
|
-
|
|
33
|
-
while (walker.nextNode()) {
|
|
34
|
-
const textNode = walker.currentNode as Text;
|
|
35
|
-
const content = textNode.textContent || "";
|
|
36
|
-
const index = content.indexOf(text);
|
|
37
|
-
|
|
38
|
-
if (index !== -1) {
|
|
39
|
-
const startOffset = currentOffset + index;
|
|
40
|
-
const endOffset = startOffset + text.length;
|
|
41
|
-
|
|
42
|
-
// Dispatch custom event that DocumentViewer listens for
|
|
43
|
-
const event = new CustomEvent("test:select-text", {
|
|
44
|
-
detail: { text, startOffset, endOffset },
|
|
45
|
-
});
|
|
46
|
-
window.dispatchEvent(event);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
currentOffset += content.length;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
throw new Error(`Text "${text}" not found in article`);
|
|
54
|
-
}, textToSelect);
|
|
55
|
-
|
|
56
|
-
// Wait for Svelte to process the selection
|
|
57
|
-
await page.waitForTimeout(100);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Add a comment to the current selection
|
|
62
|
-
* Assumes CommentInputArea is visible
|
|
63
|
-
*/
|
|
64
|
-
export async function addComment(
|
|
65
|
-
page: Page,
|
|
66
|
-
commentText: string,
|
|
67
|
-
): Promise<void> {
|
|
68
|
-
const textarea = page.locator('textarea[placeholder="Add your comment..."]');
|
|
69
|
-
await textarea.waitFor({ state: "visible", timeout: 10000 });
|
|
70
|
-
|
|
71
|
-
// Fill in the comment
|
|
72
|
-
await textarea.fill(commentText);
|
|
73
|
-
|
|
74
|
-
// Click the Add button
|
|
75
|
-
await page.getByRole("button", { name: "Add" }).click();
|
|
76
|
-
|
|
77
|
-
// Wait for the textarea to disappear (comment submitted)
|
|
78
|
-
await textarea.waitFor({ state: "hidden", timeout: 5000 });
|
|
79
|
-
}
|