create-ailk 0.12.1 → 0.14.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 +12 -4
- package/component-catalog.json +247 -33
- package/component-catalog.md +10 -6
- package/dist/apply-design.d.ts +21 -2
- package/dist/apply-design.js +120 -19
- package/dist/cli.js +1 -0
- package/dist/design-compile.d.ts +70 -2
- package/dist/design-compile.js +97 -1
- package/dist/design-home.d.ts +74 -0
- package/dist/design-home.js +256 -0
- package/dist/design-package.d.ts +39 -2
- package/dist/design-package.js +223 -9
- package/dist/design-presets.d.ts +17 -1
- package/dist/design-presets.js +17 -5
- package/dist/design-scaffold.d.ts +6 -25
- package/dist/design-scaffold.js +26 -74
- package/dist/index.d.ts +1 -0
- package/dist/lib/design-checks.d.ts +12 -1
- package/dist/lib/design-checks.js +38 -2
- package/dist/lib/module-license-gate.d.ts +9 -0
- package/dist/lib/module-license-gate.js +18 -0
- package/dist/module-architecture.js +27 -0
- package/dist/programmatic.js +70 -26
- package/dist/site-chrome.d.ts +38 -0
- package/dist/site-chrome.js +109 -2
- package/dist/site-fonts.d.ts +21 -0
- package/dist/site-fonts.js +54 -0
- package/dist/site-roster.d.ts +16 -0
- package/dist/site-roster.js +81 -0
- package/dist/starter-grid.js +15 -0
- package/dist/surfaces.js +12 -0
- package/dist/team-profiles.d.ts +29 -0
- package/dist/team-profiles.js +43 -0
- package/package.json +3 -3
- package/release-scope.json +1 -1
- package/site-templates/_base/content/en/TeamList/team.mdx +68 -0
- package/site-templates/_base/content/en/TeamMember/first-member.mdx +22 -0
- package/site-templates/_base/content/en/TeamMember/second-member.mdx +22 -0
- package/site-templates/_base/content/en/TeamMember/third-member.mdx +22 -0
- package/site-templates/designs/minimalism/DESIGN.md +164 -34
- package/site-templates/designs/minimalism/home/local-business-service.mdx +119 -0
- package/site-templates/designs/minimalism/home/organization-product.mdx +132 -0
- package/site-templates/designs/minimalism/home/organization-service.mdx +131 -0
- package/site-templates/designs/minimalism/home/person-service.mdx +101 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +20 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +22 -1
- package/site-templates/marketing-site-free/ailk-template.json +1 -1
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +26 -7
- package/site-templates/shapes/local-business-service/ailk-template.json +1 -1
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +26 -7
- package/site-templates/shapes/organization-service/ailk-template.json +1 -1
- package/templates/.env.example +23 -9
- package/templates/README.md +2 -0
- package/templates/apps/api/CLAUDE.md +24 -17
- package/templates/apps/api/src/__tests__/factories/agent-caller.ts +98 -0
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +321 -0
- package/templates/apps/api/src/__tests__/schedule-forwarded-rate-limit.test.ts +117 -0
- package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +39 -14
- package/templates/apps/api/src/config/index.ts +35 -20
- package/templates/apps/api/src/lib/__mocks__/prisma-client.js +3 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +14 -0
- package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +468 -0
- package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +32 -2
- package/templates/apps/api/src/lib/agent-store.ts +521 -0
- package/templates/apps/api/src/lib/spoke-store.ts +13 -2
- package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +384 -1
- package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +61 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +176 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +76 -0
- package/templates/apps/api/src/middleware/auth.ts +219 -5
- package/templates/apps/api/src/middleware/default-tenant.ts +29 -8
- package/templates/apps/api/src/middleware/rate-limit.ts +35 -13
- package/templates/apps/api/src/routes/leads/__tests__/spoke.test.ts +76 -15
- package/templates/apps/api/src/routes/leads/index.ts +14 -6
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -6
- package/templates/apps/api/src/routes/project-listings/index.ts +14 -9
- package/templates/apps/api/src/routes/project-listings/me.ts +4 -3
- package/templates/apps/api/src/routes/project-listings/patch-copy.ts +7 -5
- package/templates/apps/api/src/routes/workspaces/__tests__/inbox-wire-schemas.test.ts +198 -0
- package/templates/apps/api/src/server.ts +27 -0
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +374 -57
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +25 -2
- package/templates/apps/api/src/services/__tests__/spoke-resolution.test.ts +303 -17
- package/templates/apps/api/src/services/audit-log.ts +278 -47
- package/templates/apps/api/src/services/default-tenant.ts +55 -5
- package/templates/apps/api/src/services/leads-inbox.ts +12 -21
- package/templates/apps/api/src/services/project-listings.ts +1 -21
- package/templates/apps/api/src/services/public-site-resolver.ts +103 -13
- package/templates/apps/mcp/CLAUDE.md +44 -5
- package/templates/apps/mcp/__tests__/parity.test.ts +53 -1
- package/templates/apps/mcp/__tests__/workspace-surface.test.ts +364 -0
- package/templates/apps/mcp/package.json +1 -1
- package/templates/apps/mcp/src/http-transport.ts +107 -1
- package/templates/apps/mcp/src/workspace-server.ts +193 -0
- package/templates/apps/web/.env.example +64 -37
- package/templates/apps/web/CLAUDE.md +11 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/settings.test.tsx +10 -0
- package/templates/apps/web/app/[locale]/(authed)/account/settings/__tests__/theme-modes.test.ts +42 -0
- package/templates/apps/web/app/[locale]/(authed)/account/settings/theme-modes.ts +8 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +14 -1
- package/templates/apps/web/app/[locale]/contact/__tests__/site-contact-lines.test.ts +69 -0
- package/templates/apps/web/app/[locale]/contact/site-contact-lines.ts +64 -0
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +4 -4
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +15 -3
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +17 -6
- package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +14 -1
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +4 -6
- package/templates/apps/web/app/[locale]/layout.tsx +156 -53
- package/templates/apps/web/app/[locale]/page.tsx +37 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +4 -6
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +15 -2
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +43 -6
- package/templates/apps/web/app/[locale]/services/page.tsx +14 -2
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/team/[slug]/page.tsx +45 -0
- package/templates/apps/web/app/[locale]/team/__tests__/team-routes.test.tsx +155 -0
- package/templates/apps/web/app/[locale]/team/page.tsx +49 -0
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +14 -1
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/not-found.tsx +47 -0
- package/templates/apps/web/app/v1/[...path]/route.ts +16 -0
- package/templates/apps/web/lib/__tests__/profile-back-link.test.ts +95 -0
- package/templates/apps/web/lib/__tests__/relay.test.ts +515 -0
- package/templates/apps/web/lib/__tests__/site-theme-head.test.ts +70 -0
- package/templates/apps/web/lib/__tests__/site-theme.test.ts +64 -1
- package/templates/apps/web/lib/profile-back-link.ts +51 -0
- package/templates/apps/web/lib/relay.ts +279 -0
- package/templates/apps/web/lib/site-chrome.ts +12 -0
- package/templates/apps/web/lib/site-fonts.ts +15 -0
- package/templates/apps/web/lib/site-logo.tsx +39 -1
- package/templates/apps/web/lib/site-theme-head.ts +33 -0
- package/templates/apps/web/lib/site-theme.ts +13 -1
- package/templates/apps/web/messages/en.json +7 -1
- package/templates/apps/web/middleware.ts +5 -4
- package/templates/apps/web/next.config.mjs +6 -0
- package/templates/database/CHANGELOG.md +22 -0
- package/templates/database/__tests__/agents.db.test.ts +235 -0
- package/templates/database/auth/schema.prisma +61 -0
- package/templates/database/migrations/20261005120000_agents/migration.sql +84 -0
- package/templates/database/package.json +1 -1
- package/versions.json +35 -35
- package/site-templates/designs/minimalism/sample/README.md +0 -48
- package/site-templates/designs/minimalism/sample/content/en/HomePage/home.mdx +0 -244
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-1.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-2.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-3.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-4.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-5.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-6.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/hero-backdrop.svg +0 -17
- package/site-templates/designs/minimalism/sample/public/logo-placeholder-1.svg +0 -6
- package/site-templates/designs/minimalism/sample/public/logo-placeholder-2.svg +0 -6
- package/site-templates/designs/minimalism/sample/public/logo-placeholder-3.svg +0 -6
- package/site-templates/designs/minimalism/sample/public/wordmark-placeholder.svg +0 -28
package/dist/apply-design.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type HomeSeedOutcome } from "./design-home.js";
|
|
1
2
|
export interface ApplyDesignOptions {
|
|
2
3
|
/** The design: a shipped name, or a directory holding a DESIGN.md. */
|
|
3
4
|
design: string;
|
|
@@ -6,6 +7,8 @@ export interface ApplyDesignOptions {
|
|
|
6
7
|
/** Package-rooted inputs, supplied by the caller (cli.ts from its PKG_ROOT). */
|
|
7
8
|
designsDir: string;
|
|
8
9
|
siteTemplatesDir: string;
|
|
10
|
+
/** The overlay's `templates/` (#7743 D7) — where a treeless shape's home lives. Default: `templates` beside `siteTemplatesDir`. */
|
|
11
|
+
templatesDir?: string;
|
|
9
12
|
catalogPath: string;
|
|
10
13
|
/** Where a relative design path resolves from. Default process.cwd(). */
|
|
11
14
|
cwd?: string;
|
|
@@ -21,6 +24,9 @@ export type ApplyDesignChange = {
|
|
|
21
24
|
} | {
|
|
22
25
|
path: string;
|
|
23
26
|
kind: "layout";
|
|
27
|
+
} | {
|
|
28
|
+
path: string;
|
|
29
|
+
kind: "fonts";
|
|
24
30
|
} | {
|
|
25
31
|
path: string;
|
|
26
32
|
kind: "design";
|
|
@@ -28,6 +34,11 @@ export type ApplyDesignChange = {
|
|
|
28
34
|
path: string;
|
|
29
35
|
kind: "preset";
|
|
30
36
|
fields: string[];
|
|
37
|
+
} | {
|
|
38
|
+
path: string;
|
|
39
|
+
kind: "home";
|
|
40
|
+
action: "seeded" | "restored";
|
|
41
|
+
fields: string[];
|
|
31
42
|
} | {
|
|
32
43
|
path: string;
|
|
33
44
|
kind: "manifest";
|
|
@@ -39,15 +50,23 @@ export interface ApplyDesignResult {
|
|
|
39
50
|
/** In write order (D4); [] when nothing changed. */
|
|
40
51
|
changes: ApplyDesignChange[];
|
|
41
52
|
manifest: "recorded" | "unchanged" | "no-roster" | "legacy-roster";
|
|
53
|
+
/** #7743 D7 — what the design did to the home page's sections. */
|
|
54
|
+
home: HomeSeedOutcome;
|
|
55
|
+
/** #7743 D7 — the design ships a home arc for this site, so a home it leaves alone is reported. */
|
|
56
|
+
homeArc?: true;
|
|
42
57
|
/** The site-relative roster file, when there is one. */
|
|
43
58
|
rosterPath?: string;
|
|
59
|
+
/** #7720 D4.2 — the distinct families written to site-fonts.ts, display first; set only when faces are written. */
|
|
60
|
+
fonts?: string[];
|
|
61
|
+
/** #7720 D4.2 — faces were written, but the site's layout.tsx does not read lib/site-fonts. */
|
|
62
|
+
fontsUnwired?: true;
|
|
44
63
|
}
|
|
45
64
|
export interface ApplyDesignArgs {
|
|
46
65
|
design: string | undefined;
|
|
47
66
|
target: string | undefined;
|
|
48
67
|
help: boolean;
|
|
49
68
|
}
|
|
50
|
-
export declare const APPLY_DESIGN_HELP = "create-ailk apply-design \u2014 make an existing site wear a design.\n\nUsage:\n create-ailk apply-design <name> [--target <site-dir>]\n create-ailk apply-design <path> [--target <site-dir>]\n\nArguments:\n <name|path> A shipped design (standard, minimalism), or a\n directory holding a DESIGN.md. A relative path\n resolves from the current directory.\n\nOptions:\n --target <site-dir> An already-scaffolded repo (contains\n architecture.yaml). Defaults to the current dir.\n --help, -h Print this message.\n\nWrites only the look: DESIGN.md (replaced whole by the design's, keeping the\nsite's Source template and Version lines), the theme: block of\ncontent/_site.mdx, the generated apps/web/lib/site-chrome.ts, the look fields
|
|
69
|
+
export declare const APPLY_DESIGN_HELP = "create-ailk apply-design \u2014 make an existing site wear a design.\n\nUsage:\n create-ailk apply-design <name> [--target <site-dir>]\n create-ailk apply-design <path> [--target <site-dir>]\n\nArguments:\n <name|path> A shipped design (standard, minimalism), or a\n directory holding a DESIGN.md. A relative path\n resolves from the current directory.\n\nOptions:\n --target <site-dir> An already-scaffolded repo (contains\n architecture.yaml). Defaults to the current dir.\n --help, -h Print this message.\n\nWrites only the look: DESIGN.md (replaced whole by the design's, keeping the\nsite's Source template and Version lines), the theme: block of\ncontent/_site.mdx, the generated apps/web/lib/site-chrome.ts, the generated\napps/web/lib/site-fonts.ts, the look fields of sections the site's pages\nalready place, and the design/layout/theme record in .ailk-site-manifest.json.\nNever adds, removes or renames a route or page, never touches a content field\n(except a placeholder home's sections, below) or the brand: block, and leaves\nevery other page's structured data unchanged.\n\nA design may seed the placeholder home's sections \u2014 its home arc \u2014 while they\nare still a placeholder: the shape's own, or a design's arc of them. It never\nchanges a home whose sections were edited; it reports it and leaves it. A\nseeded home takes the design's looks; look values on the sections it replaced\nare not kept. A design that ships no arc restores the shape's own sections on\na home that still wears another design's untouched arc.\n\nA design other than standard needs a Pro or Pro Plus key: the site's own\nailk.key (put one there with create-ailk license set <key>), else\nAILK_LICENSE_KEY. A malformed package, a preset the validator refuses or a\nmissing key is refused before any file is written.\n\nA preset sets the look fields it names and never unsets one: a field it does\nnot name keeps whatever value the page has. Running it again changes nothing.\n";
|
|
51
70
|
/**
|
|
52
71
|
* Parse the argv AFTER the `apply-design` word (D1). Strict `node:util`
|
|
53
72
|
* parseArgs, like `parseSyncRoutesArgs`: exactly one positional, the design
|
|
@@ -57,7 +76,7 @@ export declare function parseApplyDesignArgs(argv: string[]): ApplyDesignArgs;
|
|
|
57
76
|
/**
|
|
58
77
|
* Apply a design to an existing site (D2–D7). Refuses before any write
|
|
59
78
|
* (C1–C6); otherwise writes only the files whose text changes — page files,
|
|
60
|
-
* `site-chrome.ts`, `DESIGN.md`, `_site.mdx`, then the manifest last.
|
|
79
|
+
* `site-chrome.ts`, `site-fonts.ts`, `DESIGN.md`, `_site.mdx`, then the manifest last.
|
|
61
80
|
*/
|
|
62
81
|
export declare function applyDesign(opts: ApplyDesignOptions): Promise<ApplyDesignResult>;
|
|
63
82
|
/** The changed-file report (D9). */
|
package/dist/apply-design.js
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
* resolver and theme splicer, #6816's preset validator and applier, #6760's
|
|
8
8
|
* design gate and #6813's manifest writer. Every refusal happens before the
|
|
9
9
|
* first write (D3); every new text is planned in memory, then written in one
|
|
10
|
-
* pass (D4). Routes, content fields, `brand:` and structured data never move
|
|
10
|
+
* pass (D4). Routes, content fields, `brand:` and structured data never move —
|
|
11
|
+
* except a placeholder home's `sections:`, which a design's home arc may
|
|
12
|
+
* replace (#7743 D7).
|
|
11
13
|
*
|
|
12
14
|
* Reads no module URL of its own — every package directory it reads is passed in
|
|
13
15
|
* — so Jest's CJS transform can import it, unlike `programmatic.ts`.
|
|
@@ -16,7 +18,8 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSy
|
|
|
16
18
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
17
19
|
import { parseArgs } from "node:util";
|
|
18
20
|
import { planDesignMd } from "./apply-design-md.js";
|
|
19
|
-
import { compileDesignTheme } from "./design-compile.js";
|
|
21
|
+
import { compileDesignFonts, compileDesignTheme } from "./design-compile.js";
|
|
22
|
+
import { HOME_PAGE_PATH, designHomeSeeds, planDesignHome, shapeHomePath, shippedHomeSeeds, } from "./design-home.js";
|
|
20
23
|
import { applyDesignPresets, readSectionLookCatalog, shippedPageTypes, validateDesignPresets, } from "./design-presets.js";
|
|
21
24
|
import { resolveDesign, spliceThemeBlock } from "./design-scaffold.js";
|
|
22
25
|
import { LICENSE_KEY_FILE } from "./lib/license-key-files.js";
|
|
@@ -24,7 +27,10 @@ import { CONVERSION_HINT_URL, LicenseRequiredError, assertDesignAllowed, deriveL
|
|
|
24
27
|
import { LEVEL_LABEL } from "./lib/module-tier.js";
|
|
25
28
|
import { ParseError } from "./parse-args.js";
|
|
26
29
|
import { SITE_CHROME_PATH, renderSiteChrome, resolveSiteChrome, } from "./site-chrome.js";
|
|
27
|
-
import {
|
|
30
|
+
import { SITE_FONTS_PATH, renderSiteFonts } from "./site-fonts.js";
|
|
31
|
+
import { SITE_ROSTER_PATH, planHomeRecord, planSiteManifestDesign, readSiteRoster, } from "./site-roster.js";
|
|
32
|
+
/** The site's locale layout — the one reader of the generated site-fonts.ts. */
|
|
33
|
+
const SITE_LAYOUT_PATH = "apps/web/app/[locale]/layout.tsx";
|
|
28
34
|
export const APPLY_DESIGN_HELP = `create-ailk apply-design — make an existing site wear a design.
|
|
29
35
|
|
|
30
36
|
Usage:
|
|
@@ -43,11 +49,19 @@ Options:
|
|
|
43
49
|
|
|
44
50
|
Writes only the look: DESIGN.md (replaced whole by the design's, keeping the
|
|
45
51
|
site's Source template and Version lines), the theme: block of
|
|
46
|
-
content/_site.mdx, the generated apps/web/lib/site-chrome.ts, the
|
|
47
|
-
of sections the site's pages
|
|
48
|
-
in .ailk-site-manifest.json.
|
|
52
|
+
content/_site.mdx, the generated apps/web/lib/site-chrome.ts, the generated
|
|
53
|
+
apps/web/lib/site-fonts.ts, the look fields of sections the site's pages
|
|
54
|
+
already place, and the design/layout/theme record in .ailk-site-manifest.json.
|
|
49
55
|
Never adds, removes or renames a route or page, never touches a content field
|
|
50
|
-
or the brand: block, and leaves
|
|
56
|
+
(except a placeholder home's sections, below) or the brand: block, and leaves
|
|
57
|
+
every other page's structured data unchanged.
|
|
58
|
+
|
|
59
|
+
A design may seed the placeholder home's sections — its home arc — while they
|
|
60
|
+
are still a placeholder: the shape's own, or a design's arc of them. It never
|
|
61
|
+
changes a home whose sections were edited; it reports it and leaves it. A
|
|
62
|
+
seeded home takes the design's looks; look values on the sections it replaced
|
|
63
|
+
are not kept. A design that ships no arc restores the shape's own sections on
|
|
64
|
+
a home that still wears another design's untouched arc.
|
|
51
65
|
|
|
52
66
|
A design other than standard needs a Pro or Pro Plus key: the site's own
|
|
53
67
|
ailk.key (put one there with create-ailk license set <key>), else
|
|
@@ -118,7 +132,7 @@ function readSiteKey(targetAbs, env) {
|
|
|
118
132
|
/**
|
|
119
133
|
* Apply a design to an existing site (D2–D7). Refuses before any write
|
|
120
134
|
* (C1–C6); otherwise writes only the files whose text changes — page files,
|
|
121
|
-
* `site-chrome.ts`, `DESIGN.md`, `_site.mdx`, then the manifest last.
|
|
135
|
+
* `site-chrome.ts`, `site-fonts.ts`, `DESIGN.md`, `_site.mdx`, then the manifest last.
|
|
122
136
|
*/
|
|
123
137
|
export async function applyDesign(opts) {
|
|
124
138
|
const targetAbs = resolve(opts.targetDir);
|
|
@@ -146,13 +160,18 @@ export async function applyDesign(opts) {
|
|
|
146
160
|
const { pkg } = resolved;
|
|
147
161
|
const hasPresets = Object.keys(pkg.sections).length > 0;
|
|
148
162
|
// C4 — the presets, validated exactly as scaffold() validates them.
|
|
163
|
+
let lookCatalog;
|
|
164
|
+
const catalog = () => (lookCatalog ??= readSectionLookCatalog(opts.catalogPath));
|
|
149
165
|
if (hasPresets) {
|
|
150
166
|
validateDesignPresets(pkg.sections, {
|
|
151
|
-
catalog:
|
|
167
|
+
catalog: catalog(),
|
|
152
168
|
pageTypes: shippedPageTypes(opts.siteTemplatesDir),
|
|
153
169
|
file: join(resolved.dir, "DESIGN.md"),
|
|
154
170
|
});
|
|
155
171
|
}
|
|
172
|
+
// #7743 D7 — the home seeds (the design's and every shipped design's), validated beside the presets.
|
|
173
|
+
const designSeeds = designHomeSeeds(resolved, { designsDir: opts.designsDir, catalog });
|
|
174
|
+
const shippedSeeds = shippedHomeSeeds({ designsDir: opts.designsDir, catalog });
|
|
156
175
|
// C5 — the design gate, fed the site's own key (D6). A Free design reads no key.
|
|
157
176
|
if (!resolved.free) {
|
|
158
177
|
const key = readSiteKey(targetAbs, env);
|
|
@@ -179,11 +198,45 @@ export async function applyDesign(opts) {
|
|
|
179
198
|
const chromePath = join(targetAbs, SITE_CHROME_PATH);
|
|
180
199
|
const chromeCurrent = existsSync(chromePath) ? readFileSync(chromePath, "utf8") : undefined;
|
|
181
200
|
const chromeNext = renderSiteChrome(chrome);
|
|
201
|
+
// #7720 D4.2 — a face-less design never adds the file to a site that lacks it.
|
|
202
|
+
const fonts = compileDesignFonts(pkg);
|
|
203
|
+
const fontsPath = join(targetAbs, SITE_FONTS_PATH);
|
|
204
|
+
const fontsCurrent = existsSync(fontsPath) ? readFileSync(fontsPath, "utf8") : undefined;
|
|
205
|
+
const fontsNext = renderSiteFonts(fonts);
|
|
206
|
+
const writeFonts = fontsNext !== fontsCurrent && (fontsCurrent !== undefined || fonts !== undefined);
|
|
207
|
+
const fontFamilies = writeFonts && fonts !== undefined
|
|
208
|
+
? [...new Set([fonts.display?.family, fonts.body?.family])].filter((family) => family !== undefined)
|
|
209
|
+
: undefined;
|
|
210
|
+
const layoutPath = join(targetAbs, SITE_LAYOUT_PATH);
|
|
211
|
+
const fontsUnwired = fontFamilies !== undefined &&
|
|
212
|
+
!(existsSync(layoutPath) && readFileSync(layoutPath, "utf8").includes("lib/site-fonts"));
|
|
182
213
|
const designMdPath = join(targetAbs, "DESIGN.md");
|
|
183
214
|
const designMdCurrent = existsSync(designMdPath) ? readFileSync(designMdPath, "utf8") : undefined;
|
|
184
215
|
const designMdNext = planDesignMd(resolved.dir, targetAbs);
|
|
216
|
+
// #7743 D7 — the home is planned before the presets, so a seeded home is
|
|
217
|
+
// written once, carrying both. The one `planDesignHome` call site here.
|
|
218
|
+
const shape = roster?.template.shape;
|
|
219
|
+
const target = designSeeds.find((seed) => seed.shape === shape);
|
|
220
|
+
// The arc this site would take: the shape's seed, or (with no shape to pick one) any seed at all.
|
|
221
|
+
const homeArc = target !== undefined || (shape === undefined && designSeeds.length > 0);
|
|
222
|
+
const homePath = join(targetAbs, ...HOME_PAGE_PATH.split("/"));
|
|
223
|
+
const current = existsSync(homePath) ? readFileSync(homePath, "utf8") : undefined;
|
|
224
|
+
const homePlan = planDesignHome({
|
|
225
|
+
current,
|
|
226
|
+
shape,
|
|
227
|
+
target,
|
|
228
|
+
shapeHome: shape === undefined || current === undefined
|
|
229
|
+
? ""
|
|
230
|
+
: readFileSync(shapeHomePath(shape, {
|
|
231
|
+
siteTemplatesDir: opts.siteTemplatesDir,
|
|
232
|
+
templatesDir: opts.templatesDir ?? join(dirname(opts.siteTemplatesDir), "templates"),
|
|
233
|
+
}), "utf8"),
|
|
234
|
+
shippedSeeds,
|
|
235
|
+
catalog: catalog(),
|
|
236
|
+
});
|
|
237
|
+
const homeAction = homePlan.outcome === "seeded" || homePlan.outcome === "restored" ? homePlan.outcome : undefined;
|
|
185
238
|
const pageWrites = [];
|
|
186
|
-
if (
|
|
239
|
+
if (roster !== undefined && (hasPresets || homeAction !== undefined)) {
|
|
187
240
|
const pageTypes = new Set();
|
|
188
241
|
for (const row of roster.rows) {
|
|
189
242
|
const pageType = row.value["pageType"];
|
|
@@ -191,17 +244,28 @@ export async function applyDesign(opts) {
|
|
|
191
244
|
pageTypes.add(pageType);
|
|
192
245
|
}
|
|
193
246
|
const contentDir = join(targetAbs, "content");
|
|
247
|
+
// An authored home kept from a design's arc is reported as kept, so no preset dresses it either (D3, F4).
|
|
248
|
+
const homeKept = homePlan.outcome === "authored" && homeArc;
|
|
194
249
|
const pages = listMdxFiles(contentDir)
|
|
195
|
-
.filter((path) => path !== siteMdxPath)
|
|
196
|
-
.map((path) => ({
|
|
197
|
-
|
|
198
|
-
|
|
250
|
+
.filter((path) => path !== siteMdxPath && !(homeKept && path === homePath))
|
|
251
|
+
.map((path) => ({
|
|
252
|
+
path,
|
|
253
|
+
text: path === homePath && homePlan.text !== undefined ? homePlan.text : readFileSync(path, "utf8"),
|
|
254
|
+
}));
|
|
255
|
+
const presetResult = hasPresets
|
|
256
|
+
? applyDesignPresets(pages, pkg.sections, { pageTypes })
|
|
257
|
+
: { pages: [], applied: [] };
|
|
258
|
+
for (const page of pages) {
|
|
259
|
+
const preset = presetResult.pages.find((p) => p.path === page.path);
|
|
260
|
+
const home = page.path === homePath ? homeAction : undefined;
|
|
261
|
+
if (preset === undefined && home === undefined)
|
|
262
|
+
continue;
|
|
199
263
|
const fields = [
|
|
200
264
|
...new Set(presetResult.applied
|
|
201
265
|
.filter((a) => a.path === page.path)
|
|
202
266
|
.map((a) => `${a.sectionType}.${a.field}`)),
|
|
203
267
|
].sort();
|
|
204
|
-
pageWrites.push({ path: page.path, text: page.text, fields });
|
|
268
|
+
pageWrites.push({ path: page.path, text: preset?.text ?? page.text, fields, ...(home !== undefined ? { home } : {}) });
|
|
205
269
|
}
|
|
206
270
|
}
|
|
207
271
|
let manifest = "no-roster";
|
|
@@ -210,23 +274,37 @@ export async function applyDesign(opts) {
|
|
|
210
274
|
if (roster !== undefined && rosterAbs !== undefined) {
|
|
211
275
|
if (roster.path === SITE_ROSTER_PATH) {
|
|
212
276
|
manifestNext = planSiteManifestDesign(readFileSync(rosterAbs, "utf8"), { design: resolved.recorded, layout: chrome, theme }, rosterAbs);
|
|
277
|
+
// #7743 D6 — the `/` row records the home as written, in the same single write.
|
|
278
|
+
const home = pageWrites.find((page) => page.home !== undefined);
|
|
279
|
+
if (home !== undefined) {
|
|
280
|
+
manifestNext =
|
|
281
|
+
planHomeRecord(manifestNext ?? readFileSync(rosterAbs, "utf8"), home.text, catalog()) ?? manifestNext;
|
|
282
|
+
}
|
|
213
283
|
manifest = manifestNext === undefined ? "unchanged" : "recorded";
|
|
214
284
|
}
|
|
215
285
|
else {
|
|
216
286
|
manifest = "legacy-roster";
|
|
217
287
|
}
|
|
218
288
|
}
|
|
219
|
-
// Write — page files, site-chrome.ts, DESIGN.md, _site.mdx, the manifest last (D4).
|
|
289
|
+
// Write — page files, site-chrome.ts, site-fonts.ts, DESIGN.md, _site.mdx, the manifest last (D4).
|
|
220
290
|
const changes = [];
|
|
221
291
|
for (const page of pageWrites) {
|
|
222
292
|
writeFileSync(page.path, page.text);
|
|
223
|
-
|
|
293
|
+
const path = siteRelative(targetAbs, page.path);
|
|
294
|
+
changes.push(page.home !== undefined
|
|
295
|
+
? { path, kind: "home", action: page.home, fields: page.fields }
|
|
296
|
+
: { path, kind: "preset", fields: page.fields });
|
|
224
297
|
}
|
|
225
298
|
if (chromeNext !== chromeCurrent) {
|
|
226
299
|
mkdirSync(dirname(chromePath), { recursive: true });
|
|
227
300
|
writeFileSync(chromePath, chromeNext);
|
|
228
301
|
changes.push({ path: SITE_CHROME_PATH, kind: "layout" });
|
|
229
302
|
}
|
|
303
|
+
if (writeFonts) {
|
|
304
|
+
mkdirSync(dirname(fontsPath), { recursive: true });
|
|
305
|
+
writeFileSync(fontsPath, fontsNext);
|
|
306
|
+
changes.push({ path: SITE_FONTS_PATH, kind: "fonts" });
|
|
307
|
+
}
|
|
230
308
|
if (designMdNext !== designMdCurrent) {
|
|
231
309
|
writeFileSync(designMdPath, designMdNext);
|
|
232
310
|
changes.push({ path: "DESIGN.md", kind: "design" });
|
|
@@ -245,7 +323,11 @@ export async function applyDesign(opts) {
|
|
|
245
323
|
display: resolved.display,
|
|
246
324
|
changes,
|
|
247
325
|
manifest,
|
|
326
|
+
home: homePlan.outcome,
|
|
327
|
+
...(homeArc ? { homeArc: true } : {}),
|
|
248
328
|
...(roster !== undefined ? { rosterPath: roster.path } : {}),
|
|
329
|
+
...(fontFamilies !== undefined ? { fonts: fontFamilies } : {}),
|
|
330
|
+
...(fontsUnwired ? { fontsUnwired: true } : {}),
|
|
249
331
|
};
|
|
250
332
|
}
|
|
251
333
|
/** The changed-file report (D9). */
|
|
@@ -257,11 +339,30 @@ export function formatApplyDesignReport(result) {
|
|
|
257
339
|
else {
|
|
258
340
|
lines.push(`[create-ailk] Applied design "${result.design}" to ${result.targetDir}`);
|
|
259
341
|
for (const change of result.changes) {
|
|
260
|
-
const suffix = change.kind === "preset"
|
|
342
|
+
const suffix = change.kind === "preset"
|
|
343
|
+
? ` (${change.fields.join(", ")})`
|
|
344
|
+
: change.kind === "home"
|
|
345
|
+
? ` (${[change.action, change.fields.join(", ")].filter((part) => part !== "").join("; ")})`
|
|
346
|
+
: "";
|
|
261
347
|
lines.push(` ${change.kind.padEnd(8)} ${change.path}${suffix}`);
|
|
262
348
|
}
|
|
263
349
|
const n = result.changes.length;
|
|
264
|
-
|
|
350
|
+
const homeWritten = result.changes.some((change) => change.kind === "home");
|
|
351
|
+
lines.push(`${n} file${n === 1 ? "" : "s"} changed. ${homeWritten
|
|
352
|
+
? "Routes are unchanged; the home page's sections were replaced."
|
|
353
|
+
: "Routes and structured data are unchanged."}`);
|
|
354
|
+
}
|
|
355
|
+
if (result.home === "authored" && result.homeArc === true) {
|
|
356
|
+
lines.push(`home kept: ${HOME_PAGE_PATH} has authored sections — the ${result.design} home arc was not applied`);
|
|
357
|
+
}
|
|
358
|
+
else if (result.home === "no-shape" && result.homeArc === true) {
|
|
359
|
+
lines.push("home not seeded: the site records no shape");
|
|
360
|
+
}
|
|
361
|
+
if (result.fonts !== undefined) {
|
|
362
|
+
lines.push(`fonts: ${result.fonts.join(", ")} load at next build through next/font/google — a family that is not a variable Google font fails there`);
|
|
363
|
+
}
|
|
364
|
+
if (result.fontsUnwired === true) {
|
|
365
|
+
lines.push(`fonts not loaded: ${SITE_LAYOUT_PATH} predates lib/site-fonts — update the layout from the current create-ailk overlay`);
|
|
265
366
|
}
|
|
266
367
|
if (result.manifest === "legacy-roster") {
|
|
267
368
|
lines.push(`manifest not recorded: ${result.rosterPath} predates the design record`);
|
package/dist/cli.js
CHANGED
|
@@ -201,6 +201,7 @@ async function main() {
|
|
|
201
201
|
targetDir: args.target ?? process.cwd(),
|
|
202
202
|
designsDir: resolve(PKG_ROOT, "site-templates", "designs"),
|
|
203
203
|
siteTemplatesDir: resolve(PKG_ROOT, "site-templates"),
|
|
204
|
+
templatesDir: resolve(PKG_ROOT, "templates"),
|
|
204
205
|
catalogPath: resolve(PKG_ROOT, "component-catalog.json"),
|
|
205
206
|
});
|
|
206
207
|
process.stdout.write(formatApplyDesignReport(result));
|
package/dist/design-compile.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* this compiles to what `SiteThemeSchema` accepts is test-only, by relative
|
|
16
16
|
* path, in `design-compile.test.ts`.
|
|
17
17
|
*/
|
|
18
|
-
import type
|
|
18
|
+
import { type DesignPackage } from "./design-package.js";
|
|
19
19
|
/** The nine runtime colour slots this compile emits, per mode (D4). */
|
|
20
20
|
export type RuntimeColorSlot = "bg" | "bgMuted" | "surface" | "border" | "text" | "textMuted" | "primary" | "primaryFg" | "accent";
|
|
21
21
|
/** One mode's compiled palette — the shape `SiteThemeSchema`'s partial palette accepts. */
|
|
@@ -28,11 +28,72 @@ export interface CompiledDesignTheme {
|
|
|
28
28
|
eyebrowCase?: "upper" | "sentence";
|
|
29
29
|
placeholders?: "outline" | "fill";
|
|
30
30
|
checkmarks?: "circle" | "plain";
|
|
31
|
+
/** #7741 D7 — the button shape, copied from `treatments.buttonShape`. */
|
|
32
|
+
buttonShape?: "control" | "pill";
|
|
33
|
+
/** #7739 D2 — the ink of headings and labels on a dark ground, verbatim from the package. */
|
|
34
|
+
darkInk?: "fg" | "accent";
|
|
35
|
+
/** #7739 D2 — the stat-numeral treatment, verbatim from the package. */
|
|
36
|
+
figures?: "platform" | "display";
|
|
37
|
+
/**
|
|
38
|
+
* #7720 D2 — the display leading pair, present only when the display role
|
|
39
|
+
* declares `lineHeightMobile`; or, under `typeScale: design` (#7739 D2.2),
|
|
40
|
+
* every declared field of the five compiled roles.
|
|
41
|
+
*/
|
|
42
|
+
typography?: CompiledTypography;
|
|
43
|
+
/**
|
|
44
|
+
* #7719 D6 — `"site"` paints dark-scoped bands with the site's own dark
|
|
45
|
+
* colours. Derived (never authored): emitted when the package names the
|
|
46
|
+
* `solid-dark` surface or declares `darkInk: accent` (#7739 D2.3); absent otherwise.
|
|
47
|
+
*/
|
|
48
|
+
darkBands?: "platform" | "site";
|
|
31
49
|
colors: {
|
|
32
50
|
light: CompiledColorPalette;
|
|
33
51
|
dark: CompiledColorPalette;
|
|
34
52
|
};
|
|
35
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* The compiled type roles (#7720, #7739 D2.2) — the shape `SiteThemeSchema`'s
|
|
56
|
+
* `typography` accepts. Each role is present only when it carries a field.
|
|
57
|
+
*/
|
|
58
|
+
export interface CompiledTypography {
|
|
59
|
+
display?: {
|
|
60
|
+
fontSize?: string;
|
|
61
|
+
fontSizeMobile?: string;
|
|
62
|
+
fontWeight?: number;
|
|
63
|
+
lineHeight?: number;
|
|
64
|
+
lineHeightMobile?: number;
|
|
65
|
+
};
|
|
66
|
+
"heading-section"?: {
|
|
67
|
+
fontSize?: string;
|
|
68
|
+
fontSizeMobile?: string;
|
|
69
|
+
fontWeight?: number;
|
|
70
|
+
};
|
|
71
|
+
"heading-item"?: {
|
|
72
|
+
fontSize?: string;
|
|
73
|
+
fontSizeMobile?: string;
|
|
74
|
+
fontWeight?: number;
|
|
75
|
+
};
|
|
76
|
+
body?: {
|
|
77
|
+
fontSize?: string;
|
|
78
|
+
lineHeight?: number;
|
|
79
|
+
};
|
|
80
|
+
eyebrow?: {
|
|
81
|
+
fontSize?: string;
|
|
82
|
+
fontWeight?: number;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** One sourced face (#7720 D3.1): the first family, unquoted, and the rest of the authored stack. */
|
|
86
|
+
export interface CompiledFace {
|
|
87
|
+
source: "google";
|
|
88
|
+
family: string;
|
|
89
|
+
/** The authored stack after the first comma, trimmed and verbatim; "" when there is none. */
|
|
90
|
+
fallback: string;
|
|
91
|
+
}
|
|
92
|
+
/** `compileDesignFonts`'s return shape — the faces `apps/web/lib/site-fonts.ts` loads. */
|
|
93
|
+
export interface CompiledDesignFonts {
|
|
94
|
+
display?: CompiledFace;
|
|
95
|
+
body?: CompiledFace;
|
|
96
|
+
}
|
|
36
97
|
/** Every authored colour key the map may read from (design-package.ts D3). */
|
|
37
98
|
type DesignAuthoredColorKey = "bg" | "fg" | "surface" | "surface-secondary" | "border" | "text-muted" | "brand" | "fg-on-brand" | "accent" | "bg-dark" | "fg-dark" | "border-dark" | "brand-dark" | "surface-dark" | "surface-secondary-dark" | "text-muted-dark" | "fg-on-brand-dark" | "accent-dark";
|
|
38
99
|
/** The five named lossy cases with a row (D4) — an authored key absent falls back. */
|
|
@@ -59,7 +120,7 @@ export declare const DESIGN_COLOR_MAP: readonly DesignColorMapRow[];
|
|
|
59
120
|
*/
|
|
60
121
|
export declare const DESIGN_COMPILE_LOSSY: {
|
|
61
122
|
readonly L6: "accentFg, destructive and destructiveFg have no authored source and are never emitted by this compile — the partial palette inherits the platform default.";
|
|
62
|
-
readonly L7: "typography, rounded and spacing are carried in the package and not compiled — SiteThemeSchema rejects fonts/radius/spacing by name.";
|
|
123
|
+
readonly L7: "typography, rounded and spacing are carried in the package and not compiled — SiteThemeSchema rejects fonts/radius/spacing by name. Three exceptions: the display role's leading pair (lineHeight, lineHeightMobile) compiles into theme.typography.display when lineHeightMobile is declared (#7720); the display/body faces a fontSource names compile to apps/web/lib/site-fonts.ts (#7720); and under treatments.typeScale: design the display, heading-section, heading-item, body and eyebrow roles' sizes, phone sizes, weights and leadings compile into theme.typography (#7739) — never their families or letterSpacing.";
|
|
63
124
|
readonly L8: "an absent treatments key is omitted from the compiled theme block, not defaulted.";
|
|
64
125
|
};
|
|
65
126
|
/**
|
|
@@ -68,4 +129,11 @@ export declare const DESIGN_COMPILE_LOSSY: {
|
|
|
68
129
|
* performs no I/O.
|
|
69
130
|
*/
|
|
70
131
|
export declare function compileDesignTheme(pkg: DesignPackage): CompiledDesignTheme;
|
|
132
|
+
/**
|
|
133
|
+
* The faces a package's display and body roles source (#7720 D3.1), or
|
|
134
|
+
* `undefined` when neither declares `fontSource`. Pure, like
|
|
135
|
+
* `compileDesignTheme`; `readDesignPackage` has already constrained every
|
|
136
|
+
* stack it reads here.
|
|
137
|
+
*/
|
|
138
|
+
export declare function compileDesignFonts(pkg: DesignPackage): CompiledDesignFonts | undefined;
|
|
71
139
|
export {};
|
package/dist/design-compile.js
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compile a `DesignPackage` (design-package.ts) into the `theme:` block a
|
|
3
|
+
* site's `_site.mdx` carries (#6812 D4, spec docs/specs/6812-the-design-package-design-md-spec.md).
|
|
4
|
+
*
|
|
5
|
+
* `compileDesignTheme` is pure and idempotent: it reads `pkg` only, never
|
|
6
|
+
* mutates it, and has no I/O. The authored→runtime key map is the single
|
|
7
|
+
* exported table `DESIGN_COLOR_MAP` below — every lossy translation (a
|
|
8
|
+
* runtime slot the authored quartet has no direct source for) is named as a
|
|
9
|
+
* `fallback` + `lossy` case, and the cases with no row at all are named in
|
|
10
|
+
* `DESIGN_COMPILE_LOSSY`.
|
|
11
|
+
*
|
|
12
|
+
* This module imports nothing from `@working-theory/site-config` — the
|
|
13
|
+
* `theme:` block's shape is reproduced structurally here so `create-ailk`
|
|
14
|
+
* gains no runtime dependency (§ 1.5); the cross-package read that proves
|
|
15
|
+
* this compiles to what `SiteThemeSchema` accepts is test-only, by relative
|
|
16
|
+
* path, in `design-compile.test.ts`.
|
|
17
|
+
*/
|
|
18
|
+
import { unquoteFamily } from "./design-package.js";
|
|
19
|
+
/**
|
|
20
|
+
* #7739 D2.2 — the fields `typeScale: design` compiles, per role, in emission
|
|
21
|
+
* order. Roles are emitted in this table's order.
|
|
22
|
+
*/
|
|
23
|
+
const COMPILED_TYPE_FIELDS = {
|
|
24
|
+
display: ["fontSize", "fontSizeMobile", "fontWeight", "lineHeight", "lineHeightMobile"],
|
|
25
|
+
"heading-section": ["fontSize", "fontSizeMobile", "fontWeight"],
|
|
26
|
+
"heading-item": ["fontSize", "fontSizeMobile", "fontWeight"],
|
|
27
|
+
body: ["fontSize", "lineHeight"],
|
|
28
|
+
eyebrow: ["fontSize", "fontWeight"],
|
|
29
|
+
};
|
|
1
30
|
/**
|
|
2
31
|
* The authored→runtime key map (D4) — 18 rows, one per emitted runtime slot
|
|
3
32
|
* per mode, in the overlay block's key order (bg bgMuted surface border text
|
|
@@ -62,9 +91,46 @@ export const DESIGN_COLOR_MAP = [
|
|
|
62
91
|
*/
|
|
63
92
|
export const DESIGN_COMPILE_LOSSY = {
|
|
64
93
|
L6: "accentFg, destructive and destructiveFg have no authored source and are never emitted by this compile — the partial palette inherits the platform default.",
|
|
65
|
-
L7: "typography, rounded and spacing are carried in the package and not compiled — SiteThemeSchema rejects fonts/radius/spacing by name.",
|
|
94
|
+
L7: "typography, rounded and spacing are carried in the package and not compiled — SiteThemeSchema rejects fonts/radius/spacing by name. Three exceptions: the display role's leading pair (lineHeight, lineHeightMobile) compiles into theme.typography.display when lineHeightMobile is declared (#7720); the display/body faces a fontSource names compile to apps/web/lib/site-fonts.ts (#7720); and under treatments.typeScale: design the display, heading-section, heading-item, body and eyebrow roles' sizes, phone sizes, weights and leadings compile into theme.typography (#7739) — never their families or letterSpacing.",
|
|
66
95
|
L8: "an absent treatments key is omitted from the compiled theme block, not defaulted.",
|
|
67
96
|
};
|
|
97
|
+
/** Whether a package names the `solid-dark` surface — in its deck grounds or any section preset (#7719 D6). */
|
|
98
|
+
function usesSolidDark(pkg) {
|
|
99
|
+
if (pkg.layout.deckGrounds?.includes("solid-dark"))
|
|
100
|
+
return true;
|
|
101
|
+
return Object.values(pkg.sections).some((sections) => Object.values(sections).some((preset) => typeof preset === "object" &&
|
|
102
|
+
preset !== null &&
|
|
103
|
+
preset.surface === "solid-dark"));
|
|
104
|
+
}
|
|
105
|
+
/** The `typography` group a package compiles (#7720 D2, #7739 D2.2), or `undefined`. */
|
|
106
|
+
function compileTypography(pkg) {
|
|
107
|
+
if (pkg.treatments?.typeScale === "design") {
|
|
108
|
+
const result = {};
|
|
109
|
+
for (const [role, fields] of Object.entries(COMPILED_TYPE_FIELDS)) {
|
|
110
|
+
const def = pkg.typography[role];
|
|
111
|
+
if (def === undefined)
|
|
112
|
+
continue;
|
|
113
|
+
const compiled = {};
|
|
114
|
+
for (const field of fields) {
|
|
115
|
+
const value = def[field];
|
|
116
|
+
if (value !== undefined)
|
|
117
|
+
compiled[field] = value;
|
|
118
|
+
}
|
|
119
|
+
if (Object.keys(compiled).length > 0)
|
|
120
|
+
result[role] = compiled;
|
|
121
|
+
}
|
|
122
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
123
|
+
}
|
|
124
|
+
const display = pkg.typography["display"];
|
|
125
|
+
if (display?.lineHeightMobile === undefined)
|
|
126
|
+
return undefined;
|
|
127
|
+
return {
|
|
128
|
+
display: {
|
|
129
|
+
...(display.lineHeight !== undefined && { lineHeight: display.lineHeight }),
|
|
130
|
+
lineHeightMobile: display.lineHeightMobile,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
68
134
|
/**
|
|
69
135
|
* Translate a `DesignPackage` into the `theme:` block `SiteThemeSchema.parse`
|
|
70
136
|
* accepts (D4). Pure and idempotent — reads `pkg` only, mutates nothing, and
|
|
@@ -88,11 +154,41 @@ export function compileDesignTheme(pkg) {
|
|
|
88
154
|
target[row.runtime] = value;
|
|
89
155
|
}
|
|
90
156
|
const treatments = pkg.treatments ?? {};
|
|
157
|
+
const typography = compileTypography(pkg);
|
|
158
|
+
// #7739 D2.3 — accent ink needs the site's own palette on the band, or the
|
|
159
|
+
// accent there would be the platform's.
|
|
160
|
+
const darkBands = usesSolidDark(pkg) || treatments.darkInk === "accent";
|
|
91
161
|
return {
|
|
92
162
|
...(treatments.corners !== undefined && { corners: treatments.corners }),
|
|
93
163
|
...(treatments.eyebrowCase !== undefined && { eyebrowCase: treatments.eyebrowCase }),
|
|
94
164
|
...(treatments.placeholders !== undefined && { placeholders: treatments.placeholders }),
|
|
95
165
|
...(treatments.checkmarks !== undefined && { checkmarks: treatments.checkmarks }),
|
|
166
|
+
...(treatments.buttonShape !== undefined && { buttonShape: treatments.buttonShape }),
|
|
167
|
+
...(treatments.darkInk !== undefined && { darkInk: treatments.darkInk }),
|
|
168
|
+
...(treatments.figures !== undefined && { figures: treatments.figures }),
|
|
169
|
+
...(typography !== undefined && { typography }),
|
|
170
|
+
...(darkBands && { darkBands: "site" }),
|
|
96
171
|
colors: { light, dark },
|
|
97
172
|
};
|
|
98
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* The faces a package's display and body roles source (#7720 D3.1), or
|
|
176
|
+
* `undefined` when neither declares `fontSource`. Pure, like
|
|
177
|
+
* `compileDesignTheme`; `readDesignPackage` has already constrained every
|
|
178
|
+
* stack it reads here.
|
|
179
|
+
*/
|
|
180
|
+
export function compileDesignFonts(pkg) {
|
|
181
|
+
const fonts = {};
|
|
182
|
+
for (const role of ["display", "body"]) {
|
|
183
|
+
const def = pkg.typography[role];
|
|
184
|
+
if (def?.fontSource === undefined || def.fontFamily === undefined)
|
|
185
|
+
continue;
|
|
186
|
+
const comma = def.fontFamily.indexOf(",");
|
|
187
|
+
fonts[role] = {
|
|
188
|
+
source: def.fontSource,
|
|
189
|
+
family: unquoteFamily(comma === -1 ? def.fontFamily : def.fontFamily.slice(0, comma)),
|
|
190
|
+
fallback: comma === -1 ? "" : def.fontFamily.slice(comma + 1).trim(),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return fonts.display !== undefined || fonts.body !== undefined ? fonts : undefined;
|
|
194
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type SectionLookCatalog } from "./design-presets.js";
|
|
2
|
+
import type { ResolvedDesign } from "./design-scaffold.js";
|
|
3
|
+
/** Where a site's home page lives, site-relative. */
|
|
4
|
+
export declare const HOME_PAGE_PATH = "content/en/HomePage/home.mdx";
|
|
5
|
+
/** One `home/<shape>.mdx` file of a design package (D1). */
|
|
6
|
+
export interface HomeSeed {
|
|
7
|
+
readonly shape: string;
|
|
8
|
+
/** Absolute path, for refusals. */
|
|
9
|
+
readonly file: string;
|
|
10
|
+
readonly text: string;
|
|
11
|
+
}
|
|
12
|
+
export type HomeSeedOutcome = "seeded" | "restored" | "unchanged" | "authored" | "no-shape" | "no-home";
|
|
13
|
+
/**
|
|
14
|
+
* Read and validate `<dir>/home/*` (D1). Refuses with a `DesignPackageError`
|
|
15
|
+
* naming the file, before any write: a basename that is not a recognized
|
|
16
|
+
* shape, a page whose `type` is not `HomePage`, `sections` that cannot be
|
|
17
|
+
* located (or are not in the flow form a carry can splice), a section type
|
|
18
|
+
* the catalog does not carry, or a `logoCloud` whose `logos` is not `[]`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function readDesignHomeSeeds(dir: string, opts: {
|
|
21
|
+
catalog: () => SectionLookCatalog;
|
|
22
|
+
}): HomeSeed[];
|
|
23
|
+
/**
|
|
24
|
+
* The seeds a resolved design uses (D1): its own `home/`, else — a
|
|
25
|
+
* byte-identical copy of a shipped design IS that design — the shipped
|
|
26
|
+
* design's, else none.
|
|
27
|
+
*/
|
|
28
|
+
export declare function designHomeSeeds(resolved: ResolvedDesign, opts: {
|
|
29
|
+
designsDir: string;
|
|
30
|
+
catalog: () => SectionLookCatalog;
|
|
31
|
+
}): HomeSeed[];
|
|
32
|
+
/** Every shipped design's seeds (D3's placeholder set reads them all). */
|
|
33
|
+
export declare function shippedHomeSeeds(opts: {
|
|
34
|
+
designsDir: string;
|
|
35
|
+
catalog: () => SectionLookCatalog;
|
|
36
|
+
}): HomeSeed[];
|
|
37
|
+
/** The shape's own home: its shape tree's, else the overlay's (spec § 1.4). */
|
|
38
|
+
export declare function shapeHomePath(shape: string, opts: {
|
|
39
|
+
siteTemplatesDir: string;
|
|
40
|
+
templatesDir: string;
|
|
41
|
+
}): string;
|
|
42
|
+
/**
|
|
43
|
+
* A home's content fingerprint (D3): per section in order, its type and its
|
|
44
|
+
* non-look `data` entries as `[key, normalized raw value]`, sorted by key.
|
|
45
|
+
* A reflow, a look-field change or a preset leaves it unchanged; ids, the
|
|
46
|
+
* title, the description and the body are not in it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function fingerprint(text: string, catalog: SectionLookCatalog, file?: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* The seed's `sections:` value with D4's carry applied from `home`: a seed
|
|
51
|
+
* `logoCloud` (its `logos: []`) takes the raw `logos` of the home's first
|
|
52
|
+
* `logoCloud`, or is dropped when the home has none.
|
|
53
|
+
*/
|
|
54
|
+
export declare function applySeed(home: string, seed: HomeSeed, file?: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Plan a site home under a design (D3). Writes nothing: `text` is the new
|
|
57
|
+
* home when the outcome is `seeded` or `restored`, and absent otherwise.
|
|
58
|
+
*/
|
|
59
|
+
export declare function planDesignHome(opts: {
|
|
60
|
+
/** The site's content/en/HomePage/home.mdx, if any. */
|
|
61
|
+
current: string | undefined;
|
|
62
|
+
/** The roster's template shape. */
|
|
63
|
+
shape: string | undefined;
|
|
64
|
+
/** The applied design's seed for that shape (D1). */
|
|
65
|
+
target: HomeSeed | undefined;
|
|
66
|
+
/** The text of the shape's own home file (spec § 1.4). */
|
|
67
|
+
shapeHome: string;
|
|
68
|
+
/** Every shipped design's seeds (filtered to `shape` here). */
|
|
69
|
+
shippedSeeds: readonly HomeSeed[];
|
|
70
|
+
catalog: SectionLookCatalog;
|
|
71
|
+
}): {
|
|
72
|
+
outcome: HomeSeedOutcome;
|
|
73
|
+
text?: string;
|
|
74
|
+
};
|