@stnd/layout 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Base.astro +17 -13
- package/CHANGELOG.md +12 -0
- package/Encrypted.astro +12 -0
- package/Footer.astro +54 -46
- package/Header.astro +16 -7
- package/Meta.astro +85 -30
- package/README.md +6 -3
- package/StndInit.astro +27 -2
- package/package.json +7 -4
package/Base.astro
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
/**
|
|
3
|
-
* @
|
|
3
|
+
* @component Base
|
|
4
|
+
* @description The definitive root layout for all Standard Garden
|
|
5
|
+
* websites — `<html>`/`<head>`/`<body>`, theme/color-mode/language
|
|
6
|
+
* resolution, and the header/footer slots every page gets by default.
|
|
4
7
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* backpack — App-level extensions (DropZone, Graft, etc.). Hydrated.
|
|
13
|
-
*
|
|
14
|
-
* To exclude any of these, use `moduleExclude` in your astro.config:
|
|
15
|
-
* standard({ moduleExclude: ["@stnd/modules/confetti"] })
|
|
8
|
+
* System-layer components (Toast, Confetti, Lab) are not hardcoded here —
|
|
9
|
+
* they're registered as Gold Standard modules and injected via `StndInit`'s
|
|
10
|
+
* hook zones: `stnd:base` (static Astro components, no hydration) and
|
|
11
|
+
* `stnd:client` (hydrated Svelte components). To exclude one, use
|
|
12
|
+
* `moduleExclude` in `astro.config`:
|
|
13
|
+
* `standard({ moduleExclude: ["@stnd/modules/confetti"] })`. See
|
|
14
|
+
* [Hooks & Extension Points](/manual/modules/hooks) for the full list.
|
|
16
15
|
*/
|
|
17
16
|
|
|
18
17
|
import "virtual:stnd/styles";
|
|
@@ -26,21 +25,26 @@ import LauncherHint from "@stnd/ui/LauncherHint.astro";
|
|
|
26
25
|
import { ClientRouter } from "astro:transitions";
|
|
27
26
|
|
|
28
27
|
interface Props {
|
|
28
|
+
/** Page title — passed straight through to `<Meta>`. Defaults to the site's own config title. */
|
|
29
29
|
title?: string;
|
|
30
30
|
description?: string;
|
|
31
|
+
/** Social-share image URL. */
|
|
31
32
|
image?: string;
|
|
32
33
|
created?: string;
|
|
33
34
|
modified?: string;
|
|
34
35
|
tags?: string[];
|
|
36
|
+
/** Theme name — defaults to the visitor's saved preference, then the site's system default. */
|
|
35
37
|
theme?: string;
|
|
36
38
|
lang?: string;
|
|
39
|
+
/** Skip the default header/footer entirely (still renders `<html>`/`<head>`/`<body>`). */
|
|
37
40
|
hideLayout?: boolean;
|
|
38
41
|
class?: string;
|
|
39
42
|
inlineStyle?: string;
|
|
43
|
+
/** Extra raw markup injected into `<head>`. */
|
|
40
44
|
headExtras?: string;
|
|
41
45
|
visibility?: "public" | "private" | "unlisted";
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
/** Forwarded to `<Header>` — brand text/link and nav items. */
|
|
44
48
|
header?: {
|
|
45
49
|
brand?: string;
|
|
46
50
|
brandHref?: string;
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @stnd/layout
|
|
2
2
|
|
|
3
|
+
## 0.5.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Auto-bumped @stnd/layout due to modified code.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
- Updated dependencies
|
|
11
|
+
- @stnd/lab@1.0.2
|
|
12
|
+
- @stnd/log@0.5.1
|
|
13
|
+
- @stnd/ui@0.5.2
|
|
14
|
+
|
|
3
15
|
## 0.5.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/Encrypted.astro
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
---
|
|
2
|
+
/**
|
|
3
|
+
* @component Encrypted
|
|
4
|
+
* @description A standalone password-gate page layout — not composed with
|
|
5
|
+
* `<Base>`, renders its own full `<html>`. Shows a password form; on
|
|
6
|
+
* submit, XORs the decoded `encryptedData` against a SHA-256 hash of the
|
|
7
|
+
* typed password entirely client-side (no server round-trip) and, if the
|
|
8
|
+
* result looks like real HTML, replaces the form with it in place. Wrong
|
|
9
|
+
* guesses are rate-limited to 5 attempts per page load.
|
|
10
|
+
*
|
|
11
|
+
* @property {Object} [site] - Site info; only `site.language` is read (sets `<html lang>`, default "en").
|
|
12
|
+
* @property {string} encryptedData - Base64-encoded, XOR-"encrypted" HTML payload to reveal on success.
|
|
13
|
+
*/
|
|
2
14
|
import "virtual:stnd/styles";
|
|
3
15
|
|
|
4
16
|
const { site = {}, encryptedData } = Astro.props;
|
package/Footer.astro
CHANGED
|
@@ -1,30 +1,38 @@
|
|
|
1
1
|
---
|
|
2
2
|
/**
|
|
3
|
-
* Footer
|
|
3
|
+
* @component Footer
|
|
4
|
+
* @description Generic, flexible site footer. Reads defaults from
|
|
5
|
+
* `virtual:stnd/config` (title, navigation) so it works with zero props;
|
|
6
|
+
* a `grid-8` layout (3 columns brand/info, 5 for custom link columns),
|
|
7
|
+
* responsive out of the box. Slots: `brand`, `description`, `copyright`,
|
|
8
|
+
* `bottom-left`, `bottom-right`, and default (extra link columns).
|
|
4
9
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* <Footer title="My Garden">
|
|
14
|
-
* <div class="col-1">
|
|
15
|
-
* <h4>Section</h4>
|
|
16
|
-
* <ul><li><a href="/">Home</a></li></ul>
|
|
17
|
-
* </div>
|
|
18
|
-
* </Footer>
|
|
10
|
+
* @example astro
|
|
11
|
+
* <Footer />
|
|
12
|
+
* <Footer title="My Garden">
|
|
13
|
+
* <div class="col-1">
|
|
14
|
+
* <h4>Section</h4>
|
|
15
|
+
* <ul><li><a href="/">Home</a></li></ul>
|
|
16
|
+
* </div>
|
|
17
|
+
* </Footer>
|
|
19
18
|
*/
|
|
20
19
|
import config from "virtual:stnd/config";
|
|
21
20
|
|
|
22
21
|
interface Props {
|
|
22
|
+
/** Brand name — defaults to the site config's own title, then "Home". */
|
|
23
23
|
title?: string;
|
|
24
24
|
class?: string;
|
|
25
|
+
hideBrand?: boolean;
|
|
26
|
+
/** Hide the nav column entirely (built from `config.nav.footer` / `config.nav.header`). */
|
|
27
|
+
hideNav?: boolean;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
const {
|
|
30
|
+
const {
|
|
31
|
+
title = config.title || "Home",
|
|
32
|
+
class: className,
|
|
33
|
+
hideBrand = false,
|
|
34
|
+
hideNav = false,
|
|
35
|
+
} = Astro.props;
|
|
28
36
|
|
|
29
37
|
const footerNav =
|
|
30
38
|
config.nav?.footer ??
|
|
@@ -35,36 +43,36 @@ const year = new Date().getFullYear();
|
|
|
35
43
|
|
|
36
44
|
<footer class:list={["stnd-footer", "compact", className]}>
|
|
37
45
|
<div class="grid-8 sm:grid-1">
|
|
38
|
-
<!-- Column 1: Brand & Info
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
46
|
+
<!-- Column 1: Brand & Info -->
|
|
47
|
+
{
|
|
48
|
+
!hideBrand && (
|
|
49
|
+
<div class="col-2 sm:col-1">
|
|
50
|
+
<h4 class="footer-title mb-2">
|
|
51
|
+
<a href={config.url} class="no-decoration">{title}</a>
|
|
52
|
+
</h4>
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
footerNav.length > 0 && !hideNav && (
|
|
56
|
+
<ul class="no-bullet">
|
|
57
|
+
{footerNav.map((item: any) => (
|
|
58
|
+
<li>
|
|
59
|
+
<a href={item.url}>{item.title}</a>
|
|
60
|
+
</li>
|
|
61
|
+
))}
|
|
62
|
+
</ul>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
{
|
|
67
|
+
config.description && (
|
|
68
|
+
<p class="text-2xs mt-half compact">{config.description}</p>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
<p class="text-2xs mt-half">© {year}.</p>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
68
76
|
|
|
69
77
|
<!-- Column 2: Navigation -->
|
|
70
78
|
{/*
|
package/Header.astro
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
---
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* @component Header
|
|
4
|
+
* @description Minimalist responsive header — brand on the left, nav menu on
|
|
5
|
+
* the right, hamburger on mobile (below 768px). Fixed to the viewport top;
|
|
6
|
+
* hides on scroll-down and reappears on scroll-up when `startHidden` is set.
|
|
7
|
+
* Reads nav defaults from `virtual:stnd/config`. Slots: `brand` (replaces the
|
|
8
|
+
* default brand link), `actions` (page-level buttons — Share/Edit/etc. —
|
|
9
|
+
* rendered before the burger, visible regardless of `menu`/`hideMenu`),
|
|
10
|
+
* `right` (extra content after the nav menu), and two viewport-fixed slots
|
|
11
|
+
* rendered as siblings of the header itself — `bottom-left` / `bottom-right`.
|
|
4
12
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* <Header />
|
|
10
|
-
* <Header brand="@stnd" items={[{ title: "Docs", url: "/docs" }]} />
|
|
13
|
+
* @example astro
|
|
14
|
+
* <Header />
|
|
15
|
+
* <Header brand="@stnd" items={[{ title: "Docs", url: "/docs" }]} />
|
|
11
16
|
*/
|
|
12
17
|
import config from "virtual:stnd/config";
|
|
13
18
|
|
|
14
19
|
interface Props {
|
|
20
|
+
/** Brand text shown in the default `brand` slot. Defaults to the site config's own title, then "Home". */
|
|
15
21
|
brand?: string;
|
|
22
|
+
/** Link target for the default brand. Default: "https://standard.garden/" */
|
|
16
23
|
brandHref?: string;
|
|
24
|
+
/** Nav items — defaults to `config.nav.header` (or `config.nav` if it's an array). */
|
|
17
25
|
items?: Array<{ title: string; url: string; external?: boolean }>;
|
|
18
26
|
/** When false, the burger button and nav menu are not rendered. Default: true */
|
|
19
27
|
menu?: boolean;
|
|
@@ -84,6 +92,7 @@ const renderedItems = safeItems.map((item) => {
|
|
|
84
92
|
<a href={brandHref} class="stnd-header-brand">{brand}</a>
|
|
85
93
|
</slot>
|
|
86
94
|
<div class="flex items-center gap-half">
|
|
95
|
+
<slot name="actions" />
|
|
87
96
|
{
|
|
88
97
|
menuEnabled && (
|
|
89
98
|
<button
|
package/Meta.astro
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* @component Meta
|
|
4
|
+
* @description Universal `<head>` meta tags — canonical link, description,
|
|
5
|
+
* Open Graph, Twitter Cards, Schema.org JSON-LD, favicons, theme-color, and
|
|
6
|
+
* mobile/Windows tile tags. Self-contained and zero-config: reads site
|
|
7
|
+
* defaults from `virtual:stnd/config`, with every prop overriding its
|
|
8
|
+
* matching config value for the current page only.
|
|
4
9
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* <Meta /> — site defaults from config
|
|
10
|
-
* <Meta title="My Page" /> — override title
|
|
11
|
-
* <Meta title="Article" type="article" created="2024-01-01" />
|
|
10
|
+
* @example astro
|
|
11
|
+
* <Meta />
|
|
12
|
+
* <Meta title="My Page" />
|
|
13
|
+
* <Meta title="Article" type="article" created="2024-01-01" />
|
|
12
14
|
*/
|
|
13
15
|
import config from "virtual:stnd/config";
|
|
14
16
|
|
|
@@ -23,25 +25,33 @@ interface Props {
|
|
|
23
25
|
modified?: string;
|
|
24
26
|
tags?: string[];
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
/** Controls `og:type` and the Schema.org `@type` (article → BlogPosting, profile → ProfilePage, else WebSite). Default: "website" */
|
|
27
29
|
type?: "website" | "article" | "profile";
|
|
28
30
|
|
|
29
31
|
// Appearance
|
|
32
|
+
/** `theme-color` meta tag. A single string is used for both color schemes; pass `{light, dark}` to vary by `prefers-color-scheme`. */
|
|
30
33
|
themeColor?: string | { light: string; dark: string };
|
|
34
|
+
/** `msapplication-TileColor` for Windows tiles. Default: "#ffffff" */
|
|
31
35
|
tileColor?: string;
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
/** "private"/"unlisted" emit `noindex, nofollow`; "public" (default) allows indexing. */
|
|
34
38
|
visibility?: "public" | "private" | "unlisted";
|
|
35
39
|
|
|
36
40
|
// Optional features
|
|
37
41
|
rssFeedUrl?: string | null;
|
|
38
42
|
rssFeedTitle?: string;
|
|
43
|
+
/** Path prefix for the generated favicon package (.ico, PNG sizes, browserconfig.xml). Default: "/assets/favicon" */
|
|
39
44
|
faviconPath?: string;
|
|
45
|
+
/**
|
|
46
|
+
* A single image URL used as *the* site icon, replacing the whole
|
|
47
|
+
* `faviconPath` set. Takes precedence over `faviconPath` when both are given.
|
|
48
|
+
* Modern browsers are happy with one SVG or PNG; use `faviconPath` when you
|
|
49
|
+
* have a full generated package (.ico, apple-touch-icon, browserconfig…).
|
|
50
|
+
*/
|
|
51
|
+
favicon?: string | null;
|
|
40
52
|
headExtensions?: string | string[];
|
|
41
53
|
|
|
42
|
-
|
|
43
|
-
// constant — pass this when the actual per-request origin, e.g. a
|
|
44
|
-
// user's verified custom domain, differs from the build's default site).
|
|
54
|
+
/** Absolute origin overriding `Astro.site` (a build-time constant) — pass this when the actual per-request origin, e.g. a user's verified custom domain, differs from the build's default site. */
|
|
45
55
|
siteUrl?: string;
|
|
46
56
|
}
|
|
47
57
|
|
|
@@ -61,6 +71,7 @@ const {
|
|
|
61
71
|
rssFeedUrl,
|
|
62
72
|
rssFeedTitle,
|
|
63
73
|
faviconPath,
|
|
74
|
+
favicon,
|
|
64
75
|
headExtensions,
|
|
65
76
|
siteUrl: propsSiteUrl,
|
|
66
77
|
} = Astro.props as Props;
|
|
@@ -111,7 +122,30 @@ const dateModified = modified
|
|
|
111
122
|
: null;
|
|
112
123
|
|
|
113
124
|
// ── Favicon ──
|
|
125
|
+
// A single `favicon` image replaces the generated package entirely. Anything
|
|
126
|
+
// else falls back to the `faviconPath` set (.ico + PNG sizes + browserconfig).
|
|
114
127
|
const faviconBase = faviconPath || "/assets/favicon";
|
|
128
|
+
const singleFavicon =
|
|
129
|
+
typeof favicon === "string" && favicon.trim() !== "" ? favicon.trim() : null;
|
|
130
|
+
|
|
131
|
+
const FAVICON_MIME = {
|
|
132
|
+
svg: "image/svg+xml",
|
|
133
|
+
png: "image/png",
|
|
134
|
+
ico: "image/x-icon",
|
|
135
|
+
gif: "image/gif",
|
|
136
|
+
jpg: "image/jpeg",
|
|
137
|
+
jpeg: "image/jpeg",
|
|
138
|
+
webp: "image/webp",
|
|
139
|
+
avif: "image/avif",
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// The extension can carry a query string (`/cdn/logo.svg?v=2`); strip it before
|
|
143
|
+
// matching, and leave `type` off entirely when the URL gives us nothing to go on.
|
|
144
|
+
const singleFaviconType = singleFavicon
|
|
145
|
+
? (FAVICON_MIME[
|
|
146
|
+
(singleFavicon.split(/[?#]/)[0].split(".").pop() || "").toLowerCase()
|
|
147
|
+
] ?? undefined)
|
|
148
|
+
: undefined;
|
|
115
149
|
|
|
116
150
|
// ── Theme colors ──
|
|
117
151
|
const resolvedThemeColor =
|
|
@@ -259,19 +293,34 @@ const headExtraHtml = Array.isArray(headExtensions)
|
|
|
259
293
|
/>
|
|
260
294
|
|
|
261
295
|
<!-- Favicons -->
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
<
|
|
270
|
-
<link
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
296
|
+
{
|
|
297
|
+
singleFavicon ? (
|
|
298
|
+
<Fragment>
|
|
299
|
+
<link rel="icon" type={singleFaviconType} href={singleFavicon} />
|
|
300
|
+
<link rel="apple-touch-icon" href={singleFavicon} />
|
|
301
|
+
</Fragment>
|
|
302
|
+
) : (
|
|
303
|
+
<Fragment>
|
|
304
|
+
<link
|
|
305
|
+
rel="icon"
|
|
306
|
+
type="image/png"
|
|
307
|
+
href={`${faviconBase}/favicon-96x96.png`}
|
|
308
|
+
sizes="96x96"
|
|
309
|
+
/>
|
|
310
|
+
<link
|
|
311
|
+
rel="icon"
|
|
312
|
+
type="image/svg+xml"
|
|
313
|
+
href={`${faviconBase}/favicon.svg`}
|
|
314
|
+
/>
|
|
315
|
+
<link rel="shortcut icon" href={`${faviconBase}/favicon.ico`} />
|
|
316
|
+
<link
|
|
317
|
+
rel="apple-touch-icon"
|
|
318
|
+
sizes="180x180"
|
|
319
|
+
href={`${faviconBase}/apple-touch-icon.png`}
|
|
320
|
+
/>
|
|
321
|
+
</Fragment>
|
|
322
|
+
)
|
|
323
|
+
}
|
|
275
324
|
<meta name="apple-mobile-web-app-title" content={siteTitle || title || ""} />
|
|
276
325
|
<link rel="manifest" href="/site.webmanifest" />
|
|
277
326
|
|
|
@@ -294,7 +343,13 @@ const headExtraHtml = Array.isArray(headExtensions)
|
|
|
294
343
|
|
|
295
344
|
<!-- Windows Tiles -->
|
|
296
345
|
<meta name="msapplication-TileColor" content={resolvedTileColor} />
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
346
|
+
{
|
|
347
|
+
/* Only meaningful alongside the generated package — a single `favicon`
|
|
348
|
+
has no browserconfig.xml to point at. */
|
|
349
|
+
!singleFavicon && (
|
|
350
|
+
<meta
|
|
351
|
+
name="msapplication-config"
|
|
352
|
+
content={`${faviconBase}/browserconfig.xml`}
|
|
353
|
+
/>
|
|
354
|
+
)
|
|
355
|
+
}
|
package/README.md
CHANGED
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
title: "@stnd/layout"
|
|
3
3
|
aliases: []
|
|
4
4
|
created: 2026-07-05 07:43
|
|
5
|
-
modified: 2026-
|
|
5
|
+
modified: 2026-09-16T17:28:37.447Z
|
|
6
6
|
last_audited: 2026-07-14
|
|
7
7
|
audit_interval_days: 90
|
|
8
8
|
next_audit: 2026-10-12
|
|
9
9
|
audit_priority: 3
|
|
10
10
|
maturity: tree
|
|
11
11
|
mode: read
|
|
12
|
-
publish:
|
|
12
|
+
publish: true
|
|
13
13
|
status: active
|
|
14
14
|
tags:
|
|
15
15
|
- package
|
|
16
16
|
- stnd
|
|
17
17
|
theme: kernel
|
|
18
18
|
type: package
|
|
19
|
-
visibility:
|
|
19
|
+
visibility: public
|
|
20
|
+
garden-url: https://standard.garden/@francis/readme
|
|
21
|
+
garden-short: https://stnd.gd/wAgx4e
|
|
22
|
+
permalink: readme
|
|
20
23
|
---
|
|
21
24
|
|
|
22
25
|
# @[stnd](../README)/layout
|
package/StndInit.astro
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
---
|
|
2
|
+
/**
|
|
3
|
+
* @component StndInit
|
|
4
|
+
* @description System-layer mount point rendered once per page, right after
|
|
5
|
+
* `</body>` in `<Base>`. Renders external module CDN assets, the Gold
|
|
6
|
+
* Standard module Hook zones (`stnd:base` for static Astro components like
|
|
7
|
+
* Toast/Confetti, `stnd:client` for hydrated Svelte components like Lab), the
|
|
8
|
+
* module store/scripts bundle, and a global `standard:ready` boot event. Not
|
|
9
|
+
* meant to be used outside `<Base>`.
|
|
10
|
+
*/
|
|
2
11
|
import config from "virtual:stnd/config";
|
|
3
12
|
import Hook from "@stnd/core/Hook";
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
/**
|
|
16
|
+
* Optional allow-list handed to every Hook in this shell. A module opts a
|
|
17
|
+
* component out of the default "always mounted" behavior by giving it
|
|
18
|
+
* `meta: { scope: "something" }`; that entry only renders when `something`
|
|
19
|
+
* appears in `activeScopes`. Omitted (the default) means unrestricted —
|
|
20
|
+
* every app that doesn't pass this prop keeps today's behavior untouched.
|
|
21
|
+
* The framework never interprets what a scope name MEANS — that's the
|
|
22
|
+
* app's vocabulary, computed and passed in by whichever layout renders
|
|
23
|
+
* this component.
|
|
24
|
+
*/
|
|
25
|
+
activeScopes?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { activeScopes } = Astro.props as Props;
|
|
4
29
|
---
|
|
5
30
|
|
|
6
31
|
<head>
|
|
@@ -24,10 +49,10 @@ import Hook from "@stnd/core/Hook";
|
|
|
24
49
|
<!-- SYSTEM LAYER — Components injected by Gold Standard modules -->
|
|
25
50
|
<div id="stnd-os" transition:persist="stnd-os">
|
|
26
51
|
<!-- Static system components: Toast, Confetti (Astro SSR, zero JS overhead) -->
|
|
27
|
-
<Hook zone="stnd:base" />
|
|
52
|
+
<Hook zone="stnd:base" activeScopes={activeScopes} />
|
|
28
53
|
|
|
29
54
|
<!-- Hydrated system components: Lab, DropZone, Graft, etc. (Svelte, client-side) -->
|
|
30
|
-
<Hook zone="stnd:client" hydrated={true} />
|
|
55
|
+
<Hook zone="stnd:client" hydrated={true} activeScopes={activeScopes} />
|
|
31
56
|
</div>
|
|
32
57
|
|
|
33
58
|
<!-- MODULE SCRIPTS (bypasses broken injectScript pipeline) -->
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stnd/layout",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "Francis Fontaine",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
8
|
+
"./*.astro": "./*.astro",
|
|
9
|
+
"./*.svelte": "./*.svelte",
|
|
8
10
|
"./*": {
|
|
9
11
|
"astro": "./*.astro",
|
|
10
12
|
"svelte": "./*.svelte",
|
|
@@ -12,9 +14,10 @@
|
|
|
12
14
|
}
|
|
13
15
|
},
|
|
14
16
|
"dependencies": {
|
|
15
|
-
"wrangler": "^4.
|
|
16
|
-
"@stnd/lab": "1.0.
|
|
17
|
-
"@stnd/log": "0.5.
|
|
17
|
+
"wrangler": "^4.129.0",
|
|
18
|
+
"@stnd/lab": "1.0.2",
|
|
19
|
+
"@stnd/log": "0.5.1",
|
|
20
|
+
"@stnd/ui": "0.5.2"
|
|
18
21
|
},
|
|
19
22
|
"peerDependencies": {
|
|
20
23
|
"astro": ">=5.0.0"
|