docrev 0.8.1 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +9 -0
- package/PLAN-tables-and-postprocess.md +850 -0
- package/README.md +33 -0
- package/bin/rev.js +12 -131
- package/bin/rev.ts +145 -0
- package/dist/bin/rev.d.ts +9 -0
- package/dist/bin/rev.d.ts.map +1 -0
- package/dist/bin/rev.js +118 -0
- package/dist/bin/rev.js.map +1 -0
- package/dist/lib/annotations.d.ts +91 -0
- package/dist/lib/annotations.d.ts.map +1 -0
- package/dist/lib/annotations.js +554 -0
- package/dist/lib/annotations.js.map +1 -0
- package/dist/lib/build.d.ts +171 -0
- package/dist/lib/build.d.ts.map +1 -0
- package/dist/lib/build.js +755 -0
- package/dist/lib/build.js.map +1 -0
- package/dist/lib/citations.d.ts +34 -0
- package/dist/lib/citations.d.ts.map +1 -0
- package/dist/lib/citations.js +140 -0
- package/dist/lib/citations.js.map +1 -0
- package/dist/lib/commands/build.d.ts +13 -0
- package/dist/lib/commands/build.d.ts.map +1 -0
- package/dist/lib/commands/build.js +678 -0
- package/dist/lib/commands/build.js.map +1 -0
- package/dist/lib/commands/citations.d.ts +11 -0
- package/dist/lib/commands/citations.d.ts.map +1 -0
- package/dist/lib/commands/citations.js +428 -0
- package/dist/lib/commands/citations.js.map +1 -0
- package/dist/lib/commands/comments.d.ts +11 -0
- package/dist/lib/commands/comments.d.ts.map +1 -0
- package/dist/lib/commands/comments.js +883 -0
- package/dist/lib/commands/comments.js.map +1 -0
- package/dist/lib/commands/context.d.ts +35 -0
- package/dist/lib/commands/context.d.ts.map +1 -0
- package/dist/lib/commands/context.js +59 -0
- package/dist/lib/commands/context.js.map +1 -0
- package/dist/lib/commands/core.d.ts +11 -0
- package/dist/lib/commands/core.d.ts.map +1 -0
- package/dist/lib/commands/core.js +246 -0
- package/dist/lib/commands/core.js.map +1 -0
- package/dist/lib/commands/doi.d.ts +11 -0
- package/dist/lib/commands/doi.d.ts.map +1 -0
- package/dist/lib/commands/doi.js +373 -0
- package/dist/lib/commands/doi.js.map +1 -0
- package/dist/lib/commands/history.d.ts +11 -0
- package/dist/lib/commands/history.d.ts.map +1 -0
- package/dist/lib/commands/history.js +245 -0
- package/dist/lib/commands/history.js.map +1 -0
- package/dist/lib/commands/index.d.ts +28 -0
- package/dist/lib/commands/index.d.ts.map +1 -0
- package/dist/lib/commands/index.js +35 -0
- package/dist/lib/commands/index.js.map +1 -0
- package/dist/lib/commands/init.d.ts +11 -0
- package/dist/lib/commands/init.d.ts.map +1 -0
- package/dist/lib/commands/init.js +209 -0
- package/dist/lib/commands/init.js.map +1 -0
- package/dist/lib/commands/response.d.ts +11 -0
- package/dist/lib/commands/response.d.ts.map +1 -0
- package/dist/lib/commands/response.js +317 -0
- package/dist/lib/commands/response.js.map +1 -0
- package/dist/lib/commands/sections.d.ts +11 -0
- package/dist/lib/commands/sections.d.ts.map +1 -0
- package/dist/lib/commands/sections.js +1071 -0
- package/dist/lib/commands/sections.js.map +1 -0
- package/dist/lib/commands/utilities.d.ts +19 -0
- package/dist/lib/commands/utilities.d.ts.map +1 -0
- package/dist/lib/commands/utilities.js +2009 -0
- package/dist/lib/commands/utilities.js.map +1 -0
- package/dist/lib/comment-realign.d.ts +50 -0
- package/dist/lib/comment-realign.d.ts.map +1 -0
- package/dist/lib/comment-realign.js +372 -0
- package/dist/lib/comment-realign.js.map +1 -0
- package/dist/lib/config.d.ts +41 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +76 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/crossref.d.ts +108 -0
- package/dist/lib/crossref.d.ts.map +1 -0
- package/dist/lib/crossref.js +597 -0
- package/dist/lib/crossref.js.map +1 -0
- package/dist/lib/dependencies.d.ts +30 -0
- package/dist/lib/dependencies.d.ts.map +1 -0
- package/dist/lib/dependencies.js +95 -0
- package/dist/lib/dependencies.js.map +1 -0
- package/dist/lib/doi-cache.d.ts +29 -0
- package/dist/lib/doi-cache.d.ts.map +1 -0
- package/dist/lib/doi-cache.js +104 -0
- package/dist/lib/doi-cache.js.map +1 -0
- package/dist/lib/doi.d.ts +65 -0
- package/dist/lib/doi.d.ts.map +1 -0
- package/dist/lib/doi.js +710 -0
- package/dist/lib/doi.js.map +1 -0
- package/dist/lib/equations.d.ts +61 -0
- package/dist/lib/equations.d.ts.map +1 -0
- package/dist/lib/equations.js +445 -0
- package/dist/lib/equations.js.map +1 -0
- package/dist/lib/errors.d.ts +60 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +303 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/format.d.ts +104 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +416 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/git.d.ts +88 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +304 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/grammar.d.ts +62 -0
- package/dist/lib/grammar.d.ts.map +1 -0
- package/dist/lib/grammar.js +244 -0
- package/dist/lib/grammar.js.map +1 -0
- package/dist/lib/image-registry.d.ts +68 -0
- package/dist/lib/image-registry.d.ts.map +1 -0
- package/dist/lib/image-registry.js +112 -0
- package/dist/lib/image-registry.js.map +1 -0
- package/dist/lib/import.d.ts +184 -0
- package/dist/lib/import.d.ts.map +1 -0
- package/dist/lib/import.js +1581 -0
- package/dist/lib/import.js.map +1 -0
- package/dist/lib/journals.d.ts +55 -0
- package/dist/lib/journals.d.ts.map +1 -0
- package/dist/lib/journals.js +417 -0
- package/dist/lib/journals.js.map +1 -0
- package/dist/lib/merge.d.ts +138 -0
- package/dist/lib/merge.d.ts.map +1 -0
- package/dist/lib/merge.js +603 -0
- package/dist/lib/merge.js.map +1 -0
- package/dist/lib/orcid.d.ts +36 -0
- package/dist/lib/orcid.d.ts.map +1 -0
- package/dist/lib/orcid.js +117 -0
- package/dist/lib/orcid.js.map +1 -0
- package/dist/lib/pdf-comments.d.ts +95 -0
- package/dist/lib/pdf-comments.d.ts.map +1 -0
- package/dist/lib/pdf-comments.js +192 -0
- package/dist/lib/pdf-comments.js.map +1 -0
- package/dist/lib/pdf-import.d.ts +118 -0
- package/dist/lib/pdf-import.d.ts.map +1 -0
- package/dist/lib/pdf-import.js +397 -0
- package/dist/lib/pdf-import.js.map +1 -0
- package/dist/lib/plugins.d.ts +76 -0
- package/dist/lib/plugins.d.ts.map +1 -0
- package/dist/lib/plugins.js +235 -0
- package/dist/lib/plugins.js.map +1 -0
- package/dist/lib/postprocess.d.ts +42 -0
- package/dist/lib/postprocess.d.ts.map +1 -0
- package/dist/lib/postprocess.js +138 -0
- package/dist/lib/postprocess.js.map +1 -0
- package/dist/lib/pptx-template.d.ts +59 -0
- package/dist/lib/pptx-template.d.ts.map +1 -0
- package/dist/lib/pptx-template.js +613 -0
- package/dist/lib/pptx-template.js.map +1 -0
- package/dist/lib/pptx-themes.d.ts +80 -0
- package/dist/lib/pptx-themes.d.ts.map +1 -0
- package/dist/lib/pptx-themes.js +818 -0
- package/dist/lib/pptx-themes.js.map +1 -0
- package/dist/lib/protect-restore.d.ts +137 -0
- package/dist/lib/protect-restore.d.ts.map +1 -0
- package/dist/lib/protect-restore.js +394 -0
- package/dist/lib/protect-restore.js.map +1 -0
- package/dist/lib/rate-limiter.d.ts +27 -0
- package/dist/lib/rate-limiter.d.ts.map +1 -0
- package/dist/lib/rate-limiter.js +79 -0
- package/dist/lib/rate-limiter.js.map +1 -0
- package/dist/lib/response.d.ts +41 -0
- package/dist/lib/response.d.ts.map +1 -0
- package/dist/lib/response.js +150 -0
- package/dist/lib/response.js.map +1 -0
- package/dist/lib/review.d.ts +35 -0
- package/dist/lib/review.d.ts.map +1 -0
- package/dist/lib/review.js +263 -0
- package/dist/lib/review.js.map +1 -0
- package/dist/lib/schema.d.ts +66 -0
- package/dist/lib/schema.d.ts.map +1 -0
- package/dist/lib/schema.js +339 -0
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/scientific-words.d.ts +6 -0
- package/dist/lib/scientific-words.d.ts.map +1 -0
- package/dist/lib/scientific-words.js +66 -0
- package/dist/lib/scientific-words.js.map +1 -0
- package/dist/lib/sections.d.ts +40 -0
- package/dist/lib/sections.d.ts.map +1 -0
- package/dist/lib/sections.js +288 -0
- package/dist/lib/sections.js.map +1 -0
- package/dist/lib/slides.d.ts +86 -0
- package/dist/lib/slides.d.ts.map +1 -0
- package/dist/lib/slides.js +676 -0
- package/dist/lib/slides.js.map +1 -0
- package/dist/lib/spelling.d.ts +76 -0
- package/dist/lib/spelling.d.ts.map +1 -0
- package/dist/lib/spelling.js +272 -0
- package/dist/lib/spelling.js.map +1 -0
- package/dist/lib/templates.d.ts +30 -0
- package/dist/lib/templates.d.ts.map +1 -0
- package/dist/lib/templates.js +504 -0
- package/dist/lib/templates.js.map +1 -0
- package/dist/lib/themes.d.ts +85 -0
- package/dist/lib/themes.d.ts.map +1 -0
- package/dist/lib/themes.js +652 -0
- package/dist/lib/themes.js.map +1 -0
- package/dist/lib/trackchanges.d.ts +51 -0
- package/dist/lib/trackchanges.d.ts.map +1 -0
- package/dist/lib/trackchanges.js +202 -0
- package/dist/lib/trackchanges.js.map +1 -0
- package/dist/lib/tui.d.ts +76 -0
- package/dist/lib/tui.d.ts.map +1 -0
- package/dist/lib/tui.js +377 -0
- package/dist/lib/tui.js.map +1 -0
- package/dist/lib/types.d.ts +447 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +6 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/undo.d.ts +57 -0
- package/dist/lib/undo.d.ts.map +1 -0
- package/dist/lib/undo.js +185 -0
- package/dist/lib/undo.js.map +1 -0
- package/dist/lib/utils.d.ts +16 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +40 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/lib/variables.d.ts +42 -0
- package/dist/lib/variables.d.ts.map +1 -0
- package/dist/lib/variables.js +141 -0
- package/dist/lib/variables.js.map +1 -0
- package/dist/lib/word.d.ts +80 -0
- package/dist/lib/word.d.ts.map +1 -0
- package/dist/lib/word.js +360 -0
- package/dist/lib/word.js.map +1 -0
- package/dist/lib/wordcomments.d.ts +51 -0
- package/dist/lib/wordcomments.d.ts.map +1 -0
- package/dist/lib/wordcomments.js +587 -0
- package/dist/lib/wordcomments.js.map +1 -0
- package/eslint.config.js +27 -0
- package/lib/annotations.ts +622 -0
- package/lib/apply-buildup-colors.py +88 -0
- package/lib/build.ts +1013 -0
- package/lib/{citations.js → citations.ts} +38 -27
- package/lib/commands/{build.js → build.ts} +80 -27
- package/lib/commands/{citations.js → citations.ts} +36 -18
- package/lib/commands/{comments.js → comments.ts} +187 -54
- package/lib/commands/{context.js → context.ts} +18 -8
- package/lib/commands/{core.js → core.ts} +34 -20
- package/lib/commands/{doi.js → doi.ts} +32 -16
- package/lib/commands/{history.js → history.ts} +25 -12
- package/lib/commands/{index.js → index.ts} +9 -5
- package/lib/commands/{init.js → init.ts} +20 -8
- package/lib/commands/{response.js → response.ts} +47 -20
- package/lib/commands/{sections.js → sections.ts} +273 -68
- package/lib/commands/{utilities.js → utilities.ts} +338 -158
- package/lib/{comment-realign.js → comment-realign.ts} +117 -45
- package/lib/config.ts +84 -0
- package/lib/{crossref.js → crossref.ts} +213 -138
- package/lib/dependencies.ts +106 -0
- package/lib/doi-cache.ts +115 -0
- package/lib/{doi.js → doi.ts} +115 -281
- package/lib/{equations.js → equations.ts} +60 -64
- package/lib/{errors.js → errors.ts} +56 -48
- package/lib/{format.js → format.ts} +137 -63
- package/lib/{git.js → git.ts} +66 -63
- package/lib/{grammar.js → grammar.ts} +45 -32
- package/lib/image-registry.ts +180 -0
- package/lib/import.ts +2060 -0
- package/lib/journals.ts +505 -0
- package/lib/{merge.js → merge.ts} +185 -135
- package/lib/{orcid.js → orcid.ts} +17 -22
- package/lib/{pdf-comments.js → pdf-comments.ts} +76 -18
- package/lib/{pdf-import.js → pdf-import.ts} +148 -70
- package/lib/{plugins.js → plugins.ts} +82 -39
- package/lib/postprocess.ts +188 -0
- package/lib/pptx-color-filter.lua +37 -0
- package/lib/pptx-template.ts +625 -0
- package/lib/pptx-themes/academic.pptx +0 -0
- package/lib/pptx-themes/corporate.pptx +0 -0
- package/lib/pptx-themes/dark.pptx +0 -0
- package/lib/pptx-themes/default.pptx +0 -0
- package/lib/pptx-themes/minimal.pptx +0 -0
- package/lib/pptx-themes/plant.pptx +0 -0
- package/lib/pptx-themes.ts +896 -0
- package/lib/protect-restore.ts +516 -0
- package/lib/rate-limiter.ts +94 -0
- package/lib/{response.js → response.ts} +36 -21
- package/lib/{review.js → review.ts} +53 -43
- package/lib/{schema.js → schema.ts} +70 -25
- package/lib/{sections.js → sections.ts} +71 -76
- package/lib/slides.ts +793 -0
- package/lib/{spelling.js → spelling.ts} +43 -59
- package/lib/{templates.js → templates.ts} +20 -17
- package/lib/themes.ts +742 -0
- package/lib/{trackchanges.js → trackchanges.ts} +52 -23
- package/lib/types.ts +509 -0
- package/lib/{undo.js → undo.ts} +75 -52
- package/lib/utils.ts +41 -0
- package/lib/{variables.js → variables.ts} +60 -54
- package/lib/word.ts +428 -0
- package/lib/{wordcomments.js → wordcomments.ts} +94 -40
- package/package.json +15 -5
- package/skill/REFERENCE.md +67 -0
- package/tsconfig.json +26 -0
- package/lib/annotations.js +0 -414
- package/lib/build.js +0 -639
- package/lib/config.js +0 -79
- package/lib/import.js +0 -1145
- package/lib/journals.js +0 -629
- package/lib/word.js +0 -225
- /package/lib/{scientific-words.js → scientific-words.ts} +0 -0
package/lib/annotations.js
DELETED
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CriticMarkup annotation parsing and manipulation
|
|
3
|
-
*
|
|
4
|
-
* Syntax:
|
|
5
|
-
* {++inserted text++} - Insertions
|
|
6
|
-
* {--deleted text--} - Deletions
|
|
7
|
-
* {~~old~>new~~} - Substitutions
|
|
8
|
-
* {>>Author: comment<<} - Comments
|
|
9
|
-
* {==text==} - Highlights
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
// Patterns for each annotation type
|
|
13
|
-
const PATTERNS = {
|
|
14
|
-
insert: /\{\+\+(.+?)\+\+\}/gs,
|
|
15
|
-
delete: /\{--(.+?)--\}/gs,
|
|
16
|
-
substitute: /\{~~(.+?)~>(.+?)~~\}/gs,
|
|
17
|
-
comment: /\{>>(.+?)<<\}/gs,
|
|
18
|
-
highlight: /\{==(.+?)==\}/gs,
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Check if a potential comment is actually a false positive
|
|
23
|
-
* (e.g., figure caption, nested inside other annotation, code block, etc.)
|
|
24
|
-
* @param {string} commentContent - The content inside {>>...<<}
|
|
25
|
-
* @param {string} fullText - The full document text
|
|
26
|
-
* @param {number} position - Position of the comment in the text
|
|
27
|
-
* @returns {boolean} true if this is a false positive (not a real comment)
|
|
28
|
-
*/
|
|
29
|
-
function isCommentFalsePositive(commentContent, fullText, position) {
|
|
30
|
-
// Check if inside a code block (fenced or indented)
|
|
31
|
-
const textBefore = fullText.slice(Math.max(0, position - 2000), position);
|
|
32
|
-
const textAfter = fullText.slice(position, Math.min(fullText.length, position + 2000));
|
|
33
|
-
|
|
34
|
-
// Count unclosed fenced code blocks (``` or ~~~)
|
|
35
|
-
const fenceOpens = (textBefore.match(/^```|^~~~/gm) || []).length;
|
|
36
|
-
const fenceCloses = (textBefore.match(/```$|~~~$/gm) || []).length;
|
|
37
|
-
if (fenceOpens > fenceCloses) return true; // Inside code block
|
|
38
|
-
|
|
39
|
-
// Check if on an indented line (4+ spaces or tab at line start = code)
|
|
40
|
-
const lineStart = textBefore.lastIndexOf('\n') + 1;
|
|
41
|
-
const linePrefix = fullText.slice(lineStart, position);
|
|
42
|
-
if (/^(\t| )/.test(linePrefix)) return true; // Indented code
|
|
43
|
-
|
|
44
|
-
// Check if inside inline code backticks
|
|
45
|
-
const backticksBefore = (linePrefix.match(/`/g) || []).length;
|
|
46
|
-
if (backticksBefore % 2 === 1) return true; // Inside inline code
|
|
47
|
-
|
|
48
|
-
// Check if nested inside a deletion or insertion block
|
|
49
|
-
const nearTextBefore = fullText.slice(Math.max(0, position - 500), position);
|
|
50
|
-
|
|
51
|
-
// Count unclosed deletion markers
|
|
52
|
-
const delOpens = (nearTextBefore.match(/\{--/g) || []).length;
|
|
53
|
-
const delCloses = (nearTextBefore.match(/--\}/g) || []).length;
|
|
54
|
-
if (delOpens > delCloses) return true; // Nested inside deletion
|
|
55
|
-
|
|
56
|
-
// Count unclosed insertion markers
|
|
57
|
-
const insOpens = (nearTextBefore.match(/\{\+\+/g) || []).length;
|
|
58
|
-
const insCloses = (nearTextBefore.match(/\+\+\}/g) || []).length;
|
|
59
|
-
if (insOpens > insCloses) return true; // Nested inside insertion
|
|
60
|
-
|
|
61
|
-
// Heuristics for figure captions and other false positives:
|
|
62
|
-
|
|
63
|
-
// Contains image/figure path patterns
|
|
64
|
-
if (/\(figures?\/|\(images?\/|\.png|\.jpg|\.jpeg|\.gif|\.svg|\.pdf/i.test(commentContent)) return true;
|
|
65
|
-
|
|
66
|
-
// Contains markdown figure reference syntax
|
|
67
|
-
if (/\{#fig:|!\[/.test(commentContent)) return true;
|
|
68
|
-
|
|
69
|
-
// Contains URL patterns (likely a link, not a comment)
|
|
70
|
-
if (/https?:\/\/|www\./i.test(commentContent) && commentContent.length < 150) return true;
|
|
71
|
-
|
|
72
|
-
// Looks like code (contains programming patterns)
|
|
73
|
-
if (/function\s*\(|=>|import\s+|export\s+|const\s+|let\s+|var\s+/.test(commentContent)) return true;
|
|
74
|
-
|
|
75
|
-
// Very long without clear author pattern (likely caption, not comment)
|
|
76
|
-
// Real comments typically have "Author:" at start and are shorter
|
|
77
|
-
const hasAuthorPrefix = /^[A-Za-z][A-Za-z\s]{0,20}:\s/.test(commentContent.trim());
|
|
78
|
-
const hasResolvedMark = /^[✓✔]\s/.test(commentContent.trim());
|
|
79
|
-
if (!hasAuthorPrefix && !hasResolvedMark && commentContent.length > 200) return true;
|
|
80
|
-
|
|
81
|
-
// Looks like a figure caption (starts with "Fig" or contains typical caption words)
|
|
82
|
-
if (/^(Fig\.?|Figure|Table|Sankey|Diagram|Proportion|Distribution|Map|Chart|Graph|Plot|Panel)/i.test(commentContent.trim())) {
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Contains LaTeX-like patterns (likely equation, not comment)
|
|
87
|
-
if (/\\[a-z]+\{|\\frac|\\sum|\\int|\\begin\{/.test(commentContent)) return true;
|
|
88
|
-
|
|
89
|
-
// Looks like BibTeX entry (not a comment)
|
|
90
|
-
if (/@article\{|@book\{|@inproceedings\{/i.test(commentContent)) return true;
|
|
91
|
-
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Combined pattern for any track change (not comments)
|
|
96
|
-
const TRACK_CHANGE_PATTERN = /(\{\+\+.+?\+\+\}|\{--.+?--\}|\{~~.+?~>.+?~~\})/gs;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Parse all annotations from text
|
|
100
|
-
* @param {string} text
|
|
101
|
-
* @returns {Array<{type: string, match: string, content: string, replacement?: string, author?: string, position: number, line: number}>}
|
|
102
|
-
*/
|
|
103
|
-
export function parseAnnotations(text) {
|
|
104
|
-
const annotations = [];
|
|
105
|
-
|
|
106
|
-
// Build line number lookup
|
|
107
|
-
const lines = text.split('\n');
|
|
108
|
-
let pos = 0;
|
|
109
|
-
const lineStarts = lines.map((line) => {
|
|
110
|
-
const start = pos;
|
|
111
|
-
pos += line.length + 1;
|
|
112
|
-
return start;
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
function getLine(position) {
|
|
116
|
-
for (let i = 0; i < lineStarts.length; i++) {
|
|
117
|
-
if (lineStarts[i] > position) return i;
|
|
118
|
-
}
|
|
119
|
-
return lineStarts.length;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function getContext(position, length) {
|
|
123
|
-
const start = Math.max(0, position - 50);
|
|
124
|
-
const end = Math.min(text.length, position + length + 50);
|
|
125
|
-
const before = text.slice(start, position).split('\n').pop() || '';
|
|
126
|
-
const after = text.slice(position + length, end).split('\n')[0] || '';
|
|
127
|
-
return { before, after };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Parse insertions
|
|
131
|
-
for (const match of text.matchAll(PATTERNS.insert)) {
|
|
132
|
-
const ctx = getContext(match.index, match[0].length);
|
|
133
|
-
annotations.push({
|
|
134
|
-
type: 'insert',
|
|
135
|
-
match: match[0],
|
|
136
|
-
content: match[1],
|
|
137
|
-
position: match.index,
|
|
138
|
-
line: getLine(match.index),
|
|
139
|
-
...ctx,
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Parse deletions
|
|
144
|
-
for (const match of text.matchAll(PATTERNS.delete)) {
|
|
145
|
-
const ctx = getContext(match.index, match[0].length);
|
|
146
|
-
annotations.push({
|
|
147
|
-
type: 'delete',
|
|
148
|
-
match: match[0],
|
|
149
|
-
content: match[1],
|
|
150
|
-
position: match.index,
|
|
151
|
-
line: getLine(match.index),
|
|
152
|
-
...ctx,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Parse substitutions
|
|
157
|
-
for (const match of text.matchAll(PATTERNS.substitute)) {
|
|
158
|
-
const ctx = getContext(match.index, match[0].length);
|
|
159
|
-
annotations.push({
|
|
160
|
-
type: 'substitute',
|
|
161
|
-
match: match[0],
|
|
162
|
-
content: match[1],
|
|
163
|
-
replacement: match[2],
|
|
164
|
-
position: match.index,
|
|
165
|
-
line: getLine(match.index),
|
|
166
|
-
...ctx,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Parse comments (with false positive filtering)
|
|
171
|
-
for (const match of text.matchAll(PATTERNS.comment)) {
|
|
172
|
-
// Skip false positives (figure captions, nested annotations, etc.)
|
|
173
|
-
if (isCommentFalsePositive(match[1], text, match.index)) {
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const ctx = getContext(match.index, match[0].length);
|
|
178
|
-
let commentText = match[1];
|
|
179
|
-
let author = '';
|
|
180
|
-
|
|
181
|
-
// Extract author if present (format: "Author: comment")
|
|
182
|
-
const colonIdx = commentText.indexOf(':');
|
|
183
|
-
if (colonIdx > 0 && colonIdx < 30) {
|
|
184
|
-
author = commentText.slice(0, colonIdx).trim();
|
|
185
|
-
commentText = commentText.slice(colonIdx + 1).trim();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
annotations.push({
|
|
189
|
-
type: 'comment',
|
|
190
|
-
match: match[0],
|
|
191
|
-
content: commentText,
|
|
192
|
-
author,
|
|
193
|
-
position: match.index,
|
|
194
|
-
line: getLine(match.index),
|
|
195
|
-
...ctx,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Sort by position
|
|
200
|
-
annotations.sort((a, b) => a.position - b.position);
|
|
201
|
-
return annotations;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Strip annotations from text, applying changes
|
|
206
|
-
* Handles nested annotations by iterating until stable
|
|
207
|
-
* @param {string} text
|
|
208
|
-
* @param {{keepComments?: boolean}} options
|
|
209
|
-
* @returns {string}
|
|
210
|
-
*/
|
|
211
|
-
export function stripAnnotations(text, options = {}) {
|
|
212
|
-
const { keepComments = false } = options;
|
|
213
|
-
|
|
214
|
-
// Iterate until no more changes (handles nested annotations)
|
|
215
|
-
let prev;
|
|
216
|
-
let iterations = 0;
|
|
217
|
-
const maxIterations = 20; // Safety limit
|
|
218
|
-
|
|
219
|
-
do {
|
|
220
|
-
prev = text;
|
|
221
|
-
|
|
222
|
-
// Apply substitutions: {~~old~>new~~} → new
|
|
223
|
-
text = text.replace(PATTERNS.substitute, '$2');
|
|
224
|
-
|
|
225
|
-
// Apply insertions: {++text++} → text
|
|
226
|
-
text = text.replace(PATTERNS.insert, '$1');
|
|
227
|
-
|
|
228
|
-
// Apply deletions: {--text--} → nothing
|
|
229
|
-
text = text.replace(PATTERNS.delete, '');
|
|
230
|
-
|
|
231
|
-
// Remove highlights: {==text==} → text
|
|
232
|
-
text = text.replace(PATTERNS.highlight, '$1');
|
|
233
|
-
|
|
234
|
-
// Remove comments unless keeping
|
|
235
|
-
if (!keepComments) {
|
|
236
|
-
text = text.replace(PATTERNS.comment, '');
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Clean up partial/orphaned markers within the loop
|
|
240
|
-
// This handles cases where nested annotations leave behind fragments
|
|
241
|
-
|
|
242
|
-
// Empty annotations (from nested stripping)
|
|
243
|
-
text = text.replace(/\{----\}/g, '');
|
|
244
|
-
text = text.replace(/\{\+\+\+\+\}/g, '');
|
|
245
|
-
text = text.replace(/\{--\s*--\}/g, '');
|
|
246
|
-
text = text.replace(/\{\+\+\s*\+\+\}/g, '');
|
|
247
|
-
|
|
248
|
-
// Orphaned substitution fragments: ~>text~~} or {~~text (no proper pairs)
|
|
249
|
-
text = text.replace(/~>[^{]*?~~\}/g, '');
|
|
250
|
-
text = text.replace(/\{~~[^~}]*$/gm, '');
|
|
251
|
-
|
|
252
|
-
// Handle malformed substitution from nested: {~~{~~old → just strip the {~~
|
|
253
|
-
text = text.replace(/\{~~\{~~/g, '{~~');
|
|
254
|
-
text = text.replace(/~~\}~~\}/g, '~~}');
|
|
255
|
-
|
|
256
|
-
iterations++;
|
|
257
|
-
} while (text !== prev && iterations < maxIterations);
|
|
258
|
-
|
|
259
|
-
// Final cleanup of any remaining orphaned markers
|
|
260
|
-
// Orphaned closing markers
|
|
261
|
-
text = text.replace(/--\}(?:--\})+/g, '');
|
|
262
|
-
text = text.replace(/\+\+\}(?:\+\+\})+/g, '');
|
|
263
|
-
text = text.replace(/~~\}(?:~~\})+/g, '');
|
|
264
|
-
text = text.replace(/--\}/g, '');
|
|
265
|
-
text = text.replace(/\+\+\}/g, '');
|
|
266
|
-
text = text.replace(/~~\}/g, '');
|
|
267
|
-
|
|
268
|
-
// Orphaned opening markers
|
|
269
|
-
text = text.replace(/\{--(?:\{--)+/g, '');
|
|
270
|
-
text = text.replace(/\{\+\+(?:\{\+\+)+/g, '');
|
|
271
|
-
text = text.replace(/\{~~(?:\{~~)+/g, '');
|
|
272
|
-
text = text.replace(/\{--/g, '');
|
|
273
|
-
text = text.replace(/\{\+\+/g, '');
|
|
274
|
-
text = text.replace(/\{~~/g, '');
|
|
275
|
-
text = text.replace(/~>/g, '');
|
|
276
|
-
|
|
277
|
-
// Clean up multiple spaces (but preserve structure like newlines)
|
|
278
|
-
text = text.replace(/ +/g, ' ');
|
|
279
|
-
|
|
280
|
-
return text;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Check if text contains any CriticMarkup annotations
|
|
285
|
-
* @param {string} text
|
|
286
|
-
* @returns {boolean}
|
|
287
|
-
*/
|
|
288
|
-
export function hasAnnotations(text) {
|
|
289
|
-
return PATTERNS.insert.test(text) ||
|
|
290
|
-
PATTERNS.delete.test(text) ||
|
|
291
|
-
PATTERNS.substitute.test(text) ||
|
|
292
|
-
PATTERNS.comment.test(text) ||
|
|
293
|
-
PATTERNS.highlight.test(text);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Apply a decision to a single annotation
|
|
298
|
-
* @param {string} text
|
|
299
|
-
* @param {{type: string, match: string, content: string, replacement?: string}} annotation
|
|
300
|
-
* @param {boolean} accept
|
|
301
|
-
* @returns {string}
|
|
302
|
-
*/
|
|
303
|
-
export function applyDecision(text, annotation, accept) {
|
|
304
|
-
let replacement;
|
|
305
|
-
|
|
306
|
-
switch (annotation.type) {
|
|
307
|
-
case 'insert':
|
|
308
|
-
replacement = accept ? annotation.content : '';
|
|
309
|
-
break;
|
|
310
|
-
case 'delete':
|
|
311
|
-
replacement = accept ? '' : annotation.content;
|
|
312
|
-
break;
|
|
313
|
-
case 'substitute':
|
|
314
|
-
replacement = accept ? annotation.replacement : annotation.content;
|
|
315
|
-
break;
|
|
316
|
-
default:
|
|
317
|
-
return text;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return text.replace(annotation.match, replacement);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Get track changes only (no comments)
|
|
325
|
-
* @param {string} text
|
|
326
|
-
* @returns {Array}
|
|
327
|
-
*/
|
|
328
|
-
export function getTrackChanges(text) {
|
|
329
|
-
return parseAnnotations(text).filter((a) => a.type !== 'comment');
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Get comments only
|
|
334
|
-
* @param {string} text
|
|
335
|
-
* @param {object} options
|
|
336
|
-
* @returns {Array}
|
|
337
|
-
*/
|
|
338
|
-
export function getComments(text, options = {}) {
|
|
339
|
-
const { pendingOnly = false, resolvedOnly = false } = options;
|
|
340
|
-
let comments = parseAnnotations(text).filter((a) => a.type === 'comment');
|
|
341
|
-
|
|
342
|
-
// Check for resolved status marker at end of comment
|
|
343
|
-
comments = comments.map((c) => {
|
|
344
|
-
const resolved = c.content.endsWith('[RESOLVED]') || c.content.endsWith('[✓]');
|
|
345
|
-
return {
|
|
346
|
-
...c,
|
|
347
|
-
resolved,
|
|
348
|
-
content: resolved
|
|
349
|
-
? c.content.replace(/\s*\[(RESOLVED|✓)\]$/, '').trim()
|
|
350
|
-
: c.content,
|
|
351
|
-
};
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
if (pendingOnly) {
|
|
355
|
-
comments = comments.filter((c) => !c.resolved);
|
|
356
|
-
}
|
|
357
|
-
if (resolvedOnly) {
|
|
358
|
-
comments = comments.filter((c) => c.resolved);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return comments;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Mark a comment as resolved or pending
|
|
366
|
-
* @param {string} text - Document text
|
|
367
|
-
* @param {object} comment - Comment object with position and match
|
|
368
|
-
* @param {boolean} resolved - Whether to mark as resolved
|
|
369
|
-
* @returns {string} Updated text
|
|
370
|
-
*/
|
|
371
|
-
export function setCommentStatus(text, comment, resolved) {
|
|
372
|
-
// Find the comment in the text
|
|
373
|
-
const originalMatch = comment.match;
|
|
374
|
-
|
|
375
|
-
if (resolved) {
|
|
376
|
-
// Add [RESOLVED] marker before the closing <<
|
|
377
|
-
const newMatch = originalMatch.replace(/<<\}$/, ' [RESOLVED]<<}');
|
|
378
|
-
return text.replace(originalMatch, newMatch);
|
|
379
|
-
} else {
|
|
380
|
-
// Remove resolved markers
|
|
381
|
-
const newMatch = originalMatch.replace(/\s*\[(RESOLVED|✓)\]<<\}$/, '<<}');
|
|
382
|
-
return text.replace(originalMatch, newMatch);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Count annotations by type
|
|
388
|
-
* @param {string} text
|
|
389
|
-
* @returns {{inserts: number, deletes: number, substitutes: number, comments: number, total: number}}
|
|
390
|
-
*/
|
|
391
|
-
export function countAnnotations(text) {
|
|
392
|
-
const annotations = parseAnnotations(text);
|
|
393
|
-
const counts = { inserts: 0, deletes: 0, substitutes: 0, comments: 0, total: 0 };
|
|
394
|
-
|
|
395
|
-
for (const a of annotations) {
|
|
396
|
-
counts.total++;
|
|
397
|
-
switch (a.type) {
|
|
398
|
-
case 'insert':
|
|
399
|
-
counts.inserts++;
|
|
400
|
-
break;
|
|
401
|
-
case 'delete':
|
|
402
|
-
counts.deletes++;
|
|
403
|
-
break;
|
|
404
|
-
case 'substitute':
|
|
405
|
-
counts.substitutes++;
|
|
406
|
-
break;
|
|
407
|
-
case 'comment':
|
|
408
|
-
counts.comments++;
|
|
409
|
-
break;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return counts;
|
|
414
|
-
}
|