astroidjs 0.12.1 → 0.13.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 +42 -42
- package/bin/astroid.mjs +25 -25
- package/dist/analytics/index.d.ts +3 -3
- package/dist/analytics/index.js +9 -9
- package/dist/astro/csp.d.ts +5 -5
- package/dist/astro/csp.js +6 -6
- package/dist/astro/index.js +1 -1
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +5 -5
- package/dist/commerce/adapters.d.ts +6 -6
- package/dist/commerce/adapters.js +9 -9
- package/dist/commerce/checkout-scaffold.d.ts +5 -5
- package/dist/commerce/checkout-scaffold.js +9 -9
- package/dist/commerce/checkout.d.ts +12 -12
- package/dist/commerce/checkout.js +7 -7
- package/dist/commerce/loader.d.ts +2 -2
- package/dist/commerce/loader.js +3 -3
- package/dist/commerce/mirror.d.ts +4 -4
- package/dist/commerce/mirror.js +9 -9
- package/dist/commerce/roles.d.ts +10 -10
- package/dist/commerce/roles.js +13 -13
- package/dist/commerce/secrets.d.ts +9 -9
- package/dist/commerce/secrets.js +9 -9
- package/dist/commerce/sync.d.ts +7 -7
- package/dist/commerce/sync.js +5 -5
- package/dist/components/sections.d.ts +9 -9
- package/dist/components/sections.js +12 -12
- package/dist/config.d.ts +62 -62
- package/dist/config.js +18 -18
- package/dist/email/inquiry.d.ts +2 -2
- package/dist/email/inquiry.js +1 -1
- package/dist/email/send.d.ts +4 -4
- package/dist/email/send.js +7 -7
- package/dist/email/templates.js +3 -3
- package/dist/email/theme.d.ts +1 -1
- package/dist/email/theme.js +4 -4
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/index.js +1 -1
- package/dist/map/pmtiles.d.ts +5 -5
- package/dist/map/pmtiles.js +5 -5
- package/dist/map/scaffold.d.ts +2 -2
- package/dist/map/scaffold.js +4 -4
- package/dist/map/style.d.ts +4 -4
- package/dist/map/style.js +1 -1
- package/dist/portal/config.d.ts +2 -2
- package/dist/portal/config.js +3 -3
- package/dist/portal/guard.d.ts +4 -4
- package/dist/portal/guard.js +4 -4
- package/dist/portal/nav.js +2 -2
- package/dist/portal/scaffold.d.ts +4 -4
- package/dist/portal/scaffold.js +6 -6
- package/dist/portal/session.d.ts +2 -2
- package/dist/portal/session.js +5 -5
- package/dist/portfolio/scaffold.d.ts +1 -1
- package/dist/portfolio/scaffold.js +4 -4
- package/dist/project/actions.d.ts +1 -1
- package/dist/project/actions.js +6 -6
- package/dist/project/generate.d.ts +4 -4
- package/dist/project/generate.js +15 -15
- package/dist/project/index.js +1 -1
- package/dist/project/scaffold.d.ts +2 -2
- package/dist/project/scaffold.js +11 -11
- package/dist/pwa/generate.d.ts +11 -11
- package/dist/pwa/generate.js +12 -12
- package/dist/queues/consumer.d.ts +3 -3
- package/dist/queues/consumer.js +2 -2
- package/dist/queues/messages.d.ts +4 -4
- package/dist/queues/messages.js +2 -2
- package/dist/queues/scaffold.d.ts +4 -4
- package/dist/queues/scaffold.js +8 -8
- package/dist/queues/webhook.d.ts +5 -5
- package/dist/queues/webhook.js +3 -3
- package/dist/realtime/scaffold.d.ts +4 -4
- package/dist/realtime/scaffold.js +8 -8
- package/dist/schema/collections.d.ts +6 -6
- package/dist/schema/collections.js +23 -23
- package/dist/schema/framework.d.ts +1 -1
- package/dist/schema/framework.js +2 -2
- package/dist/schema/generate.js +2 -2
- package/dist/schema/index.js +1 -1
- package/dist/secrets.d.ts +6 -6
- package/dist/secrets.js +6 -6
- package/dist/security/csp-origins.d.ts +1 -1
- package/dist/security/csp-origins.js +3 -3
- package/dist/security/rate-rules.d.ts +2 -2
- package/dist/security/rate-rules.js +8 -8
- package/dist/seo/resolve.d.ts +5 -5
- package/dist/seo/resolve.js +2 -2
- package/dist/seo/routes.d.ts +5 -5
- package/dist/seo/routes.js +3 -3
- package/dist/seo/structured-data.d.ts +6 -6
- package/dist/seo/structured-data.js +7 -7
- package/dist/status.d.ts +5 -5
- package/dist/status.js +7 -7
- package/dist/tenancy/index.d.ts +3 -3
- package/dist/tenancy/index.js +6 -6
- package/dist/worker/generate.d.ts +2 -2
- package/dist/worker/generate.js +19 -19
- package/dist/worker/index.js +1 -1
- package/dist/worker/routes.js +1 -1
- package/dist/workflow/advance.d.ts +3 -3
- package/dist/workflow/advance.js +6 -6
- package/dist/workflow/config.d.ts +4 -4
- package/dist/workflow/config.js +4 -4
- package/dist/workflow/generate.d.ts +2 -2
- package/dist/workflow/generate.js +4 -4
- package/package.json +3 -4
- package/src/components/Collection.tsx +5 -5
- package/src/components/Editable.astro +9 -9
- package/src/components/JustifiedGallery.astro +8 -8
- package/src/components/MediaSlot.astro +12 -12
- package/src/components/PortalShell.astro +4 -4
- package/src/components/RegisterSW.astro +3 -3
- package/src/components/Section.astro +8 -8
- package/src/components/Sections.astro +6 -6
- package/src/components/Seo.astro +3 -3
- package/src/components/StageBar.astro +3 -3
- package/src/components/StructuredData.astro +2 -2
- package/src/components/justify.ts +9 -9
- package/src/components/media-meta.ts +10 -10
- package/src/components/sections/AboutIntro.astro +1 -1
- package/src/components/sections/Contact.astro +1 -1
- package/src/components/sections/Cta.astro +1 -1
- package/src/components/sections/Faq.astro +1 -1
- package/src/components/sections/FeatureGrid.astro +2 -2
- package/src/components/sections/Hero.astro +1 -1
- package/src/components/sections/PricingTiers.astro +1 -1
- package/src/components/sections/ProductGrid.astro +1 -1
- package/src/components/sections/SplitImage.astro +1 -1
- package/src/components/sections/Steps.astro +1 -1
- package/src/components/sections/Testimonial.astro +1 -1
- package/src/components/sections.ts +17 -17
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
//
|
|
5
5
|
// The layout every photo grid actually wants: images keep their aspect ratios,
|
|
6
6
|
// each row is exactly as wide as the container, and rows land near a target
|
|
7
|
-
// height. CSS can't express it
|
|
7
|
+
// height. CSS can't express it—`grid` needs uniform tracks, and `columns`
|
|
8
8
|
// gives you a masonry *column* flow where reading order runs down instead of
|
|
9
|
-
// across
|
|
9
|
+
// across—so it's arithmetic, done once here rather than re-derived per site.
|
|
10
10
|
//
|
|
11
11
|
// Pure and synchronous on purpose. It's called twice for the same gallery:
|
|
12
12
|
// once during SSR with the aspect ratios the media library recorded, and again
|
|
@@ -28,14 +28,14 @@ export interface JustifyItem {
|
|
|
28
28
|
* One image in a `<JustifiedGallery>`.
|
|
29
29
|
*
|
|
30
30
|
* Declared here rather than in the `.astro` component so consumers can import
|
|
31
|
-
* the type from a plain module
|
|
31
|
+
* the type from a plain module—an `.astro` file's non-`Props` exports are
|
|
32
32
|
* awkward to reach from TypeScript, and a scaffolded page shouldn't have to.
|
|
33
33
|
*/
|
|
34
34
|
export interface GalleryItem {
|
|
35
35
|
src: string;
|
|
36
36
|
/**
|
|
37
37
|
* Alternative text. Required, and `""` is a legitimate value for a purely
|
|
38
|
-
* decorative tile
|
|
38
|
+
* decorative tile—what must not happen is the attribute going missing.
|
|
39
39
|
*/
|
|
40
40
|
alt: string;
|
|
41
41
|
/** Intrinsic dimensions, when the media library recorded them. Used for the
|
|
@@ -49,7 +49,7 @@ export interface GalleryItem {
|
|
|
49
49
|
|
|
50
50
|
/** A laid-out item: its position in the input, at a concrete pixel size. */
|
|
51
51
|
export interface JustifiedBox {
|
|
52
|
-
/** Index into the input array
|
|
52
|
+
/** Index into the input array—the caller maps this back to its own data. */
|
|
53
53
|
index: number;
|
|
54
54
|
width: number;
|
|
55
55
|
height: number;
|
|
@@ -80,8 +80,8 @@ export interface JustifyOptions {
|
|
|
80
80
|
lastRowSlack?: number;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
/** Aspect used for an item whose real ratio isn't known yet (3:2 landscape
|
|
84
|
-
|
|
83
|
+
/** Aspect used for an item whose real ratio isn't known yet (3:2 landscape—*
|
|
84
|
+
the most common photographic frame, so first paint is usually close). */
|
|
85
85
|
export const DEFAULT_ASPECT = 1.5;
|
|
86
86
|
|
|
87
87
|
/** Clamp a caller-supplied aspect into something layout-safe. A zero or NaN
|
|
@@ -97,7 +97,7 @@ function safeAspect(aspect: number): number {
|
|
|
97
97
|
* The rule per row: with n items of total aspect A and n−1 gaps, the height
|
|
98
98
|
* that makes the row exactly fill is `(containerWidth − gap·(n−1)) / A`. Adding
|
|
99
99
|
* items only ever *lowers* that height, so items are appended until it drops to
|
|
100
|
-
* `targetHeight` and the row closes
|
|
100
|
+
* `targetHeight` and the row closes—which is why rows come out near, but
|
|
101
101
|
* never exactly at, the target.
|
|
102
102
|
*
|
|
103
103
|
* ```ts
|
|
@@ -135,7 +135,7 @@ export function justifyRows(items: JustifyItem[], options: JustifyOptions): Just
|
|
|
135
135
|
// Every box but the last takes its proportional share; the last takes
|
|
136
136
|
// whatever is left, so the row's widths sum to `available` exactly and the
|
|
137
137
|
// right edge stays flush. An un-stretched trailing row keeps natural
|
|
138
|
-
// widths instead
|
|
138
|
+
// widths instead—it isn't meant to reach the edge.
|
|
139
139
|
const width =
|
|
140
140
|
stretch && last
|
|
141
141
|
? available - used
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
//
|
|
5
5
|
// A section stores an image as a URL. The `alt` and `caption` an editor typed
|
|
6
6
|
// live on the media ASSET, so rendering a page's images correctly means joining
|
|
7
|
-
// every image field back to the media registry. Done naively
|
|
8
|
-
// `<MediaSlot
|
|
7
|
+
// every image field back to the media registry. Done naively—a query per
|
|
8
|
+
// `<MediaSlot>`—a gallery section is thirty round-trips to D1 on every render.
|
|
9
9
|
//
|
|
10
10
|
// So the whole page is resolved in ONE bounded `IN (...)` lookup before anything
|
|
11
11
|
// renders, and the result is threaded down as `mediaMeta`. This is the pattern
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
//
|
|
14
14
|
// The collection step is SCHEMA-DRIVEN: it walks the catalog looking for fields
|
|
15
15
|
// of `type: "image"` rather than hardcoding field names. That's what keeps it
|
|
16
|
-
// correct as sections are added
|
|
16
|
+
// correct as sections are added—a new section with an image field is picked up
|
|
17
17
|
// because it declared one, not because someone remembered to update a list here.
|
|
18
18
|
//
|
|
19
19
|
// Self-contained (ships as source): type-only imports, no astroid `src/*` reach.
|
|
@@ -26,7 +26,7 @@ import type { MediaMeta } from "./sections.js";
|
|
|
26
26
|
const CHUNK = 100;
|
|
27
27
|
|
|
28
28
|
/** Walk one item's declared fields, collecting the values of `image` ones.
|
|
29
|
-
* Recurses into `array` item fields
|
|
29
|
+
* Recurses into `array` item fields—a gallery's images live there. */
|
|
30
30
|
function collectFrom(
|
|
31
31
|
item: Record<string, unknown>,
|
|
32
32
|
fields: Record<string, SectionField> | undefined,
|
|
@@ -41,7 +41,7 @@ function collectFrom(
|
|
|
41
41
|
for (const row of value) {
|
|
42
42
|
if (typeof row !== "object" || row === null || Array.isArray(row)) continue;
|
|
43
43
|
// With a discriminator, an item's variant contributes extra fields on
|
|
44
|
-
// top of the shared set
|
|
44
|
+
// top of the shared set—an image declared only on the "image" variant
|
|
45
45
|
// of a blocks-style array would be missed otherwise.
|
|
46
46
|
const sub = row as Record<string, unknown>;
|
|
47
47
|
let itemFields = field.itemFields ?? {};
|
|
@@ -60,7 +60,7 @@ function collectFrom(
|
|
|
60
60
|
/**
|
|
61
61
|
* Every media URL a page's sections reference, deduplicated.
|
|
62
62
|
*
|
|
63
|
-
* Blocks are walked too, against the block catalog
|
|
63
|
+
* Blocks are walked too, against the block catalog—a section's images and its
|
|
64
64
|
* blocks' images are the same lookup, and splitting them would reintroduce the
|
|
65
65
|
* per-section query this exists to avoid.
|
|
66
66
|
*/
|
|
@@ -96,7 +96,7 @@ export interface MediaMetaDatabase {
|
|
|
96
96
|
/**
|
|
97
97
|
* Turn a public media URL into its registry `key`.
|
|
98
98
|
*
|
|
99
|
-
* The public URL is `MEDIA_URL + "/" + key`, so this is prefix removal
|
|
99
|
+
* The public URL is `MEDIA_URL + "/" + key`, so this is prefix removal—but it
|
|
100
100
|
* has to tolerate an absolute URL too (a site whose media base is a full origin),
|
|
101
101
|
* which is why it falls back to the pathname's last segment rather than assuming
|
|
102
102
|
* the string starts with `mediaBase`.
|
|
@@ -105,7 +105,7 @@ export function mediaKeyFromUrl(url: string, mediaBase: string): string | null {
|
|
|
105
105
|
if (!url) return null;
|
|
106
106
|
const base = mediaBase.replace(/\/+$/, "");
|
|
107
107
|
if (base && url.startsWith(`${base}/`)) return url.slice(base.length + 1);
|
|
108
|
-
// Absolute or unexpected shape
|
|
108
|
+
// Absolute or unexpected shape—take the path's tail, which is the key for
|
|
109
109
|
// every URL the media route produces.
|
|
110
110
|
try {
|
|
111
111
|
const path = url.startsWith("http") ? new URL(url).pathname : url;
|
|
@@ -122,7 +122,7 @@ export function mediaKeyFromUrl(url: string, mediaBase: string): string | null {
|
|
|
122
122
|
* Returns `{}` on any failure rather than throwing: missing alt text degrades a
|
|
123
123
|
* page, but a media table that isn't provisioned yet must not take the whole
|
|
124
124
|
* render down with it. The caller's per-image `alt` (a usage-level override)
|
|
125
|
-
* always wins over what comes back here
|
|
125
|
+
* always wins over what comes back here—this is the ASSET-level fallback.
|
|
126
126
|
*/
|
|
127
127
|
export async function resolveSectionMedia(
|
|
128
128
|
db: MediaMetaDatabase | undefined,
|
|
@@ -131,7 +131,7 @@ export async function resolveSectionMedia(
|
|
|
131
131
|
): Promise<MediaMeta> {
|
|
132
132
|
if (!db || urls.length === 0) return {};
|
|
133
133
|
|
|
134
|
-
// key → the
|
|
134
|
+
// key → the URLs that produced it, so results map back to what the sections
|
|
135
135
|
// actually reference.
|
|
136
136
|
const byKey = new Map<string, string[]>();
|
|
137
137
|
for (const url of urls) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
// A portrait beside an introduction
|
|
2
|
+
// A portrait beside an introduction—the "about the maker" opener.
|
|
3
3
|
import type { SectionRenderProps } from "../sections.js";
|
|
4
4
|
import { colorwayClass, field, mediaAlt, setting } from "../sections.js";
|
|
5
5
|
import Editable from "../Editable.astro";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
// Contact section
|
|
2
|
+
// Contact section—a heading + blurb + link to the contact route (the generated
|
|
3
3
|
// worker's contact form lives at /contact).
|
|
4
4
|
import type { SectionRenderProps } from "../sections.js";
|
|
5
5
|
import { colorwayClass, field, setting } from "../sections.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
// Call-to-action band. Defaults to the brand colorway
|
|
2
|
+
// Call-to-action band. Defaults to the brand colorway—a setting token, so an
|
|
3
3
|
// editor can change it in the inspector without touching this file.
|
|
4
4
|
import type { SectionRenderProps } from "../sections.js";
|
|
5
5
|
import { alignClass, colorwayClass, field, setting } from "../sections.js";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Native rather than a JS accordion on purpose: it is keyboard-accessible and
|
|
5
5
|
// screen-reader-correct with no script, it works before hydration, and browser
|
|
6
|
-
// find-in-page can open a collapsed answer
|
|
6
|
+
// find-in-page can open a collapsed answer—which a div-based accordion
|
|
7
7
|
// silently breaks.
|
|
8
8
|
import type { SectionRenderProps } from "../sections.js";
|
|
9
9
|
import { colorwayClass, field, itemField, list, setting } from "../sections.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
// Feature-grid section
|
|
2
|
+
// Feature-grid section—value-prop cards over an `array` field.
|
|
3
3
|
//
|
|
4
4
|
// The array is where `base` earns its keep. Each card's fields address
|
|
5
5
|
// `<base>.items.<i>.<key>`, so the marker is built from the item's position
|
|
6
|
-
// rather than a fixed slot name
|
|
6
|
+
// rather than a fixed slot name—which is why the schema can use a real
|
|
7
7
|
// repeatable array instead of the `card1…card6` flattening sites fall back to.
|
|
8
8
|
// The client's path parser is depth-agnostic (it splits on `.` and coerces
|
|
9
9
|
// numeric segments), so this needs nothing new on the editor side.
|
|
@@ -39,7 +39,7 @@ const ctaHref = field(item, "ctaHref");
|
|
|
39
39
|
{subheading}
|
|
40
40
|
</Editable>
|
|
41
41
|
)}
|
|
42
|
-
{/* The label is editable in place; the href is not
|
|
42
|
+
{/* The label is editable in place; the href is not—you can't point at a
|
|
43
43
|
URL, so it lives in the inspector (`inline: false` in the catalog). */}
|
|
44
44
|
{ctaLabel && ctaHref && (
|
|
45
45
|
<a class="btn btn-primary" href={ctaHref}>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
// Pricing tiers. Each tier's feature list is a nested array, so a feature's
|
|
3
|
-
// marker path is `<base>.items.<i>.features.<j>.text
|
|
3
|
+
// marker path is `<base>.items.<i>.features.<j>.text`—two levels of position,
|
|
4
4
|
// which the client's path parser handles because it splits on `.` and coerces
|
|
5
5
|
// every numeric segment.
|
|
6
6
|
import type { SectionRenderProps } from "../sections.js";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// Deliberately NOT a live catalog read. A section is stored content that an
|
|
5
5
|
// editor curates; the commerce mirror has its own loader (`readCatalog`) with
|
|
6
6
|
// its own freshness story. A site that wants the live catalog renders it in a
|
|
7
|
-
// page, not through the page-builder
|
|
7
|
+
// page, not through the page-builder—mixing the two would make a section's
|
|
8
8
|
// stored value and what it displays disagree, and the editor would be editing
|
|
9
9
|
// something that isn't there.
|
|
10
10
|
import type { SectionRenderProps } from "../sections.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
// Image beside copy. Which side the image sits on is a `_layout` token, so an
|
|
3
|
-
// editor flips it from the inspector and Louise stores `"imageEnd"
|
|
3
|
+
// editor flips it from the inspector and Louise stores `"imageEnd"`—never a
|
|
4
4
|
// class name. The mapping from token to CSS is here, where it belongs.
|
|
5
5
|
import type { SectionRenderProps } from "../sections.js";
|
|
6
6
|
import { colorwayClass, field, mediaAlt, setting } from "../sections.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
// A numbered sequence
|
|
2
|
+
// A numbered sequence—"how it works", a commission process, an onboarding
|
|
3
3
|
// flow. The number is generated from position rather than stored, so reordering
|
|
4
4
|
// steps in the inspector renumbers them with no content edit.
|
|
5
5
|
import type { SectionRenderProps } from "../sections.js";
|
|
@@ -41,7 +41,7 @@ const image = field(item, "image");
|
|
|
41
41
|
</Editable>
|
|
42
42
|
{role && (
|
|
43
43
|
<>
|
|
44
|
-
{attribution && <span aria-hidden="true">
|
|
44
|
+
{attribution && <span aria-hidden="true"> · </span>}
|
|
45
45
|
<Editable as="span" base={base} field="role" edit={edit}>{role}</Editable>
|
|
46
46
|
</>
|
|
47
47
|
)}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
//
|
|
6
6
|
// This file used to define a parallel universe: a `SectionProps` union
|
|
7
7
|
// discriminated on `kind`, with `colorway`/`align` as component props. Louise's
|
|
8
|
-
// actual model
|
|
9
|
-
// read
|
|
8
|
+
// actual model—the one the on-canvas editor and the write-time validator both
|
|
9
|
+
// read—is different in every particular, and it is the one that wins:
|
|
10
10
|
//
|
|
11
11
|
// • a section is a stored `SectionItem`: `{ _type, blocks?, _layout?,
|
|
12
12
|
// _settings?, ...fields }`. The discriminant is `_type`, not `kind`.
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// or validated-but-uneditable.
|
|
17
17
|
// • presentation choices are `_settings` / `_layout` **tokens**. Louise stores
|
|
18
18
|
// the token; the site maps it to CSS. That's why COLORWAY_CLASS below stays
|
|
19
|
-
|
|
19
|
+
//: it is exactly the site-owned half of that contract—while `colorway`
|
|
20
20
|
// stops being a prop and becomes a stored setting.
|
|
21
21
|
//
|
|
22
22
|
// ADR 0005 §2 names this file's job outright: "<Section> reads `_layout` /
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
//
|
|
27
27
|
// Self-contained on purpose: this module ships as SOURCE (the `.astro` files
|
|
28
28
|
// beside it import it directly), so it must not reach back into astroid's built
|
|
29
|
-
// `src
|
|
29
|
+
// `src/*`—only siblings and external packages. The `louise-toolkit/content`
|
|
30
30
|
// import is TYPE-ONLY, so it erases at build and never drags the validator (or
|
|
31
31
|
// drizzle, which that entry pulls in) into a page bundle.
|
|
32
32
|
|
|
@@ -87,7 +87,7 @@ const tokenOptions = (map: Record<string, string>) =>
|
|
|
87
87
|
*
|
|
88
88
|
* These are closed token sets, declared as `select` (#272) so the inspector
|
|
89
89
|
* renders a picker and an unknown token is rejected on write. They used to be
|
|
90
|
-
* `text` with the valid values stuffed into `placeholder
|
|
90
|
+
* `text` with the valid values stuffed into `placeholder`—which meant a typo
|
|
91
91
|
* wasn't a validation error at all, just a silent fallback to the default
|
|
92
92
|
* inside `colorwayClass` at render time.
|
|
93
93
|
*/
|
|
@@ -97,7 +97,7 @@ export const SECTION_SETTINGS: Record<string, SectionField> = {
|
|
|
97
97
|
label: "Colorway",
|
|
98
98
|
inline: false,
|
|
99
99
|
options: tokenOptions(COLORWAY_CLASS),
|
|
100
|
-
// An opaque hint
|
|
100
|
+
// An opaque hint—the schema layer doesn't know what a swatch looks like;
|
|
101
101
|
// a renderer that doesn't support it just shows a normal picker.
|
|
102
102
|
display: "swatch",
|
|
103
103
|
},
|
|
@@ -113,8 +113,8 @@ export const SECTION_SETTINGS: Record<string, SectionField> = {
|
|
|
113
113
|
* What every section render component receives.
|
|
114
114
|
*
|
|
115
115
|
* `base` is the whole point. It is this item's path within the page's `sections`
|
|
116
|
-
* array
|
|
117
|
-
*
|
|
116
|
+
* array—`"2"` for a top-level section, `"2.blocks.0"` for a block inside it—and
|
|
117
|
+
* every marker the component stamps is built from it. Passing it down (rather
|
|
118
118
|
* than having each component work out its own depth) is what lets the exact same
|
|
119
119
|
* component render as a section or as a block, and what keeps `data-louise-node`
|
|
120
120
|
* paths correct at any nesting depth.
|
|
@@ -126,8 +126,8 @@ export interface SectionRenderProps {
|
|
|
126
126
|
base: string;
|
|
127
127
|
/** Whether to stamp edit markers. Defaults to `Astro.locals.editMode`. */
|
|
128
128
|
edit?: boolean;
|
|
129
|
-
/** Alt/caption resolved from the media registry, keyed by public URL
|
|
130
|
-
|
|
129
|
+
/** Alt/caption resolved from the media registry, keyed by public URL—*
|
|
130
|
+
looked up once for the whole page by `<Sections>`. */
|
|
131
131
|
mediaMeta?: MediaMeta;
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -169,7 +169,7 @@ export function itemField(row: Record<string, unknown>, key: string): string | u
|
|
|
169
169
|
*
|
|
170
170
|
* The precedence is the whole reason `<Sections>` does its media lookup: a
|
|
171
171
|
* per-usage `alt` on the section wins, because the same photo means something
|
|
172
|
-
* different in a hero than in a thumbnail strip
|
|
172
|
+
* different in a hero than in a thumbnail strip—but when there isn't one, the
|
|
173
173
|
* alt an editor typed once in the media library is used. That's what makes
|
|
174
174
|
* fixing alt text a single edit that propagates everywhere the asset appears,
|
|
175
175
|
* instead of a hunt through every page that embeds it.
|
|
@@ -188,7 +188,7 @@ export function mediaAlt(
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
/** Caption for an image, same precedence as {@link mediaAlt}. Undefined when
|
|
191
|
-
* there is none
|
|
191
|
+
* there is none—a missing caption renders nothing, unlike a missing alt. */
|
|
192
192
|
export function mediaCaption(
|
|
193
193
|
mediaMeta: MediaMeta | undefined,
|
|
194
194
|
src: string | undefined,
|
|
@@ -208,7 +208,7 @@ export function mediaCaption(
|
|
|
208
208
|
* `satisfies` rather than a `: SectionCatalog` annotation, and it matters:
|
|
209
209
|
* `SectionCatalog` is `Record<string, SectionDef>`, so annotating would widen
|
|
210
210
|
* `keyof typeof` to `string` and throw away the literal keys. Those keys are
|
|
211
|
-
* the project's whole section vocabulary
|
|
211
|
+
* the project's whole section vocabulary—`SectionKind` is derived from them
|
|
212
212
|
* (config.ts), `isRenderableSection` narrows to them, and `<Section>` indexes
|
|
213
213
|
* its component map with them. Annotate this and all three silently degrade to
|
|
214
214
|
* "any string", which is how the dispatcher lost its type safety once already.
|
|
@@ -221,7 +221,7 @@ export const astroidSectionCatalog = {
|
|
|
221
221
|
heading: { type: "text", label: "Heading", validation: (r) => r.required().max(120) },
|
|
222
222
|
subheading: { type: "textarea", label: "Subheading" },
|
|
223
223
|
// A link URL is something you can't point at on the page, so it is not
|
|
224
|
-
// inline
|
|
224
|
+
// inline—it belongs in the inspector, which is what `inline: false` says.
|
|
225
225
|
ctaLabel: { type: "text", label: "Button label" },
|
|
226
226
|
ctaHref: { type: "text", label: "Button link", inline: false },
|
|
227
227
|
},
|
|
@@ -367,7 +367,7 @@ export const astroidSectionCatalog = {
|
|
|
367
367
|
name: { type: "text", label: "Name", validation: (r) => r.required() },
|
|
368
368
|
price: { type: "text", label: "Price" },
|
|
369
369
|
period: { type: "text", label: "Period (e.g. /mo)" },
|
|
370
|
-
// A list of strings isn't expressible
|
|
370
|
+
// A list of strings isn't expressible—array items are objects—so
|
|
371
371
|
// each feature is a one-field row. That also leaves room to add an
|
|
372
372
|
// `included` flag later without a data migration.
|
|
373
373
|
features: {
|
|
@@ -424,7 +424,7 @@ export const astroidSectionCatalog = {
|
|
|
424
424
|
heading: { type: "text", label: "Heading" },
|
|
425
425
|
// Deliberately hand-authored rows rather than a live catalog read. A
|
|
426
426
|
// section is stored content, and the commerce mirror is a separate
|
|
427
|
-
// concern with its own loader
|
|
427
|
+
// concern with its own loader—a site that wants the live catalog renders
|
|
428
428
|
// `readCatalog` in its own page, not through the page-builder.
|
|
429
429
|
items: {
|
|
430
430
|
type: "array",
|
|
@@ -472,7 +472,7 @@ export const astroidSectionCatalog = {
|
|
|
472
472
|
},
|
|
473
473
|
} satisfies SectionCatalog;
|
|
474
474
|
|
|
475
|
-
/** The `_type`s with a shipped render component
|
|
475
|
+
/** The `_type`s with a shipped render component—derived from the catalog, so
|
|
476
476
|
* it can't drift from what `<Section>` actually dispatches. A literal union,
|
|
477
477
|
* not `string`, because the catalog is declared with `satisfies`. */
|
|
478
478
|
export type RenderableSectionType = keyof typeof astroidSectionCatalog;
|