gp-dimm-city 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,82 @@ cut a version that has no `## [X.Y.Z]` heading below.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.10.0] - 2026-10-31
11
+
12
+ ### Changed
13
+
14
+ - **Needs Gutterpress 0.11.22 or later** (was 0.11.20-alpha.3), in
15
+ `engines.gutterpress` and the README. With `size` gone from
16
+ `styles/page-rules.css` (below), the page size comes from the book's
17
+ manifest, which Gutterpress writes from 0.11.22 on. On 0.11.21 a book would
18
+ fall back to Letter (the design guide's full-bleed plates fail its build);
19
+ now Gutterpress refuses to load the extension there and says to update.
20
+ Margins set in Book setup > Book > Margins (`page.margin`, new in
21
+ Gutterpress 0.11.23) leave the full-bleed plates and named pages theirs
22
+ from 0.11.23-beta.1 (dimm-city/gutterpress#454).
23
+ - **Full-page art plates are the sheet the book prints.** `.dc-specialty-art`
24
+ and `.dc-full-page` / `@page full-bleed` take their size from
25
+ `--gp-page-width` / `--gp-page-height` (Gutterpress 0.11.23-beta.1 on), so
26
+ they follow the book's page size instead of a copy of the `dtrpg` sheet; on
27
+ 0.11.22 they fall back to that sheet. At that sheet the PDF is identical.
28
+ - **Tests and the guide build run on Gutterpress 0.11.23.** The dev
29
+ dependency moves from 0.11.20-alpha.3, three releases behind, to the 0.11.23
30
+ line the 1.10 release targets. The guide is 172 pages, four fewer than on
31
+ 0.11.23-alpha.6: Gutterpress now keeps the guide's own `break-before: auto`
32
+ on specialty specimens in doc pages (dimm-city/gutterpress#476), so the
33
+ blank pages it put before them are gone; the Part 2 example still opens its
34
+ spread on a left page. The design
35
+ guide's manifest uses 0.11.23's flat shape: `chapters:` (was
36
+ `source.files`), no `page.tolerance` (the preset's), and no
37
+ `validate.source.markdownlint` (the repo's `.markdownlint.jsonc` is found by
38
+ walking up to the repository root).
39
+ - **Docs follow Gutterpress 0.11.23.** Vendored copies live under
40
+ `extensions/npm/` (a book vendored earlier keeps `plugins/npm/` until its
41
+ next `ext add` or `ext update`), the app adds an npm extension on Book
42
+ setup's Extensions tab, and the README and `package.json` give the same minimum
43
+ Gutterpress version. The README's `ext list` sample shows
44
+ 1.9.0 with the badges in the order Gutterpress prints them.
45
+ - **The design guide and the snippets type the plain spellings** (`---`, `--`,
46
+ `...`, `-`, `'`). `source.characters` findings on the guide drop from 367 to
47
+ 0 (its six minus signs are typed on purpose, and Gutterpress no longer
48
+ flags them from 0.11.23-beta.1), and the five snippets paste into a book
49
+ with none.
50
+
51
+ ### Removed
52
+
53
+ - **`size` from `styles/page-rules.css`'s `@page`.** Gutterpress 0.11.23
54
+ writes the manifest's page size after every stylesheet and reports a `size`
55
+ in a look as `printsafe/page-size-in-stylesheet` on every build. The sheet is
56
+ the same 8.625 × 11.25 in.
57
+ - **`--page-width` and `--page-height` from `dc-palette.css`.** They copied
58
+ the `dtrpg` sheet and did not follow a book's page size; read
59
+ `--gp-page-width` / `--gp-page-height` instead.
60
+ - **`gutterpress.tokensFile` from `package.json`.** Gutterpress 0.11.23 reads
61
+ every enabled look's `:root` tokens and no longer reads the field.
62
+ - **The `lint` npm script and the `gutterpress lint` row in the design guide.**
63
+ The command does not exist; the print-safety lint runs inside
64
+ `gutterpress build` and `gutterpress validate`, and
65
+ `gutterpress validate <book> --category source` runs the source checks
66
+ alone.
67
+
68
+ ### Fixed
69
+
70
+ - **`@outcome` rolls and ladder bands accept `--`.** `11--19` prints as
71
+ 11–19 and a ladder table typed that way is recognised; the `–` spelling
72
+ still works. The character check asks for `--`, and Gutterpress 0.11.23's
73
+ example books type it.
74
+ - **An alert's first paragraph is typeset like any other.** `---`, `--`, `...`
75
+ and straight quotes inside `> [!NOTE]`, `> [!VIBE]` and the other alert
76
+ blocks printed as typed, and `&mdash;` printed as `<>`, in the paragraph
77
+ that followed the marker; later paragraphs were fine. The marker is now cut
78
+ from the text markdown-it had already typeset rather than re-parsed. The six
79
+ em dashes the guide had to leave typed for this (chapters 5 and 11) type
80
+ `---` now.
81
+ - **The guide keeps its six minus signs.** The `−1` (U+2212) in chapters 6,
82
+ 11, 304 and 306 print as typed; a minus is not a hyphen and is not converted
83
+ to one. The character check flags them until Gutterpress 0.11.23 stops
84
+ asking for `-`.
85
+
10
86
  ## [1.9.0] - 2026-10-26
11
87
 
12
88
  ### Changed
package/README.md CHANGED
@@ -8,10 +8,11 @@ the package renders them and prints them.
8
8
 
9
9
  ## Install
10
10
 
11
- **In the Gutterpress app:** open the book, then Project settings → Features →
12
- *Find more on npm* (search "dimm city") or *Install from npm* and enter
13
- `gp-dimm-city`. Confirm the install prompt. The package appears under Features
14
- with a "+ look" badge, and its sheets and snippets are live in the preview.
11
+ **In the Gutterpress app:** open the book, then Book setup → Extensions, search
12
+ npm for `gp-dimm-city` and click Add (or add it by exact name under *Other ways
13
+ to add*). The package joins Your extensions, and its sheets and snippets are
14
+ live in the preview. The
15
+ [desktop guide](https://github.com/dimm-city/gutterpress/blob/main/docs/desktop-guide/06-plugins.md) has the rest.
15
16
 
16
17
  **From the command line**, in the folder that holds the book:
17
18
 
@@ -20,22 +21,22 @@ gutterpress ext add gp-dimm-city my-book
20
21
  ```
21
22
 
22
23
  Either way Gutterpress downloads the package from npm, verifies the registry
23
- hash, writes a copy under `my-book/plugins/npm/`, and pins the exact version
24
+ hash, writes a copy under `my-book/extensions/npm/`, and pins the exact version
24
25
  in the manifest:
25
26
 
26
27
  ```yaml
27
28
  extensions:
28
- - gp-dimm-city@1.5.0
29
+ - gp-dimm-city@1.9.0
29
30
  ```
30
31
 
31
- **Commit the manifest, not `plugins/npm/`.** The downloaded copy is not part of
32
- the book's history: Gutterpress adds `plugins/npm/` to the book's `.gitignore`.
33
- The pin in the manifest is what you commit. On a fresh clone, the first build,
34
- export or `gutterpress validate` downloads exactly the pinned version through
35
- the same verified path (this needs the network once); after that the book works
36
- offline.
32
+ **Commit the manifest, not `extensions/npm/`.** The downloaded copy is not part
33
+ of the book's history: Gutterpress adds `extensions/npm/` to the book's
34
+ `.gitignore`. The pin in the manifest is what you commit. On a fresh clone, the
35
+ first build, export or `gutterpress validate` downloads exactly the pinned
36
+ version through the same verified path (this needs the network once); after that
37
+ the book works offline.
37
38
 
38
- `gutterpress ext list my-book` shows `gp-dimm-city@1.5.0 [npm; markdown, styles, components, snippets]`.
39
+ `gutterpress ext list my-book` shows `gp-dimm-city@1.9.0 [npm; markdown, styles, snippets, components]`.
39
40
 
40
41
  ## What you get
41
42
 
@@ -114,9 +115,10 @@ Also in the box:
114
115
 
115
116
  ## Quick start
116
117
 
117
- Requires Gutterpress **0.11.0** or later — it wraps every extension's CSS in
118
- its own cascade layer, so a book no longer needs a starter look removed
119
- before this package can win the cascade.
118
+ Requires Gutterpress **0.11.22** or later, the first that prints the book at
119
+ its manifest's page size (this look no longer sets one). Since 0.11.0 it
120
+ wraps every extension's CSS in its own cascade layer, so a book no longer
121
+ needs a starter look removed before this package can win the cascade.
120
122
 
121
123
  ```sh
122
124
  gutterpress new "My Book" --preset dtrpg
@@ -126,8 +128,10 @@ gutterpress preview my-book
126
128
 
127
129
  Then insert the *Toc Page*, *Credits Page* and *Chapter Start Page* snippets
128
130
  to lay down the front matter, and reach for *Skill*, *Learning Path* and
129
- *Specialty* as you write. `page-rules.css` sets a Letter page with bleed; keep
130
- the manifest's `page:` in step with it.
131
+ *Specialty* as you write. The `dtrpg` preset sets the sheet, Letter with
132
+ bleed. The look's page margins are a starting point: from Gutterpress
133
+ 0.11.23, Book setup > Book > Margins (`page.margin` in the manifest) sets
134
+ the book's own, and the full-bleed plates and named pages keep theirs.
131
135
 
132
136
  ## Author overrides
133
137
 
@@ -229,7 +233,11 @@ gutterpress ext add gp-dimm-city@1.5.0 my-book
229
233
  ```
230
234
 
231
235
  Installing a version removes the book's copies of other versions of this
232
- package, so there is nothing to clean up by hand. Commit the manifest change.
236
+ package, so there is nothing to clean up by hand. A book vendored before
237
+ Gutterpress 0.11.23 keeps working from its `plugins/npm/` copy until its next
238
+ `ext add` or `ext update`, which moves the package to `extensions/npm/`, removes
239
+ the old copies and the empty `plugins/` folder, and replaces the `.gitignore`
240
+ line. Commit the manifest change.
233
241
 
234
242
  Rebuild and compare before you commit — the Dimm City books gate every bump
235
243
  with a render-parity run (text runs, image placement, and a pixel comparison
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gp-dimm-city",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Dimm City design system for Gutterpress: the markdown-it macros (@skill, @specialty, @learning-path, @callout, …), print stylesheets, design tokens, fonts and page templates the Dimm City TTRPG books are set in.",
5
5
  "type": "module",
6
6
  "main": "plugin.js",
@@ -55,7 +55,6 @@
55
55
  "styles/page-rules.css",
56
56
  "styles/dc-native.css"
57
57
  ],
58
- "tokensFile": "styles/dc-palette.css",
59
58
  "components": "components.yaml",
60
59
  "snippets": "snippets"
61
60
  },
@@ -63,18 +62,17 @@
63
62
  "dev": "gutterpress preview design-guide --open",
64
63
  "build": "gutterpress build design-guide --format pdf --out .build/design-guide.pdf",
65
64
  "build:html": "gutterpress build design-guide --format html --out .build/design-guide-site",
66
- "lint": "gutterpress lint design-guide",
67
65
  "test": "bun test",
68
66
  "test:update-snapshot": "bun test --update-snapshots",
69
67
  "book-diff": "bun scripts/book-diff.mjs",
70
68
  "pack:check": "npm pack --dry-run"
71
69
  },
72
70
  "devDependencies": {
73
- "gutterpress": "0.11.20-alpha.3",
71
+ "gutterpress": "0.11.23",
74
72
  "markdown-it": "^14.1.0"
75
73
  },
76
74
  "engines": {
77
- "gutterpress": ">=0.11.20-alpha.3"
75
+ "gutterpress": ">=0.11.22"
78
76
  },
79
77
  "peerDependencies": {
80
78
  "markdown-it": ">=14"
package/plugin.js CHANGED
@@ -386,9 +386,9 @@ const OUTCOME_NAMES = { crit: 'Crit', hit: 'Hit', mixed: 'Hard Choice', miss: 'M
386
386
  // Any other set of bands (six bands, three, a split at 10) is NOT a ladder.
387
387
  const LADDER_TIER_OF_BAND = { '20': 'crit', '11-19': 'hit', '6-10': 'mixed', '2-5': 'miss', '1': 'fail' };
388
388
 
389
- // "11 – 19" → [11, 19], "**20**" → [20, 20]; anything that is not a number or a range → null.
389
+ // "11 – 19" → [11, 19], "11--19" → [11, 19], "**20**" → [20, 20]; anything that is not a number or a range → null.
390
390
  function parseBand(cell) {
391
- const text = cell.replace(/[*_`\s]/g, '').replace(/[\u2010-\u2015\u2212]/g, '-');
391
+ const text = cell.replace(/[*_`\s]/g, '').replace(/[\u2010-\u2015\u2212]|-{2,3}/g, '-');
392
392
  const m = /^(\d{1,3})(?:-(\d{1,3}))?$/.exec(text);
393
393
  if (!m) return null;
394
394
  const [lo, hi] = [Number(m[1]), Number(m[2] ?? m[1])];
@@ -423,7 +423,7 @@ function outcomeRowsHtml(rows, md, head = []) {
423
423
  : ' <div class="dc-outcomes-label">Outcomes</div>\n';
424
424
  rows.forEach(({ tier, name, roll, text }) => {
425
425
  html += ' <div class="dc-outcome-row ' + tier + '">\n';
426
- html += ' <span class="dc-outcome-key tier-' + tier + '"><span class="dc-outcome-name">' + esc(name) + '</span><span class="dc-outcome-roll">' + esc(roll) + '</span></span>\n';
426
+ html += ' <span class="dc-outcome-key tier-' + tier + '"><span class="dc-outcome-name">' + esc(name) + '</span><span class="dc-outcome-roll">' + esc(roll.replace(/(\d)\s*--\s*(\d)/g, '$1\u2013$2')) + '</span></span>\n';
427
427
  html += ' <span class="dc-outcome-text">' + md.renderInline(text) + '</span>\n';
428
428
  html += ' </div>\n';
429
429
  });
@@ -663,7 +663,8 @@ function dcAlertsTransform(state) {
663
663
  if (closeIdx === -1 || firstInlineIdx === -1) { newTokens.push(tok); continue; }
664
664
 
665
665
  const inlineTok = tokens[firstInlineIdx];
666
- const match = inlineTok && inlineTok.content.match(DC_ALERT_PATTERN);
666
+ const lead = inlineTok && inlineTok.children && inlineTok.children[0];
667
+ const match = lead && lead.type === 'text' && inlineTok.content.match(DC_ALERT_PATTERN);
667
668
  if (!match) { newTokens.push(tok); continue; }
668
669
 
669
670
  const alertType = match[1].toUpperCase();
@@ -680,7 +681,8 @@ function dcAlertsTransform(state) {
680
681
  newTokens.push(openTok);
681
682
 
682
683
  // Strip [!TYPE] prefix from first inline; suppress its paragraph wrapper if empty.
683
- const prefixMatch = inlineTok.content.match(/^\[![A-Z_]+\][ \t]*/i);
684
+ const markerPrefix = /^\[![A-Z_]+\][ \t]*/i;
685
+ const prefixMatch = inlineTok.content.match(markerPrefix);
684
686
  const stripped = prefixMatch
685
687
  ? inlineTok.content.slice(prefixMatch[0].length).trim()
686
688
  : inlineTok.content;
@@ -697,11 +699,18 @@ function dcAlertsTransform(state) {
697
699
  if (!t) continue;
698
700
  if (j === firstInlineIdx) {
699
701
  if (stripped === '') continue;
700
- const strippedTok = new state.Token('inline', '', 0);
701
- strippedTok.content = stripped;
702
- strippedTok.children = [];
703
- state.md.inline.parse(stripped, state.md, state.env, strippedTok.children);
704
- newTokens.push(strippedTok);
702
+ // The core rules have already typeset the children, so take the marker
703
+ // off the lead text (and the line break it leaves at the front) and
704
+ // keep the rest; re-parsing would skip replacements, smartquotes and
705
+ // text_join.
706
+ lead.content = lead.content.replace(markerPrefix, '');
707
+ if (lead.content === '') {
708
+ inlineTok.children.shift();
709
+ const next = inlineTok.children[0];
710
+ if (next && (next.type === 'softbreak' || next.type === 'hardbreak')) inlineTok.children.shift();
711
+ }
712
+ inlineTok.content = stripped;
713
+ newTokens.push(inlineTok);
705
714
  continue;
706
715
  }
707
716
  if (stripped === '' && wrapsInParagraph) {
@@ -4,9 +4,9 @@
4
4
 
5
5
  > {{flavor}}
6
6
 
7
- {{hp}} HP — {{damage}} Damage
7
+ {{hp}} HP --- {{damage}} Damage
8
8
 
9
- {{role}} — {{size}}
9
+ {{role}} --- {{size}}
10
10
 
11
11
  ##### Traits
12
12
 
@@ -4,8 +4,8 @@
4
4
 
5
5
  > {{flavor}}
6
6
 
7
- {{hp}} HP — {{damage}} Damage
8
- {{role}} — {{size}}
7
+ {{hp}} HP --- {{damage}} Damage
8
+ {{role}} --- {{size}}
9
9
 
10
10
  ##### Traits
11
11
 
@@ -1,9 +1,9 @@
1
1
  @outcome
2
2
 
3
3
  20 | Triumph | {{triumph}}
4
- 11–19 | Success | {{success}}
5
- 6–10 | Hard Choice | {{hard-choice}}
6
- 2–5 | Failure | {{failure}}
4
+ 11--19 | Success | {{success}}
5
+ 6--10 | Hard Choice | {{hard-choice}}
6
+ 2--5 | Failure | {{failure}}
7
7
  1 | Catastrophe | {{catastrophe}}
8
8
 
9
9
  @end-outcome
@@ -10,6 +10,6 @@
10
10
 
11
11
  @toc
12
12
 
13
- 1. [{{chapter}}](#{{id}}) — {{blurb}}
13
+ 1. [{{chapter}}](#{{id}}) --- {{blurb}}
14
14
 
15
15
  @end-toc
package/snippets/toc.md CHANGED
@@ -1,5 +1,5 @@
1
1
  @toc
2
2
 
3
- 1. [{{chapter}}](#{{id}}) — {{blurb}}
3
+ 1. [{{chapter}}](#{{id}}) --- {{blurb}}
4
4
 
5
5
  @end-toc
@@ -430,8 +430,8 @@ hr {
430
430
 
431
431
  /* ── Specialty art full-page plate ─────────────────────────────────────────── */
432
432
  .dc-specialty-art {
433
- width: var(--page-width);
434
- height: var(--page-height);
433
+ width: var(--gp-page-width, 8.625in);
434
+ height: var(--gp-page-height, 11.25in);
435
435
  /* Its break is in page-templates.css (SPECIALTY BREAK CONTROL). */
436
436
  margin: 0;
437
437
  overflow: hidden;
@@ -170,9 +170,10 @@
170
170
  --outer-margin: 0.625in; /* outer (non-binding) page margin */
171
171
  --sidebar-width: 3.05in;
172
172
 
173
- /* ── Page Geometry ────────────────────────────────────────────────────── */
174
- --page-width: 8.625in;
175
- --page-height: 11.25in;
173
+ /* ── Page Geometry ──────────────────────────────────────────────────────
174
+ The sheet is the book's (its manifest's preset or page size); a rule that
175
+ needs its size reads Gutterpress's --gp-page-width / --gp-page-height
176
+ (0.11.23 on), with the dtrpg sheet as the fallback for 0.11.22. */
176
177
  --binding-margin: 0.5in;
177
178
 
178
179
  /* ── Shapes ───────────────────────────────────────────────────────────── */
@@ -49,14 +49,12 @@
49
49
  Default page template — Letter+bleed for DTRPG (8.625 x 11.25)
50
50
  --------------------------------------------------------------------------- */
51
51
 
52
- /* `size` stays a literal on purpose. Chromium resolves var() in @page margins
53
- — measured: `@page :left { margin-left: var(--om) }` put text at exactly
54
- 0.625in on a verso page — so the margins below name their tokens. The trim
55
- SIZE is the one value where a var() that failed to resolve would silently
56
- produce a differently-sized book, and it is duplicated by --page-width /
57
- --page-height in dc-palette.css: change both together. */
52
+ /* The sheet size comes from the book's manifest: Gutterpress 0.11.23 writes
53
+ `@page { size }` after every stylesheet, so a `size` here is ignored and
54
+ reported. A rule that needs the sheet's size reads --gp-page-width /
55
+ --gp-page-height, which Gutterpress publishes from what it prints (0.11.23
56
+ on; the rules fall back to the dtrpg sheet on 0.11.22). */
58
57
  @page {
59
- size: 8.625in 11.25in;
60
58
  margin: var(--page-margin) var(--outer-margin) var(--bottom-margin);
61
59
  background-color: var(--bg);
62
60
  }
@@ -93,10 +93,10 @@
93
93
  /* Empty full-bleed pages need explicit geometry so the engine allocates a page.
94
94
  The height is core's published content box for THIS page's context
95
95
  (core's `gp-page-full-bleed` is margin: 0, so its content box IS the sheet)
96
- rather than `var(--page-height)`: a non-zero margin on that page would
96
+ rather than the sheet's height: a non-zero margin on that page would
97
97
  push the plate past the page box with nothing to catch it. */
98
98
  :is(.dc-full-page, .gp-page-full-bleed) {
99
- width: var(--page-width);
99
+ width: var(--gp-page-width, 8.625in);
100
100
  height: var(--gp-content-h, 11.25in);
101
101
  background-color: var(--bg);
102
102
  break-before: page;