gp-dimm-city 1.1.5 → 1.2.0-alpha.2

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,155 @@ cut a version that has no `## [X.Y.Z]` heading below.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.0] - prerelease
11
+
12
+ Requires Gutterpress 0.11.16-alpha.2 or newer (the desktop app or CLI at
13
+ that version): older versions reject the `section: true` markers below.
14
+ Books pinned to an earlier gp-dimm-city are unaffected until re-pinned.
15
+
16
+ ### Changed
17
+
18
+ - **Eleven plain wrappers are now declared in a `markers` table** instead of
19
+ being hand-written in the plugin's state machine: `@sidebar`,
20
+ `@sidebar-box`, `@definition`, `@specialty-intro`, `@specialty-art`,
21
+ `@specialty-card`, `@gear`, `@toc`, `@lede`, `@glossary` and `@block`.
22
+ Gutterpress core now parses, nests and closes them, and the editor lists
23
+ them. The emitted elements and classes are unchanged; rendering the design
24
+ guide and both books through the old and new plugin gives the same HTML
25
+ apart from the attributes listed below. The state machine loses about 300
26
+ lines.
27
+ - New on these wrappers: `data-source-range`, `data-label="…"` on a labelled
28
+ `@block`, and `data-sidebar="inset"` when written `@sidebar inset`.
29
+ - `@sidebar inset` works beside `@sidebar .inset`. `@block panel|slate|shard|codex`
30
+ works beside `@block .dc-panel` (the `.dc-*` class form is unchanged).
31
+ - `@lede`, `@toc` and `@glossary` now accept extra classes and an `#id`,
32
+ as the other wrappers always did.
33
+ - `@specialty-card`'s `data-position="odd|even"` now comes from a small core
34
+ rule that numbers the cards in document order, as before.
35
+ - Snippets for `@sidebar-box`, `@glossary`, `@toc` and `@specialty-intro`.
36
+ The `@block` snippet uses the variant word.
37
+ - **Nine section-styled components are declared as real core sections**
38
+ (Gutterpress 0.11.16-alpha.2, `section: true`): `@column-panel`, `@tabbed`,
39
+ `@card-grid`, `@citizen-walkthrough`, `@fiction-excerpt`, `@npc-stat`,
40
+ `@flaws`, `@ideals` and `@dreams`, each with an `@end-…` closer and a
41
+ snippet. `@npc-stat` is exactly `@section .dc-npc-stat`: same element, same
42
+ classes, so every book that writes `@section .dc-…` renders as before.
43
+ Modifier classes stay author classes (`@column-panel .gp-columns-2`). No
44
+ variants: none of these is styled per specialty. The design guide uses the
45
+ new markers; `components.yaml` lists them as plugin-origin, and its
46
+ `card-variants` entry now describes `.section.dc-flaws` (the class the CSS
47
+ actually selects).
48
+ - **`@specialty`, `@learning-path` and `@skill` are declared too, then
49
+ transformed** ("declare, then transform"). Core now opens, nests and closes
50
+ them, and two ordinary markdown-it core rules (`dcLearningPaths`,
51
+ `dcSkillCards`, over a small inlined `forEachComponent` helper) rewrite what
52
+ sits inside: the path's title, subtitle and sticker chain, `data-path-ref`,
53
+ and one skill card per `####` heading. The state machine drops from 865 to
54
+ 468 lines (`plugin.js` is 97 lines shorter overall, new rules included):
55
+ its skill, path and specialty flags and branches are gone. `@continue`
56
+ inside a skill's card still opens the
57
+ `{name} ▸` continuation card; the classifier that claims it ahead of core
58
+ now reads core's marker tokens instead of the raw paragraphs.
59
+ - **`@card`, `@outcome` and `@procedure` are declared and transformed the same
60
+ way.** Three more core rules (`dcCards`, `dcOutcomes`, `dcProcedures`) run
61
+ ahead of the skill rules and rewrite what sits inside each. The state
62
+ machine drops from 468 to 122 lines: it now holds only `@callout`,
63
+ `@dm-note`, `@tape`, the retired `@roll-table` / `@options-table` no-ops and
64
+ the image-only paragraph class. `plugin.js` is another 208 lines shorter
65
+ (1,765 to 1,557), new rules, validation and docs comments included. The
66
+ emitted HTML is unchanged: rendering the design guide and both books
67
+ through the old and new plugin gives the same markup apart from the
68
+ attributes and the one page listed below.
69
+ - `@card` is `div.dc-card` with its heading, pull quote, body and footer as
70
+ before, and takes classes, `#id` and `key=value` as before.
71
+ - `@outcome` is `div.dc-outcomes`; `flush` is now a declared variant (it
72
+ adds `.dc-flush` and `data-outcome="flush"`), and `@outcome .dc-flush`
73
+ works too. The compact form (rows with no blank lines) still works.
74
+ - `@procedure` still renders only the `ol.dc-steps`: its declared wrapper is
75
+ hidden, so no `div` appears in the HTML.
76
+ - A `@card`, `@outcome` or `@procedure` inside a skill stays inside the
77
+ skill's card, as before.
78
+ - New on cards and outcomes: `data-source-range`, so the editor can map them.
79
+ - **More structure checks.** An `@outcome` line that is not `roll | name |
80
+ text`, a table, list or heading inside an `@outcome` (they are left out), an
81
+ `@outcome` with no rows, and a `@procedure` with no numbered list are
82
+ reported in plain words, at the line.
83
+ - `@specialty augmerc` is the new spelling for a specialty (names:
84
+ `augmerc`, `proxy`, `streetwarden`, `gutterdruid`, `cybersurgeon`,
85
+ `wirephreak`, `technosorcerer`, `etherlock`, `dualist`, `generalist`, each
86
+ styled by `.dc-specialty.<name>`). `@specialty .augmerc` and
87
+ `@specialty {.augmerc}` work unchanged and give the same element.
88
+ `data-path-ref` reads the enclosing specialty's variant or class, so
89
+ `@specialty .augmerc` still gives `AUG1`.
90
+ - **Structure checks, in plain words.** Skills sitting directly in a
91
+ `@specialty` are reported once per run, at the first one ("This skill (and
92
+ any right after it) is outside a learning path…"), and so
93
+ is a `@learning-path` or `@specialty` that starts inside a skill. The usual
94
+ cause is an `@end-learning-path` above the skills it was meant to close.
95
+ - Snippets: `specialty` shows the whole specialty > learning path > skill
96
+ hierarchy, and `learning-path` shows its skills.
97
+ - devDependency `gutterpress` is `0.11.16-alpha.2`.
98
+ - `docs/plans/specialty-section-variants.md`: the plan for styling section
99
+ components per specialty (`@npc-stat wirephreak`), for a later release.
100
+
101
+ ### Removed
102
+
103
+ - `@block variant=panel`. No book or guide page used it; write `@block panel`.
104
+ - `@end-skills` (no book used it; it is plain text now).
105
+ - `@end-skill` no longer closes the `@learning-path` around the skill. It
106
+ closes the skill, like every declared closer; `@end-learning-path` ends the
107
+ path.
108
+
109
+ ### Behaviour notes
110
+
111
+ - Declared wrappers nest as a stack and close at the next `@page`,
112
+ `@section` or `@chapter`, or at the end of the file. A hand-written marker
113
+ (`@learning-path`, `@callout`, …) no longer closes a wrapper that is still
114
+ open. No book relied on that.
115
+ - A wrapper's `key=value` attribute is always `data-key` now: `lang=en` is
116
+ `data-lang`, where the old code emitted `lang` (likewise `dir`, `role`,
117
+ `tabindex` and `aria-*`).
118
+ - `@end-<wrapper>` with no open wrapper now warns
119
+ (`declared_marker_close_without_open`) instead of being ignored. That now
120
+ includes a stray `@end-skill`, `@end-learning-path` and `@end-specialty`.
121
+ - Specialties, paths and skills close like every declared marker: a new one
122
+ of the same kind closes the previous and what is inside it, `@end-<name>`
123
+ closes it and what is inside it, and `@page`, `@section`, `@chapter` and
124
+ `@continue` close all of them. A `@page` written inside a specialty used to
125
+ leave the specialty open across the page; it now ends the specialty. Use
126
+ `@page-break` to break a page inside one.
127
+ - A `@skill` glued to the line above it (no blank line) is now a marker, as
128
+ every declared marker is; it used to print as the text "@skill".
129
+ - A skill inside a learning path is numbered `PATHREF.N` by its place in the
130
+ path. An `@end-skill` in the middle of a path used to end the path (the
131
+ skills after it lost their tier); the path now stays open.
132
+ - A closer straight under a list item or a quote is a closer now, because core
133
+ reads declared markers there: `@end-procedure` under the last step and
134
+ `@end-card` under the footer quote no longer need a blank line (they used to
135
+ be folded into the item or the quote and never closed).
136
+ - `@procedure` and `@outcome` left open at the end of a file warn with core's
137
+ `declared_marker_eof_close` ("An open @procedure reached end-of-document…"),
138
+ replacing the plugin's `unclosed_procedure`. An unclosed `@outcome` used to
139
+ swallow the rest of the file without a word. `@card` still closes silently.
140
+ - `@end-card`, `@end-outcome` and `@end-procedure` with nothing open now warn
141
+ (`declared_marker_close_without_open`) instead of being ignored.
142
+ - `@callout` and `@dm-note` are still hand-written, so they no longer close a
143
+ `@card` or `@procedure` that is open, and a card or procedure no longer
144
+ closes them. `@callout`, `@procedure`, `@end-procedure`, `@end-callout` is a
145
+ callout with the procedure in it. One book chapter wrote exactly that; it
146
+ used to close the callout at the `@procedure` and leave the steps and the
147
+ closing paragraph outside its box, and now renders them inside it, as
148
+ written.
149
+ - A bare word after `@card`, `@outcome` or `@procedure` is no longer a class
150
+ (core reads it as a variant, and only `flush` is one): write `@card .wide`.
151
+ No book or guide page used one.
152
+ - An `@outcome` shows only the lines of its own paragraphs: a list or heading
153
+ inside it is no longer read as a row (it is reported instead).
154
+ - Core drops a declared marker that is already open and nests the next one, so
155
+ a book that closes a path early (`@end-learning-path` above its skills) now
156
+ has its next `@learning-path` nested in the last skill. The checks above name
157
+ each place; remove the early `@end-learning-path`.
158
+
10
159
  ## [1.1.5] - 2026-10-09
11
160
 
12
161
  ### Changed
package/components.yaml CHANGED
@@ -18,7 +18,7 @@
18
18
  # styles/) · breaks (documentation; omitted where the component has no
19
19
  # fragmentation rule of its own — variants inherit their parent's) · usage
20
20
  # (literal strings that mark a use of a component with no marker or class:
21
- # `ROLL THE DIE!`, `| highlight`, `variant=panel`, …) · unstyled (emitted
21
+ # `ROLL THE DIE!`, `| highlight`, …) · unstyled (emitted
22
22
  # classes acknowledged to have no CSS) · notes.
23
23
  #
24
24
  # Status meanings (measured against the Dimm City books that consume this
@@ -115,7 +115,7 @@ components:
115
115
  - name: sidebar
116
116
  status: live
117
117
  origin: plugin
118
- recipe: "@sidebar [.inset] … @end-sidebar (also authored as raw <div class=\"dc-sidebar\"> around an NPC human-callout)"
118
+ recipe: "@sidebar [inset | .inset] … @end-sidebar (also authored as raw <div class=\"dc-sidebar\"> around an NPC human-callout)"
119
119
  emits: div.dc-sidebar
120
120
  css: [styles/components/images.css, styles/page-templates.css]
121
121
  tokens:
@@ -156,43 +156,44 @@ components:
156
156
  emits: ol.dc-steps > li > span.dc-step-no
157
157
  css: styles/components/data.css
158
158
  breaks: "break-inside: avoid"
159
+ notes: "Declared marker; no element of its own (its wrapper renders nothing), each numbered list inside is the step list. Left open at the end of the file it warns (declared_marker_eof_close); with no numbered list inside it is reported."
159
160
 
160
161
  - name: block
161
162
  status: live
162
163
  origin: plugin
163
- recipe: "@block .dc-panel|.dc-slate|.dc-shard|.dc-codex label=\"Title\" … @end-block"
164
+ recipe: "@block panel|slate|shard|codex label=\"Title\" … @end-block (`.dc-panel` class form works too)"
164
165
  emits: div.dc-block > div.dc-block-title
165
166
  css: styles/components/section.css
166
167
  tokens: [--dc-block-bg, --dc-block-fg, --dc-block-accent, --dc-block-title-bg, --dc-block-title-fg]
167
168
  breaks: "break-inside: avoid"
168
- notes: "The variant class is author-supplied (see block-variants; also accepted as variant=panel). Design guide only."
169
+ notes: "A declared marker: the variant word (or the .dc-* class) selects the treatment (see block-variants). The old variant=panel attribute form was dropped; no book used it. Design guide only."
169
170
 
170
171
  - name: block-variants
171
172
  status: live
172
173
  origin: author
173
- recipe: "@block .dc-panel | .dc-slate | .dc-shard | .dc-codex"
174
+ recipe: "@block panel | slate | shard | codex"
174
175
  emits: div.dc-block.dc-panel|.dc-slate|.dc-shard|.dc-codex
175
176
  css: styles/components/section.css
176
- usage: [variant=panel, variant=slate, variant=shard, variant=codex]
177
- notes: "Four clip-path/surface treatments on .dc-block; the class is passed through from the marker, or built by the plugin from variant=x (a concatenation the check's plugin scan cannot see — hence origin: author and the usage literals)."
177
+ notes: "Four clip-path/surface treatments on .dc-block. The variant word maps to the class in the plugin's `markers` table; `@block .dc-panel` passes the class straight through."
178
178
 
179
179
  - name: card
180
180
  status: live
181
181
  origin: plugin
182
- recipe: "@card [.dc-flaws|.dc-ideals|.dc-dreams] / #### Heading / > pull quote / body / > footer … @end-card"
182
+ recipe: "@card / #### Heading / > pull quote / body / > footer … @end-card"
183
183
  emits:
184
184
  - div.dc-card > div.dc-card-heading + div.dc-card-pull + div.dc-card-body
185
185
  - div.dc-card-body > blockquote.dc-card-footer (the last body blockquote, dc#44)
186
186
  css: styles/components/section.css
187
187
  breaks: "break-inside: avoid"
188
+ notes: "Declared marker, no variants: author classes (@card .dc-flaws), #id and key=value attributes ride on the wrapper. Inside a skill it stays in the skill's card. Left open at the end of the file it closes silently."
188
189
 
189
190
  - name: card-variants
190
191
  status: live
191
- origin: author
192
- recipe: "@card .dc-flaws | .dc-ideals | .dc-dreams"
193
- emits: div.dc-card.dc-flaws|.dc-ideals|.dc-dreams
192
+ origin: plugin
193
+ recipe: "@flaws | @ideals | @dreams … @end-flaws | @end-ideals | @end-dreams (or @section .dc-flaws | .dc-ideals | .dc-dreams)"
194
+ emits: div.section.dc-flaws|.dc-ideals|.dc-dreams
194
195
  css: styles/components/section.css
195
- notes: "Citizen-file card accents; the class is passed through from the marker."
196
+ notes: "Citizen-file card accents: a section of @card blocks whose class colors every card's left rule (`.section.dc-flaws > .dc-card`)."
196
197
 
197
198
  - name: gear
198
199
  status: live
@@ -243,7 +244,7 @@ components:
243
244
  - name: outcome
244
245
  status: live
245
246
  origin: plugin
246
- recipe: "@outcome [flush] / 20 | Crit | text … @end-outcome, or a | Roll | Outcome | table inside a skill card"
247
+ recipe: "@outcome [flush] / 20 | Triumph | text / 11–19 | Success | text / 6–10 | Hard Choice | text / 2–5 | Failure | text / 1 | Catastrophe | text … @end-outcome, or a | Roll | Outcome | table inside a skill card"
247
248
  emits:
248
249
  - div.dc-outcomes[.dc-flush] > div.dc-outcomes-label
249
250
  - div.dc-outcome-row.crit|hit|mixed|miss|fail > span.dc-outcome-key.tier-* > span.dc-outcome-name + span.dc-outcome-roll
@@ -261,11 +262,12 @@ components:
261
262
  - --dc-outcome-name-opacity
262
263
  usage: ["| Roll | Outcome"]
263
264
  breaks: "break-inside: avoid; the skill-table form adds data-break-inside=avoid only when the parent card allows splitting"
265
+ notes: "Declared marker; `flush` is its one variant (also writable .dc-flush). One row per `roll | name | text` line, coloured by position; a sixth row is a plain hit. Left open at the end of the file it warns (declared_marker_eof_close); a line that is not a row, a non-row block, or no rows at all is reported."
264
266
 
265
267
  - name: specialty
266
268
  status: live
267
269
  origin: plugin
268
- recipe: "@specialty .augmerc|.proxy|.streetwarden|.gutterdruid|.cybersurgeon|.wirephreak|.technosorcerer|.etherlock|.dualist|.generalist … @end-specialty"
270
+ recipe: "@specialty augmerc|proxy|streetwarden|gutterdruid|cybersurgeon|wirephreak|technosorcerer|etherlock|dualist|generalist (or .augmerc, {.augmerc}) … @end-specialty"
269
271
  emits: div.dc-specialty.<name>
270
272
  css:
271
273
  - styles/components/specialty-identity.css
@@ -286,7 +288,7 @@ components:
286
288
  - --dc-specialty-intro-title-shape
287
289
  - --dc-specialty-intro-clip
288
290
  breaks: "none on the wrapper; children carry their own"
289
- notes: "Parent-selector model: every card, path and intro inside inherits shape and colour from .dc-specialty.<name>. Auto-closes any open path or skill."
291
+ notes: "Parent-selector model: every card, path and intro inside inherits shape and colour from .dc-specialty.<name>. Declared marker: the word and the .class spelling give the same wrapper. A new @specialty, @end-specialty, @page, @section or @chapter closes it with every path and skill inside; a skill sitting directly in it (outside a learning path) is reported."
290
292
 
291
293
  - name: specialty-intro
292
294
  status: live
@@ -346,11 +348,12 @@ components:
346
348
  - --dc-path-shell-padding-bottom
347
349
  - --dc-arrow-color
348
350
  breaks: "break-before/after: auto on the block; the h3 stays with what follows"
351
+ notes: "Declared marker. data-path-ref is the enclosing specialty's code and the path's number in it (AUG1, PRX2; PATH1 for a specialty with no code). @end-learning-path closes the path and the skills inside it; a new @learning-path closes the previous one."
349
352
 
350
353
  - name: skill
351
354
  status: live
352
355
  origin: plugin
353
- recipe: "@skill [id=…] [{.dc-allow-split}] [{.dc-two-col}] / #### Name | Tier / > flavor / 1. **N AP** *Ability:* text / ##### Sub-header … @end-skill (alias @end-skills)"
356
+ recipe: "@skill [id=…] [{.dc-allow-split}] [{.dc-two-col}] / #### Name | Tier / > flavor / 1. **N AP** *Ability:* text / ##### Sub-header … @end-skill"
354
357
  emits:
355
358
  - div.dc-skill-card[name][data-break-inside=avoid] > div.dc-card-tab > h4.dc-tab-title + span.dc-tab-tier
356
359
  - div.dc-card-body > div.dc-card-inner > p.dc-flavor + div.dc-sub-header
@@ -365,7 +368,7 @@ components:
365
368
  - --dc-card-gap
366
369
  - --dc-card-tab-shadow
367
370
  breaks: "data-break-inside=avoid by default; {.dc-allow-split} opts into splitting (see allow-split); tab break-after: avoid; last two abilities glued"
368
- notes: "A ##### Outcomes sub-header is swallowed (the outcomes block carries its own label)."
371
+ notes: "Declared marker. Every #### heading in one @skill is its own card, with the @skill line's classes and attributes. @end-skill closes only the skill, never the path around it. A ##### Outcomes sub-header is swallowed (the outcomes block carries its own label)."
369
372
 
370
373
  - name: skill-highlight
371
374
  status: live
@@ -427,16 +430,16 @@ components:
427
430
  # ── Author classes on core markers, attrs and raw HTML ────────────────
428
431
  - name: column-panel
429
432
  status: live
430
- origin: author
431
- recipe: "@section .gp-columns-2 .dc-column-panel"
433
+ origin: plugin
434
+ recipe: "@column-panel [.gp-columns-2] … @end-column-panel (or @section .gp-columns-2 .dc-column-panel)"
432
435
  emits: div.section.dc-column-panel
433
436
  css: styles/components/section.css
434
437
  breaks: "first heading glued to its first paragraph"
435
438
 
436
439
  - name: tabbed
437
440
  status: live
438
- origin: author
439
- recipe: "@section .dc-tabbed"
441
+ origin: plugin
442
+ recipe: "@tabbed … @end-tabbed (or @section .dc-tabbed)"
440
443
  emits: div.section.dc-tabbed
441
444
  css: styles/components/section.css
442
445
  tokens: [--dc-section-tab-indent, --dc-section-tab-overlap]
@@ -444,16 +447,16 @@ components:
444
447
 
445
448
  - name: card-grid
446
449
  status: live
447
- origin: author
448
- recipe: "@section .dc-card-grid"
450
+ origin: plugin
451
+ recipe: "@card-grid … @end-card-grid (or @section .dc-card-grid)"
449
452
  emits: div.section.dc-card-grid
450
453
  css: [styles/components/section.css, styles/page-templates.css]
451
454
  breaks: "break-inside: auto on the grid; each specialty card avoid"
452
455
 
453
456
  - name: citizen-walkthrough
454
457
  status: live
455
- origin: author
456
- recipe: "@section .dc-citizen-walkthrough [.gp-columns-2]"
458
+ origin: plugin
459
+ recipe: "@citizen-walkthrough [.gp-columns-2] … @end-citizen-walkthrough (or @section .dc-citizen-walkthrough)"
457
460
  emits: div.section.dc-citizen-walkthrough
458
461
  css: [styles/components/section.css, styles/dc-native.css]
459
462
  notes: "Themed through the section chassis tokens --dc-section-accent / --dc-section-bg (see section), which the design guide documents."
@@ -468,8 +471,8 @@ components:
468
471
 
469
472
  - name: fiction-excerpt
470
473
  status: live
471
- origin: author
472
- recipe: "@section .dc-fiction-excerpt"
474
+ origin: plugin
475
+ recipe: "@fiction-excerpt … @end-fiction-excerpt (or @section .dc-fiction-excerpt)"
473
476
  emits: div.section.dc-fiction-excerpt
474
477
  css: [styles/components/section.css]
475
478
  tokens:
@@ -483,8 +486,8 @@ components:
483
486
 
484
487
  - name: npc-stat
485
488
  status: live
486
- origin: author
487
- recipe: "@section .dc-npc-stat"
489
+ origin: plugin
490
+ recipe: "@npc-stat … @end-npc-stat (or @section .dc-npc-stat)"
488
491
  emits: div.section.dc-npc-stat
489
492
  css: [styles/components/section.css]
490
493
  tokens:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gp-dimm-city",
3
- "version": "1.1.5",
3
+ "version": "1.2.0-alpha.2",
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",
@@ -65,10 +65,11 @@
65
65
  "lint": "gutterpress lint design-guide",
66
66
  "test": "bun test",
67
67
  "test:update-snapshot": "bun test --update-snapshots",
68
+ "book-diff": "bun scripts/book-diff.mjs",
68
69
  "pack:check": "npm pack --dry-run"
69
70
  },
70
71
  "devDependencies": {
71
- "gutterpress": "0.11.15",
72
+ "gutterpress": "0.11.16-alpha.2",
72
73
  "markdown-it": "^14.1.0"
73
74
  },
74
75
  "peerDependencies": {