@rathnasgala2/template 3.0.1 → 3.1.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/README.md +13 -11
- package/contracts/render-policy.jcs +1 -1
- package/package.json +4 -3
- package/src/core/index.js +74 -9
- package/src/core/internal/appearance/base-layer.js +81 -0
- package/src/core/internal/content-security.js +0 -0
- package/src/core/internal/eleventy-render.js +5 -3
- package/src/core/internal/icons.js +2 -0
- package/src/core/internal/interactions.js +349 -0
- package/src/core/internal/messages.js +6 -0
- package/src/core/internal/page-kinds.js +24 -1
- package/src/core/internal/render-policy-content.js +40 -5
- package/src/modules/interactions/browser/gala-interactions.js +1891 -0
- package/types/index.d.ts +30 -2
package/README.md
CHANGED
|
@@ -551,7 +551,7 @@ npm run format # apply Prettier
|
|
|
551
551
|
npm run format:check # CI check
|
|
552
552
|
npm run lint # ESLint flat config
|
|
553
553
|
npm run typecheck # tsc --checkJs --noEmit against types/index.d.ts
|
|
554
|
-
npm run architecture # dependency-cruiser:
|
|
554
|
+
npm run architecture # dependency-cruiser: core never imports src/modules, no circular deps
|
|
555
555
|
npm run duplication # jscpd, 3% / 50 tokens
|
|
556
556
|
npm run contracts:generate # (re)write contracts/*.jcs from their reviewed source modules
|
|
557
557
|
npm run contracts:check # fail if committed contracts drifted
|
|
@@ -569,23 +569,25 @@ npm run audit # npm audit --audit-level=high
|
|
|
569
569
|
## Package layout
|
|
570
570
|
|
|
571
571
|
```text
|
|
572
|
-
src/core/ # the
|
|
572
|
+
src/core/ # the renderer
|
|
573
|
+
src/modules/interactions/browser/ # the reader-interactions browser script (emitted as data)
|
|
573
574
|
contracts/render-policy.jcs # normalized-body policy identity (real content)
|
|
574
575
|
contracts/theme-styling-contract.jcs # published styling catalog (real and closed)
|
|
575
576
|
```
|
|
576
577
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
`
|
|
581
|
-
`
|
|
582
|
-
|
|
583
|
-
|
|
578
|
+
`src/core/` and `src/modules/interactions/` are the only source roots, and
|
|
579
|
+
`src/core` never imports from `src/modules`: the interactions browser script is
|
|
580
|
+
read from disk and emitted as `assets/gala-interactions-v1.js` when
|
|
581
|
+
`build-input.modules.interactions` is present.
|
|
582
|
+
`test/module-tree-absence.test.js`, `.dependency-cruiser.cjs` and the ESLint
|
|
583
|
+
`no-restricted-imports` rule enforce this. `whitelabel`, `newsletter` and
|
|
584
|
+
`prism` have no module directory, package, configuration schema, stub, output or
|
|
585
|
+
runtime code in this MVP.
|
|
584
586
|
|
|
585
587
|
## Consuming `@rathnasgala2/schemas`
|
|
586
588
|
|
|
587
|
-
This repository consumes `@rathnasgala2/schemas@3.
|
|
588
|
-
registry, declared as `"@rathnasgala2/schemas": "3.
|
|
589
|
+
This repository consumes `@rathnasgala2/schemas@3.1.0` from the public npm
|
|
590
|
+
registry, declared as `"@rathnasgala2/schemas": "3.1.0"` (exact pin, no range)
|
|
589
591
|
so `package-lock.json` records the resolved version and integrity hash. The
|
|
590
592
|
LOCAL-1/LOCAL-39 local-tarball convention
|
|
591
593
|
(`file:../../local-packages/rathnasgala2-schemas-*.tgz`) is retired for this
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"contentSecurityPolicy":"default-src 'none'; base-uri 'none'; object-src 'none'; frame-ancestors 'none'; form-action 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; media-src 'self'; manifest-src 'self'; worker-src 'none'","contentSecurityPolicyHeaderOnlyDirectives":["frame-ancestors 'none'"],"contentSecurityPolicyMeta":"default-src 'none'; base-uri 'none'; object-src 'none'; form-action 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; media-src 'self'; manifest-src 'self'; worker-src 'none'","highlighter":{"grammars":["bash","css","diff","java","javascript","json","jsx","markdown","markup","python","sql","tsx","typescript","yaml"],"package":"@11ty/eleventy-plugin-syntaxhighlight","tokenClasses":["atrule","attr-equals","attr-name","attr-value","bold","boolean","builtin","cdata","char","class-name","comment","constant","content","coord","deleted","deleted-sign","doctype","entity","for-or-select","function","function-variable","important","inserted","inserted-sign","interpolation","interpolation-punctuation","italic","key","keyword","namespace","null","number","operator","prefix","prolog","property","punctuation","regex","rule","selector","shebang","string","symbol","tag","template-punctuation","template-string","title","token","url","variable"],"version":"5.0.2"},"markdownIt":{"options":{"html":false,"linkify":false,"typographer":false},"package":"markdown-it","version":"14.3.1"},"name":"gala-render-policy","sanitizer":{"allowedAttributes":{"a":["href","rel"],"code":["class"],"h1":["id"],"h2":["id"],"h3":["id"],"h4":["id"],"h5":["id"],"h6":["id"],"img":["src","alt"],"ol":["start"],"pre":["class"],"span":["class"]},"allowedTags":["p","br","hr","h1","h2","h3","h4","h5","h6","ul","ol","li","blockquote","pre","code","strong","em","b","i","u","s","del","ins","sub","sup","mark","small","wbr","a","img","table","thead","tbody","tr","th","td","dl","dt","dd","figure","figcaption","span"],"allowedUrlSchemes":["https","http","mailto"],"package":"sanitize-html","version":"2.17.7"},"version":"2.0.0"}
|
|
1
|
+
{"contentSecurityPolicy":"default-src 'none'; base-uri 'none'; object-src 'none'; frame-ancestors 'none'; form-action 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self' https://api.galascribe.com; media-src 'self'; manifest-src 'self'; worker-src 'none'","contentSecurityPolicyHeaderOnlyDirectives":["frame-ancestors 'none'"],"contentSecurityPolicyMeta":"default-src 'none'; base-uri 'none'; object-src 'none'; form-action 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self' https://api.galascribe.com; media-src 'self'; manifest-src 'self'; worker-src 'none'","highlighter":{"grammars":["bash","css","diff","java","javascript","json","jsx","markdown","markup","python","sql","tsx","typescript","yaml"],"package":"@11ty/eleventy-plugin-syntaxhighlight","tokenClasses":["atrule","attr-equals","attr-name","attr-value","bold","boolean","builtin","cdata","char","class-name","comment","constant","content","coord","deleted","deleted-sign","doctype","entity","for-or-select","function","function-variable","important","inserted","inserted-sign","interpolation","interpolation-punctuation","italic","key","keyword","namespace","null","number","operator","prefix","prolog","property","punctuation","regex","rule","selector","shebang","string","symbol","tag","template-punctuation","template-string","title","token","url","variable"],"version":"5.0.2"},"markdownIt":{"options":{"html":false,"linkify":false,"typographer":false},"package":"markdown-it","version":"14.3.1"},"name":"gala-render-policy","sanitizer":{"allowedAttributes":{"a":["href","rel"],"code":["class"],"h1":["id"],"h2":["id"],"h3":["id"],"h4":["id"],"h5":["id"],"h6":["id"],"img":["src","alt"],"ol":["start"],"pre":["class"],"span":["class"]},"allowedTags":["p","br","hr","h1","h2","h3","h4","h5","h6","ul","ol","li","blockquote","pre","code","strong","em","b","i","u","s","del","ins","sub","sup","mark","small","wbr","a","img","table","thead","tbody","tr","th","td","dl","dt","dd","figure","figcaption","span"],"allowedUrlSchemes":["https","http","mailto"],"package":"sanitize-html","version":"2.17.7"},"version":"2.0.0"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rathnasgala2/template",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Deterministic Eleventy-backed static publication renderer for Galascribe portable repositories.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"types": "./types/index.d.ts",
|
|
23
23
|
"files": [
|
|
24
24
|
"src/core/",
|
|
25
|
+
"src/modules/",
|
|
25
26
|
"contracts/",
|
|
26
27
|
"types/",
|
|
27
28
|
"LICENSE",
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
"format": "prettier --write .",
|
|
53
54
|
"format:check": "prettier --check .",
|
|
54
55
|
"lint": "eslint .",
|
|
55
|
-
"typecheck": "tsc --project tsconfig.json",
|
|
56
|
+
"typecheck": "tsc --project tsconfig.json && tsc --project tsconfig.modules.json",
|
|
56
57
|
"architecture": "depcruise --validate .dependency-cruiser.cjs src contracts eslint.config.js",
|
|
57
58
|
"duplication": "jscpd src scripts test eslint.config.js",
|
|
58
59
|
"contracts:generate": "node scripts/generate-contracts.mjs",
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
"dependencies": {
|
|
72
73
|
"@11ty/eleventy": "3.1.6",
|
|
73
74
|
"@11ty/eleventy-plugin-syntaxhighlight": "5.0.2",
|
|
74
|
-
"@rathnasgala2/schemas": "3.
|
|
75
|
+
"@rathnasgala2/schemas": "3.1.0",
|
|
75
76
|
"jpeg-js": "0.4.4",
|
|
76
77
|
"markdown-it": "14.3.1",
|
|
77
78
|
"sanitize-html": "2.17.7"
|
package/src/core/index.js
CHANGED
|
@@ -83,8 +83,16 @@ import {
|
|
|
83
83
|
assertPolicyConformantHtml,
|
|
84
84
|
assertRenderPolicyIdentity,
|
|
85
85
|
computeRenderPolicyIdentity,
|
|
86
|
+
contentSecurityPolicyMetaTag,
|
|
86
87
|
} from './internal/content-security.js';
|
|
87
88
|
import { renderPagesWithEleventy } from './internal/eleventy-render.js';
|
|
89
|
+
import {
|
|
90
|
+
INTERACTIONS_SCRIPT_MEDIA_TYPE,
|
|
91
|
+
INTERACTIONS_SCRIPT_PATH,
|
|
92
|
+
interactionsActive,
|
|
93
|
+
loadInteractionsScript,
|
|
94
|
+
validateInteractionsModule,
|
|
95
|
+
} from './internal/interactions.js';
|
|
88
96
|
import {
|
|
89
97
|
ATOM_FEED_ROUTE,
|
|
90
98
|
RSS_FEED_ROUTE,
|
|
@@ -241,8 +249,9 @@ function assertOptions(options) {
|
|
|
241
249
|
* Verify every `renderableBody.renderPolicy` in the publication profile,
|
|
242
250
|
* footer card and every content record byte-equals the currently published
|
|
243
251
|
* render-policy identity, and that no module or placement value leaked
|
|
244
|
-
* through (belt-and-suspenders on top of the `build-input
|
|
245
|
-
*
|
|
252
|
+
* through (belt-and-suspenders on top of the `build-input` schema's module
|
|
253
|
+
* closure: only a valid `interactions` module is admitted, `placements` stays
|
|
254
|
+
* empty). Fails closed before any
|
|
246
255
|
* directory is created or Eleventy ever runs: an absent policy file, wrong
|
|
247
256
|
* path/name/version/digest, mixed identity between records, or body
|
|
248
257
|
* produced under a different policy rejects before build.
|
|
@@ -252,14 +261,20 @@ function assertOptions(options) {
|
|
|
252
261
|
* @returns {Promise<void>} resolves once every reference is verified
|
|
253
262
|
*/
|
|
254
263
|
async function assertRenderPolicyCompliance(validatedInput) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
validatedInput.placements.length !== 0
|
|
258
|
-
) {
|
|
264
|
+
const moduleKeys = Object.keys(validatedInput.modules);
|
|
265
|
+
if (moduleKeys.some((key) => key !== 'interactions')) {
|
|
259
266
|
throw new RenderPolicyViolationError(
|
|
260
|
-
'build-input.modules
|
|
267
|
+
'build-input.modules may only carry interactions',
|
|
261
268
|
);
|
|
262
269
|
}
|
|
270
|
+
if (validatedInput.placements.length !== 0) {
|
|
271
|
+
throw new RenderPolicyViolationError(
|
|
272
|
+
'build-input.placements must be empty',
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
if (validatedInput.modules.interactions !== undefined) {
|
|
276
|
+
validateInteractionsModule(validatedInput.modules.interactions);
|
|
277
|
+
}
|
|
263
278
|
|
|
264
279
|
const identity = await computeRenderPolicyIdentity();
|
|
265
280
|
|
|
@@ -343,6 +358,17 @@ export async function renderPublication(buildInput, options) {
|
|
|
343
358
|
validatedInput.baseUrl,
|
|
344
359
|
validatedInput.basePath,
|
|
345
360
|
);
|
|
361
|
+
const interactionsModule = validatedInput.modules.interactions;
|
|
362
|
+
const interactionsOn = interactionsActive(interactionsModule);
|
|
363
|
+
// One CSP per build: the production policy, plus (local stack only) the
|
|
364
|
+
// interactions module's differing API origin in `connect-src`.
|
|
365
|
+
const cspMetaTag = contentSecurityPolicyMetaTag(
|
|
366
|
+
interactionsModule?.apiOrigin,
|
|
367
|
+
);
|
|
368
|
+
const interactionsScriptHref = joinBasePathAndRoute(
|
|
369
|
+
publicBasePath,
|
|
370
|
+
`/${INTERACTIONS_SCRIPT_PATH}`,
|
|
371
|
+
);
|
|
346
372
|
const urlPrefix = derivePublicBasePath(validatedInput.baseUrl, '/');
|
|
347
373
|
const homeRoute = joinPublicRoute(publicBasePath, '/');
|
|
348
374
|
|
|
@@ -500,7 +526,10 @@ export async function renderPublication(buildInput, options) {
|
|
|
500
526
|
const generatedPages = buildGeneratedPages(validatedInput, {
|
|
501
527
|
mediaAssets,
|
|
502
528
|
mediaDimensions,
|
|
529
|
+
interactions: interactionsOn ? interactionsModule : undefined,
|
|
503
530
|
});
|
|
531
|
+
/** @type {Set<string>} output paths of the interaction-bearing routes */
|
|
532
|
+
const interactionBearingPaths = new Set();
|
|
504
533
|
generatedPages.forEach((page, index) => {
|
|
505
534
|
const joinedRoute = joinBasePathAndRoute(
|
|
506
535
|
validatedInput.basePath,
|
|
@@ -520,10 +549,12 @@ export async function renderPublication(buildInput, options) {
|
|
|
520
549
|
basePath: publicBasePath,
|
|
521
550
|
});
|
|
522
551
|
const ogImage = ogImageInfo?.url;
|
|
552
|
+
const pagePermalink = projectRouteToFilePath(joinedRoute, routeProfile);
|
|
553
|
+
if (page.interactionBearing) interactionBearingPaths.add(pagePermalink);
|
|
523
554
|
contentPages.push({
|
|
524
555
|
virtualPath: `pages/${page.kind}-${index}.html`,
|
|
525
556
|
content: bodyHtml,
|
|
526
|
-
permalink:
|
|
557
|
+
permalink: pagePermalink,
|
|
527
558
|
layout: 'skeleton.njk',
|
|
528
559
|
data: {
|
|
529
560
|
title: page.documentTitle ?? page.title,
|
|
@@ -552,6 +583,10 @@ export async function renderPublication(buildInput, options) {
|
|
|
552
583
|
atomFeedUrl: feeds.atomSelfUrl,
|
|
553
584
|
rssFeedUrl: feeds.rssSelfUrl,
|
|
554
585
|
appearanceScriptHref: appearanceScriptHrefValue,
|
|
586
|
+
interactionsScriptHref: page.interactionBearing
|
|
587
|
+
? interactionsScriptHref
|
|
588
|
+
: undefined,
|
|
589
|
+
cspMetaTag,
|
|
555
590
|
baseStylesheetHref: baseStylesheetHrefValue,
|
|
556
591
|
themeStylesheetLinksHtml,
|
|
557
592
|
},
|
|
@@ -623,6 +658,7 @@ export async function renderPublication(buildInput, options) {
|
|
|
623
658
|
atomFeedUrl: feeds.atomSelfUrl,
|
|
624
659
|
rssFeedUrl: feeds.rssSelfUrl,
|
|
625
660
|
appearanceScriptHref: appearanceScriptHrefValue,
|
|
661
|
+
cspMetaTag,
|
|
626
662
|
baseStylesheetHref: baseStylesheetHrefValue,
|
|
627
663
|
themeStylesheetLinksHtml,
|
|
628
664
|
},
|
|
@@ -767,7 +803,7 @@ export async function renderPublication(buildInput, options) {
|
|
|
767
803
|
byteLength: String(bytes.byteLength),
|
|
768
804
|
sha256: digestBytes(bytes),
|
|
769
805
|
routeClass: special?.routeClass ?? 'html',
|
|
770
|
-
interactionBearing:
|
|
806
|
+
interactionBearing: interactionBearingPaths.has(filePath),
|
|
771
807
|
});
|
|
772
808
|
}
|
|
773
809
|
routes.sort((a, b) => compareUtf8Bytes(a.path, b.path));
|
|
@@ -897,10 +933,39 @@ export async function renderPublication(buildInput, options) {
|
|
|
897
933
|
immutable: false,
|
|
898
934
|
};
|
|
899
935
|
|
|
936
|
+
// The reader-interactions browser script, only when the module is on. Its
|
|
937
|
+
// bytes are the template package's own file, so the asset row is a pure
|
|
938
|
+
// function of the template version.
|
|
939
|
+
/** @type {import('../../types/index.d.ts').ManifestAssetEntry[]} */
|
|
940
|
+
const interactionsAssets = [];
|
|
941
|
+
if (interactionsOn) {
|
|
942
|
+
const interactionsScriptBytes = await loadInteractionsScript();
|
|
943
|
+
const joinedInteractionsScriptPath = projectFixedAssetPath(
|
|
944
|
+
validatedInput.basePath,
|
|
945
|
+
`/${INTERACTIONS_SCRIPT_PATH}`,
|
|
946
|
+
);
|
|
947
|
+
const interactionsScriptDestination = path.join(
|
|
948
|
+
outputDirectory,
|
|
949
|
+
joinedInteractionsScriptPath,
|
|
950
|
+
);
|
|
951
|
+
await mkdir(path.dirname(interactionsScriptDestination), {
|
|
952
|
+
recursive: true,
|
|
953
|
+
});
|
|
954
|
+
await writeFile(interactionsScriptDestination, interactionsScriptBytes);
|
|
955
|
+
interactionsAssets.push({
|
|
956
|
+
path: joinedInteractionsScriptPath,
|
|
957
|
+
mediaType: INTERACTIONS_SCRIPT_MEDIA_TYPE,
|
|
958
|
+
byteLength: String(interactionsScriptBytes.byteLength),
|
|
959
|
+
sha256: digestBytes(interactionsScriptBytes),
|
|
960
|
+
immutable: false,
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
900
964
|
const assets = [
|
|
901
965
|
...joinedMediaAssets,
|
|
902
966
|
appearanceScriptAsset,
|
|
903
967
|
baseStylesheetAsset,
|
|
968
|
+
...interactionsAssets,
|
|
904
969
|
...(themeAssetsResult ? themeAssetsResult.assets : []),
|
|
905
970
|
].sort((a, b) => compareUtf8Bytes(a.path, b.path));
|
|
906
971
|
|
|
@@ -654,6 +654,83 @@ export const GALA_BASE_COMPONENT_CSS = `
|
|
|
654
654
|
[aria-selected="true"] > .g-search-hit > .g-icon { opacity: 1; color: var(--gala-color-accent); }
|
|
655
655
|
.g-search-empty { padding: var(--gala-space-6) var(--gala-space-4); color: var(--gala-color-text-muted); font-family: var(--gala-font-ui); text-align: center; }
|
|
656
656
|
|
|
657
|
+
/* reader interactions: server-rendered section and the classes the browser script creates */
|
|
658
|
+
.g-interactions { display: grid; gap: var(--gala-space-4); min-width: 0; padding: var(--gala-space-6); border-radius: var(--gala-radius-large); background: var(--gala-color-surface); border: var(--gala-border-card); font-family: var(--gala-font-ui); }
|
|
659
|
+
.g-interactions__title { font-family: var(--gala-font-display); font-weight: var(--gala-weight-title); font-size: 1.25rem; letter-spacing: var(--gala-tracking-title); text-transform: var(--gala-title-transform); line-height: 1.2; }
|
|
660
|
+
.g-interactions__notice, .g-interactions__status { font-size: .875rem; color: var(--gala-color-text-muted); overflow-wrap: anywhere; }
|
|
661
|
+
.g-interactions__status:empty { display: none; }
|
|
662
|
+
.g-interactions__signout { display: inline; padding: 0; border: 0; background: none; font: inherit; color: var(--gala-color-link); text-decoration: underline; text-underline-offset: var(--gala-link-offset); cursor: pointer; }
|
|
663
|
+
.g-interactions__signout:hover { color: var(--gala-color-text); }
|
|
664
|
+
.g-reactions { display: flex; flex-wrap: wrap; gap: var(--gala-space-2); }
|
|
665
|
+
.g-reaction {
|
|
666
|
+
display: inline-flex; align-items: center; gap: .5em; min-height: 2.75rem; max-width: 100%; padding: .45rem .95rem;
|
|
667
|
+
border-radius: var(--gala-radius-pill); border: var(--gala-border-width) solid var(--gala-color-border); background: var(--gala-color-canvas); color: var(--gala-color-text-muted);
|
|
668
|
+
font-family: var(--gala-font-ui); font-weight: var(--gala-weight-ui); font-size: .9rem; line-height: 1.2; cursor: pointer;
|
|
669
|
+
transition: transform var(--gala-duration-fast) var(--gala-ease-standard), border-color var(--gala-duration-fast) var(--gala-ease-standard), color var(--gala-duration-fast) var(--gala-ease-standard), background-color var(--gala-duration-fast) var(--gala-ease-standard);
|
|
670
|
+
}
|
|
671
|
+
.g-reaction:hover:not(:disabled) { border-color: var(--gala-color-text); color: var(--gala-color-text); }
|
|
672
|
+
.g-reaction:active:not(:disabled) { transform: scale(.96); }
|
|
673
|
+
.g-reaction:disabled { opacity: .55; cursor: default; }
|
|
674
|
+
.g-reaction__icon { display: inline-grid; place-items: center; font-size: 1.1rem; line-height: 1; }
|
|
675
|
+
.g-reaction__icon .g-icon { width: 1.15rem; height: 1.15rem; }
|
|
676
|
+
.g-reaction__label { overflow-wrap: anywhere; }
|
|
677
|
+
.g-reaction__count { font-variant-numeric: tabular-nums; font-size: .8rem; color: var(--gala-color-text-faint); padding-left: var(--gala-space-2); border-left: var(--gala-border-width) solid var(--gala-color-border); }
|
|
678
|
+
.g-reaction__count:empty { display: none; }
|
|
679
|
+
.g-reaction[aria-pressed="true"], .g-reaction--active { border-color: var(--gala-color-accent); background: var(--gala-paint-chip); color: var(--gala-color-chip-text); }
|
|
680
|
+
.g-reaction[aria-pressed="true"] .g-icon, .g-reaction--active .g-icon { fill: currentColor; color: var(--gala-color-accent); }
|
|
681
|
+
.g-comments { display: grid; gap: var(--gala-space-4); min-width: 0; padding-top: var(--gala-space-4); border-top: var(--gala-border-width) solid var(--gala-color-border); }
|
|
682
|
+
.g-comments__title { font-family: var(--gala-font-display); font-weight: var(--gala-weight-title); font-size: 1.05rem; }
|
|
683
|
+
.g-comments__count { font-family: var(--gala-font-ui); font-weight: var(--gala-weight-normal); font-size: .875rem; color: var(--gala-color-text-faint); font-variant-numeric: tabular-nums; }
|
|
684
|
+
.g-comments__more {
|
|
685
|
+
justify-self: start; min-height: 2.75rem; padding: .5rem 1.1rem; border-radius: var(--gala-radius-pill); border: var(--gala-border-width) solid var(--gala-color-border); background: transparent; color: var(--gala-color-text);
|
|
686
|
+
font-family: var(--gala-font-ui); font-weight: var(--gala-weight-ui); font-size: .9rem; cursor: pointer;
|
|
687
|
+
transition: border-color var(--gala-duration-fast) var(--gala-ease-standard), transform var(--gala-duration-fast) var(--gala-ease-standard);
|
|
688
|
+
}
|
|
689
|
+
.g-comments__more:hover { border-color: var(--gala-color-text); }
|
|
690
|
+
.g-comments__more:active { transform: scale(.97); }
|
|
691
|
+
.g-composer { display: grid; gap: var(--gala-space-3); min-width: 0; }
|
|
692
|
+
.g-composer label { font-size: .875rem; font-weight: var(--gala-weight-ui); color: var(--gala-color-text-muted); }
|
|
693
|
+
.g-composer__input, .g-report select, .g-report textarea {
|
|
694
|
+
width: 100%; max-width: 100%; min-height: 2.75rem; padding: .7rem .9rem; border-radius: var(--gala-radius-medium); border: var(--gala-border-width) solid var(--gala-color-border);
|
|
695
|
+
background: var(--gala-color-canvas); color: var(--gala-color-text); font-family: var(--gala-font-body); font-size: 1rem; line-height: 1.5;
|
|
696
|
+
}
|
|
697
|
+
.g-composer__input, .g-report textarea { min-height: 6rem; resize: vertical; }
|
|
698
|
+
.g-composer__input:hover, .g-report select:hover, .g-report textarea:hover { border-color: var(--gala-color-text-muted); }
|
|
699
|
+
.g-composer__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gala-space-3); justify-content: space-between; font-size: .875rem; color: var(--gala-color-text-muted); }
|
|
700
|
+
.g-composer__submit, .g-composer__signin, .g-report button[type="submit"] {
|
|
701
|
+
display: inline-flex; align-items: center; justify-content: center; gap: .55em; min-height: 2.75rem; padding: .6rem 1.2rem; border-radius: var(--gala-radius-pill);
|
|
702
|
+
background: var(--gala-paint-button); color: var(--gala-color-btn-text); border: var(--gala-border-button); box-shadow: var(--gala-shadow-button);
|
|
703
|
+
font-family: var(--gala-font-ui); font-weight: var(--gala-weight-ui); font-size: .9375rem; cursor: pointer;
|
|
704
|
+
transition: transform var(--gala-duration-fast) var(--gala-ease-standard), filter var(--gala-duration-base) var(--gala-ease-standard);
|
|
705
|
+
}
|
|
706
|
+
.g-composer__submit:hover:not(:disabled), .g-composer__signin:hover, .g-report button[type="submit"]:hover { filter: brightness(1.07) saturate(1.05); }
|
|
707
|
+
.g-composer__submit:active:not(:disabled), .g-composer__signin:active { transform: scale(.97); }
|
|
708
|
+
.g-composer__submit:disabled { opacity: .55; cursor: default; box-shadow: none; }
|
|
709
|
+
.g-composer__cancel, .g-report button[type="button"] {
|
|
710
|
+
min-height: 2.75rem; padding: .6rem 1rem; border-radius: var(--gala-radius-pill); border: var(--gala-border-width) solid var(--gala-color-border); background: transparent; color: var(--gala-color-text);
|
|
711
|
+
font-family: var(--gala-font-ui); font-weight: var(--gala-weight-ui); font-size: .9rem; cursor: pointer;
|
|
712
|
+
}
|
|
713
|
+
.g-composer__cancel:hover, .g-report button[type="button"]:hover { border-color: var(--gala-color-text); }
|
|
714
|
+
.g-comment-list { display: grid; gap: var(--gala-space-4); min-width: 0; }
|
|
715
|
+
.g-comment { display: grid; gap: var(--gala-space-2); min-width: 0; }
|
|
716
|
+
.g-comment__head { display: flex; align-items: center; gap: var(--gala-space-3); min-width: 0; }
|
|
717
|
+
.g-comment__avatar { display: inline-grid; place-items: center; width: 2.25rem; height: 2.25rem; flex: none; border-radius: var(--gala-radius-avatar); background: var(--gala-paint-button); color: var(--gala-color-btn-text); box-shadow: var(--gala-shadow-avatar-ring); font-family: var(--gala-font-display); font-weight: var(--gala-weight-display); font-size: .8rem; text-transform: uppercase; }
|
|
718
|
+
.g-comment__author { font-weight: var(--gala-weight-ui); font-size: .9375rem; overflow-wrap: anywhere; min-width: 0; }
|
|
719
|
+
.g-comment__time { font-size: .8125rem; color: var(--gala-color-text-faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
720
|
+
.g-comment__body { margin: 0; font-family: var(--gala-font-body); font-size: 1rem; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
721
|
+
.g-comment__body a { color: var(--gala-color-link); text-decoration: underline; text-decoration-color: var(--gala-color-link-underline); text-underline-offset: var(--gala-link-offset); }
|
|
722
|
+
.g-comment__body a:hover { text-decoration-color: var(--gala-color-link-underline-hover); }
|
|
723
|
+
.g-comment__actions { display: flex; flex-wrap: wrap; gap: var(--gala-space-1) var(--gala-space-3); }
|
|
724
|
+
.g-comment__action { min-height: 2.5rem; padding: 0 .5rem; margin-left: -.5rem; border: 0; border-radius: var(--gala-radius-small); background: transparent; color: var(--gala-color-text-muted); font-family: var(--gala-font-ui); font-weight: var(--gala-weight-ui); font-size: .8125rem; cursor: pointer; transition: color var(--gala-duration-fast) var(--gala-ease-standard), background-color var(--gala-duration-fast) var(--gala-ease-standard); }
|
|
725
|
+
.g-comment__action:hover { color: var(--gala-color-text); background: var(--gala-color-surface-raised); }
|
|
726
|
+
.g-comment__replies { display: grid; gap: var(--gala-space-4); min-width: 0; margin-top: var(--gala-space-2); padding-left: var(--gala-space-4); border-left: var(--gala-border-width) solid var(--gala-color-border); }
|
|
727
|
+
.g-comment--tombstone > .g-comment__body { font-style: italic; color: var(--gala-color-text-faint); }
|
|
728
|
+
.g-comment--mine > .g-comment__head .g-comment__avatar { box-shadow: 0 0 0 2px var(--gala-color-accent); }
|
|
729
|
+
.g-report { width: min(28rem, calc(100% - 2rem)); max-width: 100%; padding: var(--gala-space-6); border-radius: var(--gala-radius-large); border: var(--gala-border-card); background: var(--gala-color-surface); color: var(--gala-color-text); box-shadow: var(--gala-shadow-dialog); font-family: var(--gala-font-ui); }
|
|
730
|
+
.g-report::backdrop { background: var(--gala-color-overlay); }
|
|
731
|
+
.g-report form { display: grid; gap: var(--gala-space-3); }
|
|
732
|
+
.g-report menu { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--gala-space-3); margin: 0; padding: 0; }
|
|
733
|
+
|
|
657
734
|
/* toast */
|
|
658
735
|
.g-toast {
|
|
659
736
|
position: fixed; left: 50%; bottom: var(--gala-space-6); z-index: 60; max-width: calc(100% - 2rem); padding: .7rem 1.1rem;
|
|
@@ -709,6 +786,10 @@ export const GALA_BASE_COMPONENT_CSS = `
|
|
|
709
786
|
.g-article-cover img { aspect-ratio: 16 / 10; }
|
|
710
787
|
.g-footer-grid { grid-template-columns: minmax(0, 1fr); }
|
|
711
788
|
.g-author-card { flex-direction: column; }
|
|
789
|
+
.g-interactions { padding: var(--gala-space-4); }
|
|
790
|
+
.g-comment__replies { padding-left: var(--gala-space-3); }
|
|
791
|
+
.g-composer__actions { flex-direction: column; align-items: stretch; }
|
|
792
|
+
.g-composer__submit, .g-composer__signin { width: 100%; }
|
|
712
793
|
.g-topic-head { align-items: flex-start; }
|
|
713
794
|
}
|
|
714
795
|
|
|
Binary file
|
|
@@ -38,7 +38,6 @@ import {
|
|
|
38
38
|
APPEARANCE_SERVER_DEFAULT_RESOLVED_MODE,
|
|
39
39
|
COLOR_SCHEME_META_CONTENT,
|
|
40
40
|
} from './appearance/contract.js';
|
|
41
|
-
import { contentSecurityPolicyMetaTag } from './content-security.js';
|
|
42
41
|
import { PAGE_KIND_ATTRIBUTE } from './page-kinds.js';
|
|
43
42
|
|
|
44
43
|
/**
|
|
@@ -133,9 +132,12 @@ const SKELETON_LAYOUT_SOURCE = [
|
|
|
133
132
|
'<head>',
|
|
134
133
|
'<meta charset="utf-8">',
|
|
135
134
|
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
136
|
-
|
|
135
|
+
'{{ cspMetaTag | safe }}',
|
|
137
136
|
`<meta name="color-scheme" content="${COLOR_SCHEME_META_CONTENT}">`,
|
|
138
|
-
|
|
137
|
+
// The interactions script (article pages, module on) is deferred and sits
|
|
138
|
+
// on the appearance script's line, so a page without it is byte-identical
|
|
139
|
+
// to before.
|
|
140
|
+
'<script src="{{ appearanceScriptHref }}"></script>{% if interactionsScriptHref %}<script src="{{ interactionsScriptHref }}" defer></script>{% endif %}',
|
|
139
141
|
'<title>{{ title }}</title>',
|
|
140
142
|
'{% if description %}<meta name="description" content="{{ description }}">{% endif %}',
|
|
141
143
|
'{% if robotsContent %}<meta name="robots" content="{{ robotsContent }}">{% endif %}',
|
|
@@ -35,6 +35,8 @@ export const ICONS = Object.freeze({
|
|
|
35
35
|
'<path d="M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z"/><path d="M19 16l.7 2 2 .7-2 .7-.7 2-.7-2-2-.7 2-.7z"/>',
|
|
36
36
|
list: '<path d="M9 6h11M9 12h11M9 18h11M4 6h.01M4 12h.01M4 18h.01"/>',
|
|
37
37
|
layers: '<path d="m12 3 9 5-9 5-9-5z"/><path d="m3 13 9 5 9-5"/>',
|
|
38
|
+
heart:
|
|
39
|
+
'<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7z"/>',
|
|
38
40
|
pen: '<path d="M4 20h4L19 9l-4-4L4 16z"/><path d="m13 7 4 4"/>',
|
|
39
41
|
});
|
|
40
42
|
|