gutterpress 0.11.22 → 0.11.23-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +5 -5
  2. package/dist/{README-3nj919rm.md → README-8ef336pp.md} +1 -1
  3. package/dist/api/index.js +4 -4
  4. package/dist/{build-53bw14h7.js → build-09cd1n7s.js} +8 -8
  5. package/dist/{chapter-01-rkpyvjna.md → chapter-01-gsbjt584.md} +1 -1
  6. package/dist/chapter-01-s71xrf5q.md +69 -0
  7. package/dist/{chapter-01-7jwqg1ct.md → chapter-01-wgjfz4w6.md} +1 -1
  8. package/dist/checks/source/character-table.d.ts +12 -6
  9. package/dist/{cli-ke86zsz6.js → cli-5hkmp2wy.js} +70 -40
  10. package/dist/{cli-kx4hbnx2.js → cli-b0vyjvmt.js} +1 -1
  11. package/dist/{cli-1hxxwbaq.js → cli-ccp7sxz3.js} +1 -1
  12. package/dist/{cli-zj5xaepv.js → cli-dd115fa8.js} +22 -6
  13. package/dist/{cli-8kg0wjdt.js → cli-dtckwda1.js} +1 -1
  14. package/dist/{cli-1ngqzg8s.js → cli-fe43evmf.js} +21 -5
  15. package/dist/{cli-ntwqmdnw.js → cli-h17e3mtz.js} +462 -4
  16. package/dist/{cli-cjftezxr.js → cli-nr6j6300.js} +2 -1
  17. package/dist/cli.js +12 -12
  18. package/dist/{doctor-xka1frnx.js → doctor-nddspx4n.js} +3 -3
  19. package/dist/{engine-p93f06gj.js → engine-892swzzc.js} +2 -2
  20. package/dist/{engine-0ndvy8vj.js → engine-yjzknt0g.js} +2 -2
  21. package/dist/{ext-dwfqyy1j.js → ext-zjpen4yx.js} +9 -9
  22. package/dist/{extension-restore-7vp3vh77.js → extension-restore-jvs60x9b.js} +3 -3
  23. package/dist/{extension-restore-ct1wk344.js → extension-restore-w8xt4kbq.js} +5 -5
  24. package/dist/{index-53nrm5j1.js → index-0w2azsk1.js} +1 -1
  25. package/dist/{index-4v7e621e.js → index-2jpk25en.js} +464 -5
  26. package/dist/{index-vay3ff1f.js → index-dsvk7q6s.js} +21 -5
  27. package/dist/{index-6cmn2wpd.js → index-m4hhzg3c.js} +21 -5
  28. package/dist/{index-kh7fayjv.js → index-zrhyb967.js} +328 -311
  29. package/dist/index.js +5 -5
  30. package/dist/lib/build-runner.d.ts +1 -1
  31. package/dist/lib/extension-manager.d.ts +1 -1
  32. package/dist/lib/extension-specifier.d.ts +1 -1
  33. package/dist/lib/markdown/assemble.d.ts +7 -1
  34. package/dist/lib/markdown/gutterpress-css.d.ts +1 -1
  35. package/dist/lib/markdown/index.d.ts +4 -0
  36. package/dist/lib/markdown/math.d.ts +43 -0
  37. package/dist/lib/markdown/renderer.d.ts +11 -8
  38. package/dist/lib/markdown/table.d.ts +46 -0
  39. package/dist/lib/markdown/typography.d.ts +12 -0
  40. package/dist/lib/presets.d.ts +1 -1
  41. package/dist/lib/project-scaffold.d.ts +1 -1
  42. package/dist/lib/project-templates.d.ts +9 -1
  43. package/dist/manifest-8f10dr4f.yaml +17 -0
  44. package/dist/{manifest.schema-399bx3vc.json → manifest.schema-9aeaered.json} +8 -1
  45. package/dist/{new-1pawj2qf.js → new-xhq8wczn.js} +8 -8
  46. package/dist/package-zttkgydq.json +10 -0
  47. package/dist/{plugins-fr4fnzhb.js → plugins-9en8m9y0.js} +3 -3
  48. package/dist/{plugins-g4dgw7mn.js → plugins-gf0gkjmv.js} +1 -1
  49. package/dist/{preflight-ept9q57t.js → preflight-negnxg7b.js} +8 -8
  50. package/dist/preview/file-watcher.d.ts +1 -0
  51. package/dist/{preview-fxpnjv7k.js → preview-cep2s9cg.js} +8 -8
  52. package/dist/{publish-zhqqsgmr.js → publish-xery6qc6.js} +8 -8
  53. package/dist/render.js +470 -11
  54. package/dist/schema/manifest.types.d.ts +8 -0
  55. package/dist/theme-9c58td2d.css +76 -0
  56. package/dist/{validate-t68gvckq.js → validate-3d0sgf4g.js} +8 -8
  57. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -116,7 +116,7 @@ import {
116
116
  substituteVariables,
117
117
  syncProject,
118
118
  testRemoteAccess
119
- } from "./index-kh7fayjv.js";
119
+ } from "./index-zrhyb967.js";
120
120
  import {
121
121
  BUILT_IN_STYLE_SET_IDS,
122
122
  BUNDLED_EXTENSIONS,
@@ -154,7 +154,7 @@ import {
154
154
  setExtensionEnabled,
155
155
  updateExtensions,
156
156
  validateProjectExtensions
157
- } from "./index-6cmn2wpd.js";
157
+ } from "./index-m4hhzg3c.js";
158
158
  import {
159
159
  EXTENSION_MANIFEST_FILENAME,
160
160
  assertExtensionContained,
@@ -165,15 +165,15 @@ import {
165
165
  pathEscapesFolder,
166
166
  readExtensionMeta,
167
167
  resolveExtension
168
- } from "./index-4v7e621e.js";
168
+ } from "./index-2jpk25en.js";
169
169
  import"./index-ht1bcqdd.js";
170
170
  import {
171
171
  BUILD_DIAGNOSTIC_CODES
172
- } from "./index-53nrm5j1.js";
172
+ } from "./index-0w2azsk1.js";
173
173
  import"./index-apc8w9w8.js";
174
174
  import {
175
175
  BuildError
176
- } from "./index-vay3ff1f.js";
176
+ } from "./index-dsvk7q6s.js";
177
177
  import"./index-2ppw6q9v.js";
178
178
  import {
179
179
  DEFAULT_PRINT_OPTS,
@@ -169,7 +169,7 @@ export interface BuildContext {
169
169
  * The build's ONE plugin load (#262). `null` until {@link loadBuildPlugins}
170
170
  * runs; every stage that needs plugins (the preValidate gate in
171
171
  * {@link runQualityGates}, and {@link renderBook}) calls that function and
172
- * gets the SAME resolved `{ plugins, pluginCss, pluginStylePaths }` back,
172
+ * gets the SAME resolved `{ plugins, pluginStyles, pluginStylePaths }` back,
173
173
  * whichever of them runs first.
174
174
  *
175
175
  * One load means one answer: the validation gate checks exactly the plugin
@@ -74,7 +74,7 @@ export interface RecommendedExtension {
74
74
  */
75
75
  export declare const RECOMMENDED_EXTENSIONS: RecommendedExtension[];
76
76
  /** The built-in looks shipped as embedded assets (ids are folder names). */
77
- export declare const BUILT_IN_STYLE_SET_IDS: readonly ["clean-book", "zine", "technical-doc"];
77
+ export declare const BUILT_IN_STYLE_SET_IDS: readonly ["clean-book", "zine", "technical-doc", "academic"];
78
78
  export type BuiltInStyleSetId = (typeof BUILT_IN_STYLE_SET_IDS)[number];
79
79
  /** A built-in look, as listed for the desktop's Look view. */
80
80
  export interface BuiltInStyleSet {
@@ -3,7 +3,7 @@
3
3
  * `markdown/renderer.ts` — a test pins the two lists together). Writing one
4
4
  * of these names installs nothing: the loader resolves it from the bundle.
5
5
  */
6
- export declare const BUNDLED_EXTENSIONS: readonly ["markdown-it-mark", "markdown-it-sub", "markdown-it-sup", "markdown-it-abbr", "gutterpress-gfm-alerts"];
6
+ export declare const BUNDLED_EXTENSIONS: readonly ["markdown-it-mark", "markdown-it-sub", "markdown-it-sup", "markdown-it-abbr", "gutterpress-gfm-alerts", "gutterpress-math"];
7
7
  export type BundledExtensionName = (typeof BUNDLED_EXTENSIONS)[number];
8
8
  export declare function isBundledExtension(name: string): name is BundledExtensionName;
9
9
  /** `./x`, `../x`, `/x`, or a Windows drive path (`C:\x`). Nothing else is a path. */
@@ -4,7 +4,7 @@ import { type PrintAreaSource } from "../../engine/shared/print-area.ts";
4
4
  /** Reader injected by the host: resolve a project-root-relative file → its text. */
5
5
  export type ReadText = (relPath: string) => Promise<string>;
6
6
  /**
7
- * One author-mistake warning emitted by Gutterpress's marker parser.
7
+ * One author-mistake warning, most emitted by Gutterpress's marker parser.
8
8
  * Mirrors the shape `markers.js`'s `warn()` pushes onto
9
9
  * `env.layoutWarnings` — see that file's header comment for the warning
10
10
  * `type`s (`ambiguous_marker_token`, `unrecognized_marker_token`,
@@ -36,6 +36,10 @@ export type ReadText = (relPath: string) => Promise<string>;
36
36
  * for an `@page` / `@section` word that is a near-miss of a declared page /
37
37
  * section variant (any other word there is a free name and stays silent).
38
38
  *
39
+ * Two come from outside the marker parser: `table_extra_cells` (a table
40
+ * row with more cells than the header, `table.ts`) and `math_error` (a
41
+ * formula MathJax could not typeset, `math.ts`).
42
+ *
39
43
  * There is deliberately no `section_without_page`: a @section with no open
40
44
  * @page is valid authoring (17/17 false positives across two real books).
41
45
  */
@@ -60,6 +64,8 @@ export declare const PAGE_BACKGROUND_FIXED_MILESTONE = 152;
60
64
  export interface AssembleBookHtmlOptions {
61
65
  /** Ordered list of project-root-relative `.md` files to concatenate. */
62
66
  files: string[];
67
+ /** The book's BCP 47 language tag (manifest `language`); `en` when absent. */
68
+ language?: string;
63
69
  /** Async reader the assembler uses to fetch each file's contents. */
64
70
  readText: ReadText;
65
71
  /**
@@ -151,7 +151,7 @@
151
151
  * carrying gp-left could start shifting; that is standards-tracking per
152
152
  * CLAUDE.md ("Chrome wins once it ships"), not a bug in the author's book.
153
153
  */
154
- export declare const GUTTERPRESS_CSS = "\n/* gp-* author image/block vocabulary. One vocabulary, gp-* only \u2014 the\n pre-vocabulary utility names (.center/.float-left/.float-right/\n .full-width/.full-bleed) were REMOVED when gp-* shipped; books rename\n the classes in their markdown (see the migration note). Source ORDER is\n the contract \u2014 see the doctrine comment above. */\n\n/* flow positions */\n.gp-left {\n float: left;\n margin: 0 var(--gp-gap, 1em) var(--gp-gap, 1em) 0;\n max-width: 50%;\n}\n.gp-right {\n float: right;\n margin: 0 0 var(--gp-gap, 1em) var(--gp-gap, 1em);\n max-width: 50%;\n}\n.gp-center {\n display: block;\n float: none;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n}\n.gp-full {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n@page gp-full-bleed { margin-left: 0; margin-right: 0; }\n.gp-bleed {\n display: block;\n float: none;\n break-before: page;\n page: gp-full-bleed;\n max-width: none;\n width: 100%;\n margin-left: 0;\n margin-right: 0;\n}\n\n/* sizes \u2014 AFTER the flow positions so max-width:100% lifts the floats' 50%\n cap at equal specificity */\n.gp-size-10 { width: 10%; max-width: 100%; }\n.gp-size-20 { width: 20%; max-width: 100%; }\n.gp-size-30 { width: 30%; max-width: 100%; }\n.gp-size-40 { width: 40%; max-width: 100%; }\n.gp-size-50 { width: 50%; max-width: 100%; }\n.gp-size-60 { width: 60%; max-width: 100%; }\n.gp-size-70 { width: 70%; max-width: 100%; }\n.gp-size-80 { width: 80%; max-width: 100%; }\n.gp-size-90 { width: 90%; max-width: 100%; }\n\n/* float clearance presets \u2014 consumed by var(--gp-gap) in the float rules\n above and by .gp-shape's shape-margin below; --gp-gap itself is\n author-settable CSS */\n.gp-tight { --gp-gap: 0.5em; }\n.gp-loose { --gp-gap: 2em; }\n\n/* column runs \u2014 plain CSS Multi-column, emitted by the core @columns marker\n (markers.js): @columns [1-5] ... @end-columns is the ONE way to lay\n content out in columns, and @column-break moves to the next column.\n The classes below are the CSS that marker emits, not author vocabulary.\n\n Permanent vocabulary, not a shim: Chromium implements multicol natively\n and these rules are the standard properties verbatim, so there is no\n spec gap here to remove later. Deliberately minimal \u2014 column-fill is\n NOT set (the CSS initial balance applies).\n Themes style a run through two tokens instead of writing column rules:\n --gp-column-gap default 1.5em\n --gp-column-rule default none (any column-rule shorthand value)\n Both hooks sit at zero specificity (:where), so any author rule wins.\n .gp-columns is two columns on its own; a count class (bare @columns\n emits gp-columns-2) wins because it comes later at the same specificity. */\n:where(.gp-columns, .gp-columns-1, .gp-columns-2, .gp-columns-3, .gp-columns-4, .gp-columns-5) {\n column-gap: var(--gp-column-gap, 1.5em);\n column-rule: var(--gp-column-rule, none);\n}\n.gp-columns { columns: 2; }\n.gp-columns-1 { columns: 1; }\n.gp-columns-2 { columns: 2; }\n.gp-columns-3 { columns: 3; }\n.gp-columns-4 { columns: 4; }\n.gp-columns-5 { columns: 5; }\n\n/* fragmentation controls \u2014 generic pagination utilities for any element,\n independent of the column vocabulary above (a book reached for\n these just as often outside a multicol run: keeping a card whole,\n forcing a section to start a fresh page). Standard properties only \u2014\n this project is Chromium-only (CLAUDE.md), so there are no legacy\n page-break-* twins to also emit. One name each. */\n.gp-no-break { break-inside: avoid; }\n.gp-break-before { break-before: page; }\n\n/* shape wrap \u2014 text follows the image's alpha silhouette instead of its\n rectangular box. shape-outside only applies to floats, so this is inert\n without .gp-left/.gp-right (and under .gp-pin, which un-floats). The\n shape URL cannot be written in CSS (url() contexts can't read attr()),\n so the image renderer rule (images.ts) mirrors the src into an inline\n --gp-shape:url(...) custom property whenever it sees this class --\n authors only ever type the class. threshold 0.2 ignores near-transparent\n anti-aliasing halos; shape-margin shares the float-gap vocabulary. */\nimg.gp-shape {\n shape-outside: var(--gp-shape);\n shape-image-threshold: 0.2;\n shape-margin: var(--gp-gap, 1em);\n}\n\n/* pin \u2014 within the nearest positioned ancestor (.page/.spread, rule above).\n The four insets are the pin box: 0 (the text block's edge) unless an\n engine set --gp-pin-<edge> on the page root for an edge a pin uses (the\n art-safe line). Non-auto insets and the explicit centers are\n load-bearing; see doctrine comment. */\n.gp-pin {\n position: absolute;\n top: var(--gp-pin-top, 0px);\n right: var(--gp-pin-right, 0px);\n bottom: var(--gp-pin-bottom, 0px);\n left: var(--gp-pin-left, 0px);\n align-self: center;\n justify-self: center;\n margin: 0;\n max-width: 100%;\n}\n\n/* pin edge modifiers \u2014 AFTER .gp-pin to beat its center defaults;\n justify-self is inert on in-flow floats, so gp-left/gp-right safely do\n double duty as flow float + pin edge */\n.gp-top { align-self: start; }\n.gp-bottom { align-self: end; }\n.gp-left { justify-self: start; }\n.gp-right { justify-self: end; }\n\n/* the escape hatch \u2014 the paper's edge, past the printer's safe area: the\n pin box loses its art-safe insets. Set on the pin itself, so it beats the\n values the page root hands down. */\n.gp-pin.gp-flush {\n --gp-pin-top: 0px;\n --gp-pin-right: 0px;\n --gp-pin-bottom: 0px;\n --gp-pin-left: 0px;\n}\n\n/* wrapper-margin neutralization (same pattern and rationale as the\n .gp-bleed paragraph-margin note in the doctrine comment; for pin, the\n emptied paragraph would otherwise leave a phantom margin gap in flow) */\n:where(p:has(> img.gp-bleed:only-child)) { margin: 0; }\n:where(p:has(> img.gp-pin:only-child)) { margin: 0; }\n\n/* depth \u2014 a named ladder for z-index, so books stop hand-tuning bare\n integers. A real book measured 21 z-index declarations using only four\n distinct values (-1, 0, 1, 2), each written literally at its use site.\n The custom properties are the author-settable surface (a book needing a\n deeper stack raises them once); the classes are the shorthand.\n\n NOT named \"layer\": CSS Paged Media 3 \u00A73.1 already defines \"page layers\"\n (page background, canvas, borders, contents, margin boxes) and those are\n parts of the PAGE BOX, not a z-ladder for content. Reusing the word for a\n different concept would collide with the spec vocabulary this project\n tracks. The pin EDGE modifiers already own .gp-top/.gp-bottom, so the\n ladder avoids those words too.\n\n .gp-behind is the one that earns its place: it puts a pinned image UNDER\n the page's text, which is otherwise impossible to express without a bare\n negative z-index. \"Above\" needs no class \u2014 an out-of-flow pin already\n paints above in-flow content.\n\n Two things silently defeat .gp-behind, neither visible at the use site:\n - a stacking context on the .page/.spread ancestor (z-index, isolation,\n opacity, filter, transform on it traps the negative layer inside).\n Core keeps .page/.spread at 'position: relative; z-index: auto'\n precisely so they are not stacking contexts.\n - a clipping ancestor (overflow other than visible) \u2014 but only where\n the art actually overhangs that ancestor's clip box on a clipped\n axis: the overhang is cut off, the same mechanism that clips a\n .gp-bleed plate back to the wrapper's width. Clipping never reorders\n layers \u2014 within-bounds art under a clipping .page prints whole and\n still behind (measured; see the build audit's comment in\n engine/compiler/build.ts), and a static wrapper's overflow never\n binds an abspos .gp-pin at all.\n The build-time engine.layer.trapped audit reports both against the live\n ancestor chain. printsafe/page-containment is only an early source hint for\n declarations written directly on .page/.spread. */\n:root {\n --gp-z-behind: -1;\n --gp-z-base: 0;\n --gp-z-raised: 1;\n --gp-z-front: 2;\n}\n.gp-behind { z-index: var(--gp-z-behind); }\n.gp-base { z-index: var(--gp-z-base); }\n.gp-raised { z-index: var(--gp-z-raised); }\n.gp-front { z-index: var(--gp-z-front); }\n\n/* GFM-style alert/callout boxes (#237) \u2014 the DOM gfm-alerts.ts emits from\n \"> [!TYPE]\" blockquote syntax when a project opts into the bundled\n gutterpress-gfm-alerts feature (BUILTIN_OPTIONAL_PLUGINS, renderer.ts).\n The plugin is optional and inert by default; these rules are equally\n inert wherever the classes never appear, exactly like every other\n utility above, so shipping them costs nothing to a book that has not\n turned the feature on.\n\n This is authored-COMPONENT vocabulary, not the @marker structural family\n (markers.js/MARKER_CSS) \u2014 CLAUDE.md \u00A76 splits the two core CSS blocks by\n ROLE, and an author-facing box an author invokes via inline syntax is the\n same role as every other gp-* utility here, not page/chapter/section DOM.\n\n Deliberately minimal (CLAUDE.md \"thin over capable\" / the issue's own\n \"a rule and a label\"): a left border plus a bold label, :where()'d to\n zero specificity so a theme's own (unlayered) rule for any of these\n classes wins outright regardless of selector weight \u2014 see the\n cascade-layers note atop this file. Five semantic accent colors (the same\n NOTE/TIP/IMPORTANT/WARNING/CAUTION hues GitHub's own alerts use \u2014 a\n long-established, non-proprietary convention, not DC branding) are\n exposed as :root custom properties, the same author-settable-surface\n pattern as the --gp-z-* ladder above: a book retints every alert of one\n type by overriding a single property, no specificity fight required.\n --gp-alert-color cascades from the type modifier down to .gp-alert-title\n through ordinary CSS inheritance (the title is always a DOM descendant of\n the box), so it is set in exactly five places, not ten. */\n:root {\n --gp-alert-note-color: #0969da;\n --gp-alert-tip-color: #1a7f37;\n --gp-alert-important-color: #8250df;\n --gp-alert-warning-color: #9a6700;\n --gp-alert-caution-color: #cf222e;\n}\n:where(.gp-alert) {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 0.25em solid var(--gp-alert-color, currentColor);\n}\n:where(.gp-alert-title) {\n margin: 0 0 0.5em;\n font-weight: bold;\n color: var(--gp-alert-color, inherit);\n}\n:where(.gp-alert-note) { --gp-alert-color: var(--gp-alert-note-color); }\n:where(.gp-alert-tip) { --gp-alert-color: var(--gp-alert-tip-color); }\n:where(.gp-alert-important) { --gp-alert-color: var(--gp-alert-important-color); }\n:where(.gp-alert-warning) { --gp-alert-color: var(--gp-alert-warning-color); }\n:where(.gp-alert-caution) { --gp-alert-color: var(--gp-alert-caution-color); }\n";
154
+ export declare const GUTTERPRESS_CSS = "\n/* gp-* author image/block vocabulary. One vocabulary, gp-* only \u2014 the\n pre-vocabulary utility names (.center/.float-left/.float-right/\n .full-width/.full-bleed) were REMOVED when gp-* shipped; books rename\n the classes in their markdown (see the migration note). Source ORDER is\n the contract \u2014 see the doctrine comment above. */\n\n/* flow positions */\n.gp-left {\n float: left;\n margin: 0 var(--gp-gap, 1em) var(--gp-gap, 1em) 0;\n max-width: 50%;\n}\n.gp-right {\n float: right;\n margin: 0 0 var(--gp-gap, 1em) var(--gp-gap, 1em);\n max-width: 50%;\n}\n.gp-center {\n display: block;\n float: none;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n}\n.gp-full {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n@page gp-full-bleed { margin-left: 0; margin-right: 0; }\n.gp-bleed {\n display: block;\n float: none;\n break-before: page;\n page: gp-full-bleed;\n max-width: none;\n width: 100%;\n margin-left: 0;\n margin-right: 0;\n}\n\n/* sizes \u2014 AFTER the flow positions so max-width:100% lifts the floats' 50%\n cap at equal specificity */\n.gp-size-10 { width: 10%; max-width: 100%; }\n.gp-size-20 { width: 20%; max-width: 100%; }\n.gp-size-30 { width: 30%; max-width: 100%; }\n.gp-size-40 { width: 40%; max-width: 100%; }\n.gp-size-50 { width: 50%; max-width: 100%; }\n.gp-size-60 { width: 60%; max-width: 100%; }\n.gp-size-70 { width: 70%; max-width: 100%; }\n.gp-size-80 { width: 80%; max-width: 100%; }\n.gp-size-90 { width: 90%; max-width: 100%; }\n\n/* float clearance presets \u2014 consumed by var(--gp-gap) in the float rules\n above and by .gp-shape's shape-margin below; --gp-gap itself is\n author-settable CSS */\n.gp-tight { --gp-gap: 0.5em; }\n.gp-loose { --gp-gap: 2em; }\n\n/* column runs \u2014 plain CSS Multi-column, emitted by the core @columns marker\n (markers.js): @columns [1-5] ... @end-columns is the ONE way to lay\n content out in columns, and @column-break moves to the next column.\n The classes below are the CSS that marker emits, not author vocabulary.\n\n Permanent vocabulary, not a shim: Chromium implements multicol natively\n and these rules are the standard properties verbatim, so there is no\n spec gap here to remove later. Deliberately minimal \u2014 column-fill is\n NOT set (the CSS initial balance applies).\n Themes style a run through two tokens instead of writing column rules:\n --gp-column-gap default 1.5em\n --gp-column-rule default none (any column-rule shorthand value)\n Both hooks sit at zero specificity (:where), so any author rule wins.\n .gp-columns is two columns on its own; a count class (bare @columns\n emits gp-columns-2) wins because it comes later at the same specificity. */\n:where(.gp-columns, .gp-columns-1, .gp-columns-2, .gp-columns-3, .gp-columns-4, .gp-columns-5) {\n column-gap: var(--gp-column-gap, 1.5em);\n column-rule: var(--gp-column-rule, none);\n}\n.gp-columns { columns: 2; }\n.gp-columns-1 { columns: 1; }\n.gp-columns-2 { columns: 2; }\n.gp-columns-3 { columns: 3; }\n.gp-columns-4 { columns: 4; }\n.gp-columns-5 { columns: 5; }\n\n/* fragmentation controls \u2014 generic pagination utilities for any element,\n independent of the column vocabulary above (a book reached for\n these just as often outside a multicol run: keeping a card whole,\n forcing a section to start a fresh page). Standard properties only \u2014\n this project is Chromium-only (CLAUDE.md), so there are no legacy\n page-break-* twins to also emit. One name each. */\n.gp-no-break { break-inside: avoid; }\n.gp-break-before { break-before: page; }\n\n/* shape wrap \u2014 text follows the image's alpha silhouette instead of its\n rectangular box. shape-outside only applies to floats, so this is inert\n without .gp-left/.gp-right (and under .gp-pin, which un-floats). The\n shape URL cannot be written in CSS (url() contexts can't read attr()),\n so the image renderer rule (images.ts) mirrors the src into an inline\n --gp-shape:url(...) custom property whenever it sees this class --\n authors only ever type the class. threshold 0.2 ignores near-transparent\n anti-aliasing halos; shape-margin shares the float-gap vocabulary. */\nimg.gp-shape {\n shape-outside: var(--gp-shape);\n shape-image-threshold: 0.2;\n shape-margin: var(--gp-gap, 1em);\n}\n\n/* pin \u2014 within the nearest positioned ancestor (.page/.spread, rule above).\n The four insets are the pin box: 0 (the text block's edge) unless an\n engine set --gp-pin-<edge> on the page root for an edge a pin uses (the\n art-safe line). Non-auto insets and the explicit centers are\n load-bearing; see doctrine comment. */\n.gp-pin {\n position: absolute;\n top: var(--gp-pin-top, 0px);\n right: var(--gp-pin-right, 0px);\n bottom: var(--gp-pin-bottom, 0px);\n left: var(--gp-pin-left, 0px);\n align-self: center;\n justify-self: center;\n margin: 0;\n max-width: 100%;\n}\n\n/* pin edge modifiers \u2014 AFTER .gp-pin to beat its center defaults;\n justify-self is inert on in-flow floats, so gp-left/gp-right safely do\n double duty as flow float + pin edge */\n.gp-top { align-self: start; }\n.gp-bottom { align-self: end; }\n.gp-left { justify-self: start; }\n.gp-right { justify-self: end; }\n\n/* the escape hatch \u2014 the paper's edge, past the printer's safe area: the\n pin box loses its art-safe insets. Set on the pin itself, so it beats the\n values the page root hands down. */\n.gp-pin.gp-flush {\n --gp-pin-top: 0px;\n --gp-pin-right: 0px;\n --gp-pin-bottom: 0px;\n --gp-pin-left: 0px;\n}\n\n/* wrapper-margin neutralization (same pattern and rationale as the\n .gp-bleed paragraph-margin note in the doctrine comment; for pin, the\n emptied paragraph would otherwise leave a phantom margin gap in flow) */\n:where(p:has(> img.gp-bleed:only-child)) { margin: 0; }\n:where(p:has(> img.gp-pin:only-child)) { margin: 0; }\n\n/* depth \u2014 a named ladder for z-index, so books stop hand-tuning bare\n integers. A real book measured 21 z-index declarations using only four\n distinct values (-1, 0, 1, 2), each written literally at its use site.\n The custom properties are the author-settable surface (a book needing a\n deeper stack raises them once); the classes are the shorthand.\n\n NOT named \"layer\": CSS Paged Media 3 \u00A73.1 already defines \"page layers\"\n (page background, canvas, borders, contents, margin boxes) and those are\n parts of the PAGE BOX, not a z-ladder for content. Reusing the word for a\n different concept would collide with the spec vocabulary this project\n tracks. The pin EDGE modifiers already own .gp-top/.gp-bottom, so the\n ladder avoids those words too.\n\n .gp-behind is the one that earns its place: it puts a pinned image UNDER\n the page's text, which is otherwise impossible to express without a bare\n negative z-index. \"Above\" needs no class \u2014 an out-of-flow pin already\n paints above in-flow content.\n\n Two things silently defeat .gp-behind, neither visible at the use site:\n - a stacking context on the .page/.spread ancestor (z-index, isolation,\n opacity, filter, transform on it traps the negative layer inside).\n Core keeps .page/.spread at 'position: relative; z-index: auto'\n precisely so they are not stacking contexts.\n - a clipping ancestor (overflow other than visible) \u2014 but only where\n the art actually overhangs that ancestor's clip box on a clipped\n axis: the overhang is cut off, the same mechanism that clips a\n .gp-bleed plate back to the wrapper's width. Clipping never reorders\n layers \u2014 within-bounds art under a clipping .page prints whole and\n still behind (measured; see the build audit's comment in\n engine/compiler/build.ts), and a static wrapper's overflow never\n binds an abspos .gp-pin at all.\n The build-time engine.layer.trapped audit reports both against the live\n ancestor chain. printsafe/page-containment is only an early source hint for\n declarations written directly on .page/.spread. */\n:root {\n --gp-z-behind: -1;\n --gp-z-base: 0;\n --gp-z-raised: 1;\n --gp-z-front: 2;\n}\n.gp-behind { z-index: var(--gp-z-behind); }\n.gp-base { z-index: var(--gp-z-base); }\n.gp-raised { z-index: var(--gp-z-raised); }\n.gp-front { z-index: var(--gp-z-front); }\n\n/* GFM-style alert/callout boxes (#237) \u2014 the DOM gfm-alerts.ts emits from\n \"> [!TYPE]\" blockquote syntax when a project opts into the bundled\n gutterpress-gfm-alerts feature (BUILTIN_OPTIONAL_PLUGINS, renderer.ts).\n The plugin is optional and inert by default; these rules are equally\n inert wherever the classes never appear, exactly like every other\n utility above, so shipping them costs nothing to a book that has not\n turned the feature on.\n\n This is authored-COMPONENT vocabulary, not the @marker structural family\n (markers.js/MARKER_CSS) \u2014 CLAUDE.md \u00A76 splits the two core CSS blocks by\n ROLE, and an author-facing box an author invokes via inline syntax is the\n same role as every other gp-* utility here, not page/chapter/section DOM.\n\n Deliberately minimal (CLAUDE.md \"thin over capable\" / the issue's own\n \"a rule and a label\"): a left border plus a bold label, :where()'d to\n zero specificity so a theme's own (unlayered) rule for any of these\n classes wins outright regardless of selector weight \u2014 see the\n cascade-layers note atop this file. Five semantic accent colors (the same\n NOTE/TIP/IMPORTANT/WARNING/CAUTION hues GitHub's own alerts use \u2014 a\n long-established, non-proprietary convention, not DC branding) are\n exposed as :root custom properties, the same author-settable-surface\n pattern as the --gp-z-* ladder above: a book retints every alert of one\n type by overriding a single property, no specificity fight required.\n --gp-alert-color cascades from the type modifier down to .gp-alert-title\n through ordinary CSS inheritance (the title is always a DOM descendant of\n the box), so it is set in exactly five places, not ten. */\n:root {\n --gp-alert-note-color: #0969da;\n --gp-alert-tip-color: #1a7f37;\n --gp-alert-important-color: #8250df;\n --gp-alert-warning-color: #9a6700;\n --gp-alert-caution-color: #cf222e;\n}\n:where(.gp-alert) {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 0.25em solid var(--gp-alert-color, currentColor);\n}\n:where(.gp-alert-title) {\n margin: 0 0 0.5em;\n font-weight: bold;\n color: var(--gp-alert-color, inherit);\n}\n:where(.gp-alert-note) { --gp-alert-color: var(--gp-alert-note-color); }\n:where(.gp-alert-tip) { --gp-alert-color: var(--gp-alert-tip-color); }\n:where(.gp-alert-important) { --gp-alert-color: var(--gp-alert-important-color); }\n:where(.gp-alert-warning) { --gp-alert-color: var(--gp-alert-warning-color); }\n:where(.gp-alert-caution) { --gp-alert-color: var(--gp-alert-caution-color); }\n\n/* Math (the bundled gutterpress-math feature, math.ts): MathJax's own rules\n for the <mjx-container> SVG output it emits, from its SVG output jax's\n stylesheet, less the ones for its interactive menu and tooltips. Inert in\n a book without formulas. */\nmjx-container[jax=\"SVG\"] { direction: ltr; }\nmjx-container[jax=\"SVG\"] > svg { overflow: visible; min-height: 1px; min-width: 1px; }\nmjx-container[jax=\"SVG\"] > svg a { fill: blue; stroke: blue; }\nmjx-container[jax=\"SVG\"][display=\"true\"] { display: block; text-align: center; margin: 1em 0; }\nmjx-container[jax=\"SVG\"][display=\"true\"][width=\"full\"] { display: flex; }\nmjx-container[jax=\"SVG\"][justify=\"left\"] { text-align: left; }\nmjx-container[jax=\"SVG\"][justify=\"right\"] { text-align: right; }\nmjx-container[jax=\"SVG\"] path[data-c], mjx-container[jax=\"SVG\"] use[data-c] { stroke-width: 3; }\nmjx-assistive-mml {\n position: absolute !important;\n top: 0px; left: 0px;\n clip: rect(1px, 1px, 1px, 1px);\n padding: 1px 0px 0px 0px !important;\n border: 0px !important;\n display: block !important;\n width: auto !important;\n overflow: hidden !important;\n user-select: none;\n}\nmjx-assistive-mml[display=\"block\"] { width: 100% !important; }\ng[data-mml-node=\"merror\"] > g { fill: red; stroke: red; }\ng[data-mml-node=\"merror\"] > rect[data-background] { fill: yellow; stroke: none; }\ng[data-mml-node=\"mtable\"] > line[data-line], svg[data-table] > g > line[data-line] { stroke-width: 70px; fill: none; }\ng[data-mml-node=\"mtable\"] > rect[data-frame], svg[data-table] > g > rect[data-frame] { stroke-width: 70px; fill: none; }\ng[data-mml-node=\"mtable\"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed { stroke-dasharray: 140; }\ng[data-mml-node=\"mtable\"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted { stroke-linecap: round; stroke-dasharray: 0,140; }\ng[data-mml-node=\"mtable\"] > g > svg { overflow: visible; }\n";
155
155
  /**
156
156
  * GP_CLASSES — every class an author may legitimately write with a `gp-`
157
157
  * prefix: the `.gp-*` selectors in `GUTTERPRESS_CSS` above, the two
@@ -32,6 +32,8 @@ export declare function resolveActiveMarkdownFiles(inputDir: string, configuredF
32
32
  */
33
33
  export declare function renderChapters(inputDir: string, opts?: {
34
34
  title?: string;
35
+ /** See {@link AssembleBookHtmlOptions.language}. */
36
+ language?: string;
35
37
  /** See {@link AssembleBookHtmlOptions.print}. */
36
38
  print?: PrintPadding;
37
39
  /** See {@link AssembleBookHtmlOptions.printArea}. */
@@ -73,6 +75,8 @@ export declare function renderChapters(inputDir: string, opts?: {
73
75
  */
74
76
  export declare function renderChaptersToFile(inputDir: string, outDir: string, opts?: {
75
77
  title?: string;
78
+ /** See {@link AssembleBookHtmlOptions.language}. */
79
+ language?: string;
76
80
  /** See {@link AssembleBookHtmlOptions.print}. */
77
81
  print?: PrintPadding;
78
82
  /** See {@link AssembleBookHtmlOptions.printArea}. */
@@ -0,0 +1,43 @@
1
+ import type MarkdownIt from "markdown-it";
2
+ /**
3
+ * LaTeX math — `$…$` in a line, `$$…$$` on its own lines — typeset by
4
+ * MathJax (`mathjax-full`, the same engine arXiv and Wikipedia print with).
5
+ *
6
+ * Bundled, OPT-IN markdown feature (`gutterpress-math` in
7
+ * `BUILTIN_OPTIONAL_PLUGINS`, renderer.ts; "Math" in
8
+ * `RECOMMENDED_EXTENSIONS`, extension-manager.ts), exactly like the Callouts
9
+ * feature: a book that never lists it renders `$` as the character it is.
10
+ *
11
+ * WHY SVG. Each formula is written as a drawing of its own glyphs, so the
12
+ * PDF needs no math font on the machine that builds it, no stylesheet and
13
+ * no font files in the project, prints the same everywhere, and is laid
14
+ * out when the Markdown is rendered — so the live preview paginates exactly
15
+ * like the printed PDF (docs/native-parity-gate.md). The MathML and HTML
16
+ * outputs of KaTeX and MathJax fail one or other of those: measured on a
17
+ * machine with no math font, KaTeX's MathML printed broken fractions, and
18
+ * its HTML output needs ~20 font files copied into every book.
19
+ *
20
+ * The `$` grammar is Pandoc's, as every markdown-it math plugin implements
21
+ * it (ported from markdown-it-katex, MIT, Copyright (c) 2016 Waylon Flinn,
22
+ * via markdown-it-mathjax3): an opening `$` is not followed by a space, a
23
+ * closing one is not preceded by a space and not followed by a digit. That
24
+ * is what keeps "$5 and $10" prose. It lives here rather than in a
25
+ * dependency because the table rule (table.ts) must know where a formula
26
+ * ends, so a `|` inside one does not split the cell — one scanner, two users.
27
+ *
28
+ * ERRORS are never silent: a formula MathJax cannot typeset renders in red,
29
+ * as MathJax does, AND is reported on `env.layoutWarnings` (`math_error`,
30
+ * with the line), the channel the marker parser uses, so the Problems panel
31
+ * and `gutterpress validate` name the mistake — "Missing close brace",
32
+ * "Undefined control sequence \fraction". MathJax's `noerrors` and
33
+ * `noundefined` packages, which would swallow exactly those, are left out.
34
+ *
35
+ * `\newcommand` definitions persist for the rest of the book's render and no
36
+ * further: the TeX state is created per renderer, so one build cannot see
37
+ * another's macros. Styling is in `GUTTERPRESS_CSS` (gutterpress-css.ts),
38
+ * MathJax's own rules for its output.
39
+ */
40
+ /** Index of the `$` that closes the inline formula opened at `start`; -1 when
41
+ * it opens none; -2 for an empty `$$`, which is never inline math. */
42
+ export declare function findInlineMathEnd(src: string, start: number, max: number): number;
43
+ export default function gpMath(md: MarkdownIt): void;
@@ -319,13 +319,14 @@ export interface LoadedPlugin {
319
319
  * error. (attrs/footnote/deflist are NOT here — they are always-on defaults
320
320
  * applied unconditionally below.)
321
321
  *
322
- * `gutterpress-gfm-alerts` (#237) is keyed differently from its four
323
- * siblings: it is not a real npm package, it is Gutterpress's OWN code
324
- * (gfm-alerts.ts) registered under a name that reads like one, matching the
325
- * shape "keyed by npm name" is written for. There is nothing to install
326
- * either way — the lookup below always wins before any npm resolution is
327
- * attempted (`plugins.ts`'s `loadPlugin`) — so a real npm package never
328
- * existing under this exact name costs nothing.
322
+ * `gutterpress-gfm-alerts` (#237) and `gutterpress-math` are keyed
323
+ * differently from the four markdown-it-* entries: they are not real npm
324
+ * packages, they are Gutterpress's OWN code (gfm-alerts.ts, math.ts)
325
+ * registered under names that read like one, matching the shape "keyed by
326
+ * npm name" is written for. There is nothing to install either way — the
327
+ * lookup below always wins before any npm resolution is attempted
328
+ * (`plugins.ts`'s `loadPlugin`) — so a real npm package never existing
329
+ * under either name costs nothing.
329
330
  */
330
331
  export declare const BUILTIN_OPTIONAL_PLUGINS: Record<string, GutterpressPlugin>;
331
332
  /**
@@ -376,7 +377,9 @@ export declare const BUILTIN_OPTIONAL_PLUGINS: Record<string, GutterpressPlugin>
376
377
  *
377
378
  * @param customPlugins - Optional array of custom plugins to load
378
379
  */
379
- export declare function createMarkdownRenderer(customPlugins?: LoadedPlugin[]): MarkdownIt;
380
+ export declare function createMarkdownRenderer(customPlugins?: LoadedPlugin[], options?: {
381
+ language?: string;
382
+ }): MarkdownIt;
380
383
  /**
381
384
  * Apply loaded plugins to a markdown-it instance.
382
385
  *
@@ -0,0 +1,46 @@
1
+ import type MarkdownIt from "markdown-it";
2
+ /**
3
+ * GFM tables, with one change to how a row is split into cells.
4
+ *
5
+ * This is markdown-it's own table rule (`lib/rules_block/table.mjs`, MIT,
6
+ * Copyright (c) 2014 Vitaly Puzrin, Alex Kocharin), re-registered under the
7
+ * same name with its `escapedSplit` replaced by {@link splitCells}:
8
+ *
9
+ * - markdown-it splits a row at every unescaped `|` BEFORE any inline
10
+ * syntax is parsed (the GFM spec's rule), so a `|` inside a code span
11
+ * or a formula splits the cell, and the cells past the header's count
12
+ * are silently dropped. Here an inline span is opaque to the split: a
13
+ * code span always, and a `$…$` formula when the math feature is on
14
+ * (math.ts re-registers this rule with its own scanner). `\|` still
15
+ * escapes a pipe in plain cell text; inside a span nothing is touched,
16
+ * so `\|` in a formula is LaTeX's own `\|`.
17
+ * - a row with MORE cells than the header is reported
18
+ * (`table_extra_cells` on `env.layoutWarnings`, the channel Gutterpress's
19
+ * marker parser uses) instead of losing them without a word.
20
+ *
21
+ * Everything else is the upstream rule, verbatim where possible, so a table
22
+ * renders and paginates exactly as before.
23
+ */
24
+ /**
25
+ * Where to resume scanning a row after the span that starts at `start`
26
+ * (the character there is the span's opening delimiter): just past the
27
+ * span, or just past the opening delimiter when it opens nothing. Always
28
+ * greater than `start`.
29
+ */
30
+ export type SpanScanner = (src: string, start: number, max: number) => number;
31
+ /** The scanners a row is split with, by the character code that opens each span. */
32
+ export type SpanScanners = Readonly<Record<number, SpanScanner>>;
33
+ /** A CommonMark code span: a run of backticks closed by a run of the same length. */
34
+ export declare const scanCodeSpan: SpanScanner;
35
+ /** What core splits with: code spans. The math feature adds `$` (math.ts). */
36
+ export declare const DEFAULT_SPANS: SpanScanners;
37
+ /**
38
+ * Replace markdown-it's table rule with the span-aware one. Core applies it
39
+ * with code spans; a feature that adds an inline span (math) applies it
40
+ * again with its own scanner added, and the later registration wins.
41
+ * `at` resets the rule's `alt` chains, so upstream's are given again: a
42
+ * table may interrupt a paragraph.
43
+ */
44
+ export declare function gpTable(md: MarkdownIt, options?: {
45
+ spans?: SpanScanners;
46
+ }): void;
@@ -0,0 +1,12 @@
1
+ import type MarkdownIt from "markdown-it";
2
+ /**
3
+ * The quotation marks straight quotes print as — markdown-it's `quotes`
4
+ * option: outer open, outer close, inner open, inner close — for the book's
5
+ * language (manifest `language`, a BCP 47 tag). A language not listed gets
6
+ * the English marks. Only the languages whose convention is settled are
7
+ * here; add one with its source, not from memory. The desktop's selection
8
+ * search must fold every mark here; its test reads this table.
9
+ */
10
+ export declare const QUOTES: Record<string, string | string[]>;
11
+ export declare function quotesFor(language?: string): string | string[];
12
+ export declare function gpTypography(md: MarkdownIt): void;
@@ -8,7 +8,7 @@ import type { ResolvedConfig } from "../schema/manifest.types";
8
8
  * Every preset value is overridable from the manifest, leaf by leaf
9
9
  * (resolveConfig's mergeShape; precedence cli > manifest > target > preset).
10
10
  */
11
- export interface VendorPreset extends Omit<ResolvedConfig, "title" | "authors" | "preset" | "targets" | "page"> {
11
+ export interface VendorPreset extends Omit<ResolvedConfig, "title" | "authors" | "language" | "preset" | "targets" | "page"> {
12
12
  /** This preset's id: what the manifest's `preset:` names and a resolved config reports. */
13
13
  id: PresetId;
14
14
  /**
@@ -6,7 +6,7 @@ import { type CustomPageOptions } from "./page-size.ts";
6
6
  * into the binary via `embedded-assets.ts`. `"book"` is the default; the others
7
7
  * give non-technical authors a head start for common formats (#29).
8
8
  */
9
- export type ProjectTemplateId = "book" | "zine" | "technical";
9
+ export type ProjectTemplateId = "book" | "zine" | "technical" | "paper";
10
10
  /**
11
11
  * How (or whether) to put the new project under local version history.
12
12
  *
@@ -1,5 +1,7 @@
1
+ import type { ProjectTemplateId } from "./project-scaffold.ts";
2
+ import type { BuiltInStyleSetId } from "./extension-manager.ts";
1
3
  /** The built-in templates shipped as embedded assets. */
2
- export declare const BUILT_IN_TEMPLATE_IDS: readonly ["book", "zine", "technical"];
4
+ export declare const BUILT_IN_TEMPLATE_IDS: readonly ["book", "zine", "technical", "paper"];
3
5
  /** Author-friendly metadata for one template (built-in or custom). */
4
6
  export interface TemplateInfo {
5
7
  /** Stable id. For built-ins, the {@link ProjectTemplateId}; for custom, a slug. */
@@ -22,6 +24,12 @@ export interface TemplateInfo {
22
24
  /** The `targets:` this template's manifest declares, when it has one. */
23
25
  targets?: string[];
24
26
  }
27
+ /** `look`: the built-in look a new book from the template starts with. */
28
+ export declare const BUILT_IN_META: Record<ProjectTemplateId, {
29
+ label: string;
30
+ description: string;
31
+ look?: BuiltInStyleSetId;
32
+ }>;
25
33
  /**
26
34
  * List the built-in templates. The `preset`/`targets` each one starts a book
27
35
  * from are read from that template's own embedded manifest — one source of
@@ -0,0 +1,17 @@
1
+ title: "{{TITLE}}"
2
+ authors:
3
+ - "{{AUTHOR}}"
4
+ language: en
5
+ source:
6
+ files:
7
+ - chapter-01.md
8
+ styles:
9
+ - styles/book.css
10
+ # A paper prints on US Letter; for A4 set width 595 and height 842.
11
+ page:
12
+ width: 612
13
+ height: 792
14
+ extensions:
15
+ - markdown-it-sup
16
+ - gutterpress-math
17
+ preset: book
@@ -11,6 +11,13 @@
11
11
  "default": "Document",
12
12
  "examples": ["My Book Title", "Technical Documentation", "Game Module"]
13
13
  },
14
+ "language": {
15
+ "type": "string",
16
+ "description": "The book's language as a BCP 47 tag. It is the lang of the built HTML, which hyphenation follows, and sets the quotation marks straight quotes print as.",
17
+ "default": "en",
18
+ "pattern": "^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$",
19
+ "examples": ["en", "de", "de-CH", "fr", "pt-BR"]
20
+ },
14
21
  "authors": {
15
22
  "type": "array",
16
23
  "description": "List of document authors.",
@@ -51,7 +58,7 @@
51
58
  },
52
59
  "extensions": {
53
60
  "type": "array",
54
- "description": "Extensions, in load order: markdown-it plugins, looks (stylesheets), component libraries — anything from npm or a folder. Each entry is a specifier: a bundled feature name (markdown-it-mark, markdown-it-sub, markdown-it-sup, markdown-it-abbr, gutterpress-gfm-alerts), a path starting with ./ or ../ (relative to this file), or an npm package name, optionally pinned as name@version. A later entry loads later: its markdown runs after earlier entries' and its CSS wins ties. The project's own `styles` always load after every extension.",
61
+ "description": "Extensions, in load order: markdown-it plugins, looks (stylesheets), component libraries — anything from npm or a folder. Each entry is a specifier: a bundled feature name (markdown-it-mark, markdown-it-sub, markdown-it-sup, markdown-it-abbr, gutterpress-gfm-alerts, gutterpress-math), a path starting with ./ or ../ (relative to this file), or an npm package name, optionally pinned as name@version. A later entry loads later: its markdown runs after earlier entries' and its CSS wins ties. The project's own `styles` always load after every extension.",
55
62
  "items": {
56
63
  "oneOf": [
57
64
  {
@@ -3,12 +3,12 @@ import {
3
3
  EXTENSION_KINDS,
4
4
  scaffoldExtension,
5
5
  scaffoldProject
6
- } from "./cli-ke86zsz6.js";
6
+ } from "./cli-5hkmp2wy.js";
7
7
  import {
8
8
  resolveGhostscript
9
- } from "./cli-1hxxwbaq.js";
9
+ } from "./cli-ccp7sxz3.js";
10
10
  import"./cli-ht1bcqdd.js";
11
- import"./cli-8kg0wjdt.js";
11
+ import"./cli-dtckwda1.js";
12
12
  import"./cli-2ppw6q9v.js";
13
13
  import"./cli-eyyggect.js";
14
14
  import {
@@ -21,16 +21,16 @@ import {
21
21
  PRESET_IDS,
22
22
  TARGETS,
23
23
  TARGET_IDS
24
- } from "./cli-zj5xaepv.js";
25
- import"./cli-ntwqmdnw.js";
26
- import"./cli-kx4hbnx2.js";
24
+ } from "./cli-dd115fa8.js";
25
+ import"./cli-h17e3mtz.js";
26
+ import"./cli-b0vyjvmt.js";
27
27
  import {
28
28
  UsageError,
29
29
  rejectExtraPositionals,
30
30
  rejectUnknownFlags
31
31
  } from "./cli-d8xjns66.js";
32
- import"./cli-cjftezxr.js";
33
- import"./cli-1ngqzg8s.js";
32
+ import"./cli-nr6j6300.js";
33
+ import"./cli-fe43evmf.js";
34
34
  import {
35
35
  EXIT_CODES
36
36
  } from "./cli-46ycxe6r.js";
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "academic",
3
+ "description": "A journal-article look: serif text, justified and hyphenated, ruled tables, notes and references set smaller.",
4
+ "author": "gutterpress",
5
+ "keywords": ["gutterpress", "gutterpress-look"],
6
+ "gutterpress": {
7
+ "styles": ["theme.css"],
8
+ "preview": null
9
+ }
10
+ }
@@ -8,9 +8,9 @@ import {
8
8
  loadPlugins,
9
9
  loadPluginsWithCss,
10
10
  loadPublishFunction
11
- } from "./cli-ntwqmdnw.js";
12
- import"./cli-kx4hbnx2.js";
13
- import"./cli-cjftezxr.js";
11
+ } from "./cli-h17e3mtz.js";
12
+ import"./cli-b0vyjvmt.js";
13
+ import"./cli-nr6j6300.js";
14
14
  import"./cli-h3phdstf.js";
15
15
  import"./cli-37x76zdn.js";
16
16
  export {
@@ -8,7 +8,7 @@ import {
8
8
  loadPlugins,
9
9
  loadPluginsWithCss,
10
10
  loadPublishFunction
11
- } from "./index-4v7e621e.js";
11
+ } from "./index-2jpk25en.js";
12
12
  import"./index-r7jcsk6h.js";
13
13
  import"./index-37x76zdn.js";
14
14
  export {
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  executeValidation,
3
3
  reportMissingTools
4
- } from "./cli-ke86zsz6.js";
5
- import"./cli-1hxxwbaq.js";
4
+ } from "./cli-5hkmp2wy.js";
5
+ import"./cli-ccp7sxz3.js";
6
6
  import"./cli-ht1bcqdd.js";
7
- import"./cli-8kg0wjdt.js";
7
+ import"./cli-dtckwda1.js";
8
8
  import"./cli-2ppw6q9v.js";
9
9
  import"./cli-eyyggect.js";
10
10
  import"./cli-asap4t1z.js";
@@ -12,18 +12,18 @@ import"./cli-habkc5ng.js";
12
12
  import"./cli-v5mp7a6q.js";
13
13
  import {
14
14
  publishTargetFor
15
- } from "./cli-zj5xaepv.js";
16
- import"./cli-ntwqmdnw.js";
15
+ } from "./cli-dd115fa8.js";
16
+ import"./cli-h17e3mtz.js";
17
17
  import {
18
18
  log
19
- } from "./cli-kx4hbnx2.js";
19
+ } from "./cli-b0vyjvmt.js";
20
20
  import {
21
21
  UsageError,
22
22
  rejectExtraPositionals,
23
23
  rejectUnknownFlags
24
24
  } from "./cli-d8xjns66.js";
25
- import"./cli-cjftezxr.js";
26
- import"./cli-1ngqzg8s.js";
25
+ import"./cli-nr6j6300.js";
26
+ import"./cli-fe43evmf.js";
27
27
  import {
28
28
  BuildError,
29
29
  EXIT_CODES
@@ -41,6 +41,7 @@ export declare function injectPreviewScripts(html: string): string;
41
41
  */
42
42
  export declare function generateAndWriteHtml(inputPath: string, tempDir: string, config: {
43
43
  title?: string;
44
+ language?: string;
44
45
  styles?: string[];
45
46
  source?: {
46
47
  files?: string[] | null;
@@ -3,23 +3,23 @@ import {
3
3
  runBuild,
4
4
  splitOutPath,
5
5
  startPreviewServer
6
- } from "./cli-ke86zsz6.js";
7
- import"./cli-1hxxwbaq.js";
6
+ } from "./cli-5hkmp2wy.js";
7
+ import"./cli-ccp7sxz3.js";
8
8
  import {
9
9
  previewArgs
10
10
  } from "./cli-75qkn0k5.js";
11
11
  import"./cli-ht1bcqdd.js";
12
- import"./cli-8kg0wjdt.js";
12
+ import"./cli-dtckwda1.js";
13
13
  import"./cli-2ppw6q9v.js";
14
14
  import"./cli-eyyggect.js";
15
15
  import"./cli-asap4t1z.js";
16
16
  import"./cli-habkc5ng.js";
17
17
  import"./cli-v5mp7a6q.js";
18
- import"./cli-zj5xaepv.js";
19
- import"./cli-ntwqmdnw.js";
18
+ import"./cli-dd115fa8.js";
19
+ import"./cli-h17e3mtz.js";
20
20
  import {
21
21
  log
22
- } from "./cli-kx4hbnx2.js";
22
+ } from "./cli-b0vyjvmt.js";
23
23
  import {
24
24
  UsageError,
25
25
  parseFormat,
@@ -28,8 +28,8 @@ import {
28
28
  rejectUnknownFlags,
29
29
  resolvePort
30
30
  } from "./cli-d8xjns66.js";
31
- import"./cli-cjftezxr.js";
32
- import"./cli-1ngqzg8s.js";
31
+ import"./cli-nr6j6300.js";
32
+ import"./cli-fe43evmf.js";
33
33
  import {
34
34
  BuildError
35
35
  } from "./cli-46ycxe6r.js";
@@ -10,12 +10,12 @@ import {
10
10
  resolvePublishFormat,
11
11
  resolvePublishProvider,
12
12
  runPublish
13
- } from "./cli-ke86zsz6.js";
13
+ } from "./cli-5hkmp2wy.js";
14
14
  import {
15
15
  FileTokenStore
16
- } from "./cli-1hxxwbaq.js";
16
+ } from "./cli-ccp7sxz3.js";
17
17
  import"./cli-ht1bcqdd.js";
18
- import"./cli-8kg0wjdt.js";
18
+ import"./cli-dtckwda1.js";
19
19
  import"./cli-2ppw6q9v.js";
20
20
  import"./cli-eyyggect.js";
21
21
  import"./cli-asap4t1z.js";
@@ -23,18 +23,18 @@ import"./cli-habkc5ng.js";
23
23
  import"./cli-v5mp7a6q.js";
24
24
  import {
25
25
  loadManifestWithPath
26
- } from "./cli-zj5xaepv.js";
27
- import"./cli-ntwqmdnw.js";
26
+ } from "./cli-dd115fa8.js";
27
+ import"./cli-h17e3mtz.js";
28
28
  import {
29
29
  log
30
- } from "./cli-kx4hbnx2.js";
30
+ } from "./cli-b0vyjvmt.js";
31
31
  import {
32
32
  UsageError,
33
33
  rejectExtraPositionals,
34
34
  rejectUnknownFlags
35
35
  } from "./cli-d8xjns66.js";
36
- import"./cli-cjftezxr.js";
37
- import"./cli-1ngqzg8s.js";
36
+ import"./cli-nr6j6300.js";
37
+ import"./cli-fe43evmf.js";
38
38
  import {
39
39
  BuildError,
40
40
  EXIT_CODES