create-ailk 0.13.0 → 0.15.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 +250 -36
- package/component-catalog.md +10 -6
- package/dist/add-module.d.ts +7 -0
- package/dist/add-module.js +34 -2
- package/dist/add-route.js +18 -2
- package/dist/apply-design.d.ts +32 -2
- package/dist/apply-design.js +155 -22
- package/dist/cli.js +24 -3
- package/dist/derive-architecture.d.ts +15 -2
- package/dist/derive-architecture.js +34 -6
- 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 +224 -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 +2 -1
- package/dist/index.js +1 -1
- package/dist/lib/chrome-readers.d.ts +21 -0
- package/dist/lib/chrome-readers.js +100 -0
- package/dist/lib/design-checks.d.ts +12 -1
- package/dist/lib/design-checks.js +38 -2
- package/dist/lib/extract-module-bundle.d.ts +11 -0
- package/dist/lib/extract-module-bundle.js +36 -6
- package/dist/lib/license-key-files.d.ts +6 -1
- package/dist/lib/license-key-files.js +9 -4
- package/dist/lib/module-license-gate.d.ts +9 -0
- package/dist/lib/module-license-gate.js +18 -0
- package/dist/module-architecture.d.ts +9 -0
- package/dist/module-architecture.js +6 -0
- package/dist/parse-args.d.ts +22 -0
- package/dist/parse-args.js +73 -0
- package/dist/programmatic.d.ts +20 -0
- package/dist/programmatic.js +180 -35
- package/dist/site-chrome.d.ts +42 -0
- package/dist/site-chrome.js +140 -2
- package/dist/site-fonts.d.ts +21 -0
- package/dist/site-fonts.js +54 -0
- package/dist/site-manifest.d.ts +12 -5
- package/dist/site-manifest.js +25 -11
- 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/sync-routes.js +11 -2
- package/dist/team-profiles.d.ts +29 -0
- package/dist/team-profiles.js +43 -0
- package/dist/topology.d.ts +131 -0
- package/dist/topology.js +533 -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 +166 -36
- 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/README.md +2 -0
- package/templates/apps/api/CLAUDE.md +6 -1
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/factories/agent-caller.ts +5 -1
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +10 -0
- package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +114 -1
- package/templates/apps/api/src/lib/__tests__/site.test.ts +54 -0
- package/templates/apps/api/src/lib/agent-store.ts +95 -3
- package/templates/apps/api/src/lib/site.ts +37 -0
- package/templates/apps/api/src/lib/spoke-store.ts +18 -2
- package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +4 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +22 -0
- package/templates/apps/api/src/middleware/auth.ts +3 -0
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +23 -0
- package/templates/apps/api/src/openapi/spec.ts +38 -2
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +50 -2
- package/templates/apps/api/src/routes/checkout/sessions.ts +7 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +275 -0
- package/templates/apps/api/src/routes/leads/index.ts +69 -6
- package/templates/apps/api/src/routes/webhooks/README.md +39 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +366 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +61 -1
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +199 -0
- package/templates/apps/api/src/services/default-tenant.ts +41 -7
- package/templates/apps/api/src/services/lead-routing.ts +148 -29
- package/templates/apps/api/src/services/public-site-resolver.ts +48 -0
- package/templates/apps/mcp/__tests__/index.test.ts +5 -5
- package/templates/apps/mcp/package.json +1 -1
- package/templates/apps/mcp/src/index.ts +6 -4
- package/templates/apps/mcp/src/lib/content-adapter.ts +2 -2
- package/templates/apps/mcp/src/lib/topology.generated.ts +24 -0
- package/templates/apps/web/CLAUDE.md +12 -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]/[slug]/[detail]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +3 -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__/page-renderer-migration.test.tsx +66 -14
- package/templates/apps/web/app/[locale]/contact/__tests__/site-contact-lines.test.ts +163 -0
- package/templates/apps/web/app/[locale]/contact/site-contact-lines.ts +68 -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 +212 -57
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +3 -0
- 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]/projects/__tests__/page-renderer-migration.test.tsx +5 -2
- 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]/thanks/[slug]/__tests__/page.test.tsx +3 -0
- 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/openapi.json/__tests__/route.test.ts +89 -1
- package/templates/apps/web/app/openapi.json/route.ts +69 -1
- package/templates/apps/web/instrumentation.ts +7 -4
- package/templates/apps/web/lib/__tests__/closing-footer-path.test.ts +96 -0
- package/templates/apps/web/lib/__tests__/profile-back-link.test.ts +95 -0
- package/templates/apps/web/lib/__tests__/relay.test.ts +290 -0
- package/templates/apps/web/lib/__tests__/site-brand.test.ts +52 -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/closing-footer-path.ts +50 -0
- package/templates/apps/web/lib/profile-back-link.ts +51 -0
- package/templates/apps/web/lib/relay.ts +206 -2
- package/templates/apps/web/lib/site-brand.tsx +22 -1
- package/templates/apps/web/lib/site-chrome.ts +15 -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/lib/topology.generated.ts +23 -0
- package/templates/apps/web/messages/en.json +7 -1
- package/templates/apps/web/package.json +1 -1
- package/templates/database/CHANGELOG.md +32 -0
- package/templates/database/__tests__/agents.db.test.ts +65 -0
- package/templates/database/__tests__/lead-idempotency-key.db.test.ts +92 -0
- package/templates/database/auth/schema.prisma +23 -0
- package/templates/database/inbox/schema.prisma +13 -0
- package/templates/database/jest.config.cjs +7 -11
- package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +35 -0
- package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +20 -0
- package/templates/database/migrations/20261009120000_agent_grants/migration.sql +33 -0
- package/templates/database/package.json +1 -1
- package/versions.json +35 -34
- 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
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// Auth route group — the marketing chrome is dropped on the five (auth) paths (#5963)
|
|
8
8
|
// Account area — the marketing chrome AND the consent Banner are dropped on /account paths (#7430)
|
|
9
9
|
// Site-declared theme — `_site.mdx` `theme:` block → token overlay → CSS vars
|
|
10
|
+
// Design web faces — generated `lib/site-fonts.ts` → beneath `brand.fonts` (#7720)
|
|
10
11
|
//
|
|
11
12
|
// Sits under app/[locale]/ and sets the <html lang> attribute to the
|
|
12
13
|
// active locale. next-intl's NextIntlClientProvider is wired here so
|
|
@@ -26,7 +27,6 @@ import "../globals.css";
|
|
|
26
27
|
|
|
27
28
|
import { Telemetry, VerificationMeta } from "@working-theory/analytics";
|
|
28
29
|
import { MarketingShell } from "@working-theory/templates/shells";
|
|
29
|
-
import { themeToCssVarsBothModes } from "@working-theory/theme";
|
|
30
30
|
import { Link, isRtl, localeHref } from "@working-theory/ui";
|
|
31
31
|
import { BRAND_PATHS, LocaleSwitcherNav } from "@working-theory/ui/blocks";
|
|
32
32
|
import {
|
|
@@ -47,18 +47,19 @@ import { NextIntlClientProvider } from "next-intl";
|
|
|
47
47
|
import { getMessages, getTranslations } from "next-intl/server";
|
|
48
48
|
import type { ReactElement, ReactNode } from "react";
|
|
49
49
|
|
|
50
|
-
import { auth } from "../../auth";
|
|
51
50
|
import { GoogleAnalytics } from "../../components/google-analytics";
|
|
52
51
|
import {
|
|
53
52
|
SignedInNav,
|
|
54
53
|
type SignedInNavProps,
|
|
55
54
|
} from "../../components/signed-in-nav";
|
|
56
55
|
import { isAccountPath, isAuthPath } from "../../lib/auth-routes";
|
|
56
|
+
import { isClosingFooterPath } from "../../lib/closing-footer-path";
|
|
57
57
|
import { assertEnabledLocale } from "../../lib/locale-guard";
|
|
58
58
|
import { footerColumns, navItems } from "../../lib/nav-config";
|
|
59
59
|
import { getOgImageUrl } from "../../lib/og";
|
|
60
|
-
import {
|
|
61
|
-
import {
|
|
60
|
+
import { backLinkHref } from "../../lib/profile-back-link";
|
|
61
|
+
import { COPYRIGHT_HOLDER, SITE_NAME } from "../../lib/site-brand";
|
|
62
|
+
import { siteChrome, type SiteChrome } from "../../lib/site-chrome";
|
|
62
63
|
import { siteLogo } from "../../lib/site-logo";
|
|
63
64
|
// Registers the `_site.mdx` identity with page-renderer at module load, so
|
|
64
65
|
// every page rendered under this layout carries the sitewide JSON-LD (#6597).
|
|
@@ -67,9 +68,25 @@ import "../../lib/site-identity";
|
|
|
67
68
|
// live listicle's landing page gets its shelves and each entry its page
|
|
68
69
|
// (#7521). Inert without AILK_LISTING_PUBLIC_KEY or the paid package.
|
|
69
70
|
import "../../lib/listings";
|
|
70
|
-
import {
|
|
71
|
+
import { siteThemeHead } from "../../lib/site-theme-head";
|
|
72
|
+
import { SIGN_IN_ENABLED, readSession } from "../../lib/topology.generated";
|
|
71
73
|
import { resolveBaseUrl } from "../../src/lib/routes";
|
|
72
74
|
|
|
75
|
+
import { readSiteContactLines } from "./contact/site-contact-lines";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The generated `siteChrome`, read through a widened type (#7740 D8): a
|
|
79
|
+
* scaffold generated before `deckPresentation` existed has no such member,
|
|
80
|
+
* so this compiles against both shapes of the generated file, as the home
|
|
81
|
+
* page reads `deckGrounds`. `colorMode` and `chromeStyle` (#7742 D1) likewise.
|
|
82
|
+
*/
|
|
83
|
+
const chrome: SiteChrome & {
|
|
84
|
+
deckPresentation?: true;
|
|
85
|
+
colorMode?: "light";
|
|
86
|
+
chromeStyle?: "minimal";
|
|
87
|
+
closingFooterPaths?: readonly string[];
|
|
88
|
+
} = siteChrome;
|
|
89
|
+
|
|
73
90
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
74
91
|
|
|
75
92
|
const DEFAULT_LOCALE = "en";
|
|
@@ -127,6 +144,15 @@ async function resolveThemeMode(): Promise<ThemeMode> {
|
|
|
127
144
|
}
|
|
128
145
|
}
|
|
129
146
|
|
|
147
|
+
/**
|
|
148
|
+
* The deck home's first slide (#7782): the element the consent bar waits on.
|
|
149
|
+
* The same section-slide selector `DeckSlideAdvance` walks (its `SLIDES`, minus
|
|
150
|
+
* the closing footer slide), so the first match is slide 1. The story that pins
|
|
151
|
+
* the geometry (`Shells/MarketingShell` → `Deck Consent Bar`) carries a copy.
|
|
152
|
+
*/
|
|
153
|
+
const DECK_FIRST_SLIDE_SELECTOR =
|
|
154
|
+
'.ailk-snap-deck .ailk-section-renderer > :not(.ailk-section-separator):not([data-section="separator"])';
|
|
155
|
+
|
|
130
156
|
/**
|
|
131
157
|
* Whether this visitor has already accepted or declined cookies (#5557).
|
|
132
158
|
*
|
|
@@ -226,11 +252,19 @@ export default async function LocaleLayout({
|
|
|
226
252
|
// message load or session read.
|
|
227
253
|
assertEnabledLocale(locale);
|
|
228
254
|
const messages = await getMessages();
|
|
229
|
-
|
|
255
|
+
// #7742 D2 — `colorMode: "light"` pins the mode: a saved dark cookie is
|
|
256
|
+
// ignored, no flash script runs and no theme switch renders.
|
|
257
|
+
const pinLight = chrome.colorMode === "light";
|
|
258
|
+
const themeMode: ThemeMode = pinLight ? "light" : await resolveThemeMode();
|
|
259
|
+
// #7742 D3/D5 — the minimal chrome: the closing slide, the hairline consent
|
|
260
|
+
// bar and the cue offset.
|
|
261
|
+
const isMinimalChrome = chrome.chromeStyle === "minimal";
|
|
230
262
|
const consentDecided = await hasConsentChoice();
|
|
231
263
|
// Server-side session read (#4984) — the same `auth.api.getSession` idiom
|
|
232
|
-
// the `(authed)` segment guard uses
|
|
233
|
-
|
|
264
|
+
// the `(authed)` segment guard uses, through the generated topology seam
|
|
265
|
+
// (#7426 D5): a spoke has no sign-in, so its render always answers null.
|
|
266
|
+
// Null when signed out or auth is unset.
|
|
267
|
+
const session = await readSession(await headers());
|
|
234
268
|
|
|
235
269
|
// Resolve nav labels server-side so Nav + Footer components stay
|
|
236
270
|
// i18n-hook-free and are testable without a NextIntlClientProvider wrapper.
|
|
@@ -325,6 +359,18 @@ export default async function LocaleLayout({
|
|
|
325
359
|
// `kind: "flow"` so Nav hands their triggers back through
|
|
326
360
|
// `renderFlowTrigger`, which `SignedInNav` owns (see NAV_CLIENT_ACTION_FLOW
|
|
327
361
|
// above).
|
|
362
|
+
// The signed-out auth pair, shared by the header (every page but the deck
|
|
363
|
+
// home) and the deck home's closing footer slide (#7718 D4).
|
|
364
|
+
// A spoke (#7426 D11) has no sign-in, so it offers no auth pair anywhere:
|
|
365
|
+
// not in the header, the deck's closing footer or a bare page's footer.
|
|
366
|
+
const authActions: readonly Extract<NavAction, { path: string }>[] =
|
|
367
|
+
SIGN_IN_ENABLED
|
|
368
|
+
? [
|
|
369
|
+
// NO_HAND_IMPLEMENT_OK: operator-directed live Free-template pass.
|
|
370
|
+
{ labelKey: "signIn", path: "/sign-in", variant: "outline-strong" },
|
|
371
|
+
{ labelKey: "signUp", path: "/sign-up", variant: "primary" },
|
|
372
|
+
]
|
|
373
|
+
: [];
|
|
328
374
|
const navActions: NavAction[] = session
|
|
329
375
|
? [
|
|
330
376
|
{ labelKey: "account", kind: "flow", flow: NAV_CLIENT_ACTION_FLOW },
|
|
@@ -335,11 +381,47 @@ export default async function LocaleLayout({
|
|
|
335
381
|
variant: "outline-strong",
|
|
336
382
|
},
|
|
337
383
|
]
|
|
338
|
-
: [
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
384
|
+
: [...authActions];
|
|
385
|
+
|
|
386
|
+
// The announcement bar is HOME-ONLY (#5469 D5): its route gate matches the
|
|
387
|
+
// pathname literally, and under next-intl's `as-needed` prefixing the home
|
|
388
|
+
// path is `/` for the default locale and `/<locale>` for every other one.
|
|
389
|
+
const homePath = locale === DEFAULT_LOCALE ? "/" : `/${locale}`;
|
|
390
|
+
|
|
391
|
+
// Deck home (#5612 D3): `siteChrome.home === "deck"` only ever applies
|
|
392
|
+
// `snap="mandatory"` + `deckHero` on the locale-root route — every other
|
|
393
|
+
// path renders byte-identically regardless of the knob. `x-pathname` is the
|
|
394
|
+
// same header `generateMetadata` above reads (forwarded by middleware.ts);
|
|
395
|
+
// Next.js's `headers()` is request-scoped and cheap to call again here.
|
|
396
|
+
const pathname = (await headers()).get("x-pathname") ?? homePath;
|
|
397
|
+
const isDeckHome = siteChrome.home === "deck" && pathname === homePath;
|
|
398
|
+
// Inner pages (#7721 D4): under `innerPages: "bare"` every page but the
|
|
399
|
+
// home wears the bare header (back arrow + the mark) and the one-bar
|
|
400
|
+
// footer. Fail-safe like the deck gate: a missing `x-pathname` resolves to
|
|
401
|
+
// the home path, so the full bar and its auth buttons render.
|
|
402
|
+
const isBareInner = siteChrome.innerPages === "bare" && pathname !== homePath;
|
|
403
|
+
|
|
404
|
+
// The deck home's header is bare (#7718 D1): no actions, signed in or out.
|
|
405
|
+
// Its auth links move to the footer, the deck's closing slide (D4) — the
|
|
406
|
+
// auth pair signed out, an Account link signed in (the route where Sign
|
|
407
|
+
// out lives; its label is the user's name, `navLabels.items.account`).
|
|
408
|
+
const headerActions: NavAction[] = isDeckHome || isBareInner ? [] : navActions;
|
|
409
|
+
const deckFooterActions = session
|
|
410
|
+
? [{ labelKey: "account", path: "/account", variant: "outline-strong" as const }]
|
|
411
|
+
: [...authActions];
|
|
412
|
+
// A bare inner page's auth (#7721 D3/D4) rides the one-bar footer's link
|
|
413
|
+
// group: Sign in / Sign up signed out, Account signed in (where Sign out
|
|
414
|
+
// lives). Labels from the existing `nav` namespace.
|
|
415
|
+
const accountLinks = !isBareInner
|
|
416
|
+
? []
|
|
417
|
+
: session
|
|
418
|
+
? [{ label: tNav("account"), href: localeHref(locale, "/account") }]
|
|
419
|
+
: SIGN_IN_ENABLED
|
|
420
|
+
? [
|
|
421
|
+
{ label: tNav("signIn"), href: localeHref(locale, "/sign-in") },
|
|
422
|
+
{ label: tNav("signUp"), href: localeHref(locale, "/sign-up") },
|
|
423
|
+
]
|
|
424
|
+
: [];
|
|
343
425
|
|
|
344
426
|
// A flow action needs `renderFlowTrigger`, a function — and this layout is a
|
|
345
427
|
// Server Component. Nav forwards that function into NavMobileMenu ("use
|
|
@@ -352,7 +434,7 @@ export default async function LocaleLayout({
|
|
|
352
434
|
// `renderFlowTrigger` and imports `navItems` (icon components are functions
|
|
353
435
|
// too) on the client side. Without one, Nav stays a Server Component and
|
|
354
436
|
// nothing callable crosses.
|
|
355
|
-
const hasFlowAction =
|
|
437
|
+
const hasFlowAction = headerActions.some((action) => action.kind === "flow");
|
|
356
438
|
|
|
357
439
|
// Shared brand mark injected into both Nav and Footer chrome slots. The
|
|
358
440
|
// mark comes from site-logo.tsx (the mark the /sign-in card also renders);
|
|
@@ -372,18 +454,17 @@ export default async function LocaleLayout({
|
|
|
372
454
|
// middleware and hreflang alternates read (NEXT_PUBLIC_ENABLED_LOCALES).
|
|
373
455
|
const enabledLocales = readLocales();
|
|
374
456
|
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const isDeckHome = siteChrome.home === "deck" && pathname === homePath;
|
|
457
|
+
// #7797 — an inner page the site lists in `closingFooterPaths` ends on the
|
|
458
|
+
// closing footer: the wordmark, the contact lines and the legal line. The
|
|
459
|
+
// home is never an inner page, and an absent or empty list is false for
|
|
460
|
+
// every page, so a site that lists none renders exactly as before.
|
|
461
|
+
const isClosingInner =
|
|
462
|
+
pathname !== homePath &&
|
|
463
|
+
isClosingFooterPath({
|
|
464
|
+
pathname,
|
|
465
|
+
paths: chrome.closingFooterPaths,
|
|
466
|
+
locales: enabledLocales,
|
|
467
|
+
});
|
|
387
468
|
|
|
388
469
|
// The (auth) route group's chrome branch (#5963).
|
|
389
470
|
//
|
|
@@ -424,7 +505,13 @@ export default async function LocaleLayout({
|
|
|
424
505
|
// `wordmark` (Nav.tsx:344), which are passed below. Scoped to the SAME
|
|
425
506
|
// `isDeckHome` gate MarketingShell's snap/deckHero use, so a non-deck
|
|
426
507
|
// route's render is byte-identical to before this branch existed.
|
|
427
|
-
|
|
508
|
+
// #7718 D1 — and `bar: "bare"`: no rule, fill or actions, the logomark
|
|
509
|
+
// alone, centred.
|
|
510
|
+
// #7721 D4 (gap F) — `mark-only` means the mark alone: no wordmark beside
|
|
511
|
+
// it on any page. On the deck home `deckMorph` still governs the brand:
|
|
512
|
+
// Nav spreads its `full-logo` composition after `logoDisplay`'s.
|
|
513
|
+
...(siteChrome.nav === "mark-only" ? { logoDisplay: "mark" as const } : {}),
|
|
514
|
+
...(isDeckHome ? { sticky: true, deckMorph: true, bar: "bare" as const } : {}),
|
|
428
515
|
siteName: SITE_NAME,
|
|
429
516
|
frame: "contained",
|
|
430
517
|
navAlignment: "right",
|
|
@@ -432,7 +519,7 @@ export default async function LocaleLayout({
|
|
|
432
519
|
logo: brandLogo,
|
|
433
520
|
wordmark: brandWordmark,
|
|
434
521
|
overflowLabel: navOverflowLabel,
|
|
435
|
-
actions:
|
|
522
|
+
...(headerActions.length > 0 ? { actions: headerActions } : {}),
|
|
436
523
|
};
|
|
437
524
|
|
|
438
525
|
// Placeholder social handles (#5469 D5): `{{COMPANY_HANDLE}}` is a
|
|
@@ -464,7 +551,8 @@ export default async function LocaleLayout({
|
|
|
464
551
|
|
|
465
552
|
// The site's own name (#6942) — `SITE_NAME` resolves from `_site.mdx`'s
|
|
466
553
|
// `siteName:`, so a customer's footer never carries the kit vendor's name.
|
|
467
|
-
|
|
554
|
+
// The holder is `siteIdentity.legalName` when declared (#7796), else the name.
|
|
555
|
+
const copyrightText = `© ${new Date().getFullYear()} ${COPYRIGHT_HOLDER}. All rights reserved.`;
|
|
468
556
|
|
|
469
557
|
// Footer legal links (#5612 D3). The centered variant folds in FAQ — the
|
|
470
558
|
// roster's third always-scaffolded core route (`/faq`, alongside `/privacy`
|
|
@@ -483,40 +571,38 @@ export default async function LocaleLayout({
|
|
|
483
571
|
: []),
|
|
484
572
|
];
|
|
485
573
|
|
|
486
|
-
// The site's own declared colours
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
// Both theme modes, gated by [data-theme="dark"] (#1532 — sibling fix; the
|
|
495
|
-
// single-mode call inlined an unconditional :root block that never
|
|
496
|
-
// responded to a client-side data-theme toggle). themeMode still drives
|
|
497
|
-
// the initial data-theme attribute below for a no-flash first paint.
|
|
498
|
-
// Site shape settings (`theme.corners`, `theme.eyebrowCase`) appended after
|
|
499
|
-
// the token block so they win; "" for a site that sets neither.
|
|
500
|
-
const inlineThemeStyle =
|
|
501
|
-
themeToCssVarsBothModes(siteBrandTokens ? { brand: siteBrandTokens } : {}) +
|
|
502
|
-
loadSiteShapeCss();
|
|
574
|
+
// The site's own declared colours (`content/_site.mdx` `theme:` + `brand:`)
|
|
575
|
+
// as a token overlay, the design's web faces (#7720) beneath `brand.fonts`,
|
|
576
|
+
// both theme modes gated by [data-theme="dark"] (#1532) and the site's shape
|
|
577
|
+
// settings after them — one helper, shared with the minimal 404 (#7742 D6).
|
|
578
|
+
// A site that declares no theme gets byte-for-byte the CSS it got before
|
|
579
|
+
// this wiring existed; with no design faces <html> gets no class.
|
|
580
|
+
// themeMode still drives the initial data-theme attribute below.
|
|
581
|
+
const themeHead = siteThemeHead();
|
|
503
582
|
|
|
504
583
|
return (
|
|
505
584
|
<html
|
|
506
585
|
lang={locale}
|
|
507
586
|
dir={isRtl(locale) ? "rtl" : "ltr"}
|
|
508
587
|
data-theme={themeMode}
|
|
588
|
+
className={themeHead.fontClassName}
|
|
589
|
+
// #7742 D5 — scopes the cue's consent-bar offset; absent otherwise.
|
|
590
|
+
{...(isMinimalChrome ? { "data-chrome-style": "minimal" } : {})}
|
|
509
591
|
>
|
|
510
592
|
<head>
|
|
511
|
-
{/* Flash-prevention: runs synchronously before any styled element
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
593
|
+
{/* Flash-prevention: runs synchronously before any styled element.
|
|
594
|
+
Not under `colorMode: light` (#7742 D2) — the mode is pinned, and
|
|
595
|
+
the script would apply the OS dark preference. */}
|
|
596
|
+
{pinLight ? null : (
|
|
597
|
+
<script
|
|
598
|
+
data-testid="theme-flash-prevention"
|
|
599
|
+
dangerouslySetInnerHTML={{ __html: FLASH_PREVENTION_SCRIPT }}
|
|
600
|
+
/>
|
|
601
|
+
)}
|
|
516
602
|
{/* Inline CSS-variable declarations for the active theme mode */}
|
|
517
603
|
<style
|
|
518
604
|
data-ailk="theme-vars"
|
|
519
|
-
dangerouslySetInnerHTML={{ __html:
|
|
605
|
+
dangerouslySetInnerHTML={{ __html: themeHead.css }}
|
|
520
606
|
/>
|
|
521
607
|
{/* Favicons — single brand mark (theme-independent), PNG fallbacks, ICO legacy */}
|
|
522
608
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
@@ -628,10 +714,41 @@ export default async function LocaleLayout({
|
|
|
628
714
|
}
|
|
629
715
|
: {})}
|
|
630
716
|
{...(isDeckHome
|
|
631
|
-
? {
|
|
717
|
+
? {
|
|
718
|
+
snap: "mandatory" as const,
|
|
719
|
+
deckHero: true,
|
|
720
|
+
// #7740 D8 — the design's deck presentation dial.
|
|
721
|
+
...(chrome.deckPresentation === true ? { presentation: true } : {}),
|
|
722
|
+
// The deck's slide-advance controls (#7732) — named from
|
|
723
|
+
// the `nav` catalog, never the shell's English default.
|
|
724
|
+
slideAdvanceLabels: {
|
|
725
|
+
next: tNav("nextSlide"),
|
|
726
|
+
top: tNav("backToTop"),
|
|
727
|
+
},
|
|
728
|
+
}
|
|
632
729
|
: {})}
|
|
633
730
|
nav={
|
|
634
|
-
|
|
731
|
+
// #7721 D4 — a bare inner page's header: the back arrow home
|
|
732
|
+
// and the mark. It carries no actions, so it is always the
|
|
733
|
+
// server Nav (nothing callable crosses the RSC boundary).
|
|
734
|
+
// #7744 D7 — on a deck home a team profile's arrow returns
|
|
735
|
+
// to the people slide (`/#slide-people`) instead.
|
|
736
|
+
isBareInner ? (
|
|
737
|
+
<Nav
|
|
738
|
+
{...navProps}
|
|
739
|
+
navItems={[]}
|
|
740
|
+
bar="bare"
|
|
741
|
+
backLink={{
|
|
742
|
+
href: backLinkHref({
|
|
743
|
+
pathname,
|
|
744
|
+
homePath,
|
|
745
|
+
home: siteChrome.home,
|
|
746
|
+
locales: enabledLocales,
|
|
747
|
+
}),
|
|
748
|
+
label: tNav("home"),
|
|
749
|
+
}}
|
|
750
|
+
/>
|
|
751
|
+
) : hasFlowAction ? (
|
|
635
752
|
<SignedInNav {...navProps} />
|
|
636
753
|
) : (
|
|
637
754
|
<Nav
|
|
@@ -650,10 +767,40 @@ export default async function LocaleLayout({
|
|
|
650
767
|
logo={brandLogo}
|
|
651
768
|
wordmark={brandWordmark}
|
|
652
769
|
social={[...socialLinks]}
|
|
653
|
-
|
|
654
|
-
|
|
770
|
+
// #7742 D3 — under `chromeStyle: minimal` the deck home's
|
|
771
|
+
// closing slide is the wordmark, the contact lines and the
|
|
772
|
+
// legal line: no actions and no centred lockup, so Sign in /
|
|
773
|
+
// Sign up are on no part of the home.
|
|
774
|
+
// #7721 D4 — a bare inner page's footer is the one bar, its
|
|
775
|
+
// link group carrying the account links the header drops.
|
|
776
|
+
// #7797 — a listed inner page takes the same closing footer.
|
|
777
|
+
// Under `innerPages: bare` the one-bar footer is where the
|
|
778
|
+
// account links live (the header drops them), so the closing
|
|
779
|
+
// footer carries the deck's auth actions in their place; on
|
|
780
|
+
// any other inner page the header still holds them.
|
|
781
|
+
{...(isDeckHome && isMinimalChrome
|
|
782
|
+
? { layout: "closing" as const, contactLines: readSiteContactLines() }
|
|
783
|
+
: isClosingInner
|
|
784
|
+
? {
|
|
785
|
+
layout: "closing" as const,
|
|
786
|
+
contactLines: readSiteContactLines(),
|
|
787
|
+
...(isBareInner ? { actions: deckFooterActions } : {}),
|
|
788
|
+
}
|
|
789
|
+
: isBareInner
|
|
790
|
+
? { layout: "line" as const, accountLinks }
|
|
791
|
+
: siteChrome.footer === "centered"
|
|
792
|
+
? { layout: "centered" as const }
|
|
793
|
+
: {})}
|
|
794
|
+
// #7718 D4 — the deck home's closing slide carries the
|
|
795
|
+
// centred brand and the auth links its bare header drops.
|
|
796
|
+
{...(isDeckHome && !isMinimalChrome
|
|
797
|
+
? { centeredBrand: true, actions: deckFooterActions }
|
|
655
798
|
: {})}
|
|
656
|
-
|
|
799
|
+
// #7823 — the minimal closing slide's actions are a button,
|
|
800
|
+
// then a text link; Footer reads the style from this.
|
|
801
|
+
{...(isMinimalChrome ? { chromeStyle: "minimal" as const } : {})}
|
|
802
|
+
// #7742 D2 — no theme switch under `colorMode: light`.
|
|
803
|
+
showThemeToggle={!pinLight}
|
|
657
804
|
bottomBarAlign="content"
|
|
658
805
|
copyrightText={copyrightText}
|
|
659
806
|
legalLinks={legalLinks}
|
|
@@ -713,6 +860,14 @@ export default async function LocaleLayout({
|
|
|
713
860
|
secondaryAction={{ label: tConsent("rejectLabel") }}
|
|
714
861
|
rejectLabel={tConsent("rejectLabel")}
|
|
715
862
|
locale={locale}
|
|
863
|
+
// #7742 D5 — the minimal chrome's consent bar: the page ground,
|
|
864
|
+
// a hairline, no shadow.
|
|
865
|
+
{...(isMinimalChrome ? { consentAppearance: "hairline" as const } : {})}
|
|
866
|
+
// #7782 — on the minimal deck home the bar first shows once the
|
|
867
|
+
// first slide has left view; every other page shows it on load.
|
|
868
|
+
{...(isDeckHome && isMinimalChrome
|
|
869
|
+
? { consentRevealAfter: DECK_FIRST_SLIDE_SELECTOR }
|
|
870
|
+
: {})}
|
|
716
871
|
/>
|
|
717
872
|
)}
|
|
718
873
|
<Telemetry />
|
|
@@ -11,6 +11,9 @@ import React from "react";
|
|
|
11
11
|
import { render, screen } from "@testing-library/react";
|
|
12
12
|
|
|
13
13
|
jest.mock("@working-theory/ui", () => ({
|
|
14
|
+
// #7789: renderPage's fallback page head renders inside the section Container.
|
|
15
|
+
Container: ({ children }: { children?: React.ReactNode }) =>
|
|
16
|
+
React.createElement("div", null, children),
|
|
14
17
|
Heading: ({ children, level }: { children: React.ReactNode; level: number }) =>
|
|
15
18
|
React.createElement(`h${level}`, null, children),
|
|
16
19
|
SectionRenderer: ({
|
|
@@ -14,6 +14,19 @@ import {
|
|
|
14
14
|
import type { Metadata } from "next";
|
|
15
15
|
import type { ReactElement } from "react";
|
|
16
16
|
|
|
17
|
+
import { siteChrome, type SiteChrome } from "../../lib/site-chrome";
|
|
18
|
+
import { siteLogomarkAsset, siteWordmarkAsset } from "../../lib/site-logo";
|
|
19
|
+
|
|
20
|
+
/** The ground sequence `renderPage` cycles over a deck home's slides (#7719). */
|
|
21
|
+
type DeckGrounds = NonNullable<Parameters<typeof renderPage>[0]["deckGrounds"]>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The generated `siteChrome`, read through a widened type: its interface
|
|
25
|
+
* carries `deckGrounds` only when the site's design sets one (#7719 D3), so
|
|
26
|
+
* this compiles against both shapes of the generated file.
|
|
27
|
+
*/
|
|
28
|
+
const chrome: SiteChrome & { deckGrounds?: DeckGrounds; deckPresentation?: true } = siteChrome;
|
|
29
|
+
|
|
17
30
|
interface Props {
|
|
18
31
|
params: Promise<{ locale: string }>;
|
|
19
32
|
/**
|
|
@@ -22,6 +35,12 @@ interface Props {
|
|
|
22
35
|
*/
|
|
23
36
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
24
37
|
getPageSections?: typeof defaultGetPageSections;
|
|
38
|
+
/** The site's `layout.home` knob; defaults to the generated `siteChrome`. */
|
|
39
|
+
home?: SiteChrome["home"];
|
|
40
|
+
/** The design's `layout.deckGrounds`; defaults to the generated `siteChrome`'s. */
|
|
41
|
+
deckGrounds?: DeckGrounds;
|
|
42
|
+
/** The design's `layout.deckPresentation` (#7740); defaults to the generated `siteChrome`'s. */
|
|
43
|
+
deckPresentation?: boolean;
|
|
25
44
|
}
|
|
26
45
|
|
|
27
46
|
export async function generateMetadata({
|
|
@@ -35,6 +54,9 @@ export default async function Page({
|
|
|
35
54
|
params,
|
|
36
55
|
getMdxSource,
|
|
37
56
|
getPageSections,
|
|
57
|
+
home = chrome.home,
|
|
58
|
+
deckGrounds = chrome.deckGrounds,
|
|
59
|
+
deckPresentation = chrome.deckPresentation,
|
|
38
60
|
}: Props): Promise<ReactElement> {
|
|
39
61
|
const { locale } = await params;
|
|
40
62
|
return renderPage({
|
|
@@ -46,5 +68,20 @@ export default async function Page({
|
|
|
46
68
|
emptyStateTestId: "home-description",
|
|
47
69
|
...(getMdxSource !== undefined && { getMdxSource }),
|
|
48
70
|
...(getPageSections !== undefined && { getPageSections }),
|
|
71
|
+
// #7733 — the deck home's header is bare on slide 1 (#5697), so its
|
|
72
|
+
// landing carries the brand: a centred leading hero renders as bondcap's
|
|
73
|
+
// landing with the site's wordmark in the headline's place; any other
|
|
74
|
+
// leading hero keeps its composition and takes the logomark (#7711).
|
|
75
|
+
// Content that authors its own lockup keeps it.
|
|
76
|
+
...(home === "deck" && {
|
|
77
|
+
deckLanding: { wordmark: siteWordmarkAsset(), logomark: siteLogomarkAsset() },
|
|
78
|
+
}),
|
|
79
|
+
// #7719 — a deck home cycles its design's ground sequence, slide by
|
|
80
|
+
// slide, onto every slide that authors no surface of its own.
|
|
81
|
+
...(home === "deck" && deckGrounds !== undefined && { deckGrounds }),
|
|
82
|
+
// #7740 — a deck home under the presentation dial: every slide with an
|
|
83
|
+
// alignment field centred, card grids one column on phones, and slide
|
|
84
|
+
// anchors clear of the page's JSON-LD @id fragments.
|
|
85
|
+
...(home === "deck" && deckPresentation === true && { deckPresentation: true }),
|
|
49
86
|
});
|
|
50
87
|
}
|
|
@@ -68,7 +68,7 @@ describe("partners/page — snapshot regression", () => {
|
|
|
68
68
|
});
|
|
69
69
|
const { container } = render(element);
|
|
70
70
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
71
|
-
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Partners","description":"Organisations partnering with AI Launch Kit.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"Organization"}]}}</script><main id="main"><h1>Partners</h1><p data-testid="partners-description">Organisations partnering with AI Launch Kit.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-rendered">mdx-body-content</div></article></div></div></main>"`,
|
|
71
|
+
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Partners","description":"Organisations partnering with AI Launch Kit.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"Organization"}]}}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Partners</h1><p data-testid="partners-description" class="mt-block-tight text-lead">Organisations partnering with AI Launch Kit.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle mt-block-loose"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div class="ailk-prose max-w-none text-(--color-fg) text-body-sm leading-relaxed [&>*:first-child]:mt-0 [&_h2]:mt-block-loose [&_h2]:scroll-mt-24 [&_h2]:text-heading-3 [&_h2]:font-display [&_h2]:text-(--color-fg) [&_h3]:mt-block-loose [&_h3]:scroll-mt-24 [&_h3]:text-heading-4 [&_h3]:font-display [&_h3]:text-(--color-fg) [&_h4]:mt-block-tight [&_h4]:scroll-mt-24 [&_h4]:text-heading-4 [&_h4]:font-display [&_h4]:text-(--color-fg) [&_h5]:mt-block-tight [&_h5]:scroll-mt-24 [&_h5]:text-heading-4 [&_h5]:font-display [&_h5]:text-(--color-fg) [&_h6]:mt-block-tight [&_h6]:scroll-mt-24 [&_h6]:text-heading-4 [&_h6]:font-display [&_h6]:text-(--color-fg) [&_p]:mt-paragraph [&_strong]:font-semibold [&_strong]:text-(--color-fg) [&_em]:italic [&_ul]:mt-paragraph [&_ul]:list-disc [&_ul]:ps-6 [&_ol]:mt-paragraph [&_ol]:list-decimal [&_ol]:ps-6 [&_li]:mt-1 [&_li]:ps-1 [&_li]:marker:text-(--color-text-muted) [&_li>ul]:mt-1 [&_li>ol]:mt-1 [&_blockquote]:mt-paragraph [&_blockquote]:border-s-4 [&_blockquote]:border-(--color-border-strong) [&_blockquote]:ps-4 [&_blockquote]:italic [&_blockquote]:text-(--color-text-muted) [&_hr]:my-block-loose [&_hr]:border-(--color-border) [&_a]:text-(--color-accent) [&_a]:underline [&_a]:underline-offset-4 [&_:not(pre)>code]:rounded [&_:not(pre)>code]:bg-(--color-surface-secondary) [&_:not(pre)>code]:px-1.5 [&_:not(pre)>code]:py-0.5 [&_:not(pre)>code]:text-code [&_:not(pre)>code]:font-mono [&_:not(pre)>code]:text-(--color-fg) [&_pre]:mt-paragraph [&_pre]:overflow-auto [&_pre]:rounded-md [&_pre]:border-hairline [&_pre]:border-(--color-border) [&_pre]:bg-(--color-bg) [&_pre]:p-4 [&_pre]:font-mono [&_pre]:text-body-sm [&_img]:mt-paragraph [&_img]:h-auto [&_img]:max-w-full [&_img]:rounded-lg [&_table]:mt-paragraph [&_table]:w-full [&_table]:border-collapse [&_table]:text-body-sm [&_th]:border-b [&_th]:border-(--color-border) [&_th]:bg-(--color-surface-secondary) [&_th]:px-3 [&_th]:py-2 [&_th]:text-start [&_th]:font-semibold [&_th]:text-(--color-fg) [&_td]:border-b [&_td]:border-(--color-border) [&_td]:px-3 [&_td]:py-2 [&_kbd]:rounded [&_kbd]:border-hairline [&_kbd]:border-(--color-border) [&_kbd]:bg-(--color-surface-secondary) [&_kbd]:px-1.5 [&_kbd]:py-0.5 [&_kbd]:text-caption [&_kbd]:font-mono" data-testid="mdx-content"><div data-testid="mdx-rendered">mdx-body-content</div></div></article></div></div></div></main>"`,
|
|
72
72
|
);
|
|
73
73
|
});
|
|
74
74
|
});
|
|
@@ -93,7 +93,7 @@ describe("partners/[slug]/page — snapshot regression", () => {
|
|
|
93
93
|
});
|
|
94
94
|
const { container } = render(element);
|
|
95
95
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
96
|
-
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"Acme Corp","description":"Acme Corp is a strategic partner.","mainEntity":{"@context":"https://schema.org","@type":"Organization","name":"Acme Corp","description":"Acme Corp is a strategic partner."}},{"@context":"https://schema.org","@type":"BreadcrumbList","inLanguage":"en","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http://localhost:3000/en/"},{"@type":"ListItem","position":2,"name":"Partners","item":"http://localhost:3000/en/partners"},{"@type":"ListItem","position":3,"name":"Acme Corp","item":"http://localhost:3000/en/partners/acme-corp"}]}]}</script><main id="main"><h1>Acme Corp</h1><p data-testid="acme-corp-description">Acme Corp is a strategic partner.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-rendered">mdx-body-content</div></article></div></div></main>"`,
|
|
96
|
+
`"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"Acme Corp","description":"Acme Corp is a strategic partner.","mainEntity":{"@context":"https://schema.org","@type":"Organization","name":"Acme Corp","description":"Acme Corp is a strategic partner."}},{"@context":"https://schema.org","@type":"BreadcrumbList","inLanguage":"en","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http://localhost:3000/en/"},{"@type":"ListItem","position":2,"name":"Partners","item":"http://localhost:3000/en/partners"},{"@type":"ListItem","position":3,"name":"Acme Corp","item":"http://localhost:3000/en/partners/acme-corp"}]}]}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Acme Corp</h1><p data-testid="acme-corp-description" class="mt-block-tight text-lead">Acme Corp is a strategic partner.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle mt-block-loose"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div class="ailk-prose max-w-none text-(--color-fg) text-body-sm leading-relaxed [&>*:first-child]:mt-0 [&_h2]:mt-block-loose [&_h2]:scroll-mt-24 [&_h2]:text-heading-3 [&_h2]:font-display [&_h2]:text-(--color-fg) [&_h3]:mt-block-loose [&_h3]:scroll-mt-24 [&_h3]:text-heading-4 [&_h3]:font-display [&_h3]:text-(--color-fg) [&_h4]:mt-block-tight [&_h4]:scroll-mt-24 [&_h4]:text-heading-4 [&_h4]:font-display [&_h4]:text-(--color-fg) [&_h5]:mt-block-tight [&_h5]:scroll-mt-24 [&_h5]:text-heading-4 [&_h5]:font-display [&_h5]:text-(--color-fg) [&_h6]:mt-block-tight [&_h6]:scroll-mt-24 [&_h6]:text-heading-4 [&_h6]:font-display [&_h6]:text-(--color-fg) [&_p]:mt-paragraph [&_strong]:font-semibold [&_strong]:text-(--color-fg) [&_em]:italic [&_ul]:mt-paragraph [&_ul]:list-disc [&_ul]:ps-6 [&_ol]:mt-paragraph [&_ol]:list-decimal [&_ol]:ps-6 [&_li]:mt-1 [&_li]:ps-1 [&_li]:marker:text-(--color-text-muted) [&_li>ul]:mt-1 [&_li>ol]:mt-1 [&_blockquote]:mt-paragraph [&_blockquote]:border-s-4 [&_blockquote]:border-(--color-border-strong) [&_blockquote]:ps-4 [&_blockquote]:italic [&_blockquote]:text-(--color-text-muted) [&_hr]:my-block-loose [&_hr]:border-(--color-border) [&_a]:text-(--color-accent) [&_a]:underline [&_a]:underline-offset-4 [&_:not(pre)>code]:rounded [&_:not(pre)>code]:bg-(--color-surface-secondary) [&_:not(pre)>code]:px-1.5 [&_:not(pre)>code]:py-0.5 [&_:not(pre)>code]:text-code [&_:not(pre)>code]:font-mono [&_:not(pre)>code]:text-(--color-fg) [&_pre]:mt-paragraph [&_pre]:overflow-auto [&_pre]:rounded-md [&_pre]:border-hairline [&_pre]:border-(--color-border) [&_pre]:bg-(--color-bg) [&_pre]:p-4 [&_pre]:font-mono [&_pre]:text-body-sm [&_img]:mt-paragraph [&_img]:h-auto [&_img]:max-w-full [&_img]:rounded-lg [&_table]:mt-paragraph [&_table]:w-full [&_table]:border-collapse [&_table]:text-body-sm [&_th]:border-b [&_th]:border-(--color-border) [&_th]:bg-(--color-surface-secondary) [&_th]:px-3 [&_th]:py-2 [&_th]:text-start [&_th]:font-semibold [&_th]:text-(--color-fg) [&_td]:border-b [&_td]:border-(--color-border) [&_td]:px-3 [&_td]:py-2 [&_kbd]:rounded [&_kbd]:border-hairline [&_kbd]:border-(--color-border) [&_kbd]:bg-(--color-surface-secondary) [&_kbd]:px-1.5 [&_kbd]:py-0.5 [&_kbd]:text-caption [&_kbd]:font-mono" data-testid="mdx-content"><div data-testid="mdx-rendered">mdx-body-content</div></div></article></div></div></div></main>"`,
|
|
97
97
|
);
|
|
98
98
|
});
|
|
99
99
|
|
|
@@ -87,9 +87,8 @@ describe("ProductsPage — snapshot regression", () => {
|
|
|
87
87
|
});
|
|
88
88
|
const { container } = render(element);
|
|
89
89
|
expect(container.innerHTML).toMatchInlineSnapshot(`
|
|
90
|
-
"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Products","description":"All products.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"SoftwareApplication"}]}}</script><main id="main"><h1>Products</h1><p data-testid="products-description">All products.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-remote" data-source="
|
|
91
|
-
|
|
92
|
-
Body."></div></article></div></div></main>"
|
|
90
|
+
"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Products","description":"All products.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"SoftwareApplication"}]}}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">Products</h1><p data-testid="products-description" class="mt-block-tight text-lead">All products.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle mt-block-loose"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div class="ailk-prose max-w-none text-(--color-fg) text-body-sm leading-relaxed [&>*:first-child]:mt-0 [&_h2]:mt-block-loose [&_h2]:scroll-mt-24 [&_h2]:text-heading-3 [&_h2]:font-display [&_h2]:text-(--color-fg) [&_h3]:mt-block-loose [&_h3]:scroll-mt-24 [&_h3]:text-heading-4 [&_h3]:font-display [&_h3]:text-(--color-fg) [&_h4]:mt-block-tight [&_h4]:scroll-mt-24 [&_h4]:text-heading-4 [&_h4]:font-display [&_h4]:text-(--color-fg) [&_h5]:mt-block-tight [&_h5]:scroll-mt-24 [&_h5]:text-heading-4 [&_h5]:font-display [&_h5]:text-(--color-fg) [&_h6]:mt-block-tight [&_h6]:scroll-mt-24 [&_h6]:text-heading-4 [&_h6]:font-display [&_h6]:text-(--color-fg) [&_p]:mt-paragraph [&_strong]:font-semibold [&_strong]:text-(--color-fg) [&_em]:italic [&_ul]:mt-paragraph [&_ul]:list-disc [&_ul]:ps-6 [&_ol]:mt-paragraph [&_ol]:list-decimal [&_ol]:ps-6 [&_li]:mt-1 [&_li]:ps-1 [&_li]:marker:text-(--color-text-muted) [&_li>ul]:mt-1 [&_li>ol]:mt-1 [&_blockquote]:mt-paragraph [&_blockquote]:border-s-4 [&_blockquote]:border-(--color-border-strong) [&_blockquote]:ps-4 [&_blockquote]:italic [&_blockquote]:text-(--color-text-muted) [&_hr]:my-block-loose [&_hr]:border-(--color-border) [&_a]:text-(--color-accent) [&_a]:underline [&_a]:underline-offset-4 [&_:not(pre)>code]:rounded [&_:not(pre)>code]:bg-(--color-surface-secondary) [&_:not(pre)>code]:px-1.5 [&_:not(pre)>code]:py-0.5 [&_:not(pre)>code]:text-code [&_:not(pre)>code]:font-mono [&_:not(pre)>code]:text-(--color-fg) [&_pre]:mt-paragraph [&_pre]:overflow-auto [&_pre]:rounded-md [&_pre]:border-hairline [&_pre]:border-(--color-border) [&_pre]:bg-(--color-bg) [&_pre]:p-4 [&_pre]:font-mono [&_pre]:text-body-sm [&_img]:mt-paragraph [&_img]:h-auto [&_img]:max-w-full [&_img]:rounded-lg [&_table]:mt-paragraph [&_table]:w-full [&_table]:border-collapse [&_table]:text-body-sm [&_th]:border-b [&_th]:border-(--color-border) [&_th]:bg-(--color-surface-secondary) [&_th]:px-3 [&_th]:py-2 [&_th]:text-start [&_th]:font-semibold [&_th]:text-(--color-fg) [&_td]:border-b [&_td]:border-(--color-border) [&_td]:px-3 [&_td]:py-2 [&_kbd]:rounded [&_kbd]:border-hairline [&_kbd]:border-(--color-border) [&_kbd]:bg-(--color-surface-secondary) [&_kbd]:px-1.5 [&_kbd]:py-0.5 [&_kbd]:text-caption [&_kbd]:font-mono" data-testid="mdx-content"><div data-testid="mdx-remote" data-source="
|
|
91
|
+
Body."></div></div></article></div></div></div></main>"
|
|
93
92
|
`);
|
|
94
93
|
});
|
|
95
94
|
});
|
|
@@ -111,9 +110,8 @@ describe("ProductSlugPage — snapshot regression", () => {
|
|
|
111
110
|
});
|
|
112
111
|
const { container } = render(element);
|
|
113
112
|
expect(container.innerHTML).toMatchInlineSnapshot(`
|
|
114
|
-
"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"AI Launch Kit","description":"The AX-first OSS template.","mainEntity":{"@context":"https://schema.org","@type":"SoftwareApplication","name":"AI Launch Kit","description":"The AX-first OSS template."}},{"@context":"https://schema.org","@type":"BreadcrumbList","inLanguage":"en","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http://localhost:3000/en/"},{"@type":"ListItem","position":2,"name":"Products","item":"http://localhost:3000/en/products"},{"@type":"ListItem","position":3,"name":"AI Launch Kit","item":"http://localhost:3000/en/products/launch-kit"}]}]}</script><main id="main"><h1>AI Launch Kit</h1><p data-testid="product-description">The AX-first OSS template.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div data-testid="mdx-remote" data-source="
|
|
115
|
-
|
|
116
|
-
Body."></div></article></div></div></main>"
|
|
113
|
+
"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"AI Launch Kit","description":"The AX-first OSS template.","mainEntity":{"@context":"https://schema.org","@type":"SoftwareApplication","name":"AI Launch Kit","description":"The AX-first OSS template."}},{"@context":"https://schema.org","@type":"BreadcrumbList","inLanguage":"en","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http://localhost:3000/en/"},{"@type":"ListItem","position":2,"name":"Products","item":"http://localhost:3000/en/products"},{"@type":"ListItem","position":3,"name":"AI Launch Kit","item":"http://localhost:3000/en/products/launch-kit"}]}]}</script><main id="main"><div class="mx-auto w-full max-w-container-xl px-4 md:px-6 lg:px-8 py-section"><h1 class="text-heading-1 lg:text-display-sm font-display text-fg hero-type-display">AI Launch Kit</h1><p data-testid="product-description" class="mt-block-tight text-lead">The AX-first OSS template.</p><div data-testid="markdown-view-toggle" class="ailk-markdown-view-toggle mt-block-loose"><div class="flex justify-end mb-3"><div role="radiogroup" aria-label="Page view" data-testid="markdown-view-toggle" class="inline-flex items-center gap-0.5 rounded-lg p-1 bg-(--color-surface-secondary)"><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) bg-(--color-bg) text-(--color-fg) shadow-sm" type="button" role="radio" aria-checked="true" aria-label="Rendered" tabindex="0" data-testid="markdown-view-toggle-rendered">Rendered</button><button class="inline-flex items-center justify-center gap-2 font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:shrink-0 text-(--color-fg) hover:bg-(--color-surface-secondary) active:bg-gray-200 rounded-control px-control-x h-(--control-height-thin) text-caption leading-none rounded-md px-3 text-body-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--color-accent) focus-visible:ring-offset-1 min-w-(--control-height-thin) min-h-(--control-height-big) text-(--color-text-muted) hover:text-(--color-fg)" type="button" role="radio" aria-checked="false" aria-label="Markdown" tabindex="-1" data-testid="markdown-view-toggle-markdown">Markdown</button></div></div><div role="status" aria-live="polite" aria-atomic="true" class="sr-only" data-testid="markdown-view-toggle-status">Showing Rendered view</div><div data-testid="markdown-view-rendered"><article data-testid="mdx-body"><div class="ailk-prose max-w-none text-(--color-fg) text-body-sm leading-relaxed [&>*:first-child]:mt-0 [&_h2]:mt-block-loose [&_h2]:scroll-mt-24 [&_h2]:text-heading-3 [&_h2]:font-display [&_h2]:text-(--color-fg) [&_h3]:mt-block-loose [&_h3]:scroll-mt-24 [&_h3]:text-heading-4 [&_h3]:font-display [&_h3]:text-(--color-fg) [&_h4]:mt-block-tight [&_h4]:scroll-mt-24 [&_h4]:text-heading-4 [&_h4]:font-display [&_h4]:text-(--color-fg) [&_h5]:mt-block-tight [&_h5]:scroll-mt-24 [&_h5]:text-heading-4 [&_h5]:font-display [&_h5]:text-(--color-fg) [&_h6]:mt-block-tight [&_h6]:scroll-mt-24 [&_h6]:text-heading-4 [&_h6]:font-display [&_h6]:text-(--color-fg) [&_p]:mt-paragraph [&_strong]:font-semibold [&_strong]:text-(--color-fg) [&_em]:italic [&_ul]:mt-paragraph [&_ul]:list-disc [&_ul]:ps-6 [&_ol]:mt-paragraph [&_ol]:list-decimal [&_ol]:ps-6 [&_li]:mt-1 [&_li]:ps-1 [&_li]:marker:text-(--color-text-muted) [&_li>ul]:mt-1 [&_li>ol]:mt-1 [&_blockquote]:mt-paragraph [&_blockquote]:border-s-4 [&_blockquote]:border-(--color-border-strong) [&_blockquote]:ps-4 [&_blockquote]:italic [&_blockquote]:text-(--color-text-muted) [&_hr]:my-block-loose [&_hr]:border-(--color-border) [&_a]:text-(--color-accent) [&_a]:underline [&_a]:underline-offset-4 [&_:not(pre)>code]:rounded [&_:not(pre)>code]:bg-(--color-surface-secondary) [&_:not(pre)>code]:px-1.5 [&_:not(pre)>code]:py-0.5 [&_:not(pre)>code]:text-code [&_:not(pre)>code]:font-mono [&_:not(pre)>code]:text-(--color-fg) [&_pre]:mt-paragraph [&_pre]:overflow-auto [&_pre]:rounded-md [&_pre]:border-hairline [&_pre]:border-(--color-border) [&_pre]:bg-(--color-bg) [&_pre]:p-4 [&_pre]:font-mono [&_pre]:text-body-sm [&_img]:mt-paragraph [&_img]:h-auto [&_img]:max-w-full [&_img]:rounded-lg [&_table]:mt-paragraph [&_table]:w-full [&_table]:border-collapse [&_table]:text-body-sm [&_th]:border-b [&_th]:border-(--color-border) [&_th]:bg-(--color-surface-secondary) [&_th]:px-3 [&_th]:py-2 [&_th]:text-start [&_th]:font-semibold [&_th]:text-(--color-fg) [&_td]:border-b [&_td]:border-(--color-border) [&_td]:px-3 [&_td]:py-2 [&_kbd]:rounded [&_kbd]:border-hairline [&_kbd]:border-(--color-border) [&_kbd]:bg-(--color-surface-secondary) [&_kbd]:px-1.5 [&_kbd]:py-0.5 [&_kbd]:text-caption [&_kbd]:font-mono" data-testid="mdx-content"><div data-testid="mdx-remote" data-source="
|
|
114
|
+
Body."></div></div></article></div></div></div></main>"
|
|
117
115
|
`);
|
|
118
116
|
});
|
|
119
117
|
|