@ai-matrx/print 0.5.10 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0
4
+
5
+ - **Added: page breaks.** A line that is exactly `<!-- pagebreak -->`
6
+ (canonical), `<!-- page-break -->`, `<!-- newpage -->`, `\pagebreak`,
7
+ `\newpage`, or a Typora-style `<div style="page-break-after: always"></div>`
8
+ starts a new printed page. On screen it draws a dashed rule labelled
9
+ "Page break"; in print it is a real `break-after: page`. Both skins emit the
10
+ same `.matrx-page-break` element.
11
+ - **Added: `@ai-matrx/print/directives`** — the zero-dependency page-break
12
+ grammar (`isPageBreakLine`, `splitAtPageBreaks`, `PAGE_BREAK_MARKDOWN`,
13
+ `PAGE_BREAK_CLASS`, `PAGE_BREAK_LABEL`) for any surface that renders
14
+ markdown, so the chat preview and the printers share one definition.
15
+ - **Added:** `markdownToPdfBlob` honours page breaks — every section after a
16
+ break starts on a new PDF page (the canvas lane ignores CSS breaks, so the
17
+ content is captured per section).
18
+ - **Fixed:** `<artifact type id version title>…</artifact>` blocks pasted their
19
+ raw body into the output — a full HTML artifact injected its own `<html>`,
20
+ `<head>`, `<style>` and `<script>` into the host page, and the emphasis pass
21
+ rewrote `*` inside the script, so the script threw. Artifacts now render as a
22
+ titled panel: source-code types (html, react, svg, mermaid, diff, code) and
23
+ JSON bodies print as escaped source; markdown bodies convert in the same skin.
24
+ - **Fixed:** the document skin ran emphasis, lists and paragraphs inside fenced
25
+ and inline code (`1*2*3` became `1<em>2</em>3`). Code is now protected and
26
+ escaped in both skins; `~~~` fences and indented fences are recognised.
27
+ - **Fixed:** page-level markup outside a fence (`<script>`, `<style>`, `<head>`,
28
+ the `<html>`/`<body>` shell) is removed — paired regions whole, stray tags
29
+ alone, repeated until stable so a split tag cannot reassemble — and raw HTML
30
+ loses `on…` handlers, `srcdoc`, and script-scheme URLs (entity-obfuscated
31
+ forms included). Announced once via the new `html-stripped` notice with the
32
+ remedy. Inline code is protected first, so prose about tags survives.
33
+ - **Fixed:** emphasis follows the CommonMark intraword rule (`snake_case_name`
34
+ stays literal); a bullet that opens with bold stays a bullet; bold/code
35
+ inside link text renders; URLs with one level of parentheses parse.
36
+ - **Parity (both skins now handle the same markdown):** GFM task lists print as
37
+ real boxes, `~~strikethrough~~`, table column alignment (`:-:`, `--:`) and
38
+ empty cells (they used to shift columns left), the chat's `***` accent and
39
+ `#===` heavy dividers, nested lists in the article skin, and `<flashcards>`
40
+ in the article skin (printed as question/answer pairs).
41
+ - **Print polish:** table header rows repeat on every page and rows never
42
+ split; long code lines wrap instead of being cut off; headings stay with the
43
+ content that follows; page margins are set; pages are numbered "3 / 12"
44
+ (bottom centre) — `getMarkdownStylesheet({ pageNumbers: false })` turns the
45
+ numbering off. New `MARKDOWN_SHARED_CSS` / `MARKDOWN_PAGE_CSS` exports.
46
+ - Consumer action: none — refresh to latest. `stripDecorativeLineBreaks`-style
47
+ host regexes should match `class="matrx-hr…"` (accent/heavy rules add a
48
+ second class).
49
+
3
50
  ## 0.5.10
4
51
 
5
52
  Automatic changed-only republish (docs/metadata drift since the last tag — see
package/README.md CHANGED
@@ -137,6 +137,20 @@ it by passing `tokens: ":root{--matrx-print-accent:#0f766e}"` — appended last,
137
137
  so the host wins. Both sheets carry the print hard parts (`@media print`,
138
138
  page-break avoidance, orphans/widows, `print-color-adjust`).
139
139
 
140
+ ### `@ai-matrx/print/directives` — page breaks, one grammar everywhere
141
+
142
+ ```ts
143
+ import { isPageBreakLine, splitAtPageBreaks, PAGE_BREAK_MARKDOWN } from "@ai-matrx/print/directives";
144
+ ```
145
+
146
+ Put `<!-- pagebreak -->` on its own line to start a new printed page
147
+ (`\pagebreak`, `\newpage`, `<!-- newpage -->` and
148
+ `<div style="page-break-after: always"></div>` are accepted too). Both
149
+ converters render it as `<div class="matrx-page-break">` — a dashed "Page
150
+ break" rule on screen, a real page break on paper — and `markdownToPdfBlob`
151
+ starts a new PDF page there. The entry has no dependencies, so a chat or editor
152
+ renderer can recognise the same syntax without loading the stylesheets.
153
+
140
154
  ### `@ai-matrx/print/pdf` — DOM capture & markdown → PDF
141
155
 
142
156
  ```ts
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/directives.ts
21
+ var directives_exports = {};
22
+ __export(directives_exports, {
23
+ PAGE_BREAK_CLASS: () => PAGE_BREAK_CLASS,
24
+ PAGE_BREAK_LABEL: () => PAGE_BREAK_LABEL,
25
+ PAGE_BREAK_MARKDOWN: () => PAGE_BREAK_MARKDOWN,
26
+ isPageBreakLine: () => isPageBreakLine,
27
+ splitAtPageBreaks: () => splitAtPageBreaks
28
+ });
29
+ module.exports = __toCommonJS(directives_exports);
30
+ var PAGE_BREAK_MARKDOWN = "<!-- pagebreak -->";
31
+ var PAGE_BREAK_CLASS = "matrx-page-break";
32
+ var PAGE_BREAK_LABEL = "Page break";
33
+ var COMMENT_FORM = /^<!--\s*(?:page[\s_-]?break|new[\s_-]?page)\s*-->$/i;
34
+ var LATEX_FORM = /^\\(?:pagebreak|newpage|clearpage)\s*(?:\{\s*\})?$/i;
35
+ var DIV_FORM = /^<div\b([^>]*)>\s*<\/div>$|^<div\b([^>]*)\/>$/i;
36
+ var DIV_BREAK_ATTR = /(?:page-break-(?:after|before)\s*:\s*always|break-(?:after|before)\s*:\s*page|class\s*=\s*["'](?:[^"']*\s)?page-?break(?:\s[^"']*)?["'])/i;
37
+ function isPageBreakLine(line) {
38
+ const t = line.trim();
39
+ if (t.length < 8 || t.length > 200) return false;
40
+ if (COMMENT_FORM.test(t) || LATEX_FORM.test(t)) return true;
41
+ const div = DIV_FORM.exec(t);
42
+ if (!div) return false;
43
+ return DIV_BREAK_ATTR.test(div[1] ?? div[2] ?? "");
44
+ }
45
+ function splitAtPageBreaks(markdown) {
46
+ const lines = markdown.split(/\r?\n/);
47
+ const pieces = [];
48
+ let current = [];
49
+ let fence = null;
50
+ for (const line of lines) {
51
+ const fenceMatch = /^\s*(`{3,}|~{3,})/.exec(line);
52
+ if (fenceMatch) {
53
+ const marker = fenceMatch[1];
54
+ if (fence === null) fence = marker;
55
+ else if (marker[0] === fence[0] && marker.length >= fence.length) fence = null;
56
+ } else if (fence === null && isPageBreakLine(line)) {
57
+ if (current.join("\n").trim()) pieces.push(current.join("\n"));
58
+ current = [];
59
+ continue;
60
+ }
61
+ current.push(line);
62
+ }
63
+ if (current.join("\n").trim() || pieces.length === 0) pieces.push(current.join("\n"));
64
+ return pieces;
65
+ }
66
+ //# sourceMappingURL=directives.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/directives.ts"],"sourcesContent":["/**\n * @ai-matrx/print/directives — the ONE definition of the print directives\n * markdown can carry, shared by every surface that reads markdown.\n *\n * Why a separate, zero-dependency entry: the directive syntax has three\n * readers — this package's two converters (print window / PDF / HTML page),\n * the chat renderer's remark plugin (the on-screen preview), and any editor\n * that inserts one. If each re-derived the regex they would drift the first\n * time someone added an alias. `/markdown` carries ~20KB of stylesheet, so the\n * chat bundle imports the syntax from here instead and pays nothing.\n *\n * ── Page break ────────────────────────────────────────────────────────────\n * A line that is exactly one of these (case-insensitive, surrounding\n * whitespace ignored) forces the next content onto a new printed page:\n *\n * <!-- pagebreak --> ← canonical; invisible in any other renderer\n * <!-- page-break --> <!-- page break --> <!-- newpage -->\n * \\pagebreak \\newpage ← LaTeX / pandoc habit\n * <div style=\"page-break-after: always\"></div> ← Typora / VS Code habit\n * (also `page-break-before`, `break-after: page`, `break-before: page`,\n * and `<div class=\"page-break\"></div>` / `class=\"pagebreak\"`)\n *\n * On screen every surface draws the same idea: a dashed rule labelled\n * \"Page break\" with generous padding. In print it is a zero-height element\n * with `break-after: page`.\n */\n\n/** The form editors insert and docs recommend. */\nexport const PAGE_BREAK_MARKDOWN = \"<!-- pagebreak -->\";\n\n/** The class every surface puts on a rendered page break. */\nexport const PAGE_BREAK_CLASS = \"matrx-page-break\";\n\n/** Screen label for a rendered page break. */\nexport const PAGE_BREAK_LABEL = \"Page break\";\n\nconst COMMENT_FORM = /^<!--\\s*(?:page[\\s_-]?break|new[\\s_-]?page)\\s*-->$/i;\nconst LATEX_FORM = /^\\\\(?:pagebreak|newpage|clearpage)\\s*(?:\\{\\s*\\})?$/i;\nconst DIV_FORM = /^<div\\b([^>]*)>\\s*<\\/div>$|^<div\\b([^>]*)\\/>$/i;\nconst DIV_BREAK_ATTR =\n /(?:page-break-(?:after|before)\\s*:\\s*always|break-(?:after|before)\\s*:\\s*page|class\\s*=\\s*[\"'](?:[^\"']*\\s)?page-?break(?:\\s[^\"']*)?[\"'])/i;\n\n/** Is this single line a page-break directive? */\nexport function isPageBreakLine(line: string): boolean {\n const t = line.trim();\n if (t.length < 8 || t.length > 200) return false;\n if (COMMENT_FORM.test(t) || LATEX_FORM.test(t)) return true;\n const div = DIV_FORM.exec(t);\n if (!div) return false;\n return DIV_BREAK_ATTR.test(div[1] ?? div[2] ?? \"\");\n}\n\n/**\n * Split markdown into the pieces that belong on separate pages. Fence-aware:\n * a directive inside a ``` / ~~~ code block is code, not a break. Always\n * returns at least one piece; empty pieces (two breaks in a row, a break at\n * the very start or end) are kept out so no blank page is produced.\n */\nexport function splitAtPageBreaks(markdown: string): string[] {\n const lines = markdown.split(/\\r?\\n/);\n const pieces: string[] = [];\n let current: string[] = [];\n let fence: string | null = null;\n\n for (const line of lines) {\n // Any indent — the converters accept list-nested fences, so must this.\n const fenceMatch = /^\\s*(`{3,}|~{3,})/.exec(line);\n if (fenceMatch) {\n const marker = fenceMatch[1] as string;\n if (fence === null) fence = marker;\n else if (marker[0] === fence[0] && marker.length >= fence.length) fence = null;\n } else if (fence === null && isPageBreakLine(line)) {\n if (current.join(\"\\n\").trim()) pieces.push(current.join(\"\\n\"));\n current = [];\n continue;\n }\n current.push(line);\n }\n if (current.join(\"\\n\").trim() || pieces.length === 0) pieces.push(current.join(\"\\n\"));\n return pieces;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BO,IAAM,sBAAsB;AAG5B,IAAM,mBAAmB;AAGzB,IAAM,mBAAmB;AAEhC,IAAM,eAAe;AACrB,IAAM,aAAa;AACnB,IAAM,WAAW;AACjB,IAAM,iBACJ;AAGK,SAAS,gBAAgB,MAAuB;AACrD,QAAM,IAAI,KAAK,KAAK;AACpB,MAAI,EAAE,SAAS,KAAK,EAAE,SAAS,IAAK,QAAO;AAC3C,MAAI,aAAa,KAAK,CAAC,KAAK,WAAW,KAAK,CAAC,EAAG,QAAO;AACvD,QAAM,MAAM,SAAS,KAAK,CAAC;AAC3B,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,eAAe,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE;AACnD;AAQO,SAAS,kBAAkB,UAA4B;AAC5D,QAAM,QAAQ,SAAS,MAAM,OAAO;AACpC,QAAM,SAAmB,CAAC;AAC1B,MAAI,UAAoB,CAAC;AACzB,MAAI,QAAuB;AAE3B,aAAW,QAAQ,OAAO;AAExB,UAAM,aAAa,oBAAoB,KAAK,IAAI;AAChD,QAAI,YAAY;AACd,YAAM,SAAS,WAAW,CAAC;AAC3B,UAAI,UAAU,KAAM,SAAQ;AAAA,eACnB,OAAO,CAAC,MAAM,MAAM,CAAC,KAAK,OAAO,UAAU,MAAM,OAAQ,SAAQ;AAAA,IAC5E,WAAW,UAAU,QAAQ,gBAAgB,IAAI,GAAG;AAClD,UAAI,QAAQ,KAAK,IAAI,EAAE,KAAK,EAAG,QAAO,KAAK,QAAQ,KAAK,IAAI,CAAC;AAC7D,gBAAU,CAAC;AACX;AAAA,IACF;AACA,YAAQ,KAAK,IAAI;AAAA,EACnB;AACA,MAAI,QAAQ,KAAK,IAAI,EAAE,KAAK,KAAK,OAAO,WAAW,EAAG,QAAO,KAAK,QAAQ,KAAK,IAAI,CAAC;AACpF,SAAO;AACT;","names":[]}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @ai-matrx/print/directives — the ONE definition of the print directives
3
+ * markdown can carry, shared by every surface that reads markdown.
4
+ *
5
+ * Why a separate, zero-dependency entry: the directive syntax has three
6
+ * readers — this package's two converters (print window / PDF / HTML page),
7
+ * the chat renderer's remark plugin (the on-screen preview), and any editor
8
+ * that inserts one. If each re-derived the regex they would drift the first
9
+ * time someone added an alias. `/markdown` carries ~20KB of stylesheet, so the
10
+ * chat bundle imports the syntax from here instead and pays nothing.
11
+ *
12
+ * ── Page break ────────────────────────────────────────────────────────────
13
+ * A line that is exactly one of these (case-insensitive, surrounding
14
+ * whitespace ignored) forces the next content onto a new printed page:
15
+ *
16
+ * <!-- pagebreak --> ← canonical; invisible in any other renderer
17
+ * <!-- page-break --> <!-- page break --> <!-- newpage -->
18
+ * \pagebreak \newpage ← LaTeX / pandoc habit
19
+ * <div style="page-break-after: always"></div> ← Typora / VS Code habit
20
+ * (also `page-break-before`, `break-after: page`, `break-before: page`,
21
+ * and `<div class="page-break"></div>` / `class="pagebreak"`)
22
+ *
23
+ * On screen every surface draws the same idea: a dashed rule labelled
24
+ * "Page break" with generous padding. In print it is a zero-height element
25
+ * with `break-after: page`.
26
+ */
27
+ /** The form editors insert and docs recommend. */
28
+ declare const PAGE_BREAK_MARKDOWN = "<!-- pagebreak -->";
29
+ /** The class every surface puts on a rendered page break. */
30
+ declare const PAGE_BREAK_CLASS = "matrx-page-break";
31
+ /** Screen label for a rendered page break. */
32
+ declare const PAGE_BREAK_LABEL = "Page break";
33
+ /** Is this single line a page-break directive? */
34
+ declare function isPageBreakLine(line: string): boolean;
35
+ /**
36
+ * Split markdown into the pieces that belong on separate pages. Fence-aware:
37
+ * a directive inside a ``` / ~~~ code block is code, not a break. Always
38
+ * returns at least one piece; empty pieces (two breaks in a row, a break at
39
+ * the very start or end) are kept out so no blank page is produced.
40
+ */
41
+ declare function splitAtPageBreaks(markdown: string): string[];
42
+
43
+ export { PAGE_BREAK_CLASS, PAGE_BREAK_LABEL, PAGE_BREAK_MARKDOWN, isPageBreakLine, splitAtPageBreaks };
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @ai-matrx/print/directives — the ONE definition of the print directives
3
+ * markdown can carry, shared by every surface that reads markdown.
4
+ *
5
+ * Why a separate, zero-dependency entry: the directive syntax has three
6
+ * readers — this package's two converters (print window / PDF / HTML page),
7
+ * the chat renderer's remark plugin (the on-screen preview), and any editor
8
+ * that inserts one. If each re-derived the regex they would drift the first
9
+ * time someone added an alias. `/markdown` carries ~20KB of stylesheet, so the
10
+ * chat bundle imports the syntax from here instead and pays nothing.
11
+ *
12
+ * ── Page break ────────────────────────────────────────────────────────────
13
+ * A line that is exactly one of these (case-insensitive, surrounding
14
+ * whitespace ignored) forces the next content onto a new printed page:
15
+ *
16
+ * <!-- pagebreak --> ← canonical; invisible in any other renderer
17
+ * <!-- page-break --> <!-- page break --> <!-- newpage -->
18
+ * \pagebreak \newpage ← LaTeX / pandoc habit
19
+ * <div style="page-break-after: always"></div> ← Typora / VS Code habit
20
+ * (also `page-break-before`, `break-after: page`, `break-before: page`,
21
+ * and `<div class="page-break"></div>` / `class="pagebreak"`)
22
+ *
23
+ * On screen every surface draws the same idea: a dashed rule labelled
24
+ * "Page break" with generous padding. In print it is a zero-height element
25
+ * with `break-after: page`.
26
+ */
27
+ /** The form editors insert and docs recommend. */
28
+ declare const PAGE_BREAK_MARKDOWN = "<!-- pagebreak -->";
29
+ /** The class every surface puts on a rendered page break. */
30
+ declare const PAGE_BREAK_CLASS = "matrx-page-break";
31
+ /** Screen label for a rendered page break. */
32
+ declare const PAGE_BREAK_LABEL = "Page break";
33
+ /** Is this single line a page-break directive? */
34
+ declare function isPageBreakLine(line: string): boolean;
35
+ /**
36
+ * Split markdown into the pieces that belong on separate pages. Fence-aware:
37
+ * a directive inside a ``` / ~~~ code block is code, not a break. Always
38
+ * returns at least one piece; empty pieces (two breaks in a row, a break at
39
+ * the very start or end) are kept out so no blank page is produced.
40
+ */
41
+ declare function splitAtPageBreaks(markdown: string): string[];
42
+
43
+ export { PAGE_BREAK_CLASS, PAGE_BREAK_LABEL, PAGE_BREAK_MARKDOWN, isPageBreakLine, splitAtPageBreaks };
@@ -0,0 +1,45 @@
1
+ // src/directives.ts
2
+ var PAGE_BREAK_MARKDOWN = "<!-- pagebreak -->";
3
+ var PAGE_BREAK_CLASS = "matrx-page-break";
4
+ var PAGE_BREAK_LABEL = "Page break";
5
+ var COMMENT_FORM = /^<!--\s*(?:page[\s_-]?break|new[\s_-]?page)\s*-->$/i;
6
+ var LATEX_FORM = /^\\(?:pagebreak|newpage|clearpage)\s*(?:\{\s*\})?$/i;
7
+ var DIV_FORM = /^<div\b([^>]*)>\s*<\/div>$|^<div\b([^>]*)\/>$/i;
8
+ var DIV_BREAK_ATTR = /(?:page-break-(?:after|before)\s*:\s*always|break-(?:after|before)\s*:\s*page|class\s*=\s*["'](?:[^"']*\s)?page-?break(?:\s[^"']*)?["'])/i;
9
+ function isPageBreakLine(line) {
10
+ const t = line.trim();
11
+ if (t.length < 8 || t.length > 200) return false;
12
+ if (COMMENT_FORM.test(t) || LATEX_FORM.test(t)) return true;
13
+ const div = DIV_FORM.exec(t);
14
+ if (!div) return false;
15
+ return DIV_BREAK_ATTR.test(div[1] ?? div[2] ?? "");
16
+ }
17
+ function splitAtPageBreaks(markdown) {
18
+ const lines = markdown.split(/\r?\n/);
19
+ const pieces = [];
20
+ let current = [];
21
+ let fence = null;
22
+ for (const line of lines) {
23
+ const fenceMatch = /^\s*(`{3,}|~{3,})/.exec(line);
24
+ if (fenceMatch) {
25
+ const marker = fenceMatch[1];
26
+ if (fence === null) fence = marker;
27
+ else if (marker[0] === fence[0] && marker.length >= fence.length) fence = null;
28
+ } else if (fence === null && isPageBreakLine(line)) {
29
+ if (current.join("\n").trim()) pieces.push(current.join("\n"));
30
+ current = [];
31
+ continue;
32
+ }
33
+ current.push(line);
34
+ }
35
+ if (current.join("\n").trim() || pieces.length === 0) pieces.push(current.join("\n"));
36
+ return pieces;
37
+ }
38
+ export {
39
+ PAGE_BREAK_CLASS,
40
+ PAGE_BREAK_LABEL,
41
+ PAGE_BREAK_MARKDOWN,
42
+ isPageBreakLine,
43
+ splitAtPageBreaks
44
+ };
45
+ //# sourceMappingURL=directives.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/directives.ts"],"sourcesContent":["/**\n * @ai-matrx/print/directives — the ONE definition of the print directives\n * markdown can carry, shared by every surface that reads markdown.\n *\n * Why a separate, zero-dependency entry: the directive syntax has three\n * readers — this package's two converters (print window / PDF / HTML page),\n * the chat renderer's remark plugin (the on-screen preview), and any editor\n * that inserts one. If each re-derived the regex they would drift the first\n * time someone added an alias. `/markdown` carries ~20KB of stylesheet, so the\n * chat bundle imports the syntax from here instead and pays nothing.\n *\n * ── Page break ────────────────────────────────────────────────────────────\n * A line that is exactly one of these (case-insensitive, surrounding\n * whitespace ignored) forces the next content onto a new printed page:\n *\n * <!-- pagebreak --> ← canonical; invisible in any other renderer\n * <!-- page-break --> <!-- page break --> <!-- newpage -->\n * \\pagebreak \\newpage ← LaTeX / pandoc habit\n * <div style=\"page-break-after: always\"></div> ← Typora / VS Code habit\n * (also `page-break-before`, `break-after: page`, `break-before: page`,\n * and `<div class=\"page-break\"></div>` / `class=\"pagebreak\"`)\n *\n * On screen every surface draws the same idea: a dashed rule labelled\n * \"Page break\" with generous padding. In print it is a zero-height element\n * with `break-after: page`.\n */\n\n/** The form editors insert and docs recommend. */\nexport const PAGE_BREAK_MARKDOWN = \"<!-- pagebreak -->\";\n\n/** The class every surface puts on a rendered page break. */\nexport const PAGE_BREAK_CLASS = \"matrx-page-break\";\n\n/** Screen label for a rendered page break. */\nexport const PAGE_BREAK_LABEL = \"Page break\";\n\nconst COMMENT_FORM = /^<!--\\s*(?:page[\\s_-]?break|new[\\s_-]?page)\\s*-->$/i;\nconst LATEX_FORM = /^\\\\(?:pagebreak|newpage|clearpage)\\s*(?:\\{\\s*\\})?$/i;\nconst DIV_FORM = /^<div\\b([^>]*)>\\s*<\\/div>$|^<div\\b([^>]*)\\/>$/i;\nconst DIV_BREAK_ATTR =\n /(?:page-break-(?:after|before)\\s*:\\s*always|break-(?:after|before)\\s*:\\s*page|class\\s*=\\s*[\"'](?:[^\"']*\\s)?page-?break(?:\\s[^\"']*)?[\"'])/i;\n\n/** Is this single line a page-break directive? */\nexport function isPageBreakLine(line: string): boolean {\n const t = line.trim();\n if (t.length < 8 || t.length > 200) return false;\n if (COMMENT_FORM.test(t) || LATEX_FORM.test(t)) return true;\n const div = DIV_FORM.exec(t);\n if (!div) return false;\n return DIV_BREAK_ATTR.test(div[1] ?? div[2] ?? \"\");\n}\n\n/**\n * Split markdown into the pieces that belong on separate pages. Fence-aware:\n * a directive inside a ``` / ~~~ code block is code, not a break. Always\n * returns at least one piece; empty pieces (two breaks in a row, a break at\n * the very start or end) are kept out so no blank page is produced.\n */\nexport function splitAtPageBreaks(markdown: string): string[] {\n const lines = markdown.split(/\\r?\\n/);\n const pieces: string[] = [];\n let current: string[] = [];\n let fence: string | null = null;\n\n for (const line of lines) {\n // Any indent — the converters accept list-nested fences, so must this.\n const fenceMatch = /^\\s*(`{3,}|~{3,})/.exec(line);\n if (fenceMatch) {\n const marker = fenceMatch[1] as string;\n if (fence === null) fence = marker;\n else if (marker[0] === fence[0] && marker.length >= fence.length) fence = null;\n } else if (fence === null && isPageBreakLine(line)) {\n if (current.join(\"\\n\").trim()) pieces.push(current.join(\"\\n\"));\n current = [];\n continue;\n }\n current.push(line);\n }\n if (current.join(\"\\n\").trim() || pieces.length === 0) pieces.push(current.join(\"\\n\"));\n return pieces;\n}\n"],"mappings":";AA4BO,IAAM,sBAAsB;AAG5B,IAAM,mBAAmB;AAGzB,IAAM,mBAAmB;AAEhC,IAAM,eAAe;AACrB,IAAM,aAAa;AACnB,IAAM,WAAW;AACjB,IAAM,iBACJ;AAGK,SAAS,gBAAgB,MAAuB;AACrD,QAAM,IAAI,KAAK,KAAK;AACpB,MAAI,EAAE,SAAS,KAAK,EAAE,SAAS,IAAK,QAAO;AAC3C,MAAI,aAAa,KAAK,CAAC,KAAK,WAAW,KAAK,CAAC,EAAG,QAAO;AACvD,QAAM,MAAM,SAAS,KAAK,CAAC;AAC3B,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,eAAe,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE;AACnD;AAQO,SAAS,kBAAkB,UAA4B;AAC5D,QAAM,QAAQ,SAAS,MAAM,OAAO;AACpC,QAAM,SAAmB,CAAC;AAC1B,MAAI,UAAoB,CAAC;AACzB,MAAI,QAAuB;AAE3B,aAAW,QAAQ,OAAO;AAExB,UAAM,aAAa,oBAAoB,KAAK,IAAI;AAChD,QAAI,YAAY;AACd,YAAM,SAAS,WAAW,CAAC;AAC3B,UAAI,UAAU,KAAM,SAAQ;AAAA,eACnB,OAAO,CAAC,MAAM,MAAM,CAAC,KAAK,OAAO,UAAU,MAAM,OAAQ,SAAQ;AAAA,IAC5E,WAAW,UAAU,QAAQ,gBAAgB,IAAI,GAAG;AAClD,UAAI,QAAQ,KAAK,IAAI,EAAE,KAAK,EAAG,QAAO,KAAK,QAAQ,KAAK,IAAI,CAAC;AAC7D,gBAAU,CAAC;AACX;AAAA,IACF;AACA,YAAQ,KAAK,IAAI;AAAA,EACnB;AACA,MAAI,QAAQ,KAAK,IAAI,EAAE,KAAK,KAAK,OAAO,WAAW,EAAG,QAAO,KAAK,QAAQ,KAAK,IAAI,CAAC;AACpF,SAAO;AACT;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @ai-matrx/print — root entry: TYPES ONLY.\n *\n * The runtime lives in the subpaths (each import-inert, RSC-safe except\n * /react which is a client entry):\n * @ai-matrx/print/core BlockPrinter contract + print-window composition\n * @ai-matrx/print/qr QR generation + e-commerce payload builders\n * @ai-matrx/print/labels label-sheet engine (templates, formats, print/PDF)\n * @ai-matrx/print/flashcards the deck printer (10 variants)\n * @ai-matrx/print/pdf DOM-capture / markdown → PDF\n * @ai-matrx/print/markdown the shipped markdown converter + print stylesheets\n * @ai-matrx/print/barcode 1-D barcodes (Code128 / EAN-13 / UPC-A)\n * @ai-matrx/print/booklet saddle-stitch imposition + 2-up print lane\n * @ai-matrx/print/qr-styled branded/logo QR (browser-only at call time)\n * @ai-matrx/print/react PrintOptionsDialog + LabelSheetPreview\n */\n\nexport type {\n BlockPrinter,\n PrintVariant,\n PrintSetting,\n PrintSettings,\n PrintOutcome,\n} from \"./core\";\n\nexport type { QrEcLevel, QrOptions } from \"./qr\";\n\nexport type {\n LabelTemplate,\n QrLabel,\n QrLabelPrintData,\n QrLabelSettings,\n ScannabilityReport,\n LabelField,\n LabelElement,\n LabelFormat,\n} from \"./labels\";\n\nexport type { Flashcard, FlashcardsVariant } from \"./flashcards\";\n\nexport type {\n DomCaptureOptions,\n MarkdownPdfDeps,\n MarkdownPdfNotice,\n} from \"./pdf\";\n\nexport type {\n MarkdownSkin,\n MarkdownNotice,\n MarkdownToHtmlOptions,\n MarkdownStylesheetOptions,\n RenderMarkdownDocumentOptions,\n PrintMarkdownOptions,\n} from \"./markdown\";\n\nexport type { BarcodeSymbology, BarcodeOptions } from \"./barcode\";\n\nexport type {\n BookletSheet,\n BookletImposition,\n BookletPrintOptions,\n} from \"./booklet\";\n\nexport type {\n StyledQrOptions,\n StyledQrEcLevel,\n StyledQrDotStyle,\n StyledQrCornerStyle,\n} from \"./qr-styled\";\n\nexport type {\n CheatSheetData,\n CheatSheetSection,\n CheatSheetItem,\n GlossaryData,\n GlossaryEntry,\n StudyCalendarData,\n StudyWeek,\n StudyDay,\n} from \"./education\";\n\nexport type {\n PracticeTestData,\n ExamQuestion,\n ExamChoice,\n PracticeTestVariant,\n} from \"./exam\";\n\nexport type {\n CertificateData,\n SignatureLine,\n CertificateVariant,\n WorkbookSpec,\n WorkbookSection,\n} from \"./certificate\";\n\nexport type { ZplDpi, ZplOptions } from \"./zpl\";\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @ai-matrx/print — root entry: TYPES ONLY.\n *\n * The runtime lives in the subpaths (each import-inert, RSC-safe except\n * /react which is a client entry):\n * @ai-matrx/print/core BlockPrinter contract + print-window composition\n * @ai-matrx/print/qr QR generation + e-commerce payload builders\n * @ai-matrx/print/labels label-sheet engine (templates, formats, print/PDF)\n * @ai-matrx/print/flashcards the deck printer (10 variants)\n * @ai-matrx/print/pdf DOM-capture / markdown → PDF\n * @ai-matrx/print/markdown the shipped markdown converter + print stylesheets\n * @ai-matrx/print/directives the page-break grammar every markdown surface shares\n * @ai-matrx/print/barcode 1-D barcodes (Code128 / EAN-13 / UPC-A)\n * @ai-matrx/print/booklet saddle-stitch imposition + 2-up print lane\n * @ai-matrx/print/qr-styled branded/logo QR (browser-only at call time)\n * @ai-matrx/print/react PrintOptionsDialog + LabelSheetPreview\n */\n\nexport type {\n BlockPrinter,\n PrintVariant,\n PrintSetting,\n PrintSettings,\n PrintOutcome,\n} from \"./core\";\n\nexport type { QrEcLevel, QrOptions } from \"./qr\";\n\nexport type {\n LabelTemplate,\n QrLabel,\n QrLabelPrintData,\n QrLabelSettings,\n ScannabilityReport,\n LabelField,\n LabelElement,\n LabelFormat,\n} from \"./labels\";\n\nexport type { Flashcard, FlashcardsVariant } from \"./flashcards\";\n\nexport type {\n DomCaptureOptions,\n MarkdownPdfDeps,\n MarkdownPdfNotice,\n} from \"./pdf\";\n\nexport type {\n MarkdownSkin,\n MarkdownNotice,\n MarkdownToHtmlOptions,\n MarkdownStylesheetOptions,\n RenderMarkdownDocumentOptions,\n PrintMarkdownOptions,\n} from \"./markdown\";\n\nexport type { BarcodeSymbology, BarcodeOptions } from \"./barcode\";\n\nexport type {\n BookletSheet,\n BookletImposition,\n BookletPrintOptions,\n} from \"./booklet\";\n\nexport type {\n StyledQrOptions,\n StyledQrEcLevel,\n StyledQrDotStyle,\n StyledQrCornerStyle,\n} from \"./qr-styled\";\n\nexport type {\n CheatSheetData,\n CheatSheetSection,\n CheatSheetItem,\n GlossaryData,\n GlossaryEntry,\n StudyCalendarData,\n StudyWeek,\n StudyDay,\n} from \"./education\";\n\nexport type {\n PracticeTestData,\n ExamQuestion,\n ExamChoice,\n PracticeTestVariant,\n} from \"./exam\";\n\nexport type {\n CertificateData,\n SignatureLine,\n CertificateVariant,\n WorkbookSpec,\n WorkbookSection,\n} from \"./certificate\";\n\nexport type { ZplDpi, ZplOptions } from \"./zpl\";\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
package/dist/index.d.cts CHANGED
@@ -11,3 +11,4 @@ export { CheatSheetData, CheatSheetItem, CheatSheetSection, GlossaryData, Glossa
11
11
  export { ExamChoice, ExamQuestion, PracticeTestData, PracticeTestVariant } from './exam.cjs';
12
12
  export { CertificateData, CertificateVariant, SignatureLine, WorkbookSection, WorkbookSpec } from './certificate.cjs';
13
13
  export { ZplDpi, ZplOptions } from './zpl.cjs';
14
+ import './directives.cjs';
package/dist/index.d.ts CHANGED
@@ -11,3 +11,4 @@ export { CheatSheetData, CheatSheetItem, CheatSheetSection, GlossaryData, Glossa
11
11
  export { ExamChoice, ExamQuestion, PracticeTestData, PracticeTestVariant } from './exam.js';
12
12
  export { CertificateData, CertificateVariant, SignatureLine, WorkbookSection, WorkbookSpec } from './certificate.js';
13
13
  export { ZplDpi, ZplOptions } from './zpl.js';
14
+ import './directives.js';