@heroiclands/package-build 23.12.1 → 23.12.3

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
@@ -1,5 +1,23 @@
1
1
  # @heroiclands/package-build
2
2
 
3
+ ## 23.12.3
4
+
5
+ ### Patch Changes
6
+
7
+ In Foundry, every package's journals now show marked names in bold, mark links to pages no package publishes, and set captioned pictures, tables and verse with their labels, as the site and the preview do.
8
+
9
+ Sites build again on Hugo 0.163, where a page without a definition list stopped the whole build.
10
+
11
+ Site search no longer finds or quotes the text of secrets.
12
+
13
+ A module's journals, scenes, macros and adventures load under any system; only its Actor and Item packs belong to the system it declares.
14
+
15
+ ## 23.12.2
16
+
17
+ ### Patch Changes
18
+
19
+ A wikilink whose label also names a different note no longer asks for its full name first.
20
+
3
21
  ## 23.12.1
4
22
 
5
23
  ### Patch Changes
@@ -15,10 +15,12 @@
15
15
  * The base stylesheet every package ships, staged into the package root and
16
16
  * named first in the manifest's `styles`.
17
17
  *
18
- * It carries only what every package needs for its compiled content to be
19
- * legible, which today is the definition list: an infobox emits one, and so
20
- * does the ordinary markdown `Term` / `: definition` syntax. A package keeps
21
- * its own stylesheet for its own needs and overrides anything here from it.
18
+ * It carries what every package's compiled content needs to read in Foundry as
19
+ * it does in the preview and on the site: definition lists, poetry, alerts,
20
+ * inline spoilers, marked names, unresolved links and captioned blocks. Every
21
+ * class the Foundry renderer emits is styled here or exempted by name in
22
+ * `tests/base-styles.test.ts`. A package keeps its own stylesheet for its own
23
+ * needs and overrides anything here from it.
22
24
  *
23
25
  * `packageBuild.baseStyles: false` declines it.
24
26
  *
@@ -351,3 +353,83 @@
351
353
  outline: 2px solid currentColor;
352
354
  outline-offset: 2px;
353
355
  }
356
+
357
+ /*
358
+ * A name the text marks is bold, as on the site, in the preview and in the book.
359
+ * Foundry's interface and a system's sheets use `.name` for their own elements,
360
+ * which the content roots leave untouched.
361
+ */
362
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .name {
363
+ font-weight: var(--pb-name-weight, 700);
364
+ }
365
+
366
+ /*
367
+ * A wikilink whose target no package publishes keeps its text and is marked, so
368
+ * the sentence still reads and a reader can tell a link was meant. Each value
369
+ * defers to a SoHL custom property, so the system's theme wins where it is
370
+ * installed. `light-dark()` follows the colour scheme Foundry sets, rather than
371
+ * the operating system's. The underline serves a reader who cannot separate
372
+ * the hue from body text.
373
+ */
374
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
375
+ .sohl-unresolved-link {
376
+ font-weight: var(--sohl-unresolved-link-weight, 700);
377
+ color: var(--sohl-unresolved-link-color, light-dark(#b3261e, #ff8a80));
378
+ text-decoration: var(--sohl-unresolved-link-decoration, underline dotted currentcolor);
379
+ text-underline-offset: 0.15em;
380
+ cursor: help;
381
+ }
382
+
383
+ /*
384
+ * A captioned block: picture, table, verse or passage. Every kind is styled
385
+ * alike, so a kind added to the vocabulary arrives styled. The block owns the
386
+ * space around what it wraps, and its label reads as a caption rather than as
387
+ * another paragraph.
388
+ */
389
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .content-figure {
390
+ margin: 1.5rem 0;
391
+ }
392
+
393
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
394
+ .content-figure
395
+ > :first-child {
396
+ margin-top: 0;
397
+ }
398
+
399
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) figure.note-image {
400
+ display: table;
401
+ max-width: 100%;
402
+ margin: 1.5rem 0;
403
+ }
404
+
405
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
406
+ .content-figure
407
+ > figure.note-image {
408
+ margin: 0;
409
+ }
410
+
411
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
412
+ figure.note-image
413
+ img {
414
+ display: block;
415
+ max-width: 100%;
416
+ height: auto;
417
+ border: none;
418
+ }
419
+
420
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
421
+ .content-figure-label {
422
+ margin: 0.5rem 0 0;
423
+ font-size: 0.85em;
424
+ line-height: 1.4;
425
+ opacity: 0.75;
426
+ }
427
+
428
+ /* `.border` is the one class an author may write on a block, and it draws the
429
+ * block as a boxed aside. */
430
+ :is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
431
+ .content-figure.border {
432
+ padding: 1rem 1.25rem;
433
+ border: 1px solid var(--pb-figure-border, currentcolor);
434
+ border-radius: 4px;
435
+ }
@@ -718,17 +718,17 @@ packs:
718
718
  - { name: journals, type: JournalEntry, label: Journals }
719
719
  ```
720
720
 
721
- | Key (under `packs[]`) | Type | Required | Default |
722
- | --------------------- | --------------------------------------------------------------------- | -------- | --------------------------------------- |
723
- | `packs[].name` | string | yes | — |
724
- | `packs[].type` | one of `Actor`, `Adventure`, `Item`, `JournalEntry`, `Macro`, `Scene` | yes | — |
725
- | `packs[].label` | string | no | `packs[].name` |
726
- | `packs[].private` | boolean | no | `false` |
727
- | `packs[].companions` | array, same shape, one level only | no | `[]` |
728
- | `packs[].mayBeEmpty` | boolean | no | `false` |
729
- | `packs[].default` | boolean | no | `false` |
730
- | `packs[].prebuilt` | string (directory) | no | `null` |
731
- | `packs[].system` | string | no | `null` — falls back to `stats.systemId` |
721
+ | Key (under `packs[]`) | Type | Required | Default |
722
+ | --------------------- | --------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------ |
723
+ | `packs[].name` | string | yes | — |
724
+ | `packs[].type` | one of `Actor`, `Adventure`, `Item`, `JournalEntry`, `Macro`, `Scene` | yes | — |
725
+ | `packs[].label` | string | no | `packs[].name` |
726
+ | `packs[].private` | boolean | no | `false` |
727
+ | `packs[].companions` | array, same shape, one level only | no | `[]` |
728
+ | `packs[].mayBeEmpty` | boolean | no | `false` |
729
+ | `packs[].default` | boolean | no | `false` |
730
+ | `packs[].prebuilt` | string (directory) | no | `null` |
731
+ | `packs[].system` | string | no | `null` — an ActiveEffect, Actor or Item pack falls back to `stats.systemId`; any other pack names none |
732
732
 
733
733
  `private: true` restricts a Foundry compendium to GMs. A note tagged `gm`
734
734
  compiles only into private packs. Set `data.pack` to a private pack for the
@@ -1861,10 +1861,13 @@ Whether the package takes the shared base stylesheet. It defaults to `true`,
1861
1861
  is staged to `styles/package-build-base.css` under the stage root, and is
1862
1862
  named first in the generated manifest's `styles`.
1863
1863
 
1864
- The sheet carries only what every package's compiled content relies on to be
1865
- legible — the definition-list style an infobox and the markdown `Term` /
1866
- `: definition` syntax both emit — and is scoped to the journal, enriched-field
1867
- and editor containers so it cannot reach Foundry's own interface.
1864
+ The sheet carries what every package's compiled content needs to read in
1865
+ Foundry as it does in the preview and on the site: definition lists (an
1866
+ infobox and the markdown `Term` / `: definition` syntax both emit one),
1867
+ poetry, alerts, inline spoilers, marked names in bold, unresolved links and
1868
+ captioned blocks. It is scoped to the journal, enriched-field and editor
1869
+ containers so it cannot reach Foundry's own interface, where `.name` and `dl`
1870
+ style Foundry's and the system's own elements.
1868
1871
 
1869
1872
  It loads into the cascade layer Foundry reserves for the package kind,
1870
1873
  `system` or `modules`, which is the weakest layer a package can occupy. A
@@ -14,11 +14,11 @@
14
14
  /**
15
15
  * The **base stylesheet** every package ships, and where it lands.
16
16
  *
17
- * Compiled content relies on a handful of rules to be legible — a definition
18
- * list is the whole of it today — and five packages need them. Four of those
19
- * have no stylesheet at all, so writing the rules by hand means one edit and
20
- * four new files, each with a configuration entry, each free to diverge from
21
- * the first time one of them is touched. This repository already ships
17
+ * Compiled content relies on a handful of rules to read the same in Foundry as
18
+ * in the preview and on the site, and every package needs them. Most packages
19
+ * have no stylesheet of their own, so writing the rules by hand means a new
20
+ * file in each, each with a configuration entry, each free to diverge from the
21
+ * first time one of them is touched. This repository already ships
22
22
  * configuration to consumers and already generates the manifest that declares
23
23
  * a package's `styles`, so it can ship the sheet the same way: one file,
24
24
  * staged into every package, corrected in one place.
@@ -290,7 +290,7 @@ export function renderBlocks(source, target) {
290
290
  } else {
291
291
  output.push(
292
292
  "",
293
- `<details ${attributes}>`,
293
+ `<details ${attributes} data-pagefind-ignore>`,
294
294
  `<summary class="${escapeAttribute(block.name)}">${title}</summary>`,
295
295
  "",
296
296
  held,
@@ -307,7 +307,7 @@ export function renderSpans(source) {
307
307
  );
308
308
  const content =
309
309
  secret ?
310
- `<span${attrs} data-inline-secret><button type="button" class="inline-secret-toggle" aria-label="Show spoiler" aria-expanded="false">Show spoiler</button><span class="inline-secret-content" hidden>${inner}</span></span>`
310
+ `<span${attrs} data-inline-secret data-pagefind-ignore><button type="button" class="inline-secret-toggle" aria-label="Show spoiler" aria-expanded="false">Show spoiler</button><span class="inline-secret-content" hidden>${inner}</span></span>`
311
311
  : `<span${attrs}>${inner}</span>`;
312
312
  markdown = markdown.slice(0, span.start) + content + markdown.slice(span.end);
313
313
  }
@@ -328,7 +328,7 @@ export function createFirstUseChecker({
328
328
  `“${entry.full}” is linked or marked more than once`,
329
329
  );
330
330
  // A resolved wikilink identifies its note, so its label may be any natural form.
331
- const introduces = entry.fullUse || entry.key === event.preferred?.key;
331
+ const introduces = entry.fullUse || event.preferred !== undefined;
332
332
  if (!introduces && !fullUsed.has(entry.key))
333
333
  add(
334
334
  event.start,
@@ -1,53 +1,58 @@
1
1
  {{- /* Group the final Goldmark HTML. Quote-aware tokens preserve attributes,
2
2
  literal examples, comments and raw blocks; a stack gives each nested list
3
3
  its own rows. Existing grouped lists keep their authored divs. */ -}}
4
+ {{- /* One return, at the end: Hugo 0.163, which the CI runners use, fails on
5
+ a `return` inside a block. */ -}}
4
6
  {{- $source := string . -}}
5
- {{- if not (findRE `(?i)<dl(?:\s|>)` $source) -}}{{- return ($source | safeHTML) -}}{{- end -}}
6
- {{- $pattern := `(?is)<!--.*?-->|<script\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</script\s*>|<style\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</style\s*>|<pre\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</pre\s*>|<textarea\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</textarea\s*>|<![^>]*>|</?[a-z][a-z0-9:-]*(?:[^"'<>]|"[^"]*"|'[^']*')*>|[^<]+|<` -}}
7
- {{- $parts := slice -}}
8
- {{- $lists := slice -}}
9
- {{- $elements := slice -}}
10
- {{- $tokens := findRE $pattern $source -}}
11
- {{- range $i, $token := $tokens -}}
12
- {{- if and (gt (len $lists) 0) (gt $i 0) (lt (add $i 1) (len $tokens)) (findRE `^\s+$` $token) (findRE `(?i)^</dt\s*>$` (index $tokens (sub $i 1))) (findRE `(?i)^<dd(?:\s|>)` (index $tokens (add $i 1))) -}}
13
- {{- $token = "" -}}
14
- {{- end -}}
15
- {{- if and (gt (len $lists) 0) (gt $i 0) (lt (add $i 1) (len $tokens)) (findRE `^\s+$` $token) (findRE `(?i)^<dd(?:\s[^>]*)?>$` (index $tokens (sub $i 1))) (findRE `(?i)^<p(?:\s|>)` (index $tokens (add $i 1))) -}}
16
- {{- $token = "" -}}
17
- {{- end -}}
18
- {{- $tag := "" -}}
19
- {{- $opaque := findRE `(?i)^<(?:!--|script\b|style\b|pre\b|textarea\b)` $token -}}
20
- {{- if and (not $opaque) (findRE `(?i)^</?[a-z]` $token) -}}
21
- {{- $tag = lower (replaceRE `(?is)^</?([a-z][a-z0-9:-]*).*` "$1" $token) -}}
22
- {{- $closing := hasPrefix $token "</" -}}
23
- {{- if and (eq $tag "dl") (not $closing) -}}
24
- {{- $list := newScratch -}}
25
- {{- $list.Set "row" false -}}
26
- {{- $lists = $lists | append $list -}}
27
- {{- if or (findRE `(?i)\sclass\s*=\s*["'](?:[^"']*\s)?dictionary(?:\s|["'])` $token) (findRE `(?i)\sclass\s*=\s*dictionary(?:\s|>)` $token) -}}
28
- {{- else if findRE `(?i)\sclass\s*=\s*["']` $token -}}
29
- {{- $token = replaceRE `(?i)(\sclass\s*=\s*["'])` "${1}dictionary " $token -}}
30
- {{- else if findRE `(?i)\sclass\s*=` $token -}}
31
- {{- $token = replaceRE `(?i)(\sclass\s*=\s*)([^\s>]+)` `${1}"dictionary ${2}"` $token -}}
32
- {{- else -}}
33
- {{- $token = replaceRE `(?i)^<dl` `<dl class="dictionary"` $token -}}
34
- {{- end -}}
35
- {{- else if and (eq $tag "dt") (not $closing) (gt (len $lists) 0) (eq (index $elements (sub (len $elements) 1)) "dl") -}}
36
- {{- $list := index $lists (sub (len $lists) 1) -}}
37
- {{- if $list.Get "row" -}}{{- $parts = $parts | append "</div>" -}}{{- end -}}
38
- {{- $parts = $parts | append "<div>" -}}
39
- {{- $list.Set "row" true -}}
40
- {{- else if and (eq $tag "dl") $closing (gt (len $lists) 0) -}}
41
- {{- $list := index $lists (sub (len $lists) 1) -}}
42
- {{- if $list.Get "row" -}}{{- $parts = $parts | append "</div>" -}}{{- end -}}
43
- {{- $lists = first (sub (len $lists) 1) $lists -}}
7
+ {{- $result := $source -}}
8
+ {{- if findRE `(?i)<dl(?:\s|>)` $source -}}
9
+ {{- $pattern := `(?is)<!--.*?-->|<script\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</script\s*>|<style\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</style\s*>|<pre\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</pre\s*>|<textarea\b(?:[^"'<>]|"[^"]*"|'[^']*')*>.*?</textarea\s*>|<![^>]*>|</?[a-z][a-z0-9:-]*(?:[^"'<>]|"[^"]*"|'[^']*')*>|[^<]+|<` -}}
10
+ {{- $parts := slice -}}
11
+ {{- $lists := slice -}}
12
+ {{- $elements := slice -}}
13
+ {{- $tokens := findRE $pattern $source -}}
14
+ {{- range $i, $token := $tokens -}}
15
+ {{- if and (gt (len $lists) 0) (gt $i 0) (lt (add $i 1) (len $tokens)) (findRE `^\s+$` $token) (findRE `(?i)^</dt\s*>$` (index $tokens (sub $i 1))) (findRE `(?i)^<dd(?:\s|>)` (index $tokens (add $i 1))) -}}
16
+ {{- $token = "" -}}
44
17
  {{- end -}}
45
- {{- if $closing -}}
46
- {{- if gt (len $elements) 0 -}}{{- $elements = first (sub (len $elements) 1) $elements -}}{{- end -}}
47
- {{- else if not (or (findRE `/\s*>$` $token) (in (slice "area" "base" "br" "col" "embed" "hr" "img" "input" "link" "meta" "param" "source" "track" "wbr") $tag)) -}}
48
- {{- $elements = $elements | append $tag -}}
18
+ {{- if and (gt (len $lists) 0) (gt $i 0) (lt (add $i 1) (len $tokens)) (findRE `^\s+$` $token) (findRE `(?i)^<dd(?:\s[^>]*)?>$` (index $tokens (sub $i 1))) (findRE `(?i)^<p(?:\s|>)` (index $tokens (add $i 1))) -}}
19
+ {{- $token = "" -}}
20
+ {{- end -}}
21
+ {{- $tag := "" -}}
22
+ {{- $opaque := findRE `(?i)^<(?:!--|script\b|style\b|pre\b|textarea\b)` $token -}}
23
+ {{- if and (not $opaque) (findRE `(?i)^</?[a-z]` $token) -}}
24
+ {{- $tag = lower (replaceRE `(?is)^</?([a-z][a-z0-9:-]*).*` "$1" $token) -}}
25
+ {{- $closing := hasPrefix $token "</" -}}
26
+ {{- if and (eq $tag "dl") (not $closing) -}}
27
+ {{- $list := newScratch -}}
28
+ {{- $list.Set "row" false -}}
29
+ {{- $lists = $lists | append $list -}}
30
+ {{- if or (findRE `(?i)\sclass\s*=\s*["'](?:[^"']*\s)?dictionary(?:\s|["'])` $token) (findRE `(?i)\sclass\s*=\s*dictionary(?:\s|>)` $token) -}}
31
+ {{- else if findRE `(?i)\sclass\s*=\s*["']` $token -}}
32
+ {{- $token = replaceRE `(?i)(\sclass\s*=\s*["'])` "${1}dictionary " $token -}}
33
+ {{- else if findRE `(?i)\sclass\s*=` $token -}}
34
+ {{- $token = replaceRE `(?i)(\sclass\s*=\s*)([^\s>]+)` `${1}"dictionary ${2}"` $token -}}
35
+ {{- else -}}
36
+ {{- $token = replaceRE `(?i)^<dl` `<dl class="dictionary"` $token -}}
37
+ {{- end -}}
38
+ {{- else if and (eq $tag "dt") (not $closing) (gt (len $lists) 0) (eq (index $elements (sub (len $elements) 1)) "dl") -}}
39
+ {{- $list := index $lists (sub (len $lists) 1) -}}
40
+ {{- if $list.Get "row" -}}{{- $parts = $parts | append "</div>" -}}{{- end -}}
41
+ {{- $parts = $parts | append "<div>" -}}
42
+ {{- $list.Set "row" true -}}
43
+ {{- else if and (eq $tag "dl") $closing (gt (len $lists) 0) -}}
44
+ {{- $list := index $lists (sub (len $lists) 1) -}}
45
+ {{- if $list.Get "row" -}}{{- $parts = $parts | append "</div>" -}}{{- end -}}
46
+ {{- $lists = first (sub (len $lists) 1) $lists -}}
47
+ {{- end -}}
48
+ {{- if $closing -}}
49
+ {{- if gt (len $elements) 0 -}}{{- $elements = first (sub (len $elements) 1) $elements -}}{{- end -}}
50
+ {{- else if not (or (findRE `/\s*>$` $token) (in (slice "area" "base" "br" "col" "embed" "hr" "img" "input" "link" "meta" "param" "source" "track" "wbr") $tag)) -}}
51
+ {{- $elements = $elements | append $tag -}}
52
+ {{- end -}}
49
53
  {{- end -}}
54
+ {{- $parts = $parts | append $token -}}
50
55
  {{- end -}}
51
- {{- $parts = $parts | append $token -}}
56
+ {{- $result = delimit $parts "" -}}
52
57
  {{- end -}}
53
- {{- return (delimit $parts "" | safeHTML) -}}
58
+ {{- return ($result | safeHTML) -}}
package/manifest.mjs CHANGED
@@ -233,6 +233,9 @@ const MANIFEST_KEY_ORDER = Object.freeze([
233
233
  "download",
234
234
  ]);
235
235
 
236
+ /** The pack types Foundry requires to name a system. */
237
+ const SYSTEM_SPECIFIC_PACK_TYPES = Object.freeze(["ActiveEffect", "Actor", "Item"]);
238
+
236
239
  /**
237
240
  * The manifest's `packs`, derived from the one pack list the build already has.
238
241
  *
@@ -254,11 +257,12 @@ export function manifestPacks(config) {
254
257
  return config.packs.flatMap(flatten).map((pack) => {
255
258
  // Foundry requires `system` on ActiveEffect, Actor and Item packs and
256
259
  // on no others, so the value is per pack: its own declaration first,
257
- // then the package-wide one. With neither, the key is omitted — an
258
- // Adventure, Scene or JournalEntry pack that declares a system is
259
- // hidden from every other system, which is rarely what a package that
260
- // declined to name one meant.
261
- const system = pack.system ?? config.stats.systemId;
260
+ // then, for those three types only, the package-wide one. Any other
261
+ // pack that declares a system is hidden from every other system, so it
262
+ // carries one only when it names one itself.
263
+ const system =
264
+ pack.system ??
265
+ (SYSTEM_SPECIFIC_PACK_TYPES.includes(pack.type) ? config.stats.systemId : undefined);
262
266
  return {
263
267
  label: pack.label,
264
268
  type: pack.type,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heroiclands/package-build",
3
- "version": "23.12.1",
3
+ "version": "23.12.3",
4
4
  "description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "type": "module",