@ai-matrx/print 0.5.10 → 0.6.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.
- package/CHANGELOG.md +53 -0
- package/README.md +14 -0
- package/dist/directives.cjs +66 -0
- package/dist/directives.cjs.map +1 -0
- package/dist/directives.d.cts +43 -0
- package/dist/directives.d.ts +43 -0
- package/dist/directives.js +45 -0
- package/dist/directives.js.map +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/markdown.cjs +723 -321
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.d.cts +36 -10
- package/dist/markdown.d.ts +36 -10
- package/dist/markdown.js +723 -321
- package/dist/markdown.js.map +1 -1
- package/dist/pdf.cjs +86 -33
- package/dist/pdf.cjs.map +1 -1
- package/dist/pdf.d.cts +1 -0
- package/dist/pdf.d.ts +1 -0
- package/dist/pdf.js +84 -33
- package/dist/pdf.js.map +1 -1
- package/package.json +11 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
+
`git diff npm/print/v0.5.10..npm/print/v0.6.1 -- apps/shared/print`).
|
|
7
|
+
No source changes intended and no consumer action required.
|
|
8
|
+
|
|
9
|
+
## 0.6.0
|
|
10
|
+
|
|
11
|
+
- **Added: page breaks.** A line that is exactly `<!-- pagebreak -->`
|
|
12
|
+
(canonical), `<!-- page-break -->`, `<!-- newpage -->`, `\pagebreak`,
|
|
13
|
+
`\newpage`, or a Typora-style `<div style="page-break-after: always"></div>`
|
|
14
|
+
starts a new printed page. On screen it draws a dashed rule labelled
|
|
15
|
+
"Page break"; in print it is a real `break-after: page`. Both skins emit the
|
|
16
|
+
same `.matrx-page-break` element.
|
|
17
|
+
- **Added: `@ai-matrx/print/directives`** — the zero-dependency page-break
|
|
18
|
+
grammar (`isPageBreakLine`, `splitAtPageBreaks`, `PAGE_BREAK_MARKDOWN`,
|
|
19
|
+
`PAGE_BREAK_CLASS`, `PAGE_BREAK_LABEL`) for any surface that renders
|
|
20
|
+
markdown, so the chat preview and the printers share one definition.
|
|
21
|
+
- **Added:** `markdownToPdfBlob` honours page breaks — every section after a
|
|
22
|
+
break starts on a new PDF page (the canvas lane ignores CSS breaks, so the
|
|
23
|
+
content is captured per section).
|
|
24
|
+
- **Fixed:** `<artifact type id version title>…</artifact>` blocks pasted their
|
|
25
|
+
raw body into the output — a full HTML artifact injected its own `<html>`,
|
|
26
|
+
`<head>`, `<style>` and `<script>` into the host page, and the emphasis pass
|
|
27
|
+
rewrote `*` inside the script, so the script threw. Artifacts now render as a
|
|
28
|
+
titled panel: source-code types (html, react, svg, mermaid, diff, code) and
|
|
29
|
+
JSON bodies print as escaped source; markdown bodies convert in the same skin.
|
|
30
|
+
- **Fixed:** the document skin ran emphasis, lists and paragraphs inside fenced
|
|
31
|
+
and inline code (`1*2*3` became `1<em>2</em>3`). Code is now protected and
|
|
32
|
+
escaped in both skins; `~~~` fences and indented fences are recognised.
|
|
33
|
+
- **Fixed:** page-level markup outside a fence (`<script>`, `<style>`, `<head>`,
|
|
34
|
+
the `<html>`/`<body>` shell) is removed — paired regions whole, stray tags
|
|
35
|
+
alone, repeated until stable so a split tag cannot reassemble — and raw HTML
|
|
36
|
+
loses `on…` handlers, `srcdoc`, and script-scheme URLs (entity-obfuscated
|
|
37
|
+
forms included). Announced once via the new `html-stripped` notice with the
|
|
38
|
+
remedy. Inline code is protected first, so prose about tags survives.
|
|
39
|
+
- **Fixed:** emphasis follows the CommonMark intraword rule (`snake_case_name`
|
|
40
|
+
stays literal); a bullet that opens with bold stays a bullet; bold/code
|
|
41
|
+
inside link text renders; URLs with one level of parentheses parse.
|
|
42
|
+
- **Parity (both skins now handle the same markdown):** GFM task lists print as
|
|
43
|
+
real boxes, `~~strikethrough~~`, table column alignment (`:-:`, `--:`) and
|
|
44
|
+
empty cells (they used to shift columns left), the chat's `***` accent and
|
|
45
|
+
`#===` heavy dividers, nested lists in the article skin, and `<flashcards>`
|
|
46
|
+
in the article skin (printed as question/answer pairs).
|
|
47
|
+
- **Print polish:** table header rows repeat on every page and rows never
|
|
48
|
+
split; long code lines wrap instead of being cut off; headings stay with the
|
|
49
|
+
content that follows; page margins are set; pages are numbered "3 / 12"
|
|
50
|
+
(bottom centre) — `getMarkdownStylesheet({ pageNumbers: false })` turns the
|
|
51
|
+
numbering off. New `MARKDOWN_SHARED_CSS` / `MARKDOWN_PAGE_CSS` exports.
|
|
52
|
+
- Consumer action: none — refresh to latest. `stripDecorativeLineBreaks`-style
|
|
53
|
+
host regexes should match `class="matrx-hr…"` (accent/heavy rules add a
|
|
54
|
+
second class).
|
|
55
|
+
|
|
3
56
|
## 0.5.10
|
|
4
57
|
|
|
5
58
|
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":[]}
|
package/dist/index.cjs.map
CHANGED
|
@@ -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';
|