docrev 0.10.0 → 0.10.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.
Files changed (126) hide show
  1. package/.gitattributes +1 -1
  2. package/CHANGELOG.md +173 -164
  3. package/PLAN-tables-and-postprocess.md +850 -850
  4. package/README.md +431 -431
  5. package/bin/rev.js +11 -11
  6. package/bin/rev.ts +145 -145
  7. package/completions/rev.bash +127 -127
  8. package/completions/rev.ps1 +210 -210
  9. package/completions/rev.zsh +207 -207
  10. package/dist/lib/anchor-match.d.ts +1 -1
  11. package/dist/lib/anchor-match.d.ts.map +1 -1
  12. package/dist/lib/anchor-match.js +17 -47
  13. package/dist/lib/anchor-match.js.map +1 -1
  14. package/dist/lib/build.js +4 -4
  15. package/dist/lib/commands/context.d.ts +1 -1
  16. package/dist/lib/commands/context.d.ts.map +1 -1
  17. package/dist/lib/commands/context.js +1 -1
  18. package/dist/lib/commands/context.js.map +1 -1
  19. package/dist/lib/commands/sections.js +7 -7
  20. package/dist/lib/commands/sections.js.map +1 -1
  21. package/dist/lib/commands/sync.d.ts.map +1 -1
  22. package/dist/lib/commands/sync.js +15 -14
  23. package/dist/lib/commands/sync.js.map +1 -1
  24. package/dist/lib/commands/utilities.js +164 -164
  25. package/dist/lib/commands/verify-anchors.js +6 -6
  26. package/dist/lib/commands/verify-anchors.js.map +1 -1
  27. package/dist/lib/commands/word-tools.js +8 -8
  28. package/dist/lib/grammar.js +3 -3
  29. package/dist/lib/macro-filter.lua +201 -201
  30. package/dist/lib/pdf-comments.js +44 -44
  31. package/dist/lib/plugins.js +57 -57
  32. package/dist/lib/pptx-color-filter.lua +37 -37
  33. package/dist/lib/pptx-themes.js +115 -115
  34. package/dist/lib/sections.d.ts +35 -0
  35. package/dist/lib/sections.d.ts.map +1 -1
  36. package/dist/lib/sections.js +81 -0
  37. package/dist/lib/sections.js.map +1 -1
  38. package/dist/lib/spelling.js +2 -2
  39. package/dist/lib/templates.js +387 -387
  40. package/dist/lib/themes.js +51 -51
  41. package/docs-src/build.py +113 -113
  42. package/docs-src/extra.css +208 -208
  43. package/docs-src/md-to-html.lua +6 -6
  44. package/docs-src/template.html +116 -116
  45. package/eslint.config.js +27 -27
  46. package/lib/anchor-match.ts +276 -308
  47. package/lib/annotations.ts +644 -644
  48. package/lib/build.ts +1766 -1766
  49. package/lib/citations.ts +160 -160
  50. package/lib/commands/build.ts +855 -855
  51. package/lib/commands/citations.ts +515 -515
  52. package/lib/commands/comments.ts +1050 -1050
  53. package/lib/commands/context.ts +176 -174
  54. package/lib/commands/core.ts +309 -309
  55. package/lib/commands/doi.ts +435 -435
  56. package/lib/commands/file-ops.ts +372 -372
  57. package/lib/commands/history.ts +320 -320
  58. package/lib/commands/index.ts +87 -87
  59. package/lib/commands/init.ts +259 -259
  60. package/lib/commands/merge-resolve.ts +378 -378
  61. package/lib/commands/preview.ts +178 -178
  62. package/lib/commands/project-info.ts +244 -244
  63. package/lib/commands/quality.ts +517 -517
  64. package/lib/commands/response.ts +454 -454
  65. package/lib/commands/section-boundaries.ts +82 -82
  66. package/lib/commands/sections.ts +451 -451
  67. package/lib/commands/sync.ts +709 -706
  68. package/lib/commands/text-ops.ts +449 -449
  69. package/lib/commands/utilities.ts +448 -448
  70. package/lib/commands/verify-anchors.ts +272 -272
  71. package/lib/commands/word-tools.ts +340 -340
  72. package/lib/comment-realign.ts +517 -517
  73. package/lib/config.ts +84 -84
  74. package/lib/crossref.ts +781 -781
  75. package/lib/csl.ts +191 -191
  76. package/lib/dependencies.ts +98 -98
  77. package/lib/diff-engine.ts +465 -465
  78. package/lib/doi-cache.ts +115 -115
  79. package/lib/doi.ts +897 -897
  80. package/lib/equations.ts +506 -506
  81. package/lib/errors.ts +346 -346
  82. package/lib/format.ts +541 -541
  83. package/lib/git.ts +326 -326
  84. package/lib/grammar.ts +303 -303
  85. package/lib/image-registry.ts +180 -180
  86. package/lib/import.ts +911 -911
  87. package/lib/journals.ts +543 -543
  88. package/lib/macro-filter.lua +201 -201
  89. package/lib/macros.ts +273 -273
  90. package/lib/merge.ts +633 -633
  91. package/lib/orcid.ts +144 -144
  92. package/lib/pdf-comments.ts +263 -263
  93. package/lib/pdf-import.ts +524 -524
  94. package/lib/plugins.ts +362 -362
  95. package/lib/postprocess.ts +188 -188
  96. package/lib/pptx-color-filter.lua +37 -37
  97. package/lib/pptx-template.ts +469 -469
  98. package/lib/pptx-themes.ts +483 -483
  99. package/lib/protect-restore.ts +520 -520
  100. package/lib/rate-limiter.ts +94 -94
  101. package/lib/response.ts +197 -197
  102. package/lib/restore-references.ts +240 -240
  103. package/lib/review.ts +327 -327
  104. package/lib/schema.ts +488 -488
  105. package/lib/scientific-words.ts +73 -73
  106. package/lib/sections.ts +425 -335
  107. package/lib/slides.ts +756 -756
  108. package/lib/spelling.ts +334 -334
  109. package/lib/templates.ts +526 -526
  110. package/lib/themes.ts +742 -742
  111. package/lib/trackchanges.ts +247 -247
  112. package/lib/tui.ts +450 -450
  113. package/lib/types.ts +550 -550
  114. package/lib/undo.ts +250 -250
  115. package/lib/utils.ts +69 -69
  116. package/lib/variables.ts +179 -179
  117. package/lib/word-extraction.ts +806 -806
  118. package/lib/word.ts +643 -643
  119. package/lib/wordcomments.ts +840 -840
  120. package/mkdocs.yml +64 -64
  121. package/package.json +137 -137
  122. package/scripts/postbuild.js +47 -47
  123. package/skill/REFERENCE.md +539 -539
  124. package/skill/SKILL.md +295 -295
  125. package/tsconfig.json +26 -26
  126. package/types/index.d.ts +525 -525
@@ -1,201 +1,201 @@
1
- --[[
2
- docrev macro filter.
3
-
4
- Reads a JSON sidecar describing one-argument LaTeX-style macros and expands
5
- them per output FORMAT. Used for the built-in \tofill{X} (bold orange [X]
6
- placeholder) and any user-declared macros from rev.yaml.
7
-
8
- Sidecar path is passed via the DOCREV_MACROS_FILE environment variable, set
9
- by build.ts before spawning pandoc. Env vars (not metadata) because pandoc's
10
- filter traversal runs RawInline/RawBlock BEFORE Meta, so by the time we'd
11
- read metadata the inline expansions have already happened.
12
-
13
- Why raw OpenXML for docx? Pandoc 3.x's docx writer does NOT honor
14
- `Span{style="color: #..."}` — those spans render as plain text with no
15
- <w:color> run property. So for docx we emit raw <w:r> nodes directly. Same
16
- reasoning for the pptx-color-filter.
17
-
18
- For latex/pdf/beamer the markdown source already contains \tofill{X} as a raw
19
- LaTeX inline; we leave it alone because build.ts injects a \providecommand
20
- into header-includes. For html we emit a raw <span> with inline style. For
21
- everything else (markdown, gfm, plain) we degrade to **bold [X]** so the
22
- placeholder never silently disappears.
23
- ]]
24
-
25
- local json = require('pandoc.json')
26
-
27
- local macros_by_name = {}
28
-
29
- local function load_sidecar()
30
- local path = os.getenv('DOCREV_MACROS_FILE')
31
- if not path or path == '' then
32
- return
33
- end
34
- local fh = io.open(path, 'r')
35
- if not fh then
36
- io.stderr:write('docrev macro-filter: cannot read sidecar: ' .. path .. '\n')
37
- return
38
- end
39
- local content = fh:read('*a')
40
- fh:close()
41
- local ok, parsed = pcall(json.decode, content)
42
- if not ok or type(parsed) ~= 'table' or type(parsed.macros) ~= 'table' then
43
- io.stderr:write('docrev macro-filter: malformed sidecar JSON\n')
44
- return
45
- end
46
- for _, m in ipairs(parsed.macros) do
47
- if type(m) == 'table' and type(m.name) == 'string' then
48
- macros_by_name[m.name] = m
49
- end
50
- end
51
- end
52
-
53
- load_sidecar()
54
-
55
- local function xml_escape(s)
56
- return (s:gsub('&', '&amp;'):gsub('<', '&lt;'):gsub('>', '&gt;'))
57
- end
58
-
59
- local function html_escape(s)
60
- return (s
61
- :gsub('&', '&amp;')
62
- :gsub('<', '&lt;')
63
- :gsub('>', '&gt;')
64
- :gsub('"', '&quot;'))
65
- end
66
-
67
- -- Resolve effective style for a macro in the current pandoc format.
68
- -- Per-format entry wins over `default` (replacement, not merge — matches
69
- -- macros.ts semantics).
70
- local function pick_style(macro, format)
71
- if macro.formats and macro.formats[format] then
72
- return macro.formats[format]
73
- end
74
- return macro.default or {}
75
- end
76
-
77
- -- Build the inside of the bracket: [prefix][arg][suffix], optionally without
78
- -- brackets when style.bracket == false.
79
- local function compose_text(style, arg)
80
- local prefix = style.prefix or ''
81
- local suffix = style.suffix or ''
82
- local inner = prefix .. arg .. suffix
83
- if style.bracket == false then
84
- return inner
85
- end
86
- return '[' .. inner .. ']'
87
- end
88
-
89
- local function render_docx_run(style, arg)
90
- local rpr = {}
91
- if style.color then
92
- table.insert(rpr, '<w:color w:val="' .. style.color .. '"/>')
93
- end
94
- if style.bold then
95
- table.insert(rpr, '<w:b/>')
96
- end
97
- if style.italic then
98
- table.insert(rpr, '<w:i/>')
99
- end
100
- local rpr_xml = ''
101
- if #rpr > 0 then
102
- rpr_xml = '<w:rPr>' .. table.concat(rpr) .. '</w:rPr>'
103
- end
104
- local text = xml_escape(compose_text(style, arg))
105
- return '<w:r>' .. rpr_xml ..
106
- '<w:t xml:space="preserve">' .. text .. '</w:t></w:r>'
107
- end
108
-
109
- local function render_html(style, arg)
110
- local css = {}
111
- if style.color then
112
- table.insert(css, 'color:#' .. style.color)
113
- end
114
- if style.bold then
115
- table.insert(css, 'font-weight:bold')
116
- end
117
- if style.italic then
118
- table.insert(css, 'font-style:italic')
119
- end
120
- local text = html_escape(compose_text(style, arg))
121
- if #css == 0 then
122
- return '<span>' .. text .. '</span>'
123
- end
124
- return '<span style="' .. table.concat(css, ';') .. '">' .. text .. '</span>'
125
- end
126
-
127
- -- Fallback path: produce native pandoc inlines so the macro never silently
128
- -- disappears in markdown/gfm/plain output. Used when the current format has
129
- -- no native rich-text path (or we couldn't open the sidecar).
130
- local function fallback_inlines(style, arg)
131
- local doc = pandoc.read(compose_text(style, arg), 'markdown')
132
- local inlines = pandoc.utils.blocks_to_inlines(doc.blocks)
133
- if style.bold then
134
- inlines = { pandoc.Strong(inlines) }
135
- end
136
- if style.italic then
137
- inlines = { pandoc.Emph(inlines) }
138
- end
139
- return inlines
140
- end
141
-
142
- -- Match `\NAME{...}` (with balanced braces inside the argument is NOT
143
- -- supported — the use case is plain placeholder text, mirroring the reference
144
- -- filter; users who need nested braces should use a different mechanism).
145
- local function parse_call(text)
146
- local name, arg = text:match('^\\([A-Za-z][A-Za-z0-9]*)%s*{(.*)}%s*$')
147
- if name and arg and macros_by_name[name] then
148
- return name, arg
149
- end
150
- return nil, nil
151
- end
152
-
153
- local function expand_inline(el)
154
- if el.format ~= 'tex' and el.format ~= 'latex' then
155
- return nil
156
- end
157
- local name, arg = parse_call(el.text)
158
- if not name then return nil end
159
- local macro = macros_by_name[name]
160
- local style = pick_style(macro, FORMAT)
161
-
162
- if FORMAT == 'docx' then
163
- return pandoc.RawInline('openxml', render_docx_run(style, arg))
164
- elseif FORMAT == 'html' or FORMAT == 'html4' or FORMAT == 'html5' or FORMAT == 'chunkedhtml' then
165
- return pandoc.RawInline('html', render_html(style, arg))
166
- elseif FORMAT == 'latex' or FORMAT == 'beamer' or FORMAT == 'context' then
167
- -- Leave the raw LaTeX as-is. build.ts injects \providecommand into
168
- -- header-includes, so the LaTeX engine renders it directly.
169
- return nil
170
- else
171
- return fallback_inlines(style, arg)
172
- end
173
- end
174
-
175
- local function expand_block(el)
176
- if el.format ~= 'tex' and el.format ~= 'latex' then
177
- return nil
178
- end
179
- local name, arg = parse_call(el.text)
180
- if not name then return nil end
181
- local macro = macros_by_name[name]
182
- local style = pick_style(macro, FORMAT)
183
-
184
- if FORMAT == 'docx' then
185
- return pandoc.RawBlock('openxml', '<w:p>' .. render_docx_run(style, arg) .. '</w:p>')
186
- elseif FORMAT == 'html' or FORMAT == 'html4' or FORMAT == 'html5' or FORMAT == 'chunkedhtml' then
187
- return pandoc.RawBlock('html', '<p>' .. render_html(style, arg) .. '</p>')
188
- elseif FORMAT == 'latex' or FORMAT == 'beamer' or FORMAT == 'context' then
189
- return nil
190
- else
191
- return pandoc.Para(fallback_inlines(style, arg))
192
- end
193
- end
194
-
195
- function RawInline(el)
196
- return expand_inline(el)
197
- end
198
-
199
- function RawBlock(el)
200
- return expand_block(el)
201
- end
1
+ --[[
2
+ docrev macro filter.
3
+
4
+ Reads a JSON sidecar describing one-argument LaTeX-style macros and expands
5
+ them per output FORMAT. Used for the built-in \tofill{X} (bold orange [X]
6
+ placeholder) and any user-declared macros from rev.yaml.
7
+
8
+ Sidecar path is passed via the DOCREV_MACROS_FILE environment variable, set
9
+ by build.ts before spawning pandoc. Env vars (not metadata) because pandoc's
10
+ filter traversal runs RawInline/RawBlock BEFORE Meta, so by the time we'd
11
+ read metadata the inline expansions have already happened.
12
+
13
+ Why raw OpenXML for docx? Pandoc 3.x's docx writer does NOT honor
14
+ `Span{style="color: #..."}` — those spans render as plain text with no
15
+ <w:color> run property. So for docx we emit raw <w:r> nodes directly. Same
16
+ reasoning for the pptx-color-filter.
17
+
18
+ For latex/pdf/beamer the markdown source already contains \tofill{X} as a raw
19
+ LaTeX inline; we leave it alone because build.ts injects a \providecommand
20
+ into header-includes. For html we emit a raw <span> with inline style. For
21
+ everything else (markdown, gfm, plain) we degrade to **bold [X]** so the
22
+ placeholder never silently disappears.
23
+ ]]
24
+
25
+ local json = require('pandoc.json')
26
+
27
+ local macros_by_name = {}
28
+
29
+ local function load_sidecar()
30
+ local path = os.getenv('DOCREV_MACROS_FILE')
31
+ if not path or path == '' then
32
+ return
33
+ end
34
+ local fh = io.open(path, 'r')
35
+ if not fh then
36
+ io.stderr:write('docrev macro-filter: cannot read sidecar: ' .. path .. '\n')
37
+ return
38
+ end
39
+ local content = fh:read('*a')
40
+ fh:close()
41
+ local ok, parsed = pcall(json.decode, content)
42
+ if not ok or type(parsed) ~= 'table' or type(parsed.macros) ~= 'table' then
43
+ io.stderr:write('docrev macro-filter: malformed sidecar JSON\n')
44
+ return
45
+ end
46
+ for _, m in ipairs(parsed.macros) do
47
+ if type(m) == 'table' and type(m.name) == 'string' then
48
+ macros_by_name[m.name] = m
49
+ end
50
+ end
51
+ end
52
+
53
+ load_sidecar()
54
+
55
+ local function xml_escape(s)
56
+ return (s:gsub('&', '&amp;'):gsub('<', '&lt;'):gsub('>', '&gt;'))
57
+ end
58
+
59
+ local function html_escape(s)
60
+ return (s
61
+ :gsub('&', '&amp;')
62
+ :gsub('<', '&lt;')
63
+ :gsub('>', '&gt;')
64
+ :gsub('"', '&quot;'))
65
+ end
66
+
67
+ -- Resolve effective style for a macro in the current pandoc format.
68
+ -- Per-format entry wins over `default` (replacement, not merge — matches
69
+ -- macros.ts semantics).
70
+ local function pick_style(macro, format)
71
+ if macro.formats and macro.formats[format] then
72
+ return macro.formats[format]
73
+ end
74
+ return macro.default or {}
75
+ end
76
+
77
+ -- Build the inside of the bracket: [prefix][arg][suffix], optionally without
78
+ -- brackets when style.bracket == false.
79
+ local function compose_text(style, arg)
80
+ local prefix = style.prefix or ''
81
+ local suffix = style.suffix or ''
82
+ local inner = prefix .. arg .. suffix
83
+ if style.bracket == false then
84
+ return inner
85
+ end
86
+ return '[' .. inner .. ']'
87
+ end
88
+
89
+ local function render_docx_run(style, arg)
90
+ local rpr = {}
91
+ if style.color then
92
+ table.insert(rpr, '<w:color w:val="' .. style.color .. '"/>')
93
+ end
94
+ if style.bold then
95
+ table.insert(rpr, '<w:b/>')
96
+ end
97
+ if style.italic then
98
+ table.insert(rpr, '<w:i/>')
99
+ end
100
+ local rpr_xml = ''
101
+ if #rpr > 0 then
102
+ rpr_xml = '<w:rPr>' .. table.concat(rpr) .. '</w:rPr>'
103
+ end
104
+ local text = xml_escape(compose_text(style, arg))
105
+ return '<w:r>' .. rpr_xml ..
106
+ '<w:t xml:space="preserve">' .. text .. '</w:t></w:r>'
107
+ end
108
+
109
+ local function render_html(style, arg)
110
+ local css = {}
111
+ if style.color then
112
+ table.insert(css, 'color:#' .. style.color)
113
+ end
114
+ if style.bold then
115
+ table.insert(css, 'font-weight:bold')
116
+ end
117
+ if style.italic then
118
+ table.insert(css, 'font-style:italic')
119
+ end
120
+ local text = html_escape(compose_text(style, arg))
121
+ if #css == 0 then
122
+ return '<span>' .. text .. '</span>'
123
+ end
124
+ return '<span style="' .. table.concat(css, ';') .. '">' .. text .. '</span>'
125
+ end
126
+
127
+ -- Fallback path: produce native pandoc inlines so the macro never silently
128
+ -- disappears in markdown/gfm/plain output. Used when the current format has
129
+ -- no native rich-text path (or we couldn't open the sidecar).
130
+ local function fallback_inlines(style, arg)
131
+ local doc = pandoc.read(compose_text(style, arg), 'markdown')
132
+ local inlines = pandoc.utils.blocks_to_inlines(doc.blocks)
133
+ if style.bold then
134
+ inlines = { pandoc.Strong(inlines) }
135
+ end
136
+ if style.italic then
137
+ inlines = { pandoc.Emph(inlines) }
138
+ end
139
+ return inlines
140
+ end
141
+
142
+ -- Match `\NAME{...}` (with balanced braces inside the argument is NOT
143
+ -- supported — the use case is plain placeholder text, mirroring the reference
144
+ -- filter; users who need nested braces should use a different mechanism).
145
+ local function parse_call(text)
146
+ local name, arg = text:match('^\\([A-Za-z][A-Za-z0-9]*)%s*{(.*)}%s*$')
147
+ if name and arg and macros_by_name[name] then
148
+ return name, arg
149
+ end
150
+ return nil, nil
151
+ end
152
+
153
+ local function expand_inline(el)
154
+ if el.format ~= 'tex' and el.format ~= 'latex' then
155
+ return nil
156
+ end
157
+ local name, arg = parse_call(el.text)
158
+ if not name then return nil end
159
+ local macro = macros_by_name[name]
160
+ local style = pick_style(macro, FORMAT)
161
+
162
+ if FORMAT == 'docx' then
163
+ return pandoc.RawInline('openxml', render_docx_run(style, arg))
164
+ elseif FORMAT == 'html' or FORMAT == 'html4' or FORMAT == 'html5' or FORMAT == 'chunkedhtml' then
165
+ return pandoc.RawInline('html', render_html(style, arg))
166
+ elseif FORMAT == 'latex' or FORMAT == 'beamer' or FORMAT == 'context' then
167
+ -- Leave the raw LaTeX as-is. build.ts injects \providecommand into
168
+ -- header-includes, so the LaTeX engine renders it directly.
169
+ return nil
170
+ else
171
+ return fallback_inlines(style, arg)
172
+ end
173
+ end
174
+
175
+ local function expand_block(el)
176
+ if el.format ~= 'tex' and el.format ~= 'latex' then
177
+ return nil
178
+ end
179
+ local name, arg = parse_call(el.text)
180
+ if not name then return nil end
181
+ local macro = macros_by_name[name]
182
+ local style = pick_style(macro, FORMAT)
183
+
184
+ if FORMAT == 'docx' then
185
+ return pandoc.RawBlock('openxml', '<w:p>' .. render_docx_run(style, arg) .. '</w:p>')
186
+ elseif FORMAT == 'html' or FORMAT == 'html4' or FORMAT == 'html5' or FORMAT == 'chunkedhtml' then
187
+ return pandoc.RawBlock('html', '<p>' .. render_html(style, arg) .. '</p>')
188
+ elseif FORMAT == 'latex' or FORMAT == 'beamer' or FORMAT == 'context' then
189
+ return nil
190
+ else
191
+ return pandoc.Para(fallback_inlines(style, arg))
192
+ end
193
+ end
194
+
195
+ function RawInline(el)
196
+ return expand_inline(el)
197
+ end
198
+
199
+ function RawBlock(el)
200
+ return expand_block(el)
201
+ end