gutterpress 0.11.19-alpha.3 → 0.11.20-alpha.1

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 (28) hide show
  1. package/dist/api/index.js +2 -2
  2. package/dist/{build-6631d323.js → build-cmf4nbpm.js} +4 -4
  3. package/dist/{cli-zpawttnh.js → cli-7shp2kmj.js} +1 -1
  4. package/dist/{cli-1b65ntcd.js → cli-mmm22cza.js} +2 -1
  5. package/dist/{cli-xx94a1k6.js → cli-rtpm04dv.js} +4 -4
  6. package/dist/{cli-4m7sgga3.js → cli-x1nfhaf1.js} +26 -15
  7. package/dist/cli.js +12 -12
  8. package/dist/{doctor-vw0a32kd.js → doctor-0d6esj02.js} +2 -2
  9. package/dist/{engine-fe0n7v9p.js → engine-1v8kmv3a.js} +1 -1
  10. package/dist/{engine-zehw6tkv.js → engine-adxptzf0.js} +2 -2
  11. package/dist/{ext-7t9fgc3x.js → ext-65nfh7st.js} +4 -4
  12. package/dist/{gutterpress-viewer-k4mpqjh0.js → gutterpress-viewer-0r4rt4de.js} +222 -233
  13. package/dist/{index-bja72kwa.js → index-1y885h1j.js} +26 -14
  14. package/dist/{index-b866g2ph.js → index-cngw658w.js} +1 -1
  15. package/dist/{index-jt552wtf.js → index-pnvzgftg.js} +4 -4
  16. package/dist/index.js +3 -3
  17. package/dist/lib/ghostscript.d.ts +7 -0
  18. package/dist/{manifest.schema-5pzhscqh.json → manifest.schema-nrykd2c6.json} +14 -1
  19. package/dist/{new-b7nz21pm.js → new-y4a2xz3g.js} +4 -4
  20. package/dist/{preflight-00vckfwg.js → preflight-kt7wfgbd.js} +5 -5
  21. package/dist/preview/file-watcher.d.ts +9 -1
  22. package/dist/{preview-mjmnnnpp.js → preview-5ze728kp.js} +4 -4
  23. package/dist/{preview-interface-2n1d9pbf.js → preview-interface-k2mj5kvq.js} +12 -12
  24. package/dist/{preview-shell-hyp7g158.js → preview-shell-7jfe6vsc.js} +3 -3
  25. package/dist/{publish-qkz561hp.js → publish-5tpcyysh.js} +4 -4
  26. package/dist/schema/manifest.types.d.ts +17 -3
  27. package/dist/{validate-nwet2ksq.js → validate-p0hkqkfe.js} +4 -4
  28. package/package.json +1 -1
@@ -62,7 +62,6 @@
62
62
  --gp-sheet-bg: #fff;
63
63
  --gp-stage-bg: #4a4a52;
64
64
  --gp-guide: #e5484d;
65
- --gp-safe: #30a46c;
66
65
  background: var(--gp-stage-bg);
67
66
  margin: 0;
68
67
  padding: 32px;
@@ -275,110 +274,8 @@
275
274
  .gp-marginbox[data-align="center"] > .gp-marginbox-content { justify-content: center; }
276
275
  .gp-marginbox[data-align="end"] > .gp-marginbox-content { justify-content: flex-end; }
277
276
 
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
-
379
277
  /* designer mode ------------------------------------------------------- */
380
278
  .gp-guide-trim,
381
- .gp-guide-safe,
382
279
  .gp-crop-mark {
383
280
  position: absolute;
384
281
  pointer-events: none;
@@ -386,13 +283,11 @@
386
283
  }
387
284
 
388
285
  .gp-stage[data-designer="on"] .gp-guide-trim,
389
- .gp-stage[data-designer="on"] .gp-guide-safe,
390
286
  .gp-stage[data-designer="on"] .gp-crop-mark {
391
287
  display: block;
392
288
  }
393
289
 
394
290
  .gp-guide-trim { outline: 1px dashed var(--gp-guide); }
395
- .gp-guide-safe { outline: 1px dashed var(--gp-safe); }
396
291
  .gp-crop-mark { background: var(--gp-guide); }
397
292
 
398
293
  .gp-warning {
@@ -419,10 +314,8 @@
419
314
  transform: none;
420
315
  }
421
316
  .gp-layer,
422
- .gp-ov,
423
317
  .gp-warning,
424
318
  .gp-guide-trim,
425
- .gp-guide-safe,
426
319
  .gp-crop-mark {
427
320
  display: none !important;
428
321
  }
@@ -2202,66 +2095,28 @@
2202
2095
  return /right/.test(name) ? "end" : "start";
2203
2096
  }
2204
2097
 
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
2098
  // 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;
2099
+ function pageBoxes(area, side, scale = 1) {
2100
+ const w = area.width * scale;
2101
+ const h = area.height * scale;
2102
+ const b = Math.max(0, area.bleed) * scale;
2103
+ const s = Math.max(0, area.safe) * scale;
2104
+ const trim = { x: side === "verso" ? b : 0, y: b, w: Math.max(0, w - b), h: Math.max(0, h - 2 * b) };
2233
2105
  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,
2106
+ sheet: { x: 0, y: 0, w, h },
2107
+ trim,
2108
+ safe: { x: trim.x + s, y: trim.y + s, w: Math.max(0, trim.w - 2 * s), h: Math.max(0, trim.h - 2 * s) },
2254
2109
  bleed: b
2255
2110
  };
2256
2111
  }
2257
- var SEVERITY = { inside: 0, margin: 1, bleed: 2, "off-page": 3 };
2112
+ var SEVERITY = { inside: 0, unsafe: 1, bleed: 2, "off-page": 3 };
2258
2113
  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
2114
  function classifyRect(r, boxes, tol = 0) {
2260
- if (within(r, boxes.content, tol))
2115
+ if (within(r, boxes.safe, tol))
2261
2116
  return "inside";
2262
2117
  if (within(r, boxes.trim, tol))
2263
- return "margin";
2264
- if (boxes.bleed > 0 && within(r, boxes.bleedBox, tol))
2118
+ return "unsafe";
2119
+ if (boxes.bleed > 0 && within(r, boxes.sheet, tol))
2265
2120
  return "bleed";
2266
2121
  return "off-page";
2267
2122
  }
@@ -2280,18 +2135,176 @@
2280
2135
  out.strength = s.strength;
2281
2136
  return out;
2282
2137
  }
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 };
2138
+ var OVERLAY_OFF = { on: false, issues: 0, intended: 0, bleedPt: 0, safePt: 0 };
2139
+ function printAreaFromMeta() {
2140
+ const raw = document.querySelector('meta[name="gp-print-area"]')?.getAttribute("content");
2141
+ if (!raw)
2142
+ return null;
2143
+ try {
2144
+ const v = JSON.parse(raw);
2145
+ const n = (x) => typeof x === "number" && Number.isFinite(x) && x > 0 ? x : 0;
2146
+ if (!n(v.width) || !n(v.height))
2147
+ return null;
2148
+ return { width: n(v.width), height: n(v.height), bleed: n(v.bleed), safe: n(v.safe) };
2149
+ } catch {
2150
+ return null;
2151
+ }
2288
2152
  }
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;
2153
+ function sheetArea(sheet) {
2154
+ const px = (name) => parseFloat(sheet.style.getPropertyValue(name)) || 0;
2155
+ return { width: px("--gp-page-w") / PX_PER_PT, height: px("--gp-page-h") / PX_PER_PT, bleed: 0, safe: 0 };
2156
+ }
2157
+ var OVERLAY_CSS = `
2158
+ .root {
2159
+ --gp-ov-color: #2f80ed; /* safe-area line + the tint outside it */
2160
+ --gp-ov-width: 2px; /* line width as seen on screen, at any zoom */
2161
+ --gp-ov-style: dashed; /* safe-area line style */
2162
+ --gp-ov-tint: 0.1; /* opacity of the tint between safe area and trim */
2163
+ --gp-ov-bleed-color: #e5484d; /* bleed band */
2164
+ --gp-ov-bleed-alpha: 0.45; /* opacity of the bleed band */
2165
+ --gp-ov-trim-color: #8a0b18; /* the trim line: darker than the band so it reads as an edge */
2166
+ --gp-ov-flag-width: 2px; /* overflow flag outline */
2167
+ --gp-ov-warn: #f59e0b; /* leaves the safe area */
2168
+ --gp-ov-error: #e5484d; /* crosses the trim / leaves the sheet */
2169
+ --gp-ov-intent: #8b5cf6; /* placed on purpose */
2170
+ /* The overlay is scaled with the stage, so a 2px line would shrink with the
2171
+ zoom; dividing by it keeps lines the same width on screen at every zoom. */
2172
+ --gp-ov-w: calc(var(--gp-ov-width) / var(--gp-ov-z, 1));
2173
+ --gp-ov-flag-w: calc(var(--gp-ov-flag-width) / var(--gp-ov-z, 1));
2174
+ position: absolute;
2175
+ inset: 0;
2176
+ pointer-events: none;
2177
+ }
2178
+ .root[data-strength="subtle"] {
2179
+ --gp-ov-width: 1px;
2180
+ --gp-ov-tint: 0.05;
2181
+ --gp-ov-bleed-alpha: 0.3;
2182
+ }
2183
+ .root[data-strength="strong"] {
2184
+ --gp-ov-width: 3px;
2185
+ --gp-ov-tint: 0.2;
2186
+ --gp-ov-bleed-alpha: 0.65;
2187
+ --gp-ov-flag-width: 3px;
2188
+ }
2189
+ .gp-ov-page, .gp-ov-tint, .gp-ov-safe, .gp-ov-bleed, .gp-ov-trim, .gp-ov-flag {
2190
+ position: absolute;
2191
+ box-sizing: border-box;
2192
+ }
2193
+ /* the band between the safe area and the trim, as a REGION (clip-path, set in
2194
+ overlay.ts), so it reads as an area, not a hairline */
2195
+ .gp-ov-tint {
2196
+ background: color-mix(in srgb, var(--gp-ov-color) calc(var(--gp-ov-tint) * 100%), transparent);
2197
+ }
2198
+ .gp-ov-safe {
2199
+ border: var(--gp-ov-w) var(--gp-ov-style) var(--gp-ov-color);
2200
+ }
2201
+ /* bleed: a translucent band between the trim and the sheet edge (its width is the border) */
2202
+ .gp-ov-bleed {
2203
+ border-style: solid;
2204
+ border-color: color-mix(in srgb, var(--gp-ov-bleed-color) calc(var(--gp-ov-bleed-alpha) * 100%), transparent);
2205
+ }
2206
+ /* trim: where the sheet will be cut, drawn only when there is bleed */
2207
+ .gp-ov-trim {
2208
+ outline: var(--gp-ov-w) solid var(--gp-ov-trim-color);
2209
+ outline-offset: calc(-1 * var(--gp-ov-w));
2210
+ }
2211
+ /* overflow flags: outline + faint tint, never a box change */
2212
+ .gp-ov-flag[data-kind="unsafe"] {
2213
+ outline: var(--gp-ov-flag-w) solid var(--gp-ov-warn);
2214
+ background: color-mix(in srgb, var(--gp-ov-warn) 16%, transparent);
2215
+ }
2216
+ .gp-ov-flag[data-kind="bleed"],
2217
+ .gp-ov-flag[data-kind="off-page"] {
2218
+ outline: var(--gp-ov-flag-w) solid var(--gp-ov-error);
2219
+ background: color-mix(in srgb, var(--gp-ov-error) 18%, transparent);
2220
+ }
2221
+ .gp-ov-flag[data-kind="intended"] {
2222
+ outline: var(--gp-ov-flag-w) dotted var(--gp-ov-intent);
2223
+ }
2224
+ @media print {
2225
+ .root { display: none; }
2226
+ }
2227
+ `;
2228
+ var HOST_STYLE = [
2229
+ "all: initial",
2230
+ "display: block",
2231
+ "position: absolute",
2232
+ "left: 0",
2233
+ "top: 0",
2234
+ "width: 0",
2235
+ "height: 0",
2236
+ "margin: 0",
2237
+ "padding: 0",
2238
+ "border: 0",
2239
+ "overflow: visible",
2240
+ "visibility: visible",
2241
+ "opacity: 1",
2242
+ "filter: none",
2243
+ "clip-path: none",
2244
+ "mask: none",
2245
+ "z-index: 2147483647",
2246
+ "pointer-events: none",
2247
+ "transform-origin: 0 0",
2248
+ "contain: style"
2249
+ ].map((d) => `${d} !important`).join("; ");
2250
+ var OVERLAY_TAG = "gp-overlay";
2251
+ var host = null;
2252
+ var root = null;
2253
+ var observer = null;
2254
+ var currentStyle = {};
2255
+ function applyStyle() {
2256
+ if (!root)
2257
+ return;
2258
+ if (currentStyle.strength)
2259
+ root.dataset.strength = currentStyle.strength;
2260
+ if (currentStyle.color)
2261
+ root.style.setProperty("--gp-ov-color", currentStyle.color);
2262
+ }
2263
+ function setOverlayStyle(style) {
2264
+ currentStyle = { ...currentStyle, ...style };
2265
+ applyStyle();
2266
+ }
2267
+ function stageFrame() {
2268
+ const stage = document.body;
2269
+ const r = stage.getBoundingClientRect();
2270
+ const zoom = stage.offsetWidth > 0 ? r.width / stage.offsetWidth : 1;
2271
+ return { left: r.left, top: r.top, zoom: zoom || 1 };
2272
+ }
2273
+ function syncToStage() {
2274
+ if (!host || !root)
2275
+ return;
2276
+ const { left, top, zoom } = stageFrame();
2277
+ host.style.setProperty("transform", `translate(${left + window.scrollX}px, ${top + window.scrollY}px) scale(${zoom})`, "important");
2278
+ root.style.setProperty("--gp-ov-z", String(zoom));
2279
+ }
2280
+ function ensureRoot() {
2281
+ if (host && root && host.isConnected) {
2282
+ root.textContent = "";
2283
+ return root;
2284
+ }
2285
+ host = document.createElement(OVERLAY_TAG);
2286
+ host.setAttribute("style", HOST_STYLE);
2287
+ host.setAttribute("aria-hidden", "true");
2288
+ const shadow = host.attachShadow({ mode: "open" });
2289
+ const sheet = document.createElement("style");
2290
+ sheet.textContent = OVERLAY_CSS;
2291
+ root = document.createElement("div");
2292
+ root.className = "root";
2293
+ shadow.append(sheet, root);
2294
+ document.documentElement.appendChild(host);
2295
+ applyStyle();
2296
+ observer = new MutationObserver(syncToStage);
2297
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ["style"] });
2298
+ observer.observe(document.body, { attributes: true, attributeFilter: ["style"] });
2299
+ return root;
2300
+ }
2301
+ function clearOverlay() {
2302
+ observer?.disconnect();
2303
+ observer = null;
2304
+ host?.remove();
2305
+ host = null;
2306
+ root = null;
2292
2307
  }
2293
- var SKIP = new Set(["SCRIPT", "STYLE", "LINK", "TEMPLATE", "BR", "WBR", "SOURCE", "TRACK", "META"]);
2294
- var TOL_PX = 1;
2295
2308
  function div(cls, box) {
2296
2309
  const el = document.createElement("div");
2297
2310
  el.className = cls;
@@ -2305,55 +2318,45 @@
2305
2318
  el.style.width = `${r.w}px`;
2306
2319
  el.style.height = `${r.h}px`;
2307
2320
  }
2308
- function marginTint(b) {
2309
- const el = div("gp-ov-tint");
2321
+ function unsafeTint(b) {
2322
+ const el = div("gp-ov-tint", b.sheet);
2310
2323
  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)`;
2324
+ const s = b.safe;
2325
+ el.style.clipPath = `polygon(evenodd, ${t.x}px ${t.y}px, ${t.x + t.w}px ${t.y}px, ${t.x + t.w}px ${t.y + t.h}px, ${t.x}px ${t.y + t.h}px, ${t.x}px ${t.y}px, ` + `${s.x}px ${s.y}px, ${s.x}px ${s.y + s.h}px, ${s.x + s.w}px ${s.y + s.h}px, ${s.x + s.w}px ${s.y}px, ${s.x}px ${s.y}px)`;
2319
2326
  return el;
2320
2327
  }
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;
2328
+ var TOL_PX = 1;
2329
+ var SKIP = new Set(["SCRIPT", "STYLE", "LINK", "TEMPLATE", "BR", "WBR", "SOURCE", "TRACK", "META"]);
2330
+ function paintOverlay(pages, strips, area) {
2331
+ const layer = ensureRoot();
2332
+ syncToStage();
2333
+ const stage = stageFrame();
2329
2334
  const painted = new Map;
2330
2335
  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);
2336
+ const side = p.sheet.dataset.side === "verso" ? "verso" : "recto";
2337
+ const boxes = pageBoxes(area ?? sheetArea(p.sheet), side, PX_PER_PT);
2338
+ const rect = p.sheet.getBoundingClientRect();
2339
+ const host2 = div("gp-ov-page", {
2340
+ x: (rect.left - stage.left) / stage.zoom,
2341
+ y: (rect.top - stage.top) / stage.zoom,
2342
+ w: boxes.sheet.w,
2343
+ h: boxes.sheet.h
2344
+ });
2345
+ host2.dataset.page = String(p.index + 1);
2345
2346
  if (boxes.bleed > 0) {
2346
- const bb = boxes.bleedBox;
2347
+ const sh = boxes.sheet;
2347
2348
  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));
2349
+ const band = div("gp-ov-bleed", sh);
2350
+ band.style.borderWidth = `${t.y - sh.y}px ${sh.x + sh.w - (t.x + t.w)}px ${sh.y + sh.h - (t.y + t.h)}px ${t.x - sh.x}px`;
2351
+ host2.appendChild(band);
2352
+ host2.appendChild(div("gp-ov-trim", t));
2353
+ }
2354
+ if (area && area.safe > 0) {
2355
+ host2.appendChild(unsafeTint(boxes));
2356
+ host2.appendChild(div("gp-ov-safe", boxes.safe));
2352
2357
  }
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 });
2358
+ layer.appendChild(host2);
2359
+ painted.set(p.index, { host: host2, boxes, sheet: p.sheet, rect });
2357
2360
  }
2358
2361
  const flags = [];
2359
2362
  let issues = 0;
@@ -2405,11 +2408,11 @@
2405
2408
  visit(child);
2406
2409
  }
2407
2410
  for (const f of flags) {
2408
- const el = div(`gp-ov-flag`, f.box);
2411
+ const el = div("gp-ov-flag", f.box);
2409
2412
  el.dataset.kind = f.kind;
2410
2413
  f.host.appendChild(el);
2411
2414
  }
2412
- return inSheet ? { on: true, issues, intended, bleedPt: insetBleedPt, bleedInSheet: true } : { on: true, issues, intended, bleedPt };
2415
+ return { on: true, issues, intended, bleedPt: area?.bleed ?? 0, safePt: area?.safe ?? 0 };
2413
2416
  }
2414
2417
 
2415
2418
  // src/engine/viewer/decorate.ts
@@ -2444,7 +2447,7 @@
2444
2447
  let overlayPages = [];
2445
2448
  let overlaySummary = OVERLAY_OFF;
2446
2449
  const paintOverlayNow = () => {
2447
- overlaySummary = paintOverlay(overlayPages, layout.strips, documentBleedPt(model), configInsetBleedPt());
2450
+ overlaySummary = paintOverlay(overlayPages, layout.strips, printAreaFromMeta());
2448
2451
  window.dispatchEvent(new CustomEvent("gp:overlay", { detail: overlaySummary }));
2449
2452
  };
2450
2453
  const api = {
@@ -2459,11 +2462,12 @@
2459
2462
  },
2460
2463
  setOverlay(on, style) {
2461
2464
  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);
2465
+ const clean = sanitizeOverlayStyle(style);
2466
+ setOverlayStyle(clean);
2467
+ if (clean.strength)
2468
+ document.body.dataset.overlayStrength = clean.strength;
2469
+ if (clean.color)
2470
+ document.body.dataset.overlayColor = clean.color;
2467
2471
  }
2468
2472
  const wasOn = overlayOn;
2469
2473
  overlayOn = on;
@@ -2678,13 +2682,7 @@
2678
2682
  drawMarginBoxes(sheet, ctx, layout.totalPages - paddedPages.size);
2679
2683
  drawGuides(sheet, ctx);
2680
2684
  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
- });
2685
+ overlayPages.push({ index: bookIndex, sheet, strip });
2688
2686
  }
2689
2687
  const rows = Math.max(1, Math.ceil((strip.pages + shift) / perRow));
2690
2688
  run.style.height = `${rowStride * (rows - 1) + PX_PER_PT * g.height}px`;
@@ -2773,15 +2771,6 @@
2773
2771
  });
2774
2772
  sheet.appendChild(trim);
2775
2773
  }
2776
- const safe = document.createElement("div");
2777
- safe.className = "gp-guide-safe";
2778
- Object.assign(safe.style, {
2779
- left: px(g.margin.left),
2780
- top: px(g.margin.top),
2781
- width: px(g.width - g.margin.left - g.margin.right),
2782
- height: px(g.height - g.margin.top - g.margin.bottom)
2783
- });
2784
- sheet.appendChild(safe);
2785
2774
  }
2786
2775
  function checkOverflow(strip, out) {
2787
2776
  const h = strip.el.clientHeight;