gutterpress 0.9.0-alpha.2 → 0.10.0-alpha.4

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 (138) hide show
  1. package/README.md +18 -4
  2. package/dist/api/index.d.ts +11 -5
  3. package/dist/api/index.js +20 -15
  4. package/dist/assets/preview/scripts/preview-bridge.d.ts +1 -0
  5. package/dist/assets/preview/scripts/preview-interface.d.ts +1 -0
  6. package/dist/{audit-nhn2pjz3.js → audit-k1vnfwvc.js} +10 -7
  7. package/dist/{build-san7fv2z.js → build-hqmwgvdw.js} +14 -8
  8. package/dist/checks/source/index.d.ts +1 -0
  9. package/dist/checks/source/layout-markers.d.ts +3 -0
  10. package/dist/checks/source/local-ref-parser.d.ts +28 -0
  11. package/dist/{cli-e5zhb0xs.js → cli-0r0tq16s.js} +17 -24
  12. package/dist/cli-46ycxe6r.js +18 -0
  13. package/dist/cli-c41yr7he.js +241 -0
  14. package/dist/cli-hp9r2pzt.js +2343 -0
  15. package/dist/{index-yzrh708h.js → cli-k1065rkg.js} +2 -5
  16. package/dist/{cli-k4bd06sd.js → cli-n25qycwz.js} +4986 -5889
  17. package/dist/{cli-najycadg.js → cli-ra0ed2xt.js} +69 -253
  18. package/dist/{cli-506tg37g.js → cli-revgt4pr.js} +2 -1
  19. package/dist/{cli-wchtvxvw.js → cli-v5mp7a6q.js} +14 -1
  20. package/dist/cli.js +20 -18
  21. package/dist/{doctor-hrk0kxxz.js → doctor-dxms7ehm.js} +4 -2
  22. package/dist/engine/compiler/build.d.ts +139 -0
  23. package/dist/engine/compiler/postprocess.d.ts +23 -0
  24. package/dist/engine/compiler/tier2.d.ts +78 -0
  25. package/dist/engine/shared/cdp.d.ts +104 -0
  26. package/dist/engine/shared/content-value.d.ts +64 -0
  27. package/dist/engine/shared/gcpm-extract.d.ts +119 -0
  28. package/dist/engine/shared/margin-box-support.d.ts +12 -0
  29. package/dist/engine/shared/pdf-inspect.d.ts +24 -0
  30. package/dist/engine/shared/synthesis.d.ts +155 -0
  31. package/dist/engine-wa7y9av9.js +41 -0
  32. package/dist/engine-z4p9sr4h.js +40 -0
  33. package/dist/gutterpress-agent-1ctgfz92.js +576 -0
  34. package/dist/gutterpress-viewer-cem7dmr5.js +2349 -0
  35. package/dist/{index-bynn850m.js → index-05y3dnxq.js} +3049 -4015
  36. package/dist/index-9tyq9kks.js +708 -0
  37. package/dist/{cli-yzrh708h.js → index-mdefp0y5.js} +1 -1
  38. package/dist/{index-wchtvxvw.js → index-v5mp7a6q.js} +14 -1
  39. package/dist/index-xxg4zfrg.js +1907 -0
  40. package/dist/index.d.ts +3 -1
  41. package/dist/index.js +24 -17
  42. package/dist/lib/asset-inline.d.ts +36 -0
  43. package/dist/lib/browser-pool.d.ts +18 -0
  44. package/dist/lib/build-error.d.ts +1 -1
  45. package/dist/lib/build-preflight.d.ts +35 -2
  46. package/dist/lib/build-runner.d.ts +45 -17
  47. package/dist/lib/build-staging.d.ts +9 -55
  48. package/dist/lib/cli-args.d.ts +8 -0
  49. package/dist/lib/desktop.d.ts +2 -2
  50. package/dist/lib/embedded-assets.d.ts +1 -1
  51. package/dist/lib/engine.d.ts +33 -0
  52. package/dist/lib/ghostscript.d.ts +46 -1
  53. package/dist/lib/markdown/assemble.d.ts +18 -10
  54. package/dist/lib/markdown/gp-pin-scope.d.ts +1 -0
  55. package/dist/lib/markdown/gutterpress-css.d.ts +125 -0
  56. package/dist/lib/markdown/images.d.ts +26 -0
  57. package/dist/lib/markdown/index.d.ts +4 -2
  58. package/dist/lib/markdown/inline-source.d.ts +10 -0
  59. package/dist/lib/markdown/markers.d.ts +32 -0
  60. package/dist/lib/markdown/renderer.d.ts +8 -3
  61. package/dist/lib/markdown/source-range.d.ts +61 -0
  62. package/dist/lib/missing-asset-placeholder.d.ts +52 -0
  63. package/dist/lib/presets.d.ts +1 -1
  64. package/dist/lib/printsafe.d.ts +2 -3
  65. package/dist/lib/remote-auth/converge-merge.d.ts +48 -0
  66. package/dist/lib/remote-auth/image-clash.d.ts +17 -0
  67. package/dist/lib/remote-auth/recovery/classify.d.ts +55 -67
  68. package/dist/lib/remote-auth/recovery/inspect.d.ts +14 -13
  69. package/dist/lib/remote-auth/recovery/locks.d.ts +20 -0
  70. package/dist/lib/remote-auth/recovery/repair.d.ts +29 -0
  71. package/dist/lib/remote-auth/recovery/types.d.ts +8 -204
  72. package/dist/lib/remote-auth/sync-messages.d.ts +2 -3
  73. package/dist/lib/remote-auth/sync-types.d.ts +35 -63
  74. package/dist/lib/remote-auth/sync.d.ts +11 -18
  75. package/dist/lib/remote-auth/transport.d.ts +14 -7
  76. package/dist/lib/theme-import.d.ts +4 -5
  77. package/dist/{lint-96j9hrj4.js → lint-xjwm5ep8.js} +10 -7
  78. package/dist/{manifest.schema-z61rzw44.json → manifest.schema-zxgxnbg7.json} +21 -0
  79. package/dist/{new-8p38wavc.js → new-kwdwpf0j.js} +12 -8
  80. package/dist/{plugin-ees6nhkc.js → plugin-rg4tnn96.js} +10 -7
  81. package/dist/{preflight-1q6c2edh.js → preflight-3127y25z.js} +10 -7
  82. package/dist/preview/file-watcher.d.ts +13 -17
  83. package/dist/preview/lifecycle.d.ts +1 -1
  84. package/dist/{pagedjs-bridge-vn4hk9fx.js → preview-bridge-fz7vpk8m.js} +8 -0
  85. package/dist/preview-interface-435cczt5.js +1059 -0
  86. package/dist/{preview-y5a2zen1.js → preview-ncgfhqmw.js} +16 -9
  87. package/dist/preview-shell-c5mfa3q0.js +346 -0
  88. package/dist/{project-source-p0gn1wd5.js → project-source-ekcyp63q.js} +1 -1
  89. package/dist/{publish-rm9yb3wh.js → publish-pr0rwh6p.js} +10 -7
  90. package/dist/render.d.ts +3 -4
  91. package/dist/render.js +728 -64
  92. package/dist/{repair-zgq7q2g6.js → repair-8270smfw.js} +45 -79
  93. package/dist/schema/manifest.types.d.ts +38 -0
  94. package/dist/{source-provider-c1rjm2c0.js → source-provider-3tcj6qg2.js} +2 -2
  95. package/dist/source-provider-vanafrt9.js +40 -0
  96. package/dist/{theme-zz2ktzqs.css → theme-h5recz6c.css} +8 -7
  97. package/dist/{theme-570zmh2t.css → theme-j2bagrfx.css} +8 -7
  98. package/dist/{theme-nya4nqh6.css → theme-nn6d53zy.css} +8 -7
  99. package/dist/types.d.ts +7 -0
  100. package/dist/{validate-nr0xa6sa.js → validate-54e17rae.js} +10 -7
  101. package/package.json +6 -6
  102. package/dist/cli-yja077f6.js +0 -92
  103. package/dist/git-http-yrb4ag6z.js +0 -17
  104. package/dist/index-yja077f6.js +0 -92
  105. package/dist/lib/markdown/markdown-it-paged.d.ts +0 -30
  106. package/dist/lib/pagedjs-marker.d.ts +0 -42
  107. package/dist/lib/pagedjs.d.ts +0 -26
  108. package/dist/lib/pagination.d.ts +0 -149
  109. package/dist/lib/remote-auth/conflict-resolution.d.ts +0 -29
  110. package/dist/lib/remote-auth/recovery/abort-interrupted-operation.d.ts +0 -103
  111. package/dist/lib/remote-auth/recovery/backup.d.ts +0 -113
  112. package/dist/lib/remote-auth/recovery/context.d.ts +0 -47
  113. package/dist/lib/remote-auth/recovery/dispatch.d.ts +0 -28
  114. package/dist/lib/remote-auth/recovery/failsafe.d.ts +0 -33
  115. package/dist/lib/remote-auth/recovery/manual-guidance.d.ts +0 -28
  116. package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +0 -59
  117. package/dist/lib/remote-auth/recovery/policy.d.ts +0 -47
  118. package/dist/lib/remote-auth/recovery/recover-auth.d.ts +0 -42
  119. package/dist/lib/remote-auth/recovery/recover-binary-conflict.d.ts +0 -37
  120. package/dist/lib/remote-auth/recovery/recover-corrupt-index.d.ts +0 -40
  121. package/dist/lib/remote-auth/recovery/recover-detached-head.d.ts +0 -70
  122. package/dist/lib/remote-auth/recovery/recover-interrupted-cherry-pick.d.ts +0 -23
  123. package/dist/lib/remote-auth/recovery/recover-interrupted-merge.d.ts +0 -28
  124. package/dist/lib/remote-auth/recovery/recover-interrupted-rebase.d.ts +0 -39
  125. package/dist/lib/remote-auth/recovery/recover-merge-conflict.d.ts +0 -34
  126. package/dist/lib/remote-auth/recovery/recover-missing-git-dir.d.ts +0 -37
  127. package/dist/lib/remote-auth/recovery/recover-missing-objects.d.ts +0 -56
  128. package/dist/lib/remote-auth/recovery/recover-network.d.ts +0 -34
  129. package/dist/lib/remote-auth/recovery/recover-non-fast-forward.d.ts +0 -27
  130. package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +0 -68
  131. package/dist/lib/remote-auth/recovery/recover-unrelated-histories.d.ts +0 -44
  132. package/dist/lib/remote-auth/recovery/recover-wrong-remote.d.ts +0 -35
  133. package/dist/lib/remote-auth/resolution-plan.d.ts +0 -64
  134. package/dist/paged.polyfill-n95pbxfn.js +0 -33288
  135. package/dist/pagedjs-interface-qxvzgwd7.js +0 -557
  136. package/dist/preview-shell-6dqexx1m.js +0 -581
  137. /package/dist/assets/{preview/scripts/pagedjs-bridge.d.ts → engine/gutterpress-agent.d.ts} +0 -0
  138. /package/dist/assets/{preview/scripts/pagedjs-interface.d.ts → engine/gutterpress-viewer.d.ts} +0 -0
@@ -1,557 +0,0 @@
1
- // Interface adapter: exposes window.previewAPI for the parent toolbar.
2
- // Paged.js paginates into .pagedjs_page elements. We use PagedConfig.after
3
- // to know when rendering is done.
4
-
5
- (function () {
6
- 'use strict';
7
-
8
- var pages = [];
9
- var currentPage = 1;
10
- var debugMode = false;
11
- var currentViewMode = 'two-column';
12
- var ignoreScrollUntil = 0;
13
- var lastSourceLine = -1;
14
- var lastSourceChapter = null;
15
-
16
- function refreshPages() {
17
- pages = Array.from(document.querySelectorAll('.pagedjs_page'));
18
- return pages;
19
- }
20
-
21
- function clampPage(n) {
22
- if (pages.length === 0) return 1;
23
- var page = Number(n);
24
- if (!Number.isFinite(page)) page = 1;
25
- return Math.max(1, Math.min(Math.round(page), pages.length));
26
- }
27
-
28
- function detectVisiblePage() {
29
- if (pages.length === 0) return 1;
30
- // Use getBoundingClientRect (viewport-relative, post-zoom) rather than
31
- // offsetTop. The desktop applies CSS `zoom` for fit-width; under `zoom`,
32
- // offsetTop stays in PRE-zoom layout coords while window.scrollY is POST-zoom,
33
- // so mixing them pinned the detected page to 1. getBoundingClientRect is
34
- // consistent with the rendered viewport at any zoom.
35
- var line = window.innerHeight / 3; // reference line in the upper third
36
- var vh = window.innerHeight;
37
- var last = 0;
38
- for (var i = 0; i < pages.length; i++) {
39
- var top = pages[i].getBoundingClientRect().top;
40
- if (top <= line) last = i; // last page whose top is at/above the line
41
- else if (top > vh) break; // well below the viewport — stop scanning
42
- }
43
- // In spread/two-column view a row holds two pages at the same top; report the
44
- // FIRST page of that row (matches single view, where each row is one page).
45
- var rowTop = pages[last].getBoundingClientRect().top;
46
- while (last > 0 && Math.abs(pages[last - 1].getBoundingClientRect().top - rowTop) < 2) last--;
47
- return last + 1;
48
- }
49
-
50
- function scrollToCurrentPage() {
51
- if (pages.length === 0) return;
52
- var page = clampPage(currentPage);
53
- currentPage = page;
54
- ignoreScrollUntil = Date.now() + 300;
55
- pages[page - 1].scrollIntoView({ behavior: 'instant', block: 'start', inline: 'nearest' });
56
- recordVisibleSource();
57
- }
58
-
59
- function pageStep(mode) {
60
- return (mode || currentViewMode) === 'single' ? 1 : 2;
61
- }
62
-
63
- // ── Source-mapping helpers (ADR 0005) ──────────────────────────────────────
64
- // Every block element carries data-source-line (markdown-it-source-map). These
65
- // map rendered DOM <-> markdown source line and rendered DOM <-> paged.js page,
66
- // which is the same-origin info the cross-iframe host cannot compute itself.
67
- function lineOf(el) {
68
- if (!el || !el.getAttribute) return null;
69
- var n = parseInt(el.getAttribute('data-source-line'), 10);
70
- return Number.isFinite(n) ? n : null;
71
- }
72
-
73
- function pageIndexOf(el) {
74
- if (!el || !el.closest) return 0;
75
- var pg = el.closest('.pagedjs_page');
76
- if (!pg) return 0;
77
- if (pages.length === 0) refreshPages();
78
- var idx = pages.indexOf(pg);
79
- return idx >= 0 ? idx + 1 : 0;
80
- }
81
-
82
- function sourcedBlocks() {
83
- return Array.from(document.querySelectorAll('[data-source-line]'));
84
- }
85
-
86
- // data-source-line resets PER FILE, so a line number is only unambiguous when
87
- // paired with its chapter (data-chapter-src — the source filename). These two
88
- // helpers scope line lookups to a chapter so editor<->preview sync maps to the
89
- // right file in a multi-chapter book.
90
- function chapterOf(el) {
91
- var c = el && el.closest ? el.closest('[data-chapter-src]') : null;
92
- return c ? c.getAttribute('data-chapter-src') : null;
93
- }
94
-
95
- function blocksInChapter(chapter) {
96
- if (!chapter) return sourcedBlocks();
97
- return sourcedBlocks().filter(function (block) {
98
- return chapterOf(block) === chapter;
99
- });
100
- }
101
-
102
- // The block straddling the top of the viewport (greatest top still at/above a
103
- // reference line just below the viewport edge); falls back to the first block
104
- // below the fold when scrolled to the very top.
105
- function topVisibleSourceEl() {
106
- var blocks = sourcedBlocks();
107
- if (blocks.length === 0) return null;
108
- var ref = 4;
109
- var best = null, bestTop = -Infinity;
110
- for (var i = 0; i < blocks.length; i++) {
111
- var top = blocks[i].getBoundingClientRect().top;
112
- if (top <= ref && top > bestTop) { bestTop = top; best = blocks[i]; }
113
- }
114
- if (!best) {
115
- for (var j = 0; j < blocks.length; j++) {
116
- if (blocks[j].getBoundingClientRect().top >= 0) { best = blocks[j]; break; }
117
- }
118
- }
119
- return best || blocks[0];
120
- }
121
-
122
- // {el, line} of the viewport-top source position, with the line interpolated
123
- // within the straddling block by the viewport top's fractional distance to the
124
- // next annotated block (source-map only annotates top-level blocks, so the
125
- // block's start line alone can be tens of lines above the visible content).
126
- function visibleSourcePosition() {
127
- var el = topVisibleSourceEl();
128
- if (!el) return null;
129
- var line = lineOf(el);
130
- if (line == null) return { el: el, line: null };
131
- var ref = 4; // same reference line as topVisibleSourceEl
132
- var top = el.getBoundingClientRect().top;
133
- if (top < ref) {
134
- // Find the next annotated sibling-in-flow (document order, higher line —
135
- // explicitly constrained to this chapter because source lines restart per
136
- // file and a following chapter may begin at any line after front matter).
137
- var blocks = sourcedBlocks();
138
- var idx = blocks.indexOf(el);
139
- var chapter = chapterOf(el);
140
- for (var i = idx + 1; i < blocks.length; i++) {
141
- if (chapterOf(blocks[i]) !== chapter) break;
142
- var nl = lineOf(blocks[i]);
143
- if (nl == null) continue;
144
- if (nl <= line) break;
145
- var nextTop = blocks[i].getBoundingClientRect().top;
146
- if (nextTop > top) {
147
- var f = Math.max(0, Math.min(1, (ref - top) / (nextTop - top)));
148
- line = line + Math.round(f * (nl - line));
149
- }
150
- break;
151
- }
152
- }
153
- return { el: el, line: line };
154
- }
155
-
156
- function recordVisibleSource() {
157
- var pos = visibleSourcePosition();
158
- lastSourceLine = pos && pos.line != null ? pos.line : -1;
159
- lastSourceChapter = pos ? chapterOf(pos.el) : null;
160
- }
161
-
162
- function preserveViewport(change) {
163
- refreshPages();
164
- var position = visibleSourcePosition();
165
- var anchor = position ? {
166
- el: position.el,
167
- top: position.el.getBoundingClientRect().top
168
- } : null;
169
- change();
170
- refreshPages();
171
- if (!anchor) {
172
- scrollToCurrentPage();
173
- return;
174
- }
175
- ignoreScrollUntil = Date.now() + 300;
176
- var delta = anchor.el.getBoundingClientRect().top - anchor.top;
177
- if (delta) {
178
- window.scrollBy({ top: delta, behavior: 'instant' });
179
- }
180
- currentPage = detectVisiblePage();
181
- recordVisibleSource();
182
- }
183
-
184
- // Resolve a {line, chapter?} target to its enclosing annotated block PLUS the
185
- // following annotated block, so callers can interpolate within long blocks
186
- // (markdown-it-source-map only annotates top-level *_open tokens, so a target
187
- // line deep inside a list/table/paragraph would otherwise snap to the block's
188
- // start line — measured 35-line snap-backs on real chapters).
189
- function resolveLinePosition(target) {
190
- var line = Number(target.line);
191
- var blocks = blocksInChapter(target.chapter);
192
- var best = null, bestLine = -Infinity, next = null, nextLine = Infinity;
193
- for (var i = 0; i < blocks.length; i++) {
194
- var l = lineOf(blocks[i]);
195
- if (l == null) continue;
196
- if (l <= line && l > bestLine) { bestLine = l; best = blocks[i]; }
197
- else if (l > line && l < nextLine) { nextLine = l; next = blocks[i]; }
198
- }
199
- if (!best) return blocks[0] ? { el: blocks[0], line: lineOf(blocks[0]), nextEl: null, nextLine: null } : null;
200
- return { el: best, line: bestLine, nextEl: next, nextLine: next ? nextLine : null };
201
- }
202
-
203
- // Resolve a scrollTo/highlight target ({line}|{id}|{selector}|{page} or a bare
204
- // line number) to a DOM element.
205
- function resolveTarget(target) {
206
- if (target == null) return null;
207
- if (typeof target === 'number') target = { line: target };
208
- if (target.selector) { try { return document.querySelector(target.selector); } catch (_e) { return null; } }
209
- if (target.id) return document.getElementById(target.id);
210
- if (target.page != null) { refreshPages(); return pages[clampPage(target.page) - 1] || null; }
211
- if (target.line != null) {
212
- var pos = resolveLinePosition(target);
213
- return pos ? pos.el : null;
214
- }
215
- return null;
216
- }
217
-
218
- var api = {
219
- getTotalPages: function () { refreshPages(); return pages.length; },
220
- getCurrentPage: function () { return currentPage; },
221
- goToPage: function (n) {
222
- refreshPages();
223
- currentPage = clampPage(n);
224
- scrollToCurrentPage();
225
- return api.notifyPageChange();
226
- },
227
- getPageDimensions: function () {
228
- refreshPages();
229
- var page = pages[0] || null;
230
- var pagesEl = document.querySelector('.pagedjs_pages');
231
- if (!page) return null;
232
- return {
233
- width: currentViewMode === 'single' ? page.offsetWidth : (pagesEl ? pagesEl.scrollWidth : page.offsetWidth),
234
- height: page.offsetHeight
235
- };
236
- },
237
- firstPage: function () { return api.goToPage(1); },
238
- prevPage: function (mode) { return api.goToPage(currentPage - pageStep(mode)); },
239
- nextPage: function (mode) { return api.goToPage(currentPage + pageStep(mode)); },
240
- lastPage: function () { refreshPages(); return api.goToPage(pages.length); },
241
- setViewMode: function (mode, silent) {
242
- preserveViewport(function () {
243
- currentViewMode = mode || 'two-column';
244
- document.body.classList.remove('view-single', 'view-spread', 'view-two-column');
245
- if (mode) document.body.classList.add('view-' + mode);
246
- });
247
- if (!silent) return api.notifyPageChange();
248
- },
249
- setZoom: function (z, silent) {
250
- preserveViewport(function () {
251
- document.documentElement.style.setProperty('--gutterpress-zoom', z);
252
- });
253
- if (!silent) api.notifyPageChange();
254
- },
255
- toggleDebugMode: function () {
256
- debugMode = !debugMode;
257
- document.body.classList.toggle('debug', debugMode);
258
- return debugMode;
259
- },
260
- notifyPageChange: function () {
261
- var detail = { currentPage: api.getCurrentPage(), totalPages: pages.length };
262
- window.dispatchEvent(new CustomEvent('pageChanged', { detail: detail }));
263
- return detail;
264
- },
265
- notifyRenderingComplete: function () {
266
- window.dispatchEvent(new CustomEvent('renderingComplete', {
267
- detail: { totalPages: pages.length }
268
- }));
269
- },
270
- // Re-read the page list and recompute the current page from the scroll
271
- // position, then notify. Hosts may call this after replacing paginated DOM
272
- // so cached page counters and scroll synchronization stay current.
273
- refresh: function () {
274
- refreshPages();
275
- currentPage = detectVisiblePage();
276
- recordVisibleSource();
277
- return api.notifyPageChange();
278
- },
279
-
280
- // ── ADR 0005 generic primitives ─────────────────────────────────────────
281
- // Bumped whenever a command/event is added so a hot-updated SPA can
282
- // feature-detect against an older bundled lib.
283
- getProtocolVersion: function () { return 3; },
284
-
285
- // Publish any debounced reader movement before a host atomically replaces
286
- // this frame. `silent` lets the shell relay the returned event itself so a
287
- // queued postMessage from the outgoing frame cannot arrive after the swap.
288
- flushScroll: function (silent) {
289
- if (scrollTimer) clearTimeout(scrollTimer);
290
- scrollTimer = null;
291
- return publishScrollPosition(true, silent === true);
292
- },
293
-
294
- // Heading tree with page + source line — powers chapter jump (UX-013), TOC,
295
- // minimap, scrollspy. Page math needs same-origin paged.js access, so it
296
- // lives here rather than being derived host-side.
297
- getOutline: function () {
298
- refreshPages();
299
- var hs = Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,h6'));
300
- var out = [];
301
- for (var i = 0; i < hs.length; i++) {
302
- var h = hs[i];
303
- var text = (h.textContent || '').trim();
304
- if (!text) continue;
305
- out.push({
306
- level: parseInt(h.tagName.charAt(1), 10),
307
- text: text,
308
- id: h.id || null,
309
- sourceLine: lineOf(h),
310
- chapter: chapterOf(h),
311
- page: pageIndexOf(h),
312
- index: i
313
- });
314
- }
315
- return out;
316
- },
317
-
318
- // Single anchored-jump primitive: target {line}|{id}|{selector}|{page}.
319
- // Returns the resolved {page, sourceLine}. Suppresses the scroll-driven
320
- // pageChanged/sourceLineChanged echo so host-driven jumps don't loop back.
321
- scrollTo: function (target, opts) {
322
- opts = opts || {};
323
- if (typeof target === 'number') target = { line: target };
324
- var el = null;
325
- var fraction = 0;
326
- var nextEl = null;
327
- if (target && target.line != null && target.selector == null && target.id == null && target.page == null) {
328
- var pos = resolveLinePosition(target);
329
- if (pos) {
330
- el = pos.el;
331
- if (pos.nextEl && pos.nextLine != null && pos.nextLine > pos.line) {
332
- nextEl = pos.nextEl;
333
- fraction = (Number(target.line) - pos.line) / (pos.nextLine - pos.line);
334
- fraction = Math.max(0, Math.min(1, fraction));
335
- }
336
- }
337
- } else {
338
- el = resolveTarget(target);
339
- }
340
- if (!el) return null;
341
- ignoreScrollUntil = Date.now() + 350;
342
- var behavior = opts.smooth ? 'smooth' : 'instant';
343
- el.scrollIntoView({
344
- behavior: fraction > 0 ? 'instant' : behavior,
345
- block: opts.block || 'start',
346
- inline: 'nearest'
347
- });
348
- if (fraction > 0 && nextEl) {
349
- // Interpolate within the block: nudge the scroll by the target line's
350
- // fractional distance between this block's top and the NEXT annotated
351
- // block's top. Only when the next block is genuinely lower on screen —
352
- // a facing-page or next-column neighbour (top <= ours) degrades to the
353
- // plain block-top scroll, which is the pre-existing behaviour.
354
- var top = el.getBoundingClientRect().top;
355
- var nextTop = nextEl.getBoundingClientRect().top;
356
- if (nextTop > top) {
357
- window.scrollBy({ top: fraction * (nextTop - top), behavior: behavior });
358
- }
359
- }
360
- var page = pageIndexOf(el);
361
- if (page) currentPage = page;
362
- if (!opts.smooth) recordVisibleSource();
363
- return { page: page || currentPage, sourceLine: lineOf(el) };
364
- },
365
-
366
- // Source line + page of the block at the top of the viewport (host scroll
367
- // position read for preview->editor sync).
368
- getVisibleSource: function () {
369
- var pos = visibleSourcePosition();
370
- return pos ? { sourceLine: pos.line, chapter: chapterOf(pos.el), page: pageIndexOf(pos.el) } : null;
371
- },
372
-
373
- // Generic, read-only DOM extraction. fields: 'text'|'id'|'sourceLine'|
374
- // 'page'|'tag'|'rectTop'|{attr:'name'}. No eval, no innerHTML — this is the
375
- // forward-compat hook that lets the SPA build find/figure-list/etc. with no
376
- // further lib change.
377
- queryDom: function (spec) {
378
- spec = spec || {};
379
- if (!spec.selector) return [];
380
- var fields = spec.fields || ['text'];
381
- var els;
382
- try { els = Array.from(document.querySelectorAll(spec.selector)); }
383
- catch (_e) { return []; }
384
- if (spec.limit && els.length > spec.limit) els = els.slice(0, spec.limit);
385
- return els.map(function (el) {
386
- var row = {};
387
- for (var i = 0; i < fields.length; i++) {
388
- var f = fields[i];
389
- if (f === 'text') row.text = (el.textContent || '').trim();
390
- else if (f === 'id') row.id = el.id || null;
391
- else if (f === 'sourceLine') row.sourceLine = lineOf(el);
392
- else if (f === 'page') row.page = pageIndexOf(el);
393
- else if (f === 'chapter') row.chapter = chapterOf(el);
394
- else if (f === 'tag') row.tag = el.tagName.toLowerCase();
395
- else if (f === 'rectTop') row.rectTop = el.getBoundingClientRect().top;
396
- else if (f && typeof f === 'object' && f.attr) row['attr:' + f.attr] = el.getAttribute(f.attr);
397
- }
398
- return row;
399
- });
400
- },
401
-
402
- // Toggle a marker class on matched elements (DOM write inside the frame).
403
- // spec: {line?|id?|selector?, group?, scroll?, transient?, transientMs?}.
404
- // Powers find-in-page, editor-cursor echo, annotations.
405
- highlight: function (spec) {
406
- spec = spec || {};
407
- var group = spec.group || 'default';
408
- var els = [];
409
- if (spec.selector) { try { els = Array.from(document.querySelectorAll(spec.selector)); } catch (_e) {} }
410
- else if (spec.id) { var byId = document.getElementById(spec.id); if (byId) els = [byId]; }
411
- else if (spec.line != null) { var one = resolveTarget({ line: spec.line }); if (one) els = [one]; }
412
- for (var i = 0; i < els.length; i++) {
413
- els[i].classList.add('gutterpress-hl');
414
- els[i].setAttribute('data-gutterpress-hl-group', group);
415
- }
416
- if (spec.scroll && els[0]) {
417
- ignoreScrollUntil = Date.now() + 350;
418
- els[0].scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });
419
- recordVisibleSource();
420
- }
421
- if (spec.transient && els.length) {
422
- setTimeout(function () {
423
- for (var j = 0; j < els.length; j++) {
424
- if (els[j].getAttribute('data-gutterpress-hl-group') === group) {
425
- els[j].classList.remove('gutterpress-hl');
426
- els[j].removeAttribute('data-gutterpress-hl-group');
427
- }
428
- }
429
- }, spec.transientMs || 1200);
430
- }
431
- return { count: els.length };
432
- },
433
-
434
- clearHighlights: function (group) {
435
- var sel = group ? '.gutterpress-hl[data-gutterpress-hl-group="' + group + '"]' : '.gutterpress-hl';
436
- var els = Array.from(document.querySelectorAll(sel));
437
- for (var i = 0; i < els.length; i++) {
438
- els[i].classList.remove('gutterpress-hl');
439
- els[i].removeAttribute('data-gutterpress-hl-group');
440
- }
441
- return { cleared: els.length };
442
- }
443
- };
444
-
445
- window.previewAPI = api;
446
-
447
- // Default highlight style (preview-only; never part of the PDF build path).
448
- // Guarded so a minimal/headless DOM (e.g. unit-test harness) can't crash here.
449
- if (typeof document.createElement === 'function') {
450
- try {
451
- var hlStyle = document.createElement('style');
452
- hlStyle.textContent =
453
- '.gutterpress-hl{outline:2px solid var(--gutterpress-hl-color,#4ea1ff);outline-offset:2px;' +
454
- 'background:var(--gutterpress-hl-bg,rgba(78,161,255,.14));' +
455
- 'transition:outline-color .2s,background .2s;}';
456
- (document.head || document.documentElement).appendChild(hlStyle);
457
- } catch (_e) { /* non-fatal: highlight just renders unstyled */ }
458
- }
459
-
460
- // Click-to-source: emit elementActivated when the user clicks a source-mapped
461
- // block. Never preventDefault (links/selection keep working); the host decides
462
- // whether to act. (ADR 0005)
463
- if (typeof document.addEventListener === 'function') {
464
- document.addEventListener('click', function (e) {
465
- var el = e.target && e.target.closest ? e.target.closest('[data-source-line]') : null;
466
- if (!el) return;
467
- window.dispatchEvent(new CustomEvent('elementActivated', {
468
- detail: { sourceLine: lineOf(el), chapter: chapterOf(el), id: el.id || null, tag: el.tagName.toLowerCase() }
469
- }));
470
- }, true);
471
- }
472
-
473
- var observedPageCount = 0;
474
- var pageObserverQueued = false;
475
- function publishObservedPageCount() {
476
- pageObserverQueued = false;
477
- if (window.__PAGED_RENDERED__ === true) return;
478
- var count = refreshPages().length;
479
- if (count > observedPageCount) {
480
- observedPageCount = count;
481
- window.dispatchEvent(new CustomEvent('pageChanged', {
482
- detail: { currentPage: count, totalPages: count }
483
- }));
484
- }
485
- }
486
- var pageObserver = new MutationObserver(function () {
487
- if (window.__PAGED_RENDERED__ === true) return;
488
- if (pageObserverQueued) return;
489
- pageObserverQueued = true;
490
- window.requestAnimationFrame(publishObservedPageCount);
491
- });
492
-
493
- function startPageObserver() {
494
- var target = document.body || document.documentElement;
495
- if (!target) return false;
496
- pageObserver.observe(target, { childList: true, subtree: true });
497
- return true;
498
- }
499
-
500
- if (!startPageObserver()) {
501
- document.addEventListener('DOMContentLoaded', function onReady() {
502
- document.removeEventListener('DOMContentLoaded', onReady);
503
- startPageObserver();
504
- });
505
- }
506
-
507
- // Scroll tracking
508
- var scrollTimer = null;
509
- function publishScrollPosition(force, silent) {
510
- scrollTimer = null;
511
- var remainingGuard = ignoreScrollUntil - Date.now();
512
- if (!force && remainingGuard > 0) {
513
- scrollTimer = setTimeout(publishScrollPosition, remainingGuard);
514
- return null;
515
- }
516
- if (pages.length === 0) refreshPages();
517
- if (pages.length === 0) return null;
518
- var page = detectVisiblePage();
519
- if (page !== currentPage) {
520
- currentPage = page;
521
- if (!silent) api.notifyPageChange();
522
- }
523
- // Emit finer-grained source position for editor sync (ADR 0005).
524
- var pos = visibleSourcePosition();
525
- var sl = pos ? pos.line : null;
526
- var chapter = pos ? chapterOf(pos.el) : null;
527
- if (sl != null && (sl !== lastSourceLine || chapter !== lastSourceChapter)) {
528
- lastSourceLine = sl;
529
- lastSourceChapter = chapter;
530
- var detail = { sourceLine: sl, chapter: chapter, page: pageIndexOf(pos.el) };
531
- if (!silent) {
532
- window.dispatchEvent(new CustomEvent('sourceLineChanged', { detail: detail }));
533
- }
534
- return detail;
535
- }
536
- return null;
537
- }
538
- window.addEventListener('scroll', function () {
539
- if (scrollTimer) clearTimeout(scrollTimer);
540
- scrollTimer = setTimeout(publishScrollPosition, 150);
541
- });
542
-
543
- // Paged.js calls this when rendering is complete
544
- window.PagedConfig = window.PagedConfig || {};
545
- window.PagedConfig.after = function (flow) {
546
- refreshPages();
547
- observedPageCount = pages.length;
548
- pageObserver.disconnect();
549
- currentPage = 1;
550
- ignoreScrollUntil = Date.now() + 300;
551
- window.scrollTo({ top: 0, left: 0, behavior: 'instant' });
552
- recordVisibleSource();
553
- console.log('Paged.js rendered ' + pages.length + ' pages');
554
- api.notifyRenderingComplete();
555
- setTimeout(api.notifyPageChange, 0);
556
- };
557
- })();