create-ailk 0.28.1 → 0.29.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.
@@ -42,6 +42,8 @@ import {
42
42
  import type { Metadata } from "next";
43
43
  import type { ReactElement } from "react";
44
44
 
45
+ import { honourNavOnly } from "../../../lib/hub-children";
46
+
45
47
  // The content type is written as a literal in both renderPage calls below
46
48
  // (not a shared constant): `lib/__tests__/structured-data.test.tsx` and
47
49
  // tools/lint-check-content-renders.ts read `renderPage({ type: "…" })` off
@@ -87,6 +89,8 @@ export default async function Page({
87
89
  const load = getMdxSource ?? loadMdxSource;
88
90
  const type = await resolveType(slug, locale, load);
89
91
  if (type === "BlogList") {
92
+ const source = await load("BlogList", slug, locale);
93
+ honourNavOnly(source?.frontmatter, locale, []);
90
94
  return renderPage({
91
95
  type: "BlogList",
92
96
  slug,
@@ -27,6 +27,8 @@ import type { Metadata } from "next";
27
27
  import { notFound } from "next/navigation";
28
28
  import type { ReactElement } from "react";
29
29
 
30
+ import { honourNavOnly } from "../../../lib/hub-children";
31
+
30
32
  interface Props {
31
33
  params: Promise<{ locale: string }>;
32
34
  getMdxSource?: typeof defaultGetMdxSource;
@@ -60,6 +62,13 @@ export default async function Page({
60
62
  notFound();
61
63
  }
62
64
 
65
+ const slugs = await getMdxSlugs("ComparisonPage", locale);
66
+ honourNavOnly(
67
+ source.frontmatter,
68
+ locale,
69
+ slugs.map((slug) => `/vs/${slug}`),
70
+ );
71
+
63
72
  /**
64
73
  * Append one generated `items` section listing every ComparisonPage spoke
65
74
  * — D9's "the visible list and the CollectionPage's ItemList always
@@ -69,7 +78,6 @@ export default async function Page({
69
78
  */
70
79
  async function withSpokes(frontmatter: unknown): Promise<Section[]> {
71
80
  const declared = await loadSections(frontmatter);
72
- const slugs = await getMdxSlugs("ComparisonPage", locale);
73
81
  if (slugs.length === 0) return declared;
74
82
 
75
83
  const items = await Promise.all(
@@ -25,6 +25,8 @@ import type { Metadata } from "next";
25
25
  import { getTranslations } from "next-intl/server";
26
26
  import type { ReactElement } from "react";
27
27
 
28
+ import { honourNavOnly } from "../../../lib/hub-children";
29
+
28
30
  interface Props {
29
31
  params: Promise<{ locale: string }>;
30
32
  getMdxSource?: typeof defaultGetMdxSource;
@@ -75,6 +77,26 @@ export default async function Page({
75
77
  const loadSections = getPageSections ?? loadPageSections;
76
78
  const t = await getTranslations({ locale, namespace: "careers" });
77
79
 
80
+ const slugs = await listSlugs("JobPosting", locale);
81
+ const postings = await Promise.all(
82
+ slugs.map(async (slug) => {
83
+ const source = await load("JobPosting", slug, locale);
84
+ return { slug, fm: (source?.frontmatter ?? {}) as PostingFields };
85
+ }),
86
+ );
87
+ // Newest `publishedAt` first, then by slug; no date sorts last.
88
+ postings.sort(
89
+ (a, b) =>
90
+ (b.fm.publishedAt ?? "").localeCompare(a.fm.publishedAt ?? "") ||
91
+ a.slug.localeCompare(b.slug),
92
+ );
93
+ const source = await load("CareersList", "careers", locale);
94
+ honourNavOnly(
95
+ source?.frontmatter,
96
+ locale,
97
+ postings.map(({ slug }) => `/careers/${slug}`),
98
+ );
99
+
78
100
  /**
79
101
  * Put one generated `items` section first — the postings are the page's
80
102
  * main content — followed by the declared sections. Absent when there are
@@ -82,21 +104,7 @@ export default async function Page({
82
104
  */
83
105
  async function withPostings(frontmatter: unknown): Promise<Section[]> {
84
106
  const declared = await loadSections(frontmatter);
85
- const slugs = await listSlugs("JobPosting", locale);
86
- if (slugs.length === 0) return declared;
87
-
88
- const postings = await Promise.all(
89
- slugs.map(async (slug) => {
90
- const source = await load("JobPosting", slug, locale);
91
- return { slug, fm: (source?.frontmatter ?? {}) as PostingFields };
92
- }),
93
- );
94
- // Newest `publishedAt` first, then by slug; no date sorts last.
95
- postings.sort(
96
- (a, b) =>
97
- (b.fm.publishedAt ?? "").localeCompare(a.fm.publishedAt ?? "") ||
98
- a.slug.localeCompare(b.slug),
99
- );
107
+ if (postings.length === 0) return declared;
100
108
 
101
109
  const items = postings.map(({ slug, fm }) => {
102
110
  const type =
@@ -3,10 +3,16 @@
3
3
  import type {
4
4
  getMdxSource as defaultGetMdxSource,
5
5
  } from "@working-theory/page-renderer";
6
- import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
6
+ import {
7
+ getMdxSource as loadMdxSource,
8
+ renderPage,
9
+ renderPageMetadata,
10
+ } from "@working-theory/page-renderer";
7
11
  import type { Metadata } from "next";
8
12
  import type { ReactElement } from "react";
9
13
 
14
+ import { honourNavOnly } from "../../../lib/hub-children";
15
+
10
16
  interface Props {
11
17
  params: Promise<{ locale: string }>;
12
18
  getMdxSource?: typeof defaultGetMdxSource;
@@ -24,6 +30,12 @@ export default async function Page({
24
30
  getMdxSource,
25
31
  }: Props): Promise<ReactElement> {
26
32
  const { locale } = await params;
33
+ const source = await (getMdxSource ?? loadMdxSource)(
34
+ "ClientsList",
35
+ "clients",
36
+ locale,
37
+ );
38
+ honourNavOnly(source?.frontmatter, locale, []);
27
39
  return renderPage({
28
40
  type: "ClientsList",
29
41
  slug: "clients",
@@ -5,12 +5,15 @@ import type {
5
5
  getMdxSource as defaultGetMdxSource,
6
6
  getPageSections as defaultGetPageSections} from "@working-theory/page-renderer";
7
7
  import {
8
+ getMdxSource as loadMdxSource,
8
9
  renderPage,
9
10
  renderPageMetadata
10
11
  } from "@working-theory/page-renderer";
11
12
  import type { Metadata } from "next";
12
13
  import type { ReactElement } from "react";
13
14
 
15
+ import { honourNavOnly } from "../../../lib/hub-children";
16
+
14
17
  interface Props {
15
18
  params: Promise<{ locale: string }>;
16
19
  getMdxSource?: typeof defaultGetMdxSource;
@@ -30,6 +33,12 @@ export default async function Page({
30
33
  getPageSections,
31
34
  }: Props): Promise<ReactElement> {
32
35
  const { locale } = await params;
36
+ const source = await (getMdxSource ?? loadMdxSource)(
37
+ "FAQPage",
38
+ "faq",
39
+ locale,
40
+ );
41
+ honourNavOnly(source?.frontmatter, locale, []);
33
42
  return renderPage({
34
43
  type: "FAQPage",
35
44
  slug: "faq",
@@ -13,6 +13,8 @@ import {
13
13
  import type { Metadata } from "next";
14
14
  import type { ReactElement } from "react";
15
15
 
16
+ import { honourNavOnly } from "../../../lib/hub-children";
17
+
16
18
  interface Props {
17
19
  params: Promise<{ locale: string }>;
18
20
  getMdxSource?: typeof defaultGetMdxSource;
@@ -35,6 +37,7 @@ export default async function Page({
35
37
  }: Props): Promise<ReactElement> {
36
38
  const { locale } = await params;
37
39
  const source = await (getMdxSource ?? loadMdxSource)("GlossaryList", "glossary", locale);
40
+ honourNavOnly(source?.frontmatter, locale, []);
38
41
  const composed =
39
42
  source !== null &&
40
43
  Array.isArray((source.frontmatter as { sections?: unknown[] }).sections) &&
@@ -27,6 +27,7 @@ import { notFound } from "next/navigation";
27
27
  import { getTranslations } from "next-intl/server";
28
28
  import type { ReactElement } from "react";
29
29
 
30
+ import { honourNavOnly } from "../../../lib/hub-children";
30
31
  import {
31
32
  insertBeforeClosingCtas,
32
33
  listDetailPages,
@@ -67,6 +68,11 @@ export default async function Page({
67
68
  }
68
69
 
69
70
  const pages = await listDetailPages("TemplatePage", locale, load);
71
+ honourNavOnly(
72
+ source.frontmatter,
73
+ locale,
74
+ pages.map((page) => `/templates/${page.slug}`),
75
+ );
70
76
  const t = await getTranslations({ namespace: "templates", locale });
71
77
  const labels = templateLabels(t);
72
78
 
@@ -3,10 +3,16 @@
3
3
  import type {
4
4
  getMdxSource as defaultGetMdxSource,
5
5
  } from "@working-theory/page-renderer";
6
- import { renderPage, renderPageMetadata } from "@working-theory/page-renderer";
6
+ import {
7
+ getMdxSource as loadMdxSource,
8
+ renderPage,
9
+ renderPageMetadata,
10
+ } from "@working-theory/page-renderer";
7
11
  import type { Metadata } from "next";
8
12
  import type { ReactElement } from "react";
9
13
 
14
+ import { honourNavOnly } from "../../../lib/hub-children";
15
+
10
16
  interface Props {
11
17
  params: Promise<{ locale: string }>;
12
18
  getMdxSource?: typeof defaultGetMdxSource;
@@ -28,6 +34,12 @@ export default async function Page({
28
34
  getMdxSource,
29
35
  }: Props): Promise<ReactElement> {
30
36
  const { locale } = await params;
37
+ const source = await (getMdxSource ?? loadMdxSource)(
38
+ "TestimonialsList",
39
+ "testimonials",
40
+ locale,
41
+ );
42
+ honourNavOnly(source?.frontmatter, locale, []);
31
43
  return renderPage({
32
44
  type: "TestimonialsList",
33
45
  slug: "testimonials",
@@ -30,7 +30,9 @@ import {
30
30
  getMdxSource as loadMdxSource,
31
31
  getPageSections as loadPageSections,
32
32
  } from "@working-theory/page-renderer";
33
+ import { localeHref } from "@working-theory/ui";
33
34
  import { SectionRenderer } from "@working-theory/ui/sections";
35
+ import { notFound, permanentRedirect } from "next/navigation";
34
36
 
35
37
  import {
36
38
  type DetailPage,
@@ -242,6 +244,26 @@ function hasSections(frontmatter: unknown): boolean {
242
244
  return Array.isArray(sections) && sections.length > 0;
243
245
  }
244
246
 
247
+ /**
248
+ * Nav only (#10200): a list that is a heading in the site navigation with no
249
+ * page of its own answers a permanent redirect to its first child — in the
250
+ * route's own child order — and 404 when it lists none. Any other page
251
+ * returns, and renders as before.
252
+ */
253
+ export function honourNavOnly(
254
+ frontmatter: unknown,
255
+ locale: string,
256
+ childHrefs: readonly string[],
257
+ ): void {
258
+ if ((frontmatter as { navOnly?: unknown } | undefined)?.navOnly !== true)
259
+ return;
260
+ const first = childHrefs[0];
261
+ if (first === undefined) notFound();
262
+ permanentRedirect(
263
+ localeHref(locale, first, process.env.NEXT_PUBLIC_DEFAULT_LOCALE ?? "en"),
264
+ );
265
+ }
266
+
245
267
  type RenderPageInput = Parameters<typeof renderPage>[0];
246
268
 
247
269
  /**
@@ -279,25 +301,32 @@ export async function hubPageInput(
279
301
  };
280
302
  }
281
303
 
282
- const declared = await (loaders.getPageSections ?? loadPageSections)(
283
- source.frontmatter,
284
- );
285
- const pages = await listDetailPages(
304
+ const listed = await listDetailPages(
286
305
  config.childType,
287
306
  locale,
288
307
  load,
289
308
  loaders.getMdxSlugs ?? loadMdxSlugs,
290
309
  );
310
+ const pages = config.includeChild
311
+ ? listed.filter(config.includeChild)
312
+ : listed;
313
+ honourNavOnly(
314
+ source.frontmatter,
315
+ locale,
316
+ pages.flatMap((page) => {
317
+ const card = config.card?.(page) ?? defaultCard(page, config);
318
+ return card.link === undefined ? [] : [card.link.href];
319
+ }),
320
+ );
321
+ const declared = await (loaders.getPageSections ?? loadPageSections)(
322
+ source.frontmatter,
323
+ );
291
324
  // A prose hub renders its MDX body, never its declared sections, so its
292
325
  // existing links are read from the body and its cards stand alone after it.
293
326
  const linked = mdxBody
294
327
  ? bodyLinkPaths(source.body)
295
328
  : linkedPaths(declared);
296
- const generated = childCardsSection(
297
- linked,
298
- config.includeChild ? pages.filter(config.includeChild) : pages,
299
- config,
300
- );
329
+ const generated = childCardsSection(linked, pages, config);
301
330
  const placed =
302
331
  generated === null
303
332
  ? declared
@@ -1396,7 +1396,6 @@
1396
1396
  "notFoundHeadline": "Page not found",
1397
1397
  "notFoundDescription": "There is no page at this address. It may have been renamed or removed.",
1398
1398
  "editor": {
1399
- "tabPreview": "Preview",
1400
1399
  "toolChanges": "Show unsaved changes",
1401
1400
  "backLabel": "Pages",
1402
1401
  "statusDraft": "Draft",
@@ -1427,6 +1426,26 @@
1427
1426
  "toolNumberedList": "Numbered list",
1428
1427
  "toolLink": "Link",
1429
1428
  "toolImage": "Insert image",
1429
+ "toolView": "View",
1430
+ "toolRendered": "Rendered — how readers see it",
1431
+ "toolCode": "Markdown — the words with their marks",
1432
+ "toolInsert": "Insert from your library",
1433
+ "insertMeeting": "Add meeting",
1434
+ "insertMagnet": "Add magnet",
1435
+ "insertOffer": "Add offer",
1436
+ "insertAsset": "Add asset",
1437
+ "tips": {
1438
+ "h1": "Your page title is the H1. Use H2 for sections.",
1439
+ "h2": "Types ## before the line → a section heading.",
1440
+ "h3": "Types ### before the line → a smaller heading.",
1441
+ "bold": "Types **word** → bold.",
1442
+ "italic": "Types *word* → italic.",
1443
+ "bulletList": "Types - before each line → a bulleted list.",
1444
+ "numberedList": "Types 1. before each line → a numbered list.",
1445
+ "link": "Types [words](address) → a link.",
1446
+ "insert": "A meeting, magnet, offer or image from your library. AI uses these when it lays out the page.",
1447
+ "changes": "Compare your unsaved edits with the last save."
1448
+ },
1430
1449
  "issueRequired": "{field} is required.",
1431
1450
  "issueInvalid": "{field} isn't a valid choice.",
1432
1451
  "forYou": {
@@ -1455,7 +1474,8 @@
1455
1474
  "imageInsertError": "Could not upload that image. Try again.",
1456
1475
  "diffLegendAdded": "Added",
1457
1476
  "diffLegendRemoved": "Removed",
1458
- "diffLegendUnchanged": "Unchanged"
1477
+ "diffLegendUnchanged": "Unchanged",
1478
+ "tagPlaceholder": "{kind}: {slug}"
1459
1479
  },
1460
1480
  "sections": {
1461
1481
  "heading": "Sections",
package/versions.json CHANGED
@@ -1,66 +1,66 @@
1
1
  {
2
- "@working-theory/admin-ui": "0.50.0",
3
- "@working-theory/aeo": "0.50.0",
4
- "@working-theory/agent-context": "0.50.0",
2
+ "@working-theory/admin-ui": "0.51.0",
3
+ "@working-theory/aeo": "0.51.0",
4
+ "@working-theory/agent-context": "0.51.0",
5
5
  "@working-theory/agent-contract": "0.1.0",
6
6
  "@working-theory/agent-memory": "0.0.0",
7
7
  "@working-theory/ai": "0.1.0",
8
- "@working-theory/analytics": "0.50.0",
9
- "@working-theory/api": "0.50.0",
10
- "@working-theory/api-client": "0.50.0",
8
+ "@working-theory/analytics": "0.51.0",
9
+ "@working-theory/api": "0.51.0",
10
+ "@working-theory/api-client": "0.51.0",
11
11
  "@working-theory/cda-filesystem": "0.0.0",
12
- "@working-theory/cli": "0.50.0",
13
- "@working-theory/common": "0.50.0",
12
+ "@working-theory/cli": "0.51.0",
13
+ "@working-theory/common": "0.51.0",
14
14
  "@working-theory/consent": "0.1.0",
15
- "@working-theory/content-adapters": "0.50.0",
16
- "@working-theory/content-projection": "0.50.0",
17
- "@working-theory/email": "0.50.0",
15
+ "@working-theory/content-adapters": "0.51.0",
16
+ "@working-theory/content-projection": "0.51.0",
17
+ "@working-theory/email": "0.51.0",
18
18
  "@working-theory/env": "0.0.0",
19
- "@working-theory/eslint-config": "0.50.0",
20
- "@working-theory/fumadocs-schema": "0.50.0",
21
- "@working-theory/ids": "0.16.1",
22
- "@working-theory/jsonld": "0.50.0",
23
- "@working-theory/license": "0.50.0",
24
- "@working-theory/mcp-server": "0.50.0",
25
- "@working-theory/metadata": "0.50.0",
26
- "@working-theory/observability": "0.50.0",
27
- "@working-theory/page-renderer": "0.50.0",
19
+ "@working-theory/eslint-config": "0.51.0",
20
+ "@working-theory/fumadocs-schema": "0.51.0",
21
+ "@working-theory/ids": "0.17.0",
22
+ "@working-theory/jsonld": "0.51.0",
23
+ "@working-theory/license": "0.51.0",
24
+ "@working-theory/mcp-server": "0.51.0",
25
+ "@working-theory/metadata": "0.51.0",
26
+ "@working-theory/observability": "0.51.0",
27
+ "@working-theory/page-renderer": "0.51.0",
28
28
  "@working-theory/plugin-mcp": "0.1.3",
29
- "@working-theory/schema": "0.50.0",
30
- "@working-theory/schema-pack-registry": "0.50.0",
31
- "@working-theory/schema-validator": "0.50.0",
32
- "@working-theory/sections-registry": "0.50.0",
33
- "@working-theory/site-config": "0.50.0",
34
- "@working-theory/spoke-checkout": "0.7.6",
35
- "@working-theory/stripe-client": "0.50.0",
36
- "@working-theory/templates": "0.50.0",
37
- "@working-theory/theme": "0.50.0",
38
- "@working-theory/ui": "0.50.0",
39
- "@working-theory/validation": "0.50.0",
40
- "@working-theory/web-i18n": "0.50.0",
41
- "@working-theory/admin": "0.7.1",
42
- "@working-theory/api-commerce": "0.1.4",
43
- "@working-theory/api-content-write": "0.1.4",
44
- "@working-theory/api-hub": "0.3.1",
45
- "@working-theory/api-multi-tenant": "0.1.4",
46
- "@working-theory/api-scheduling": "0.4.1",
47
- "@working-theory/api-signal-collectors": "0.2.3",
48
- "@working-theory/api-workspace": "0.3.0",
29
+ "@working-theory/schema": "0.51.0",
30
+ "@working-theory/schema-pack-registry": "0.51.0",
31
+ "@working-theory/schema-validator": "0.51.0",
32
+ "@working-theory/sections-registry": "0.51.0",
33
+ "@working-theory/site-config": "0.51.0",
34
+ "@working-theory/spoke-checkout": "0.7.7",
35
+ "@working-theory/stripe-client": "0.51.0",
36
+ "@working-theory/templates": "0.51.0",
37
+ "@working-theory/theme": "0.51.0",
38
+ "@working-theory/ui": "0.51.0",
39
+ "@working-theory/validation": "0.51.0",
40
+ "@working-theory/web-i18n": "0.51.0",
41
+ "@working-theory/admin": "0.8.0",
42
+ "@working-theory/api-commerce": "0.1.5",
43
+ "@working-theory/api-content-write": "0.2.0",
44
+ "@working-theory/api-hub": "0.3.2",
45
+ "@working-theory/api-multi-tenant": "0.2.0",
46
+ "@working-theory/api-scheduling": "0.4.2",
47
+ "@working-theory/api-signal-collectors": "0.2.4",
48
+ "@working-theory/api-workspace": "0.3.1",
49
49
  "@working-theory/billing-client": "0.0.0",
50
50
  "@working-theory/branding": "0.0.0",
51
51
  "@working-theory/license-client": "0.0.0",
52
52
  "@working-theory/license-manifest": "0.0.0",
53
- "@working-theory/mcp-scheduling": "0.3.3",
54
- "@working-theory/mcp-signal-collectors": "0.2.4",
55
- "@working-theory/mcp-workspace": "0.3.3",
53
+ "@working-theory/mcp-scheduling": "0.3.4",
54
+ "@working-theory/mcp-signal-collectors": "0.2.5",
55
+ "@working-theory/mcp-workspace": "0.4.0",
56
56
  "@working-theory/partner-session": "0.0.0",
57
57
  "@working-theory/preview-protocol": "0.0.0",
58
- "@working-theory/ui-commerce": "0.23.28",
59
- "@working-theory/ui-docs-blog": "0.23.28",
60
- "@working-theory/ui-scheduling": "0.25.2",
61
- "@working-theory/ui-signal-collectors": "0.27.6",
58
+ "@working-theory/ui-commerce": "0.23.29",
59
+ "@working-theory/ui-docs-blog": "0.23.29",
60
+ "@working-theory/ui-scheduling": "0.25.3",
61
+ "@working-theory/ui-signal-collectors": "0.27.7",
62
62
  "@working-theory/web": "0.0.0",
63
63
  "@working-theory/app-api": "1.0.0",
64
64
  "@working-theory/mcp": "0.0.0",
65
- "@working-theory/database": "0.50.0"
65
+ "@working-theory/database": "0.51.0"
66
66
  }