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.
Files changed (306) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/PLAN-tables-and-postprocess.md +850 -0
  3. package/README.md +33 -0
  4. package/bin/rev.js +12 -131
  5. package/bin/rev.ts +145 -0
  6. package/dist/bin/rev.d.ts +9 -0
  7. package/dist/bin/rev.d.ts.map +1 -0
  8. package/dist/bin/rev.js +118 -0
  9. package/dist/bin/rev.js.map +1 -0
  10. package/dist/lib/annotations.d.ts +91 -0
  11. package/dist/lib/annotations.d.ts.map +1 -0
  12. package/dist/lib/annotations.js +554 -0
  13. package/dist/lib/annotations.js.map +1 -0
  14. package/dist/lib/build.d.ts +171 -0
  15. package/dist/lib/build.d.ts.map +1 -0
  16. package/dist/lib/build.js +755 -0
  17. package/dist/lib/build.js.map +1 -0
  18. package/dist/lib/citations.d.ts +34 -0
  19. package/dist/lib/citations.d.ts.map +1 -0
  20. package/dist/lib/citations.js +140 -0
  21. package/dist/lib/citations.js.map +1 -0
  22. package/dist/lib/commands/build.d.ts +13 -0
  23. package/dist/lib/commands/build.d.ts.map +1 -0
  24. package/dist/lib/commands/build.js +678 -0
  25. package/dist/lib/commands/build.js.map +1 -0
  26. package/dist/lib/commands/citations.d.ts +11 -0
  27. package/dist/lib/commands/citations.d.ts.map +1 -0
  28. package/dist/lib/commands/citations.js +428 -0
  29. package/dist/lib/commands/citations.js.map +1 -0
  30. package/dist/lib/commands/comments.d.ts +11 -0
  31. package/dist/lib/commands/comments.d.ts.map +1 -0
  32. package/dist/lib/commands/comments.js +883 -0
  33. package/dist/lib/commands/comments.js.map +1 -0
  34. package/dist/lib/commands/context.d.ts +35 -0
  35. package/dist/lib/commands/context.d.ts.map +1 -0
  36. package/dist/lib/commands/context.js +59 -0
  37. package/dist/lib/commands/context.js.map +1 -0
  38. package/dist/lib/commands/core.d.ts +11 -0
  39. package/dist/lib/commands/core.d.ts.map +1 -0
  40. package/dist/lib/commands/core.js +246 -0
  41. package/dist/lib/commands/core.js.map +1 -0
  42. package/dist/lib/commands/doi.d.ts +11 -0
  43. package/dist/lib/commands/doi.d.ts.map +1 -0
  44. package/dist/lib/commands/doi.js +373 -0
  45. package/dist/lib/commands/doi.js.map +1 -0
  46. package/dist/lib/commands/history.d.ts +11 -0
  47. package/dist/lib/commands/history.d.ts.map +1 -0
  48. package/dist/lib/commands/history.js +245 -0
  49. package/dist/lib/commands/history.js.map +1 -0
  50. package/dist/lib/commands/index.d.ts +28 -0
  51. package/dist/lib/commands/index.d.ts.map +1 -0
  52. package/dist/lib/commands/index.js +35 -0
  53. package/dist/lib/commands/index.js.map +1 -0
  54. package/dist/lib/commands/init.d.ts +11 -0
  55. package/dist/lib/commands/init.d.ts.map +1 -0
  56. package/dist/lib/commands/init.js +209 -0
  57. package/dist/lib/commands/init.js.map +1 -0
  58. package/dist/lib/commands/response.d.ts +11 -0
  59. package/dist/lib/commands/response.d.ts.map +1 -0
  60. package/dist/lib/commands/response.js +317 -0
  61. package/dist/lib/commands/response.js.map +1 -0
  62. package/dist/lib/commands/sections.d.ts +11 -0
  63. package/dist/lib/commands/sections.d.ts.map +1 -0
  64. package/dist/lib/commands/sections.js +1071 -0
  65. package/dist/lib/commands/sections.js.map +1 -0
  66. package/dist/lib/commands/utilities.d.ts +19 -0
  67. package/dist/lib/commands/utilities.d.ts.map +1 -0
  68. package/dist/lib/commands/utilities.js +2009 -0
  69. package/dist/lib/commands/utilities.js.map +1 -0
  70. package/dist/lib/comment-realign.d.ts +50 -0
  71. package/dist/lib/comment-realign.d.ts.map +1 -0
  72. package/dist/lib/comment-realign.js +372 -0
  73. package/dist/lib/comment-realign.js.map +1 -0
  74. package/dist/lib/config.d.ts +41 -0
  75. package/dist/lib/config.d.ts.map +1 -0
  76. package/dist/lib/config.js +76 -0
  77. package/dist/lib/config.js.map +1 -0
  78. package/dist/lib/crossref.d.ts +108 -0
  79. package/dist/lib/crossref.d.ts.map +1 -0
  80. package/dist/lib/crossref.js +597 -0
  81. package/dist/lib/crossref.js.map +1 -0
  82. package/dist/lib/dependencies.d.ts +30 -0
  83. package/dist/lib/dependencies.d.ts.map +1 -0
  84. package/dist/lib/dependencies.js +95 -0
  85. package/dist/lib/dependencies.js.map +1 -0
  86. package/dist/lib/doi-cache.d.ts +29 -0
  87. package/dist/lib/doi-cache.d.ts.map +1 -0
  88. package/dist/lib/doi-cache.js +104 -0
  89. package/dist/lib/doi-cache.js.map +1 -0
  90. package/dist/lib/doi.d.ts +65 -0
  91. package/dist/lib/doi.d.ts.map +1 -0
  92. package/dist/lib/doi.js +710 -0
  93. package/dist/lib/doi.js.map +1 -0
  94. package/dist/lib/equations.d.ts +61 -0
  95. package/dist/lib/equations.d.ts.map +1 -0
  96. package/dist/lib/equations.js +445 -0
  97. package/dist/lib/equations.js.map +1 -0
  98. package/dist/lib/errors.d.ts +60 -0
  99. package/dist/lib/errors.d.ts.map +1 -0
  100. package/dist/lib/errors.js +303 -0
  101. package/dist/lib/errors.js.map +1 -0
  102. package/dist/lib/format.d.ts +104 -0
  103. package/dist/lib/format.d.ts.map +1 -0
  104. package/dist/lib/format.js +416 -0
  105. package/dist/lib/format.js.map +1 -0
  106. package/dist/lib/git.d.ts +88 -0
  107. package/dist/lib/git.d.ts.map +1 -0
  108. package/dist/lib/git.js +304 -0
  109. package/dist/lib/git.js.map +1 -0
  110. package/dist/lib/grammar.d.ts +62 -0
  111. package/dist/lib/grammar.d.ts.map +1 -0
  112. package/dist/lib/grammar.js +244 -0
  113. package/dist/lib/grammar.js.map +1 -0
  114. package/dist/lib/image-registry.d.ts +68 -0
  115. package/dist/lib/image-registry.d.ts.map +1 -0
  116. package/dist/lib/image-registry.js +112 -0
  117. package/dist/lib/image-registry.js.map +1 -0
  118. package/dist/lib/import.d.ts +184 -0
  119. package/dist/lib/import.d.ts.map +1 -0
  120. package/dist/lib/import.js +1581 -0
  121. package/dist/lib/import.js.map +1 -0
  122. package/dist/lib/journals.d.ts +55 -0
  123. package/dist/lib/journals.d.ts.map +1 -0
  124. package/dist/lib/journals.js +417 -0
  125. package/dist/lib/journals.js.map +1 -0
  126. package/dist/lib/merge.d.ts +138 -0
  127. package/dist/lib/merge.d.ts.map +1 -0
  128. package/dist/lib/merge.js +603 -0
  129. package/dist/lib/merge.js.map +1 -0
  130. package/dist/lib/orcid.d.ts +36 -0
  131. package/dist/lib/orcid.d.ts.map +1 -0
  132. package/dist/lib/orcid.js +117 -0
  133. package/dist/lib/orcid.js.map +1 -0
  134. package/dist/lib/pdf-comments.d.ts +95 -0
  135. package/dist/lib/pdf-comments.d.ts.map +1 -0
  136. package/dist/lib/pdf-comments.js +192 -0
  137. package/dist/lib/pdf-comments.js.map +1 -0
  138. package/dist/lib/pdf-import.d.ts +118 -0
  139. package/dist/lib/pdf-import.d.ts.map +1 -0
  140. package/dist/lib/pdf-import.js +397 -0
  141. package/dist/lib/pdf-import.js.map +1 -0
  142. package/dist/lib/plugins.d.ts +76 -0
  143. package/dist/lib/plugins.d.ts.map +1 -0
  144. package/dist/lib/plugins.js +235 -0
  145. package/dist/lib/plugins.js.map +1 -0
  146. package/dist/lib/postprocess.d.ts +42 -0
  147. package/dist/lib/postprocess.d.ts.map +1 -0
  148. package/dist/lib/postprocess.js +138 -0
  149. package/dist/lib/postprocess.js.map +1 -0
  150. package/dist/lib/pptx-template.d.ts +59 -0
  151. package/dist/lib/pptx-template.d.ts.map +1 -0
  152. package/dist/lib/pptx-template.js +613 -0
  153. package/dist/lib/pptx-template.js.map +1 -0
  154. package/dist/lib/pptx-themes.d.ts +80 -0
  155. package/dist/lib/pptx-themes.d.ts.map +1 -0
  156. package/dist/lib/pptx-themes.js +818 -0
  157. package/dist/lib/pptx-themes.js.map +1 -0
  158. package/dist/lib/protect-restore.d.ts +137 -0
  159. package/dist/lib/protect-restore.d.ts.map +1 -0
  160. package/dist/lib/protect-restore.js +394 -0
  161. package/dist/lib/protect-restore.js.map +1 -0
  162. package/dist/lib/rate-limiter.d.ts +27 -0
  163. package/dist/lib/rate-limiter.d.ts.map +1 -0
  164. package/dist/lib/rate-limiter.js +79 -0
  165. package/dist/lib/rate-limiter.js.map +1 -0
  166. package/dist/lib/response.d.ts +41 -0
  167. package/dist/lib/response.d.ts.map +1 -0
  168. package/dist/lib/response.js +150 -0
  169. package/dist/lib/response.js.map +1 -0
  170. package/dist/lib/review.d.ts +35 -0
  171. package/dist/lib/review.d.ts.map +1 -0
  172. package/dist/lib/review.js +263 -0
  173. package/dist/lib/review.js.map +1 -0
  174. package/dist/lib/schema.d.ts +66 -0
  175. package/dist/lib/schema.d.ts.map +1 -0
  176. package/dist/lib/schema.js +339 -0
  177. package/dist/lib/schema.js.map +1 -0
  178. package/dist/lib/scientific-words.d.ts +6 -0
  179. package/dist/lib/scientific-words.d.ts.map +1 -0
  180. package/dist/lib/scientific-words.js +66 -0
  181. package/dist/lib/scientific-words.js.map +1 -0
  182. package/dist/lib/sections.d.ts +40 -0
  183. package/dist/lib/sections.d.ts.map +1 -0
  184. package/dist/lib/sections.js +288 -0
  185. package/dist/lib/sections.js.map +1 -0
  186. package/dist/lib/slides.d.ts +86 -0
  187. package/dist/lib/slides.d.ts.map +1 -0
  188. package/dist/lib/slides.js +676 -0
  189. package/dist/lib/slides.js.map +1 -0
  190. package/dist/lib/spelling.d.ts +76 -0
  191. package/dist/lib/spelling.d.ts.map +1 -0
  192. package/dist/lib/spelling.js +272 -0
  193. package/dist/lib/spelling.js.map +1 -0
  194. package/dist/lib/templates.d.ts +30 -0
  195. package/dist/lib/templates.d.ts.map +1 -0
  196. package/dist/lib/templates.js +504 -0
  197. package/dist/lib/templates.js.map +1 -0
  198. package/dist/lib/themes.d.ts +85 -0
  199. package/dist/lib/themes.d.ts.map +1 -0
  200. package/dist/lib/themes.js +652 -0
  201. package/dist/lib/themes.js.map +1 -0
  202. package/dist/lib/trackchanges.d.ts +51 -0
  203. package/dist/lib/trackchanges.d.ts.map +1 -0
  204. package/dist/lib/trackchanges.js +202 -0
  205. package/dist/lib/trackchanges.js.map +1 -0
  206. package/dist/lib/tui.d.ts +76 -0
  207. package/dist/lib/tui.d.ts.map +1 -0
  208. package/dist/lib/tui.js +377 -0
  209. package/dist/lib/tui.js.map +1 -0
  210. package/dist/lib/types.d.ts +447 -0
  211. package/dist/lib/types.d.ts.map +1 -0
  212. package/dist/lib/types.js +6 -0
  213. package/dist/lib/types.js.map +1 -0
  214. package/dist/lib/undo.d.ts +57 -0
  215. package/dist/lib/undo.d.ts.map +1 -0
  216. package/dist/lib/undo.js +185 -0
  217. package/dist/lib/undo.js.map +1 -0
  218. package/dist/lib/utils.d.ts +16 -0
  219. package/dist/lib/utils.d.ts.map +1 -0
  220. package/dist/lib/utils.js +40 -0
  221. package/dist/lib/utils.js.map +1 -0
  222. package/dist/lib/variables.d.ts +42 -0
  223. package/dist/lib/variables.d.ts.map +1 -0
  224. package/dist/lib/variables.js +141 -0
  225. package/dist/lib/variables.js.map +1 -0
  226. package/dist/lib/word.d.ts +80 -0
  227. package/dist/lib/word.d.ts.map +1 -0
  228. package/dist/lib/word.js +360 -0
  229. package/dist/lib/word.js.map +1 -0
  230. package/dist/lib/wordcomments.d.ts +51 -0
  231. package/dist/lib/wordcomments.d.ts.map +1 -0
  232. package/dist/lib/wordcomments.js +587 -0
  233. package/dist/lib/wordcomments.js.map +1 -0
  234. package/eslint.config.js +27 -0
  235. package/lib/annotations.ts +622 -0
  236. package/lib/apply-buildup-colors.py +88 -0
  237. package/lib/build.ts +1013 -0
  238. package/lib/{citations.js → citations.ts} +38 -27
  239. package/lib/commands/{build.js → build.ts} +80 -27
  240. package/lib/commands/{citations.js → citations.ts} +36 -18
  241. package/lib/commands/{comments.js → comments.ts} +187 -54
  242. package/lib/commands/{context.js → context.ts} +18 -8
  243. package/lib/commands/{core.js → core.ts} +34 -20
  244. package/lib/commands/{doi.js → doi.ts} +32 -16
  245. package/lib/commands/{history.js → history.ts} +25 -12
  246. package/lib/commands/{index.js → index.ts} +9 -5
  247. package/lib/commands/{init.js → init.ts} +20 -8
  248. package/lib/commands/{response.js → response.ts} +47 -20
  249. package/lib/commands/{sections.js → sections.ts} +273 -68
  250. package/lib/commands/{utilities.js → utilities.ts} +338 -158
  251. package/lib/{comment-realign.js → comment-realign.ts} +117 -45
  252. package/lib/config.ts +84 -0
  253. package/lib/{crossref.js → crossref.ts} +213 -138
  254. package/lib/dependencies.ts +106 -0
  255. package/lib/doi-cache.ts +115 -0
  256. package/lib/{doi.js → doi.ts} +115 -281
  257. package/lib/{equations.js → equations.ts} +60 -64
  258. package/lib/{errors.js → errors.ts} +56 -48
  259. package/lib/{format.js → format.ts} +137 -63
  260. package/lib/{git.js → git.ts} +66 -63
  261. package/lib/{grammar.js → grammar.ts} +45 -32
  262. package/lib/image-registry.ts +180 -0
  263. package/lib/import.ts +2060 -0
  264. package/lib/journals.ts +505 -0
  265. package/lib/{merge.js → merge.ts} +185 -135
  266. package/lib/{orcid.js → orcid.ts} +17 -22
  267. package/lib/{pdf-comments.js → pdf-comments.ts} +76 -18
  268. package/lib/{pdf-import.js → pdf-import.ts} +148 -70
  269. package/lib/{plugins.js → plugins.ts} +82 -39
  270. package/lib/postprocess.ts +188 -0
  271. package/lib/pptx-color-filter.lua +37 -0
  272. package/lib/pptx-template.ts +625 -0
  273. package/lib/pptx-themes/academic.pptx +0 -0
  274. package/lib/pptx-themes/corporate.pptx +0 -0
  275. package/lib/pptx-themes/dark.pptx +0 -0
  276. package/lib/pptx-themes/default.pptx +0 -0
  277. package/lib/pptx-themes/minimal.pptx +0 -0
  278. package/lib/pptx-themes/plant.pptx +0 -0
  279. package/lib/pptx-themes.ts +896 -0
  280. package/lib/protect-restore.ts +516 -0
  281. package/lib/rate-limiter.ts +94 -0
  282. package/lib/{response.js → response.ts} +36 -21
  283. package/lib/{review.js → review.ts} +53 -43
  284. package/lib/{schema.js → schema.ts} +70 -25
  285. package/lib/{sections.js → sections.ts} +71 -76
  286. package/lib/slides.ts +793 -0
  287. package/lib/{spelling.js → spelling.ts} +43 -59
  288. package/lib/{templates.js → templates.ts} +20 -17
  289. package/lib/themes.ts +742 -0
  290. package/lib/{trackchanges.js → trackchanges.ts} +52 -23
  291. package/lib/types.ts +509 -0
  292. package/lib/{undo.js → undo.ts} +75 -52
  293. package/lib/utils.ts +41 -0
  294. package/lib/{variables.js → variables.ts} +60 -54
  295. package/lib/word.ts +428 -0
  296. package/lib/{wordcomments.js → wordcomments.ts} +94 -40
  297. package/package.json +15 -5
  298. package/skill/REFERENCE.md +67 -0
  299. package/tsconfig.json +26 -0
  300. package/lib/annotations.js +0 -414
  301. package/lib/build.js +0 -639
  302. package/lib/config.js +0 -79
  303. package/lib/import.js +0 -1145
  304. package/lib/journals.js +0 -629
  305. package/lib/word.js +0 -225
  306. /package/lib/{scientific-words.js → scientific-words.ts} +0 -0
@@ -4,21 +4,24 @@
4
4
  * Fetches author information from ORCID public API
5
5
  */
6
6
 
7
+ export interface OrcidProfile {
8
+ orcid: string;
9
+ name: string;
10
+ affiliation: string;
11
+ email: string;
12
+ }
13
+
7
14
  /**
8
15
  * Validate ORCID format (0000-0000-0000-0000)
9
- * @param {string} orcid
10
- * @returns {boolean}
11
16
  */
12
- export function isValidOrcid(orcid) {
17
+ export function isValidOrcid(orcid: string): boolean {
13
18
  return /^(\d{4}-){3}\d{3}[\dX]$/i.test(orcid);
14
19
  }
15
20
 
16
21
  /**
17
22
  * Clean ORCID input (removes URLs, whitespace)
18
- * @param {string} input
19
- * @returns {string}
20
23
  */
21
- export function cleanOrcid(input) {
24
+ export function cleanOrcid(input: string): string {
22
25
  if (!input) return '';
23
26
 
24
27
  // Remove URL prefix if present
@@ -32,10 +35,8 @@ export function cleanOrcid(input) {
32
35
 
33
36
  /**
34
37
  * Fetch author info from ORCID public API
35
- * @param {string} orcid
36
- * @returns {Promise<{name: string, affiliation: string, email: string, works: number}>}
37
38
  */
38
- export async function fetchOrcidProfile(orcid) {
39
+ export async function fetchOrcidProfile(orcid: string): Promise<OrcidProfile> {
39
40
  const cleanId = cleanOrcid(orcid);
40
41
 
41
42
  if (!isValidOrcid(cleanId)) {
@@ -57,7 +58,7 @@ export async function fetchOrcidProfile(orcid) {
57
58
  throw new Error(`ORCID API error: ${response.status}`);
58
59
  }
59
60
 
60
- const data = await response.json();
61
+ const data = await response.json() as any;
61
62
 
62
63
  // Extract name
63
64
  const nameData = data.name;
@@ -79,7 +80,7 @@ export async function fetchOrcidProfile(orcid) {
79
80
  // Extract email (if public)
80
81
  let email = '';
81
82
  const emails = data.emails?.email || [];
82
- const primaryEmail = emails.find(e => e.primary) || emails[0];
83
+ const primaryEmail = emails.find((e: any) => e.primary) || emails[0];
83
84
  if (primaryEmail?.email) {
84
85
  email = primaryEmail.email;
85
86
  }
@@ -94,10 +95,8 @@ export async function fetchOrcidProfile(orcid) {
94
95
 
95
96
  /**
96
97
  * Fetch work count from ORCID
97
- * @param {string} orcid
98
- * @returns {Promise<number>}
99
98
  */
100
- export async function fetchOrcidWorkCount(orcid) {
99
+ export async function fetchOrcidWorkCount(orcid: string): Promise<number> {
101
100
  const cleanId = cleanOrcid(orcid);
102
101
 
103
102
  if (!isValidOrcid(cleanId)) {
@@ -116,17 +115,15 @@ export async function fetchOrcidWorkCount(orcid) {
116
115
  return 0;
117
116
  }
118
117
 
119
- const data = await response.json();
118
+ const data = await response.json() as any;
120
119
  return data.group?.length || 0;
121
120
  }
122
121
 
123
122
  /**
124
123
  * Format author for YAML
125
- * @param {object} profile
126
- * @returns {string}
127
124
  */
128
- export function formatAuthorYaml(profile) {
129
- const lines = [];
125
+ export function formatAuthorYaml(profile: OrcidProfile): string {
126
+ const lines: string[] = [];
130
127
  lines.push(` - name: ${profile.name}`);
131
128
  if (profile.affiliation) {
132
129
  lines.push(` affiliation: ${profile.affiliation}`);
@@ -140,10 +137,8 @@ export function formatAuthorYaml(profile) {
140
137
 
141
138
  /**
142
139
  * Generate ORCID badge markdown
143
- * @param {string} orcid
144
- * @returns {string}
145
140
  */
146
- export function getOrcidBadge(orcid) {
141
+ export function getOrcidBadge(orcid: string): string {
147
142
  const cleanId = cleanOrcid(orcid);
148
143
  return `[![ORCID](https://img.shields.io/badge/ORCID-${cleanId}-a6ce39)](https://orcid.org/${cleanId})`;
149
144
  }
@@ -47,15 +47,35 @@ export const SIMPLE_MARGIN_PREAMBLE = `
47
47
  }
48
48
  `;
49
49
 
50
+ /**
51
+ * Options for converting comments to margin notes
52
+ */
53
+ export interface CommentConversionOptions {
54
+ useTodonotes?: boolean;
55
+ stripResolved?: boolean;
56
+ }
57
+
58
+ /**
59
+ * Result of comment conversion
60
+ */
61
+ export interface CommentConversionResult {
62
+ markdown: string;
63
+ commentCount: number;
64
+ preamble: string;
65
+ }
66
+
50
67
  /**
51
68
  * Convert CriticMarkup comments to LaTeX margin notes
52
69
  * {>>Author: comment text<<} -> \margincomment{Author: comment text}
53
70
  *
54
- * @param {string} markdown - Markdown with CriticMarkup comments
55
- * @param {object} options - { useTodonotes: boolean, stripResolved: boolean }
56
- * @returns {{markdown: string, commentCount: number, preamble: string}}
71
+ * @param markdown - Markdown with CriticMarkup comments
72
+ * @param options - { useTodonotes: boolean, stripResolved: boolean }
73
+ * @returns Converted markdown with comment count and preamble
57
74
  */
58
- export function convertCommentsToMarginNotes(markdown, options = {}) {
75
+ export function convertCommentsToMarginNotes(
76
+ markdown: string,
77
+ options: CommentConversionOptions = {}
78
+ ): CommentConversionResult {
59
79
  const { useTodonotes = true, stripResolved = true } = options;
60
80
 
61
81
  let commentCount = 0;
@@ -77,7 +97,7 @@ export function convertCommentsToMarginNotes(markdown, options = {}) {
77
97
 
78
98
  if (useTodonotes) {
79
99
  // Check if content has author prefix (Author: text)
80
- const authorMatch = escaped.match(/^([^:]+):\s*(.+)$/s);
100
+ const authorMatch = escaped.match(/^([^:]+):\s*([\s\S]+)$/);
81
101
  if (authorMatch) {
82
102
  const [, author, text] = authorMatch;
83
103
  return `\\reviewercomment{${author}}{${text}}`;
@@ -99,10 +119,10 @@ export function convertCommentsToMarginNotes(markdown, options = {}) {
99
119
 
100
120
  /**
101
121
  * Escape LaTeX special characters
102
- * @param {string} text
103
- * @returns {string}
122
+ * @param text - Text to escape
123
+ * @returns Escaped text
104
124
  */
105
- function escapeLatex(text) {
125
+ function escapeLatex(text: string): string {
106
126
  return text
107
127
  .replace(/\\/g, '\\textbackslash{}')
108
128
  .replace(/([#$%&_{}])/g, '\\$1')
@@ -111,16 +131,24 @@ function escapeLatex(text) {
111
131
  .replace(/\n/g, ' '); // Replace newlines with spaces
112
132
  }
113
133
 
134
+ /**
135
+ * Result of track changes conversion
136
+ */
137
+ export interface TrackChangesResult {
138
+ markdown: string;
139
+ preamble: string;
140
+ }
141
+
114
142
  /**
115
143
  * Convert track changes to visible LaTeX formatting
116
144
  * {++inserted++} -> \textcolor{green}{inserted}
117
145
  * {--deleted--} -> \textcolor{red}{\sout{deleted}}
118
146
  * {~~old~>new~~} -> \textcolor{red}{\sout{old}}\textcolor{green}{new}
119
147
  *
120
- * @param {string} markdown
121
- * @returns {{markdown: string, preamble: string}}
148
+ * @param markdown - Markdown with track changes
149
+ * @returns Converted markdown and preamble
122
150
  */
123
- export function convertTrackChangesToLatex(markdown) {
151
+ export function convertTrackChangesToLatex(markdown: string): TrackChangesResult {
124
152
  let result = markdown;
125
153
 
126
154
  // Insertions: {++text++} -> green text
@@ -149,12 +177,21 @@ export function convertTrackChangesToLatex(markdown) {
149
177
  return { markdown: result, preamble };
150
178
  }
151
179
 
180
+ /**
181
+ * Options for combined preamble
182
+ */
183
+ export interface PreambleOptions {
184
+ comments?: boolean;
185
+ trackChanges?: boolean;
186
+ useTodonotes?: boolean;
187
+ }
188
+
152
189
  /**
153
190
  * Get combined preamble for comments and track changes
154
- * @param {object} options - { comments: boolean, trackChanges: boolean, useTodonotes: boolean }
155
- * @returns {string}
191
+ * @param options - { comments: boolean, trackChanges: boolean, useTodonotes: boolean }
192
+ * @returns Combined LaTeX preamble
156
193
  */
157
- export function getCombinedPreamble(options = {}) {
194
+ export function getCombinedPreamble(options: PreambleOptions = {}): string {
158
195
  const { comments = true, trackChanges = false, useTodonotes = true } = options;
159
196
 
160
197
  let preamble = '';
@@ -180,15 +217,36 @@ export function getCombinedPreamble(options = {}) {
180
217
  return preamble;
181
218
  }
182
219
 
220
+ /**
221
+ * Options for preparing markdown for annotated PDF
222
+ */
223
+ export interface AnnotatedPdfOptions {
224
+ showTrackChanges?: boolean;
225
+ useTodonotes?: boolean;
226
+ stripResolved?: boolean;
227
+ }
228
+
229
+ /**
230
+ * Result of preparing markdown for annotated PDF
231
+ */
232
+ export interface AnnotatedPdfResult {
233
+ markdown: string;
234
+ preamble: string;
235
+ commentCount: number;
236
+ }
237
+
183
238
  /**
184
239
  * Prepare markdown for PDF with visible comments
185
240
  * Converts comments to margin notes and optionally shows track changes
186
241
  *
187
- * @param {string} markdown
188
- * @param {object} options - { showTrackChanges: boolean, useTodonotes: boolean }
189
- * @returns {{markdown: string, preamble: string, commentCount: number}}
242
+ * @param markdown - Markdown content
243
+ * @param options - { showTrackChanges: boolean, useTodonotes: boolean }
244
+ * @returns Converted markdown with preamble and comment count
190
245
  */
191
- export function prepareMarkdownForAnnotatedPdf(markdown, options = {}) {
246
+ export function prepareMarkdownForAnnotatedPdf(
247
+ markdown: string,
248
+ options: AnnotatedPdfOptions = {}
249
+ ): AnnotatedPdfResult {
192
250
  const { showTrackChanges = false, useTodonotes = true, stripResolved = true } = options;
193
251
 
194
252
  let result = markdown;
@@ -22,13 +22,82 @@ const COMMENT_TYPES = [
22
22
  'Popup', // Popup comments (attached to other annotations)
23
23
  ];
24
24
 
25
+ /**
26
+ * Raw PDF annotation extracted from pdf-lib
27
+ */
28
+ export interface PdfAnnotation {
29
+ type: string;
30
+ page: number;
31
+ contents: string;
32
+ author: string;
33
+ date: string;
34
+ rect: number[];
35
+ quadPoints: number[];
36
+ }
37
+
38
+ /**
39
+ * PDF comment converted to CriticMarkup format
40
+ */
41
+ export interface PdfComment {
42
+ author: string;
43
+ text: string;
44
+ page: number;
45
+ type: string;
46
+ date?: string;
47
+ }
48
+
49
+ /**
50
+ * PDF annotation with extracted highlighted text
51
+ */
52
+ export interface PdfAnnotationWithText extends PdfAnnotation {
53
+ highlightedText: string;
54
+ }
55
+
56
+ /**
57
+ * Options for PDF extraction
58
+ */
59
+ export interface ExtractOptions {
60
+ timeout?: number;
61
+ }
62
+
63
+ /**
64
+ * Options for markdown insertion
65
+ */
66
+ export interface InsertOptions {
67
+ sectionPerPage?: boolean;
68
+ }
69
+
70
+ /**
71
+ * Statistics about PDF comments
72
+ */
73
+ export interface PdfCommentStats {
74
+ total: number;
75
+ byType: Record<string, number>;
76
+ byAuthor: Record<string, number>;
77
+ byPage: Record<number, number>;
78
+ }
79
+
80
+ /**
81
+ * Text item from pdfjs-dist
82
+ */
83
+ interface PdfTextItem {
84
+ str: string;
85
+ x: number;
86
+ y: number;
87
+ width: number;
88
+ height: number;
89
+ }
90
+
25
91
  /**
26
92
  * Extract raw annotations from a PDF file
27
- * @param {string} pdfPath - Path to PDF file
28
- * @param {object} options - { timeout: number (ms) }
29
- * @returns {Promise<Array<{type: string, page: number, contents: string, author: string, date: string, rect: number[], quadPoints: number[]}>>}
93
+ * @param pdfPath - Path to PDF file
94
+ * @param options - { timeout: number (ms) }
95
+ * @returns Array of PDF annotations
30
96
  */
31
- export async function extractPdfAnnotations(pdfPath, options = {}) {
97
+ export async function extractPdfAnnotations(
98
+ pdfPath: string,
99
+ options: ExtractOptions = {}
100
+ ): Promise<PdfAnnotation[]> {
32
101
  const { timeout = 30000 } = options;
33
102
 
34
103
  // Validate file exists
@@ -36,32 +105,34 @@ export async function extractPdfAnnotations(pdfPath, options = {}) {
36
105
  throw new Error(`File not found: ${pdfPath}`);
37
106
  }
38
107
 
39
- let pdfBytes;
108
+ let pdfBytes: Buffer;
40
109
  try {
41
110
  pdfBytes = fs.readFileSync(pdfPath);
42
111
  } catch (err) {
43
- throw new Error(`Cannot read PDF file: ${err.message}`);
112
+ const error = err as Error;
113
+ throw new Error(`Cannot read PDF file: ${error.message}`);
44
114
  }
45
115
 
46
116
  // Create a promise that rejects after timeout
47
- const timeoutPromise = new Promise((_, reject) => {
117
+ const timeoutPromise = new Promise<never>((_, reject) => {
48
118
  setTimeout(() => reject(new Error(`PDF extraction timed out after ${timeout / 1000}s`)), timeout);
49
119
  });
50
120
 
51
- let pdfDoc;
121
+ let pdfDoc: PDFDocument;
52
122
  try {
53
123
  pdfDoc = await Promise.race([
54
124
  PDFDocument.load(pdfBytes, { ignoreEncryption: true }),
55
125
  timeoutPromise,
56
126
  ]);
57
127
  } catch (err) {
58
- if (err.message.includes('timed out')) {
59
- throw err;
128
+ const error = err as Error;
129
+ if (error.message.includes('timed out')) {
130
+ throw error;
60
131
  }
61
- throw new Error(`Invalid or corrupted PDF file: ${err.message}`);
132
+ throw new Error(`Invalid or corrupted PDF file: ${error.message}`);
62
133
  }
63
134
 
64
- const annotations = [];
135
+ const annotations: PdfAnnotation[] = [];
65
136
  const pages = pdfDoc.getPages();
66
137
 
67
138
  for (let pageNum = 0; pageNum < pages.length; pageNum++) {
@@ -74,7 +145,7 @@ export async function extractPdfAnnotations(pdfPath, options = {}) {
74
145
 
75
146
  for (const annotRef of annotRefs) {
76
147
  try {
77
- const annot = annotRef.dict || annotRef;
148
+ const annot = (annotRef as any).dict || annotRef;
78
149
  if (!annot) continue;
79
150
 
80
151
  // Get annotation type
@@ -98,11 +169,11 @@ export async function extractPdfAnnotations(pdfPath, options = {}) {
98
169
 
99
170
  // Extract rectangle (position on page)
100
171
  const rectObj = annot.get(pdfDoc.context.obj('Rect'));
101
- const rect = rectObj?.asArray?.()?.map(n => n?.asNumber?.() || 0) || [0, 0, 0, 0];
172
+ const rect = rectObj?.asArray?.()?.map((n: any) => n?.asNumber?.() || 0) || [0, 0, 0, 0];
102
173
 
103
174
  // Extract QuadPoints for highlights (the actual text bounds)
104
175
  const quadObj = annot.get(pdfDoc.context.obj('QuadPoints'));
105
- const quadPoints = quadObj?.asArray?.()?.map(n => n?.asNumber?.() || 0) || [];
176
+ const quadPoints = quadObj?.asArray?.()?.map((n: any) => n?.asNumber?.() || 0) || [];
106
177
 
107
178
  // Skip empty annotations
108
179
  if (!contents.trim() && subtype !== 'StrikeOut') continue;
@@ -135,10 +206,10 @@ export async function extractPdfAnnotations(pdfPath, options = {}) {
135
206
 
136
207
  /**
137
208
  * Parse PDF date string (D:YYYYMMDDHHmmSS format)
138
- * @param {string} dateStr
139
- * @returns {string} ISO date string
209
+ * @param dateStr - PDF date string
210
+ * @returns ISO date string
140
211
  */
141
- function parsePdfDate(dateStr) {
212
+ function parsePdfDate(dateStr: string): string {
142
213
  if (!dateStr) return '';
143
214
 
144
215
  // Remove D: prefix and timezone info
@@ -156,10 +227,10 @@ function parsePdfDate(dateStr) {
156
227
 
157
228
  /**
158
229
  * Clean PDF string (remove parentheses, decode escape sequences)
159
- * @param {string} str
160
- * @returns {string}
230
+ * @param str - Raw PDF string
231
+ * @returns Cleaned string
161
232
  */
162
- function cleanPdfString(str) {
233
+ function cleanPdfString(str: string): string {
163
234
  if (!str) return '';
164
235
 
165
236
  return str
@@ -176,10 +247,10 @@ function cleanPdfString(str) {
176
247
 
177
248
  /**
178
249
  * Convert PDF annotations to CriticMarkup comments
179
- * @param {Array} annotations - From extractPdfAnnotations
180
- * @returns {Array<{author: string, text: string, page: number, type: string}>}
250
+ * @param annotations - From extractPdfAnnotations
251
+ * @returns Array of PDF comments
181
252
  */
182
- export function annotationsToComments(annotations) {
253
+ export function annotationsToComments(annotations: PdfAnnotation[]): PdfComment[] {
183
254
  return annotations
184
255
  .filter(a => a.contents.trim())
185
256
  .map(a => ({
@@ -193,10 +264,10 @@ export function annotationsToComments(annotations) {
193
264
 
194
265
  /**
195
266
  * Extract comments from PDF and format for display
196
- * @param {string} pdfPath
197
- * @returns {Promise<Array<{author: string, text: string, page: number, type: string, date: string}>>}
267
+ * @param pdfPath - Path to PDF file
268
+ * @returns Array of PDF comments
198
269
  */
199
- export async function extractPdfComments(pdfPath) {
270
+ export async function extractPdfComments(pdfPath: string): Promise<PdfComment[]> {
200
271
  const annotations = await extractPdfAnnotations(pdfPath);
201
272
  return annotationsToComments(annotations);
202
273
  }
@@ -206,32 +277,36 @@ export async function extractPdfComments(pdfPath) {
206
277
  * Since PDFs don't have direct text anchors like Word, we use page numbers
207
278
  * and append comments to the end of corresponding sections
208
279
  *
209
- * @param {string} markdown - The markdown content
210
- * @param {Array} comments - Comments from extractPdfComments
211
- * @param {object} options - { sectionPerPage: boolean }
212
- * @returns {string} Markdown with comments inserted
280
+ * @param markdown - The markdown content
281
+ * @param comments - Comments from extractPdfComments
282
+ * @param options - { sectionPerPage: boolean }
283
+ * @returns Markdown with comments inserted
213
284
  */
214
- export function insertPdfCommentsIntoMarkdown(markdown, comments, options = {}) {
285
+ export function insertPdfCommentsIntoMarkdown(
286
+ markdown: string,
287
+ comments: PdfComment[],
288
+ options: InsertOptions = {}
289
+ ): string {
215
290
  if (comments.length === 0) return markdown;
216
291
 
217
292
  // Group comments by page
218
- const commentsByPage = new Map();
293
+ const commentsByPage = new Map<number, PdfComment[]>();
219
294
  for (const c of comments) {
220
295
  if (!commentsByPage.has(c.page)) {
221
296
  commentsByPage.set(c.page, []);
222
297
  }
223
- commentsByPage.get(c.page).push(c);
298
+ commentsByPage.get(c.page)!.push(c);
224
299
  }
225
300
 
226
301
  // Strategy: Append all comments at the end with page references
227
302
  // This is the safest approach since we can't reliably map PDF positions to markdown
228
303
  const lines = markdown.split('\n');
229
- const commentBlock = [];
304
+ const commentBlock: string[] = [];
230
305
 
231
306
  commentBlock.push('');
232
307
  commentBlock.push('<!-- PDF Comments -->');
233
308
 
234
- for (const [page, pageComments] of commentsByPage) {
309
+ for (const [page, pageComments] of Array.from(commentsByPage.entries())) {
235
310
  for (const c of pageComments) {
236
311
  const authorPrefix = c.author ? `${c.author}: ` : '';
237
312
  const pageRef = `[p.${page}]`;
@@ -244,15 +319,15 @@ export function insertPdfCommentsIntoMarkdown(markdown, comments, options = {})
244
319
 
245
320
  /**
246
321
  * Format PDF comments for CLI display
247
- * @param {Array} comments
248
- * @returns {string}
322
+ * @param comments - Array of PDF comments
323
+ * @returns Formatted string
249
324
  */
250
- export function formatPdfComments(comments) {
325
+ export function formatPdfComments(comments: PdfComment[]): string {
251
326
  if (comments.length === 0) {
252
327
  return 'No comments found in PDF.';
253
328
  }
254
329
 
255
- const lines = [];
330
+ const lines: string[] = [];
256
331
  let currentPage = 0;
257
332
 
258
333
  for (const c of comments) {
@@ -272,10 +347,10 @@ export function formatPdfComments(comments) {
272
347
 
273
348
  /**
274
349
  * Get icon for annotation type
275
- * @param {string} type
276
- * @returns {string}
350
+ * @param type - Annotation type
351
+ * @returns Icon string
277
352
  */
278
- function getTypeIcon(type) {
353
+ function getTypeIcon(type: string): string {
279
354
  switch (type) {
280
355
  case 'Text': return '📝'; // Sticky note
281
356
  case 'FreeText': return '💬'; // Text box
@@ -289,11 +364,11 @@ function getTypeIcon(type) {
289
364
 
290
365
  /**
291
366
  * Get statistics about PDF comments
292
- * @param {Array} comments
293
- * @returns {{total: number, byType: object, byAuthor: object, byPage: object}}
367
+ * @param comments - Array of PDF comments
368
+ * @returns Statistics object
294
369
  */
295
- export function getPdfCommentStats(comments) {
296
- const stats = {
370
+ export function getPdfCommentStats(comments: PdfComment[]): PdfCommentStats {
371
+ const stats: PdfCommentStats = {
297
372
  total: comments.length,
298
373
  byType: {},
299
374
  byAuthor: {},
@@ -311,12 +386,12 @@ export function getPdfCommentStats(comments) {
311
386
 
312
387
  /**
313
388
  * Extract text content from a PDF page
314
- * @param {object} page - pdfjs page object
315
- * @returns {Promise<Array<{str: string, x: number, y: number, width: number, height: number}>>}
389
+ * @param page - pdfjs page object
390
+ * @returns Array of text items with positions
316
391
  */
317
- async function getPageTextItems(page) {
392
+ async function getPageTextItems(page: any): Promise<PdfTextItem[]> {
318
393
  const textContent = await page.getTextContent();
319
- return textContent.items.map(item => ({
394
+ return textContent.items.map((item: any) => ({
320
395
  str: item.str,
321
396
  x: item.transform[4],
322
397
  y: item.transform[5],
@@ -328,12 +403,12 @@ async function getPageTextItems(page) {
328
403
  /**
329
404
  * Check if a point is inside a quadrilateral defined by QuadPoints
330
405
  * QuadPoints format: [x1,y1, x2,y2, x3,y3, x4,y4] for each quad
331
- * @param {number} x
332
- * @param {number} y
333
- * @param {number[]} quad - 8 numbers defining corners
334
- * @returns {boolean}
406
+ * @param x - X coordinate
407
+ * @param y - Y coordinate
408
+ * @param quad - 8 numbers defining corners
409
+ * @returns True if point is inside quad
335
410
  */
336
- function isPointInQuad(x, y, quad) {
411
+ function isPointInQuad(x: number, y: number, quad: number[]): boolean {
337
412
  if (quad.length < 8) return false;
338
413
 
339
414
  // Get bounding box from quad points
@@ -349,11 +424,14 @@ function isPointInQuad(x, y, quad) {
349
424
 
350
425
  /**
351
426
  * Extract highlighted text from a PDF using QuadPoints
352
- * @param {string} pdfPath - Path to PDF file
353
- * @param {Array} annotations - Annotations with quadPoints from extractPdfAnnotations
354
- * @returns {Promise<Array<{...annotation, highlightedText: string}>>}
427
+ * @param pdfPath - Path to PDF file
428
+ * @param annotations - Annotations with quadPoints from extractPdfAnnotations
429
+ * @returns Annotations with highlighted text extracted
355
430
  */
356
- export async function extractHighlightedText(pdfPath, annotations) {
431
+ export async function extractHighlightedText(
432
+ pdfPath: string,
433
+ annotations: PdfAnnotation[]
434
+ ): Promise<PdfAnnotationWithText[]> {
357
435
  const pdfBytes = fs.readFileSync(pdfPath);
358
436
  const data = new Uint8Array(pdfBytes);
359
437
 
@@ -362,7 +440,7 @@ export async function extractHighlightedText(pdfPath, annotations) {
362
440
  const loadingTask = getDocument({ data, useSystemFonts: true });
363
441
  const pdfDoc = await loadingTask.promise;
364
442
 
365
- const results = [];
443
+ const results: PdfAnnotationWithText[] = [];
366
444
 
367
445
  for (const annot of annotations) {
368
446
  // Only process text markup annotations (Highlight, Underline, StrikeOut, Squiggly)
@@ -381,13 +459,13 @@ export async function extractHighlightedText(pdfPath, annotations) {
381
459
  const textItems = await getPageTextItems(page);
382
460
 
383
461
  // Split quadPoints into individual quads (8 numbers each)
384
- const quads = [];
462
+ const quads: number[][] = [];
385
463
  for (let i = 0; i < annot.quadPoints.length; i += 8) {
386
464
  quads.push(annot.quadPoints.slice(i, i + 8));
387
465
  }
388
466
 
389
467
  // Find text items that fall within any of the quads
390
- const matchedText = [];
468
+ const matchedText: string[] = [];
391
469
  for (const item of textItems) {
392
470
  // Check if text item center is in any quad
393
471
  const centerX = item.x + (item.width || 0) / 2;
@@ -416,23 +494,23 @@ export async function extractHighlightedText(pdfPath, annotations) {
416
494
 
417
495
  /**
418
496
  * Extract annotations with highlighted text in one call
419
- * @param {string} pdfPath
420
- * @returns {Promise<Array>}
497
+ * @param pdfPath - Path to PDF file
498
+ * @returns Annotations with highlighted text
421
499
  */
422
- export async function extractPdfAnnotationsWithText(pdfPath) {
500
+ export async function extractPdfAnnotationsWithText(pdfPath: string): Promise<PdfAnnotationWithText[]> {
423
501
  const annotations = await extractPdfAnnotations(pdfPath);
424
502
  return extractHighlightedText(pdfPath, annotations);
425
503
  }
426
504
 
427
505
  /**
428
506
  * Format annotation with highlighted text for display
429
- * @param {object} annot - Annotation with highlightedText
430
- * @returns {string}
507
+ * @param annot - Annotation with highlightedText
508
+ * @returns Formatted string
431
509
  */
432
- export function formatAnnotationWithText(annot) {
510
+ export function formatAnnotationWithText(annot: PdfAnnotationWithText): string {
433
511
  const typeIcon = getTypeIcon(annot.type);
434
512
  const author = annot.author || 'Unknown';
435
- const parts = [`${typeIcon} [${author}]`];
513
+ const parts: string[] = [`${typeIcon} [${author}]`];
436
514
 
437
515
  if (annot.highlightedText) {
438
516
  parts.push(`"${annot.highlightedText}"`);