gutterpress 0.10.5 → 0.10.6-beta.2
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.js +1 -1
- package/dist/{audit-ax5ps29k.js → audit-gwhqj45v.js} +4 -4
- package/dist/{build-79dv44cn.js → build-by1rwswq.js} +4 -4
- package/dist/{cli-v031r2h6.js → cli-980t4qp9.js} +1 -1
- package/dist/{cli-0t4zfevc.js → cli-bwbs3qm4.js} +1 -1
- package/dist/{cli-chkwtg9m.js → cli-nde3d8wk.js} +341 -192
- package/dist/{cli-5pq315qe.js → cli-vntks53w.js} +1 -1
- package/dist/cli.js +14 -14
- package/dist/{doctor-qejmbv32.js → doctor-gxetpd6x.js} +2 -2
- package/dist/{engine-mtyjc4v6.js → engine-4ctgbwj5.js} +1 -1
- package/dist/{engine-fwe8djyn.js → engine-bhhcne06.js} +1 -1
- package/dist/{index-ya1yw5me.js → index-dxvn0dr9.js} +160 -11
- package/dist/{index-ja0p4w5f.js → index-eyzv51dw.js} +1 -1
- package/dist/index.js +2 -2
- package/dist/lib/markdown/assemble.d.ts +3 -1
- package/dist/lib/markdown/gutterpress-css.d.ts +31 -5
- package/dist/lib/markdown/markers.d.ts +79 -3
- package/dist/lib/pdf-inspect.d.ts +33 -5
- package/dist/lib/render-parity.d.ts +108 -0
- package/dist/{lint-wweg6pf0.js → lint-p5m5gn2f.js} +4 -4
- package/dist/{new-yf8emyp3.js → new-cgnm1vz6.js} +4 -4
- package/dist/{plugin-dj6crat4.js → plugin-2zwx1tex.js} +4 -4
- package/dist/{preflight-d34pzgr6.js → preflight-gypyj54v.js} +4 -4
- package/dist/{preview-g2f5982y.js → preview-670h69jp.js} +4 -4
- package/dist/{publish-6vt6yqm4.js → publish-zw39dmgm.js} +4 -4
- package/dist/render.js +146 -6
- package/dist/{validate-vjwgxswp.js → validate-2fnfyyx8.js} +4 -4
- package/package.json +1 -1
|
@@ -77,7 +77,21 @@ export declare function getPageLabels(doc: PDFDocumentProxy): Promise<string[] |
|
|
|
77
77
|
* for the boolean "are there any links?" question (TOC-links check).
|
|
78
78
|
*/
|
|
79
79
|
export declare function countLinkAnnotations(doc: PDFDocumentProxy, stopAtFirst?: boolean): Promise<number>;
|
|
80
|
-
|
|
80
|
+
/** One non-whitespace text run's exact string plus its extents, in PDF points. */
|
|
81
|
+
export interface TextRun {
|
|
82
|
+
/** Verbatim run string (never normalized/rounded) — pdf.js item `str`. */
|
|
83
|
+
s: string;
|
|
84
|
+
/** Baseline origin x — item `transform[4]`. */
|
|
85
|
+
x: number;
|
|
86
|
+
/** Baseline origin y — item `transform[5]`. */
|
|
87
|
+
y: number;
|
|
88
|
+
/** Advance width, "in device space" per pdf.js (points, at the unscaled
|
|
89
|
+
* viewport this module always reads at — see getPageSize). */
|
|
90
|
+
w: number;
|
|
91
|
+
/** Glyph height, same space as `w`. */
|
|
92
|
+
h: number;
|
|
93
|
+
}
|
|
94
|
+
export interface TextPass {
|
|
81
95
|
/** Non-whitespace-stripped text per page (index 0 = page 1). */
|
|
82
96
|
textByPage: string[];
|
|
83
97
|
/** Every text run's baseline origin, for layout-variance analysis. */
|
|
@@ -85,22 +99,37 @@ interface TextPass {
|
|
|
85
99
|
x: number;
|
|
86
100
|
y: number;
|
|
87
101
|
}>;
|
|
102
|
+
/**
|
|
103
|
+
* Every non-whitespace text run's exact string + extents, per page (index
|
|
104
|
+
* 0 = page 1) — same filter as `positions` above (whitespace-only items
|
|
105
|
+
* excluded), plus the run's width/height. Added for the render-parity gate
|
|
106
|
+
* (render-parity.ts's `extractReport`); `textByPage`/`positions` are
|
|
107
|
+
* untouched and still hold their original values.
|
|
108
|
+
*/
|
|
109
|
+
runsByPage: TextRun[][];
|
|
88
110
|
}
|
|
89
111
|
export declare function getTextPass(doc: PDFDocumentProxy): Promise<TextPass>;
|
|
90
|
-
interface ImageRef {
|
|
112
|
+
export interface ImageRef {
|
|
91
113
|
/** XObject resource name (or "(inline)" for inline images). */
|
|
92
114
|
name: string;
|
|
93
115
|
/** Rendered width/height on the page, in points (from the CTM). */
|
|
94
116
|
placedW: number;
|
|
95
117
|
placedH: number;
|
|
118
|
+
/**
|
|
119
|
+
* Placed origin, in points — the CTM's translation components (`ctm[4]`,
|
|
120
|
+
* `ctm[5]`). Added for the render-parity gate (render-parity.ts); `name`/
|
|
121
|
+
* `placedW`/`placedH`/`page` are untouched.
|
|
122
|
+
*/
|
|
123
|
+
x: number;
|
|
124
|
+
y: number;
|
|
96
125
|
/** 1-based page number. */
|
|
97
126
|
page: number;
|
|
98
127
|
}
|
|
99
|
-
interface FontRef {
|
|
128
|
+
export interface FontRef {
|
|
100
129
|
name: string;
|
|
101
130
|
embedded: boolean;
|
|
102
131
|
}
|
|
103
|
-
interface OpPass {
|
|
132
|
+
export interface OpPass {
|
|
104
133
|
imagesByPage: Map<number, ImageRef[]>;
|
|
105
134
|
fonts: FontRef[];
|
|
106
135
|
}
|
|
@@ -120,4 +149,3 @@ export declare function getImageResolutions(doc: PDFDocumentProxy): Promise<Arra
|
|
|
120
149
|
* catches PDFs that are actually broken/unparseable (ADR 0002).
|
|
121
150
|
*/
|
|
122
151
|
export declare function isLoadable(doc: PDFDocumentProxy): Promise<boolean>;
|
|
123
|
-
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/** Bumped only if the canonical report shape changes in a way old reports
|
|
2
|
+
* cannot be compared against (e.g. a committed baseline in the book repo). */
|
|
3
|
+
export declare const REPORT_VERSION = 1;
|
|
4
|
+
export interface TextRunReport {
|
|
5
|
+
s: string;
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
w: number;
|
|
9
|
+
h: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ImageReport {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
w: number;
|
|
15
|
+
h: number;
|
|
16
|
+
}
|
|
17
|
+
export interface PageReport {
|
|
18
|
+
w: number;
|
|
19
|
+
h: number;
|
|
20
|
+
text: TextRunReport[];
|
|
21
|
+
images: ImageReport[];
|
|
22
|
+
}
|
|
23
|
+
export interface Report {
|
|
24
|
+
version: 1;
|
|
25
|
+
pageCount: number;
|
|
26
|
+
pages: PageReport[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Extract the canonical parity report for one PDF. Keys are always written in
|
|
30
|
+
* the same order (`version`, `pageCount`, `pages`; each page `w`, `h`,
|
|
31
|
+
* `text`, `images`; each run/image field in the order declared above) so
|
|
32
|
+
* `serializeReport` produces byte-identical output for byte-identical input,
|
|
33
|
+
* and so a committed baseline report diffs sanely in git.
|
|
34
|
+
*/
|
|
35
|
+
export declare function extractReport(pdfPath: string): Promise<Report>;
|
|
36
|
+
/**
|
|
37
|
+
* Fixed layout serialization: `JSON.stringify(report, null, 1)` plus a
|
|
38
|
+
* trailing newline. Two extractions of the same PDF are byte-identical
|
|
39
|
+
* strings — the acceptance test for this whole gate — because `extractReport`
|
|
40
|
+
* always builds the object in the same key order and every number is already
|
|
41
|
+
* rounded to 3 decimals before it gets here.
|
|
42
|
+
*/
|
|
43
|
+
export declare function serializeReport(report: Report): string;
|
|
44
|
+
export type WaiverKind = "page-count" | "page-size" | "text" | "image";
|
|
45
|
+
export interface Waiver {
|
|
46
|
+
/** 1-based page number. Omit for kind "page-count" (there is no page). */
|
|
47
|
+
page?: number;
|
|
48
|
+
kind: WaiverKind;
|
|
49
|
+
/** Optional substring the text run's string must contain. Only meaningful
|
|
50
|
+
* for kind "text"; ignored for the other kinds. */
|
|
51
|
+
match?: string;
|
|
52
|
+
/** Non-empty, one line: why this diff is expected/acceptable. Required —
|
|
53
|
+
* a waiver with no reason is a usage error, not a silent pass. */
|
|
54
|
+
reason: string;
|
|
55
|
+
}
|
|
56
|
+
export interface Diff {
|
|
57
|
+
/** 1-based page number. Absent for kind "page-count". */
|
|
58
|
+
page?: number;
|
|
59
|
+
kind: WaiverKind;
|
|
60
|
+
/** Index within the page's text/image array, when the diff is about one
|
|
61
|
+
* run/image rather than the array's length. */
|
|
62
|
+
index?: number;
|
|
63
|
+
before: string;
|
|
64
|
+
after: string;
|
|
65
|
+
/** The base run's string, for kind "text" — what a waiver's `match` tests
|
|
66
|
+
* against, and what a human reads to find the run in the source. */
|
|
67
|
+
text?: string;
|
|
68
|
+
}
|
|
69
|
+
export interface CompareOptions {
|
|
70
|
+
/** Points. Applies to text run and image extents (x/y/w/h). Default 0.5. */
|
|
71
|
+
tolerance?: number;
|
|
72
|
+
waivers?: Waiver[];
|
|
73
|
+
}
|
|
74
|
+
export interface CompareResult {
|
|
75
|
+
/** Diffs NOT excused by any waiver — non-empty means the gate fails. */
|
|
76
|
+
diffs: Diff[];
|
|
77
|
+
/** Diffs that were found but matched a waiver. */
|
|
78
|
+
waived: Diff[];
|
|
79
|
+
/** Waivers that matched nothing this run — stale, should be deleted. */
|
|
80
|
+
unusedWaivers: Waiver[];
|
|
81
|
+
}
|
|
82
|
+
/** A waiver failed validation (currently: missing/empty `reason`). The CLI
|
|
83
|
+
* treats this as a usage error (exit 2), distinct from a real diff (exit 1). */
|
|
84
|
+
export declare class WaiverValidationError extends Error {
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Compare two reports. Throws {@link WaiverValidationError} (a usage error,
|
|
88
|
+
* not a diff) if any waiver is missing its reason — validated up front, before
|
|
89
|
+
* any comparison work, so a malformed waivers file never silently passes.
|
|
90
|
+
*
|
|
91
|
+
* Every waiver is matched against the FULL diff set (not a display-truncated
|
|
92
|
+
* one — see `formatDiffs` for the printed-output cap) so an unwaived diff
|
|
93
|
+
* past the cap still fails the gate, and a waiver is reported unused only
|
|
94
|
+
* when it genuinely excused nothing.
|
|
95
|
+
*/
|
|
96
|
+
export declare function compareReports(base: Report, cand: Report, options?: CompareOptions): CompareResult;
|
|
97
|
+
/**
|
|
98
|
+
* Render a `CompareResult` as the lines the CLI prints: every unwaived diff
|
|
99
|
+
* (capped per page — see `MAX_LINES_PER_PAGE`), every waived diff (marked, so
|
|
100
|
+
* a reviewer can see what was excused without it counting as a failure),
|
|
101
|
+
* every unused waiver as a warning, and a final one-line summary — `CLEAN: N
|
|
102
|
+
* pages` or `DIFF: B vs C pages, N diff(s) (W waived), tolerance Tpt`.
|
|
103
|
+
*/
|
|
104
|
+
export declare function formatDiffs(result: CompareResult, info: {
|
|
105
|
+
basePageCount: number;
|
|
106
|
+
candPageCount: number;
|
|
107
|
+
tolerance: number;
|
|
108
|
+
}): string[];
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MANIFEST_FILENAMES,
|
|
3
3
|
runLint
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-nde3d8wk.js";
|
|
5
5
|
import {
|
|
6
6
|
log
|
|
7
|
-
} from "./cli-
|
|
7
|
+
} from "./cli-vntks53w.js";
|
|
8
8
|
import {
|
|
9
9
|
UsageError,
|
|
10
10
|
rejectExtraPositionals,
|
|
11
11
|
rejectUnknownFlags
|
|
12
|
-
} from "./cli-
|
|
13
|
-
import"./cli-
|
|
12
|
+
} from "./cli-980t4qp9.js";
|
|
13
|
+
import"./cli-bwbs3qm4.js";
|
|
14
14
|
import"./cli-c41yr7he.js";
|
|
15
15
|
import {
|
|
16
16
|
EXIT_CODES
|
|
@@ -5,16 +5,16 @@ import {
|
|
|
5
5
|
TARGETS,
|
|
6
6
|
TARGET_IDS,
|
|
7
7
|
scaffoldProject
|
|
8
|
-
} from "./cli-
|
|
8
|
+
} from "./cli-nde3d8wk.js";
|
|
9
9
|
import {
|
|
10
10
|
resolveGhostscript
|
|
11
|
-
} from "./cli-
|
|
11
|
+
} from "./cli-vntks53w.js";
|
|
12
12
|
import {
|
|
13
13
|
UsageError,
|
|
14
14
|
rejectExtraPositionals,
|
|
15
15
|
rejectUnknownFlags
|
|
16
|
-
} from "./cli-
|
|
17
|
-
import"./cli-
|
|
16
|
+
} from "./cli-980t4qp9.js";
|
|
17
|
+
import"./cli-bwbs3qm4.js";
|
|
18
18
|
import {
|
|
19
19
|
isToolAvailable
|
|
20
20
|
} from "./cli-c41yr7he.js";
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
addNpmPlugin,
|
|
3
3
|
parseNpmPluginSpec
|
|
4
|
-
} from "./cli-
|
|
5
|
-
import"./cli-
|
|
4
|
+
} from "./cli-nde3d8wk.js";
|
|
5
|
+
import"./cli-vntks53w.js";
|
|
6
6
|
import {
|
|
7
7
|
UsageError,
|
|
8
8
|
rejectExtraPositionals,
|
|
9
9
|
rejectUnknownFlags
|
|
10
|
-
} from "./cli-
|
|
11
|
-
import"./cli-
|
|
10
|
+
} from "./cli-980t4qp9.js";
|
|
11
|
+
import"./cli-bwbs3qm4.js";
|
|
12
12
|
import"./cli-c41yr7he.js";
|
|
13
13
|
import {
|
|
14
14
|
EXIT_CODES
|
|
@@ -2,16 +2,16 @@ import {
|
|
|
2
2
|
executeValidation,
|
|
3
3
|
publishTargetFor,
|
|
4
4
|
reportMissingTools
|
|
5
|
-
} from "./cli-
|
|
5
|
+
} from "./cli-nde3d8wk.js";
|
|
6
6
|
import {
|
|
7
7
|
log
|
|
8
|
-
} from "./cli-
|
|
8
|
+
} from "./cli-vntks53w.js";
|
|
9
9
|
import {
|
|
10
10
|
UsageError,
|
|
11
11
|
rejectExtraPositionals,
|
|
12
12
|
rejectUnknownFlags
|
|
13
|
-
} from "./cli-
|
|
14
|
-
import"./cli-
|
|
13
|
+
} from "./cli-980t4qp9.js";
|
|
14
|
+
import"./cli-bwbs3qm4.js";
|
|
15
15
|
import"./cli-c41yr7he.js";
|
|
16
16
|
import {
|
|
17
17
|
EXIT_CODES
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
runBuild,
|
|
7
7
|
splitOutPath,
|
|
8
8
|
startPreviewServer
|
|
9
|
-
} from "./cli-
|
|
9
|
+
} from "./cli-nde3d8wk.js";
|
|
10
10
|
import {
|
|
11
11
|
log
|
|
12
|
-
} from "./cli-
|
|
12
|
+
} from "./cli-vntks53w.js";
|
|
13
13
|
import {
|
|
14
14
|
UsageError,
|
|
15
15
|
parseEngine,
|
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
rejectExtraPositionals,
|
|
19
19
|
rejectUnknownFlags,
|
|
20
20
|
resolvePort
|
|
21
|
-
} from "./cli-
|
|
22
|
-
import"./cli-
|
|
21
|
+
} from "./cli-980t4qp9.js";
|
|
22
|
+
import"./cli-bwbs3qm4.js";
|
|
23
23
|
import"./cli-c41yr7he.js";
|
|
24
24
|
import {
|
|
25
25
|
BuildError
|
|
@@ -11,17 +11,17 @@ import {
|
|
|
11
11
|
publishProviderFor,
|
|
12
12
|
resolvePublishFormat,
|
|
13
13
|
runPublish
|
|
14
|
-
} from "./cli-
|
|
14
|
+
} from "./cli-nde3d8wk.js";
|
|
15
15
|
import {
|
|
16
16
|
FileTokenStore,
|
|
17
17
|
log
|
|
18
|
-
} from "./cli-
|
|
18
|
+
} from "./cli-vntks53w.js";
|
|
19
19
|
import {
|
|
20
20
|
UsageError,
|
|
21
21
|
rejectExtraPositionals,
|
|
22
22
|
rejectUnknownFlags
|
|
23
|
-
} from "./cli-
|
|
24
|
-
import"./cli-
|
|
23
|
+
} from "./cli-980t4qp9.js";
|
|
24
|
+
import"./cli-bwbs3qm4.js";
|
|
25
25
|
import"./cli-c41yr7he.js";
|
|
26
26
|
import {
|
|
27
27
|
EXIT_CODES
|
package/dist/render.js
CHANGED
|
@@ -596,6 +596,8 @@ body { margin: 0; }
|
|
|
596
596
|
:where(h1,h2,h3,h4,h5,h6) { break-after: avoid; }
|
|
597
597
|
:where(img, svg, video) { max-width: 100%; }
|
|
598
598
|
:where(p > img:only-child, figure > img) { width: fit-content; max-width: 100%; height: auto; vertical-align: bottom; }
|
|
599
|
+
:where(p) > :where(img:not([class])) { max-height: calc(var(--gp-content-h) - 4px); object-fit: contain; }
|
|
600
|
+
:where(figure) { break-inside: avoid; }
|
|
599
601
|
:where(.section, figure) > :where(:first-child) { break-before: avoid; }
|
|
600
602
|
|
|
601
603
|
`;
|
|
@@ -674,6 +676,32 @@ var GUTTERPRESS_CSS = `
|
|
|
674
676
|
.gp-columns-2 { columns: 2; column-gap: var(--gp-column-gap, 1.5em); }
|
|
675
677
|
.gp-columns-3 { columns: 3; column-gap: var(--gp-column-gap, 1.5em); }
|
|
676
678
|
|
|
679
|
+
/* the per-shape decisions the paragraph above deliberately leaves to the
|
|
680
|
+
author, named instead of left as raw CSS every book was reinventing
|
|
681
|
+
(2026-09-01 CSS architecture review, findings C1/C7 — CLAUDE.md §0:
|
|
682
|
+
"behavior broadly useful to non-technical authors belongs in core").
|
|
683
|
+
Permanent vocabulary, standard properties verbatim — same rationale as
|
|
684
|
+
the column/grid runs above. One name each, no aliases:
|
|
685
|
+
.gp-columns-all column-span: all a heading or block that
|
|
686
|
+
spans every column in the
|
|
687
|
+
run it sits inside.
|
|
688
|
+
.gp-columns-flow column-fill: auto a run that FRAGMENTS across
|
|
689
|
+
pages — every page's
|
|
690
|
+
columns fill instead of
|
|
691
|
+
only the last one
|
|
692
|
+
balancing (the dead-column
|
|
693
|
+
collapse the build's
|
|
694
|
+
engine.multicol.dead-column
|
|
695
|
+
warning names this fix
|
|
696
|
+
for).
|
|
697
|
+
.gp-columns-balanced column-fill: balance a run that fits on ONE
|
|
698
|
+
page (the CSS initial
|
|
699
|
+
value — ragged columns
|
|
700
|
+
would be wrong here). */
|
|
701
|
+
.gp-columns-all { column-span: all; }
|
|
702
|
+
.gp-columns-flow { column-fill: auto; }
|
|
703
|
+
.gp-columns-balanced { column-fill: balance; }
|
|
704
|
+
|
|
677
705
|
/* grid runs — the SLOTTED counterpart to the column runs above. Grid places
|
|
678
706
|
each child into the next cell, across then down (deterministic slots: card
|
|
679
707
|
layouts, stat blocks, image-plus-caption pairs); columns FLOW one text run
|
|
@@ -694,6 +722,15 @@ var GUTTERPRESS_CSS = `
|
|
|
694
722
|
.gp-grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--gp-grid-gap, 1.5em); }
|
|
695
723
|
.gp-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gp-grid-gap, 1.5em); }
|
|
696
724
|
|
|
725
|
+
/* fragmentation controls — generic pagination utilities for any element,
|
|
726
|
+
independent of the column/grid vocabulary above (a book reached for
|
|
727
|
+
these just as often outside a multicol run: keeping a card whole,
|
|
728
|
+
forcing a section to start a fresh page). Standard properties only —
|
|
729
|
+
this project is Chromium-only (CLAUDE.md), so there are no legacy
|
|
730
|
+
page-break-* twins to also emit. One name each. */
|
|
731
|
+
.gp-no-break { break-inside: avoid; }
|
|
732
|
+
.gp-break-before { break-before: page; }
|
|
733
|
+
|
|
697
734
|
/* shape wrap — text follows the image's alpha silhouette instead of its
|
|
698
735
|
rectangular box. shape-outside only applies to floats, so this is inert
|
|
699
736
|
without .gp-left/.gp-right (and under .gp-pin, which un-floats). The
|
|
@@ -778,6 +815,39 @@ img.gp-shape {
|
|
|
778
815
|
.gp-raised { z-index: var(--gp-z-raised); }
|
|
779
816
|
.gp-front { z-index: var(--gp-z-front); }
|
|
780
817
|
`;
|
|
818
|
+
var GP_CLASSES = new Set([
|
|
819
|
+
"gp-left",
|
|
820
|
+
"gp-right",
|
|
821
|
+
"gp-center",
|
|
822
|
+
"gp-full",
|
|
823
|
+
"gp-bleed",
|
|
824
|
+
"gp-small",
|
|
825
|
+
"gp-medium",
|
|
826
|
+
"gp-large",
|
|
827
|
+
"gp-tight",
|
|
828
|
+
"gp-loose",
|
|
829
|
+
"gp-columns-2",
|
|
830
|
+
"gp-columns-3",
|
|
831
|
+
"gp-columns-all",
|
|
832
|
+
"gp-columns-flow",
|
|
833
|
+
"gp-columns-balanced",
|
|
834
|
+
"gp-no-break",
|
|
835
|
+
"gp-break-before",
|
|
836
|
+
"gp-grid-2",
|
|
837
|
+
"gp-grid-3",
|
|
838
|
+
"gp-shape",
|
|
839
|
+
"gp-pin",
|
|
840
|
+
"gp-top",
|
|
841
|
+
"gp-bottom",
|
|
842
|
+
"gp-behind",
|
|
843
|
+
"gp-base",
|
|
844
|
+
"gp-raised",
|
|
845
|
+
"gp-front",
|
|
846
|
+
"gp-page-break",
|
|
847
|
+
"gp-column-break",
|
|
848
|
+
"gp-continued",
|
|
849
|
+
"gp-flush"
|
|
850
|
+
]);
|
|
781
851
|
|
|
782
852
|
// src/lib/markdown/chapter-id.ts
|
|
783
853
|
function canonicalChapterId(p) {
|
|
@@ -831,12 +901,70 @@ function warn2(env, line, type, message, marker) {
|
|
|
831
901
|
env.layoutWarnings = [];
|
|
832
902
|
env.layoutWarnings.push({ line, type, message, marker });
|
|
833
903
|
}
|
|
904
|
+
function editDistance2(a, b) {
|
|
905
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
906
|
+
for (let i = 1;i <= a.length; i++) {
|
|
907
|
+
const cur = [i];
|
|
908
|
+
for (let j = 1;j <= b.length; j++) {
|
|
909
|
+
cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
910
|
+
}
|
|
911
|
+
prev = cur;
|
|
912
|
+
}
|
|
913
|
+
return prev[b.length];
|
|
914
|
+
}
|
|
915
|
+
function nearestGpClass(cls) {
|
|
916
|
+
const threshold = Math.max(2, Math.floor(cls.length / 3));
|
|
917
|
+
let best = null;
|
|
918
|
+
let bestDistance = Infinity;
|
|
919
|
+
for (const known of GP_CLASSES) {
|
|
920
|
+
const d = editDistance2(cls, known);
|
|
921
|
+
if (d < bestDistance) {
|
|
922
|
+
bestDistance = d;
|
|
923
|
+
best = known;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return best !== null && bestDistance > 0 && bestDistance <= threshold ? best : null;
|
|
927
|
+
}
|
|
928
|
+
function hasPinClass(token) {
|
|
929
|
+
const cls = token.attrGet && token.attrGet("class");
|
|
930
|
+
return typeof cls === "string" && cls.split(/\s+/).includes("gp-pin");
|
|
931
|
+
}
|
|
932
|
+
function elementNameFor(token) {
|
|
933
|
+
switch (token.type) {
|
|
934
|
+
case "layout_chapter_open":
|
|
935
|
+
return "@chapter";
|
|
936
|
+
case "layout_spread_open":
|
|
937
|
+
return "@spread";
|
|
938
|
+
case "layout_page_open":
|
|
939
|
+
return "@page";
|
|
940
|
+
case "layout_section_open":
|
|
941
|
+
return "@section";
|
|
942
|
+
case "image":
|
|
943
|
+
return "an image";
|
|
944
|
+
case "heading_open":
|
|
945
|
+
return "a heading";
|
|
946
|
+
case "paragraph_open":
|
|
947
|
+
return "a paragraph";
|
|
948
|
+
case "link_open":
|
|
949
|
+
return "a link";
|
|
950
|
+
default:
|
|
951
|
+
return token.tag ? `a <${token.tag}>` : "an element";
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
function checkUnknownGpClasses(token, env, line) {
|
|
955
|
+
const cls = token.attrGet && token.attrGet("class");
|
|
956
|
+
if (typeof cls !== "string" || !cls)
|
|
957
|
+
return;
|
|
958
|
+
for (const c of cls.split(/\s+/)) {
|
|
959
|
+
if (!c.startsWith("gp-") || GP_CLASSES.has(c))
|
|
960
|
+
continue;
|
|
961
|
+
const suggestion = nearestGpClass(c);
|
|
962
|
+
const base = `Unknown class "${c}" on ${elementNameFor(token)}.`;
|
|
963
|
+
warn2(env, line, "unknown_gp_class", suggestion ? `${base} Did you mean "${suggestion}"?` : base);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
834
966
|
function gpPinScope(md) {
|
|
835
967
|
const PIN_OUTSIDE_PAGE_MSG = "A .gp-pin element is not inside any @page or @spread, so it is pinned to the whole document instead of the page it sits on — it can print on a completely different sheet. Move it inside an @page or @spread block, or remove .gp-pin.";
|
|
836
|
-
function hasPinClass(token) {
|
|
837
|
-
const cls = token.attrGet && token.attrGet("class");
|
|
838
|
-
return typeof cls === "string" && cls.split(/\s+/).includes("gp-pin");
|
|
839
|
-
}
|
|
840
968
|
md.core.ruler.push("gp_pin_scope_check", function(state) {
|
|
841
969
|
let depth = 0;
|
|
842
970
|
for (const token of state.tokens) {
|
|
@@ -855,6 +983,13 @@ function gpPinScope(md) {
|
|
|
855
983
|
warn2(state.env, (token.map?.[0] ?? 0) + 1, "pin_outside_page", PIN_OUTSIDE_PAGE_MSG);
|
|
856
984
|
}
|
|
857
985
|
}
|
|
986
|
+
const line = (token.map?.[0] ?? 0) + 1;
|
|
987
|
+
checkUnknownGpClasses(token, state.env, line);
|
|
988
|
+
if (token.type === "inline" && token.children) {
|
|
989
|
+
for (const child of token.children) {
|
|
990
|
+
checkUnknownGpClasses(child, state.env, line);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
858
993
|
}
|
|
859
994
|
});
|
|
860
995
|
}
|
|
@@ -1378,10 +1513,15 @@ ${rendered}
|
|
|
1378
1513
|
opts.onImageRefs([...imageRefs]);
|
|
1379
1514
|
}
|
|
1380
1515
|
const inlineCss = [
|
|
1516
|
+
"@layer gp.marker, gp.vocab;",
|
|
1381
1517
|
`/* gutterpress markers */
|
|
1382
|
-
|
|
1518
|
+
@layer gp.marker {
|
|
1519
|
+
${MARKER_CSS.trim()}
|
|
1520
|
+
}`,
|
|
1383
1521
|
`/* gutterpress */
|
|
1384
|
-
|
|
1522
|
+
@layer gp.vocab {
|
|
1523
|
+
${GUTTERPRESS_CSS.trim()}
|
|
1524
|
+
}`,
|
|
1385
1525
|
pluginCss ? `/* user plugin css */
|
|
1386
1526
|
${pluginCss.trim()}` : null,
|
|
1387
1527
|
projectCss ? `/* project css */
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
executeAndReport
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-nde3d8wk.js";
|
|
4
4
|
import {
|
|
5
5
|
log
|
|
6
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-vntks53w.js";
|
|
7
7
|
import {
|
|
8
8
|
UsageError,
|
|
9
9
|
rejectExtraPositionals,
|
|
10
10
|
rejectUnknownFlags
|
|
11
|
-
} from "./cli-
|
|
12
|
-
import"./cli-
|
|
11
|
+
} from "./cli-980t4qp9.js";
|
|
12
|
+
import"./cli-bwbs3qm4.js";
|
|
13
13
|
import"./cli-c41yr7he.js";
|
|
14
14
|
import {
|
|
15
15
|
EXIT_CODES
|
package/package.json
CHANGED