gutterpress 0.10.6 → 0.10.7

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 (42) hide show
  1. package/README.md +71 -3
  2. package/dist/api/index.d.ts +1 -1
  3. package/dist/api/index.js +4 -2
  4. package/dist/{audit-0a4sp2w5.js → audit-19e2431j.js} +4 -4
  5. package/dist/{build-z53bbxtx.js → build-gqqew2vr.js} +4 -4
  6. package/dist/checks/source/css-ownership.d.ts +43 -0
  7. package/dist/checks/source/index.d.ts +1 -0
  8. package/dist/{cli-he23vgjj.js → cli-h8ejbtyr.js} +15 -2
  9. package/dist/{cli-kwt5ftvc.js → cli-kzf64c2g.js} +4991 -3933
  10. package/dist/{cli-m99hj80k.js → cli-pvk2s9d7.js} +1 -1
  11. package/dist/{cli-bwbs3qm4.js → cli-s8q42x3r.js} +1 -1
  12. package/dist/cli.js +70 -37
  13. package/dist/{doctor-d8vpv1pt.js → doctor-3ccz35yz.js} +2 -2
  14. package/dist/{engine-4ctgbwj5.js → engine-88mynx9a.js} +2 -2
  15. package/dist/{engine-bhhcne06.js → engine-pb6h089e.js} +1 -1
  16. package/dist/{index-k5hcp8wj.js → index-837htyjy.js} +1 -1
  17. package/dist/{index-eyzv51dw.js → index-a0xpm188.js} +1 -1
  18. package/dist/{index-1rcfsx67.js → index-r4ny4fyr.js} +2586 -2073
  19. package/dist/index.js +5 -3
  20. package/dist/lib/cli-args.d.ts +9 -0
  21. package/dist/lib/manifest-doc.d.ts +8 -2
  22. package/dist/lib/markdown/assemble.d.ts +24 -7
  23. package/dist/lib/markdown/gfm-alerts.d.ts +7 -0
  24. package/dist/lib/markdown/gutterpress-css.d.ts +1 -1
  25. package/dist/lib/markdown/index.d.ts +16 -0
  26. package/dist/lib/markdown/plugins.d.ts +8 -12
  27. package/dist/lib/markdown/renderer.d.ts +48 -1
  28. package/dist/lib/style-declarations.d.ts +20 -0
  29. package/dist/lib/style-resolver.d.ts +2 -0
  30. package/dist/lib/theme-manager.d.ts +110 -9
  31. package/dist/{lint-nm57gn26.js → lint-mpz7vfc8.js} +4 -4
  32. package/dist/{manifest.schema-hn6ac0ae.json → manifest.schema-kwnrwv8b.json} +11 -8
  33. package/dist/{new-sawc7zh4.js → new-nbrpsw7t.js} +4 -4
  34. package/dist/{plugin-p8yghztz.js → plugin-gg5mk26h.js} +5 -11
  35. package/dist/{preflight-48kwf2pm.js → preflight-jnmd4mg2.js} +4 -4
  36. package/dist/{preview-10r4dk3d.js → preview-hsr9d12d.js} +4 -4
  37. package/dist/{publish-cd05sbdx.js → publish-pcrrn6fd.js} +4 -4
  38. package/dist/render.js +155 -52
  39. package/dist/schema/manifest.types.d.ts +20 -7
  40. package/dist/theme-6fa0yjzq.js +323 -0
  41. package/dist/{validate-70ta3br9.js → validate-7n25fx0g.js} +4 -4
  42. package/package.json +1 -1
@@ -1,14 +1,15 @@
1
1
  import {
2
2
  addNpmPlugin,
3
3
  parseNpmPluginSpec
4
- } from "./cli-kwt5ftvc.js";
5
- import"./cli-m99hj80k.js";
4
+ } from "./cli-kzf64c2g.js";
5
+ import"./cli-pvk2s9d7.js";
6
6
  import {
7
7
  UsageError,
8
+ exitForUsage,
8
9
  rejectExtraPositionals,
9
10
  rejectUnknownFlags
10
- } from "./cli-he23vgjj.js";
11
- import"./cli-bwbs3qm4.js";
11
+ } from "./cli-h8ejbtyr.js";
12
+ import"./cli-s8q42x3r.js";
12
13
  import"./cli-c41yr7he.js";
13
14
  import {
14
15
  EXIT_CODES
@@ -39,13 +40,6 @@ var pluginAddArgs = {
39
40
  }
40
41
  };
41
42
  var parentArgs = {};
42
- function exitForUsage(error) {
43
- if (error instanceof UsageError) {
44
- console.error(error.message);
45
- process.exit(error.exitCode);
46
- }
47
- throw error;
48
- }
49
43
  function rejectParentFlags(rawArgs) {
50
44
  if (rawArgs[0] === "add" || rawArgs[0] === undefined || !rawArgs[0].startsWith("-")) {
51
45
  return;
@@ -2,16 +2,16 @@ import {
2
2
  executeValidation,
3
3
  publishTargetFor,
4
4
  reportMissingTools
5
- } from "./cli-kwt5ftvc.js";
5
+ } from "./cli-kzf64c2g.js";
6
6
  import {
7
7
  log
8
- } from "./cli-m99hj80k.js";
8
+ } from "./cli-pvk2s9d7.js";
9
9
  import {
10
10
  UsageError,
11
11
  rejectExtraPositionals,
12
12
  rejectUnknownFlags
13
- } from "./cli-he23vgjj.js";
14
- import"./cli-bwbs3qm4.js";
13
+ } from "./cli-h8ejbtyr.js";
14
+ import"./cli-s8q42x3r.js";
15
15
  import"./cli-c41yr7he.js";
16
16
  import {
17
17
  EXIT_CODES
@@ -6,10 +6,10 @@ import {
6
6
  runBuild,
7
7
  splitOutPath,
8
8
  startPreviewServer
9
- } from "./cli-kwt5ftvc.js";
9
+ } from "./cli-kzf64c2g.js";
10
10
  import {
11
11
  log
12
- } from "./cli-m99hj80k.js";
12
+ } from "./cli-pvk2s9d7.js";
13
13
  import {
14
14
  UsageError,
15
15
  parseEngine,
@@ -18,8 +18,8 @@ import {
18
18
  rejectExtraPositionals,
19
19
  rejectUnknownFlags,
20
20
  resolvePort
21
- } from "./cli-he23vgjj.js";
22
- import"./cli-bwbs3qm4.js";
21
+ } from "./cli-h8ejbtyr.js";
22
+ import"./cli-s8q42x3r.js";
23
23
  import"./cli-c41yr7he.js";
24
24
  import {
25
25
  BuildError
@@ -11,17 +11,17 @@ import {
11
11
  publishProviderFor,
12
12
  resolvePublishFormat,
13
13
  runPublish
14
- } from "./cli-kwt5ftvc.js";
14
+ } from "./cli-kzf64c2g.js";
15
15
  import {
16
16
  FileTokenStore,
17
17
  log
18
- } from "./cli-m99hj80k.js";
18
+ } from "./cli-pvk2s9d7.js";
19
19
  import {
20
20
  UsageError,
21
21
  rejectExtraPositionals,
22
22
  rejectUnknownFlags
23
- } from "./cli-he23vgjj.js";
24
- import"./cli-bwbs3qm4.js";
23
+ } from "./cli-h8ejbtyr.js";
24
+ import"./cli-s8q42x3r.js";
25
25
  import"./cli-c41yr7he.js";
26
26
  import {
27
27
  EXIT_CODES
package/dist/render.js CHANGED
@@ -280,7 +280,6 @@ function plugin(md, pluginOptions = {}) {
280
280
  return;
281
281
  scanForMistypedMarkers(state);
282
282
  const out = [];
283
- setDepth(state.env, 0);
284
283
  const stack = {
285
284
  frames: [],
286
285
  get(kind) {
@@ -375,7 +374,7 @@ function plugin(md, pluginOptions = {}) {
375
374
  const t = new state.Token("layout_section_open", "div", 1);
376
375
  addClasses(t, "section", meta.attrs && meta.attrs.class ? meta.attrs.class : "");
377
376
  attachDataAttrs(t, "section", meta.name, meta.attrs || {});
378
- t.meta = { hasColumnBreak: false, line: meta.__line };
377
+ t.meta = { line: meta.__line };
379
378
  stack.open({
380
379
  kind: "section",
381
380
  classes: meta.attrs && meta.attrs.class || "",
@@ -487,13 +486,6 @@ function plugin(md, pluginOptions = {}) {
487
486
  }
488
487
  if (kind === "column-break") {
489
488
  warnBreakInsideGrid("column-break", line, meta);
490
- const openSectionFrame = stack.get("section");
491
- if (openSectionFrame && openSectionFrame.openToken && !openSectionFrame.openToken.meta.hasColumnBreak) {
492
- const sectionCls = openSectionFrame.openToken.attrGet("class") || "";
493
- if (sectionCls.includes("col-split")) {
494
- openSectionFrame.openToken.meta.hasColumnBreak = true;
495
- }
496
- }
497
489
  const t = new state.Token("layout_column_break", "div", 0);
498
490
  t.meta = { line };
499
491
  t.attrSet("class", "gp-column-break");
@@ -514,43 +506,6 @@ function plugin(md, pluginOptions = {}) {
514
506
  stack.closeAll();
515
507
  state.tokens = out;
516
508
  });
517
- function getDepth(env) {
518
- return env && env.__colSplitDepth || 0;
519
- }
520
- function setDepth(env, n) {
521
- if (env)
522
- env.__colSplitDepth = n;
523
- }
524
- md.renderer.rules.layout_chapter_open = (tokens, idx, opts, env, self) => {
525
- setDepth(env, 0);
526
- return self.renderToken(tokens, idx, opts);
527
- };
528
- md.renderer.rules.layout_page_open = (tokens, idx, opts, env, self) => {
529
- setDepth(env, 0);
530
- return self.renderToken(tokens, idx, opts);
531
- };
532
- md.renderer.rules.layout_section_open = (tokens, idx, opts, env, self) => {
533
- const token = tokens[idx];
534
- const cls = token.attrGet("class") || "";
535
- if (cls.includes("col-split") && token.meta && token.meta.hasColumnBreak) {
536
- setDepth(env, getDepth(env) + 1);
537
- const rangeAttr = token.attrGet("data-source-range");
538
- const rangeHtml = rangeAttr ? ` data-source-range="${escapeAttr(rangeAttr)}"` : "";
539
- const chapterAttr = token.attrGet("data-chapter-src");
540
- const chapterHtml = chapterAttr ? ` data-chapter-src="${escapeAttr(chapterAttr)}"` : "";
541
- return `<div class="${escapeAttr(cls)}"${rangeHtml}${chapterHtml}><div class="col">
542
- `;
543
- }
544
- return self.renderToken(tokens, idx, opts);
545
- };
546
- md.renderer.rules.layout_section_close = (tokens, idx, opts, env, self) => {
547
- if (getDepth(env) > 0) {
548
- setDepth(env, getDepth(env) - 1);
549
- return `</div></div>
550
- `;
551
- }
552
- return self.renderToken(tokens, idx, opts);
553
- };
554
509
  md.renderer.rules.layout_page_break = (tokens, idx) => {
555
510
  const cls = tokens[idx].attrGet("class") || "gp-page-break";
556
511
  const rangeAttr = tokens[idx].attrGet("data-source-range");
@@ -560,11 +515,7 @@ function plugin(md, pluginOptions = {}) {
560
515
  return `<div class="${escapeAttr(cls)}" aria-hidden="true"${rangeHtml}${chapterHtml}></div>
561
516
  `;
562
517
  };
563
- md.renderer.rules.layout_column_break = (tokens, idx, opts, env) => {
564
- if (getDepth(env) > 0) {
565
- return `</div><div class="col">
566
- `;
567
- }
518
+ md.renderer.rules.layout_column_break = (tokens, idx) => {
568
519
  const cls = tokens[idx].attrGet("class") || "gp-column-break";
569
520
  const rangeAttr = tokens[idx].attrGet("data-source-range");
570
521
  const rangeHtml = rangeAttr ? ` data-source-range="${escapeAttr(rangeAttr)}"` : "";
@@ -814,6 +765,55 @@ img.gp-shape {
814
765
  .gp-base { z-index: var(--gp-z-base); }
815
766
  .gp-raised { z-index: var(--gp-z-raised); }
816
767
  .gp-front { z-index: var(--gp-z-front); }
768
+
769
+ /* GFM-style alert/callout boxes (#237) — the DOM gfm-alerts.ts emits from
770
+ "> [!TYPE]" blockquote syntax when a project opts into the bundled
771
+ gutterpress-gfm-alerts feature (BUILTIN_OPTIONAL_PLUGINS, renderer.ts).
772
+ The plugin is optional and inert by default; these rules are equally
773
+ inert wherever the classes never appear, exactly like every other
774
+ utility above, so shipping them costs nothing to a book that has not
775
+ turned the feature on.
776
+
777
+ This is authored-COMPONENT vocabulary, not the @marker structural family
778
+ (markers.js/MARKER_CSS) — CLAUDE.md §6 splits the two core CSS blocks by
779
+ ROLE, and an author-facing box an author invokes via inline syntax is the
780
+ same role as every other gp-* utility here, not page/chapter/section DOM.
781
+
782
+ Deliberately minimal (CLAUDE.md "thin over capable" / the issue's own
783
+ "a rule and a label"): a left border plus a bold label, :where()'d to
784
+ zero specificity so a theme's own (unlayered) rule for any of these
785
+ classes wins outright regardless of selector weight — see the
786
+ cascade-layers note atop this file. Five semantic accent colors (the same
787
+ NOTE/TIP/IMPORTANT/WARNING/CAUTION hues GitHub's own alerts use — a
788
+ long-established, non-proprietary convention, not DC branding) are
789
+ exposed as :root custom properties, the same author-settable-surface
790
+ pattern as the --gp-z-* ladder above: a book retints every alert of one
791
+ type by overriding a single property, no specificity fight required.
792
+ --gp-alert-color cascades from the type modifier down to .gp-alert-title
793
+ through ordinary CSS inheritance (the title is always a DOM descendant of
794
+ the box), so it is set in exactly five places, not ten. */
795
+ :root {
796
+ --gp-alert-note-color: #0969da;
797
+ --gp-alert-tip-color: #1a7f37;
798
+ --gp-alert-important-color: #8250df;
799
+ --gp-alert-warning-color: #9a6700;
800
+ --gp-alert-caution-color: #cf222e;
801
+ }
802
+ :where(.gp-alert) {
803
+ margin: 1em 0;
804
+ padding: 0.5em 1em;
805
+ border-left: 0.25em solid var(--gp-alert-color, currentColor);
806
+ }
807
+ :where(.gp-alert-title) {
808
+ margin: 0 0 0.5em;
809
+ font-weight: bold;
810
+ color: var(--gp-alert-color, inherit);
811
+ }
812
+ :where(.gp-alert-note) { --gp-alert-color: var(--gp-alert-note-color); }
813
+ :where(.gp-alert-tip) { --gp-alert-color: var(--gp-alert-tip-color); }
814
+ :where(.gp-alert-important) { --gp-alert-color: var(--gp-alert-important-color); }
815
+ :where(.gp-alert-warning) { --gp-alert-color: var(--gp-alert-warning-color); }
816
+ :where(.gp-alert-caution) { --gp-alert-color: var(--gp-alert-caution-color); }
817
817
  `;
818
818
  var GP_CLASSES = new Set([
819
819
  "gp-left",
@@ -843,6 +843,13 @@ var GP_CLASSES = new Set([
843
843
  "gp-base",
844
844
  "gp-raised",
845
845
  "gp-front",
846
+ "gp-alert",
847
+ "gp-alert-title",
848
+ "gp-alert-note",
849
+ "gp-alert-tip",
850
+ "gp-alert-important",
851
+ "gp-alert-warning",
852
+ "gp-alert-caution",
846
853
  "gp-page-break",
847
854
  "gp-column-break",
848
855
  "gp-continued",
@@ -1002,6 +1009,101 @@ import markdownItSub from "markdown-it-sub";
1002
1009
  import markdownItSup from "markdown-it-sup";
1003
1010
  import markdownItAbbr from "markdown-it-abbr";
1004
1011
 
1012
+ // src/lib/markdown/gfm-alerts.ts
1013
+ var ALERT_LABELS = {
1014
+ note: "Note",
1015
+ tip: "Tip",
1016
+ important: "Important",
1017
+ warning: "Warning",
1018
+ caution: "Caution"
1019
+ };
1020
+ var ALERT_MARKER = /^\[!(note|tip|important|warning|caution)\]\s*$/i;
1021
+ function isFiniteMap(map) {
1022
+ return Array.isArray(map) && map.length === 2 && Number.isFinite(map[0]) && Number.isFinite(map[1]);
1023
+ }
1024
+ function blockToken(state, type, tag, nesting) {
1025
+ const token = new state.Token(type, tag, nesting);
1026
+ token.block = true;
1027
+ return token;
1028
+ }
1029
+ var gfmAlertsRule = (state) => {
1030
+ const tokens = state.tokens;
1031
+ const newTokens = [];
1032
+ for (let i = 0;i < tokens.length; i++) {
1033
+ const tok = tokens[i];
1034
+ if (tok.type !== "blockquote_open") {
1035
+ newTokens.push(tok);
1036
+ continue;
1037
+ }
1038
+ let depth = 0;
1039
+ let closeIdx = -1;
1040
+ for (let j = i;j < tokens.length; j++) {
1041
+ const jt = tokens[j];
1042
+ if (jt.type === "blockquote_open")
1043
+ depth++;
1044
+ else if (jt.type === "blockquote_close") {
1045
+ depth--;
1046
+ if (depth === 0) {
1047
+ closeIdx = j;
1048
+ break;
1049
+ }
1050
+ }
1051
+ }
1052
+ const markerParaOpen = tokens[i + 1];
1053
+ const markerInline = tokens[i + 2];
1054
+ const markerParaClose = tokens[i + 3];
1055
+ const hasMarkerParagraph = closeIdx !== -1 && markerParaOpen?.type === "paragraph_open" && markerInline?.type === "inline" && markerParaClose?.type === "paragraph_close";
1056
+ if (!hasMarkerParagraph) {
1057
+ newTokens.push(tok);
1058
+ continue;
1059
+ }
1060
+ const content = markerInline.content;
1061
+ const newlineIdx = content.indexOf(`
1062
+ `);
1063
+ const firstLine = newlineIdx === -1 ? content : content.slice(0, newlineIdx);
1064
+ const match = ALERT_MARKER.exec(firstLine);
1065
+ if (!match) {
1066
+ newTokens.push(tok);
1067
+ continue;
1068
+ }
1069
+ const type = match[1].toLowerCase();
1070
+ const label = ALERT_LABELS[type];
1071
+ const markerLine = isFiniteMap(tok.map) ? tok.map[0] + 1 : undefined;
1072
+ const alertOpen = blockToken(state, "gp_alert_open", "div", 1);
1073
+ alertOpen.attrSet("class", `gp-alert gp-alert-${type}`);
1074
+ if (markerLine !== undefined)
1075
+ alertOpen.meta = { line: markerLine };
1076
+ newTokens.push(alertOpen);
1077
+ const titleOpen = blockToken(state, "gp_alert_title_open", "p", 1);
1078
+ titleOpen.attrSet("class", "gp-alert-title");
1079
+ if (markerLine !== undefined)
1080
+ titleOpen.meta = { line: markerLine };
1081
+ newTokens.push(titleOpen);
1082
+ const titleInline = new state.Token("inline", "", 0);
1083
+ titleInline.content = label;
1084
+ titleInline.children = [];
1085
+ newTokens.push(titleInline);
1086
+ newTokens.push(blockToken(state, "gp_alert_title_close", "p", -1));
1087
+ const rest = newlineIdx === -1 ? "" : content.slice(newlineIdx + 1);
1088
+ if (rest.trim() !== "") {
1089
+ markerInline.content = rest;
1090
+ if (isFiniteMap(markerParaOpen.map)) {
1091
+ markerParaOpen.map = [markerParaOpen.map[0] + 1, markerParaOpen.map[1]];
1092
+ }
1093
+ newTokens.push(markerParaOpen, markerInline, markerParaClose);
1094
+ }
1095
+ for (let j = i + 4;j < closeIdx; j++) {
1096
+ newTokens.push(tokens[j]);
1097
+ }
1098
+ newTokens.push(blockToken(state, "gp_alert_close", "div", -1));
1099
+ i = closeIdx;
1100
+ }
1101
+ state.tokens = newTokens;
1102
+ };
1103
+ function gfmAlerts(md) {
1104
+ md.core.ruler.after("block", "gp_gfm_alerts", gfmAlertsRule);
1105
+ }
1106
+
1005
1107
  // src/lib/markdown/images.ts
1006
1108
  var isSrcsetSpace = (char) => char === "\t" || char === `
1007
1109
  ` || char === "\f" || char === "\r" || char === " ";
@@ -1423,7 +1525,8 @@ var BUILTIN_OPTIONAL_PLUGINS = {
1423
1525
  "markdown-it-mark": unwrapPlugin(markdownItMark),
1424
1526
  "markdown-it-sub": unwrapPlugin(markdownItSub),
1425
1527
  "markdown-it-sup": unwrapPlugin(markdownItSup),
1426
- "markdown-it-abbr": unwrapPlugin(markdownItAbbr)
1528
+ "markdown-it-abbr": unwrapPlugin(markdownItAbbr),
1529
+ "gutterpress-gfm-alerts": gfmAlerts
1427
1530
  };
1428
1531
  function createMarkdownRenderer(customPlugins) {
1429
1532
  const md = new MarkdownIt({
@@ -6,6 +6,13 @@ export interface PluginConfig {
6
6
  version?: string;
7
7
  /** Named module export to use when the package has no default plugin export. */
8
8
  export?: string;
9
+ /**
10
+ * Load order (default 100). Higher loads first; built-in plugins always
11
+ * load before any user plugin regardless of this value. Advanced/rarely
12
+ * needed: a plugin needs the LOWER of two priorities to see tokens the
13
+ * other one produces, since it must load (and run) after it — see
14
+ * examples/gutterpress-user-guide/05-plugins.md#plugin-load-order.
15
+ */
9
16
  priority?: number;
10
17
  options?: Record<string, unknown>;
11
18
  /**
@@ -95,15 +102,13 @@ export interface GutterpressManifest {
95
102
  engine?: "paged" | "native";
96
103
  /**
97
104
  * Engine-conditional stylesheets, appended AFTER `styles`. `.native` is the
98
- * only list that still applies; `.paged` is accepted-but-ignored (a warning
99
- * fires if it has entries).
105
+ * only list this type declares; a manifest that still carries `.paged`
106
+ * (dual-engine era, pre-0.10.7) keeps parsing — `resolveConfig` reads it
107
+ * through a widened cast and warns once — but it is no longer part of the
108
+ * authored/autocompleted shape (see `manifest.schema.json`, which drops it
109
+ * too), since the native engine is the only engine.
100
110
  */
101
111
  engineStyles?: {
102
- /**
103
- * Accepted for backward-compatible manifest parsing only; entries here are
104
- * ignored with a warning.
105
- */
106
- paged?: string[];
107
112
  native?: string[];
108
113
  };
109
114
  /**
@@ -156,6 +161,13 @@ export interface GutterpressManifest {
156
161
  markdownlint?: string | false;
157
162
  htmlhint?: string | false;
158
163
  stylelint?: string | false;
164
+ /**
165
+ * Path to a CSS ownership contract (#232), or `false` to disable
166
+ * `source.css-ownership`. Optional — absent means no contract, so the
167
+ * check reports nothing. Default discovery: `.gutterpress/css-contract.yaml`
168
+ * (or `.yml`) in the project root. See docs/css-ownership-contract.md.
169
+ */
170
+ cssOwnership?: string | false;
159
171
  /**
160
172
  * @deprecated `:::name` container syntax was removed 2026-05-17 and the
161
173
  * callout-validation check it gated was removed at the same time. This
@@ -259,6 +271,7 @@ export interface ResolvedConfig {
259
271
  markdownlint: string | false | null;
260
272
  htmlhint: string | false | null;
261
273
  stylelint: string | false | null;
274
+ cssOwnership: string | false | null;
262
275
  };
263
276
  assets: {
264
277
  maxImageSize: number;