create-ailk 0.1.0 → 0.2.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 +124 -3
- package/component-catalog.md +456 -0
- package/dist/cli.js +92 -16
- package/dist/component-catalog.d.ts +11 -0
- package/dist/component-catalog.js +39 -0
- package/dist/copy.d.ts +7 -0
- package/dist/copy.js +24 -0
- package/dist/derive-architecture.d.ts +13 -0
- package/dist/derive-architecture.js +238 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +19 -2
- package/dist/lib/extract-module-bundle.d.ts +16 -0
- package/dist/lib/extract-module-bundle.js +184 -0
- package/dist/lib/fetch-module.d.ts +55 -0
- package/dist/lib/fetch-module.js +125 -0
- package/dist/lib/module-license-gate.d.ts +48 -0
- package/dist/lib/module-license-gate.js +67 -0
- package/dist/lib/module-tier.d.ts +35 -0
- package/dist/lib/module-tier.js +37 -0
- package/dist/module-architecture.d.ts +175 -0
- package/dist/module-architecture.js +663 -0
- package/dist/parse-args.d.ts +47 -1
- package/dist/parse-args.js +143 -2
- package/dist/programmatic.d.ts +160 -3
- package/dist/programmatic.js +196 -7
- package/dist/surfaces.d.ts +211 -0
- package/dist/surfaces.js +532 -0
- package/dist/sync-routes.d.ts +92 -0
- package/dist/sync-routes.js +350 -0
- package/package.json +12 -5
- package/templates/.claude/rules/architecture.md +6 -2
- package/templates/.claude/skills/README.md +9 -9
- package/templates/.claude/skills/add-schema/SKILL.md +14 -7
- package/templates/.claude/skills/add-schema/references/schema-types.md +109 -6
- package/templates/.claude/skills/provision-config/SKILL.md +1 -1
- package/templates/.claude/skills/scaffold-commerce/SKILL.md +498 -1
- package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +370 -0
- package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +376 -0
- package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +471 -0
- package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +365 -0
- package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +60 -0
- package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +446 -0
- package/templates/.claude/skills/suggest-site-pages/references/categories.md +4 -4
- package/templates/.env.example +126 -2
- package/templates/CLAUDE.md +11 -5
- package/templates/README.md +1 -1
- package/templates/apps/api/.env.example +38 -0
- package/templates/apps/api/CLAUDE.md +78 -1
- package/templates/apps/api/LICENSE +201 -0
- package/templates/apps/api/api/index.ts +6 -141
- package/templates/apps/api/jest.config.cjs +16 -0
- package/templates/apps/api/package.json +22 -6
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +141 -0
- package/templates/apps/api/src/__tests__/server.test.ts +46 -0
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +211 -0
- package/templates/apps/api/src/bin/abandon-sweep.ts +42 -0
- package/templates/apps/api/src/bin/deliverable-resend.ts +144 -0
- package/templates/apps/api/src/bin/deliverable-upload.ts +114 -0
- package/templates/apps/api/src/bin/followup-sweep.ts +44 -0
- package/templates/apps/api/src/bin/listing-csv-sweep.ts +46 -0
- package/templates/apps/api/src/bin/seed-presets.ts +58 -0
- package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +104 -0
- package/templates/apps/api/src/bin/session-retention-sweep.ts +46 -0
- package/templates/apps/api/src/cli.ts +5 -11
- package/templates/apps/api/src/config/index.ts +35 -0
- package/templates/apps/api/src/config/modules.ts +43 -0
- package/templates/apps/api/src/lib/__mocks__/prisma-client.js +11 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -0
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +2 -2
- package/templates/apps/api/src/lib/site.ts +16 -0
- package/templates/apps/api/src/lib/stripe.ts +22 -0
- package/templates/apps/api/src/lib/tenant-db.ts +225 -0
- package/templates/apps/api/src/lib/ws-token.ts +91 -0
- package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +7 -1
- package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +7 -1
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +91 -4
- package/templates/apps/api/src/middleware/auth.ts +43 -8
- package/templates/apps/api/src/middleware/rate-limit.ts +78 -3
- package/templates/apps/api/src/middleware/tenant.ts +99 -0
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +92 -0
- package/templates/apps/api/src/openapi/spec.ts +312 -3
- package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +175 -0
- package/templates/apps/api/src/routes/aeo/index.ts +107 -0
- package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +5 -2
- package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +22 -3
- package/templates/apps/api/src/routes/auth/tokens.ts +13 -1
- package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +450 -0
- package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +262 -0
- package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +436 -0
- package/templates/apps/api/src/routes/billing/index.ts +36 -0
- package/templates/apps/api/src/routes/billing/portal.ts +182 -0
- package/templates/apps/api/src/routes/billing/read.ts +75 -0
- package/templates/apps/api/src/routes/billing/usage.ts +153 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +99 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +20 -8
- package/templates/apps/api/src/routes/content/create.ts +1 -0
- package/templates/apps/api/src/routes/content/delete.ts +1 -0
- package/templates/apps/api/src/routes/content/index.ts +42 -6
- package/templates/apps/api/src/routes/content/update.ts +1 -0
- package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +393 -0
- package/templates/apps/api/src/routes/deliverables/index.ts +200 -0
- package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +443 -0
- package/templates/apps/api/src/routes/flow-checkouts/index.ts +82 -0
- package/templates/apps/api/src/routes/flows/README.md +147 -0
- package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +752 -0
- package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +671 -0
- package/templates/apps/api/src/routes/flows/index.ts +202 -0
- package/templates/apps/api/src/routes/flows/recommender.ts +443 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +438 -31
- package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +10 -10
- package/templates/apps/api/src/routes/leads/index.ts +43 -9
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +558 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +815 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +869 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +221 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +468 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +424 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +707 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +375 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +467 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +566 -0
- package/templates/apps/api/src/routes/project-listings/get.ts +70 -0
- package/templates/apps/api/src/routes/project-listings/index.ts +120 -0
- package/templates/apps/api/src/routes/project-listings/list.ts +47 -0
- package/templates/apps/api/src/routes/project-listings/me.ts +76 -0
- package/templates/apps/api/src/routes/project-listings/patch-copy.ts +222 -0
- package/templates/apps/api/src/routes/project-listings/patch-draft.ts +97 -0
- package/templates/apps/api/src/routes/project-listings/patch.ts +136 -0
- package/templates/apps/api/src/routes/project-listings/public.ts +52 -0
- package/templates/apps/api/src/routes/project-listings/respond.ts +37 -0
- package/templates/apps/api/src/routes/project-listings/resume-token.ts +22 -0
- package/templates/apps/api/src/routes/project-listings/start.ts +106 -0
- package/templates/apps/api/src/routes/project-listings/submit.ts +122 -0
- package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +490 -0
- package/templates/apps/api/src/routes/schedule/index.ts +249 -0
- package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +385 -0
- package/templates/apps/api/src/routes/slack/actions.ts +177 -0
- package/templates/apps/api/src/routes/slack/index.ts +38 -0
- package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +16 -6
- package/templates/apps/api/src/routes/test/auth/session.ts +8 -6
- package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +577 -0
- package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +78 -0
- package/templates/apps/api/src/routes/waitlist-experiments/index.ts +41 -0
- package/templates/apps/api/src/routes/waitlist-experiments/list.ts +64 -0
- package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +42 -0
- package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +84 -0
- package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +119 -0
- package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +238 -0
- package/templates/apps/api/src/routes/waitlist-signups/index.ts +101 -0
- package/templates/apps/api/src/routes/webhooks/README.md +35 -12
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +315 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +270 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +248 -20
- package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +356 -0
- package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +671 -0
- package/templates/apps/api/src/routes/workspaces/config.ts +125 -0
- package/templates/apps/api/src/routes/workspaces/index.ts +194 -0
- package/templates/apps/api/src/routes/workspaces/sessions.ts +218 -0
- package/templates/apps/api/src/server.ts +307 -7
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +489 -0
- package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +475 -0
- package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +132 -0
- package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +65 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +282 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +523 -0
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +359 -0
- package/templates/apps/api/src/services/__tests__/entitlements.test.ts +142 -0
- package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +425 -0
- package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +2840 -0
- package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +393 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +8 -8
- package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +560 -0
- package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +684 -0
- package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +406 -0
- package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +290 -0
- package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +736 -0
- package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +475 -0
- package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +340 -0
- package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +983 -0
- package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +278 -0
- package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +71 -0
- package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +379 -0
- package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +941 -0
- package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +588 -0
- package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +768 -0
- package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +1314 -0
- package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +341 -0
- package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +567 -0
- package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +474 -0
- package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +354 -0
- package/templates/apps/api/src/services/abandon-email.ts +307 -0
- package/templates/apps/api/src/services/aeo-score.ts +288 -0
- package/templates/apps/api/src/services/answer-distributions.ts +138 -0
- package/templates/apps/api/src/services/consent.ts +236 -0
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +523 -0
- package/templates/apps/api/src/services/entitlements.ts +102 -0
- package/templates/apps/api/src/services/flow-aggregate.ts +232 -0
- package/templates/apps/api/src/services/flow-checkouts.ts +123 -0
- package/templates/apps/api/src/services/flow-engine.ts +1296 -0
- package/templates/apps/api/src/services/lead-promotion.ts +176 -0
- package/templates/apps/api/src/services/lead-routing.ts +3 -3
- package/templates/apps/api/src/services/listing-config.ts +102 -0
- package/templates/apps/api/src/services/listing-csv-sweep.ts +455 -0
- package/templates/apps/api/src/services/listing-promotion.ts +342 -0
- package/templates/apps/api/src/services/playbook-compile.ts +398 -0
- package/templates/apps/api/src/services/playbook-render.ts +263 -0
- package/templates/apps/api/src/services/project-listing-decision.ts +510 -0
- package/templates/apps/api/src/services/project-listing-flow.ts +426 -0
- package/templates/apps/api/src/services/project-listing-issue.ts +251 -0
- package/templates/apps/api/src/services/project-listings.ts +1472 -0
- package/templates/apps/api/src/services/recommender-capture.ts +835 -0
- package/templates/apps/api/src/services/scheduling/cal-provider.ts +392 -0
- package/templates/apps/api/src/services/scheduling/index.ts +63 -0
- package/templates/apps/api/src/services/scheduling/types.ts +88 -0
- package/templates/apps/api/src/services/slack-notify.ts +240 -0
- package/templates/apps/api/src/services/tenant-context.ts +451 -0
- package/templates/apps/api/src/services/usage-aggregation.ts +516 -0
- package/templates/apps/api/src/services/usage-credits.ts +340 -0
- package/templates/apps/api/src/services/usage-metering.ts +699 -0
- package/templates/apps/api/src/services/waitlist-dashboard.ts +947 -0
- package/templates/apps/api/src/services/waitlist-experiments.ts +213 -0
- package/templates/apps/api/src/services/waitlist-followup.ts +486 -0
- package/templates/apps/api/src/services/waitlist-scoring.ts +166 -0
- package/templates/apps/api/src/services/waitlist-signups.ts +165 -0
- package/templates/apps/api/src/vercel-handler.ts +165 -0
- package/templates/apps/api/tsconfig.json +0 -12
- package/templates/apps/mcp/CLAUDE.md +19 -9
- package/templates/apps/mcp/LICENSE +201 -0
- package/templates/apps/mcp/__tests__/create_page.test.ts +1 -0
- package/templates/apps/mcp/__tests__/delete_page.test.ts +1 -0
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +105 -0
- package/templates/apps/mcp/__tests__/parity.test.ts +70 -6
- package/templates/apps/mcp/__tests__/request_callback.test.ts +4 -4
- package/templates/apps/mcp/__tests__/schedule_tools.test.ts +242 -0
- package/templates/apps/mcp/__tests__/submit_lead.test.ts +4 -4
- package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +4 -4
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +47 -0
- package/templates/apps/mcp/__tests__/update_page.test.ts +1 -0
- package/templates/apps/mcp/package.json +6 -4
- package/templates/apps/mcp/src/config/modules.ts +37 -0
- package/templates/apps/mcp/src/server.ts +109 -18
- package/templates/apps/mcp/src/tools/capture_lead.ts +1 -1
- package/templates/apps/mcp/src/tools/create_booking.ts +58 -0
- package/templates/apps/mcp/src/tools/create_page.ts +1 -0
- package/templates/apps/mcp/src/tools/delete_page.ts +1 -0
- package/templates/apps/mcp/src/tools/get_event_meta.ts +55 -0
- package/templates/apps/mcp/src/tools/get_flow.ts +61 -0
- package/templates/apps/mcp/src/tools/index.ts +25 -0
- package/templates/apps/mcp/src/tools/list_availability.ts +67 -0
- package/templates/apps/mcp/src/tools/purchase.ts +3 -0
- package/templates/apps/mcp/src/tools/submit_flow_step.ts +83 -0
- package/templates/apps/mcp/src/tools/update_page.ts +1 -0
- package/templates/apps/mcp/tsconfig.json +1 -9
- package/templates/apps/web/.env.example +44 -0
- package/templates/apps/web/CLAUDE.md +28 -0
- package/templates/apps/web/LICENSE +201 -0
- package/templates/apps/web/__tests__/__mocks__/code-highlight.jest-stub.ts +24 -0
- package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +33 -0
- package/templates/apps/web/__tests__/__mocks__/next-intl.ts +18 -0
- package/templates/apps/web/__tests__/__mocks__/server-only.ts +13 -0
- package/templates/apps/web/__tests__/__mocks__/style-mock.js +4 -0
- package/templates/apps/web/__tests__/__mocks__/vercel-analytics-server.ts +9 -0
- package/templates/apps/web/__tests__/jest-support/import-meta-transformer.cjs +90 -0
- package/templates/apps/web/__tests__/setup.ts +22 -32
- package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +6 -3
- package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +8 -2
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +260 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +263 -0
- package/templates/apps/web/app/[locale]/(authed)/account/sign-out-button.tsx +39 -0
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +54 -0
- package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +96 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +309 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +149 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +273 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/gate.test.tsx +172 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +171 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +179 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +116 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ForbiddenState.tsx +24 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +66 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +61 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +114 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +139 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +85 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +67 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/format.ts +111 -0
- package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +179 -0
- package/templates/apps/web/app/[locale]/about/page.tsx +10 -1
- package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +6 -1
- package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/careers/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/case-studies/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/clients/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +21 -1
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +188 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +242 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +29 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/intake-analytics.test.ts +51 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +39 -15
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +21 -3
- package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +49 -0
- package/templates/apps/web/app/[locale]/contact/intake-script.ts +66 -0
- package/templates/apps/web/app/[locale]/contact/page.tsx +14 -4
- package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/courses/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +1 -0
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/downloads/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +12 -2
- package/templates/apps/web/app/[locale]/events/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/faq/page.tsx +10 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +265 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +445 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +341 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +287 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +302 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +128 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +254 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +144 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +108 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +532 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +121 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +457 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +396 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +114 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +52 -0
- package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/integrations/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/launch-service/page.tsx +14 -1
- package/templates/apps/web/app/[locale]/layout.tsx +127 -16
- package/templates/apps/web/app/[locale]/page.tsx +10 -1
- package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/partners/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/products/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +10 -0
- package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +110 -0
- package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +96 -0
- package/templates/apps/web/app/[locale]/projects/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +12 -2
- package/templates/apps/web/app/[locale]/resources/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +117 -0
- package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +96 -0
- package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +92 -0
- package/templates/apps/web/app/[locale]/schedule/page.tsx +82 -0
- package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +9 -0
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
- package/templates/apps/web/app/[locale]/services/page.tsx +9 -1
- package/templates/apps/web/app/[locale]/sign-in/__tests__/next-path.test.ts +31 -0
- package/templates/apps/web/app/[locale]/sign-in/__tests__/page.test.tsx +152 -0
- package/templates/apps/web/app/[locale]/sign-in/next-path.ts +0 -0
- package/templates/apps/web/app/[locale]/sign-in/page.tsx +83 -0
- package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +123 -0
- package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/testimonials/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +93 -0
- package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +48 -0
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +11 -1
- package/templates/apps/web/app/[locale]/use-cases/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +13 -1
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
- package/templates/apps/web/app/globals.css +35 -0
- package/templates/apps/web/app/llms.txt/route.ts +2 -1
- package/templates/apps/web/app/og/route.tsx +10 -2
- package/templates/apps/web/auth.ts +8 -0
- package/templates/apps/web/components/sign-out-trigger.tsx +42 -0
- package/templates/apps/web/content/en/blog/README.txt +25 -0
- package/templates/apps/web/content/en/docs/index.mdx +28 -0
- package/templates/apps/web/jest.config.cjs +90 -27
- package/templates/apps/web/lib/__tests__/site-brand.test.ts +119 -0
- package/templates/apps/web/lib/__tests__/site-theme.test.ts +112 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +499 -0
- package/templates/apps/web/lib/account-config.ts +13 -0
- package/templates/apps/web/lib/server-api.ts +85 -0
- package/templates/apps/web/lib/site-brand.tsx +121 -0
- package/templates/apps/web/lib/site-theme.ts +74 -0
- package/templates/apps/web/messages/en.json +275 -7
- package/templates/apps/web/middleware.ts +11 -3
- package/templates/apps/web/next.config.mjs +8 -2
- package/templates/apps/web/package.json +17 -7
- package/templates/apps/web/postcss.config.mjs +23 -0
- package/templates/apps/web/public/.well-known/security.txt +3 -3
- package/templates/apps/web/src/lib/routes.ts +15 -2
- package/templates/apps/web/tsconfig.json +1 -16
- package/templates/content/_site.mdx +36 -25
- package/templates/content/en/AboutPage/about.mdx +24 -14
- package/templates/content/en/ContactPage/contact.mdx +23 -14
- package/templates/content/en/FAQPage/faq.mdx +10 -392
- package/templates/content/en/HomePage/home.mdx +14 -285
- package/templates/database/CHANGELOG.md +443 -0
- package/templates/database/LICENSE +201 -0
- package/templates/database/README.md +3 -1
- package/templates/database/__tests__/flows-listing-diagnostic.test.ts +392 -0
- package/templates/database/__tests__/flows-presets.test.ts +173 -0
- package/templates/database/__tests__/prefixed-ids.db.test.ts +146 -0
- package/templates/database/__tests__/seed.test.ts +88 -0
- package/templates/database/__tests__/user-cascade.test.ts +19 -4
- package/templates/database/auth/schema.prisma +521 -0
- package/templates/database/config/schema.prisma +234 -0
- package/templates/database/content/schema.prisma +263 -0
- package/templates/database/flows/listing-diagnostic.ts +182 -0
- package/templates/database/flows/presets.ts +315 -0
- package/templates/database/inbox/__tests__/schema.test.ts +4 -0
- package/templates/database/inbox/schema.prisma +574 -3
- package/templates/database/jest.config.cjs +8 -0
- package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +201 -0
- package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +67 -0
- package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +46 -0
- package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +25 -0
- package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +49 -0
- package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +197 -0
- package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +32 -0
- package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +22 -0
- package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +151 -0
- package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +223 -0
- package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +22 -0
- package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +32 -0
- package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +40 -0
- package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +4 -0
- package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +2 -0
- package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +2 -0
- package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +29 -0
- package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +6 -0
- package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +39 -0
- package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +24 -0
- package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +3 -0
- package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +33 -0
- package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +30 -0
- package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +47 -0
- package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +16 -0
- package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +47 -0
- package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +10 -0
- package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +26 -0
- package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +90 -0
- package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +174 -0
- package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +14 -0
- package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +25 -0
- package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +32 -0
- package/templates/database/migrations/20260911180000_consent_grants/migration.sql +71 -0
- package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +30 -0
- package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +49 -0
- package/templates/database/ops/schema.prisma +27 -0
- package/templates/database/package.json +42 -4
- package/templates/database/prisma.config.ts +1 -1
- package/templates/database/schema.prisma +19 -435
- package/templates/database/seed-test.ts +14 -4
- package/templates/database/seed.ts +148 -0
- package/templates/database/tenancy.ts +45 -0
- package/templates/database/tsconfig.flows.json +15 -0
- package/templates/database/tsconfig.json +7 -1
- package/templates/database/tsconfig.tenancy.json +14 -0
- package/templates/package.json +30 -55
- package/templates/pnpm-workspace.yaml +0 -3
- package/templates/project.yaml +1 -1
- package/templates/tsconfig.json +1 -37
- package/templates/apps/web/__tests__/auth/adversarial/cookie-attributes.test.ts +0 -94
- package/templates/apps/web/__tests__/auth/adversarial/oauth-state-parameter.test.ts +0 -75
- package/templates/apps/web/__tests__/auth/adversarial/redirect-uri-allowlist.test.ts +0 -82
- package/templates/apps/web/__tests__/auth/env-helpers.test.ts +0 -168
- package/templates/apps/web/__tests__/authority-list-routes.test.tsx +0 -208
- package/templates/apps/web/__tests__/better-stack.test.ts +0 -66
- package/templates/apps/web/__tests__/blog-dir-resolution.test.ts +0 -87
- package/templates/apps/web/__tests__/blog-source-populated.test.ts +0 -130
- package/templates/apps/web/__tests__/data-testid-coverage.test.tsx +0 -60
- package/templates/apps/web/__tests__/google-analytics.test.tsx +0 -104
- package/templates/apps/web/__tests__/i18n/middleware.test.ts +0 -119
- package/templates/apps/web/__tests__/i18n/routing.test.ts +0 -114
- package/templates/apps/web/__tests__/i18n/translations.test.ts +0 -191
- package/templates/apps/web/__tests__/json-ld.test.ts +0 -98
- package/templates/apps/web/__tests__/knowledge-events-routes.test.tsx +0 -580
- package/templates/apps/web/__tests__/layout-stealth.test.ts +0 -39
- package/templates/apps/web/__tests__/lighthouse-fixtures/improvement-image-shrink.tsx +0 -103
- package/templates/apps/web/__tests__/lighthouse-fixtures/override-fixture.md +0 -114
- package/templates/apps/web/__tests__/lighthouse-fixtures/regression-bloat.tsx +0 -94
- package/templates/apps/web/__tests__/page.test.tsx +0 -230
- package/templates/apps/web/__tests__/products-services-phase1.test.tsx +0 -321
- package/templates/apps/web/__tests__/projects-routes.test.tsx +0 -392
- package/templates/apps/web/__tests__/resources-downloads-integrations-phase2.test.tsx +0 -475
- package/templates/apps/web/__tests__/routes/ai-plugin-json.test.ts +0 -53
- package/templates/apps/web/__tests__/routes/blog-route.test.tsx +0 -191
- package/templates/apps/web/__tests__/routes/llms-txt.test.ts +0 -123
- package/templates/apps/web/__tests__/routes/robots-txt.test.ts +0 -52
- package/templates/apps/web/__tests__/routes/sitemap-xml.test.ts +0 -78
- package/templates/apps/web/__tests__/routes/well-known-mcp.test.ts +0 -203
- package/templates/apps/web/__tests__/section-routes.test.tsx +0 -693
- package/templates/apps/web/__tests__/security-headers.test.ts +0 -201
- package/templates/apps/web/__tests__/sentry-config.test.ts +0 -116
- package/templates/apps/web/__tests__/sitemap-locales.test.ts +0 -415
- package/templates/apps/web/__tests__/standalone-informational-routes.test.tsx +0 -844
- package/templates/apps/web/app/[locale]/__tests__/layout.test.tsx +0 -120
- package/templates/apps/web/app/[locale]/__tests__/page-renderer-migration.test.tsx +0 -202
- package/templates/apps/web/app/__tests__/error-boundaries.test.tsx +0 -152
- package/templates/apps/web/content/en/blog/index.mdx +0 -15
- package/templates/apps/web/content/en/blog/launching-ailk.mdx +0 -20
- package/templates/apps/web/content/en/docs/getting-started.mdx +0 -30
- package/templates/content/.gitkeep +0 -0
- package/templates/content/CLAUDE.md +0 -85
- package/templates/content/ar/HomePage/home.mdx +0 -123
- package/templates/content/ar/_site.mdx +0 -35
- package/templates/content/de/_site.mdx +0 -29
- package/templates/content/en/.gitkeep +0 -0
- package/templates/content/en/AboutPage/about.schema.json +0 -82
- package/templates/content/en/BookCall/book-call.mdx +0 -19
- package/templates/content/en/BookCall/book-call.schema.json +0 -121
- package/templates/content/en/BreadcrumbList/site-breadcrumbs.mdx +0 -20
- package/templates/content/en/CareersList/careers.mdx +0 -24
- package/templates/content/en/CaseStudiesList/case-studies.mdx +0 -22
- package/templates/content/en/CaseStudiesList/showcase.mdx +0 -8
- package/templates/content/en/CaseStudy/ailk-aeo-rollout.mdx +0 -32
- package/templates/content/en/CaseStudy/schema-builder-migration.mdx +0 -41
- package/templates/content/en/ClientsList/clients.mdx +0 -38
- package/templates/content/en/ContactPage/contact.schema.json +0 -100
- package/templates/content/en/ContactPoint/primary.mdx +0 -14
- package/templates/content/en/Course/aeo-fundamentals.mdx +0 -37
- package/templates/content/en/Course/schema-builder-workshop.mdx +0 -62
- package/templates/content/en/CoursesList/courses.mdx +0 -22
- package/templates/content/en/DownloadsList/downloads.mdx +0 -22
- package/templates/content/en/Event/aeo-office-hours-may-2026.mdx +0 -39
- package/templates/content/en/Event/ailk-launch-day-2026.mdx +0 -35
- package/templates/content/en/EventsList/events.mdx +0 -26
- package/templates/content/en/FAQPage/faq.schema.json +0 -231
- package/templates/content/en/GlossaryList/glossary.mdx +0 -19
- package/templates/content/en/GlossaryList/glossary.schema.json +0 -300
- package/templates/content/en/HomePage/home.schema.json +0 -88
- package/templates/content/en/HomePage/launch-service.mdx +0 -475
- package/templates/content/en/HomePage/launch-service.schema.json +0 -138
- package/templates/content/en/HowTo/add-mdx-content.mdx +0 -38
- package/templates/content/en/HowTo/configure-aeo-scoring.mdx +0 -35
- package/templates/content/en/HowTo/set-up-json-ld.mdx +0 -36
- package/templates/content/en/Integration/github-actions.mdx +0 -31
- package/templates/content/en/Integration/openai-api.mdx +0 -26
- package/templates/content/en/Integration/vercel.mdx +0 -32
- package/templates/content/en/IntegrationsList/integrations.mdx +0 -22
- package/templates/content/en/JobPosting/content-strategist.mdx +0 -46
- package/templates/content/en/JobPosting/developer-advocate.mdx +0 -47
- package/templates/content/en/JobPosting/senior-fullstack-engineer.mdx +0 -57
- package/templates/content/en/LandingPage/aeo-audit.mdx +0 -19
- package/templates/content/en/LandingPage/aeo-audit.schema.json +0 -92
- package/templates/content/en/LandingPage/agency-waitlist.mdx +0 -340
- package/templates/content/en/LandingPage/agency-waitlist.schema.json +0 -152
- package/templates/content/en/LandingPage/developers.mdx +0 -19
- package/templates/content/en/LandingPage/developers.schema.json +0 -109
- package/templates/content/en/LandingPage/for-agencies.mdx +0 -390
- package/templates/content/en/LandingPage/for-agencies.schema.json +0 -107
- package/templates/content/en/LandingPage/for-ai-search-agencies.mdx +0 -417
- package/templates/content/en/LandingPage/for-ai-search-agencies.schema.json +0 -105
- package/templates/content/en/LandingPage/for-businesses.mdx +0 -19
- package/templates/content/en/LandingPage/for-businesses.schema.json +0 -96
- package/templates/content/en/LandingPage/platform.mdx +0 -369
- package/templates/content/en/LandingPage/platform.schema.json +0 -166
- package/templates/content/en/LandingPage/vs-ai-website-builders.mdx +0 -19
- package/templates/content/en/LandingPage/vs-ai-website-builders.schema.json +0 -102
- package/templates/content/en/LandingPage/vs-wordpress.mdx +0 -19
- package/templates/content/en/LandingPage/vs-wordpress.schema.json +0 -53
- package/templates/content/en/NewsletterSignup/newsletter.mdx +0 -236
- package/templates/content/en/NewsletterSignup/newsletter.schema.json +0 -91
- package/templates/content/en/Organization/site-owner.mdx +0 -16
- package/templates/content/en/Partner/prisma.mdx +0 -31
- package/templates/content/en/Partner/supabase.mdx +0 -30
- package/templates/content/en/Partner/vercel.mdx +0 -28
- package/templates/content/en/PartnersList/partners.mdx +0 -22
- package/templates/content/en/Person/founder.mdx +0 -18
- package/templates/content/en/Pricing/pricing.mdx +0 -538
- package/templates/content/en/Pricing/pricing.schema.json +0 -209
- package/templates/content/en/PrivacyPage/privacy.mdx +0 -165
- package/templates/content/en/PrivacyPage/privacy.schema.json +0 -64
- package/templates/content/en/Product/ailk.mdx +0 -19
- package/templates/content/en/Product/ailk.schema.json +0 -94
- package/templates/content/en/Product/api-launch-kit.mdx +0 -26
- package/templates/content/en/Product/launch-kit.mdx +0 -28
- package/templates/content/en/ProductsList/products.mdx +0 -26
- package/templates/content/en/Project/ailk-phase-2.mdx +0 -54
- package/templates/content/en/ProjectsList/projects.mdx +0 -25
- package/templates/content/en/ResourceDetail/aeo-scoring-guide.mdx +0 -27
- package/templates/content/en/ResourceDetail/content-model-whitepaper.mdx +0 -28
- package/templates/content/en/ResourceDetail/schema-org-quick-reference.mdx +0 -39
- package/templates/content/en/ResourcesHub/resources.mdx +0 -22
- package/templates/content/en/Service/consulting.mdx +0 -22
- package/templates/content/en/Service/implementation.mdx +0 -30
- package/templates/content/en/Service/launch-service.mdx +0 -232
- package/templates/content/en/Service/launch.mdx +0 -228
- package/templates/content/en/ServicesList/services.mdx +0 -84
- package/templates/content/en/SoftwareProduct/ailk-aeo.mdx +0 -44
- package/templates/content/en/SoftwareProduct/ailk-content-adapters.mdx +0 -47
- package/templates/content/en/SoftwareProduct/ailk-schema.mdx +0 -49
- package/templates/content/en/TeamList/team.mdx +0 -63
- package/templates/content/en/TeamMember/founder.mdx +0 -72
- package/templates/content/en/TermsPage/terms.mdx +0 -19
- package/templates/content/en/TermsPage/terms.schema.json +0 -99
- package/templates/content/en/TestimonialsList/testimonials.mdx +0 -44
- package/templates/content/en/UseCasesList/use-cases.mdx +0 -30
- package/templates/content/en/Whitepaper/ax-first-product-development.mdx +0 -27
- package/templates/content/en/Whitepaper/open-core-commercial-strategy.mdx +0 -27
- package/templates/content/en/Whitepaper/structured-data-for-answer-engines.mdx +0 -31
- package/templates/content/en-XA/AboutPage/about.mdx +0 -8
- package/templates/content/en-XA/FAQPage/faq.mdx +0 -8
- package/templates/content/en-XA/HomePage/home.mdx +0 -8
- package/templates/content/es/_site.mdx +0 -30
- package/templates/content/fr/_site.mdx +0 -30
- package/templates/content/ja/_site.mdx +0 -30
- package/templates/content/pt-BR/AboutPage/about.mdx +0 -8
- package/templates/content/pt-BR/FAQPage/faq.mdx +0 -8
- package/templates/content/pt-BR/HomePage/home.mdx +0 -23
package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx
CHANGED
|
@@ -73,7 +73,7 @@ describe("IntegrationsPage — snapshot regression", () => {
|
|
|
73
73
|
});
|
|
74
74
|
const { container } = render(element);
|
|
75
75
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
76
|
-
`"<main><h1>Integrations</h1><p data-testid="section-empty-state">Integrations listing coming soon.</p></main>"`,
|
|
76
|
+
`"<main><h1 class="text-heading-1 font-display font-normal tracking-tighter text-fg hero-type-display">Integrations</h1><p data-testid="section-empty-state">Integrations listing coming soon.</p></main>"`,
|
|
77
77
|
);
|
|
78
78
|
});
|
|
79
79
|
|
|
@@ -86,7 +86,7 @@ describe("IntegrationsPage — snapshot regression", () => {
|
|
|
86
86
|
});
|
|
87
87
|
const { container } = render(element);
|
|
88
88
|
expect(container.innerHTML).toMatchInlineSnapshot(`
|
|
89
|
-
"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Integrations","description":"First-party integrations for AI Launch Kit.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"SoftwareApplication"}]}}</script><main><h1>Integrations</h1><p data-testid="integrations-description">First-party integrations for 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) 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) 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="# Integrations
|
|
89
|
+
"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@type":"CollectionPage","inLanguage":"en","name":"Integrations","description":"First-party integrations for AI Launch Kit.","mainEntity":{"@type":"ItemList","itemListElement":[{"@type":"SoftwareApplication"}]}}</script><main><h1>Integrations</h1><p data-testid="integrations-description">First-party integrations for 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-remote" data-source="# Integrations
|
|
90
90
|
|
|
91
91
|
Body."></div></article></div></div></main>"
|
|
92
92
|
`);
|
|
@@ -110,7 +110,7 @@ describe("IntegrationSlugPage — snapshot regression", () => {
|
|
|
110
110
|
});
|
|
111
111
|
const { container } = render(element);
|
|
112
112
|
expect(container.innerHTML).toMatchInlineSnapshot(`
|
|
113
|
-
"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"OpenAI API","description":"Connect AILK to OpenAI.","mainEntity":{"@type":"SoftwareApplication","name":"OpenAI API"}},{"@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":"Integrations","item":"http://localhost:3000/en/integrations"},{"@type":"ListItem","position":3,"name":"OpenAI API","item":"http://localhost:3000/en/integrations/openai-api"}]}]}</script><main><h1>OpenAI API</h1><p data-testid="integration-description">Connect AILK to OpenAI.</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) 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) 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="# OpenAI API
|
|
113
|
+
"<script type="application/ld+json" data-testid="json-ld">{"@context":"https://schema.org","@graph":[{"@context":"https://schema.org","@type":"WebPage","inLanguage":"en","name":"OpenAI API","description":"Connect AILK to OpenAI.","mainEntity":{"@context":"https://schema.org","@type":"SoftwareApplication","name":"OpenAI API","description":"Connect AILK to OpenAI."}},{"@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":"Integrations","item":"http://localhost:3000/en/integrations"},{"@type":"ListItem","position":3,"name":"OpenAI API","item":"http://localhost:3000/en/integrations/openai-api"}]}]}</script><main><h1>OpenAI API</h1><p data-testid="integration-description">Connect AILK to OpenAI.</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="# OpenAI API
|
|
114
114
|
|
|
115
115
|
Body."></div></article></div></div></main>"
|
|
116
116
|
`);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Integrations list page. Reads IntegrationsList MDX via the content adapter and
|
|
2
2
|
// emits a JSON-LD CollectionPage via the dispatcher.
|
|
3
3
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
4
|
-
import { renderPage } from "@working-theory/page-renderer";
|
|
4
|
+
import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
|
|
5
|
+
import type { Metadata } from "next";
|
|
5
6
|
import type { ReactElement } from "react";
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
@@ -9,6 +10,17 @@ interface Props {
|
|
|
9
10
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
export async function generateMetadata({
|
|
14
|
+
params,
|
|
15
|
+
}: Pick<Props, "params">): Promise<Metadata> {
|
|
16
|
+
const { locale } = await params;
|
|
17
|
+
return renderPageMetadata({
|
|
18
|
+
type: "IntegrationsList",
|
|
19
|
+
slug: "integrations",
|
|
20
|
+
locale,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
12
24
|
export default async function Page({
|
|
13
25
|
params,
|
|
14
26
|
getMdxSource,
|
|
@@ -4,8 +4,10 @@ import type {
|
|
|
4
4
|
getMdxSource as defaultGetMdxSource,
|
|
5
5
|
getPageSections as defaultGetPageSections} from "@working-theory/page-renderer";
|
|
6
6
|
import {
|
|
7
|
-
renderPage
|
|
7
|
+
renderPage,
|
|
8
|
+
renderPageMetadata
|
|
8
9
|
} from "@working-theory/page-renderer";
|
|
10
|
+
import type { Metadata } from "next";
|
|
9
11
|
import type { ReactElement } from "react";
|
|
10
12
|
|
|
11
13
|
interface Props {
|
|
@@ -14,6 +16,17 @@ interface Props {
|
|
|
14
16
|
getPageSections?: typeof defaultGetPageSections;
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
export async function generateMetadata({
|
|
20
|
+
params,
|
|
21
|
+
}: Pick<Props, "params">): Promise<Metadata> {
|
|
22
|
+
const { locale } = await params;
|
|
23
|
+
return renderPageMetadata({
|
|
24
|
+
type: "Service",
|
|
25
|
+
slug: "launch-service",
|
|
26
|
+
locale,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
export default async function Page({
|
|
18
31
|
params,
|
|
19
32
|
getMdxSource,
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// Theme 1.9 — SSR theme + flash-prevention + CSS vars + meta + JSON-LD (#1322)
|
|
4
4
|
// Chrome extract — Nav + Footer from @working-theory/ui (#1451)
|
|
5
5
|
// Shell/Chrome composition — Nav + Footer wrap via @working-theory/templates MarketingShell (#2820)
|
|
6
|
+
// Session-aware Nav actions — Sign in / Account + Sign out from the server-read session (#4984)
|
|
7
|
+
// Site-declared theme — `_site.mdx` `theme:` block → token overlay → CSS vars
|
|
6
8
|
//
|
|
7
9
|
// Sits under app/[locale]/ and sets the <html lang> attribute to the
|
|
8
10
|
// active locale. next-intl's NextIntlClientProvider is wired here so
|
|
@@ -15,11 +17,21 @@
|
|
|
15
17
|
// Spec: docs/internal/specs/i18n-phase2-s1-hreflang-metadata-spec.md
|
|
16
18
|
// Spec: docs/internal/specs/theme-wiring-phase1-site-rendering-spec.md
|
|
17
19
|
|
|
20
|
+
// Global stylesheet — Tailwind v4 layer stack + @working-theory/theme tokens.
|
|
21
|
+
// Must be imported by the layout that owns <html>/<body> so it applies to
|
|
22
|
+
// every page in the tree (#4090 item 1).
|
|
23
|
+
import "../globals.css";
|
|
24
|
+
|
|
18
25
|
import { Telemetry, VerificationMeta } from "@working-theory/analytics";
|
|
19
26
|
import { MarketingShell } from "@working-theory/templates/shells";
|
|
20
|
-
import {
|
|
27
|
+
import { themeToCssVarsBothModes } from "@working-theory/theme";
|
|
21
28
|
import { isRtl } from "@working-theory/ui";
|
|
22
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
Footer,
|
|
31
|
+
Nav,
|
|
32
|
+
type NavAction,
|
|
33
|
+
type NavActionFlow,
|
|
34
|
+
} from "@working-theory/ui/chrome";
|
|
23
35
|
import {
|
|
24
36
|
buildHreflangAlternates,
|
|
25
37
|
getStealthRobots,
|
|
@@ -31,20 +43,40 @@ import { NextIntlClientProvider } from "next-intl";
|
|
|
31
43
|
import { getMessages, getTranslations } from "next-intl/server";
|
|
32
44
|
import type { ReactElement, ReactNode } from "react";
|
|
33
45
|
|
|
46
|
+
import { auth } from "../../auth";
|
|
34
47
|
import { GoogleAnalytics } from "../../components/google-analytics";
|
|
48
|
+
import { SignOutTrigger } from "../../components/sign-out-trigger";
|
|
35
49
|
import { navItems } from "../../lib/nav-config";
|
|
36
50
|
import { getOgImageUrl } from "../../lib/og";
|
|
51
|
+
import { SITE_NAME, siteLogo } from "../../lib/site-brand";
|
|
52
|
+
import { loadSiteBrandTokens } from "../../lib/site-theme";
|
|
37
53
|
import { resolveBaseUrl } from "../../src/lib/routes";
|
|
38
54
|
|
|
39
55
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
40
56
|
|
|
41
|
-
const SITE_NAME = process.env.NEXT_PUBLIC_SITE_NAME ?? "AI Launch Kit";
|
|
42
57
|
const DEFAULT_LOCALE = "en";
|
|
43
58
|
const THEME_COOKIE_NAME =
|
|
44
59
|
process.env.NEXT_PUBLIC_THEME_COOKIE_NAME ?? "ailk-theme";
|
|
45
60
|
|
|
46
61
|
type ThemeMode = "light" | "dark";
|
|
47
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Placeholder flow for the signed-in Nav actions (#4984).
|
|
65
|
+
*
|
|
66
|
+
* `NavAction` has no client-handler kind — its shapes are `link` (navigate)
|
|
67
|
+
* and `flow` (open a StepFlow lightbox). The `flow` kind + Nav's
|
|
68
|
+
* `renderFlowTrigger` composer is Nav's ONE caller-composed seam (#4341): the
|
|
69
|
+
* caller receives the rendered trigger `<Button>` and decides what wraps it.
|
|
70
|
+
* The layout uses that seam WITHOUT a lightbox — `renderFlowTrigger` below
|
|
71
|
+
* replaces both signed-in triggers (Account → a non-navigating name label,
|
|
72
|
+
* Sign out → `SignOutTrigger`), so this flow config is NEVER rendered. It only
|
|
73
|
+
* has to satisfy the type: one minimal fields-step and a dead `action`.
|
|
74
|
+
*/
|
|
75
|
+
const NAV_CLIENT_ACTION_FLOW: NavActionFlow["flow"] = {
|
|
76
|
+
steps: [{ id: "noop", fields: [{ name: "noop", label: "noop", type: "text" }] }],
|
|
77
|
+
action: "#",
|
|
78
|
+
};
|
|
79
|
+
|
|
48
80
|
// ── Theme helpers ────────────────────────────────────────────────────────────
|
|
49
81
|
|
|
50
82
|
/**
|
|
@@ -103,10 +135,10 @@ export async function generateMetadata(): Promise<Metadata> {
|
|
|
103
135
|
);
|
|
104
136
|
|
|
105
137
|
return {
|
|
106
|
-
title:
|
|
138
|
+
title: SITE_NAME,
|
|
107
139
|
description: "AX-first OSS template — API + MCP as canonical surfaces.",
|
|
108
140
|
openGraph: {
|
|
109
|
-
title:
|
|
141
|
+
title: SITE_NAME,
|
|
110
142
|
description: "AX-first OSS template — API + MCP as canonical surfaces.",
|
|
111
143
|
type: "website",
|
|
112
144
|
url: baseUrl,
|
|
@@ -114,7 +146,7 @@ export async function generateMetadata(): Promise<Metadata> {
|
|
|
114
146
|
},
|
|
115
147
|
twitter: {
|
|
116
148
|
card: "summary_large_image",
|
|
117
|
-
title:
|
|
149
|
+
title: SITE_NAME,
|
|
118
150
|
description: "AX-first OSS template — API + MCP as canonical surfaces.",
|
|
119
151
|
images: [ogImageUrl],
|
|
120
152
|
},
|
|
@@ -145,6 +177,9 @@ export default async function LocaleLayout({
|
|
|
145
177
|
const { locale } = await params;
|
|
146
178
|
const messages = await getMessages();
|
|
147
179
|
const themeMode = await resolveThemeMode();
|
|
180
|
+
// Server-side session read (#4984) — the same `auth.api.getSession` idiom
|
|
181
|
+
// the `(authed)` segment guard uses. Null when signed out or auth is unset.
|
|
182
|
+
const session = await auth.api.getSession({ headers: await headers() });
|
|
148
183
|
|
|
149
184
|
// Resolve nav labels server-side so Nav + Footer components stay
|
|
150
185
|
// i18n-hook-free and are testable without a NextIntlClientProvider wrapper.
|
|
@@ -154,21 +189,72 @@ export default async function LocaleLayout({
|
|
|
154
189
|
home: tNav("home"),
|
|
155
190
|
docs: tNav("docs"),
|
|
156
191
|
blog: tNav("blog"),
|
|
192
|
+
// The scheduling module's booking page (#4633). Present in the map
|
|
193
|
+
// unconditionally, like `docs` and `blog`: the map is a label lookup, and
|
|
194
|
+
// an entry for a surface this site did not scaffold is never reached —
|
|
195
|
+
// `nav-config.ts` only lists surfaces the declared architecture carries.
|
|
196
|
+
schedule: tNav("schedule"),
|
|
197
|
+
// Session-aware action labels (#4984). `account` carries the signed-in
|
|
198
|
+
// user's display name (name, else email) so the Nav shows WHO is signed
|
|
199
|
+
// in; it falls back to the generic label when there is no session.
|
|
200
|
+
signIn: tNav("signIn"),
|
|
201
|
+
signOut: tNav("signOut"),
|
|
202
|
+
account: session?.user
|
|
203
|
+
? (session.user.name ?? session.user.email)
|
|
204
|
+
: tNav("account"),
|
|
157
205
|
},
|
|
158
206
|
openMenu: tNav("openMenu"),
|
|
159
207
|
closeMenu: tNav("closeMenu"),
|
|
160
208
|
};
|
|
209
|
+
const navOverflowLabel = tNav("moreLinks");
|
|
210
|
+
|
|
211
|
+
// Right-side action cluster (#4984). Signed out → a single outline-strong
|
|
212
|
+
// "Sign in" link (the operator's auth-pair idiom, #4370, MINUS Sign up —
|
|
213
|
+
// this site offers no sign-up flow). Signed in → the user's name (a
|
|
214
|
+
// non-navigating label) + an outline-strong "Sign out"; both authored as
|
|
215
|
+
// `kind: "flow"` so Nav hands their triggers back through
|
|
216
|
+
// `renderFlowTrigger` (see NAV_CLIENT_ACTION_FLOW above).
|
|
217
|
+
const navActions: NavAction[] = session
|
|
218
|
+
? [
|
|
219
|
+
{ labelKey: "account", kind: "flow", flow: NAV_CLIENT_ACTION_FLOW },
|
|
220
|
+
{
|
|
221
|
+
labelKey: "signOut",
|
|
222
|
+
kind: "flow",
|
|
223
|
+
flow: NAV_CLIENT_ACTION_FLOW,
|
|
224
|
+
variant: "outline-strong",
|
|
225
|
+
},
|
|
226
|
+
]
|
|
227
|
+
: [{ labelKey: "signIn", path: "/sign-in", variant: "outline-strong" }];
|
|
228
|
+
|
|
229
|
+
// Only compose the flow seam when there is a flow action to compose. This
|
|
230
|
+
// layout is a Server Component and `renderFlowTrigger` is a function, so
|
|
231
|
+
// passing it is only legal when Nav actually consumes it here rather than
|
|
232
|
+
// forwarding it across the RSC boundary into NavMobileMenu ("use client").
|
|
233
|
+
// Passing it unconditionally crashed EVERY signed-out render — the whole of a
|
|
234
|
+
// fresh scaffold, which configures no auth provider — with "Functions cannot
|
|
235
|
+
// be passed directly to Client Components". Nav itself already guards its own
|
|
236
|
+
// forward the same way (chrome/Nav.tsx: `{...(renderFlowTrigger ? … : {})}`);
|
|
237
|
+
// this is the caller half of that contract.
|
|
238
|
+
const hasFlowAction = navActions.some((action) => action.kind === "flow");
|
|
161
239
|
|
|
162
240
|
// Shared brand mark injected into both Nav and Footer chrome slots.
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
241
|
+
const brandLogo = siteLogo();
|
|
242
|
+
|
|
243
|
+
// The site's own declared colours, read from `content/_site.mdx`'s `theme:`
|
|
244
|
+
// block and translated from that customer-facing vocabulary onto a token
|
|
245
|
+
// overlay (`siteThemeToBrandTokens`). `undefined` when the site declares no
|
|
246
|
+
// theme — which is the regression floor: no `brand` key is passed, the
|
|
247
|
+
// resolver takes its untouched fast path, and the emitted CSS is
|
|
248
|
+
// byte-for-byte what it was before this wiring existed.
|
|
249
|
+
const siteBrandTokens = loadSiteBrandTokens();
|
|
169
250
|
|
|
170
|
-
|
|
171
|
-
|
|
251
|
+
// Both theme modes, gated by [data-theme="dark"] (#1532 — sibling fix; the
|
|
252
|
+
// single-mode call inlined an unconditional :root block that never
|
|
253
|
+
// responded to a client-side data-theme toggle). themeMode still drives
|
|
254
|
+
// the initial data-theme attribute below for a no-flash first paint.
|
|
255
|
+
const inlineThemeStyle = themeToCssVarsBothModes(
|
|
256
|
+
siteBrandTokens ? { brand: siteBrandTokens } : {},
|
|
257
|
+
);
|
|
172
258
|
|
|
173
259
|
return (
|
|
174
260
|
<html lang={locale} dir={isRtl(locale) ? "rtl" : "ltr"} data-theme={themeMode}>
|
|
@@ -214,7 +300,24 @@ export default async function LocaleLayout({
|
|
|
214
300
|
labels={navLabels}
|
|
215
301
|
navItems={navItems}
|
|
216
302
|
siteName={SITE_NAME}
|
|
217
|
-
logo={
|
|
303
|
+
logo={brandLogo}
|
|
304
|
+
overflowLabel={navOverflowLabel}
|
|
305
|
+
actions={navActions}
|
|
306
|
+
{...(hasFlowAction
|
|
307
|
+
? {
|
|
308
|
+
renderFlowTrigger: (action, trigger) =>
|
|
309
|
+
action.labelKey === "signOut" ? (
|
|
310
|
+
<SignOutTrigger>{trigger}</SignOutTrigger>
|
|
311
|
+
) : (
|
|
312
|
+
<span
|
|
313
|
+
className="text-body-sm text-(--color-text-muted)"
|
|
314
|
+
data-testid="nav-account-name"
|
|
315
|
+
>
|
|
316
|
+
{navLabels.items.account}
|
|
317
|
+
</span>
|
|
318
|
+
),
|
|
319
|
+
}
|
|
320
|
+
: {})}
|
|
218
321
|
/>
|
|
219
322
|
}
|
|
220
323
|
footer={
|
|
@@ -223,7 +326,15 @@ export default async function LocaleLayout({
|
|
|
223
326
|
itemLabels={navLabels.items}
|
|
224
327
|
navItems={navItems}
|
|
225
328
|
siteName={SITE_NAME}
|
|
226
|
-
logo={
|
|
329
|
+
logo={brandLogo}
|
|
330
|
+
// #4157 — deliberately left as a literal "Built with AI Launch
|
|
331
|
+
// Kit" / https://ailaunchkit.ai, not SITE_NAME: this credits
|
|
332
|
+
// the OSS TEMPLATE this site is built with (a "powered by"
|
|
333
|
+
// link, unconditional in OSS forks per the comment above),
|
|
334
|
+
// never the customer's own identity, so it isn't the vendor-
|
|
335
|
+
// brand-as-default defect #4157 fixes. See
|
|
336
|
+
// docs/specs/4157-scaffold-brand-defaults-spec.md § Design
|
|
337
|
+
// Decisions for the fuller reasoning.
|
|
227
338
|
attribution={{
|
|
228
339
|
label: "Built with AI Launch Kit",
|
|
229
340
|
href: "https://ailaunchkit.ai",
|
|
@@ -8,8 +8,10 @@ import type {
|
|
|
8
8
|
getMdxSource as defaultGetMdxSource,
|
|
9
9
|
getPageSections as defaultGetPageSections} from "@working-theory/page-renderer";
|
|
10
10
|
import {
|
|
11
|
-
renderPage
|
|
11
|
+
renderPage,
|
|
12
|
+
renderPageMetadata
|
|
12
13
|
} from "@working-theory/page-renderer";
|
|
14
|
+
import type { Metadata } from "next";
|
|
13
15
|
import type { ReactElement } from "react";
|
|
14
16
|
|
|
15
17
|
interface Props {
|
|
@@ -22,6 +24,13 @@ interface Props {
|
|
|
22
24
|
getPageSections?: typeof defaultGetPageSections;
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
export async function generateMetadata({
|
|
28
|
+
params,
|
|
29
|
+
}: Pick<Props, "params">): Promise<Metadata> {
|
|
30
|
+
const { locale } = await params;
|
|
31
|
+
return renderPageMetadata({ type: "HomePage", slug: "home", locale });
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
export default async function Page({
|
|
26
35
|
params,
|
|
27
36
|
getMdxSource,
|
|
@@ -4,7 +4,9 @@ import {
|
|
|
4
4
|
getMdxSource as defaultGetMdxSource,
|
|
5
5
|
getMdxSlugs,
|
|
6
6
|
renderPage,
|
|
7
|
+
renderPageMetadata,
|
|
7
8
|
} from "@working-theory/page-renderer";
|
|
9
|
+
import type { Metadata } from "next";
|
|
8
10
|
import { notFound } from "next/navigation";
|
|
9
11
|
import type { ReactElement } from "react";
|
|
10
12
|
|
|
@@ -13,6 +15,13 @@ interface Props {
|
|
|
13
15
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
14
16
|
}
|
|
15
17
|
|
|
18
|
+
export async function generateMetadata({
|
|
19
|
+
params,
|
|
20
|
+
}: Pick<Props, "params">): Promise<Metadata> {
|
|
21
|
+
const { locale, slug } = await params;
|
|
22
|
+
return renderPageMetadata({ type: "Partner", slug, locale });
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
export async function generateStaticParams({
|
|
17
26
|
params,
|
|
18
27
|
}: {
|
|
@@ -46,7 +46,7 @@ describe("partners/page — snapshot regression", () => {
|
|
|
46
46
|
});
|
|
47
47
|
const { container } = render(element);
|
|
48
48
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
49
|
-
`"<main><h1>Partners</h1><p data-testid="section-empty-state">Partners listing coming soon.</p></main>"`,
|
|
49
|
+
`"<main><h1 class="text-heading-1 font-display font-normal tracking-tighter text-fg hero-type-display">Partners</h1><p data-testid="section-empty-state">Partners listing coming soon.</p></main>"`,
|
|
50
50
|
);
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -67,7 +67,7 @@ describe("partners/page — snapshot regression", () => {
|
|
|
67
67
|
});
|
|
68
68
|
const { container } = render(element);
|
|
69
69
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
70
|
-
`"<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><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) 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) 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>"`,
|
|
70
|
+
`"<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><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
71
|
);
|
|
72
72
|
});
|
|
73
73
|
});
|
|
@@ -92,7 +92,7 @@ describe("partners/[slug]/page — snapshot regression", () => {
|
|
|
92
92
|
});
|
|
93
93
|
const { container } = render(element);
|
|
94
94
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
95
|
-
`"<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><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) 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) 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>"`,
|
|
95
|
+
`"<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><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
96
|
);
|
|
97
97
|
});
|
|
98
98
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Partners list page. Reads PartnersList MDX via the content adapter and emits
|
|
2
2
|
// a JSON-LD CollectionPage via the dispatcher.
|
|
3
3
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
4
|
-
import { renderPage } from "@working-theory/page-renderer";
|
|
4
|
+
import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
|
|
5
|
+
import type { Metadata } from "next";
|
|
5
6
|
import type { ReactElement } from "react";
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
@@ -9,6 +10,13 @@ interface Props {
|
|
|
9
10
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
export async function generateMetadata({
|
|
14
|
+
params,
|
|
15
|
+
}: Pick<Props, "params">): Promise<Metadata> {
|
|
16
|
+
const { locale } = await params;
|
|
17
|
+
return renderPageMetadata({ type: "PartnersList", slug: "partners", locale });
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
export default async function Page({
|
|
13
21
|
params,
|
|
14
22
|
getMdxSource,
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
// next-mdx-remote/rsc, and emits a JSON-LD <script>. Unknown slugs trigger 404.
|
|
3
3
|
import {
|
|
4
4
|
renderPage,
|
|
5
|
+
renderPageMetadata,
|
|
5
6
|
getMdxSource as defaultGetMdxSource,
|
|
6
7
|
getMdxSlugs,
|
|
7
8
|
} from "@working-theory/page-renderer";
|
|
9
|
+
import type { Metadata } from "next";
|
|
8
10
|
import { notFound } from "next/navigation";
|
|
9
11
|
import type { ReactElement } from "react";
|
|
10
12
|
|
|
@@ -13,6 +15,13 @@ interface Props {
|
|
|
13
15
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
14
16
|
}
|
|
15
17
|
|
|
18
|
+
export async function generateMetadata({
|
|
19
|
+
params,
|
|
20
|
+
}: Pick<Props, "params">): Promise<Metadata> {
|
|
21
|
+
const { locale, slug } = await params;
|
|
22
|
+
return renderPageMetadata({ type: "Product", slug, locale });
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
export async function generateStaticParams({
|
|
17
26
|
params,
|
|
18
27
|
}: {
|
|
@@ -73,7 +73,7 @@ describe("ProductsPage — snapshot regression", () => {
|
|
|
73
73
|
});
|
|
74
74
|
const { container } = render(element);
|
|
75
75
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
|
76
|
-
`"<main><h1>Products</h1><p data-testid="section-empty-state">Products listing coming soon.</p></main>"`,
|
|
76
|
+
`"<main><h1 class="text-heading-1 font-display font-normal tracking-tighter text-fg hero-type-display">Products</h1><p data-testid="section-empty-state">Products listing coming soon.</p></main>"`,
|
|
77
77
|
);
|
|
78
78
|
});
|
|
79
79
|
|
|
@@ -86,7 +86,7 @@ describe("ProductsPage — snapshot regression", () => {
|
|
|
86
86
|
});
|
|
87
87
|
const { container } = render(element);
|
|
88
88
|
expect(container.innerHTML).toMatchInlineSnapshot(`
|
|
89
|
-
"<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><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) 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) 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="# Products
|
|
89
|
+
"<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><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="# Products
|
|
90
90
|
|
|
91
91
|
Body."></div></article></div></div></main>"
|
|
92
92
|
`);
|
|
@@ -110,7 +110,7 @@ describe("ProductSlugPage — snapshot regression", () => {
|
|
|
110
110
|
});
|
|
111
111
|
const { container } = render(element);
|
|
112
112
|
expect(container.innerHTML).toMatchInlineSnapshot(`
|
|
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><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) 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) 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="# AI Launch Kit
|
|
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><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="# AI Launch Kit
|
|
114
114
|
|
|
115
115
|
Body."></div></article></div></div></main>"
|
|
116
116
|
`);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Products list page. Reads ProductsList MDX via the content adapter and emits
|
|
2
2
|
// a JSON-LD CollectionPage via the dispatcher.
|
|
3
3
|
import type { getMdxSource as defaultGetMdxSource } from "@working-theory/page-renderer";
|
|
4
|
-
import { renderPage } from "@working-theory/page-renderer";
|
|
4
|
+
import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
|
|
5
|
+
import type { Metadata } from "next";
|
|
5
6
|
import type { ReactElement } from "react";
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
@@ -9,6 +10,13 @@ interface Props {
|
|
|
9
10
|
getMdxSource?: typeof defaultGetMdxSource;
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
export async function generateMetadata({
|
|
14
|
+
params,
|
|
15
|
+
}: Pick<Props, "params">): Promise<Metadata> {
|
|
16
|
+
const { locale } = await params;
|
|
17
|
+
return renderPageMetadata({ type: "ProductsList", slug: "products", locale });
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
export default async function Page({
|
|
13
21
|
params,
|
|
14
22
|
getMdxSource,
|
|
@@ -4,7 +4,12 @@ import type {
|
|
|
4
4
|
getMdxSource as defaultGetMdxSource,
|
|
5
5
|
getPageSections as defaultGetPageSections,
|
|
6
6
|
} from "@working-theory/page-renderer";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
getContentAdapter,
|
|
9
|
+
renderPage,
|
|
10
|
+
renderPageMetadata,
|
|
11
|
+
} from "@working-theory/page-renderer";
|
|
12
|
+
import type { Metadata } from "next";
|
|
8
13
|
import type { ReactElement } from "react";
|
|
9
14
|
|
|
10
15
|
interface Props {
|
|
@@ -13,6 +18,13 @@ interface Props {
|
|
|
13
18
|
getPageSections?: typeof defaultGetPageSections;
|
|
14
19
|
}
|
|
15
20
|
|
|
21
|
+
export async function generateMetadata({
|
|
22
|
+
params,
|
|
23
|
+
}: Pick<Props, "params">): Promise<Metadata> {
|
|
24
|
+
const { locale, slug } = await params;
|
|
25
|
+
return renderPageMetadata({ type: "Project", slug, locale });
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
export async function generateStaticParams({
|
|
17
29
|
params,
|
|
18
30
|
}: {
|
|
@@ -20,6 +20,16 @@ import { render } from "@testing-library/react";
|
|
|
20
20
|
|
|
21
21
|
// ── @working-theory/ui mock ─────────────────────────────────────────────────────────────
|
|
22
22
|
jest.mock("@working-theory/ui", () => ({
|
|
23
|
+
// #4446/#4447: renderPage's own fallback/empty-state title now composes
|
|
24
|
+
// Heading (mobile type-budget classes, #4417) instead of a bare <h1>.
|
|
25
|
+
Heading: ({
|
|
26
|
+
children,
|
|
27
|
+
level,
|
|
28
|
+
}: {
|
|
29
|
+
children: React.ReactNode;
|
|
30
|
+
level: number;
|
|
31
|
+
}): React.ReactElement =>
|
|
32
|
+
React.createElement(`h${level}`, null, children),
|
|
23
33
|
SectionRenderer: ({ sections }: { sections: unknown[] }) =>
|
|
24
34
|
React.createElement("div", {
|
|
25
35
|
"data-testid": "section-renderer",
|