gutterpress 0.11.17 → 0.11.18-alpha.10
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/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +4 -2
- package/dist/{build-gxy185pv.js → build-2tgs5j4d.js} +4 -4
- package/dist/{cli-zfgvssqz.js → cli-d0cq87s5.js} +1 -1
- package/dist/{cli-a4qtctrw.js → cli-gf1ywzeg.js} +411 -50
- package/dist/{cli-ycbg7mxk.js → cli-ptez7zcc.js} +53 -5
- package/dist/{cli-8nw6d915.js → cli-ytck518d.js} +1 -1
- package/dist/cli.js +12 -12
- package/dist/{doctor-fygmzevd.js → doctor-y4yg51zy.js} +2 -2
- package/dist/engine/compiler/build.d.ts +1 -1
- package/dist/{engine-yf2w9hj7.js → engine-wtnakejg.js} +1 -1
- package/dist/{engine-5d1rpxxq.js → engine-zy6fzs5a.js} +2 -2
- package/dist/{ext-c48s56jm.js → ext-t2sye4as.js} +4 -4
- package/dist/{gutterpress-viewer-ez7k4ha7.js → gutterpress-viewer-k4mpqjh0.js} +406 -14
- package/dist/{index-3k3fyrec.js → index-cf1kanp0.js} +454 -52
- package/dist/{index-vmcfahe2.js → index-jt552wtf.js} +5 -5
- package/dist/{index-09h3n36d.js → index-n8vs6pss.js} +51 -2
- package/dist/index.js +5 -3
- package/dist/lib/markdown/block-names.d.ts +82 -0
- package/dist/lib/markdown/renderer.d.ts +3 -2
- package/dist/lib/markdown/table-align.d.ts +32 -0
- package/dist/lib/presets.d.ts +11 -0
- package/dist/lib/printsafe.d.ts +1 -0
- package/dist/lib/remote-auth/sync.d.ts +21 -0
- package/dist/lib/unknown-classes.d.ts +12 -0
- package/dist/{manifest.schema-gh0jjve9.json → manifest.schema-5pzhscqh.json} +7 -1
- package/dist/{new-a9m2jwyy.js → new-pbd5zrds.js} +4 -4
- package/dist/{preflight-w3tnbtb8.js → preflight-vnkb8tj7.js} +4 -4
- package/dist/preview/file-watcher.d.ts +4 -1
- package/dist/{preview-bridge-8jhd1wpb.js → preview-bridge-evecsh4w.js} +5 -1
- package/dist/{preview-7z8tt0b4.js → preview-g1hp0jt8.js} +4 -4
- package/dist/{preview-interface-c44qk17f.js → preview-interface-2n1d9pbf.js} +77 -1
- package/dist/{preview-shell-xwn84kqr.js → preview-shell-hyp7g158.js} +9 -0
- package/dist/{publish-bbftdxh2.js → publish-vkbmf3tj.js} +4 -4
- package/dist/render.js +199 -28
- package/dist/schema/manifest.types.d.ts +11 -0
- package/dist/{validate-x1ha1gqj.js → validate-q1xj2qxn.js} +4 -4
- package/package.json +1 -1
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
__export(exports_fragment, {
|
|
20
20
|
wrapGeometry: () => wrapGeometry,
|
|
21
21
|
waitForLayoutReady: () => waitForLayoutReady,
|
|
22
|
+
versoShiftPt: () => versoShiftPt,
|
|
22
23
|
synthesizeColumnBreaks: () => synthesizeColumnBreaks,
|
|
23
24
|
stripMetrics: () => stripMetrics,
|
|
24
25
|
strideOf: () => strideOf,
|
|
@@ -34,7 +35,9 @@
|
|
|
34
35
|
loadStyleSources: () => loadStyleSources,
|
|
35
36
|
injectViewerCss: () => injectViewerCss,
|
|
36
37
|
injectBreakMapping: () => injectBreakMapping,
|
|
38
|
+
indexInStrip: () => indexInStrip,
|
|
37
39
|
fragmentDocument: () => fragmentDocument,
|
|
40
|
+
cssZoomOf: () => cssZoomOf,
|
|
38
41
|
contentEdgeRect: () => contentEdgeRect,
|
|
39
42
|
compensateRepeatedHeaders: () => compensateRepeatedHeaders,
|
|
40
43
|
compensateRectoBreaks: () => compensateRectoBreaks,
|
|
@@ -106,13 +109,20 @@
|
|
|
106
109
|
below is already sized to the run's full width and owns the actual
|
|
107
110
|
clipping/visibility, so the strip does not need to. */
|
|
108
111
|
.gp-strip {
|
|
112
|
+
/* Column pitch less the content width. \`--gp-verso-shift\` is set only on a
|
|
113
|
+
book with mirrored margins: decorate.ts slides each verso's sheet by it,
|
|
114
|
+
and growing the pitch by the same amount keeps the gap between a verso and
|
|
115
|
+
the recto it faces at the sheet gap (the gap across the page turn gets
|
|
116
|
+
twice the shift instead). Without it, it is 0px and nothing changes. */
|
|
117
|
+
--gp-col-gap: calc(
|
|
118
|
+
var(--gp-margin-right) + var(--gp-margin-left) + var(--gp-sheet-gap) +
|
|
119
|
+
var(--gp-verso-shift, 0px)
|
|
120
|
+
);
|
|
109
121
|
position: relative;
|
|
110
122
|
width: var(--gp-content-w);
|
|
111
123
|
height: var(--gp-content-h);
|
|
112
124
|
column-width: var(--gp-content-w);
|
|
113
|
-
column-gap:
|
|
114
|
-
var(--gp-margin-right) + var(--gp-margin-left) + var(--gp-sheet-gap)
|
|
115
|
-
);
|
|
125
|
+
column-gap: var(--gp-col-gap);
|
|
116
126
|
column-fill: auto;
|
|
117
127
|
overflow: visible;
|
|
118
128
|
margin: 0;
|
|
@@ -174,9 +184,7 @@
|
|
|
174
184
|
);
|
|
175
185
|
width: calc(
|
|
176
186
|
var(--gp-content-w) * var(--gp-wrap-cols, 2) +
|
|
177
|
-
(
|
|
178
|
-
var(--gp-margin-right) + var(--gp-margin-left) + var(--gp-sheet-gap)
|
|
179
|
-
) * (var(--gp-wrap-cols, 2) - 1)
|
|
187
|
+
var(--gp-col-gap) * (var(--gp-wrap-cols, 2) - 1)
|
|
180
188
|
);
|
|
181
189
|
}
|
|
182
190
|
|
|
@@ -267,6 +275,107 @@
|
|
|
267
275
|
.gp-marginbox[data-align="center"] > .gp-marginbox-content { justify-content: center; }
|
|
268
276
|
.gp-marginbox[data-align="end"] > .gp-marginbox-content { justify-content: flex-end; }
|
|
269
277
|
|
|
278
|
+
/* margin & bleed overlay (overlay.ts) -------------------------------- */
|
|
279
|
+
/* THE LOOK LIVES HERE, in these tokens, and nowhere else: colours, line
|
|
280
|
+
width, line style, tint strength. The desktop app's settings change the
|
|
281
|
+
colour (an inline \`--gp-ov-color\` on <body>) and pick a strength preset
|
|
282
|
+
(\`data-overlay-strength\`); nothing in a book's CSS is involved or needed. */
|
|
283
|
+
.gp-stage {
|
|
284
|
+
--gp-ov-color: #2f80ed; /* margin box line + the tint over the margins */
|
|
285
|
+
--gp-ov-width: 2px; /* line width as seen on screen, at any zoom */
|
|
286
|
+
--gp-ov-style: dashed; /* margin box line style */
|
|
287
|
+
--gp-ov-tint: 0.1; /* opacity of the tint over the margin area */
|
|
288
|
+
--gp-ov-bleed-color: #e5484d; /* bleed band + trim line */
|
|
289
|
+
--gp-ov-bleed-alpha: 0.45; /* opacity of the bleed band */
|
|
290
|
+
--gp-ov-trim-color: #8a0b18; /* the trim line: darker than the band so it reads as an edge */
|
|
291
|
+
--gp-ov-flag-width: 2px; /* overflow flag outline */
|
|
292
|
+
--gp-ov-warn: #f59e0b; /* reaches into the margin */
|
|
293
|
+
--gp-ov-error: #e5484d; /* crosses the trim / leaves the page */
|
|
294
|
+
--gp-ov-intent: #8b5cf6; /* placed on purpose */
|
|
295
|
+
}
|
|
296
|
+
.gp-stage[data-overlay-strength="subtle"] {
|
|
297
|
+
--gp-ov-width: 1px;
|
|
298
|
+
--gp-ov-tint: 0.05;
|
|
299
|
+
--gp-ov-bleed-alpha: 0.3;
|
|
300
|
+
}
|
|
301
|
+
.gp-stage[data-overlay-strength="strong"] {
|
|
302
|
+
--gp-ov-width: 3px;
|
|
303
|
+
--gp-ov-tint: 0.2;
|
|
304
|
+
--gp-ov-bleed-alpha: 0.65;
|
|
305
|
+
--gp-ov-flag-width: 3px;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/* One sibling layer per run, ABOVE the strip so the lines stay visible over
|
|
309
|
+
author backgrounds and art. Absolutely positioned and pointer-transparent:
|
|
310
|
+
it takes no flow space and no hits, so it cannot move a page break. Exists
|
|
311
|
+
only while the overlay is on. */
|
|
312
|
+
.gp-ov {
|
|
313
|
+
position: absolute;
|
|
314
|
+
inset: 0;
|
|
315
|
+
z-index: 5;
|
|
316
|
+
pointer-events: none;
|
|
317
|
+
/* The stage is a paint transform, so a 2px line would shrink with the zoom
|
|
318
|
+
and vanish at fit-to-width. Lines are sized in layout px divided by the
|
|
319
|
+
zoom, which makes them the same width on screen at every zoom. */
|
|
320
|
+
--gp-ov-w: calc(var(--gp-ov-width) / var(--gp-zoom, 1));
|
|
321
|
+
--gp-ov-flag-w: calc(var(--gp-ov-flag-width) / var(--gp-zoom, 1));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.gp-ov-page {
|
|
325
|
+
position: absolute;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* the margins as a region: the trim minus the content box (clip-path, set in
|
|
329
|
+
overlay.ts), so a margin reads as an area, not a hairline */
|
|
330
|
+
.gp-ov-tint {
|
|
331
|
+
position: absolute;
|
|
332
|
+
inset: 0;
|
|
333
|
+
background: color-mix(in srgb, var(--gp-ov-color) calc(var(--gp-ov-tint) * 100%), transparent);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* margin box: the content area's outline */
|
|
337
|
+
.gp-ov-margin {
|
|
338
|
+
position: absolute;
|
|
339
|
+
box-sizing: border-box;
|
|
340
|
+
border: var(--gp-ov-w) var(--gp-ov-style) var(--gp-ov-color);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/* bleed: a translucent band OUTSIDE the trim (its width is the border) */
|
|
344
|
+
.gp-ov-bleed {
|
|
345
|
+
position: absolute;
|
|
346
|
+
box-sizing: border-box;
|
|
347
|
+
border-style: solid;
|
|
348
|
+
border-color: color-mix(in srgb, var(--gp-ov-bleed-color) calc(var(--gp-ov-bleed-alpha) * 100%), transparent);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/* trim: where the sheet will be cut, drawn only when there is bleed — solid,
|
|
352
|
+
just inside the edge so it never paints over the band (overlay.ts sizes and
|
|
353
|
+
places it: the whole sheet, or the sheet less an in-sheet bleed) */
|
|
354
|
+
.gp-ov-trim {
|
|
355
|
+
position: absolute;
|
|
356
|
+
outline: var(--gp-ov-w) solid var(--gp-ov-trim-color);
|
|
357
|
+
outline-offset: calc(-1 * var(--gp-ov-w));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/* overflow flags — outline + faint tint, never a box change */
|
|
361
|
+
.gp-ov-flag {
|
|
362
|
+
position: absolute;
|
|
363
|
+
box-sizing: border-box;
|
|
364
|
+
}
|
|
365
|
+
.gp-ov-flag[data-kind="margin"] {
|
|
366
|
+
outline: var(--gp-ov-flag-w) solid var(--gp-ov-warn);
|
|
367
|
+
background: color-mix(in srgb, var(--gp-ov-warn) 16%, transparent);
|
|
368
|
+
}
|
|
369
|
+
.gp-ov-flag[data-kind="bleed"],
|
|
370
|
+
.gp-ov-flag[data-kind="off-page"] {
|
|
371
|
+
outline: var(--gp-ov-flag-w) solid var(--gp-ov-error);
|
|
372
|
+
background: color-mix(in srgb, var(--gp-ov-error) 18%, transparent);
|
|
373
|
+
}
|
|
374
|
+
/* placed on purpose (.gp-bleed, .gp-pin, …): present, not a problem */
|
|
375
|
+
.gp-ov-flag[data-kind="intended"] {
|
|
376
|
+
outline: var(--gp-ov-flag-w) dotted var(--gp-ov-intent);
|
|
377
|
+
}
|
|
378
|
+
|
|
270
379
|
/* designer mode ------------------------------------------------------- */
|
|
271
380
|
.gp-guide-trim,
|
|
272
381
|
.gp-guide-safe,
|
|
@@ -310,6 +419,7 @@
|
|
|
310
419
|
transform: none;
|
|
311
420
|
}
|
|
312
421
|
.gp-layer,
|
|
422
|
+
.gp-ov,
|
|
313
423
|
.gp-warning,
|
|
314
424
|
.gp-guide-trim,
|
|
315
425
|
.gp-guide-safe,
|
|
@@ -1362,6 +1472,13 @@
|
|
|
1362
1472
|
}
|
|
1363
1473
|
}
|
|
1364
1474
|
function runPageBox(model, name, warnings = []) {
|
|
1475
|
+
const { right, agree, r, l } = pageSides(model, name);
|
|
1476
|
+
if (agree)
|
|
1477
|
+
return right;
|
|
1478
|
+
warnings.push(`@page :left and @page :right give ${name ? `the "${name}" page` : "this book"} ` + `different content areas (${l.w.toFixed(1)}×${l.h.toFixed(1)}pt vs ` + `${r.w.toFixed(1)}×${r.h.toFixed(1)}pt). Print alternates between them; the preview can ` + `only show one, so page breaks here may not match the PDF. Mirror the margins instead: ` + `swap which edge is wide and keep the total the same.`);
|
|
1479
|
+
return resolvePage(model, { name }).geometry;
|
|
1480
|
+
}
|
|
1481
|
+
function pageSides(model, name) {
|
|
1365
1482
|
const right = resolvePage(model, { name, pseudos: ["right"] }).geometry;
|
|
1366
1483
|
const left = resolvePage(model, { name, pseudos: ["left"] }).geometry;
|
|
1367
1484
|
const box = (g) => ({
|
|
@@ -1370,10 +1487,23 @@
|
|
|
1370
1487
|
});
|
|
1371
1488
|
const r = box(right);
|
|
1372
1489
|
const l = box(left);
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1490
|
+
return {
|
|
1491
|
+
right,
|
|
1492
|
+
left,
|
|
1493
|
+
r,
|
|
1494
|
+
l,
|
|
1495
|
+
agree: Math.abs(r.w - l.w) < 0.01 && Math.abs(r.h - l.h) < 0.01
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
function versoShiftPt(model, name, flushEdges = []) {
|
|
1499
|
+
const { right, left, agree } = pageSides(model, name);
|
|
1500
|
+
if (!agree)
|
|
1501
|
+
return 0;
|
|
1502
|
+
const r = flushMargins(right.margin, flushEdges);
|
|
1503
|
+
const l = flushMargins(left.margin, flushEdges);
|
|
1504
|
+
if (Math.abs(r.left + r.right - (l.left + l.right)) >= 0.01)
|
|
1505
|
+
return 0;
|
|
1506
|
+
return r.left - l.left;
|
|
1377
1507
|
}
|
|
1378
1508
|
function buildStrips(model, opts = {}, warnings = []) {
|
|
1379
1509
|
const doc = document;
|
|
@@ -1399,6 +1529,10 @@
|
|
|
1399
1529
|
strip.style.setProperty("--gp-margin-right", `${pt(margin.right)}px`);
|
|
1400
1530
|
strip.style.setProperty("--gp-margin-bottom", `${pt(margin.bottom)}px`);
|
|
1401
1531
|
strip.style.setProperty("--gp-margin-left", `${pt(margin.left)}px`);
|
|
1532
|
+
const rawShift = versoShiftPt(model, run.page, run.flushEdges);
|
|
1533
|
+
const versoShift = Math.abs(rawShift) >= 0.01 ? rawShift : 0;
|
|
1534
|
+
if (versoShift)
|
|
1535
|
+
strip.style.setProperty("--gp-verso-shift", `${pt(versoShift)}px`);
|
|
1402
1536
|
run.nodes[0].before(strip);
|
|
1403
1537
|
for (const n of run.nodes)
|
|
1404
1538
|
strip.appendChild(n);
|
|
@@ -1407,6 +1541,7 @@
|
|
|
1407
1541
|
page: run.page,
|
|
1408
1542
|
geometry,
|
|
1409
1543
|
flushEdges: run.flushEdges,
|
|
1544
|
+
versoShift,
|
|
1410
1545
|
pages: 0,
|
|
1411
1546
|
offset: 0
|
|
1412
1547
|
});
|
|
@@ -2067,6 +2202,216 @@
|
|
|
2067
2202
|
return /right/.test(name) ? "end" : "start";
|
|
2068
2203
|
}
|
|
2069
2204
|
|
|
2205
|
+
// src/engine/compiler/tier2.ts
|
|
2206
|
+
var DEFAULT_SLUG_PT = 18;
|
|
2207
|
+
function pageGeometry(model, opts) {
|
|
2208
|
+
const base = resolvePage(model);
|
|
2209
|
+
const trim = { width: base.geometry.width, height: base.geometry.height };
|
|
2210
|
+
const bleed = opts.bleedPt ?? base.geometry.bleed;
|
|
2211
|
+
const wantsMarks = base.geometry.marks.length > 0 || opts.marks === true;
|
|
2212
|
+
const slug = wantsMarks ? opts.slugPt ?? DEFAULT_SLUG_PT : 0;
|
|
2213
|
+
return {
|
|
2214
|
+
trim,
|
|
2215
|
+
bleed,
|
|
2216
|
+
slug,
|
|
2217
|
+
marks: base.geometry.marks.length ? base.geometry.marks : wantsMarks ? ["crop"] : [],
|
|
2218
|
+
media: {
|
|
2219
|
+
width: trim.width + 2 * (bleed + slug),
|
|
2220
|
+
height: trim.height + 2 * (bleed + slug)
|
|
2221
|
+
}
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
// src/engine/viewer/overlay.ts
|
|
2226
|
+
function documentBleedPt(model) {
|
|
2227
|
+
return pageGeometry(model, { model }).bleed;
|
|
2228
|
+
}
|
|
2229
|
+
function pageBoxes(g, margin, bleed, scale = 1) {
|
|
2230
|
+
const w = g.width * scale;
|
|
2231
|
+
const h = g.height * scale;
|
|
2232
|
+
const b = Math.max(0, bleed) * scale;
|
|
2233
|
+
return {
|
|
2234
|
+
host: { x: 0, y: 0, w, h },
|
|
2235
|
+
trim: { x: 0, y: 0, w, h },
|
|
2236
|
+
content: {
|
|
2237
|
+
x: margin.left * scale,
|
|
2238
|
+
y: margin.top * scale,
|
|
2239
|
+
w: w - (margin.left + margin.right) * scale,
|
|
2240
|
+
h: h - (margin.top + margin.bottom) * scale
|
|
2241
|
+
},
|
|
2242
|
+
bleedBox: { x: -b, y: -b, w: w + 2 * b, h: h + 2 * b },
|
|
2243
|
+
bleed: b
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
function insetPageBoxes(g, margin, bleed, side, scale = 1) {
|
|
2247
|
+
const sheet = pageBoxes(g, margin, 0, scale);
|
|
2248
|
+
const b = Math.max(0, bleed) * scale;
|
|
2249
|
+
return {
|
|
2250
|
+
host: sheet.host,
|
|
2251
|
+
trim: { x: side === "verso" ? b : 0, y: b, w: sheet.host.w - b, h: sheet.host.h - 2 * b },
|
|
2252
|
+
content: sheet.content,
|
|
2253
|
+
bleedBox: sheet.host,
|
|
2254
|
+
bleed: b
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
var SEVERITY = { inside: 0, margin: 1, bleed: 2, "off-page": 3 };
|
|
2258
|
+
var within = (r, outer, tol) => r.x >= outer.x - tol && r.y >= outer.y - tol && r.x + r.w <= outer.x + outer.w + tol && r.y + r.h <= outer.y + outer.h + tol;
|
|
2259
|
+
function classifyRect(r, boxes, tol = 0) {
|
|
2260
|
+
if (within(r, boxes.content, tol))
|
|
2261
|
+
return "inside";
|
|
2262
|
+
if (within(r, boxes.trim, tol))
|
|
2263
|
+
return "margin";
|
|
2264
|
+
if (boxes.bleed > 0 && within(r, boxes.bleedBox, tol))
|
|
2265
|
+
return "bleed";
|
|
2266
|
+
return "off-page";
|
|
2267
|
+
}
|
|
2268
|
+
function worstKind(kinds) {
|
|
2269
|
+
return kinds.reduce((worst, k) => SEVERITY[k] > SEVERITY[worst] ? k : worst, "inside");
|
|
2270
|
+
}
|
|
2271
|
+
var INTENT_SELECTOR = ".gp-bleed, .gp-pin, .gp-flush, .gp-behind, .gp-page-full-bleed";
|
|
2272
|
+
var OVERLAY_STRENGTHS = ["subtle", "normal", "strong"];
|
|
2273
|
+
var HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
2274
|
+
function sanitizeOverlayStyle(style) {
|
|
2275
|
+
const s = style && typeof style === "object" ? style : {};
|
|
2276
|
+
const out = {};
|
|
2277
|
+
if (typeof s.color === "string" && HEX_COLOR.test(s.color.trim()))
|
|
2278
|
+
out.color = s.color.trim();
|
|
2279
|
+
if (OVERLAY_STRENGTHS.includes(s.strength))
|
|
2280
|
+
out.strength = s.strength;
|
|
2281
|
+
return out;
|
|
2282
|
+
}
|
|
2283
|
+
var OVERLAY_OFF = { on: false, issues: 0, intended: 0, bleedPt: 0 };
|
|
2284
|
+
function pageFrameMargin(margin, versoShift, side) {
|
|
2285
|
+
if (side === "recto" || !versoShift)
|
|
2286
|
+
return margin;
|
|
2287
|
+
return { ...margin, left: margin.left - versoShift, right: margin.right + versoShift };
|
|
2288
|
+
}
|
|
2289
|
+
function configInsetBleedPt() {
|
|
2290
|
+
const v = parseFloat(document.querySelector('meta[name="gp-inset-bleed"]')?.getAttribute("content") ?? "");
|
|
2291
|
+
return Number.isFinite(v) && v > 0 ? v : 0;
|
|
2292
|
+
}
|
|
2293
|
+
var SKIP = new Set(["SCRIPT", "STYLE", "LINK", "TEMPLATE", "BR", "WBR", "SOURCE", "TRACK", "META"]);
|
|
2294
|
+
var TOL_PX = 1;
|
|
2295
|
+
function div(cls, box) {
|
|
2296
|
+
const el = document.createElement("div");
|
|
2297
|
+
el.className = cls;
|
|
2298
|
+
if (box)
|
|
2299
|
+
place(el, box);
|
|
2300
|
+
return el;
|
|
2301
|
+
}
|
|
2302
|
+
function place(el, r) {
|
|
2303
|
+
el.style.left = `${r.x}px`;
|
|
2304
|
+
el.style.top = `${r.y}px`;
|
|
2305
|
+
el.style.width = `${r.w}px`;
|
|
2306
|
+
el.style.height = `${r.h}px`;
|
|
2307
|
+
}
|
|
2308
|
+
function marginTint(b) {
|
|
2309
|
+
const el = div("gp-ov-tint");
|
|
2310
|
+
const t = b.trim;
|
|
2311
|
+
const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
2312
|
+
const l = clamp(b.content.x, t.x, t.x + t.w);
|
|
2313
|
+
const r = clamp(b.content.x + b.content.w, t.x, t.x + t.w);
|
|
2314
|
+
const top = clamp(b.content.y, t.y, t.y + t.h);
|
|
2315
|
+
const bot = clamp(b.content.y + b.content.h, t.y, t.y + t.h);
|
|
2316
|
+
const x2 = t.x + t.w;
|
|
2317
|
+
const y2 = t.y + t.h;
|
|
2318
|
+
el.style.clipPath = `polygon(evenodd, ${t.x}px ${t.y}px, ${x2}px ${t.y}px, ${x2}px ${y2}px, ${t.x}px ${y2}px, ${t.x}px ${t.y}px, ` + `${l}px ${top}px, ${l}px ${bot}px, ${r}px ${bot}px, ${r}px ${top}px, ${l}px ${top}px)`;
|
|
2319
|
+
return el;
|
|
2320
|
+
}
|
|
2321
|
+
function clearOverlay() {
|
|
2322
|
+
for (const layer of Array.from(document.querySelectorAll(".gp-ov")))
|
|
2323
|
+
layer.remove();
|
|
2324
|
+
}
|
|
2325
|
+
function paintOverlay(pages, strips, bleedPt, insetBleedPt = 0) {
|
|
2326
|
+
const inSheet = bleedPt <= 0 && insetBleedPt > 0;
|
|
2327
|
+
clearOverlay();
|
|
2328
|
+
const layers = new Map;
|
|
2329
|
+
const painted = new Map;
|
|
2330
|
+
for (const p of pages) {
|
|
2331
|
+
const run = p.sheet.closest(".gp-run");
|
|
2332
|
+
if (!run)
|
|
2333
|
+
continue;
|
|
2334
|
+
let layer = layers.get(run);
|
|
2335
|
+
if (!layer) {
|
|
2336
|
+
layer = div("gp-ov");
|
|
2337
|
+
run.appendChild(layer);
|
|
2338
|
+
layers.set(run, layer);
|
|
2339
|
+
}
|
|
2340
|
+
const boxes = inSheet ? insetPageBoxes(p.geometry, p.margin, insetBleedPt, p.sheet.dataset.side === "verso" ? "verso" : "recto", PX_PER_PT) : pageBoxes(p.geometry, p.margin, bleedPt, PX_PER_PT);
|
|
2341
|
+
const host = div("gp-ov-page", boxes.host);
|
|
2342
|
+
host.style.left = p.sheet.style.left;
|
|
2343
|
+
host.style.top = p.sheet.style.top;
|
|
2344
|
+
host.dataset.page = String(p.index + 1);
|
|
2345
|
+
if (boxes.bleed > 0) {
|
|
2346
|
+
const bb = boxes.bleedBox;
|
|
2347
|
+
const t = boxes.trim;
|
|
2348
|
+
const band = div("gp-ov-bleed", bb);
|
|
2349
|
+
band.style.borderWidth = `${t.y - bb.y}px ${bb.x + bb.w - (t.x + t.w)}px ${bb.y + bb.h - (t.y + t.h)}px ${t.x - bb.x}px`;
|
|
2350
|
+
host.appendChild(band);
|
|
2351
|
+
host.appendChild(div("gp-ov-trim", t));
|
|
2352
|
+
}
|
|
2353
|
+
host.appendChild(marginTint(boxes));
|
|
2354
|
+
host.appendChild(div("gp-ov-margin", boxes.content));
|
|
2355
|
+
layer.appendChild(host);
|
|
2356
|
+
painted.set(p.index, { host, boxes, sheet: p.sheet });
|
|
2357
|
+
}
|
|
2358
|
+
const flags = [];
|
|
2359
|
+
let issues = 0;
|
|
2360
|
+
let intended = 0;
|
|
2361
|
+
for (const strip of strips) {
|
|
2362
|
+
const zoom = cssZoomOf(strip.el);
|
|
2363
|
+
const colW = (parseFloat(strip.el.style.getPropertyValue("--gp-content-w")) || 0) * zoom;
|
|
2364
|
+
const colH = (parseFloat(strip.el.style.getPropertyValue("--gp-content-h")) || 0) * zoom;
|
|
2365
|
+
const visit = (el) => {
|
|
2366
|
+
if (SKIP.has(el.tagName) || el.classList.contains("gp-wrap-spacer"))
|
|
2367
|
+
return;
|
|
2368
|
+
const kinds = [];
|
|
2369
|
+
const found = [];
|
|
2370
|
+
for (const r of Array.from(el.getClientRects())) {
|
|
2371
|
+
if (r.width <= 0 && r.height <= 0)
|
|
2372
|
+
continue;
|
|
2373
|
+
const ax = r.left + Math.min(r.width, colW || r.width) / 2;
|
|
2374
|
+
const ay = r.top + Math.min(r.height, colH || r.height) / 2;
|
|
2375
|
+
const target = painted.get(strip.offset + indexInStrip(ax, ay, strip));
|
|
2376
|
+
if (!target)
|
|
2377
|
+
continue;
|
|
2378
|
+
target.rect ??= target.sheet.getBoundingClientRect();
|
|
2379
|
+
const local = {
|
|
2380
|
+
x: (r.left - target.rect.left) / zoom,
|
|
2381
|
+
y: (r.top - target.rect.top) / zoom,
|
|
2382
|
+
w: r.width / zoom,
|
|
2383
|
+
h: r.height / zoom
|
|
2384
|
+
};
|
|
2385
|
+
const kind = classifyRect(local, target.boxes, TOL_PX);
|
|
2386
|
+
kinds.push(kind);
|
|
2387
|
+
if (kind !== "inside")
|
|
2388
|
+
found.push({ host: target.host, kind, box: local });
|
|
2389
|
+
}
|
|
2390
|
+
const worst = worstKind(kinds);
|
|
2391
|
+
if (worst !== "inside") {
|
|
2392
|
+
const isIntended = el.closest(INTENT_SELECTOR) !== null;
|
|
2393
|
+
if (isIntended)
|
|
2394
|
+
intended++;
|
|
2395
|
+
else
|
|
2396
|
+
issues++;
|
|
2397
|
+
for (const f of found)
|
|
2398
|
+
flags.push(isIntended ? { ...f, kind: "intended" } : f);
|
|
2399
|
+
return;
|
|
2400
|
+
}
|
|
2401
|
+
for (const child of Array.from(el.children))
|
|
2402
|
+
visit(child);
|
|
2403
|
+
};
|
|
2404
|
+
for (const child of Array.from(strip.el.children))
|
|
2405
|
+
visit(child);
|
|
2406
|
+
}
|
|
2407
|
+
for (const f of flags) {
|
|
2408
|
+
const el = div(`gp-ov-flag`, f.box);
|
|
2409
|
+
el.dataset.kind = f.kind;
|
|
2410
|
+
f.host.appendChild(el);
|
|
2411
|
+
}
|
|
2412
|
+
return inSheet ? { on: true, issues, intended, bleedPt: insetBleedPt, bleedInSheet: true } : { on: true, issues, intended, bleedPt };
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2070
2415
|
// src/engine/viewer/decorate.ts
|
|
2071
2416
|
var px = (v) => `${v * PX_PER_PT}px`;
|
|
2072
2417
|
function elementForHref(href) {
|
|
@@ -2095,6 +2440,13 @@
|
|
|
2095
2440
|
let blankPages = new Set;
|
|
2096
2441
|
let paddedPages = new Set;
|
|
2097
2442
|
const warnings = [];
|
|
2443
|
+
let overlayOn = false;
|
|
2444
|
+
let overlayPages = [];
|
|
2445
|
+
let overlaySummary = OVERLAY_OFF;
|
|
2446
|
+
const paintOverlayNow = () => {
|
|
2447
|
+
overlaySummary = paintOverlay(overlayPages, layout.strips, documentBleedPt(model), configInsetBleedPt());
|
|
2448
|
+
window.dispatchEvent(new CustomEvent("gp:overlay", { detail: overlaySummary }));
|
|
2449
|
+
};
|
|
2098
2450
|
const api = {
|
|
2099
2451
|
redraw: () => draw(),
|
|
2100
2452
|
sheetFor: (p) => sheets.get(p),
|
|
@@ -2104,7 +2456,29 @@
|
|
|
2104
2456
|
warnings,
|
|
2105
2457
|
setDesigner(on) {
|
|
2106
2458
|
document.body.dataset.designer = on ? "on" : "off";
|
|
2107
|
-
}
|
|
2459
|
+
},
|
|
2460
|
+
setOverlay(on, style) {
|
|
2461
|
+
if (style) {
|
|
2462
|
+
const { color, strength } = sanitizeOverlayStyle(style);
|
|
2463
|
+
if (strength)
|
|
2464
|
+
document.body.dataset.overlayStrength = strength;
|
|
2465
|
+
if (color)
|
|
2466
|
+
document.body.style.setProperty("--gp-ov-color", color);
|
|
2467
|
+
}
|
|
2468
|
+
const wasOn = overlayOn;
|
|
2469
|
+
overlayOn = on;
|
|
2470
|
+
document.body.dataset.overlay = on ? "on" : "off";
|
|
2471
|
+
if (on) {
|
|
2472
|
+
if (!(wasOn && style))
|
|
2473
|
+
paintOverlayNow();
|
|
2474
|
+
} else {
|
|
2475
|
+
clearOverlay();
|
|
2476
|
+
overlaySummary = OVERLAY_OFF;
|
|
2477
|
+
window.dispatchEvent(new CustomEvent("gp:overlay", { detail: overlaySummary }));
|
|
2478
|
+
}
|
|
2479
|
+
return overlaySummary;
|
|
2480
|
+
},
|
|
2481
|
+
overlay: () => overlaySummary
|
|
2108
2482
|
};
|
|
2109
2483
|
const canvasBg = captureCanvasBackground();
|
|
2110
2484
|
document.body.classList.add("gp-stage");
|
|
@@ -2262,9 +2636,12 @@
|
|
|
2262
2636
|
warnings.length = 0;
|
|
2263
2637
|
blankPages = new Set(layout.blankPageIndices);
|
|
2264
2638
|
paddedPages = new Set(layout.paddedPageIndices);
|
|
2639
|
+
overlayPages = [];
|
|
2265
2640
|
buildMaps();
|
|
2266
2641
|
fillXrefs();
|
|
2267
2642
|
let prevPageH = 0;
|
|
2643
|
+
let prevVersoShift = 0;
|
|
2644
|
+
let prevRunX = 0;
|
|
2268
2645
|
let first = true;
|
|
2269
2646
|
for (const strip of layout.strips) {
|
|
2270
2647
|
const run = ensureRun(strip);
|
|
@@ -2281,12 +2658,13 @@
|
|
|
2281
2658
|
const slot = i + shift;
|
|
2282
2659
|
const row = Math.floor(slot / perRow);
|
|
2283
2660
|
const colVisual = slot % perRow;
|
|
2284
|
-
const
|
|
2661
|
+
const side = bookIndex % 2 === 0 ? "recto" : "verso";
|
|
2662
|
+
const sheetLeft = colVisual * stride + (side === "verso" ? strip.versoShift * PX_PER_PT : 0);
|
|
2285
2663
|
const sheetTop = row * rowStride;
|
|
2286
2664
|
const sheet = document.createElement("div");
|
|
2287
2665
|
sheet.className = "gp-sheet";
|
|
2288
2666
|
sheet.dataset.page = String(bookIndex + 1);
|
|
2289
|
-
sheet.dataset.side =
|
|
2667
|
+
sheet.dataset.side = side;
|
|
2290
2668
|
sheet.style.left = `${sheetLeft}px`;
|
|
2291
2669
|
sheet.style.top = `${sheetTop}px`;
|
|
2292
2670
|
sheet.style.setProperty("--gp-page-w", px(ctx.geometry.width));
|
|
@@ -2300,16 +2678,30 @@
|
|
|
2300
2678
|
drawMarginBoxes(sheet, ctx, layout.totalPages - paddedPages.size);
|
|
2301
2679
|
drawGuides(sheet, ctx);
|
|
2302
2680
|
drawCropMarks(sheet, ctx);
|
|
2681
|
+
overlayPages.push({
|
|
2682
|
+
index: bookIndex,
|
|
2683
|
+
sheet,
|
|
2684
|
+
strip,
|
|
2685
|
+
geometry: ctx.geometry,
|
|
2686
|
+
margin: ctx.padded || blankPages.has(bookIndex) ? ctx.geometry.margin : pageFrameMargin(flushMargins(g.margin, strip.flushEdges ?? []), strip.versoShift, side)
|
|
2687
|
+
});
|
|
2303
2688
|
}
|
|
2304
2689
|
const rows = Math.max(1, Math.ceil((strip.pages + shift) / perRow));
|
|
2305
2690
|
run.style.height = `${rowStride * (rows - 1) + PX_PER_PT * g.height}px`;
|
|
2306
2691
|
run.style.width = `${stride * perRow}px`;
|
|
2307
|
-
|
|
2692
|
+
const composed = !!strip.wrapCols && shift === 1 && !first;
|
|
2693
|
+
run.style.marginTop = composed ? `${-(prevPageH + sheetGap)}px` : "";
|
|
2694
|
+
const runX = composed ? prevRunX + (prevVersoShift - strip.versoShift) * PX_PER_PT : 0;
|
|
2695
|
+
run.style.marginLeft = runX ? `${runX}px` : "";
|
|
2308
2696
|
prevPageH = PX_PER_PT * g.height;
|
|
2697
|
+
prevVersoShift = strip.versoShift;
|
|
2698
|
+
prevRunX = runX;
|
|
2309
2699
|
first = false;
|
|
2310
2700
|
if (opts.designer)
|
|
2311
2701
|
checkOverflow(strip, warnings);
|
|
2312
2702
|
}
|
|
2703
|
+
if (overlayOn)
|
|
2704
|
+
paintOverlayNow();
|
|
2313
2705
|
}
|
|
2314
2706
|
function drawMarginBoxes(sheet, ctx, totalPages) {
|
|
2315
2707
|
const g = ctx.geometry;
|