gutterpress 0.0.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.
Files changed (230) hide show
  1. package/README.md +422 -0
  2. package/dist/CGATS21_CRPC1-g0e3k7kr.icc +0 -0
  3. package/dist/api/index.d.ts +76 -0
  4. package/dist/api/index.js +272 -0
  5. package/dist/assets/preview/scripts/pagedjs-bridge.d.ts +1 -0
  6. package/dist/assets/preview/scripts/pagedjs-interface.d.ts +1 -0
  7. package/dist/assets/preview/scripts/preview-shell.d.ts +1 -0
  8. package/dist/audit-1jrwwacz.js +66 -0
  9. package/dist/build-5s92kgpc.js +77 -0
  10. package/dist/chapter-01-6fn9pvd0.md +31 -0
  11. package/dist/chapter-01-7jwqg1ct.md +20 -0
  12. package/dist/chapter-01-jj8ftn4c.md +29 -0
  13. package/dist/chapter-01-rkpyvjna.md +26 -0
  14. package/dist/checks/asset/approved-fonts.d.ts +3 -0
  15. package/dist/checks/asset/extensions.d.ts +41 -0
  16. package/dist/checks/asset/font-license.d.ts +3 -0
  17. package/dist/checks/asset/image-alpha.d.ts +3 -0
  18. package/dist/checks/asset/image-color-space.d.ts +3 -0
  19. package/dist/checks/asset/image-file-size.d.ts +3 -0
  20. package/dist/checks/asset/image-resolution.d.ts +3 -0
  21. package/dist/checks/asset/image-tac.d.ts +3 -0
  22. package/dist/checks/asset/index.d.ts +7 -0
  23. package/dist/checks/formatter.d.ts +3 -0
  24. package/dist/checks/heuristic/index.d.ts +4 -0
  25. package/dist/checks/heuristic/layer-count.d.ts +3 -0
  26. package/dist/checks/heuristic/placement-variance.d.ts +3 -0
  27. package/dist/checks/heuristic/section-density.d.ts +3 -0
  28. package/dist/checks/heuristic/text-density.d.ts +3 -0
  29. package/dist/checks/pdf/bleed.d.ts +3 -0
  30. package/dist/checks/pdf/bookmarks.d.ts +3 -0
  31. package/dist/checks/pdf/color-spaces.d.ts +3 -0
  32. package/dist/checks/pdf/cross-refs.d.ts +3 -0
  33. package/dist/checks/pdf/embedded-fonts.d.ts +3 -0
  34. package/dist/checks/pdf/image-resolution.d.ts +3 -0
  35. package/dist/checks/pdf/index.d.ts +15 -0
  36. package/dist/checks/pdf/ink-coverage.d.ts +3 -0
  37. package/dist/checks/pdf/page-labels.d.ts +3 -0
  38. package/dist/checks/pdf/page-size.d.ts +3 -0
  39. package/dist/checks/pdf/pdfx-markers.d.ts +3 -0
  40. package/dist/checks/pdf/pdfx-metadata.d.ts +3 -0
  41. package/dist/checks/pdf/pdfx-structure.d.ts +6 -0
  42. package/dist/checks/pdf/qpdf-structure.d.ts +3 -0
  43. package/dist/checks/pdf/rasterized-pages.d.ts +3 -0
  44. package/dist/checks/pdf/toc-links.d.ts +3 -0
  45. package/dist/checks/pdf/transparency.d.ts +3 -0
  46. package/dist/checks/policy.d.ts +61 -0
  47. package/dist/checks/register-builtins.d.ts +17 -0
  48. package/dist/checks/registry.d.ts +40 -0
  49. package/dist/checks/runner.d.ts +25 -0
  50. package/dist/checks/source/accessibility-alt-text.d.ts +3 -0
  51. package/dist/checks/source/accessibility-heading-order.d.ts +3 -0
  52. package/dist/checks/source/config-file.d.ts +8 -0
  53. package/dist/checks/source/htmlhint.d.ts +3 -0
  54. package/dist/checks/source/index.d.ts +6 -0
  55. package/dist/checks/source/local-refs.d.ts +3 -0
  56. package/dist/checks/source/markdownlint.d.ts +3 -0
  57. package/dist/checks/source/stylelint.d.ts +3 -0
  58. package/dist/checks/tool-check.d.ts +23 -0
  59. package/dist/checks/types.d.ts +52 -0
  60. package/dist/cli-1tyj4bk3.js +124 -0
  61. package/dist/cli-37x76zdn.js +4 -0
  62. package/dist/cli-506tg37g.js +26 -0
  63. package/dist/cli-5bxx0c4y.js +464 -0
  64. package/dist/cli-7ykx7pp0.js +12378 -0
  65. package/dist/cli-h7phg3ka.js +746 -0
  66. package/dist/cli-hvqjs342.js +284 -0
  67. package/dist/cli-yja077f6.js +92 -0
  68. package/dist/cli.js +157 -0
  69. package/dist/constants.d.ts +17 -0
  70. package/dist/doctor-25zjt8dz.js +57 -0
  71. package/dist/favicon-wkbm9cjn.ico +0 -0
  72. package/dist/git-http-yrb4ag6z.js +17 -0
  73. package/dist/index-1tyj4bk3.js +124 -0
  74. package/dist/index-37x76zdn.js +4 -0
  75. package/dist/index-3h3fef7r.js +15218 -0
  76. package/dist/index-5bxx0c4y.js +464 -0
  77. package/dist/index-yja077f6.js +92 -0
  78. package/dist/index.d.ts +32 -0
  79. package/dist/index.js +310 -0
  80. package/dist/lib/app-heartbeat.d.ts +102 -0
  81. package/dist/lib/asset-inline.d.ts +73 -0
  82. package/dist/lib/browser-pool.d.ts +17 -0
  83. package/dist/lib/build-error.d.ts +52 -0
  84. package/dist/lib/build-fingerprint.d.ts +15 -0
  85. package/dist/lib/build-preflight.d.ts +43 -0
  86. package/dist/lib/build-runner.d.ts +161 -0
  87. package/dist/lib/build-staging.d.ts +64 -0
  88. package/dist/lib/chromium.d.ts +20 -0
  89. package/dist/lib/cli-args.d.ts +45 -0
  90. package/dist/lib/desktop.d.ts +14 -0
  91. package/dist/lib/diagnostics.d.ts +64 -0
  92. package/dist/lib/embedded-assets.d.ts +29 -0
  93. package/dist/lib/exec.d.ts +84 -0
  94. package/dist/lib/fetch-timeout.d.ts +41 -0
  95. package/dist/lib/ghostscript.d.ts +48 -0
  96. package/dist/lib/host-policy.d.ts +75 -0
  97. package/dist/lib/image-inspect.d.ts +32 -0
  98. package/dist/lib/install-hints.d.ts +21 -0
  99. package/dist/lib/lint-runner.d.ts +10 -0
  100. package/dist/lib/manifest-config.d.ts +31 -0
  101. package/dist/lib/manifest-doc.d.ts +25 -0
  102. package/dist/lib/manifest.d.ts +41 -0
  103. package/dist/lib/markdown/assemble.d.ts +78 -0
  104. package/dist/lib/markdown/chapter-id.d.ts +24 -0
  105. package/dist/lib/markdown/images.d.ts +34 -0
  106. package/dist/lib/markdown/index.d.ts +78 -0
  107. package/dist/lib/markdown/markdown-it-paged.d.ts +30 -0
  108. package/dist/lib/markdown/plugins.d.ts +69 -0
  109. package/dist/lib/markdown/renderer.d.ts +111 -0
  110. package/dist/lib/npm-plugin-installer.d.ts +33 -0
  111. package/dist/lib/open-path.d.ts +5 -0
  112. package/dist/lib/output-paths.d.ts +23 -0
  113. package/dist/lib/pagedjs-marker.d.ts +42 -0
  114. package/dist/lib/pagedjs.d.ts +26 -0
  115. package/dist/lib/pagination.d.ts +169 -0
  116. package/dist/lib/pdf-inspect.d.ts +123 -0
  117. package/dist/lib/pdf-parse.d.ts +57 -0
  118. package/dist/lib/plugin-manager.d.ts +108 -0
  119. package/dist/lib/plugin-vendor.d.ts +107 -0
  120. package/dist/lib/presets.d.ts +45 -0
  121. package/dist/lib/printsafe.d.ts +17 -0
  122. package/dist/lib/project-scaffold.d.ts +116 -0
  123. package/dist/lib/project-source.d.ts +140 -0
  124. package/dist/lib/project-templates.d.ts +49 -0
  125. package/dist/lib/publish/butler.d.ts +10 -0
  126. package/dist/lib/publish/command-runner.d.ts +18 -0
  127. package/dist/lib/publish/connect.d.ts +23 -0
  128. package/dist/lib/publish/manifest-publish.d.ts +9 -0
  129. package/dist/lib/publish/providers/azure-swa.d.ts +3 -0
  130. package/dist/lib/publish/providers/drivethrurpg.d.ts +10 -0
  131. package/dist/lib/publish/providers/guided.d.ts +3 -0
  132. package/dist/lib/publish/providers/itch.d.ts +5 -0
  133. package/dist/lib/publish/providers/kdp.d.ts +9 -0
  134. package/dist/lib/publish/providers/shopify.d.ts +5 -0
  135. package/dist/lib/publish/registry.d.ts +5 -0
  136. package/dist/lib/publish/run-publish.d.ts +30 -0
  137. package/dist/lib/publish/selections.d.ts +25 -0
  138. package/dist/lib/publish/types.d.ts +258 -0
  139. package/dist/lib/remote-auth/clone.d.ts +87 -0
  140. package/dist/lib/remote-auth/conflict-resolution.d.ts +29 -0
  141. package/dist/lib/remote-auth/diagnose.d.ts +77 -0
  142. package/dist/lib/remote-auth/generic-auth.d.ts +97 -0
  143. package/dist/lib/remote-auth/git-http.d.ts +85 -0
  144. package/dist/lib/remote-auth/github-auth.d.ts +69 -0
  145. package/dist/lib/remote-auth/github-repos.d.ts +64 -0
  146. package/dist/lib/remote-auth/operation-log.d.ts +66 -0
  147. package/dist/lib/remote-auth/recovery/abort-interrupted-operation.d.ts +103 -0
  148. package/dist/lib/remote-auth/recovery/backup.d.ts +113 -0
  149. package/dist/lib/remote-auth/recovery/classify.d.ts +109 -0
  150. package/dist/lib/remote-auth/recovery/context.d.ts +47 -0
  151. package/dist/lib/remote-auth/recovery/dispatch.d.ts +33 -0
  152. package/dist/lib/remote-auth/recovery/failsafe.d.ts +33 -0
  153. package/dist/lib/remote-auth/recovery/inspect.d.ts +74 -0
  154. package/dist/lib/remote-auth/recovery/manual-guidance.d.ts +28 -0
  155. package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +58 -0
  156. package/dist/lib/remote-auth/recovery/policy.d.ts +47 -0
  157. package/dist/lib/remote-auth/recovery/recover-auth.d.ts +42 -0
  158. package/dist/lib/remote-auth/recovery/recover-binary-conflict.d.ts +37 -0
  159. package/dist/lib/remote-auth/recovery/recover-corrupt-index.d.ts +40 -0
  160. package/dist/lib/remote-auth/recovery/recover-detached-head.d.ts +70 -0
  161. package/dist/lib/remote-auth/recovery/recover-interrupted-cherry-pick.d.ts +23 -0
  162. package/dist/lib/remote-auth/recovery/recover-interrupted-merge.d.ts +28 -0
  163. package/dist/lib/remote-auth/recovery/recover-interrupted-rebase.d.ts +39 -0
  164. package/dist/lib/remote-auth/recovery/recover-merge-conflict.d.ts +34 -0
  165. package/dist/lib/remote-auth/recovery/recover-missing-git-dir.d.ts +37 -0
  166. package/dist/lib/remote-auth/recovery/recover-missing-objects.d.ts +56 -0
  167. package/dist/lib/remote-auth/recovery/recover-network.d.ts +34 -0
  168. package/dist/lib/remote-auth/recovery/recover-non-fast-forward.d.ts +27 -0
  169. package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +76 -0
  170. package/dist/lib/remote-auth/recovery/recover-unrelated-histories.d.ts +44 -0
  171. package/dist/lib/remote-auth/recovery/recover-wrong-remote.d.ts +35 -0
  172. package/dist/lib/remote-auth/recovery/types.d.ts +235 -0
  173. package/dist/lib/remote-auth/resolution-plan.d.ts +64 -0
  174. package/dist/lib/remote-auth/sync-messages.d.ts +26 -0
  175. package/dist/lib/remote-auth/sync-types.d.ts +225 -0
  176. package/dist/lib/remote-auth/sync.d.ts +45 -0
  177. package/dist/lib/remote-auth/test-access.d.ts +47 -0
  178. package/dist/lib/remote-auth/test-support/git-http-server.d.ts +71 -0
  179. package/dist/lib/remote-auth/token-store.d.ts +95 -0
  180. package/dist/lib/remote-auth/transport.d.ts +105 -0
  181. package/dist/lib/slug.d.ts +28 -0
  182. package/dist/lib/snippets.d.ts +39 -0
  183. package/dist/lib/source-provider.d.ts +244 -0
  184. package/dist/lib/static-serve.d.ts +54 -0
  185. package/dist/lib/style-resolver.d.ts +30 -0
  186. package/dist/lib/theme-import.d.ts +70 -0
  187. package/dist/lib/theme-manager.d.ts +128 -0
  188. package/dist/lib/tool-probe.d.ts +37 -0
  189. package/dist/lib/validation-exec.d.ts +47 -0
  190. package/dist/lib/validation-profile.d.ts +18 -0
  191. package/dist/lib/version.d.ts +9 -0
  192. package/dist/lint-f63nxjfj.js +65 -0
  193. package/dist/manifest-n1gh3qw5.yaml +9 -0
  194. package/dist/manifest-ryyg71nf.yaml +9 -0
  195. package/dist/manifest.schema-rn1dpdpn.json +564 -0
  196. package/dist/new-7b08a42h.js +105 -0
  197. package/dist/paged.polyfill-n95pbxfn.js +33288 -0
  198. package/dist/pagedjs-bridge-vn4hk9fx.js +111 -0
  199. package/dist/pagedjs-interface-80g3xa8p.js +498 -0
  200. package/dist/platform.d.ts +100 -0
  201. package/dist/plugin-wf2cbmwc.js +129 -0
  202. package/dist/preflight-tg4mpz1h.js +222 -0
  203. package/dist/preview/file-watcher.d.ts +174 -0
  204. package/dist/preview/http-server.d.ts +45 -0
  205. package/dist/preview/lifecycle.d.ts +67 -0
  206. package/dist/preview/server-context.d.ts +36 -0
  207. package/dist/preview-h2bwbb2x.js +102 -0
  208. package/dist/preview-shell-rghtfmc8.js +155 -0
  209. package/dist/project-source-0tg0r6vm.js +17 -0
  210. package/dist/publish-ztaet53r.js +233 -0
  211. package/dist/render.d.ts +20 -0
  212. package/dist/render.js +682 -0
  213. package/dist/repair-jkp4n3j8.js +171 -0
  214. package/dist/schema/manifest.types.d.ts +218 -0
  215. package/dist/server.d.ts +34 -0
  216. package/dist/source-provider-hp96b5jf.js +46 -0
  217. package/dist/test-helpers/testkit.d.ts +91 -0
  218. package/dist/theme-0yzvthe8.css +65 -0
  219. package/dist/theme-570zmh2t.css +58 -0
  220. package/dist/theme-6a2fngc8.json +6 -0
  221. package/dist/theme-cd6efzjw.json +6 -0
  222. package/dist/theme-nya4nqh6.css +70 -0
  223. package/dist/theme-tqfypc1w.json +6 -0
  224. package/dist/theme-xt0hjb1b.json +6 -0
  225. package/dist/theme-zz2ktzqs.css +60 -0
  226. package/dist/types.d.ts +31 -0
  227. package/dist/utils/file-utils.d.ts +53 -0
  228. package/dist/utils/logger.d.ts +59 -0
  229. package/dist/validate-k2drs19j.js +99 -0
  230. package/package.json +102 -0
@@ -0,0 +1,111 @@
1
+ // Cross-origin postMessage bridge for window.previewAPI.
2
+ //
3
+ // The desktop (Svelte toolbar) and this iframe are on different origins
4
+ // (SvelteKit on port A, gutterpress preview on port B), so the toolbar can't
5
+ // reach window.previewAPI directly. This bridge listens for command
6
+ // messages, calls the local previewAPI, and posts results / events back to
7
+ // the parent window.
8
+ //
9
+ // Protocol:
10
+ // parent -> iframe: { type: 'gutterpress:cmd', id: <number>, cmd: <string>, args?: [...] }
11
+ // iframe -> parent: { type: 'gutterpress:reply', id: <number>, ok: true, result: <any> }
12
+ // or { type: 'gutterpress:reply', id: <number>, ok: false, error: <string> }
13
+ // iframe -> parent: { type: 'gutterpress:event', name: 'pageChanged'|'renderingComplete'|'ready', detail }
14
+ //
15
+ // Commands map 1:1 to previewAPI methods: getTotalPages, getCurrentPage,
16
+ // goToPage, firstPage, prevPage, nextPage, lastPage, setViewMode, setZoom,
17
+ // toggleDebugMode. Plus a synthetic 'print' command that calls window.print().
18
+ //
19
+ // Additional messages:
20
+ // parent -> iframe: { type: 'gutterpress:inject-styles', id: <attr-name>, css: <string> }
21
+ // Inserts or replaces a <style data-{id}="true"> block in the iframe's <head>.
22
+ // Used to push view-mode CSS and debug CSS into the cross-origin iframe.
23
+
24
+ (function () {
25
+ 'use strict';
26
+
27
+ function post(msg) {
28
+ try {
29
+ window.parent.postMessage(msg, '*');
30
+ } catch (_e) {
31
+ // No parent or sandboxed; ignore.
32
+ }
33
+ }
34
+
35
+ function call(cmd, args) {
36
+ if (cmd === 'print') {
37
+ window.print();
38
+ return true;
39
+ }
40
+ var api = window.previewAPI;
41
+ if (!api || typeof api[cmd] !== 'function') {
42
+ throw new Error('Unknown command: ' + cmd);
43
+ }
44
+ return api[cmd].apply(api, args || []);
45
+ }
46
+
47
+ window.addEventListener('message', function (e) {
48
+ var data = e.data;
49
+ if (!data || data.type !== 'gutterpress:cmd') return;
50
+ var id = data.id;
51
+ try {
52
+ var result = call(data.cmd, data.args);
53
+ post({ type: 'gutterpress:reply', id: id, ok: true, result: result });
54
+ } catch (err) {
55
+ post({
56
+ type: 'gutterpress:reply',
57
+ id: id,
58
+ ok: false,
59
+ error: err && err.message ? err.message : String(err),
60
+ });
61
+ }
62
+ });
63
+
64
+ window.addEventListener('pageChanged', function (e) {
65
+ post({ type: 'gutterpress:event', name: 'pageChanged', detail: e.detail });
66
+ });
67
+ window.addEventListener('renderingComplete', function (e) {
68
+ post({ type: 'gutterpress:event', name: 'renderingComplete', detail: e.detail });
69
+ });
70
+ // ADR 0005: source-position sync + click-to-source.
71
+ window.addEventListener('sourceLineChanged', function (e) {
72
+ post({ type: 'gutterpress:event', name: 'sourceLineChanged', detail: e.detail });
73
+ });
74
+ window.addEventListener('elementActivated', function (e) {
75
+ post({ type: 'gutterpress:event', name: 'elementActivated', detail: e.detail });
76
+ });
77
+
78
+ // Announce readiness as soon as previewAPI is defined.
79
+ function announceReady() {
80
+ if (window.previewAPI) {
81
+ post({ type: 'gutterpress:event', name: 'ready', detail: {} });
82
+ } else {
83
+ setTimeout(announceReady, 50);
84
+ }
85
+ }
86
+ announceReady();
87
+
88
+ // Set background color via inline style on <html> (toolbar's bg-color picker).
89
+ window.addEventListener('message', function (e) {
90
+ var data = e.data;
91
+ if (!data || data.type !== 'gutterpress:bg-color') return;
92
+ document.documentElement.style.background = data.color;
93
+ });
94
+
95
+ // Inject or replace a named <style> block in the iframe's <head>.
96
+ // { type: 'gutterpress:inject-styles', id: <string>, css: <string> }
97
+ // The <style> element gets data-gutterpress-<id>="true" so subsequent calls update
98
+ // the same block rather than appending duplicates.
99
+ window.addEventListener('message', function (e) {
100
+ var data = e.data;
101
+ if (!data || data.type !== 'gutterpress:inject-styles') return;
102
+ var attrName = 'data-gutterpress-' + data.id;
103
+ var existing = document.querySelector('style[' + attrName + ']');
104
+ if (!existing) {
105
+ existing = document.createElement('style');
106
+ existing.setAttribute(attrName, 'true');
107
+ document.head.appendChild(existing);
108
+ }
109
+ existing.textContent = data.css;
110
+ });
111
+ })();
@@ -0,0 +1,498 @@
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
+
14
+ function refreshPages() {
15
+ pages = Array.from(document.querySelectorAll('.pagedjs_page'));
16
+ return pages;
17
+ }
18
+
19
+ function clampPage(n) {
20
+ if (pages.length === 0) return 1;
21
+ var page = Number(n);
22
+ if (!Number.isFinite(page)) page = 1;
23
+ return Math.max(1, Math.min(Math.round(page), pages.length));
24
+ }
25
+
26
+ function detectVisiblePage() {
27
+ if (pages.length === 0) return 1;
28
+ // Use getBoundingClientRect (viewport-relative, post-zoom) rather than
29
+ // offsetTop. The desktop applies CSS `zoom` for fit-width; under `zoom`,
30
+ // offsetTop stays in PRE-zoom layout coords while window.scrollY is POST-zoom,
31
+ // so mixing them pinned the detected page to 1. getBoundingClientRect is
32
+ // consistent with the rendered viewport at any zoom.
33
+ var line = window.innerHeight / 3; // reference line in the upper third
34
+ var vh = window.innerHeight;
35
+ var last = 0;
36
+ for (var i = 0; i < pages.length; i++) {
37
+ var top = pages[i].getBoundingClientRect().top;
38
+ if (top <= line) last = i; // last page whose top is at/above the line
39
+ else if (top > vh) break; // well below the viewport — stop scanning
40
+ }
41
+ // In spread/two-column view a row holds two pages at the same top; report the
42
+ // FIRST page of that row (matches single view, where each row is one page).
43
+ var rowTop = pages[last].getBoundingClientRect().top;
44
+ while (last > 0 && Math.abs(pages[last - 1].getBoundingClientRect().top - rowTop) < 2) last--;
45
+ return last + 1;
46
+ }
47
+
48
+ function scrollToCurrentPage() {
49
+ if (pages.length === 0) return;
50
+ var page = clampPage(currentPage);
51
+ currentPage = page;
52
+ ignoreScrollUntil = Date.now() + 300;
53
+ pages[page - 1].scrollIntoView({ behavior: 'instant', block: 'start', inline: 'nearest' });
54
+ }
55
+
56
+ function pageStep(mode) {
57
+ return (mode || currentViewMode) === 'single' ? 1 : 2;
58
+ }
59
+
60
+ // ── Source-mapping helpers (ADR 0005) ──────────────────────────────────────
61
+ // Every block element carries data-source-line (markdown-it-source-map). These
62
+ // map rendered DOM <-> markdown source line and rendered DOM <-> paged.js page,
63
+ // which is the same-origin info the cross-iframe host cannot compute itself.
64
+ function lineOf(el) {
65
+ if (!el || !el.getAttribute) return null;
66
+ var n = parseInt(el.getAttribute('data-source-line'), 10);
67
+ return Number.isFinite(n) ? n : null;
68
+ }
69
+
70
+ function pageIndexOf(el) {
71
+ if (!el || !el.closest) return 0;
72
+ var pg = el.closest('.pagedjs_page');
73
+ if (!pg) return 0;
74
+ if (pages.length === 0) refreshPages();
75
+ var idx = pages.indexOf(pg);
76
+ return idx >= 0 ? idx + 1 : 0;
77
+ }
78
+
79
+ function sourcedBlocks() {
80
+ return Array.from(document.querySelectorAll('[data-source-line]'));
81
+ }
82
+
83
+ // data-source-line resets PER FILE, so a line number is only unambiguous when
84
+ // paired with its chapter (data-chapter-src — the source filename). These two
85
+ // helpers scope line lookups to a chapter so editor<->preview sync maps to the
86
+ // right file in a multi-chapter book.
87
+ function chapterOf(el) {
88
+ var c = el && el.closest ? el.closest('[data-chapter-src]') : null;
89
+ return c ? c.getAttribute('data-chapter-src') : null;
90
+ }
91
+
92
+ function blocksInChapter(chapter) {
93
+ if (!chapter) return sourcedBlocks();
94
+ return sourcedBlocks().filter(function (block) {
95
+ return chapterOf(block) === chapter;
96
+ });
97
+ }
98
+
99
+ // The block straddling the top of the viewport (greatest top still at/above a
100
+ // reference line just below the viewport edge); falls back to the first block
101
+ // below the fold when scrolled to the very top.
102
+ function topVisibleSourceEl() {
103
+ var blocks = sourcedBlocks();
104
+ if (blocks.length === 0) return null;
105
+ var ref = 4;
106
+ var best = null, bestTop = -Infinity;
107
+ for (var i = 0; i < blocks.length; i++) {
108
+ var top = blocks[i].getBoundingClientRect().top;
109
+ if (top <= ref && top > bestTop) { bestTop = top; best = blocks[i]; }
110
+ }
111
+ if (!best) {
112
+ for (var j = 0; j < blocks.length; j++) {
113
+ if (blocks[j].getBoundingClientRect().top >= 0) { best = blocks[j]; break; }
114
+ }
115
+ }
116
+ return best || blocks[0];
117
+ }
118
+
119
+ // {el, line} of the viewport-top source position, with the line interpolated
120
+ // within the straddling block by the viewport top's fractional distance to the
121
+ // next annotated block (source-map only annotates top-level blocks, so the
122
+ // block's start line alone can be tens of lines above the visible content).
123
+ function visibleSourcePosition() {
124
+ var el = topVisibleSourceEl();
125
+ if (!el) return null;
126
+ var line = lineOf(el);
127
+ if (line == null) return { el: el, line: null };
128
+ var ref = 4; // same reference line as topVisibleSourceEl
129
+ var top = el.getBoundingClientRect().top;
130
+ if (top < ref) {
131
+ // Find the next annotated sibling-in-flow (document order, higher line —
132
+ // a higher line also guarantees we haven't crossed into the next chapter,
133
+ // where data-source-line restarts).
134
+ var blocks = sourcedBlocks();
135
+ var idx = blocks.indexOf(el);
136
+ for (var i = idx + 1; i < blocks.length; i++) {
137
+ var nl = lineOf(blocks[i]);
138
+ if (nl == null) continue;
139
+ if (nl <= line) break;
140
+ var nextTop = blocks[i].getBoundingClientRect().top;
141
+ if (nextTop > top) {
142
+ var f = Math.max(0, Math.min(1, (ref - top) / (nextTop - top)));
143
+ line = line + Math.round(f * (nl - line));
144
+ }
145
+ break;
146
+ }
147
+ }
148
+ return { el: el, line: line };
149
+ }
150
+
151
+ // Resolve a {line, chapter?} target to its enclosing annotated block PLUS the
152
+ // following annotated block, so callers can interpolate within long blocks
153
+ // (markdown-it-source-map only annotates top-level *_open tokens, so a target
154
+ // line deep inside a list/table/paragraph would otherwise snap to the block's
155
+ // start line — measured 35-line snap-backs on real chapters).
156
+ function resolveLinePosition(target) {
157
+ var line = Number(target.line);
158
+ var blocks = blocksInChapter(target.chapter);
159
+ var best = null, bestLine = -Infinity, next = null, nextLine = Infinity;
160
+ for (var i = 0; i < blocks.length; i++) {
161
+ var l = lineOf(blocks[i]);
162
+ if (l == null) continue;
163
+ if (l <= line && l > bestLine) { bestLine = l; best = blocks[i]; }
164
+ else if (l > line && l < nextLine) { nextLine = l; next = blocks[i]; }
165
+ }
166
+ if (!best) return blocks[0] ? { el: blocks[0], line: lineOf(blocks[0]), nextEl: null, nextLine: null } : null;
167
+ return { el: best, line: bestLine, nextEl: next, nextLine: next ? nextLine : null };
168
+ }
169
+
170
+ // Resolve a scrollTo/highlight target ({line}|{id}|{selector}|{page} or a bare
171
+ // line number) to a DOM element.
172
+ function resolveTarget(target) {
173
+ if (target == null) return null;
174
+ if (typeof target === 'number') target = { line: target };
175
+ if (target.selector) { try { return document.querySelector(target.selector); } catch (_e) { return null; } }
176
+ if (target.id) return document.getElementById(target.id);
177
+ if (target.page != null) { refreshPages(); return pages[clampPage(target.page) - 1] || null; }
178
+ if (target.line != null) {
179
+ var pos = resolveLinePosition(target);
180
+ return pos ? pos.el : null;
181
+ }
182
+ return null;
183
+ }
184
+
185
+ var api = {
186
+ getTotalPages: function () { refreshPages(); return pages.length; },
187
+ getCurrentPage: function () { return currentPage; },
188
+ goToPage: function (n) {
189
+ refreshPages();
190
+ currentPage = clampPage(n);
191
+ scrollToCurrentPage();
192
+ return api.notifyPageChange();
193
+ },
194
+ getPageDimensions: function () {
195
+ refreshPages();
196
+ var page = pages[0] || null;
197
+ var pagesEl = document.querySelector('.pagedjs_pages');
198
+ if (!page) return null;
199
+ return {
200
+ width: currentViewMode === 'single' ? page.offsetWidth : (pagesEl ? pagesEl.scrollWidth : page.offsetWidth),
201
+ height: page.offsetHeight
202
+ };
203
+ },
204
+ firstPage: function () { return api.goToPage(1); },
205
+ prevPage: function (mode) { return api.goToPage(currentPage - pageStep(mode)); },
206
+ nextPage: function (mode) { return api.goToPage(currentPage + pageStep(mode)); },
207
+ lastPage: function () { refreshPages(); return api.goToPage(pages.length); },
208
+ setViewMode: function (mode) {
209
+ refreshPages();
210
+ currentViewMode = mode || 'two-column';
211
+ document.body.classList.remove('view-single', 'view-spread', 'view-two-column');
212
+ if (mode) document.body.classList.add('view-' + mode);
213
+ scrollToCurrentPage();
214
+ return api.notifyPageChange();
215
+ },
216
+ setZoom: function (z) {
217
+ document.documentElement.style.setProperty('--gutterpress-zoom', z);
218
+ },
219
+ toggleDebugMode: function () {
220
+ debugMode = !debugMode;
221
+ document.body.classList.toggle('debug', debugMode);
222
+ return debugMode;
223
+ },
224
+ notifyPageChange: function () {
225
+ var detail = { currentPage: api.getCurrentPage(), totalPages: pages.length };
226
+ window.dispatchEvent(new CustomEvent('pageChanged', { detail: detail }));
227
+ return detail;
228
+ },
229
+ notifyRenderingComplete: function () {
230
+ window.dispatchEvent(new CustomEvent('renderingComplete', {
231
+ detail: { totalPages: pages.length }
232
+ }));
233
+ },
234
+ // Re-read the page list and recompute the current page from the scroll
235
+ // position, then notify. Hosts may call this after replacing paginated DOM
236
+ // so cached page counters and scroll synchronization stay current.
237
+ refresh: function () {
238
+ refreshPages();
239
+ currentPage = detectVisiblePage();
240
+ return api.notifyPageChange();
241
+ },
242
+
243
+ // ── ADR 0005 generic primitives ─────────────────────────────────────────
244
+ // Bumped whenever a command/event is added so a hot-updated SPA can
245
+ // feature-detect against an older bundled lib.
246
+ getProtocolVersion: function () { return 2; },
247
+
248
+ // Heading tree with page + source line — powers chapter jump (UX-013), TOC,
249
+ // minimap, scrollspy. Page math needs same-origin paged.js access, so it
250
+ // lives here rather than being derived host-side.
251
+ getOutline: function () {
252
+ refreshPages();
253
+ var hs = Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,h6'));
254
+ var out = [];
255
+ for (var i = 0; i < hs.length; i++) {
256
+ var h = hs[i];
257
+ var text = (h.textContent || '').trim();
258
+ if (!text) continue;
259
+ out.push({
260
+ level: parseInt(h.tagName.charAt(1), 10),
261
+ text: text,
262
+ id: h.id || null,
263
+ sourceLine: lineOf(h),
264
+ chapter: chapterOf(h),
265
+ page: pageIndexOf(h),
266
+ index: i
267
+ });
268
+ }
269
+ return out;
270
+ },
271
+
272
+ // Single anchored-jump primitive: target {line}|{id}|{selector}|{page}.
273
+ // Returns the resolved {page, sourceLine}. Suppresses the scroll-driven
274
+ // pageChanged/sourceLineChanged echo so host-driven jumps don't loop back.
275
+ scrollTo: function (target, opts) {
276
+ opts = opts || {};
277
+ if (typeof target === 'number') target = { line: target };
278
+ var el = null;
279
+ var fraction = 0;
280
+ var nextEl = null;
281
+ if (target && target.line != null && target.selector == null && target.id == null && target.page == null) {
282
+ var pos = resolveLinePosition(target);
283
+ if (pos) {
284
+ el = pos.el;
285
+ if (pos.nextEl && pos.nextLine != null && pos.nextLine > pos.line) {
286
+ nextEl = pos.nextEl;
287
+ fraction = (Number(target.line) - pos.line) / (pos.nextLine - pos.line);
288
+ fraction = Math.max(0, Math.min(1, fraction));
289
+ }
290
+ }
291
+ } else {
292
+ el = resolveTarget(target);
293
+ }
294
+ if (!el) return null;
295
+ ignoreScrollUntil = Date.now() + 350;
296
+ var behavior = opts.smooth ? 'smooth' : 'instant';
297
+ el.scrollIntoView({
298
+ behavior: fraction > 0 ? 'instant' : behavior,
299
+ block: opts.block || 'start',
300
+ inline: 'nearest'
301
+ });
302
+ if (fraction > 0 && nextEl) {
303
+ // Interpolate within the block: nudge the scroll by the target line's
304
+ // fractional distance between this block's top and the NEXT annotated
305
+ // block's top. Only when the next block is genuinely lower on screen —
306
+ // a facing-page or next-column neighbour (top <= ours) degrades to the
307
+ // plain block-top scroll, which is the pre-existing behaviour.
308
+ var top = el.getBoundingClientRect().top;
309
+ var nextTop = nextEl.getBoundingClientRect().top;
310
+ if (nextTop > top) {
311
+ window.scrollBy({ top: fraction * (nextTop - top), behavior: behavior });
312
+ }
313
+ }
314
+ var page = pageIndexOf(el);
315
+ if (page) currentPage = page;
316
+ return { page: page || currentPage, sourceLine: lineOf(el) };
317
+ },
318
+
319
+ // Source line + page of the block at the top of the viewport (host scroll
320
+ // position read for preview->editor sync).
321
+ getVisibleSource: function () {
322
+ var pos = visibleSourcePosition();
323
+ return pos ? { sourceLine: pos.line, chapter: chapterOf(pos.el), page: pageIndexOf(pos.el) } : null;
324
+ },
325
+
326
+ // Generic, read-only DOM extraction. fields: 'text'|'id'|'sourceLine'|
327
+ // 'page'|'tag'|'rectTop'|{attr:'name'}. No eval, no innerHTML — this is the
328
+ // forward-compat hook that lets the SPA build find/figure-list/etc. with no
329
+ // further lib change.
330
+ queryDom: function (spec) {
331
+ spec = spec || {};
332
+ if (!spec.selector) return [];
333
+ var fields = spec.fields || ['text'];
334
+ var els;
335
+ try { els = Array.from(document.querySelectorAll(spec.selector)); }
336
+ catch (_e) { return []; }
337
+ if (spec.limit && els.length > spec.limit) els = els.slice(0, spec.limit);
338
+ return els.map(function (el) {
339
+ var row = {};
340
+ for (var i = 0; i < fields.length; i++) {
341
+ var f = fields[i];
342
+ if (f === 'text') row.text = (el.textContent || '').trim();
343
+ else if (f === 'id') row.id = el.id || null;
344
+ else if (f === 'sourceLine') row.sourceLine = lineOf(el);
345
+ else if (f === 'page') row.page = pageIndexOf(el);
346
+ else if (f === 'chapter') row.chapter = chapterOf(el);
347
+ else if (f === 'tag') row.tag = el.tagName.toLowerCase();
348
+ else if (f === 'rectTop') row.rectTop = el.getBoundingClientRect().top;
349
+ else if (f && typeof f === 'object' && f.attr) row['attr:' + f.attr] = el.getAttribute(f.attr);
350
+ }
351
+ return row;
352
+ });
353
+ },
354
+
355
+ // Toggle a marker class on matched elements (DOM write inside the frame).
356
+ // spec: {line?|id?|selector?, group?, scroll?, transient?, transientMs?}.
357
+ // Powers find-in-page, editor-cursor echo, annotations.
358
+ highlight: function (spec) {
359
+ spec = spec || {};
360
+ var group = spec.group || 'default';
361
+ var els = [];
362
+ if (spec.selector) { try { els = Array.from(document.querySelectorAll(spec.selector)); } catch (_e) {} }
363
+ else if (spec.id) { var byId = document.getElementById(spec.id); if (byId) els = [byId]; }
364
+ else if (spec.line != null) { var one = resolveTarget({ line: spec.line }); if (one) els = [one]; }
365
+ for (var i = 0; i < els.length; i++) {
366
+ els[i].classList.add('gutterpress-hl');
367
+ els[i].setAttribute('data-gutterpress-hl-group', group);
368
+ }
369
+ if (spec.scroll && els[0]) {
370
+ ignoreScrollUntil = Date.now() + 350;
371
+ els[0].scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });
372
+ }
373
+ if (spec.transient && els.length) {
374
+ setTimeout(function () {
375
+ for (var j = 0; j < els.length; j++) {
376
+ if (els[j].getAttribute('data-gutterpress-hl-group') === group) {
377
+ els[j].classList.remove('gutterpress-hl');
378
+ els[j].removeAttribute('data-gutterpress-hl-group');
379
+ }
380
+ }
381
+ }, spec.transientMs || 1200);
382
+ }
383
+ return { count: els.length };
384
+ },
385
+
386
+ clearHighlights: function (group) {
387
+ var sel = group ? '.gutterpress-hl[data-gutterpress-hl-group="' + group + '"]' : '.gutterpress-hl';
388
+ var els = Array.from(document.querySelectorAll(sel));
389
+ for (var i = 0; i < els.length; i++) {
390
+ els[i].classList.remove('gutterpress-hl');
391
+ els[i].removeAttribute('data-gutterpress-hl-group');
392
+ }
393
+ return { cleared: els.length };
394
+ }
395
+ };
396
+
397
+ window.previewAPI = api;
398
+
399
+ // Default highlight style (preview-only; never part of the PDF build path).
400
+ // Guarded so a minimal/headless DOM (e.g. unit-test harness) can't crash here.
401
+ if (typeof document.createElement === 'function') {
402
+ try {
403
+ var hlStyle = document.createElement('style');
404
+ hlStyle.textContent =
405
+ '.gutterpress-hl{outline:2px solid var(--gutterpress-hl-color,#4ea1ff);outline-offset:2px;' +
406
+ 'background:var(--gutterpress-hl-bg,rgba(78,161,255,.14));' +
407
+ 'transition:outline-color .2s,background .2s;}';
408
+ (document.head || document.documentElement).appendChild(hlStyle);
409
+ } catch (_e) { /* non-fatal: highlight just renders unstyled */ }
410
+ }
411
+
412
+ // Click-to-source: emit elementActivated when the user clicks a source-mapped
413
+ // block. Never preventDefault (links/selection keep working); the host decides
414
+ // whether to act. (ADR 0005)
415
+ if (typeof document.addEventListener === 'function') {
416
+ document.addEventListener('click', function (e) {
417
+ var el = e.target && e.target.closest ? e.target.closest('[data-source-line]') : null;
418
+ if (!el) return;
419
+ window.dispatchEvent(new CustomEvent('elementActivated', {
420
+ detail: { sourceLine: lineOf(el), chapter: chapterOf(el), id: el.id || null, tag: el.tagName.toLowerCase() }
421
+ }));
422
+ }, true);
423
+ }
424
+
425
+ var observedPageCount = 0;
426
+ var pageObserverQueued = false;
427
+ function publishObservedPageCount() {
428
+ pageObserverQueued = false;
429
+ if (window.__PAGED_RENDERED__ === true) return;
430
+ var count = refreshPages().length;
431
+ if (count > observedPageCount) {
432
+ observedPageCount = count;
433
+ window.dispatchEvent(new CustomEvent('pageChanged', {
434
+ detail: { currentPage: count, totalPages: count }
435
+ }));
436
+ }
437
+ }
438
+ var pageObserver = new MutationObserver(function () {
439
+ if (window.__PAGED_RENDERED__ === true) return;
440
+ if (pageObserverQueued) return;
441
+ pageObserverQueued = true;
442
+ window.requestAnimationFrame(publishObservedPageCount);
443
+ });
444
+
445
+ function startPageObserver() {
446
+ var target = document.body || document.documentElement;
447
+ if (!target) return false;
448
+ pageObserver.observe(target, { childList: true, subtree: true });
449
+ return true;
450
+ }
451
+
452
+ if (!startPageObserver()) {
453
+ document.addEventListener('DOMContentLoaded', function onReady() {
454
+ document.removeEventListener('DOMContentLoaded', onReady);
455
+ startPageObserver();
456
+ });
457
+ }
458
+
459
+ // Scroll tracking
460
+ var scrollTimer = null;
461
+ var lastSourceLine = -1;
462
+ window.addEventListener('scroll', function () {
463
+ if (pages.length === 0) refreshPages();
464
+ if (pages.length === 0) return;
465
+ if (scrollTimer) clearTimeout(scrollTimer);
466
+ scrollTimer = setTimeout(function () {
467
+ if (Date.now() < ignoreScrollUntil) return;
468
+ var page = detectVisiblePage();
469
+ if (page !== currentPage) {
470
+ currentPage = page;
471
+ api.notifyPageChange();
472
+ }
473
+ // Emit finer-grained source position for editor sync (ADR 0005).
474
+ var pos = visibleSourcePosition();
475
+ var sl = pos ? pos.line : null;
476
+ if (sl != null && sl !== lastSourceLine) {
477
+ lastSourceLine = sl;
478
+ window.dispatchEvent(new CustomEvent('sourceLineChanged', {
479
+ detail: { sourceLine: sl, chapter: chapterOf(pos.el), page: pageIndexOf(pos.el) }
480
+ }));
481
+ }
482
+ }, 150);
483
+ });
484
+
485
+ // Paged.js calls this when rendering is complete
486
+ window.PagedConfig = window.PagedConfig || {};
487
+ window.PagedConfig.after = function (flow) {
488
+ refreshPages();
489
+ observedPageCount = pages.length;
490
+ pageObserver.disconnect();
491
+ currentPage = 1;
492
+ ignoreScrollUntil = Date.now() + 300;
493
+ window.scrollTo(0, 0);
494
+ console.log('Paged.js rendered ' + pages.length + ' pages');
495
+ api.notifyRenderingComplete();
496
+ setTimeout(api.notifyPageChange, 0);
497
+ };
498
+ })();