@heroiclands/package-build 23.11.0 → 23.12.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 +21 -0
- package/README.md +1 -0
- package/assets/preview/check-sanitizer.go +68 -0
- package/assets/preview/gitea-theme.js +36 -0
- package/assets/preview/gitea.ini +127 -0
- package/assets/styles/content-base.css +119 -16
- package/bin/content-commands.mjs +2 -2
- package/bin/package-build-preview.mjs +75 -0
- package/content-config.mjs +11 -0
- package/docs/api.md +105 -46
- package/docs/authoring/links-and-markup.md +46 -11
- package/docs/commands.md +46 -2
- package/docs/configuration.md +21 -4
- package/docs/design.md +4 -3
- package/docs/guides/book.md +10 -6
- package/docs/reference/format-details.md +33 -34
- package/docs/reference/note-types.md +0 -1
- package/docs/reference/theme-pages.md +1 -1
- package/docs/source-preview.md +172 -0
- package/engine/base-compiler.mjs +7 -8
- package/engine/being-age.mjs +19 -105
- package/engine/calendar-notes.mjs +3 -25
- package/engine/compile-corpus.mjs +2 -1
- package/engine/content-format.yaml +0 -1
- package/engine/content-index.mjs +9 -9
- package/engine/content-spans.mjs +28 -6
- package/engine/current-date.mjs +37 -0
- package/engine/definition-list.mjs +58 -0
- package/engine/frontmatter-lint.mjs +7 -2
- package/engine/helpers.mjs +2 -0
- package/engine/index.mjs +2 -0
- package/engine/infobox.mjs +1 -5
- package/engine/note-vocabulary.mjs +2 -2
- package/engine/npc-generate.mjs +7 -6
- package/engine/pdf-render.mjs +37 -4
- package/engine/preview-current-date.mjs +60 -0
- package/engine/site-build.mjs +5 -5
- package/engine/site-index.mjs +5 -2
- package/engine/site-preview.mjs +641 -325
- package/hugo-theme/layouts/_default/list.html +1 -1
- package/hugo-theme/layouts/blog/list.html +1 -1
- package/hugo-theme/layouts/blog-post/single.html +1 -1
- package/hugo-theme/layouts/partials/dictionary-lists.html +53 -0
- package/hugo-theme/layouts/partials/page-body.html +1 -1
- package/hugo-theme/static/css/style.css +22 -8
- package/package.json +8 -2
- package/types/content-config.d.mts +8 -0
- package/types/engine/base-compiler.d.mts +1 -1
- package/types/engine/being-age.d.mts +10 -44
- package/types/engine/calendar-notes.d.mts +2 -6
- package/types/engine/compile-corpus.d.mts +2 -1
- package/types/engine/content-index.d.mts +3 -1
- package/types/engine/content-spans.d.mts +4 -2
- package/types/engine/current-date.d.mts +9 -0
- package/types/engine/definition-list.d.mts +8 -0
- package/types/engine/frontmatter-lint.d.mts +6 -2
- package/types/engine/index.d.mts +2 -0
- package/types/engine/npc-generate.d.mts +2 -2
- package/types/engine/preview-current-date.d.mts +8 -0
- package/types/engine/site-index.d.mts +5 -1
- package/types/engine/site-preview.d.mts +40 -33
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 23.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
**Preview and layout**
|
|
8
|
+
|
|
9
|
+
- Preview Markdown buffers directly in light or dark themes, without building a content index; cross-note constructs remain highlighted source.
|
|
10
|
+
- Definition lists use bold terms and hanging definitions across websites, Foundry, preview and books, with a narrower definition column on small screens.
|
|
11
|
+
|
|
12
|
+
**Dates and spoilers**
|
|
13
|
+
|
|
14
|
+
- One setting-wide current date, defaulting to 720.1, drives age calculations; world-note dates are retired and authored ages are preserved.
|
|
15
|
+
- Secret spans in Foundry and preview redact until hover or keyboard focus; secret preview sections and fences open as spoilers.
|
|
16
|
+
- PDF inline secret spans print normally with their formatting; secret sections and fences retain labelled boxes.
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
**Alerts**
|
|
21
|
+
|
|
22
|
+
- Note, Tip, Important and Warning alerts have tinted backgrounds with coordinated icons, headings and borders, adapting to each surface's color scheme.
|
|
23
|
+
|
|
3
24
|
## 23.11.0
|
|
4
25
|
|
|
5
26
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -69,6 +69,7 @@ The documentation lives in the Markdown files in this repository. Start with [th
|
|
|
69
69
|
| Localization and templates | [Localization](docs/guides/localization.md) |
|
|
70
70
|
| Foundry integration testing | [End-to-end testing](docs/guides/e2e.md), including an optional S3 server |
|
|
71
71
|
| Release and deployment | [Release and deploy](docs/guides/release-and-deploy.md) |
|
|
72
|
+
| Buffer-only preview and Gitea rendering | [Source preview](docs/source-preview.md) |
|
|
72
73
|
| Every CLI command | [Commands](docs/commands.md) |
|
|
73
74
|
| Every build setting | [Configuration](docs/configuration.md) |
|
|
74
75
|
| Programmatic exports | [API](docs/api.md) |
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Copyright (c) 2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
2
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
// Replay the preview INI rules with the real bluemonday UGC sanitizer.
|
|
5
|
+
// Gitea also adds default rules; include the defaults needed by preview icons.
|
|
6
|
+
// This validates the shared policy primitives, not a deployed Gitea instance.
|
|
7
|
+
package main
|
|
8
|
+
|
|
9
|
+
import (
|
|
10
|
+
"bufio"
|
|
11
|
+
"fmt"
|
|
12
|
+
"io"
|
|
13
|
+
"os"
|
|
14
|
+
"regexp"
|
|
15
|
+
"strings"
|
|
16
|
+
|
|
17
|
+
"github.com/microcosm-cc/bluemonday"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
func main() {
|
|
21
|
+
if len(os.Args) != 2 {
|
|
22
|
+
panic("usage: check-sanitizer path/to/gitea.ini < preview.html")
|
|
23
|
+
}
|
|
24
|
+
file, err := os.Open(os.Args[1])
|
|
25
|
+
if err != nil {
|
|
26
|
+
panic(err)
|
|
27
|
+
}
|
|
28
|
+
defer file.Close()
|
|
29
|
+
policy := bluemonday.UGCPolicy()
|
|
30
|
+
// Gitea modules/markup/sanitizer_default.go permits these SVG attributes.
|
|
31
|
+
policy.AllowAttrs("viewBox", "width", "height", "aria-hidden", "data-attr-class").OnElements("svg")
|
|
32
|
+
policy.AllowAttrs("fill-rule", "d").OnElements("path")
|
|
33
|
+
policy.AllowAttrs("aria-hidden").OnElements("span", "div")
|
|
34
|
+
var section string
|
|
35
|
+
rule := map[string]string{}
|
|
36
|
+
flush := func() {
|
|
37
|
+
if !strings.HasPrefix(section, "markup.sanitizer.markdown.") {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
element, attr, pattern := rule["ELEMENT"], rule["ALLOW_ATTR"], rule["REGEXP"]
|
|
41
|
+
if element == "" || attr == "" || pattern == "" {
|
|
42
|
+
panic("incomplete sanitizer rule: " + section)
|
|
43
|
+
}
|
|
44
|
+
policy.AllowAttrs(attr).Matching(regexp.MustCompile(pattern)).OnElements(element)
|
|
45
|
+
}
|
|
46
|
+
scanner := bufio.NewScanner(file)
|
|
47
|
+
for scanner.Scan() {
|
|
48
|
+
line := strings.TrimSpace(scanner.Text())
|
|
49
|
+
if strings.HasPrefix(line, "[") {
|
|
50
|
+
flush()
|
|
51
|
+
section = strings.Trim(line, "[]")
|
|
52
|
+
rule = map[string]string{}
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
if key, value, ok := strings.Cut(line, "="); ok {
|
|
56
|
+
rule[strings.TrimSpace(key)] = strings.TrimSpace(value)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
flush()
|
|
60
|
+
if err := scanner.Err(); err != nil {
|
|
61
|
+
panic(err)
|
|
62
|
+
}
|
|
63
|
+
input, err := io.ReadAll(os.Stdin)
|
|
64
|
+
if err != nil {
|
|
65
|
+
panic(err)
|
|
66
|
+
}
|
|
67
|
+
fmt.Print(policy.Sanitize(string(input)))
|
|
68
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
+
*
|
|
5
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
+
*
|
|
8
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
+
*
|
|
11
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Installed by the Gitea administrator, outside user-authored rendered markup.
|
|
15
|
+
(() => {
|
|
16
|
+
const root = document.documentElement;
|
|
17
|
+
function update() {
|
|
18
|
+
const scheme = getComputedStyle(root).getPropertyValue("--is-dark-theme").trim();
|
|
19
|
+
// An unknown custom theme keeps the renderer's explicit initial mode.
|
|
20
|
+
if (scheme !== "true" && scheme !== "false") return;
|
|
21
|
+
for (const preview of document.querySelectorAll(".markup.markdown .hl-preview")) {
|
|
22
|
+
preview.classList.toggle("hl-preview-dark", scheme === "true");
|
|
23
|
+
preview.classList.toggle("hl-preview-light", scheme === "false");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
new MutationObserver(update).observe(root, {
|
|
27
|
+
attributes: true,
|
|
28
|
+
attributeFilter: ["data-theme"],
|
|
29
|
+
childList: true,
|
|
30
|
+
subtree: true,
|
|
31
|
+
});
|
|
32
|
+
// Theme stylesheets can finish loading after data-theme changes.
|
|
33
|
+
document.addEventListener("load", update, true);
|
|
34
|
+
document.addEventListener("DOMContentLoaded", update, { once: true });
|
|
35
|
+
update();
|
|
36
|
+
})();
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
[markup.markdown]
|
|
2
|
+
ENABLED = true
|
|
3
|
+
FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
|
|
4
|
+
RENDER_COMMAND = /opt/heroiclands/node_modules/.bin/package-build-preview
|
|
5
|
+
IS_INPUT_FILE = false
|
|
6
|
+
NEED_POST_PROCESS = false
|
|
7
|
+
RENDER_CONTENT_MODE = sanitized
|
|
8
|
+
|
|
9
|
+
[markup.sanitizer.markdown.previewDiv]
|
|
10
|
+
ELEMENT = div
|
|
11
|
+
ALLOW_ATTR = class
|
|
12
|
+
REGEXP = ^(hl-preview(?:-[a-z-]+)?|alert(?:-(?:note|tip|important|warning))?|content-figure(?:-[a-z-]+)?|poetry)( (hl-preview(?:-[a-z-]+)?|alert(?:-(?:note|tip|important|warning))?|content-figure(?:-[a-z-]+)?|poetry))*$
|
|
13
|
+
|
|
14
|
+
[markup.sanitizer.markdown.previewSpan]
|
|
15
|
+
ELEMENT = span
|
|
16
|
+
ALLOW_ATTR = class
|
|
17
|
+
REGEXP = ^(hl-preview-[a-z-]+|alert-(?:title|icon)|poetry-[a-z-]+|i[0-8])( (hl-preview-[a-z-]+|alert-(?:title|icon)|poetry-[a-z-]+|i[0-8]))*$
|
|
18
|
+
|
|
19
|
+
[markup.sanitizer.markdown.previewPre]
|
|
20
|
+
ELEMENT = pre
|
|
21
|
+
ALLOW_ATTR = class
|
|
22
|
+
REGEXP = ^hl-preview-(fence|dependent)$
|
|
23
|
+
|
|
24
|
+
[markup.sanitizer.markdown.previewCode]
|
|
25
|
+
ELEMENT = code
|
|
26
|
+
ALLOW_ATTR = class
|
|
27
|
+
REGEXP = ^(hl-preview-fence|language-[A-Za-z0-9_-]+)$
|
|
28
|
+
|
|
29
|
+
[markup.sanitizer.markdown.previewAside]
|
|
30
|
+
ELEMENT = aside
|
|
31
|
+
ALLOW_ATTR = class
|
|
32
|
+
REGEXP = ^hl-preview-infobox$
|
|
33
|
+
|
|
34
|
+
[markup.sanitizer.markdown.alertSvgFill]
|
|
35
|
+
ELEMENT = svg
|
|
36
|
+
ALLOW_ATTR = fill
|
|
37
|
+
REGEXP = ^currentColor$
|
|
38
|
+
|
|
39
|
+
[markup.sanitizer.markdown.alertCircleX]
|
|
40
|
+
ELEMENT = circle
|
|
41
|
+
ALLOW_ATTR = cx
|
|
42
|
+
REGEXP = ^[0-9.]+$
|
|
43
|
+
|
|
44
|
+
[markup.sanitizer.markdown.alertCircleY]
|
|
45
|
+
ELEMENT = circle
|
|
46
|
+
ALLOW_ATTR = cy
|
|
47
|
+
REGEXP = ^[0-9.]+$
|
|
48
|
+
|
|
49
|
+
[markup.sanitizer.markdown.alertCircleRadius]
|
|
50
|
+
ELEMENT = circle
|
|
51
|
+
ALLOW_ATTR = r
|
|
52
|
+
REGEXP = ^[0-9.]+$
|
|
53
|
+
|
|
54
|
+
[markup.sanitizer.markdown.previewMain]
|
|
55
|
+
ELEMENT = main
|
|
56
|
+
ALLOW_ATTR = class
|
|
57
|
+
REGEXP = ^hl-preview-main$
|
|
58
|
+
|
|
59
|
+
[markup.sanitizer.markdown.alertBox]
|
|
60
|
+
ELEMENT = blockquote
|
|
61
|
+
ALLOW_ATTR = class
|
|
62
|
+
REGEXP = ^alert alert-(note|tip|important|warning)$
|
|
63
|
+
|
|
64
|
+
[markup.sanitizer.markdown.previewParagraph]
|
|
65
|
+
ELEMENT = p
|
|
66
|
+
ALLOW_ATTR = class
|
|
67
|
+
REGEXP = ^(alert-title|content-figure-label)$
|
|
68
|
+
|
|
69
|
+
[markup.sanitizer.markdown.alertSvgClass]
|
|
70
|
+
ELEMENT = svg
|
|
71
|
+
ALLOW_ATTR = class
|
|
72
|
+
REGEXP = ^alert-icon$
|
|
73
|
+
|
|
74
|
+
[markup.sanitizer.markdown.previewDetails]
|
|
75
|
+
ELEMENT = details
|
|
76
|
+
ALLOW_ATTR = class
|
|
77
|
+
REGEXP = ^secret$
|
|
78
|
+
|
|
79
|
+
[markup.sanitizer.markdown.footnoteSection]
|
|
80
|
+
ELEMENT = section
|
|
81
|
+
ALLOW_ATTR = class
|
|
82
|
+
REGEXP = ^footnotes$
|
|
83
|
+
|
|
84
|
+
[markup.sanitizer.markdown.footnoteSeparator]
|
|
85
|
+
ELEMENT = hr
|
|
86
|
+
ALLOW_ATTR = class
|
|
87
|
+
REGEXP = ^footnotes-sep$
|
|
88
|
+
|
|
89
|
+
[markup.sanitizer.markdown.footnoteList]
|
|
90
|
+
ELEMENT = ol
|
|
91
|
+
ALLOW_ATTR = class
|
|
92
|
+
REGEXP = ^footnotes-list$
|
|
93
|
+
|
|
94
|
+
[markup.sanitizer.markdown.footnoteItem]
|
|
95
|
+
ELEMENT = li
|
|
96
|
+
ALLOW_ATTR = class
|
|
97
|
+
REGEXP = ^footnote-item$
|
|
98
|
+
|
|
99
|
+
[markup.sanitizer.markdown.footnoteLink]
|
|
100
|
+
ELEMENT = a
|
|
101
|
+
ALLOW_ATTR = class
|
|
102
|
+
REGEXP = ^footnote-(ref|backref)$
|
|
103
|
+
|
|
104
|
+
[markup.sanitizer.markdown.footnoteRef]
|
|
105
|
+
ELEMENT = sup
|
|
106
|
+
ALLOW_ATTR = class
|
|
107
|
+
REGEXP = ^footnote-ref$
|
|
108
|
+
|
|
109
|
+
[markup.sanitizer.markdown.tableHeaderAlign]
|
|
110
|
+
ELEMENT = th
|
|
111
|
+
ALLOW_ATTR = class
|
|
112
|
+
REGEXP = ^hl-preview-align-(left|center|right)$
|
|
113
|
+
|
|
114
|
+
[markup.sanitizer.markdown.tableCellAlign]
|
|
115
|
+
ELEMENT = td
|
|
116
|
+
ALLOW_ATTR = class
|
|
117
|
+
REGEXP = ^hl-preview-align-(left|center|right)$
|
|
118
|
+
|
|
119
|
+
[markup.sanitizer.markdown.dictionaryList]
|
|
120
|
+
ELEMENT = dl
|
|
121
|
+
ALLOW_ATTR = class
|
|
122
|
+
REGEXP = ^dictionary$
|
|
123
|
+
|
|
124
|
+
[markup.sanitizer.markdown.secretFocus]
|
|
125
|
+
ELEMENT = span
|
|
126
|
+
ALLOW_ATTR = tabindex
|
|
127
|
+
REGEXP = ^0$
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
/*
|
|
34
|
-
*
|
|
34
|
+
* Profile definition grids, in two states of one style.
|
|
35
35
|
*
|
|
36
36
|
* **Wide.** Two columns, the term right-aligned against a fixed gutter so every
|
|
37
37
|
* term in the list shares one right edge, the definition in the remaining
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
* The three tunable values are custom properties, so a package adjusts the term
|
|
59
59
|
* column, the gutter or the stacking point without copying the rule.
|
|
60
60
|
*/
|
|
61
|
-
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
61
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
62
|
+
dl:not(.dictionary) {
|
|
62
63
|
display: grid;
|
|
63
64
|
/* `minmax(0, …)` keeps the track at its full width wherever there is room
|
|
64
65
|
* and lets it shrink rather than overflow where there is not. */
|
|
@@ -68,7 +69,9 @@
|
|
|
68
69
|
container-name: pb-deflist;
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
72
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
73
|
+
dl:not(.dictionary)
|
|
74
|
+
> dt {
|
|
72
75
|
grid-column: 1;
|
|
73
76
|
/* The one rule that holds on every surface, so an author writes the term
|
|
74
77
|
* and nothing else rather than bolding it by hand. */
|
|
@@ -78,13 +81,17 @@
|
|
|
78
81
|
margin: 0;
|
|
79
82
|
}
|
|
80
83
|
|
|
81
|
-
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
84
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
85
|
+
dl:not(.dictionary)
|
|
86
|
+
> dd {
|
|
82
87
|
grid-column: 2;
|
|
83
88
|
margin: 0 0 var(--pb-deflist-row-gap, 1.5rem);
|
|
84
89
|
line-height: 1.6;
|
|
85
90
|
}
|
|
86
91
|
|
|
87
|
-
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
92
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
93
|
+
dl:not(.dictionary)
|
|
94
|
+
> dd:last-child {
|
|
88
95
|
margin-bottom: 0;
|
|
89
96
|
}
|
|
90
97
|
|
|
@@ -96,12 +103,16 @@
|
|
|
96
103
|
* against whichever ancestor container happens to be nearest.
|
|
97
104
|
*/
|
|
98
105
|
@container pb-deflist (max-width: 32rem) {
|
|
99
|
-
|
|
106
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
107
|
+
dl:not(.dictionary)
|
|
108
|
+
> dt {
|
|
100
109
|
grid-column: 1 / -1;
|
|
101
110
|
text-align: left;
|
|
102
111
|
}
|
|
103
112
|
|
|
104
|
-
|
|
113
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
114
|
+
dl:not(.dictionary)
|
|
115
|
+
> dd {
|
|
105
116
|
grid-column: 1 / -1;
|
|
106
117
|
}
|
|
107
118
|
}
|
|
@@ -205,20 +216,22 @@
|
|
|
205
216
|
}
|
|
206
217
|
}
|
|
207
218
|
|
|
208
|
-
/* Alerts
|
|
219
|
+
/* Alerts select a tinted palette from the host's color scheme. */
|
|
209
220
|
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert {
|
|
210
221
|
border-inline-start: 0.25rem solid var(--pb-alert-color);
|
|
211
|
-
|
|
222
|
+
border-radius: 0.375rem;
|
|
223
|
+
padding: 1rem;
|
|
212
224
|
margin: 1rem 0;
|
|
213
|
-
color:
|
|
214
|
-
|
|
225
|
+
color: #24292f;
|
|
226
|
+
color: light-dark(#24292f, #e6edf3);
|
|
227
|
+
background: var(--pb-alert-background);
|
|
215
228
|
}
|
|
216
229
|
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-title {
|
|
217
230
|
display: flex;
|
|
218
231
|
align-items: center;
|
|
219
232
|
gap: 0.5em;
|
|
220
233
|
margin: 0 0 0.5rem;
|
|
221
|
-
font-weight:
|
|
234
|
+
font-weight: 700;
|
|
222
235
|
color: var(--pb-alert-color);
|
|
223
236
|
}
|
|
224
237
|
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-icon {
|
|
@@ -234,17 +247,107 @@
|
|
|
234
247
|
}
|
|
235
248
|
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-note {
|
|
236
249
|
--pb-alert-color: #0969da;
|
|
237
|
-
--pb-alert-color: light-dark(#0969da, #
|
|
250
|
+
--pb-alert-color: light-dark(#0969da, #58a6ff);
|
|
251
|
+
--pb-alert-background: #f6faff;
|
|
252
|
+
--pb-alert-background: light-dark(#f6faff, #101a28);
|
|
238
253
|
}
|
|
239
254
|
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-tip {
|
|
240
255
|
--pb-alert-color: #1a7f37;
|
|
241
|
-
--pb-alert-color: light-dark(#1a7f37, #
|
|
256
|
+
--pb-alert-color: light-dark(#1a7f37, #7ee787);
|
|
257
|
+
--pb-alert-background: #f4fbf5;
|
|
258
|
+
--pb-alert-background: light-dark(#f4fbf5, #102018);
|
|
242
259
|
}
|
|
243
260
|
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-important {
|
|
244
|
-
--pb-alert-color: #
|
|
245
|
-
--pb-alert-color: light-dark(#
|
|
261
|
+
--pb-alert-color: #6c3fd1;
|
|
262
|
+
--pb-alert-color: light-dark(#6c3fd1, #c4b0ff);
|
|
263
|
+
--pb-alert-background: #faf7ff;
|
|
264
|
+
--pb-alert-background: light-dark(#faf7ff, #1c172a);
|
|
246
265
|
}
|
|
247
266
|
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .alert-warning {
|
|
248
267
|
--pb-alert-color: #9a6700;
|
|
249
268
|
--pb-alert-color: light-dark(#9a6700, #d29922);
|
|
269
|
+
--pb-alert-background: #fffaf0;
|
|
270
|
+
--pb-alert-background: light-dark(#fffaf0, #241d10);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/* Authored definition lists use dictionary rows; profile grids remain separate. */
|
|
274
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .dictionary {
|
|
275
|
+
--indent: 150px;
|
|
276
|
+
--gap: 0.75em;
|
|
277
|
+
margin: 0;
|
|
278
|
+
display: block;
|
|
279
|
+
}
|
|
280
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .dictionary > div {
|
|
281
|
+
padding-left: var(--indent);
|
|
282
|
+
text-indent: calc(-1 * var(--indent));
|
|
283
|
+
margin-bottom: 0.5em;
|
|
284
|
+
}
|
|
285
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
286
|
+
.dictionary
|
|
287
|
+
> div
|
|
288
|
+
> dt {
|
|
289
|
+
display: inline-block;
|
|
290
|
+
min-width: calc(var(--indent) - var(--gap));
|
|
291
|
+
margin-right: var(--gap);
|
|
292
|
+
font-weight: bold;
|
|
293
|
+
white-space: nowrap;
|
|
294
|
+
text-indent: 0;
|
|
295
|
+
text-align: left;
|
|
296
|
+
}
|
|
297
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
298
|
+
.dictionary
|
|
299
|
+
> div
|
|
300
|
+
> dd {
|
|
301
|
+
display: inline;
|
|
302
|
+
margin: 0;
|
|
303
|
+
font-weight: normal;
|
|
304
|
+
}
|
|
305
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
306
|
+
.dictionary
|
|
307
|
+
> div
|
|
308
|
+
> dd
|
|
309
|
+
> :is(p, ul, ol, dl, blockquote, pre, table, div) {
|
|
310
|
+
text-indent: 0;
|
|
311
|
+
}
|
|
312
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
313
|
+
.dictionary
|
|
314
|
+
> div
|
|
315
|
+
> dd
|
|
316
|
+
> p:first-child {
|
|
317
|
+
display: inline;
|
|
318
|
+
}
|
|
319
|
+
@media (max-width: 600px) {
|
|
320
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror) .dictionary {
|
|
321
|
+
--indent: 100px;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* Inline spoilers reveal on pointer hover or keyboard focus. Native secret
|
|
326
|
+
* sections retain Foundry's separate GM reveal control. */
|
|
327
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
328
|
+
span.secret[data-inline-spoiler] {
|
|
329
|
+
cursor: help;
|
|
330
|
+
border-radius: 0.15em;
|
|
331
|
+
}
|
|
332
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
333
|
+
span.secret[data-inline-spoiler]:not(:hover):not(:focus):not(:focus-within) {
|
|
334
|
+
color: transparent !important;
|
|
335
|
+
-webkit-text-fill-color: transparent !important;
|
|
336
|
+
background-color: var(--pb-redaction-fill, #24292f) !important;
|
|
337
|
+
text-shadow: none !important;
|
|
338
|
+
text-decoration-color: transparent !important;
|
|
339
|
+
}
|
|
340
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
341
|
+
span.secret[data-inline-spoiler]:not(:hover):not(:focus):not(:focus-within)
|
|
342
|
+
* {
|
|
343
|
+
color: transparent !important;
|
|
344
|
+
-webkit-text-fill-color: transparent !important;
|
|
345
|
+
visibility: hidden !important;
|
|
346
|
+
text-shadow: none !important;
|
|
347
|
+
text-decoration-color: transparent !important;
|
|
348
|
+
}
|
|
349
|
+
:is(.journal-entry-page, .journal-entry-content, .editor-content, .prosemirror)
|
|
350
|
+
span.secret[data-inline-spoiler]:focus-visible {
|
|
351
|
+
outline: 2px solid currentColor;
|
|
352
|
+
outline-offset: 2px;
|
|
250
353
|
}
|
package/bin/content-commands.mjs
CHANGED
|
@@ -144,7 +144,6 @@ import { GRAPHVIZ_ENGINES } from "../engine/map-graphviz.mjs";
|
|
|
144
144
|
import { loadMapWorld } from "../engine/map-places.mjs";
|
|
145
145
|
import { encodeAddresses } from "../engine/address-values.mjs";
|
|
146
146
|
import YAML from "yaml";
|
|
147
|
-
import { worldInvariants } from "../engine/calendar-notes.mjs";
|
|
148
147
|
import { reckoningContext } from "../engine/reckoning-markers.mjs";
|
|
149
148
|
import { BEING_LOOK_VOCABULARY } from "../engine/note-vocabulary.mjs";
|
|
150
149
|
import { profileFindings } from "../engine/appearance-profile.mjs";
|
|
@@ -1054,6 +1053,7 @@ function lintCommand() {
|
|
|
1054
1053
|
);
|
|
1055
1054
|
}
|
|
1056
1055
|
const frontmatter = lintFrontmatter(index, {
|
|
1056
|
+
currentDate: config.currentDate,
|
|
1057
1057
|
schemas: { ...ENGINE_NOTE_SCHEMAS, ...NOTE_SCHEMAS },
|
|
1058
1058
|
// The closed frontmatter regions. Passed in rather
|
|
1059
1059
|
// than reached for, so the linter stays a checker of
|
|
@@ -2905,7 +2905,7 @@ function newNpc(config, argv) {
|
|
|
2905
2905
|
names,
|
|
2906
2906
|
language: languageKey,
|
|
2907
2907
|
seed: argv.seed,
|
|
2908
|
-
|
|
2908
|
+
currentDate: config.currentDate,
|
|
2909
2909
|
dateContext: reckoningContext(index),
|
|
2910
2910
|
defaults,
|
|
2911
2911
|
taken,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
4
|
+
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
5
|
+
*
|
|
6
|
+
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
7
|
+
* You may copy, modify, and distribute it under the terms of that license.
|
|
8
|
+
*
|
|
9
|
+
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
10
|
+
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
11
|
+
*
|
|
12
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Render stdin with optional date-only configuration and no content index. */
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
import { formatDiagnostic } from "../engine/diagnostics.mjs";
|
|
18
|
+
|
|
19
|
+
const HELP = `Usage: package-build-preview [--dark] [--current-date DATE] [--css]
|
|
20
|
+
|
|
21
|
+
Read Markdown from stdin and write an HTML fragment to stdout.
|
|
22
|
+
No package configuration, content tree or content index is required.
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
--dark Select the dark palette (default: light).
|
|
26
|
+
--current-date DATE Override the setting's neutral current date (default: 720.1).
|
|
27
|
+
--css Write shared scoped CSS instead of HTML; does not read stdin.
|
|
28
|
+
--help, -h Show this help.
|
|
29
|
+
|
|
30
|
+
Findings go to stderr. Exit status: 0 for success, 1 for errors.
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
let dark = false;
|
|
35
|
+
let css = false;
|
|
36
|
+
let help = false;
|
|
37
|
+
let currentDate;
|
|
38
|
+
const args = process.argv.slice(2);
|
|
39
|
+
for (let index = 0; index < args.length; index++) {
|
|
40
|
+
const arg = args[index];
|
|
41
|
+
if (arg === "--dark") dark = true;
|
|
42
|
+
else if (arg === "--current-date") {
|
|
43
|
+
currentDate = args[++index];
|
|
44
|
+
if (!currentDate || currentDate.startsWith("--"))
|
|
45
|
+
throw new Error("--current-date requires a neutral date");
|
|
46
|
+
} else if (arg === "--css") css = true;
|
|
47
|
+
else if (arg === "--help" || arg === "-h") help = true;
|
|
48
|
+
else
|
|
49
|
+
throw new Error(
|
|
50
|
+
`${arg.startsWith("-") ? "Unknown option" : "Unexpected argument"}: ${arg}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (help) process.stdout.write(HELP);
|
|
54
|
+
else {
|
|
55
|
+
const { renderPreview, previewStyles, readPreviewCurrentDate } =
|
|
56
|
+
await import("../engine/site-preview.mjs");
|
|
57
|
+
if (css) process.stdout.write(`${previewStyles()}\n`);
|
|
58
|
+
else {
|
|
59
|
+
currentDate ??= await readPreviewCurrentDate(process.cwd());
|
|
60
|
+
const result = await renderPreview(readFileSync(0, "utf8"), {
|
|
61
|
+
dark,
|
|
62
|
+
file: "<stdin>",
|
|
63
|
+
currentDate,
|
|
64
|
+
});
|
|
65
|
+
for (const finding of result.findings) {
|
|
66
|
+
process.stderr.write(`${formatDiagnostic(finding)}\n`);
|
|
67
|
+
}
|
|
68
|
+
process.stdout.write(result.html);
|
|
69
|
+
if (!result.ok) process.exitCode = 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
process.stderr.write(`package-build-preview: ${error.message}\n`);
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
}
|
package/content-config.mjs
CHANGED
|
@@ -65,6 +65,7 @@ import fs from "node:fs";
|
|
|
65
65
|
import path from "node:path";
|
|
66
66
|
import { fileURLToPath } from "node:url";
|
|
67
67
|
import YAML from "yaml";
|
|
68
|
+
import { normalizeCurrentDate } from "./engine/current-date.mjs";
|
|
68
69
|
|
|
69
70
|
// Leaves with no local imports of their own, so naming them here cannot close
|
|
70
71
|
// a cycle around a consumer's config file (see `engine/pack-config.mjs`).
|
|
@@ -569,6 +570,7 @@ export function publishesContentPages(config) {
|
|
|
569
570
|
* repository — every configured path is
|
|
570
571
|
* resolved against it, so the build never
|
|
571
572
|
* depends on the working directory.
|
|
573
|
+
* @property {string} [currentDate] Setting-wide neutral current date; defaults to "720.1".
|
|
572
574
|
* @property {string} contentPackage Content package name — the address
|
|
573
575
|
* namespace every note in this
|
|
574
576
|
* repository is published under.
|
|
@@ -643,6 +645,7 @@ export function publishesContentPages(config) {
|
|
|
643
645
|
* The normalized, frozen configuration the toolchain reads.
|
|
644
646
|
*
|
|
645
647
|
* @typedef {object} ContentBuildConfig
|
|
648
|
+
* @property {string} currentDate Setting-wide neutral current date.
|
|
646
649
|
* @property {string} rootDir
|
|
647
650
|
* @property {string} contentPackage
|
|
648
651
|
* @property {string|null} foundryPackage `null` for a `documentation`
|
|
@@ -726,6 +729,7 @@ export function publishesContentPages(config) {
|
|
|
726
729
|
const CONFIG_KEYS = [
|
|
727
730
|
"rootDir",
|
|
728
731
|
"contentPackage",
|
|
732
|
+
"currentDate",
|
|
729
733
|
"foundryPackage",
|
|
730
734
|
"homepage",
|
|
731
735
|
"authors",
|
|
@@ -2943,6 +2947,13 @@ export function defineConfig(config) {
|
|
|
2943
2947
|
|
|
2944
2948
|
return Object.freeze({
|
|
2945
2949
|
rootDir,
|
|
2950
|
+
currentDate: (() => {
|
|
2951
|
+
try {
|
|
2952
|
+
return normalizeCurrentDate(input.currentDate);
|
|
2953
|
+
} catch (error) {
|
|
2954
|
+
fail("currentDate", error.message);
|
|
2955
|
+
}
|
|
2956
|
+
})(),
|
|
2946
2957
|
contentPackage: requireContentPackage(input.contentPackage, docEntryTypes, {
|
|
2947
2958
|
rootDir,
|
|
2948
2959
|
packageKind,
|