gutterpress 0.11.20-alpha.3 → 0.11.20-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/api/index.js +2 -2
  2. package/dist/{build-a8ycsg80.js → build-epdcrm2c.js} +4 -4
  3. package/dist/{cli-9k2tb493.js → cli-0ey902sx.js} +90 -28
  4. package/dist/{cli-x4p99w8d.js → cli-61vd4ews.js} +83 -41
  5. package/dist/{cli-hn9pk0hp.js → cli-fmksazej.js} +1 -1
  6. package/dist/{cli-y9bcya7r.js → cli-h4tfmpg3.js} +2 -2
  7. package/dist/cli.js +12 -12
  8. package/dist/{doctor-wbjkrcpc.js → doctor-78es5scm.js} +2 -2
  9. package/dist/engine/compiler/build.d.ts +1 -1
  10. package/dist/engine/shared/margin-box-support.d.ts +2 -2
  11. package/dist/engine/shared/pin-edges.d.ts +127 -0
  12. package/dist/engine/shared/print-area.d.ts +68 -0
  13. package/dist/{engine-yxnnp3hx.js → engine-67f68bxs.js} +1 -1
  14. package/dist/{engine-fdtv01a2.js → engine-7ha27n2b.js} +2 -2
  15. package/dist/{ext-pesyrs68.js → ext-9mc7sr0d.js} +4 -4
  16. package/dist/{gutterpress-agent-cazqstr1.js → gutterpress-agent-mrdnwktc.js} +78 -13
  17. package/dist/{gutterpress-viewer-1yse8cx3.js → gutterpress-viewer-jr30yw8t.js} +143 -52
  18. package/dist/{index-r87y7wzw.js → index-ad95fws8.js} +82 -40
  19. package/dist/{index-zagagnwq.js → index-gm5xbx5w.js} +3 -3
  20. package/dist/{index-tjvrchxj.js → index-t13p8y08.js} +199 -137
  21. package/dist/index.js +3 -3
  22. package/dist/lib/extension-manifest.d.ts +13 -0
  23. package/dist/lib/markdown/assemble.d.ts +12 -2
  24. package/dist/lib/markdown/gutterpress-css.d.ts +46 -30
  25. package/dist/lib/markdown/index.d.ts +5 -0
  26. package/dist/{manifest.schema-c9x8912a.json → manifest.schema-13dfax80.json} +3 -3
  27. package/dist/{new-8yah8z91.js → new-s4f0tnya.js} +4 -4
  28. package/dist/{preflight-8ccx4kdc.js → preflight-7ck4rbsq.js} +4 -4
  29. package/dist/preview/file-watcher.d.ts +4 -10
  30. package/dist/{preview-w7v9w36q.js → preview-j91sxadd.js} +4 -4
  31. package/dist/{publish-g5nh5myh.js → publish-f12xaenm.js} +4 -4
  32. package/dist/render.js +57 -6
  33. package/dist/schema/manifest.types.d.ts +8 -6
  34. package/dist/{validate-1yh59x0x.js → validate-pb5gysvr.js} +4 -4
  35. package/package.json +1 -1
  36. package/dist/engine/shared/flush.d.ts +0 -59
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  UsageError,
7
7
  package_default,
8
8
  rejectUnknownFlags
9
- } from "./cli-hn9pk0hp.js";
9
+ } from "./cli-fmksazej.js";
10
10
  import {
11
11
  EXIT_CODES
12
12
  } from "./cli-46ycxe6r.js";
@@ -18,14 +18,14 @@ import {
18
18
  import { defineCommand, parseArgs, runMain } from "citty";
19
19
  import { statSync } from "node:fs";
20
20
  var SUBCOMMANDS = {
21
- new: () => import("./new-8yah8z91.js").then((m) => m.default),
22
- preview: () => import("./preview-w7v9w36q.js").then((m) => m.default),
23
- build: () => import("./build-a8ycsg80.js").then((m) => m.default),
24
- publish: () => import("./publish-g5nh5myh.js").then((m) => m.default),
25
- validate: () => import("./validate-1yh59x0x.js").then((m) => m.default),
26
- preflight: () => import("./preflight-8ccx4kdc.js").then((m) => m.default),
27
- doctor: () => import("./doctor-wbjkrcpc.js").then((m) => m.default),
28
- ext: () => import("./ext-pesyrs68.js").then((m) => m.default)
21
+ new: () => import("./new-s4f0tnya.js").then((m) => m.default),
22
+ preview: () => import("./preview-j91sxadd.js").then((m) => m.default),
23
+ build: () => import("./build-epdcrm2c.js").then((m) => m.default),
24
+ publish: () => import("./publish-f12xaenm.js").then((m) => m.default),
25
+ validate: () => import("./validate-pb5gysvr.js").then((m) => m.default),
26
+ preflight: () => import("./preflight-7ck4rbsq.js").then((m) => m.default),
27
+ doctor: () => import("./doctor-78es5scm.js").then((m) => m.default),
28
+ ext: () => import("./ext-9mc7sr0d.js").then((m) => m.default)
29
29
  };
30
30
  var VERSION = package_default.version;
31
31
  var main = defineCommand({
@@ -50,7 +50,7 @@ async function preflightRequiredInvocations(rawArgs) {
50
50
  const [command, ...commandArgs] = rawArgs;
51
51
  try {
52
52
  if (command === "new") {
53
- const { newArgs } = await import("./new-8yah8z91.js");
53
+ const { newArgs } = await import("./new-s4f0tnya.js");
54
54
  rejectUnknownFlags(commandArgs, newArgs, "new");
55
55
  const parsed = parseArgs(commandArgs, {
56
56
  ...newArgs,
@@ -62,7 +62,7 @@ async function preflightRequiredInvocations(rawArgs) {
62
62
  return;
63
63
  }
64
64
  if (command === "preflight") {
65
- const { preflightArgs } = await import("./preflight-8ccx4kdc.js");
65
+ const { preflightArgs } = await import("./preflight-7ck4rbsq.js");
66
66
  rejectUnknownFlags(commandArgs, preflightArgs, "preflight");
67
67
  const parsed = parseArgs(commandArgs, {
68
68
  ...preflightArgs,
@@ -82,7 +82,7 @@ async function preflightRequiredInvocations(rawArgs) {
82
82
  }
83
83
  if (subcommand.startsWith("-"))
84
84
  return;
85
- const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-pesyrs68.js");
85
+ const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-9mc7sr0d.js");
86
86
  if (!EXT_SUBCOMMANDS.includes(subcommand)) {
87
87
  throw new UsageError(`gutterpress ext: unknown command "${subcommand}"`);
88
88
  }
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  getSystemDiagnostics,
3
3
  log
4
- } from "./cli-y9bcya7r.js";
4
+ } from "./cli-h4tfmpg3.js";
5
5
  import {
6
6
  UsageError,
7
7
  rejectExtraPositionals,
8
8
  rejectUnknownFlags
9
- } from "./cli-hn9pk0hp.js";
9
+ } from "./cli-fmksazej.js";
10
10
  import"./cli-46ycxe6r.js";
11
11
  import"./cli-asap4t1z.js";
12
12
  import"./cli-37x76zdn.js";
@@ -8,7 +8,7 @@ import { type PostprocessResult } from "./postprocess.ts";
8
8
  * set is closed here so a surface's label table can be asserted complete
9
9
  * against it rather than drifting silently as checks are added.
10
10
  */
11
- export type BuildDiagnosticCode = "engine.width.overflow" | "engine.width.intrinsic" | "engine.xref.broken" | "engine.abspos.leak" | "engine.layer.trapped" | "engine.multicol.dead-column" | "engine.content.overheight" | "engine.image.low-dpi" | "engine.flush.margin-box" | "engine.page-background.unreferenced" | "engine.hyphenation.unsupported";
11
+ export type BuildDiagnosticCode = "engine.width.overflow" | "engine.width.intrinsic" | "engine.xref.broken" | "engine.abspos.leak" | "engine.layer.trapped" | "engine.multicol.dead-column" | "engine.content.overheight" | "engine.image.low-dpi" | "engine.pin.margin-box" | "engine.page-background.unreferenced" | "engine.hyphenation.unsupported";
12
12
  export declare const BUILD_DIAGNOSTIC_CODES: readonly BuildDiagnosticCode[];
13
13
  export interface BuildDiagnostic {
14
14
  code: BuildDiagnosticCode;
@@ -31,8 +31,8 @@ export declare function isIgnoredMarginBoxProperty(property: string): boolean;
31
31
  * Geometry of each of the 16 margin boxes, per CSS Paged Media §5.3, in pt
32
32
  * SHEET coordinates (origin at the sheet's top-left). One implementation for
33
33
  * both furniture painters: the viewer's `drawMarginBoxes` overlays and the
34
- * compiler's `.gp-flush` furniture relocation must place a box at the same
35
- * spot or preview and print disagree about where the folio sits.
34
+ * compiler's relocation of a pinned edge's furniture must place a box at the
35
+ * same spot or preview and print disagree about where the folio sits.
36
36
  */
37
37
  export declare function marginBoxRectPt(name: string, g: {
38
38
  width: number;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Pinned-art policy — shared by the compiler and the viewer, like every rule
3
+ * that DECIDES something (see synthesis.ts's header).
4
+ *
5
+ * THE MODEL: a pin box the text never shares. A `.gp-pin` with an edge word
6
+ * (`.gp-top`, `.gp-right`, `.gp-bottom`, `.gp-left`) positions itself inside
7
+ * its page root (`.page`/`.spread`, `position: relative` in MARKER_CSS), and
8
+ * where it sits has no effect on the text:
9
+ *
10
+ * 1. The page MARGIN on each pinned edge is freed. Chromium's printable area
11
+ * IS the page area — MEASURED, a pinned box pulled into the margin with
12
+ * negative insets fragments onto the NEXT sheet, one moved there with a
13
+ * transform is clipped away entirely, and a margin box in a ~zero margin
14
+ * (1px, with padding/height compensation) simply does not render. So the
15
+ * only way art reaches the paper is page GEOMETRY, per page:
16
+ * - the COMPILER aliases the root's own page context under a generated
17
+ * name (verbatim rule copies — never a resolved flatten, which is the
18
+ * "re-implement the @page cascade" trap tier2's history records),
19
+ * zeroes the pinned margins on the alias, and assigns the root to it;
20
+ * - the VIEWER keeps the author's page context and frees the margins in
21
+ * the run's strip geometry, from these same functions.
22
+ * 2. The root gets that same margin back as PADDING (`box-decoration-break:
23
+ * clone`, so every fragment of a root that spans pages keeps it), added
24
+ * to any padding the root has of its own. Its content box is therefore
25
+ * the author's content box — same width, same height, same place, same
26
+ * line and page breaks — whatever is pinned where. That is the whole
27
+ * point: freeing the margin once let the text grow into it. (What the
28
+ * root paints itself — a background, a border — does grow with it: the
29
+ * root's box is the page area, and that area reaches the paper.)
30
+ * 3. The pin box is the root's padding box — which now reaches the paper on
31
+ * the pinned edges — inset by the printer's ART-SAFE line: `--gp-pin-<edge>`
32
+ * on the root, read by `.gp-pin`'s four insets (GUTTERPRESS_CSS). On the
33
+ * top, bottom and outside edge that is the bleed plus `page.artSafe`; at
34
+ * the binding edge `page.safe` (no bleed there) — the manifest's print
35
+ * area (print-area.ts), so a book with none sets 0, the paper's edge.
36
+ * 4. `.gp-flush` on the pin zeroes the four properties in plain CSS: the
37
+ * opt-in escape hatch to the paper's edge (the markdown pipeline warns).
38
+ * Nothing else here knows about it.
39
+ *
40
+ * Sides. Recto values are the defaults (`@page :right`'s margins, the
41
+ * binding on the left). Mirrored `:left`/`:right` margins agree on the content
42
+ * SIZE, so the viewer paginates identically either way; each engine corrects a
43
+ * verso root once it knows the side — the viewer after it measures its
44
+ * strips, the compiler in its synthesis step (`setPinGeometry`).
45
+ *
46
+ * Margin boxes live IN the margin, so a freed edge's furniture (folio, running
47
+ * head) cannot render natively there — MEASURED, no compensation trick
48
+ * survives. Both renderers therefore keep painting that furniture themselves
49
+ * at its ORIGINAL coordinates: the viewer already synthesizes all furniture as
50
+ * DOM; the compiler injects the freed edge's boxes into the page root as
51
+ * positioned elements with engine-resolved text. Nothing the author declared
52
+ * is lost — that is the contract this module's box map exists to keep.
53
+ *
54
+ * Everything the author's document gets is standard CSS: a padding, a custom
55
+ * property per edge, absolute insets, and (print only) a named page.
56
+ */
57
+ import type { PageGeometry } from "./gcpm-extract.ts";
58
+ import type { PageSide, PrintArea } from "./print-area.ts";
59
+ export declare const PIN_EDGES: readonly ["top", "right", "bottom", "left"];
60
+ export type PinEdge = (typeof PIN_EDGES)[number];
61
+ /** Every pin that names an edge. */
62
+ export declare const EDGE_PIN_SELECTOR: string;
63
+ /** Minimal DOM shapes `pinEdgesIn` needs — keeps this module node-safe. */
64
+ interface PinElement {
65
+ /** an HTML element's containing block for abspos (`HTMLElement.offsetParent`) */
66
+ readonly offsetParent?: unknown;
67
+ readonly classList: {
68
+ contains(token: string): boolean;
69
+ };
70
+ }
71
+ interface QueryRoot {
72
+ querySelectorAll(selector: string): ArrayLike<PinElement>;
73
+ }
74
+ /**
75
+ * The edges the pins of page root `root` use, in canonical t/r/b/l order — a
76
+ * pin counts when `root` is its containing block (its `offsetParent`): a pin
77
+ * in a nested page root belongs to that root, and one a positioned card
78
+ * frames belongs to the card, so neither frees this root's margins. A pin
79
+ * without an edge word frees nothing and centres in the text block.
80
+ */
81
+ export declare function pinEdgesIn(root: QueryRoot): PinEdge[];
82
+ /** Canonical short key for an edge set: "b", "rb", "trbl"… (t/r/b/l order). */
83
+ export declare function pinKey(edges: readonly PinEdge[]): string;
84
+ /**
85
+ * Generated page name for (author page context, edge set). The `gp--` double
86
+ * dash marks an engine-generated page (the `gp--blank` convention); these
87
+ * exist only in builds that actually contain an edge pin, which is why the
88
+ * width check may treat them as real author contexts.
89
+ */
90
+ export declare function pinPageName(authorPage: string | undefined, edges: readonly PinEdge[]): string;
91
+ /** The page's margins with the pinned edges freed. */
92
+ export declare function flushMargins(margin: PageGeometry["margin"], edges: readonly PinEdge[]): PageGeometry["margin"];
93
+ /**
94
+ * The art-safe line's distance from the sheet edge on each pinned edge, in pt:
95
+ * the bleed plus `artSafe` on the top, bottom and outside edge, `safe` on the
96
+ * binding edge (the left of a recto, the right of a verso), 0 on an edge the
97
+ * pins do not use (the pin box's edge is the text block's there). No print
98
+ * area means 0 everywhere: the paper's edge.
99
+ */
100
+ export declare function pinInsets(area: Pick<PrintArea, "bleed" | "safe" | "artSafe"> | null, side: PageSide, edges: readonly PinEdge[]): Record<PinEdge, number>;
101
+ /** No padding of the root's own. */
102
+ export declare const NO_PADDING: Readonly<Record<PinEdge, number>>;
103
+ /**
104
+ * A pin root's own declarations for one side, in px (pt × 96/72, like the
105
+ * strip variables): the freed margins back as padding, cloned onto every
106
+ * fragment, and the four inset properties. `margin` is the ORIGINAL margin of
107
+ * the page context the root lands on; `padding` is the root's own (author)
108
+ * padding in px, which the freed margin is added to rather than replacing,
109
+ * so a padded root's text stays put too. All four properties are set, so a
110
+ * pin never reads another root's values.
111
+ */
112
+ export declare function pinRootStyle(margin: PageGeometry["margin"], area: Pick<PrintArea, "bleed" | "safe" | "artSafe"> | null, side: PageSide, edges: readonly PinEdge[], padding?: Readonly<Record<PinEdge, number>>): Record<string, string>;
113
+ /** A computed style's four paddings, in px. */
114
+ export declare function paddingOf(cs: {
115
+ paddingTop: string;
116
+ paddingRight: string;
117
+ paddingBottom: string;
118
+ paddingLeft: string;
119
+ }): Record<PinEdge, number>;
120
+ /**
121
+ * Margin boxes that live (wholly or partly) in a freed edge's margin area —
122
+ * the set both renderers must keep painting themselves. A corner box belongs
123
+ * to BOTH of its edges: `bottom-left-corner` dies when either the bottom or
124
+ * the left margin is freed.
125
+ */
126
+ export declare function marginBoxesOnEdges(edges: readonly PinEdge[]): string[];
127
+ export {};
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The project's print area: the manifest's `page` block, carried into every
3
+ * assembled book (preview, build, publish) as ONE
4
+ * `<meta name="gp-print-area">` by the markdown assembly
5
+ * (lib/markdown/assemble.ts) and read back here by everything that needs it:
6
+ * the preview's print overlays (viewer/overlay.ts) and both engines' pinned
7
+ * art (pin-edges.ts — a pin sits on the printer's art-safe line).
8
+ *
9
+ * Only the manifest feeds it — `page.width` x `page.height` (the sheet),
10
+ * `page.bleed` (a bleed the sheet already contains on the top, bottom and
11
+ * OUTER edge; the binding edge has none, as DriveThruRPG cuts it),
12
+ * `page.safe` (the TEXT rule: its inset from the trim on every edge) and
13
+ * `page.artSafe` (the ART rule: its inset on the top, bottom and outer edge,
14
+ * while the binding edge takes `safe`; absent = `safe`) — plus the preset's
15
+ * id and the printer spec it follows, which only word what the overlay finds.
16
+ * No book or plugin CSS feeds it: not the `@page` margins (they are where the
17
+ * author put the TEXT, which is not the printer's safe area), not
18
+ * `@page { bleed }`.
19
+ *
20
+ * The meta is metadata: it changes nothing a browser renders on its own.
21
+ * Node-safe: the writer is pure, and the reader takes the document it reads.
22
+ */
23
+ /** The project's page geometry, in pt — the manifest's `page` block. */
24
+ export interface PrintArea {
25
+ width: number;
26
+ height: number;
27
+ /** in-sheet bleed on the top, bottom and outer edge; 0 = none */
28
+ bleed: number;
29
+ /** the TEXT rule: inset from the trim on every edge; 0 = none */
30
+ safe: number;
31
+ /** the ART rule: inset on the top, bottom and outer edge (the binding edge takes `safe`) */
32
+ artSafe: number;
33
+ /** the manifest's preset id, and the printer spec it follows: they only word what is found */
34
+ preset?: string;
35
+ spec?: {
36
+ label: string;
37
+ url: string;
38
+ };
39
+ }
40
+ /** Which side of the spread a page is: page 1 is a recto, so every odd page is one. */
41
+ export type PageSide = "recto" | "verso";
42
+ /** What a host knows from its resolved config: the `page` block, the preset and its spec. */
43
+ export interface PrintAreaSource {
44
+ width: number;
45
+ height: number;
46
+ bleed?: number;
47
+ safe?: number;
48
+ artSafe?: number;
49
+ preset?: string;
50
+ spec?: {
51
+ label: string;
52
+ url: string;
53
+ };
54
+ }
55
+ /**
56
+ * The `<meta>` tag for one book. An unset bleed or text rule is written as 0,
57
+ * never left out; an unset art rule stays out (the reader takes it as `safe`).
58
+ * Anything else the config's `page` block holds (`tolerance`) is not part of it.
59
+ */
60
+ export declare function printAreaMeta(page: PrintAreaSource): string;
61
+ /** The one DOM call the reader makes: a `Document` satisfies it. */
62
+ export interface MetaDocument {
63
+ querySelector(selector: string): {
64
+ getAttribute(name: string): string | null;
65
+ } | null;
66
+ }
67
+ /** The book's print area from its `<meta name="gp-print-area">`; `null` without one. */
68
+ export declare function printAreaFromMeta(doc: MetaDocument): PrintArea | null;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  build
3
- } from "./cli-x4p99w8d.js";
3
+ } from "./cli-61vd4ews.js";
4
4
  import {
5
5
  BuildError
6
6
  } from "./cli-46ycxe6r.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  build
3
- } from "./index-r87y7wzw.js";
3
+ } from "./index-ad95fws8.js";
4
4
  import {
5
5
  BuildError
6
- } from "./index-zagagnwq.js";
6
+ } from "./index-gm5xbx5w.js";
7
7
  import"./index-bt0qws5c.js";
8
8
  import"./index-37x76zdn.js";
9
9
 
@@ -12,17 +12,17 @@ import {
12
12
  searchNpmExtensions,
13
13
  setExtensionEnabled,
14
14
  updateExtensions
15
- } from "./cli-9k2tb493.js";
16
- import"./cli-y9bcya7r.js";
15
+ } from "./cli-0ey902sx.js";
16
+ import"./cli-h4tfmpg3.js";
17
17
  import {
18
18
  UsageError,
19
19
  exitForUsage,
20
20
  rejectExtraPositionals,
21
21
  rejectUnknownFlags,
22
22
  resolveProjectDir
23
- } from "./cli-hn9pk0hp.js";
23
+ } from "./cli-fmksazej.js";
24
24
  import"./cli-ht1bcqdd.js";
25
- import"./cli-x4p99w8d.js";
25
+ import"./cli-61vd4ews.js";
26
26
  import {
27
27
  EXIT_CODES
28
28
  } from "./cli-46ycxe6r.js";
@@ -43,11 +43,13 @@
43
43
  xrefSites: () => xrefSites,
44
44
  targetTexts: () => targetTexts,
45
45
  stringSources: () => stringSources,
46
+ setPinGeometry: () => setPinGeometry,
47
+ setPinFurniture: () => setPinFurniture,
46
48
  setGenerated: () => setGenerated,
47
- setFlushFurniture: () => setFlushFurniture,
49
+ printArea: () => printArea,
50
+ pinRoots: () => pinRoots,
48
51
  instrument: () => instrument,
49
52
  forcedBreakSites: () => forcedBreakSites,
50
- flushRoots: () => flushRoots,
51
53
  fillLeaders: () => fillLeaders,
52
54
  counterResetSites: () => counterResetSites,
53
55
  collectCss: () => collectCss,
@@ -302,6 +304,51 @@
302
304
  return evaluateContent(parseContent(value), ctx);
303
305
  }
304
306
 
307
+ // src/engine/shared/pin-edges.ts
308
+ var PIN_EDGES = ["top", "right", "bottom", "left"];
309
+ var EDGE_PIN_SELECTOR = PIN_EDGES.map((edge) => `.gp-pin.gp-${edge}`).join(", ");
310
+ function pinEdgesIn(root) {
311
+ const pins = Array.from(root.querySelectorAll(EDGE_PIN_SELECTOR)).filter((pin) => pin.offsetParent === root);
312
+ return PIN_EDGES.filter((edge) => pins.some((pin) => pin.classList.contains(`gp-${edge}`)));
313
+ }
314
+ function pinKey(edges) {
315
+ return PIN_EDGES.filter((e) => edges.includes(e)).map((e) => e[0]).join("");
316
+ }
317
+ function paddingOf(cs) {
318
+ return {
319
+ top: parseFloat(cs.paddingTop) || 0,
320
+ right: parseFloat(cs.paddingRight) || 0,
321
+ bottom: parseFloat(cs.paddingBottom) || 0,
322
+ left: parseFloat(cs.paddingLeft) || 0
323
+ };
324
+ }
325
+
326
+ // src/engine/shared/print-area.ts
327
+ function printAreaFromMeta(doc) {
328
+ const raw = doc.querySelector('meta[name="gp-print-area"]')?.getAttribute("content");
329
+ if (!raw)
330
+ return null;
331
+ try {
332
+ const v = JSON.parse(raw);
333
+ const n = (x) => typeof x === "number" && Number.isFinite(x) && x > 0 ? x : 0;
334
+ if (!n(v.width) || !n(v.height))
335
+ return null;
336
+ const safe = n(v.safe);
337
+ const spec = v.spec && typeof v.spec.label === "string" && typeof v.spec.url === "string" && /^https?:\/\//i.test(v.spec.url) ? v.spec : undefined;
338
+ return {
339
+ width: n(v.width),
340
+ height: n(v.height),
341
+ bleed: n(v.bleed),
342
+ safe,
343
+ artSafe: typeof v.artSafe === "number" ? n(v.artSafe) : safe,
344
+ preset: typeof v.preset === "string" ? v.preset : undefined,
345
+ spec
346
+ };
347
+ } catch {
348
+ return null;
349
+ }
350
+ }
351
+
305
352
  // src/engine/compiler/agent.ts
306
353
  var uid = 0;
307
354
  function ensureAnchor(el) {
@@ -559,29 +606,45 @@
559
606
  addCss("gp-generated-content", css);
560
607
  return entries.length;
561
608
  }
562
- function flushRoots() {
609
+ function pinRoots() {
563
610
  const out = [];
564
611
  for (const root of Array.from(document.querySelectorAll(".page, .spread"))) {
565
- const edges = ["top", "right", "bottom", "left"].filter((edge) => root.querySelector(`.gp-pin.gp-flush.gp-${edge}`));
612
+ const edges = pinEdgesIn(root);
566
613
  if (!edges.length)
567
614
  continue;
568
- const page = getComputedStyle(root).page;
569
- const key = `${page === "auto" ? "" : page}|${edges.map((e) => e[0]).join("")}`;
570
- root.dataset.gpFlush = key;
571
- out.push({ id: ensureAnchor(root), page, edges, key });
615
+ const cs = getComputedStyle(root);
616
+ const padding = paddingOf(cs);
617
+ const key = `${cs.page === "auto" ? "" : cs.page}|${pinKey(edges)}|${edges.map((e) => padding[e]).join(",")}`;
618
+ root.dataset.gpPin = key;
619
+ out.push({ id: ensureAnchor(root), page: cs.page, edges, padding, key });
572
620
  }
573
621
  return out;
574
622
  }
575
- function setFlushFurniture(items) {
623
+ function printArea() {
624
+ return printAreaFromMeta(document);
625
+ }
626
+ function setPinGeometry(items) {
627
+ let set = 0;
628
+ for (const item of items) {
629
+ const host = anchorHost(item.id);
630
+ if (!host)
631
+ continue;
632
+ for (const [prop, value] of Object.entries(item.style))
633
+ host.style.setProperty(prop, value);
634
+ set++;
635
+ }
636
+ return set;
637
+ }
638
+ function setPinFurniture(items) {
576
639
  let painted = 0;
577
640
  for (const item of items) {
578
641
  const host = anchorHost(item.id);
579
642
  if (!host)
580
643
  continue;
581
- let layer = host.querySelector(":scope > .gp-flush-furniture");
644
+ let layer = host.querySelector(":scope > .gp-pin-furniture");
582
645
  if (!layer) {
583
646
  layer = document.createElement("div");
584
- layer.className = "gp-flush-furniture";
647
+ layer.className = "gp-pin-furniture";
585
648
  layer.setAttribute("aria-hidden", "true");
586
649
  layer.setAttribute("style", "position:absolute;inset:0;pointer-events:none;z-index:10;");
587
650
  host.appendChild(layer);
@@ -616,8 +679,10 @@
616
679
  instrument,
617
680
  addCss,
618
681
  setGenerated,
619
- flushRoots,
620
- setFlushFurniture
682
+ pinRoots,
683
+ printArea,
684
+ setPinGeometry,
685
+ setPinFurniture
621
686
  };
622
687
  if (typeof window !== "undefined")
623
688
  window.__gp = api;