docrev 0.8.1 → 0.9.0
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/.gitattributes +1 -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
|
@@ -17,7 +17,45 @@ const MARKER_START_PREFIX = '⟦CMS:';
|
|
|
17
17
|
const MARKER_END_PREFIX = '⟦CME:';
|
|
18
18
|
const MARKER_SUFFIX = '⟧';
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
interface ParsedComment {
|
|
21
|
+
author: string;
|
|
22
|
+
text: string;
|
|
23
|
+
anchor: string | null;
|
|
24
|
+
start: number;
|
|
25
|
+
end: number;
|
|
26
|
+
fullMatch: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface PreparedComment extends ParsedComment {
|
|
30
|
+
isReply: boolean;
|
|
31
|
+
parentIdx: number | null;
|
|
32
|
+
commentIdx: number;
|
|
33
|
+
anchorFromReply?: boolean;
|
|
34
|
+
placesParentMarkers?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface PrepareResult {
|
|
38
|
+
markedMarkdown: string;
|
|
39
|
+
comments: PreparedComment[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface CommentWithIds extends PreparedComment {
|
|
43
|
+
id: string;
|
|
44
|
+
paraId: string;
|
|
45
|
+
paraId2: string;
|
|
46
|
+
durableId: string;
|
|
47
|
+
parentParaId?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface InjectionResult {
|
|
51
|
+
success: boolean;
|
|
52
|
+
commentCount: number;
|
|
53
|
+
replyCount?: number;
|
|
54
|
+
skippedComments: number;
|
|
55
|
+
error?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function escapeXml(str: string): string {
|
|
21
59
|
return str
|
|
22
60
|
.replace(/&/g, '&')
|
|
23
61
|
.replace(/</g, '<')
|
|
@@ -26,7 +64,7 @@ function escapeXml(str) {
|
|
|
26
64
|
.replace(/'/g, ''');
|
|
27
65
|
}
|
|
28
66
|
|
|
29
|
-
function generateParaId(commentIdx, paraNum) {
|
|
67
|
+
function generateParaId(commentIdx: number, paraNum: number): string {
|
|
30
68
|
// Generate 8-character uppercase hex ID matching Word format
|
|
31
69
|
// Word uses IDs like "3F25BC58", "0331C187"
|
|
32
70
|
// Must be deterministic - same inputs always produce same output
|
|
@@ -41,14 +79,14 @@ function generateParaId(commentIdx, paraNum) {
|
|
|
41
79
|
* - markedMarkdown: markdown with markers for parent comments only
|
|
42
80
|
* - comments: array with author, text, isReply, parentIdx
|
|
43
81
|
*/
|
|
44
|
-
export function prepareMarkdownWithMarkers(markdown) {
|
|
82
|
+
export function prepareMarkdownWithMarkers(markdown: string): PrepareResult {
|
|
45
83
|
// Match all comments with optional anchor
|
|
46
84
|
const commentPattern = /\{>>(.+?)<<\}(?:\s*\[([^\]]+)\]\{\.mark\})?/g;
|
|
47
85
|
|
|
48
|
-
const rawMatches = [];
|
|
49
|
-
let match;
|
|
86
|
+
const rawMatches: ParsedComment[] = [];
|
|
87
|
+
let match: RegExpExecArray | null;
|
|
50
88
|
while ((match = commentPattern.exec(markdown)) !== null) {
|
|
51
|
-
const content = match[1];
|
|
89
|
+
const content = match[1] ?? '';
|
|
52
90
|
let author = 'Unknown';
|
|
53
91
|
let text = content;
|
|
54
92
|
const colonIdx = content.indexOf(':');
|
|
@@ -75,12 +113,13 @@ export function prepareMarkdownWithMarkers(markdown) {
|
|
|
75
113
|
// First comment in a cluster = parent, all subsequent = replies to that parent
|
|
76
114
|
// Comments are "adjacent" if there's minimal text between them (< 10 chars)
|
|
77
115
|
const ADJACENT_THRESHOLD = 10;
|
|
78
|
-
const comments = [];
|
|
116
|
+
const comments: PreparedComment[] = [];
|
|
79
117
|
let clusterParentIdx = -1; // Index of first comment in current cluster
|
|
80
118
|
let lastCommentEnd = -1;
|
|
81
119
|
|
|
82
120
|
for (let i = 0; i < rawMatches.length; i++) {
|
|
83
121
|
const m = rawMatches[i];
|
|
122
|
+
if (!m) continue;
|
|
84
123
|
|
|
85
124
|
// Check if this comment is adjacent to the previous one
|
|
86
125
|
const gap = lastCommentEnd >= 0 ? m.start - lastCommentEnd : Infinity;
|
|
@@ -94,7 +133,12 @@ export function prepareMarkdownWithMarkers(markdown) {
|
|
|
94
133
|
if (clusterParentIdx === -1) {
|
|
95
134
|
// First comment in cluster = parent (regardless of author)
|
|
96
135
|
comments.push({
|
|
97
|
-
|
|
136
|
+
author: m.author,
|
|
137
|
+
text: m.text,
|
|
138
|
+
anchor: m.anchor,
|
|
139
|
+
start: m.start,
|
|
140
|
+
end: m.end,
|
|
141
|
+
fullMatch: m.fullMatch,
|
|
98
142
|
isReply: false,
|
|
99
143
|
parentIdx: null,
|
|
100
144
|
commentIdx: comments.length
|
|
@@ -103,7 +147,12 @@ export function prepareMarkdownWithMarkers(markdown) {
|
|
|
103
147
|
} else {
|
|
104
148
|
// Subsequent comment in cluster = reply to first comment
|
|
105
149
|
comments.push({
|
|
106
|
-
|
|
150
|
+
author: m.author,
|
|
151
|
+
text: m.text,
|
|
152
|
+
anchor: m.anchor,
|
|
153
|
+
start: m.start,
|
|
154
|
+
end: m.end,
|
|
155
|
+
fullMatch: m.fullMatch,
|
|
107
156
|
isReply: true,
|
|
108
157
|
parentIdx: clusterParentIdx,
|
|
109
158
|
commentIdx: comments.length
|
|
@@ -119,7 +168,7 @@ export function prepareMarkdownWithMarkers(markdown) {
|
|
|
119
168
|
for (const c of comments) {
|
|
120
169
|
if (c.isReply && c.anchor && c.parentIdx !== null) {
|
|
121
170
|
const parent = comments[c.parentIdx];
|
|
122
|
-
if (!parent.anchor) {
|
|
171
|
+
if (parent && !parent.anchor) {
|
|
123
172
|
parent.anchor = c.anchor;
|
|
124
173
|
parent.anchorFromReply = true; // Parent's anchor came from a reply (markers placed by reply)
|
|
125
174
|
c.placesParentMarkers = true; // This reply should place the parent's markers
|
|
@@ -134,12 +183,14 @@ export function prepareMarkdownWithMarkers(markdown) {
|
|
|
134
183
|
|
|
135
184
|
for (let i = comments.length - 1; i >= 0; i--) {
|
|
136
185
|
const c = comments[i];
|
|
186
|
+
if (!c) continue;
|
|
137
187
|
|
|
138
188
|
if (c.isReply) {
|
|
139
189
|
// Reply: remove from document entirely (will be in comments.xml only)
|
|
140
190
|
// Also consume leading whitespace to avoid double spaces
|
|
141
191
|
let removeStart = c.start;
|
|
142
|
-
|
|
192
|
+
const charBefore = markedMarkdown[removeStart - 1];
|
|
193
|
+
while (removeStart > 0 && charBefore && /\s/.test(charBefore)) {
|
|
143
194
|
removeStart--;
|
|
144
195
|
}
|
|
145
196
|
|
|
@@ -148,7 +199,7 @@ export function prepareMarkdownWithMarkers(markdown) {
|
|
|
148
199
|
// Extract anchor text from the original match
|
|
149
200
|
const anchorMatch = c.fullMatch.match(/\[([^\]]+)\]\{\.mark\}$/);
|
|
150
201
|
if (anchorMatch) {
|
|
151
|
-
const anchorText = anchorMatch[1];
|
|
202
|
+
const anchorText = anchorMatch[1] ?? '';
|
|
152
203
|
// Output markers with PARENT's index around the anchor text
|
|
153
204
|
const parentIdx = c.parentIdx;
|
|
154
205
|
const replacement = `${MARKER_START_PREFIX}${parentIdx}${MARKER_SUFFIX}${anchorText}${MARKER_END_PREFIX}${parentIdx}${MARKER_SUFFIX}`;
|
|
@@ -164,7 +215,8 @@ export function prepareMarkdownWithMarkers(markdown) {
|
|
|
164
215
|
if (c.anchorFromReply) {
|
|
165
216
|
// Anchor markers are placed by the reply, just remove this comment
|
|
166
217
|
let removeStart = c.start;
|
|
167
|
-
|
|
218
|
+
const charBefore = markedMarkdown[removeStart - 1];
|
|
219
|
+
while (removeStart > 0 && charBefore && /\s/.test(charBefore)) {
|
|
168
220
|
removeStart--;
|
|
169
221
|
}
|
|
170
222
|
markedMarkdown = markedMarkdown.slice(0, removeStart) + markedMarkdown.slice(c.end);
|
|
@@ -180,7 +232,7 @@ export function prepareMarkdownWithMarkers(markdown) {
|
|
|
180
232
|
return { markedMarkdown, comments };
|
|
181
233
|
}
|
|
182
234
|
|
|
183
|
-
function createCommentsXml(comments) {
|
|
235
|
+
function createCommentsXml(comments: CommentWithIds[]): string {
|
|
184
236
|
// Word expects date without milliseconds: 2025-12-30T08:33:00Z
|
|
185
237
|
const now = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
186
238
|
|
|
@@ -209,7 +261,7 @@ function createCommentsXml(comments) {
|
|
|
209
261
|
return xml;
|
|
210
262
|
}
|
|
211
263
|
|
|
212
|
-
function createCommentsExtendedXml(comments) {
|
|
264
|
+
function createCommentsExtendedXml(comments: CommentWithIds[]): string {
|
|
213
265
|
let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
|
|
214
266
|
// Minimal namespaces matching golden file structure
|
|
215
267
|
xml += '<w15:commentsEx xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 w15">';
|
|
@@ -228,7 +280,7 @@ function createCommentsExtendedXml(comments) {
|
|
|
228
280
|
return xml;
|
|
229
281
|
}
|
|
230
282
|
|
|
231
|
-
function generateDurableId(index) {
|
|
283
|
+
function generateDurableId(index: number): string {
|
|
232
284
|
// Generate unique 8-char hex ID for durableId
|
|
233
285
|
// CRITICAL: Must stay within signed 32-bit range (< 0x7FFFFFFF = 2147483647)
|
|
234
286
|
// Word interprets durableIds as signed 32-bit integers
|
|
@@ -237,7 +289,7 @@ function generateDurableId(index) {
|
|
|
237
289
|
return id.toString(16).toUpperCase().padStart(8, '0');
|
|
238
290
|
}
|
|
239
291
|
|
|
240
|
-
function createCommentsIdsXml(comments) {
|
|
292
|
+
function createCommentsIdsXml(comments: CommentWithIds[]): string {
|
|
241
293
|
let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
|
|
242
294
|
// Minimal namespaces matching golden file structure
|
|
243
295
|
xml += '<w16cid:commentsIds ';
|
|
@@ -257,7 +309,7 @@ function createCommentsIdsXml(comments) {
|
|
|
257
309
|
return xml;
|
|
258
310
|
}
|
|
259
311
|
|
|
260
|
-
function createCommentsExtensibleXml(comments) {
|
|
312
|
+
function createCommentsExtensibleXml(comments: CommentWithIds[]): string {
|
|
261
313
|
const now = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
262
314
|
|
|
263
315
|
let xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
|
|
@@ -278,7 +330,7 @@ function createCommentsExtensibleXml(comments) {
|
|
|
278
330
|
|
|
279
331
|
// Generate deterministic user IDs for authors (no hardcoded personal data)
|
|
280
332
|
|
|
281
|
-
function createPeopleXml(comments) {
|
|
333
|
+
function createPeopleXml(comments: CommentWithIds[]): string {
|
|
282
334
|
// Extract unique authors
|
|
283
335
|
const authors = [...new Set(comments.map(c => c.author))];
|
|
284
336
|
|
|
@@ -308,7 +360,7 @@ function createPeopleXml(comments) {
|
|
|
308
360
|
return xml;
|
|
309
361
|
}
|
|
310
362
|
|
|
311
|
-
function generateUserId(author) {
|
|
363
|
+
function generateUserId(author: string): string {
|
|
312
364
|
// Generate a deterministic 16-char hex ID from author name
|
|
313
365
|
let hash = 0;
|
|
314
366
|
for (let i = 0; i < author.length; i++) {
|
|
@@ -321,7 +373,11 @@ function generateUserId(author) {
|
|
|
321
373
|
/**
|
|
322
374
|
* Inject comments at marker positions
|
|
323
375
|
*/
|
|
324
|
-
export async function injectCommentsAtMarkers(
|
|
376
|
+
export async function injectCommentsAtMarkers(
|
|
377
|
+
docxPath: string,
|
|
378
|
+
comments: PreparedComment[],
|
|
379
|
+
outputPath: string
|
|
380
|
+
): Promise<InjectionResult> {
|
|
325
381
|
try {
|
|
326
382
|
if (!fs.existsSync(docxPath)) {
|
|
327
383
|
return { success: false, commentCount: 0, skippedComments: 0, error: `File not found: ${docxPath}` };
|
|
@@ -341,7 +397,7 @@ export async function injectCommentsAtMarkers(docxPath, comments, outputPath) {
|
|
|
341
397
|
let documentXml = zip.readAsText(documentEntry);
|
|
342
398
|
|
|
343
399
|
// Assign IDs and paraIds (IDs start at 1, not 0 - Word convention)
|
|
344
|
-
const commentsWithIds = comments.map((c, idx) => ({
|
|
400
|
+
const commentsWithIds: CommentWithIds[] = comments.map((c, idx) => ({
|
|
345
401
|
...c,
|
|
346
402
|
id: String(idx + 1),
|
|
347
403
|
paraId: generateParaId(idx, 1), // First paragraph (e.g., 10000001)
|
|
@@ -352,17 +408,21 @@ export async function injectCommentsAtMarkers(docxPath, comments, outputPath) {
|
|
|
352
408
|
// Link replies to parent paraIds
|
|
353
409
|
for (const c of commentsWithIds) {
|
|
354
410
|
if (c.isReply && c.parentIdx !== null) {
|
|
355
|
-
|
|
411
|
+
const parent = commentsWithIds[c.parentIdx];
|
|
412
|
+
if (parent) {
|
|
413
|
+
c.parentParaId = parent.paraId;
|
|
414
|
+
}
|
|
356
415
|
}
|
|
357
416
|
}
|
|
358
417
|
|
|
359
|
-
const injectedIds = new Set();
|
|
418
|
+
const injectedIds = new Set<string>();
|
|
360
419
|
|
|
361
420
|
// Process only parent comments (non-replies) for document ranges
|
|
362
421
|
const parentComments = commentsWithIds.filter(c => !c.isReply);
|
|
363
422
|
|
|
364
423
|
for (let i = parentComments.length - 1; i >= 0; i--) {
|
|
365
424
|
const comment = parentComments[i];
|
|
425
|
+
if (!comment) continue;
|
|
366
426
|
const idx = comment.commentIdx;
|
|
367
427
|
|
|
368
428
|
const startMarker = `${MARKER_START_PREFIX}${idx}${MARKER_SUFFIX}`;
|
|
@@ -392,8 +452,10 @@ export async function injectCommentsAtMarkers(docxPath, comments, outputPath) {
|
|
|
392
452
|
const textMatch = runContent.match(/<w:t[^>]*>([\s\S]*?)<\/w:t>/);
|
|
393
453
|
if (!textMatch) continue;
|
|
394
454
|
|
|
395
|
-
const fullText = textMatch[1];
|
|
396
|
-
const
|
|
455
|
+
const fullText = textMatch[1] ?? '';
|
|
456
|
+
const tElementMatch = textMatch[0].match(/<w:t[^>]*>/);
|
|
457
|
+
if (!tElementMatch) continue;
|
|
458
|
+
const tElement = tElementMatch[0];
|
|
397
459
|
|
|
398
460
|
const startInText = fullText.indexOf(startMarker);
|
|
399
461
|
const endInText = fullText.indexOf(endMarker);
|
|
@@ -407,7 +469,7 @@ export async function injectCommentsAtMarkers(docxPath, comments, outputPath) {
|
|
|
407
469
|
if (!anchorText && textAfter) {
|
|
408
470
|
const wordMatch = textAfter.match(/^\s*(\S+)/);
|
|
409
471
|
if (wordMatch) {
|
|
410
|
-
anchorText = wordMatch[1];
|
|
472
|
+
anchorText = wordMatch[1] ?? '';
|
|
411
473
|
textAfter = textAfter.slice(wordMatch[0].length);
|
|
412
474
|
}
|
|
413
475
|
}
|
|
@@ -425,7 +487,7 @@ export async function injectCommentsAtMarkers(docxPath, comments, outputPath) {
|
|
|
425
487
|
}
|
|
426
488
|
|
|
427
489
|
// Find replies to this comment
|
|
428
|
-
const replies = commentsWithIds.filter(c => c.isReply && c.parentIdx === comment
|
|
490
|
+
const replies = commentsWithIds.filter(c => c.isReply && c.parentIdx === comment?.commentIdx);
|
|
429
491
|
|
|
430
492
|
// Start ranges for parent AND all replies (nested)
|
|
431
493
|
replacement += `<w:commentRangeStart w:id="${comment.id}"/>`;
|
|
@@ -458,7 +520,7 @@ export async function injectCommentsAtMarkers(docxPath, comments, outputPath) {
|
|
|
458
520
|
}
|
|
459
521
|
|
|
460
522
|
// Add required namespaces to document.xml for comment threading
|
|
461
|
-
const requiredNs = {
|
|
523
|
+
const requiredNs: Record<string, string> = {
|
|
462
524
|
'xmlns:w14': 'http://schemas.microsoft.com/office/word/2010/wordml',
|
|
463
525
|
'xmlns:w15': 'http://schemas.microsoft.com/office/word/2012/wordml',
|
|
464
526
|
'xmlns:w16cid': 'http://schemas.microsoft.com/office/word/2016/wordml/cid',
|
|
@@ -494,7 +556,8 @@ export async function injectCommentsAtMarkers(docxPath, comments, outputPath) {
|
|
|
494
556
|
return injectedIds.has(c.id);
|
|
495
557
|
} else {
|
|
496
558
|
// Include reply if its parent was injected
|
|
497
|
-
|
|
559
|
+
const parent = c.parentIdx !== null ? commentsWithIds[c.parentIdx] : undefined;
|
|
560
|
+
return parent && injectedIds.has(parent.id);
|
|
498
561
|
}
|
|
499
562
|
});
|
|
500
563
|
|
|
@@ -639,16 +702,7 @@ export async function injectCommentsAtMarkers(docxPath, comments, outputPath) {
|
|
|
639
702
|
skippedComments: comments.length - includedComments.length,
|
|
640
703
|
};
|
|
641
704
|
|
|
642
|
-
} catch (err) {
|
|
705
|
+
} catch (err: any) {
|
|
643
706
|
return { success: false, commentCount: 0, skippedComments: 0, error: err.message };
|
|
644
707
|
}
|
|
645
708
|
}
|
|
646
|
-
|
|
647
|
-
export async function injectComments(docxPath, markdown, outputPath) {
|
|
648
|
-
console.warn('Warning: Use prepareMarkdownWithMarkers + injectCommentsAtMarkers instead');
|
|
649
|
-
return { success: false, commentCount: 0, skippedComments: 0, error: 'Use marker-based flow' };
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
export async function buildWithComments(cleanDocxPath, comments, outputPath) {
|
|
653
|
-
return injectCommentsAtMarkers(cleanDocxPath, comments, outputPath);
|
|
654
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docrev",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Academic paper revision workflow: Word ↔ Markdown round-trips, DOI validation, reviewer comments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -81,10 +81,15 @@
|
|
|
81
81
|
"rev": "bin/rev.js"
|
|
82
82
|
},
|
|
83
83
|
"scripts": {
|
|
84
|
-
"build": "
|
|
85
|
-
"
|
|
84
|
+
"build": "tsc",
|
|
85
|
+
"build:watch": "tsc --watch",
|
|
86
|
+
"dev": "tsx bin/rev.ts",
|
|
87
|
+
"test": "tsx --test test/*.test.js",
|
|
88
|
+
"test:ts": "tsx --test test/*.test.ts",
|
|
86
89
|
"test:watch": "node --test --watch test/*.test.js",
|
|
87
|
-
"test:coverage": "c8 --reporter=text --reporter=lcov node --test test/*.test.js"
|
|
90
|
+
"test:coverage": "c8 --reporter=text --reporter=lcov node --test test/*.test.js",
|
|
91
|
+
"typecheck": "tsc --noEmit",
|
|
92
|
+
"prepublishOnly": "npm run build"
|
|
88
93
|
},
|
|
89
94
|
"repository": {
|
|
90
95
|
"type": "git",
|
|
@@ -119,10 +124,15 @@
|
|
|
119
124
|
"nspell": "^2.1.5",
|
|
120
125
|
"pdf-lib": "^1.17.1",
|
|
121
126
|
"pdfjs-dist": "^5.4.530",
|
|
127
|
+
"tsx": "^4.21.0",
|
|
122
128
|
"xml2js": "^0.6.2",
|
|
123
129
|
"yaml": "^2.8.2"
|
|
124
130
|
},
|
|
125
131
|
"devDependencies": {
|
|
126
|
-
"
|
|
132
|
+
"@types/adm-zip": "^0.5.7",
|
|
133
|
+
"@types/node": "^25.2.0",
|
|
134
|
+
"@types/xml2js": "^0.4.14",
|
|
135
|
+
"c8": "^10.1.2",
|
|
136
|
+
"typescript": "^5.9.3"
|
|
127
137
|
}
|
|
128
138
|
}
|
package/skill/REFERENCE.md
CHANGED
|
@@ -313,6 +313,73 @@ rev config user "Your Name" # Set author name for replies
|
|
|
313
313
|
rev config # Show current config
|
|
314
314
|
```
|
|
315
315
|
|
|
316
|
+
## rev.yaml Settings
|
|
317
|
+
|
|
318
|
+
### Tables
|
|
319
|
+
|
|
320
|
+
Configure table formatting for PDF output:
|
|
321
|
+
|
|
322
|
+
```yaml
|
|
323
|
+
tables:
|
|
324
|
+
nowrap:
|
|
325
|
+
- Prior # Column headers to keep on one line
|
|
326
|
+
- "$\\widehat{R}$"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
In `nowrap` columns, distribution notation is auto-converted to LaTeX math:
|
|
330
|
+
- `Normal(0, 0.5)` → `$\mathcal{N}(0, 0.5)$`
|
|
331
|
+
- `Student-t(3, 0, 1)` → `$t_3(0, 1)$`
|
|
332
|
+
- `Gamma(2, 0.5)` → `$\text{Gamma}(2, 0.5)$`
|
|
333
|
+
- `Half-Normal(0, 1)` → `$\text{Half-Normal}(0, 1)$`
|
|
334
|
+
- `Exponential(1)` → `$\text{Exp}(1)$`
|
|
335
|
+
|
|
336
|
+
**Tip:** For complex tables, use simple tables (space-aligned with dashes) instead of pipe tables to avoid column width issues:
|
|
337
|
+
|
|
338
|
+
```markdown
|
|
339
|
+
Parameter Prior Description
|
|
340
|
+
---------- ------------------------- ------------------
|
|
341
|
+
alpha $\mathcal{N}(0, 0.5)$ Intercept prior
|
|
342
|
+
beta $t_3(0, 2.5)$ Slope prior
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Postprocess Scripts
|
|
346
|
+
|
|
347
|
+
Run custom scripts after output generation:
|
|
348
|
+
|
|
349
|
+
```yaml
|
|
350
|
+
postprocess:
|
|
351
|
+
pdf: ./scripts/fix-pdf.py # Runs after PDF
|
|
352
|
+
docx: ./scripts/add-meta.js # Runs after DOCX
|
|
353
|
+
tex: ./scripts/tweak-latex.sh # Runs after TEX
|
|
354
|
+
pptx: ./scripts/fix-slides.ps1 # Runs after PPTX
|
|
355
|
+
all: ./scripts/notify.js # Runs after ANY format
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Environment variables** available to scripts:
|
|
359
|
+
|
|
360
|
+
| Variable | Description |
|
|
361
|
+
|----------|-------------|
|
|
362
|
+
| `OUTPUT_FILE` | Full path to generated file |
|
|
363
|
+
| `OUTPUT_FORMAT` | Format: `pdf`, `docx`, `tex`, `pptx`, `beamer` |
|
|
364
|
+
| `PROJECT_DIR` | Directory containing rev.yaml |
|
|
365
|
+
| `CONFIG_PATH` | Full path to rev.yaml |
|
|
366
|
+
|
|
367
|
+
**Supported script types:** `.js`, `.mjs` (Node), `.py` (Python), `.ps1` (PowerShell), `.sh` (Bash)
|
|
368
|
+
|
|
369
|
+
**Example Python script** (add DOCX metadata):
|
|
370
|
+
```python
|
|
371
|
+
import os
|
|
372
|
+
from docx import Document
|
|
373
|
+
doc = Document(os.environ['OUTPUT_FILE'])
|
|
374
|
+
doc.core_properties.author = "Research Team"
|
|
375
|
+
doc.save(os.environ['OUTPUT_FILE'])
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Use `--verbose` flag to see script output:
|
|
379
|
+
```bash
|
|
380
|
+
rev build pdf --verbose
|
|
381
|
+
```
|
|
382
|
+
|
|
316
383
|
## Shell Completions
|
|
317
384
|
|
|
318
385
|
### rev completions
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": ".",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"strict": true,
|
|
13
|
+
"noImplicitAny": true,
|
|
14
|
+
"strictNullChecks": true,
|
|
15
|
+
"noImplicitReturns": true,
|
|
16
|
+
"noUncheckedIndexedAccess": false,
|
|
17
|
+
"esModuleInterop": true,
|
|
18
|
+
"skipLibCheck": true,
|
|
19
|
+
"forceConsistentCasingInFileNames": true,
|
|
20
|
+
"resolveJsonModule": true,
|
|
21
|
+
"allowJs": true,
|
|
22
|
+
"checkJs": false
|
|
23
|
+
},
|
|
24
|
+
"include": ["lib/**/*", "bin/**/*"],
|
|
25
|
+
"exclude": ["node_modules", "dist", "test"]
|
|
26
|
+
}
|