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.
- package/README.md +422 -0
- package/dist/CGATS21_CRPC1-g0e3k7kr.icc +0 -0
- package/dist/api/index.d.ts +76 -0
- package/dist/api/index.js +272 -0
- package/dist/assets/preview/scripts/pagedjs-bridge.d.ts +1 -0
- package/dist/assets/preview/scripts/pagedjs-interface.d.ts +1 -0
- package/dist/assets/preview/scripts/preview-shell.d.ts +1 -0
- package/dist/audit-1jrwwacz.js +66 -0
- package/dist/build-5s92kgpc.js +77 -0
- package/dist/chapter-01-6fn9pvd0.md +31 -0
- package/dist/chapter-01-7jwqg1ct.md +20 -0
- package/dist/chapter-01-jj8ftn4c.md +29 -0
- package/dist/chapter-01-rkpyvjna.md +26 -0
- package/dist/checks/asset/approved-fonts.d.ts +3 -0
- package/dist/checks/asset/extensions.d.ts +41 -0
- package/dist/checks/asset/font-license.d.ts +3 -0
- package/dist/checks/asset/image-alpha.d.ts +3 -0
- package/dist/checks/asset/image-color-space.d.ts +3 -0
- package/dist/checks/asset/image-file-size.d.ts +3 -0
- package/dist/checks/asset/image-resolution.d.ts +3 -0
- package/dist/checks/asset/image-tac.d.ts +3 -0
- package/dist/checks/asset/index.d.ts +7 -0
- package/dist/checks/formatter.d.ts +3 -0
- package/dist/checks/heuristic/index.d.ts +4 -0
- package/dist/checks/heuristic/layer-count.d.ts +3 -0
- package/dist/checks/heuristic/placement-variance.d.ts +3 -0
- package/dist/checks/heuristic/section-density.d.ts +3 -0
- package/dist/checks/heuristic/text-density.d.ts +3 -0
- package/dist/checks/pdf/bleed.d.ts +3 -0
- package/dist/checks/pdf/bookmarks.d.ts +3 -0
- package/dist/checks/pdf/color-spaces.d.ts +3 -0
- package/dist/checks/pdf/cross-refs.d.ts +3 -0
- package/dist/checks/pdf/embedded-fonts.d.ts +3 -0
- package/dist/checks/pdf/image-resolution.d.ts +3 -0
- package/dist/checks/pdf/index.d.ts +15 -0
- package/dist/checks/pdf/ink-coverage.d.ts +3 -0
- package/dist/checks/pdf/page-labels.d.ts +3 -0
- package/dist/checks/pdf/page-size.d.ts +3 -0
- package/dist/checks/pdf/pdfx-markers.d.ts +3 -0
- package/dist/checks/pdf/pdfx-metadata.d.ts +3 -0
- package/dist/checks/pdf/pdfx-structure.d.ts +6 -0
- package/dist/checks/pdf/qpdf-structure.d.ts +3 -0
- package/dist/checks/pdf/rasterized-pages.d.ts +3 -0
- package/dist/checks/pdf/toc-links.d.ts +3 -0
- package/dist/checks/pdf/transparency.d.ts +3 -0
- package/dist/checks/policy.d.ts +61 -0
- package/dist/checks/register-builtins.d.ts +17 -0
- package/dist/checks/registry.d.ts +40 -0
- package/dist/checks/runner.d.ts +25 -0
- package/dist/checks/source/accessibility-alt-text.d.ts +3 -0
- package/dist/checks/source/accessibility-heading-order.d.ts +3 -0
- package/dist/checks/source/config-file.d.ts +8 -0
- package/dist/checks/source/htmlhint.d.ts +3 -0
- package/dist/checks/source/index.d.ts +6 -0
- package/dist/checks/source/local-refs.d.ts +3 -0
- package/dist/checks/source/markdownlint.d.ts +3 -0
- package/dist/checks/source/stylelint.d.ts +3 -0
- package/dist/checks/tool-check.d.ts +23 -0
- package/dist/checks/types.d.ts +52 -0
- package/dist/cli-1tyj4bk3.js +124 -0
- package/dist/cli-37x76zdn.js +4 -0
- package/dist/cli-506tg37g.js +26 -0
- package/dist/cli-5bxx0c4y.js +464 -0
- package/dist/cli-7ykx7pp0.js +12378 -0
- package/dist/cli-h7phg3ka.js +746 -0
- package/dist/cli-hvqjs342.js +284 -0
- package/dist/cli-yja077f6.js +92 -0
- package/dist/cli.js +157 -0
- package/dist/constants.d.ts +17 -0
- package/dist/doctor-25zjt8dz.js +57 -0
- package/dist/favicon-wkbm9cjn.ico +0 -0
- package/dist/git-http-yrb4ag6z.js +17 -0
- package/dist/index-1tyj4bk3.js +124 -0
- package/dist/index-37x76zdn.js +4 -0
- package/dist/index-3h3fef7r.js +15218 -0
- package/dist/index-5bxx0c4y.js +464 -0
- package/dist/index-yja077f6.js +92 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +310 -0
- package/dist/lib/app-heartbeat.d.ts +102 -0
- package/dist/lib/asset-inline.d.ts +73 -0
- package/dist/lib/browser-pool.d.ts +17 -0
- package/dist/lib/build-error.d.ts +52 -0
- package/dist/lib/build-fingerprint.d.ts +15 -0
- package/dist/lib/build-preflight.d.ts +43 -0
- package/dist/lib/build-runner.d.ts +161 -0
- package/dist/lib/build-staging.d.ts +64 -0
- package/dist/lib/chromium.d.ts +20 -0
- package/dist/lib/cli-args.d.ts +45 -0
- package/dist/lib/desktop.d.ts +14 -0
- package/dist/lib/diagnostics.d.ts +64 -0
- package/dist/lib/embedded-assets.d.ts +29 -0
- package/dist/lib/exec.d.ts +84 -0
- package/dist/lib/fetch-timeout.d.ts +41 -0
- package/dist/lib/ghostscript.d.ts +48 -0
- package/dist/lib/host-policy.d.ts +75 -0
- package/dist/lib/image-inspect.d.ts +32 -0
- package/dist/lib/install-hints.d.ts +21 -0
- package/dist/lib/lint-runner.d.ts +10 -0
- package/dist/lib/manifest-config.d.ts +31 -0
- package/dist/lib/manifest-doc.d.ts +25 -0
- package/dist/lib/manifest.d.ts +41 -0
- package/dist/lib/markdown/assemble.d.ts +78 -0
- package/dist/lib/markdown/chapter-id.d.ts +24 -0
- package/dist/lib/markdown/images.d.ts +34 -0
- package/dist/lib/markdown/index.d.ts +78 -0
- package/dist/lib/markdown/markdown-it-paged.d.ts +30 -0
- package/dist/lib/markdown/plugins.d.ts +69 -0
- package/dist/lib/markdown/renderer.d.ts +111 -0
- package/dist/lib/npm-plugin-installer.d.ts +33 -0
- package/dist/lib/open-path.d.ts +5 -0
- package/dist/lib/output-paths.d.ts +23 -0
- package/dist/lib/pagedjs-marker.d.ts +42 -0
- package/dist/lib/pagedjs.d.ts +26 -0
- package/dist/lib/pagination.d.ts +169 -0
- package/dist/lib/pdf-inspect.d.ts +123 -0
- package/dist/lib/pdf-parse.d.ts +57 -0
- package/dist/lib/plugin-manager.d.ts +108 -0
- package/dist/lib/plugin-vendor.d.ts +107 -0
- package/dist/lib/presets.d.ts +45 -0
- package/dist/lib/printsafe.d.ts +17 -0
- package/dist/lib/project-scaffold.d.ts +116 -0
- package/dist/lib/project-source.d.ts +140 -0
- package/dist/lib/project-templates.d.ts +49 -0
- package/dist/lib/publish/butler.d.ts +10 -0
- package/dist/lib/publish/command-runner.d.ts +18 -0
- package/dist/lib/publish/connect.d.ts +23 -0
- package/dist/lib/publish/manifest-publish.d.ts +9 -0
- package/dist/lib/publish/providers/azure-swa.d.ts +3 -0
- package/dist/lib/publish/providers/drivethrurpg.d.ts +10 -0
- package/dist/lib/publish/providers/guided.d.ts +3 -0
- package/dist/lib/publish/providers/itch.d.ts +5 -0
- package/dist/lib/publish/providers/kdp.d.ts +9 -0
- package/dist/lib/publish/providers/shopify.d.ts +5 -0
- package/dist/lib/publish/registry.d.ts +5 -0
- package/dist/lib/publish/run-publish.d.ts +30 -0
- package/dist/lib/publish/selections.d.ts +25 -0
- package/dist/lib/publish/types.d.ts +258 -0
- package/dist/lib/remote-auth/clone.d.ts +87 -0
- package/dist/lib/remote-auth/conflict-resolution.d.ts +29 -0
- package/dist/lib/remote-auth/diagnose.d.ts +77 -0
- package/dist/lib/remote-auth/generic-auth.d.ts +97 -0
- package/dist/lib/remote-auth/git-http.d.ts +85 -0
- package/dist/lib/remote-auth/github-auth.d.ts +69 -0
- package/dist/lib/remote-auth/github-repos.d.ts +64 -0
- package/dist/lib/remote-auth/operation-log.d.ts +66 -0
- package/dist/lib/remote-auth/recovery/abort-interrupted-operation.d.ts +103 -0
- package/dist/lib/remote-auth/recovery/backup.d.ts +113 -0
- package/dist/lib/remote-auth/recovery/classify.d.ts +109 -0
- package/dist/lib/remote-auth/recovery/context.d.ts +47 -0
- package/dist/lib/remote-auth/recovery/dispatch.d.ts +33 -0
- package/dist/lib/remote-auth/recovery/failsafe.d.ts +33 -0
- package/dist/lib/remote-auth/recovery/inspect.d.ts +74 -0
- package/dist/lib/remote-auth/recovery/manual-guidance.d.ts +28 -0
- package/dist/lib/remote-auth/recovery/outcome-mapping.d.ts +58 -0
- package/dist/lib/remote-auth/recovery/policy.d.ts +47 -0
- package/dist/lib/remote-auth/recovery/recover-auth.d.ts +42 -0
- package/dist/lib/remote-auth/recovery/recover-binary-conflict.d.ts +37 -0
- package/dist/lib/remote-auth/recovery/recover-corrupt-index.d.ts +40 -0
- package/dist/lib/remote-auth/recovery/recover-detached-head.d.ts +70 -0
- package/dist/lib/remote-auth/recovery/recover-interrupted-cherry-pick.d.ts +23 -0
- package/dist/lib/remote-auth/recovery/recover-interrupted-merge.d.ts +28 -0
- package/dist/lib/remote-auth/recovery/recover-interrupted-rebase.d.ts +39 -0
- package/dist/lib/remote-auth/recovery/recover-merge-conflict.d.ts +34 -0
- package/dist/lib/remote-auth/recovery/recover-missing-git-dir.d.ts +37 -0
- package/dist/lib/remote-auth/recovery/recover-missing-objects.d.ts +56 -0
- package/dist/lib/remote-auth/recovery/recover-network.d.ts +34 -0
- package/dist/lib/remote-auth/recovery/recover-non-fast-forward.d.ts +27 -0
- package/dist/lib/remote-auth/recovery/recover-stale-lock.d.ts +76 -0
- package/dist/lib/remote-auth/recovery/recover-unrelated-histories.d.ts +44 -0
- package/dist/lib/remote-auth/recovery/recover-wrong-remote.d.ts +35 -0
- package/dist/lib/remote-auth/recovery/types.d.ts +235 -0
- package/dist/lib/remote-auth/resolution-plan.d.ts +64 -0
- package/dist/lib/remote-auth/sync-messages.d.ts +26 -0
- package/dist/lib/remote-auth/sync-types.d.ts +225 -0
- package/dist/lib/remote-auth/sync.d.ts +45 -0
- package/dist/lib/remote-auth/test-access.d.ts +47 -0
- package/dist/lib/remote-auth/test-support/git-http-server.d.ts +71 -0
- package/dist/lib/remote-auth/token-store.d.ts +95 -0
- package/dist/lib/remote-auth/transport.d.ts +105 -0
- package/dist/lib/slug.d.ts +28 -0
- package/dist/lib/snippets.d.ts +39 -0
- package/dist/lib/source-provider.d.ts +244 -0
- package/dist/lib/static-serve.d.ts +54 -0
- package/dist/lib/style-resolver.d.ts +30 -0
- package/dist/lib/theme-import.d.ts +70 -0
- package/dist/lib/theme-manager.d.ts +128 -0
- package/dist/lib/tool-probe.d.ts +37 -0
- package/dist/lib/validation-exec.d.ts +47 -0
- package/dist/lib/validation-profile.d.ts +18 -0
- package/dist/lib/version.d.ts +9 -0
- package/dist/lint-f63nxjfj.js +65 -0
- package/dist/manifest-n1gh3qw5.yaml +9 -0
- package/dist/manifest-ryyg71nf.yaml +9 -0
- package/dist/manifest.schema-rn1dpdpn.json +564 -0
- package/dist/new-7b08a42h.js +105 -0
- package/dist/paged.polyfill-n95pbxfn.js +33288 -0
- package/dist/pagedjs-bridge-vn4hk9fx.js +111 -0
- package/dist/pagedjs-interface-80g3xa8p.js +498 -0
- package/dist/platform.d.ts +100 -0
- package/dist/plugin-wf2cbmwc.js +129 -0
- package/dist/preflight-tg4mpz1h.js +222 -0
- package/dist/preview/file-watcher.d.ts +174 -0
- package/dist/preview/http-server.d.ts +45 -0
- package/dist/preview/lifecycle.d.ts +67 -0
- package/dist/preview/server-context.d.ts +36 -0
- package/dist/preview-h2bwbb2x.js +102 -0
- package/dist/preview-shell-rghtfmc8.js +155 -0
- package/dist/project-source-0tg0r6vm.js +17 -0
- package/dist/publish-ztaet53r.js +233 -0
- package/dist/render.d.ts +20 -0
- package/dist/render.js +682 -0
- package/dist/repair-jkp4n3j8.js +171 -0
- package/dist/schema/manifest.types.d.ts +218 -0
- package/dist/server.d.ts +34 -0
- package/dist/source-provider-hp96b5jf.js +46 -0
- package/dist/test-helpers/testkit.d.ts +91 -0
- package/dist/theme-0yzvthe8.css +65 -0
- package/dist/theme-570zmh2t.css +58 -0
- package/dist/theme-6a2fngc8.json +6 -0
- package/dist/theme-cd6efzjw.json +6 -0
- package/dist/theme-nya4nqh6.css +70 -0
- package/dist/theme-tqfypc1w.json +6 -0
- package/dist/theme-xt0hjb1b.json +6 -0
- package/dist/theme-zz2ktzqs.css +60 -0
- package/dist/types.d.ts +31 -0
- package/dist/utils/file-utils.d.ts +53 -0
- package/dist/utils/logger.d.ts +59 -0
- package/dist/validate-k2drs19j.js +99 -0
- 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
|
+
})();
|