gp-dimm-city 1.1.4 → 1.2.0-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.
- package/CHANGELOG.md +77 -0
- package/components.yaml +23 -24
- package/package.json +3 -2
- package/plugin.js +96 -347
- package/snippets/block.md +1 -1
- package/snippets/card-grid.md +19 -0
- package/snippets/citizen-walkthrough.md +11 -0
- package/snippets/column-panel.md +11 -0
- package/snippets/dreams.md +15 -0
- package/snippets/fiction-excerpt.md +9 -0
- package/snippets/flaws.md +15 -0
- package/snippets/glossary.md +7 -0
- package/snippets/ideals.md +15 -0
- package/snippets/npc-stat.md +18 -0
- package/snippets/sidebar-box.md +9 -0
- package/snippets/specialty-intro.md +7 -0
- package/snippets/tabbed.md +7 -0
- package/snippets/toc.md +5 -0
- package/styles/components/chrome.css +6 -18
- package/styles/page-templates.css +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,83 @@ 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
|
+
- devDependency `gutterpress` is `0.11.16-alpha.2`.
|
|
49
|
+
- `docs/plans/specialty-section-variants.md`: the plan for styling section
|
|
50
|
+
components per specialty (`@npc-stat wirephreak`), for a later release.
|
|
51
|
+
|
|
52
|
+
### Removed
|
|
53
|
+
|
|
54
|
+
- `@block variant=panel`. No book or guide page used it; write `@block panel`.
|
|
55
|
+
|
|
56
|
+
### Behaviour notes
|
|
57
|
+
|
|
58
|
+
- Declared wrappers nest as a stack and close at the next `@page`,
|
|
59
|
+
`@section` or `@chapter`, or at the end of the file. A hand-written marker
|
|
60
|
+
(`@learning-path`, `@callout`, …) no longer closes a wrapper that is still
|
|
61
|
+
open. No book relied on that.
|
|
62
|
+
- A wrapper's `key=value` attribute is always `data-key` now: `lang=en` is
|
|
63
|
+
`data-lang`, where the old code emitted `lang` (likewise `dir`, `role`,
|
|
64
|
+
`tabindex` and `aria-*`).
|
|
65
|
+
- `@end-<wrapper>` with no open wrapper now warns
|
|
66
|
+
(`declared_marker_close_without_open`) instead of being ignored.
|
|
67
|
+
|
|
68
|
+
## [1.1.5] - 2026-10-09
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- **The chapter-opener chevron rules move from `chrome.css` into the
|
|
73
|
+
chapter-opener template in `page-templates.css`.** No component sheet
|
|
74
|
+
selects `.chapter`/`.page` any more, and the integrity test enforces
|
|
75
|
+
that with no exceptions. Pixel-identical in both books.
|
|
76
|
+
- `docs/cleanup-backlog.md` gains a "Still considering" section:
|
|
77
|
+
- the Field Guide's sidebar areas, with the inset-rail rules;
|
|
78
|
+
- the specialty-card redesign;
|
|
79
|
+
- the configurable chapter prefix;
|
|
80
|
+
- the offset tokens.
|
|
81
|
+
- `docs/css-roadmap.md` records the warnings that are known and accepted:
|
|
82
|
+
- the design guide's 8 heading jumps inside component examples, which
|
|
83
|
+
need an `h4`;
|
|
84
|
+
- the `clip-path` and `page-containment` lint false positives, reported
|
|
85
|
+
as dimm-city/gutterpress#339.
|
|
86
|
+
|
|
10
87
|
## [1.1.4] - 2026-10-09
|
|
11
88
|
|
|
12
89
|
### 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`,
|
|
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:
|
|
@@ -160,26 +160,25 @@ components:
|
|
|
160
160
|
- name: block
|
|
161
161
|
status: live
|
|
162
162
|
origin: plugin
|
|
163
|
-
recipe: "@block
|
|
163
|
+
recipe: "@block panel|slate|shard|codex label=\"Title\" … @end-block (`.dc-panel` class form works too)"
|
|
164
164
|
emits: div.dc-block > div.dc-block-title
|
|
165
165
|
css: styles/components/section.css
|
|
166
166
|
tokens: [--dc-block-bg, --dc-block-fg, --dc-block-accent, --dc-block-title-bg, --dc-block-title-fg]
|
|
167
167
|
breaks: "break-inside: avoid"
|
|
168
|
-
notes: "
|
|
168
|
+
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
169
|
|
|
170
170
|
- name: block-variants
|
|
171
171
|
status: live
|
|
172
172
|
origin: author
|
|
173
|
-
recipe: "@block
|
|
173
|
+
recipe: "@block panel | slate | shard | codex"
|
|
174
174
|
emits: div.dc-block.dc-panel|.dc-slate|.dc-shard|.dc-codex
|
|
175
175
|
css: styles/components/section.css
|
|
176
|
-
|
|
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)."
|
|
176
|
+
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
177
|
|
|
179
178
|
- name: card
|
|
180
179
|
status: live
|
|
181
180
|
origin: plugin
|
|
182
|
-
recipe: "@card
|
|
181
|
+
recipe: "@card / #### Heading / > pull quote / body / > footer … @end-card"
|
|
183
182
|
emits:
|
|
184
183
|
- div.dc-card > div.dc-card-heading + div.dc-card-pull + div.dc-card-body
|
|
185
184
|
- div.dc-card-body > blockquote.dc-card-footer (the last body blockquote, dc#44)
|
|
@@ -188,11 +187,11 @@ components:
|
|
|
188
187
|
|
|
189
188
|
- name: card-variants
|
|
190
189
|
status: live
|
|
191
|
-
origin:
|
|
192
|
-
recipe: "@
|
|
193
|
-
emits: div.
|
|
190
|
+
origin: plugin
|
|
191
|
+
recipe: "@flaws | @ideals | @dreams … @end-flaws | @end-ideals | @end-dreams (or @section .dc-flaws | .dc-ideals | .dc-dreams)"
|
|
192
|
+
emits: div.section.dc-flaws|.dc-ideals|.dc-dreams
|
|
194
193
|
css: styles/components/section.css
|
|
195
|
-
notes: "Citizen-file card accents
|
|
194
|
+
notes: "Citizen-file card accents: a section of @card blocks whose class colors every card's left rule (`.section.dc-flaws > .dc-card`)."
|
|
196
195
|
|
|
197
196
|
- name: gear
|
|
198
197
|
status: live
|
|
@@ -427,16 +426,16 @@ components:
|
|
|
427
426
|
# ── Author classes on core markers, attrs and raw HTML ────────────────
|
|
428
427
|
- name: column-panel
|
|
429
428
|
status: live
|
|
430
|
-
origin:
|
|
431
|
-
recipe: "@section .gp-columns-2 .dc-column-panel"
|
|
429
|
+
origin: plugin
|
|
430
|
+
recipe: "@column-panel [.gp-columns-2] … @end-column-panel (or @section .gp-columns-2 .dc-column-panel)"
|
|
432
431
|
emits: div.section.dc-column-panel
|
|
433
432
|
css: styles/components/section.css
|
|
434
433
|
breaks: "first heading glued to its first paragraph"
|
|
435
434
|
|
|
436
435
|
- name: tabbed
|
|
437
436
|
status: live
|
|
438
|
-
origin:
|
|
439
|
-
recipe: "@section .dc-tabbed"
|
|
437
|
+
origin: plugin
|
|
438
|
+
recipe: "@tabbed … @end-tabbed (or @section .dc-tabbed)"
|
|
440
439
|
emits: div.section.dc-tabbed
|
|
441
440
|
css: styles/components/section.css
|
|
442
441
|
tokens: [--dc-section-tab-indent, --dc-section-tab-overlap]
|
|
@@ -444,16 +443,16 @@ components:
|
|
|
444
443
|
|
|
445
444
|
- name: card-grid
|
|
446
445
|
status: live
|
|
447
|
-
origin:
|
|
448
|
-
recipe: "@section .dc-card-grid"
|
|
446
|
+
origin: plugin
|
|
447
|
+
recipe: "@card-grid … @end-card-grid (or @section .dc-card-grid)"
|
|
449
448
|
emits: div.section.dc-card-grid
|
|
450
449
|
css: [styles/components/section.css, styles/page-templates.css]
|
|
451
450
|
breaks: "break-inside: auto on the grid; each specialty card avoid"
|
|
452
451
|
|
|
453
452
|
- name: citizen-walkthrough
|
|
454
453
|
status: live
|
|
455
|
-
origin:
|
|
456
|
-
recipe: "@
|
|
454
|
+
origin: plugin
|
|
455
|
+
recipe: "@citizen-walkthrough [.gp-columns-2] … @end-citizen-walkthrough (or @section .dc-citizen-walkthrough)"
|
|
457
456
|
emits: div.section.dc-citizen-walkthrough
|
|
458
457
|
css: [styles/components/section.css, styles/dc-native.css]
|
|
459
458
|
notes: "Themed through the section chassis tokens --dc-section-accent / --dc-section-bg (see section), which the design guide documents."
|
|
@@ -468,8 +467,8 @@ components:
|
|
|
468
467
|
|
|
469
468
|
- name: fiction-excerpt
|
|
470
469
|
status: live
|
|
471
|
-
origin:
|
|
472
|
-
recipe: "@section .dc-fiction-excerpt"
|
|
470
|
+
origin: plugin
|
|
471
|
+
recipe: "@fiction-excerpt … @end-fiction-excerpt (or @section .dc-fiction-excerpt)"
|
|
473
472
|
emits: div.section.dc-fiction-excerpt
|
|
474
473
|
css: [styles/components/section.css]
|
|
475
474
|
tokens:
|
|
@@ -483,8 +482,8 @@ components:
|
|
|
483
482
|
|
|
484
483
|
- name: npc-stat
|
|
485
484
|
status: live
|
|
486
|
-
origin:
|
|
487
|
-
recipe: "@section .dc-npc-stat"
|
|
485
|
+
origin: plugin
|
|
486
|
+
recipe: "@npc-stat … @end-npc-stat (or @section .dc-npc-stat)"
|
|
488
487
|
emits: div.section.dc-npc-stat
|
|
489
488
|
css: [styles/components/section.css]
|
|
490
489
|
tokens:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gp-dimm-city",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.0-alpha.1",
|
|
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.
|
|
72
|
+
"gutterpress": "0.11.16-alpha.2",
|
|
72
73
|
"markdown-it": "^14.1.0"
|
|
73
74
|
},
|
|
74
75
|
"peerDependencies": {
|
package/plugin.js
CHANGED
|
@@ -4,26 +4,34 @@
|
|
|
4
4
|
* IMPORTANT: This plugin only transforms content marked with special markers.
|
|
5
5
|
* Regular markdown content passes through unchanged.
|
|
6
6
|
*
|
|
7
|
-
* MARKERS (
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @sidebar
|
|
11
|
-
* @
|
|
7
|
+
* DECLARED MARKERS (the `markers` export at the bottom of this file — core
|
|
8
|
+
* Gutterpress parses, nests, closes and source-maps these; this file only
|
|
9
|
+
* names the element, classes and variants). Each also has an `@end-<name>`:
|
|
10
|
+
* @sidebar [inset] → div.dc-sidebar (`.inset` shorthand works too)
|
|
11
|
+
* @sidebar-box → div.dc-prose-panel.dc-sidebar-box
|
|
12
|
+
* @definition → div.dc-prose-panel.dc-definition-block
|
|
13
|
+
* @specialty-intro → div.dc-specialty-intro
|
|
14
|
+
* @specialty-art → div.dc-specialty-art (full-bleed art panel)
|
|
15
|
+
* @specialty-card → div.dc-specialty-card[data-position=odd|even]
|
|
16
|
+
* @gear → div.dc-card.dc-gear
|
|
17
|
+
* @toc → div.dc-toc
|
|
18
|
+
* @lede → div.dc-intro
|
|
19
|
+
* @glossary → div.dc-terms
|
|
20
|
+
* @block [panel|slate|shard|codex] label="Title"
|
|
21
|
+
* → div.dc-block.dc-<variant> + div.dc-block-title
|
|
22
|
+
* (`@block .dc-panel` class form works too)
|
|
23
|
+
*
|
|
24
|
+
* HAND-WRITTEN MARKERS (support optional key="value" attributes; handled by
|
|
25
|
+
* the dimm_city_transform state machine below):
|
|
12
26
|
* @specialty → Start a specialty wrapper (auto-closes any prior specialty,
|
|
13
27
|
* learning-path, or skill)
|
|
14
28
|
* @end-specialty → Manually end a specialty wrapper
|
|
15
|
-
* @definition → Start a dc-definition-block wrapper
|
|
16
|
-
* @end-definition → End definition wrapper
|
|
17
29
|
* @procedure → Start a dc-steps procedure wrapper
|
|
18
30
|
* @end-procedure → End procedure wrapper (auto-closes on EOF with a warning)
|
|
19
31
|
* @callout → Start a dc-alert callout (variant=note|warning|dm|vibe|origin|visit|gear)
|
|
20
32
|
* @end-callout → End callout wrapper
|
|
21
33
|
* @dm-note → Start a Dream Master note (sugar for @callout variant=dm)
|
|
22
34
|
* @end-dm-note → End dm-note wrapper
|
|
23
|
-
* @block → Section enclosure card (.dc-panel|.dc-slate|.dc-shard|.dc-codex label="Title")
|
|
24
|
-
* @end-block → End block enclosure
|
|
25
|
-
* @lede → Start a dc-intro lede wrapper
|
|
26
|
-
* @end-lede → End lede wrapper
|
|
27
35
|
* @learning-path → Start a learning path section (auto-closes previous sections)
|
|
28
36
|
* @end-learning-path → Manually end a learning path section
|
|
29
37
|
* @skill → Start a skill card (auto-closes previous skill)
|
|
@@ -31,19 +39,7 @@
|
|
|
31
39
|
* @continue → Continuation marker — emits a card with a "{name} ▸"
|
|
32
40
|
* tab so an oversized skill card can be split across pages
|
|
33
41
|
* while keeping a visible link to its origin card
|
|
34
|
-
* @specialty-intro → Cosmetic specialty intro wrapper
|
|
35
|
-
* @end-specialty-intro
|
|
36
|
-
* @specialty-card → Individual specialty card
|
|
37
|
-
* @end-specialty-card
|
|
38
|
-
* @specialty-art → Full-bleed specialty art panel (.dc-specialty-art)
|
|
39
|
-
* @end-specialty-art
|
|
40
|
-
* @gear → Gear card (shorthand for @card .dc-gear)
|
|
41
|
-
* @end-gear
|
|
42
42
|
* (chapter-opener composite is now markup-driven — see CSS notes below)
|
|
43
|
-
* @toc → Table-of-contents wrapper
|
|
44
|
-
* @end-toc
|
|
45
|
-
* @glossary → Glossary wrapper
|
|
46
|
-
* @end-glossary
|
|
47
43
|
* @outcome → 5-rung d20 outcome ladder block
|
|
48
44
|
* @end-outcome
|
|
49
45
|
* @tape → Inline tape divider (`<div class="dc-tape">— § —</div>`)
|
|
@@ -866,6 +862,23 @@ function dcAlertsTransform(state) {
|
|
|
866
862
|
state.tokens = newTokens;
|
|
867
863
|
}
|
|
868
864
|
|
|
865
|
+
/**
|
|
866
|
+
* `data-position="odd|even"` on every @specialty-card, counted across the
|
|
867
|
+
* document in order so the card grid alternates its tilt/offset. The wrapper
|
|
868
|
+
* itself is a declared marker (see `markers` below); this rule only numbers
|
|
869
|
+
* the open tokens core produced. It never runs under bare markdown-it, which
|
|
870
|
+
* has no declared markers.
|
|
871
|
+
*/
|
|
872
|
+
function dcSpecialtyCardPositions(state) {
|
|
873
|
+
let count = 0;
|
|
874
|
+
for (const tok of state.tokens) {
|
|
875
|
+
if (tok.type !== 'layout_component_open') continue;
|
|
876
|
+
if (!(tok.attrGet('class') || '').split(/\s+/).includes('dc-specialty-card')) continue;
|
|
877
|
+
count++;
|
|
878
|
+
tok.attrSet('data-position', count % 2 === 0 ? 'even' : 'odd');
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
869
882
|
/**
|
|
870
883
|
* Main plugin function - the default export Gutterpress loads
|
|
871
884
|
*/
|
|
@@ -885,6 +898,8 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
885
898
|
'<span class="dc-roll-the-die">' + esc(tokens[idx].content) + '</span>';
|
|
886
899
|
md.core.ruler.push('dc_roll_the_die', dcRollDieTransform);
|
|
887
900
|
|
|
901
|
+
md.core.ruler.push('dc_specialty_card_positions', dcSpecialtyCardPositions);
|
|
902
|
+
|
|
888
903
|
// NOTE: Chapter-opener composite behaviour is intentionally NOT handled
|
|
889
904
|
// by the plugin. The author markdown
|
|
890
905
|
//
|
|
@@ -918,21 +933,10 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
918
933
|
let inOutcomeBlock = false;
|
|
919
934
|
let outcomeBlockItems = [];
|
|
920
935
|
let outcomeBlockFlush = false;
|
|
921
|
-
let inLede = false;
|
|
922
|
-
let inSidebar = false;
|
|
923
|
-
let inSidebarBox = false;
|
|
924
|
-
let inDefinition = false;
|
|
925
936
|
let inProcedure = false;
|
|
926
937
|
let inCallout = false;
|
|
927
938
|
let inDmNote = false;
|
|
928
|
-
let inBlock = false;
|
|
929
939
|
let inCard = false;
|
|
930
|
-
let inGear = false;
|
|
931
|
-
let inToc = false;
|
|
932
|
-
let inGlossary = false;
|
|
933
|
-
let inSpecialtyCard = false;
|
|
934
|
-
let inSpecialtyIntro = false;
|
|
935
|
-
let inSpecialtyArt = false;
|
|
936
940
|
let cardHeadingDone = false;
|
|
937
941
|
let cardPullDone = false;
|
|
938
942
|
let cardBodyOpen = false;
|
|
@@ -958,7 +962,6 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
958
962
|
between Part 1 and the continuation. */
|
|
959
963
|
let lastCardTitle = '';
|
|
960
964
|
let lastCardTier = '';
|
|
961
|
-
let specialtyCardCount = 0; // resets per-specialty; used to emit data-position="even/odd"
|
|
962
965
|
|
|
963
966
|
// Helper to close all open structures EXCEPT specialty (specialty
|
|
964
967
|
// wraps the entire chapter section and is closed separately).
|
|
@@ -974,22 +977,6 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
974
977
|
cardPullDone = false;
|
|
975
978
|
lastCardBodyBlockquote = null;
|
|
976
979
|
}
|
|
977
|
-
if (inLede) {
|
|
978
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
979
|
-
inLede = false;
|
|
980
|
-
}
|
|
981
|
-
if (inSidebar) {
|
|
982
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
983
|
-
inSidebar = false;
|
|
984
|
-
}
|
|
985
|
-
if (inSidebarBox) {
|
|
986
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
987
|
-
inSidebarBox = false;
|
|
988
|
-
}
|
|
989
|
-
if (inDefinition) {
|
|
990
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
991
|
-
inDefinition = false;
|
|
992
|
-
}
|
|
993
980
|
if (inSkillCard) {
|
|
994
981
|
newTokens.push(makeToken('html_block', '</div></div></div>\n'));
|
|
995
982
|
inSkillCard = false;
|
|
@@ -1010,34 +997,6 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
1010
997
|
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1011
998
|
inDmNote = false;
|
|
1012
999
|
}
|
|
1013
|
-
if (inBlock) {
|
|
1014
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1015
|
-
inBlock = false;
|
|
1016
|
-
}
|
|
1017
|
-
if (inGear) {
|
|
1018
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1019
|
-
inGear = false;
|
|
1020
|
-
}
|
|
1021
|
-
if (inSpecialtyCard) {
|
|
1022
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1023
|
-
inSpecialtyCard = false;
|
|
1024
|
-
}
|
|
1025
|
-
if (inSpecialtyIntro) {
|
|
1026
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1027
|
-
inSpecialtyIntro = false;
|
|
1028
|
-
}
|
|
1029
|
-
if (inSpecialtyArt) {
|
|
1030
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1031
|
-
inSpecialtyArt = false;
|
|
1032
|
-
}
|
|
1033
|
-
if (inToc) {
|
|
1034
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1035
|
-
inToc = false;
|
|
1036
|
-
}
|
|
1037
|
-
if (inGlossary) {
|
|
1038
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1039
|
-
inGlossary = false;
|
|
1040
|
-
}
|
|
1041
1000
|
inProcedure = false;
|
|
1042
1001
|
learningPathHasTitle = false;
|
|
1043
1002
|
inSkillMode = false;
|
|
@@ -1056,44 +1015,11 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
1056
1015
|
}
|
|
1057
1016
|
currentSpecialtyCode = '';
|
|
1058
1017
|
currentLearningPathIndex = 0;
|
|
1059
|
-
// specialtyCardCount intentionally NOT reset here — it counts
|
|
1060
|
-
// all cards emitted in the current section so even/odd alternation
|
|
1061
|
-
// works across multiple @specialty blocks in the same card-grid.
|
|
1062
1018
|
}
|
|
1063
1019
|
|
|
1064
1020
|
for (let i = 0; i < tokens.length; i++) {
|
|
1065
1021
|
const tok = tokens[i];
|
|
1066
1022
|
|
|
1067
|
-
// --- @sidebar / @end-sidebar ---
|
|
1068
|
-
const sidebarMarker = parseMarker(tok, tokens, i, '@sidebar');
|
|
1069
|
-
if (sidebarMarker.matched) {
|
|
1070
|
-
if (inSidebar) {
|
|
1071
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1072
|
-
inSidebar = false;
|
|
1073
|
-
}
|
|
1074
|
-
if (inSidebarBox) {
|
|
1075
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1076
|
-
inSidebarBox = false;
|
|
1077
|
-
}
|
|
1078
|
-
if (inDefinition) {
|
|
1079
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1080
|
-
inDefinition = false;
|
|
1081
|
-
}
|
|
1082
|
-
newTokens.push(makeToken('html_block', '<div' + buildAttrs(sidebarMarker.attrs, 'dc-sidebar') + '>\n'));
|
|
1083
|
-
inSidebar = true;
|
|
1084
|
-
i += 2;
|
|
1085
|
-
continue;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
if (isMarker(tok, tokens, i, '@end-sidebar')) {
|
|
1089
|
-
if (inSidebar) {
|
|
1090
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1091
|
-
inSidebar = false;
|
|
1092
|
-
}
|
|
1093
|
-
i += 2;
|
|
1094
|
-
continue;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
1023
|
// --- @roll-table / @options-table (DEPRECATED 17.3.0) ---
|
|
1098
1024
|
// These macros emitted tier-colored classes (dc-roll-table*, dc-options-table*)
|
|
1099
1025
|
// with ZERO backing CSS and had zero usage in the book. They are now no-ops:
|
|
@@ -1231,66 +1157,6 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
1231
1157
|
continue;
|
|
1232
1158
|
}
|
|
1233
1159
|
|
|
1234
|
-
// Check for @specialty marker (must come before @learning-path/@skill
|
|
1235
|
-
// since it auto-closes everything else)
|
|
1236
|
-
const sidebarBoxMarker = parseMarker(tok, tokens, i, '@sidebar-box');
|
|
1237
|
-
if (sidebarBoxMarker.matched) {
|
|
1238
|
-
if (inSidebarBox) {
|
|
1239
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1240
|
-
inSidebarBox = false;
|
|
1241
|
-
}
|
|
1242
|
-
if (inSidebar) {
|
|
1243
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1244
|
-
inSidebar = false;
|
|
1245
|
-
}
|
|
1246
|
-
if (inDefinition) {
|
|
1247
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1248
|
-
inDefinition = false;
|
|
1249
|
-
}
|
|
1250
|
-
newTokens.push(makeToken('html_block', '<div' + buildAttrs(sidebarBoxMarker.attrs, 'dc-prose-panel dc-sidebar-box') + '>\n'));
|
|
1251
|
-
inSidebarBox = true;
|
|
1252
|
-
i += 2;
|
|
1253
|
-
continue;
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
if (isMarker(tok, tokens, i, '@end-sidebar-box')) {
|
|
1257
|
-
if (inSidebarBox) {
|
|
1258
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1259
|
-
inSidebarBox = false;
|
|
1260
|
-
}
|
|
1261
|
-
i += 2;
|
|
1262
|
-
continue;
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
const definitionMarker = parseMarker(tok, tokens, i, '@definition');
|
|
1266
|
-
if (definitionMarker.matched) {
|
|
1267
|
-
if (inDefinition) {
|
|
1268
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1269
|
-
inDefinition = false;
|
|
1270
|
-
}
|
|
1271
|
-
if (inSidebar) {
|
|
1272
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1273
|
-
inSidebar = false;
|
|
1274
|
-
}
|
|
1275
|
-
if (inSidebarBox) {
|
|
1276
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1277
|
-
inSidebarBox = false;
|
|
1278
|
-
}
|
|
1279
|
-
newTokens.push(makeToken('html_block', '<div' + buildAttrs(definitionMarker.attrs, 'dc-prose-panel dc-definition-block') + '>\n'));
|
|
1280
|
-
inDefinition = true;
|
|
1281
|
-
i += 2;
|
|
1282
|
-
continue;
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
if (isMarker(tok, tokens, i, '@end-definition')) {
|
|
1286
|
-
if (inDefinition) {
|
|
1287
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1288
|
-
inDefinition = false;
|
|
1289
|
-
}
|
|
1290
|
-
i += 2;
|
|
1291
|
-
continue;
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
1160
|
const procedureMarker = parseMarker(tok, tokens, i, '@procedure');
|
|
1295
1161
|
if (procedureMarker.matched) {
|
|
1296
1162
|
// @procedure auto-closes any prior open section so a forgotten
|
|
@@ -1371,40 +1237,6 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
1371
1237
|
continue;
|
|
1372
1238
|
}
|
|
1373
1239
|
|
|
1374
|
-
// --- @block (section enclosures) ---
|
|
1375
|
-
// Reusable card-like text section enclosures. Four variants with distinct
|
|
1376
|
-
// clip-path geometry, surface, and accent colors. Each emits a .dc-block
|
|
1377
|
-
// container with an optional titled header band (.dc-block-title).
|
|
1378
|
-
//
|
|
1379
|
-
// Syntax: @block .dc-panel|.dc-slate|.dc-shard|.dc-codex label="Title"
|
|
1380
|
-
|
|
1381
|
-
const blockUnifiedMarker = parseMarker(tok, tokens, i, '@block');
|
|
1382
|
-
if (blockUnifiedMarker.matched) {
|
|
1383
|
-
closeAll();
|
|
1384
|
-
const blockLabel = blockUnifiedMarker.attrs['label'] ? esc(blockUnifiedMarker.attrs['label']) : '';
|
|
1385
|
-
const blockTitleHtml = blockLabel ? '<div class="dc-block-title">' + blockLabel + '</div>\n' : '';
|
|
1386
|
-
let blockClass = 'dc-block';
|
|
1387
|
-
if (blockUnifiedMarker.attrs['class']) {
|
|
1388
|
-
blockClass += ' ' + blockUnifiedMarker.attrs['class'];
|
|
1389
|
-
} else if (blockUnifiedMarker.attrs['variant']) {
|
|
1390
|
-
blockClass += ' dc-' + blockUnifiedMarker.attrs['variant'].toLowerCase();
|
|
1391
|
-
}
|
|
1392
|
-
newTokens.push(makeToken('html_block', '<div class="' + blockClass + '">\n' + blockTitleHtml));
|
|
1393
|
-
inBlock = true;
|
|
1394
|
-
i += 2;
|
|
1395
|
-
continue;
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
if (isMarker(tok, tokens, i, '@end-block')) {
|
|
1400
|
-
if (inBlock) {
|
|
1401
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1402
|
-
inBlock = false;
|
|
1403
|
-
}
|
|
1404
|
-
i += 2;
|
|
1405
|
-
continue;
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
1240
|
// --- @card / @end-card ---
|
|
1409
1241
|
// Generic card primitive. Emits .dc-card with optional sub-elements:
|
|
1410
1242
|
// .dc-card-heading — first h4 after @card
|
|
@@ -1454,112 +1286,6 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
1454
1286
|
continue;
|
|
1455
1287
|
}
|
|
1456
1288
|
|
|
1457
|
-
// --- @specialty-card / @end-specialty-card ---
|
|
1458
|
-
// Summary card used in the choose-specialty overview grid.
|
|
1459
|
-
// Shape and color inherited from .specialty.<name> parent container.
|
|
1460
|
-
const specialtyCardMarker = parseMarker(tok, tokens, i, '@specialty-card');
|
|
1461
|
-
if (specialtyCardMarker.matched) {
|
|
1462
|
-
closeAll();
|
|
1463
|
-
specialtyCardCount++;
|
|
1464
|
-
const positionAttr = ' data-position="' + (specialtyCardCount % 2 === 0 ? 'even' : 'odd') + '"';
|
|
1465
|
-
newTokens.push(makeToken('html_block', '<div' + buildAttrs(specialtyCardMarker.attrs, 'dc-specialty-card') + positionAttr + '>\n'));
|
|
1466
|
-
inSpecialtyCard = true;
|
|
1467
|
-
i += 2;
|
|
1468
|
-
continue;
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
if (isMarker(tok, tokens, i, '@end-specialty-card')) {
|
|
1472
|
-
if (inSpecialtyCard) {
|
|
1473
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1474
|
-
inSpecialtyCard = false;
|
|
1475
|
-
}
|
|
1476
|
-
i += 2;
|
|
1477
|
-
continue;
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
// --- @specialty-intro / @end-specialty-intro ---
|
|
1481
|
-
// Full-page specialty intro panel. Shape/color from .specialty.<name> parent.
|
|
1482
|
-
const specialtyIntroMarker = parseMarker(tok, tokens, i, '@specialty-intro');
|
|
1483
|
-
if (specialtyIntroMarker.matched) {
|
|
1484
|
-
closeAll();
|
|
1485
|
-
newTokens.push(makeToken('html_block', '<div' + buildAttrs(specialtyIntroMarker.attrs, 'dc-specialty-intro') + '>\n'));
|
|
1486
|
-
inSpecialtyIntro = true;
|
|
1487
|
-
i += 2;
|
|
1488
|
-
continue;
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
if (isMarker(tok, tokens, i, '@end-specialty-intro')) {
|
|
1492
|
-
if (inSpecialtyIntro) {
|
|
1493
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1494
|
-
inSpecialtyIntro = false;
|
|
1495
|
-
}
|
|
1496
|
-
i += 2;
|
|
1497
|
-
continue;
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
// --- @specialty-art / @end-specialty-art ---
|
|
1501
|
-
// Full-bleed art panel for a specialty profile. Emits .dc-specialty-art
|
|
1502
|
-
// (CSS: components/*.css + page-rules.css `.dc-specialty-art { page: full; }`).
|
|
1503
|
-
const specialtyArtMarker = parseMarker(tok, tokens, i, '@specialty-art');
|
|
1504
|
-
if (specialtyArtMarker.matched) {
|
|
1505
|
-
closeAll();
|
|
1506
|
-
newTokens.push(makeToken('html_block', '<div' + buildAttrs(specialtyArtMarker.attrs, 'dc-specialty-art') + '>\n'));
|
|
1507
|
-
inSpecialtyArt = true;
|
|
1508
|
-
i += 2;
|
|
1509
|
-
continue;
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
if (isMarker(tok, tokens, i, '@end-specialty-art')) {
|
|
1513
|
-
if (inSpecialtyArt) {
|
|
1514
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1515
|
-
inSpecialtyArt = false;
|
|
1516
|
-
}
|
|
1517
|
-
i += 2;
|
|
1518
|
-
continue;
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
// --- @toc / @end-toc ---
|
|
1523
|
-
const tocMarker = parseMarker(tok, tokens, i, '@toc');
|
|
1524
|
-
if (tocMarker.matched) {
|
|
1525
|
-
closeAll();
|
|
1526
|
-
newTokens.push(makeToken('html_block', '<div class="dc-toc">\n'));
|
|
1527
|
-
inToc = true;
|
|
1528
|
-
i += 2; continue;
|
|
1529
|
-
}
|
|
1530
|
-
if (isMarker(tok, tokens, i, '@end-toc')) {
|
|
1531
|
-
if (inToc) {
|
|
1532
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1533
|
-
inToc = false;
|
|
1534
|
-
}
|
|
1535
|
-
i += 2; continue;
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
// Removed 2026-05-17: @two-column / @three-column / @no-break.
|
|
1539
|
-
// Use @section .gp-columns-2 / @section .gp-columns-3 instead — markers.js
|
|
1540
|
-
// emits identical layout semantics, with the added `.section` class that
|
|
1541
|
-
// picks up `break-inside: avoid` from MARKER_CSS.
|
|
1542
|
-
|
|
1543
|
-
// --- @gear / @end-gear ---
|
|
1544
|
-
// Shorthand for @card .dc-gear — emits a bare `.dc-card.dc-gear`.
|
|
1545
|
-
// NOTE: there is NO .section.dc-gear-list wrapper (an earlier comment
|
|
1546
|
-
// claimed one, and components/*.css carried ~40 lines of CSS keyed on
|
|
1547
|
-
// it that could never match). Style `.dc-card.dc-gear` directly.
|
|
1548
|
-
const gearMarker = parseMarker(tok, tokens, i, '@gear');
|
|
1549
|
-
if (gearMarker.matched) {
|
|
1550
|
-
closeAll();
|
|
1551
|
-
newTokens.push(makeToken('html_block', '<div' + buildAttrs(gearMarker.attrs, 'dc-card dc-gear') + '>\n'));
|
|
1552
|
-
inGear = true;
|
|
1553
|
-
i += 2; continue;
|
|
1554
|
-
}
|
|
1555
|
-
if (isMarker(tok, tokens, i, '@end-gear')) {
|
|
1556
|
-
if (inGear) {
|
|
1557
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1558
|
-
inGear = false;
|
|
1559
|
-
}
|
|
1560
|
-
i += 2; continue;
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
1289
|
// --- @tape (single-line tape divider) ---
|
|
1564
1290
|
const tapeMarker = parseMarker(tok, tokens, i, '@tape');
|
|
1565
1291
|
if (tapeMarker.matched) {
|
|
@@ -1569,41 +1295,6 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
1569
1295
|
i += 2; continue;
|
|
1570
1296
|
}
|
|
1571
1297
|
|
|
1572
|
-
// --- @lede / @end-lede ---
|
|
1573
|
-
// Emits .dc-intro (canonical). The legacy bare `lede` class was removed
|
|
1574
|
-
// 2026-05-17 — there were no CSS rules using it, only the dc-prefixed
|
|
1575
|
-
// form is styled.
|
|
1576
|
-
const ledeMarker = parseMarker(tok, tokens, i, '@lede');
|
|
1577
|
-
if (ledeMarker.matched) {
|
|
1578
|
-
closeAll();
|
|
1579
|
-
newTokens.push(makeToken('html_block', '<div class="dc-intro">\n'));
|
|
1580
|
-
inLede = true;
|
|
1581
|
-
i += 2; continue;
|
|
1582
|
-
}
|
|
1583
|
-
if (isMarker(tok, tokens, i, '@end-lede')) {
|
|
1584
|
-
if (inLede) {
|
|
1585
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1586
|
-
inLede = false;
|
|
1587
|
-
}
|
|
1588
|
-
i += 2; continue;
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
// --- @glossary / @end-glossary ---
|
|
1592
|
-
const glossaryMarker = parseMarker(tok, tokens, i, '@glossary');
|
|
1593
|
-
if (glossaryMarker.matched) {
|
|
1594
|
-
closeAll();
|
|
1595
|
-
newTokens.push(makeToken('html_block', '<div class="dc-terms">\n'));
|
|
1596
|
-
inGlossary = true;
|
|
1597
|
-
i += 2; continue;
|
|
1598
|
-
}
|
|
1599
|
-
if (isMarker(tok, tokens, i, '@end-glossary')) {
|
|
1600
|
-
if (inGlossary) {
|
|
1601
|
-
newTokens.push(makeToken('html_block', '</div>\n'));
|
|
1602
|
-
inGlossary = false;
|
|
1603
|
-
}
|
|
1604
|
-
i += 2; continue;
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
1298
|
const specialtyMarker = parseMarker(tok, tokens, i, '@specialty');
|
|
1608
1299
|
if (specialtyMarker.matched) {
|
|
1609
1300
|
closeSpecialty();
|
|
@@ -2098,6 +1789,64 @@ export default function dimmCityPlugin(md, options = {}) {
|
|
|
2098
1789
|
|
|
2099
1790
|
}
|
|
2100
1791
|
|
|
1792
|
+
/**
|
|
1793
|
+
* Declared markers — plain data Gutterpress core reads off this module (the
|
|
1794
|
+
* same relationship `metadata` has). Core parses `@name … @end-name`, merges
|
|
1795
|
+
* author classes, threads `data-source-range`, nests containers as a stack and
|
|
1796
|
+
* closes them at the next `@page`/`@section`/`@chapter`, so none of this is
|
|
1797
|
+
* hand-written in the state machine above. A bare markdown-it instance never
|
|
1798
|
+
* reads this table: declared markers exist only under Gutterpress.
|
|
1799
|
+
*
|
|
1800
|
+
* A `section: true` entry declares a section-styled component: core treats it
|
|
1801
|
+
* as a real `@section` carrying the entry's class (see below).
|
|
1802
|
+
*
|
|
1803
|
+
* To add a plain wrapper, add one line here (element + classes), a snippet in
|
|
1804
|
+
* `snippets/<name>.md`, and the usual catalog/guide entries — see
|
|
1805
|
+
* docs/adding-macros.md.
|
|
1806
|
+
*
|
|
1807
|
+
* Variants are a bare word on the marker line (`@sidebar inset`) and only add
|
|
1808
|
+
* a class, so `.inset`-style class shorthand keeps working beside them.
|
|
1809
|
+
* `autoCloseAt: ['eof']` keeps these silent at end-of-document: they have
|
|
1810
|
+
* never needed an explicit `@end-…`.
|
|
1811
|
+
*/
|
|
1812
|
+
const wrapper = (cls, extra = {}) => ({ class: cls, autoCloseAt: ['eof'], ...extra });
|
|
1813
|
+
|
|
1814
|
+
export const markers = {
|
|
1815
|
+
sidebar: wrapper('dc-sidebar', { variants: { inset: 'inset' } }),
|
|
1816
|
+
'sidebar-box': wrapper('dc-prose-panel dc-sidebar-box'),
|
|
1817
|
+
definition: wrapper('dc-prose-panel dc-definition-block'),
|
|
1818
|
+
'specialty-intro': wrapper('dc-specialty-intro'),
|
|
1819
|
+
'specialty-art': wrapper('dc-specialty-art'),
|
|
1820
|
+
'specialty-card': wrapper('dc-specialty-card'),
|
|
1821
|
+
gear: wrapper('dc-card dc-gear'),
|
|
1822
|
+
toc: wrapper('dc-toc'),
|
|
1823
|
+
lede: wrapper('dc-intro'),
|
|
1824
|
+
glossary: wrapper('dc-terms'),
|
|
1825
|
+
block: wrapper('dc-block', {
|
|
1826
|
+
variants: { panel: 'dc-panel', slate: 'dc-slate', shard: 'dc-shard', codex: 'dc-codex' },
|
|
1827
|
+
label: { tag: 'div', class: 'dc-block-title', from: 'attr:label' },
|
|
1828
|
+
}),
|
|
1829
|
+
|
|
1830
|
+
// Section-styled components. `section: true` makes each one a real core
|
|
1831
|
+
// @section: `@npc-stat` is exactly `@section .dc-npc-stat` (same element, same
|
|
1832
|
+
// classes), so the books' existing `@section .dc-…` spellings keep working
|
|
1833
|
+
// unchanged. Each class has its CSS on `.section.dc-…` in
|
|
1834
|
+
// styles/components/section.css. No variants: none of these is styled per
|
|
1835
|
+
// specialty (that lives on `.dc-specialty.<name>`, from `@specialty`), and
|
|
1836
|
+
// modifiers such as `.dc-plain`, `.dc-snug`, `.dc-allow-split` and
|
|
1837
|
+
// `.gp-columns-2` stay author classes (`@column-panel .gp-columns-2`).
|
|
1838
|
+
'column-panel': { section: true, class: 'dc-column-panel' },
|
|
1839
|
+
tabbed: { section: true, class: 'dc-tabbed' },
|
|
1840
|
+
'card-grid': { section: true, class: 'dc-card-grid' },
|
|
1841
|
+
'citizen-walkthrough': { section: true, class: 'dc-citizen-walkthrough' },
|
|
1842
|
+
'fiction-excerpt': { section: true, class: 'dc-fiction-excerpt' },
|
|
1843
|
+
'npc-stat': { section: true, class: 'dc-npc-stat' },
|
|
1844
|
+
// Citizen-file card runs: the class sets the accent of every `@card` inside.
|
|
1845
|
+
flaws: { section: true, class: 'dc-flaws' },
|
|
1846
|
+
ideals: { section: true, class: 'dc-ideals' },
|
|
1847
|
+
dreams: { section: true, class: 'dc-dreams' },
|
|
1848
|
+
};
|
|
1849
|
+
|
|
2101
1850
|
/**
|
|
2102
1851
|
* Plugin metadata (the package version in package.json is the only version).
|
|
2103
1852
|
*/
|
package/snippets/block.md
CHANGED
package/snippets/toc.md
ADDED
|
@@ -26,13 +26,10 @@
|
|
|
26
26
|
miscommunicates the semantic (CONTENTS shouldn't read as a warning).
|
|
27
27
|
Rubric: D9 (brand identity per chapter — chroma anchor must match
|
|
28
28
|
the banner's semantic role). */
|
|
29
|
-
/* Chevron banner
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
chapter-opener composite rules above. */
|
|
34
|
-
.dc-chevron,
|
|
35
|
-
.chapter[data-chapter-label] > .page[data-page="intro"] > .section > h1:first-of-type {
|
|
29
|
+
/* Chevron banner on elements marked `.dc-chevron`. The chapter opener's first
|
|
30
|
+
h1 gets the same treatment without the class; that rule lives with the
|
|
31
|
+
opener template in page-templates.css. */
|
|
32
|
+
.dc-chevron {
|
|
36
33
|
font-family: var(--font-display);
|
|
37
34
|
font-size: var(--dc-chevron-font-size);
|
|
38
35
|
letter-spacing: 0.06em;
|
|
@@ -83,14 +80,6 @@ h1.dc-chevron {
|
|
|
83
80
|
line-height: 1.2;
|
|
84
81
|
}
|
|
85
82
|
|
|
86
|
-
/* Chapter-opener heading runs at a smaller size than other h1.dc-chevron
|
|
87
|
-
instances so the section body weight stays the focal anchor below the
|
|
88
|
-
composite badge + banner. User-approved 2026-05-25. */
|
|
89
|
-
.chapter[data-chapter-label] > .page[data-page="intro"] > .section > h1:first-of-type {
|
|
90
|
-
font-size: 1.1rem;
|
|
91
|
-
line-height: 1.2;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
83
|
/* Front-matter chevron font-size overrides moved to the book's own sheet (unlayered) per A1
|
|
95
84
|
layer-ownership (page-context rules belong in the guide-specific override
|
|
96
85
|
file, not the portable component library). Three-judge gate iteration 3. */
|
|
@@ -412,9 +401,8 @@ hr {
|
|
|
412
401
|
background: repeating-linear-gradient(90deg, color-mix(in srgb, var(--crimson) 85%, var(--dc-tape-bg)) 0 6px, var(--dc-tape-bg) 6px 10px);
|
|
413
402
|
}
|
|
414
403
|
|
|
415
|
-
/* Chapter-opener badge + H1 banner — see
|
|
416
|
-
|
|
417
|
-
The legacy .dc-chapter-opener-no rules previously here were removed
|
|
404
|
+
/* Chapter-opener badge + H1 banner — see "PAGE TEMPLATE: CHAPTER OPENER"
|
|
405
|
+
in page-templates.css. The legacy .dc-chapter-opener-no rules previously here were removed
|
|
418
406
|
when the legacy @chapter-opener macro was retired. */
|
|
419
407
|
|
|
420
408
|
/* ── Specialty art full-page plate ─────────────────────────────────────────── */
|
|
@@ -686,9 +686,28 @@
|
|
|
686
686
|
attribute. Negative margins cancel the section padding so it reaches the
|
|
687
687
|
section's outer edges; its --clip-banner tail then tapers in by --clip-tail. */
|
|
688
688
|
.chapter[data-chapter-label] > .page[data-page="intro"] > .section > h1:first-of-type {
|
|
689
|
+
/* The .dc-chevron look (components/chrome.css), applied without the class
|
|
690
|
+
so authors need not write `{.dc-chevron}` on every chapter h1. */
|
|
691
|
+
font-family: var(--font-display);
|
|
692
|
+
letter-spacing: 0.06em;
|
|
693
|
+
color: var(--dc-chevron-color);
|
|
694
|
+
text-transform: uppercase;
|
|
695
|
+
background: var(--dc-chevron-bg);
|
|
696
|
+
padding: 10px 28px 10px 14px;
|
|
697
|
+
display: block;
|
|
698
|
+
box-sizing: border-box;
|
|
699
|
+
clip-path: var(--clip-banner);
|
|
700
|
+
word-break: break-word;
|
|
701
|
+
|
|
702
|
+
/* Smaller than other h1.dc-chevron so the section body stays the focal
|
|
703
|
+
anchor below the banner. User-approved 2026-05-25. */
|
|
704
|
+
font-size: 1.1rem;
|
|
705
|
+
line-height: 1.2;
|
|
706
|
+
|
|
689
707
|
margin-top: calc(var(--dc-chapter-opener-pad-top) * -1);
|
|
690
708
|
margin-left: calc(var(--dc-chapter-opener-pad-side) * -1);
|
|
691
709
|
margin-right: calc(var(--dc-chapter-opener-pad-right) * -1);
|
|
710
|
+
margin-bottom: 0;
|
|
692
711
|
--dc-chevron-bg: var(--dc-chapter-opener-accent);
|
|
693
712
|
}
|
|
694
713
|
|