docrev 0.9.18 → 0.10.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/CHANGELOG.md +15 -0
- package/README.md +25 -0
- package/dist/lib/anchor-match.d.ts +1 -1
- package/dist/lib/anchor-match.d.ts.map +1 -1
- package/dist/lib/anchor-match.js +47 -17
- package/dist/lib/anchor-match.js.map +1 -1
- package/dist/lib/build.d.ts +8 -0
- package/dist/lib/build.d.ts.map +1 -1
- package/dist/lib/build.js +58 -2
- package/dist/lib/build.js.map +1 -1
- package/dist/lib/macro-filter.lua +201 -0
- package/dist/lib/macros.d.ts +102 -0
- package/dist/lib/macros.d.ts.map +1 -0
- package/dist/lib/macros.js +218 -0
- package/dist/lib/macros.js.map +1 -0
- package/dist/lib/pptx-color-filter.lua +37 -0
- package/dist/lib/schema.d.ts.map +1 -1
- package/dist/lib/schema.js +34 -0
- package/dist/lib/schema.js.map +1 -1
- package/docs-src/build.py +113 -113
- package/docs-src/extra.css +208 -208
- package/docs-src/md-to-html.lua +6 -6
- package/docs-src/template.html +116 -116
- package/lib/anchor-match.ts +49 -17
- package/lib/build.ts +74 -2
- package/lib/macro-filter.lua +201 -0
- package/lib/macros.ts +273 -0
- package/lib/schema.ts +34 -0
- package/mkdocs.yml +64 -64
- package/package.json +1 -1
- package/scripts/postbuild.js +21 -2
- package/issues.md +0 -180
- package/site/assets/extra.css +0 -208
- package/site/commands.html +0 -926
- package/site/configuration.html +0 -469
- package/site/index.html +0 -288
- package/site/troubleshooting.html +0 -461
- package/site/workflow.html +0 -518
package/docs-src/template.html
CHANGED
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en" data-bs-theme="light">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>$if(pagetitle)$$pagetitle$ — $endif$docrev</title>
|
|
7
|
-
<meta name="description" content="CLI for writing documents in Markdown while collaborating with Word users.">
|
|
8
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.3/dist/sandstone/bootstrap.min.css">
|
|
9
|
-
$if(highlighting-css)$
|
|
10
|
-
<style>
|
|
11
|
-
$highlighting-css$
|
|
12
|
-
</style>
|
|
13
|
-
$endif$
|
|
14
|
-
<link rel="stylesheet" href="assets/extra.css">
|
|
15
|
-
</head>
|
|
16
|
-
<body>
|
|
17
|
-
|
|
18
|
-
<nav class="navbar navbar-expand-lg fixed-top" aria-label="Site navigation">
|
|
19
|
-
<div class="container">
|
|
20
|
-
<a class="navbar-brand me-2" href="index.html">docrev</a>
|
|
21
|
-
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-main" aria-controls="navbar-main" aria-expanded="false" aria-label="Toggle navigation">
|
|
22
|
-
<span class="navbar-toggler-icon"></span>
|
|
23
|
-
</button>
|
|
24
|
-
<div class="collapse navbar-collapse" id="navbar-main">
|
|
25
|
-
<ul class="navbar-nav me-auto">
|
|
26
|
-
<li class="nav-item">
|
|
27
|
-
<a class="nav-link$if(active-home)$ active$endif$" $if(active-home)$aria-current="page"$endif$ href="index.html">Home</a>
|
|
28
|
-
</li>
|
|
29
|
-
<li class="nav-item">
|
|
30
|
-
<a class="nav-link$if(active-workflow)$ active$endif$" $if(active-workflow)$aria-current="page"$endif$ href="workflow.html">Get Started</a>
|
|
31
|
-
</li>
|
|
32
|
-
<li class="nav-item">
|
|
33
|
-
<a class="nav-link$if(active-commands)$ active$endif$" $if(active-commands)$aria-current="page"$endif$ href="commands.html">Commands</a>
|
|
34
|
-
</li>
|
|
35
|
-
<li class="nav-item">
|
|
36
|
-
<a class="nav-link$if(active-configuration)$ active$endif$" $if(active-configuration)$aria-current="page"$endif$ href="configuration.html">Configuration</a>
|
|
37
|
-
</li>
|
|
38
|
-
<li class="nav-item">
|
|
39
|
-
<a class="nav-link$if(active-troubleshooting)$ active$endif$" $if(active-troubleshooting)$aria-current="page"$endif$ href="troubleshooting.html">Troubleshooting</a>
|
|
40
|
-
</li>
|
|
41
|
-
</ul>
|
|
42
|
-
<ul class="navbar-nav">
|
|
43
|
-
<li class="nav-item">
|
|
44
|
-
<button id="theme-toggle" class="btn btn-link nav-link border-0" aria-label="Toggle dark mode">
|
|
45
|
-
<svg id="icon-sun" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
46
|
-
<path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6m0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8M8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0m0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13m8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5M3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8m10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0m-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0m9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707M4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707"/>
|
|
47
|
-
</svg>
|
|
48
|
-
<svg id="icon-moon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="display:none">
|
|
49
|
-
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278"/>
|
|
50
|
-
</svg>
|
|
51
|
-
</button>
|
|
52
|
-
</li>
|
|
53
|
-
<li class="nav-item">
|
|
54
|
-
<a class="nav-link" href="https://github.com/gcol33/docrev" aria-label="GitHub">
|
|
55
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16">
|
|
56
|
-
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
|
|
57
|
-
</svg>
|
|
58
|
-
</a>
|
|
59
|
-
</li>
|
|
60
|
-
<li class="nav-item">
|
|
61
|
-
<a class="nav-link" href="https://www.npmjs.com/package/docrev" aria-label="npm">
|
|
62
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
|
|
63
|
-
<path d="M0 0v16h16V0zm5.333 13.333H2.667V5.333h2.666v5.334H8V5.333h2.667v8H5.333z"/>
|
|
64
|
-
</svg>
|
|
65
|
-
</a>
|
|
66
|
-
</li>
|
|
67
|
-
</ul>
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
</nav>
|
|
71
|
-
|
|
72
|
-
<div class="container template-$if(active-home)$home$else$article$endif$">
|
|
73
|
-
<div class="row">
|
|
74
|
-
<main id="main" class="$if(toc)$col-md-9$else$col-12$endif$">
|
|
75
|
-
$body$
|
|
76
|
-
</main>
|
|
77
|
-
$if(toc)$
|
|
78
|
-
<aside class="col-md-3 d-none d-md-block">
|
|
79
|
-
<nav id="toc" data-toggle="toc" aria-label="On this page">
|
|
80
|
-
<h2 class="h6">On this page</h2>
|
|
81
|
-
<div class="toc">$toc$</div>
|
|
82
|
-
</nav>
|
|
83
|
-
</aside>
|
|
84
|
-
$endif$
|
|
85
|
-
</div>
|
|
86
|
-
</div>
|
|
87
|
-
|
|
88
|
-
<footer class="border-top py-3 mt-auto">
|
|
89
|
-
<div class="container d-flex justify-content-between align-items-center text-muted" style="font-size:.85rem">
|
|
90
|
-
<span>docrev$if(version)$ $version$$endif$</span>
|
|
91
|
-
<span>Built with <a href="https://pandoc.org" class="text-muted">pandoc</a></span>
|
|
92
|
-
</div>
|
|
93
|
-
</footer>
|
|
94
|
-
|
|
95
|
-
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
96
|
-
<script>
|
|
97
|
-
(function () {
|
|
98
|
-
var html = document.documentElement;
|
|
99
|
-
var sun = document.getElementById('icon-sun');
|
|
100
|
-
var moon = document.getElementById('icon-moon');
|
|
101
|
-
function apply(t) {
|
|
102
|
-
html.setAttribute('data-bs-theme', t);
|
|
103
|
-
sun.style.display = t === 'dark' ? 'none' : '';
|
|
104
|
-
moon.style.display = t === 'dark' ? '' : 'none';
|
|
105
|
-
}
|
|
106
|
-
apply(localStorage.getItem('docrev-theme') || 'light');
|
|
107
|
-
document.getElementById('theme-toggle').addEventListener('click', function () {
|
|
108
|
-
var next = html.getAttribute('data-bs-theme') === 'dark' ? 'light' : 'dark';
|
|
109
|
-
localStorage.setItem('docrev-theme', next);
|
|
110
|
-
apply(next);
|
|
111
|
-
});
|
|
112
|
-
})();
|
|
113
|
-
</script>
|
|
114
|
-
|
|
115
|
-
</body>
|
|
116
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-bs-theme="light">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>$if(pagetitle)$$pagetitle$ — $endif$docrev</title>
|
|
7
|
+
<meta name="description" content="CLI for writing documents in Markdown while collaborating with Word users.">
|
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.3.3/dist/sandstone/bootstrap.min.css">
|
|
9
|
+
$if(highlighting-css)$
|
|
10
|
+
<style>
|
|
11
|
+
$highlighting-css$
|
|
12
|
+
</style>
|
|
13
|
+
$endif$
|
|
14
|
+
<link rel="stylesheet" href="assets/extra.css">
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
|
|
18
|
+
<nav class="navbar navbar-expand-lg fixed-top" aria-label="Site navigation">
|
|
19
|
+
<div class="container">
|
|
20
|
+
<a class="navbar-brand me-2" href="index.html">docrev</a>
|
|
21
|
+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-main" aria-controls="navbar-main" aria-expanded="false" aria-label="Toggle navigation">
|
|
22
|
+
<span class="navbar-toggler-icon"></span>
|
|
23
|
+
</button>
|
|
24
|
+
<div class="collapse navbar-collapse" id="navbar-main">
|
|
25
|
+
<ul class="navbar-nav me-auto">
|
|
26
|
+
<li class="nav-item">
|
|
27
|
+
<a class="nav-link$if(active-home)$ active$endif$" $if(active-home)$aria-current="page"$endif$ href="index.html">Home</a>
|
|
28
|
+
</li>
|
|
29
|
+
<li class="nav-item">
|
|
30
|
+
<a class="nav-link$if(active-workflow)$ active$endif$" $if(active-workflow)$aria-current="page"$endif$ href="workflow.html">Get Started</a>
|
|
31
|
+
</li>
|
|
32
|
+
<li class="nav-item">
|
|
33
|
+
<a class="nav-link$if(active-commands)$ active$endif$" $if(active-commands)$aria-current="page"$endif$ href="commands.html">Commands</a>
|
|
34
|
+
</li>
|
|
35
|
+
<li class="nav-item">
|
|
36
|
+
<a class="nav-link$if(active-configuration)$ active$endif$" $if(active-configuration)$aria-current="page"$endif$ href="configuration.html">Configuration</a>
|
|
37
|
+
</li>
|
|
38
|
+
<li class="nav-item">
|
|
39
|
+
<a class="nav-link$if(active-troubleshooting)$ active$endif$" $if(active-troubleshooting)$aria-current="page"$endif$ href="troubleshooting.html">Troubleshooting</a>
|
|
40
|
+
</li>
|
|
41
|
+
</ul>
|
|
42
|
+
<ul class="navbar-nav">
|
|
43
|
+
<li class="nav-item">
|
|
44
|
+
<button id="theme-toggle" class="btn btn-link nav-link border-0" aria-label="Toggle dark mode">
|
|
45
|
+
<svg id="icon-sun" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
46
|
+
<path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6m0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8M8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0m0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13m8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5M3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8m10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0m-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0m9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707M4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707"/>
|
|
47
|
+
</svg>
|
|
48
|
+
<svg id="icon-moon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16" style="display:none">
|
|
49
|
+
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278"/>
|
|
50
|
+
</svg>
|
|
51
|
+
</button>
|
|
52
|
+
</li>
|
|
53
|
+
<li class="nav-item">
|
|
54
|
+
<a class="nav-link" href="https://github.com/gcol33/docrev" aria-label="GitHub">
|
|
55
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 16 16">
|
|
56
|
+
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
|
|
57
|
+
</svg>
|
|
58
|
+
</a>
|
|
59
|
+
</li>
|
|
60
|
+
<li class="nav-item">
|
|
61
|
+
<a class="nav-link" href="https://www.npmjs.com/package/docrev" aria-label="npm">
|
|
62
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
|
|
63
|
+
<path d="M0 0v16h16V0zm5.333 13.333H2.667V5.333h2.666v5.334H8V5.333h2.667v8H5.333z"/>
|
|
64
|
+
</svg>
|
|
65
|
+
</a>
|
|
66
|
+
</li>
|
|
67
|
+
</ul>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</nav>
|
|
71
|
+
|
|
72
|
+
<div class="container template-$if(active-home)$home$else$article$endif$">
|
|
73
|
+
<div class="row">
|
|
74
|
+
<main id="main" class="$if(toc)$col-md-9$else$col-12$endif$">
|
|
75
|
+
$body$
|
|
76
|
+
</main>
|
|
77
|
+
$if(toc)$
|
|
78
|
+
<aside class="col-md-3 d-none d-md-block">
|
|
79
|
+
<nav id="toc" data-toggle="toc" aria-label="On this page">
|
|
80
|
+
<h2 class="h6">On this page</h2>
|
|
81
|
+
<div class="toc">$toc$</div>
|
|
82
|
+
</nav>
|
|
83
|
+
</aside>
|
|
84
|
+
$endif$
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<footer class="border-top py-3 mt-auto">
|
|
89
|
+
<div class="container d-flex justify-content-between align-items-center text-muted" style="font-size:.85rem">
|
|
90
|
+
<span>docrev$if(version)$ $version$$endif$</span>
|
|
91
|
+
<span>Built with <a href="https://pandoc.org" class="text-muted">pandoc</a></span>
|
|
92
|
+
</div>
|
|
93
|
+
</footer>
|
|
94
|
+
|
|
95
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
96
|
+
<script>
|
|
97
|
+
(function () {
|
|
98
|
+
var html = document.documentElement;
|
|
99
|
+
var sun = document.getElementById('icon-sun');
|
|
100
|
+
var moon = document.getElementById('icon-moon');
|
|
101
|
+
function apply(t) {
|
|
102
|
+
html.setAttribute('data-bs-theme', t);
|
|
103
|
+
sun.style.display = t === 'dark' ? 'none' : '';
|
|
104
|
+
moon.style.display = t === 'dark' ? '' : 'none';
|
|
105
|
+
}
|
|
106
|
+
apply(localStorage.getItem('docrev-theme') || 'light');
|
|
107
|
+
document.getElementById('theme-toggle').addEventListener('click', function () {
|
|
108
|
+
var next = html.getAttribute('data-bs-theme') === 'dark' ? 'light' : 'dark';
|
|
109
|
+
localStorage.setItem('docrev-theme', next);
|
|
110
|
+
apply(next);
|
|
111
|
+
});
|
|
112
|
+
})();
|
|
113
|
+
</script>
|
|
114
|
+
|
|
115
|
+
</body>
|
|
116
|
+
</html>
|
package/lib/anchor-match.ts
CHANGED
|
@@ -11,6 +11,8 @@ export type AnchorStrategy =
|
|
|
11
11
|
| 'stripped'
|
|
12
12
|
| 'partial-start'
|
|
13
13
|
| 'partial-start-stripped'
|
|
14
|
+
| 'partial-window'
|
|
15
|
+
| 'partial-window-stripped'
|
|
14
16
|
| 'context-both'
|
|
15
17
|
| 'context-before'
|
|
16
18
|
| 'context-after'
|
|
@@ -171,41 +173,69 @@ export function findAnchorInText(
|
|
|
171
173
|
return { occurrences, matchedAnchor: anchor, strategy: 'stripped', stripped: true };
|
|
172
174
|
}
|
|
173
175
|
|
|
174
|
-
// Strategy 4:
|
|
176
|
+
// Strategy 4: word window from anchor (prefix or interior).
|
|
177
|
+
// Sliding the window across the anchor catches the case where the
|
|
178
|
+
// anchor's prefix has been edited but a chunk in the middle/end
|
|
179
|
+
// survived intact (e.g. "Sensitivity analyses were performed by
|
|
180
|
+
// perturbing the prior variance" → drifted "Sensitivity analyses
|
|
181
|
+
// perturbed the prior variance" still contains "the prior variance").
|
|
175
182
|
const words = anchor.split(/\s+/);
|
|
176
183
|
if (words.length > 3) {
|
|
177
184
|
for (let n = Math.min(6, words.length); n >= 3; n--) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
|
|
185
|
+
for (let start = 0; start + n <= words.length; start++) {
|
|
186
|
+
const window = words.slice(start, start + n).join(' ');
|
|
187
|
+
const windowLower = window.toLowerCase();
|
|
188
|
+
if (windowLower.length < 15) continue;
|
|
189
|
+
|
|
190
|
+
let occ = findAllOccurrences(textLower, windowLower);
|
|
191
|
+
if (occ.length > 0) {
|
|
192
|
+
const strategy: AnchorStrategy = start === 0 ? 'partial-start' : 'partial-window';
|
|
193
|
+
return { occurrences: occ, matchedAnchor: window, strategy };
|
|
183
194
|
}
|
|
184
|
-
|
|
185
|
-
if (
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
matchedAnchor: words.slice(0, n).join(' '),
|
|
189
|
-
strategy: 'partial-start-stripped',
|
|
190
|
-
stripped: true,
|
|
191
|
-
};
|
|
195
|
+
occ = findAllOccurrences(strippedLower, windowLower);
|
|
196
|
+
if (occ.length > 0) {
|
|
197
|
+
const strategy: AnchorStrategy = start === 0 ? 'partial-start-stripped' : 'partial-window-stripped';
|
|
198
|
+
return { occurrences: occ, matchedAnchor: window, strategy, stripped: true };
|
|
192
199
|
}
|
|
193
200
|
}
|
|
194
201
|
}
|
|
195
202
|
}
|
|
196
203
|
|
|
197
|
-
// Strategy 5: context (before/after) only
|
|
204
|
+
// Strategy 5: context (before/after) only.
|
|
205
|
+
//
|
|
206
|
+
// For a non-empty anchor that already failed every text-based strategy
|
|
207
|
+
// above, we treat context as a degraded placement: classify it
|
|
208
|
+
// 'context-only' so callers can warn the user. We also reject
|
|
209
|
+
// implausible brackets — if both contexts match but the gap between
|
|
210
|
+
// them is far too small to contain the anchor (e.g. the anchored
|
|
211
|
+
// sentence was deleted), do not silently land the comment between
|
|
212
|
+
// the surviving sentences. Return 'failed' so the user is told to
|
|
213
|
+
// place it manually.
|
|
198
214
|
if (before || after) {
|
|
199
215
|
const beforeLower = before.toLowerCase();
|
|
200
216
|
const afterLower = after.toLowerCase();
|
|
217
|
+
const anchorLen = anchor.length;
|
|
201
218
|
|
|
202
219
|
if (before && after) {
|
|
203
220
|
const beforeIdx = textLower.indexOf(beforeLower.slice(-50));
|
|
204
221
|
if (beforeIdx !== -1) {
|
|
205
222
|
const searchStart = beforeIdx + beforeLower.slice(-50).length;
|
|
206
223
|
const afterIdx = textLower.indexOf(afterLower.slice(0, 50), searchStart);
|
|
207
|
-
if (afterIdx !== -1
|
|
208
|
-
|
|
224
|
+
if (afterIdx !== -1) {
|
|
225
|
+
const gap = afterIdx - searchStart;
|
|
226
|
+
// Require the bracket to plausibly contain a remnant of the anchor.
|
|
227
|
+
// Below 30% of anchor length: anchor was deleted — refuse to place.
|
|
228
|
+
// Above 2× anchor length + slack: brackets are too far apart, the
|
|
229
|
+
// matcher has latched onto unrelated repeats of common context.
|
|
230
|
+
const minGap = Math.floor(anchorLen * 0.3);
|
|
231
|
+
const maxGap = Math.min(500, anchorLen * 2 + 50);
|
|
232
|
+
if (gap >= minGap && gap <= maxGap) {
|
|
233
|
+
return { occurrences: [searchStart], matchedAnchor: null, strategy: 'context-both' };
|
|
234
|
+
}
|
|
235
|
+
// Both brackets found but gap implausible: anchor likely deleted.
|
|
236
|
+
// Don't fall back to single-side context — that would silently
|
|
237
|
+
// place the comment in the wrong location.
|
|
238
|
+
return { occurrences: [], matchedAnchor: null, strategy: 'failed' };
|
|
209
239
|
}
|
|
210
240
|
}
|
|
211
241
|
}
|
|
@@ -262,6 +292,8 @@ export function classifyStrategy(strategy: AnchorStrategy, occurrences: number):
|
|
|
262
292
|
case 'stripped':
|
|
263
293
|
case 'partial-start':
|
|
264
294
|
case 'partial-start-stripped':
|
|
295
|
+
case 'partial-window':
|
|
296
|
+
case 'partial-window-stripped':
|
|
265
297
|
case 'split-match':
|
|
266
298
|
return 'drift';
|
|
267
299
|
case 'context-both':
|
package/lib/build.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import * as fs from 'fs';
|
|
12
12
|
import * as path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
13
14
|
import { execSync, spawn, ChildProcess } from 'child_process';
|
|
14
15
|
import YAML from 'yaml';
|
|
15
16
|
import { stripAnnotations } from './annotations.js';
|
|
@@ -24,6 +25,13 @@ import { buildImageRegistry, writeImageRegistry } from './image-registry.js';
|
|
|
24
25
|
import type { Author, JournalFormatting } from './types.js';
|
|
25
26
|
import { getJournalProfile } from './journals.js';
|
|
26
27
|
import { resolveCSL } from './csl.js';
|
|
28
|
+
import {
|
|
29
|
+
type MacroDef,
|
|
30
|
+
mergeMacros,
|
|
31
|
+
generateLatexPreamble,
|
|
32
|
+
writeMacrosSidecar,
|
|
33
|
+
getMacroFilterPath,
|
|
34
|
+
} from './macros.js';
|
|
27
35
|
|
|
28
36
|
// =============================================================================
|
|
29
37
|
// Constants
|
|
@@ -155,6 +163,13 @@ export interface BuildConfig {
|
|
|
155
163
|
pptx: PptxConfig;
|
|
156
164
|
tables: TablesConfig;
|
|
157
165
|
postprocess: PostprocessConfig;
|
|
166
|
+
/**
|
|
167
|
+
* User-declared placeholder macros. Merged with the built-in macros
|
|
168
|
+
* (currently \tofill). Each entry overrides a built-in by name.
|
|
169
|
+
*
|
|
170
|
+
* See lib/macros.ts for the per-format rendering rules.
|
|
171
|
+
*/
|
|
172
|
+
macros?: MacroDef[];
|
|
158
173
|
/**
|
|
159
174
|
* Directory (relative to the project) where final outputs land. Created on
|
|
160
175
|
* demand. Set to null/empty to keep outputs alongside paper.md (legacy
|
|
@@ -313,6 +328,9 @@ export const DEFAULT_CONFIG: BuildConfig = {
|
|
|
313
328
|
beamer: null,
|
|
314
329
|
all: null, // Runs after any format
|
|
315
330
|
},
|
|
331
|
+
// Placeholder/highlight macros. Defaults are the built-ins from
|
|
332
|
+
// lib/macros.ts; users append their own here.
|
|
333
|
+
macros: [],
|
|
316
334
|
// Final outputs land here (created on demand). Set to null or '' to keep
|
|
317
335
|
// outputs in the project root.
|
|
318
336
|
outputDir: 'output',
|
|
@@ -1503,13 +1521,50 @@ export async function runPandoc(
|
|
|
1503
1521
|
args.push('--reference-doc', referenceDoc);
|
|
1504
1522
|
}
|
|
1505
1523
|
|
|
1506
|
-
// Add color filter for PPTX (handles [text]{color=#RRGGBB} syntax)
|
|
1507
|
-
|
|
1524
|
+
// Add color filter for PPTX (handles [text]{color=#RRGGBB} syntax).
|
|
1525
|
+
// fileURLToPath handles Windows paths with spaces — the old
|
|
1526
|
+
// `new URL(...).pathname` returned URL-encoded `%20` and fs.existsSync
|
|
1527
|
+
// silently failed.
|
|
1528
|
+
const colorFilterPath = path.join(
|
|
1529
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
1530
|
+
'pptx-color-filter.lua'
|
|
1531
|
+
);
|
|
1508
1532
|
if (fs.existsSync(colorFilterPath)) {
|
|
1509
1533
|
args.push('--lua-filter', colorFilterPath);
|
|
1510
1534
|
}
|
|
1511
1535
|
}
|
|
1512
1536
|
|
|
1537
|
+
// Wire placeholder macros (built-in \tofill plus user-declared entries).
|
|
1538
|
+
// - docx/html: lua filter expands \name{X} to format-specific raw runs.
|
|
1539
|
+
// - pdf/tex/beamer: inject a \providecommand preamble so LaTeX renders it
|
|
1540
|
+
// directly. `\providecommand` is non-clobbering, so a user who already
|
|
1541
|
+
// has `\providecommand{\tofill}{...}` in their own header keeps theirs.
|
|
1542
|
+
//
|
|
1543
|
+
// Sidecar path is passed to the lua filter via DOCREV_MACROS_FILE in the
|
|
1544
|
+
// child env (not pandoc metadata) because pandoc walks RawInline/RawBlock
|
|
1545
|
+
// BEFORE Meta — by the time a Meta handler could read the path, the inline
|
|
1546
|
+
// expansion has already happened.
|
|
1547
|
+
const macroTempFiles: string[] = [];
|
|
1548
|
+
let macroEnvFile: string | null = null;
|
|
1549
|
+
const macros = mergeMacros((config as { macros?: unknown }).macros);
|
|
1550
|
+
if (macros.length > 0) {
|
|
1551
|
+
if (format === 'docx' || format === 'html' || format === 'html5' || format === 'html4') {
|
|
1552
|
+
const sidecarPath = writeMacrosSidecar(directory, macros);
|
|
1553
|
+
macroTempFiles.push(sidecarPath);
|
|
1554
|
+
macroEnvFile = sidecarPath;
|
|
1555
|
+
const filterPath = getMacroFilterPath();
|
|
1556
|
+
if (fs.existsSync(filterPath)) {
|
|
1557
|
+
args.push('--lua-filter', filterPath);
|
|
1558
|
+
}
|
|
1559
|
+
} else if (format === 'pdf' || format === 'tex' || format === 'beamer') {
|
|
1560
|
+
const preamble = generateLatexPreamble(macros);
|
|
1561
|
+
const preamblePath = path.join(directory, '.macros.tex');
|
|
1562
|
+
fs.writeFileSync(preamblePath, preamble, 'utf-8');
|
|
1563
|
+
macroTempFiles.push(preamblePath);
|
|
1564
|
+
args.push('-H', path.basename(preamblePath));
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1513
1568
|
// Add crossref metadata file if exists (skip for slides - they don't use crossref)
|
|
1514
1569
|
if (format !== 'beamer' && format !== 'pptx') {
|
|
1515
1570
|
const crossrefPath = path.join(directory, 'crossref.yaml');
|
|
@@ -1532,9 +1587,14 @@ export async function runPandoc(
|
|
|
1532
1587
|
}
|
|
1533
1588
|
|
|
1534
1589
|
return new Promise((resolve) => {
|
|
1590
|
+
const pandocEnv: NodeJS.ProcessEnv = { ...process.env };
|
|
1591
|
+
if (macroEnvFile) {
|
|
1592
|
+
pandocEnv.DOCREV_MACROS_FILE = macroEnvFile;
|
|
1593
|
+
}
|
|
1535
1594
|
const pandoc: ChildProcess = spawn('pandoc', args, {
|
|
1536
1595
|
cwd: directory,
|
|
1537
1596
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1597
|
+
env: pandocEnv,
|
|
1538
1598
|
});
|
|
1539
1599
|
|
|
1540
1600
|
let stderr = '';
|
|
@@ -1542,7 +1602,18 @@ export async function runPandoc(
|
|
|
1542
1602
|
stderr += data.toString();
|
|
1543
1603
|
});
|
|
1544
1604
|
|
|
1605
|
+
const cleanupMacroTempFiles = (): void => {
|
|
1606
|
+
for (const tmp of macroTempFiles) {
|
|
1607
|
+
try {
|
|
1608
|
+
fs.unlinkSync(tmp);
|
|
1609
|
+
} catch {
|
|
1610
|
+
// ignore — best-effort cleanup
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
};
|
|
1614
|
+
|
|
1545
1615
|
pandoc.on('close', async (code) => {
|
|
1616
|
+
cleanupMacroTempFiles();
|
|
1546
1617
|
if (code === 0) {
|
|
1547
1618
|
// For PPTX, post-process to add slide numbers, buildup colors, and logos
|
|
1548
1619
|
if (format === 'pptx') {
|
|
@@ -1592,6 +1663,7 @@ export async function runPandoc(
|
|
|
1592
1663
|
});
|
|
1593
1664
|
|
|
1594
1665
|
pandoc.on('error', (err) => {
|
|
1666
|
+
cleanupMacroTempFiles();
|
|
1595
1667
|
resolve({ outputPath, success: false, error: err.message });
|
|
1596
1668
|
});
|
|
1597
1669
|
});
|