@publish.os/nextjs 0.0.1-alpha.81 → 0.0.1-alpha.90

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.
Files changed (200) hide show
  1. package/compiler-assets/nextjs/defaults/app/globals.css +1 -0
  2. package/compiler-assets/nextjs/defaults/group/index.tsx +50 -0
  3. package/compiler-assets/nextjs/defaults/group/layout.tsx +7 -3
  4. package/compiler-assets/nextjs/defaults/group/page.tsx +35 -43
  5. package/compiler-assets/nextjs/defaults/project/next.config.mjs +22 -11
  6. package/compiler-assets/nextjs/defaults/project/tsconfig.json.template +1 -4
  7. package/compiler-assets/nextjs/defaults/root/css/animation.css +131 -0
  8. package/compiler-assets/nextjs/defaults/root/css/components/dialog.css +4 -1
  9. package/compiler-assets/nextjs/defaults/root/css/components/menu.css +1 -1
  10. package/compiler-assets/nextjs/defaults/root/css/components/navigation-menu.css +1 -1
  11. package/compiler-assets/nextjs/defaults/root/css/components/popover.css +1 -1
  12. package/compiler-assets/nextjs/defaults/root/css/components/select.css +1 -1
  13. package/compiler-assets/nextjs/defaults/root/css/components/tooltip.css +1 -1
  14. package/compiler-assets/nextjs/defaults/root/css/shadow.css +6 -25
  15. package/compiler-assets/nextjs/defaults/root/css/typography.css +51 -68
  16. package/compiler-assets/nextjs/defaults/root/globals.css +25 -0
  17. package/compiler-assets/nextjs/defaults/root/{page.tsx → index.tsx} +6 -2
  18. package/compiler-assets/nextjs/defaults/root/layout.tsx +7 -1
  19. package/compiler-assets/nextjs/defaults/root/theme.css +22 -22
  20. package/compiler-assets/nextjs/shims/generated-group-page.tsx +19 -2
  21. package/compiler-assets/nextjs/shims/generated-leaf-page.tsx +16 -2
  22. package/compiler-assets/nextjs/shims/global-layout.tsx +11 -43
  23. package/compiler-assets/nextjs/shims/proxy.ts +21 -9
  24. package/compiler-assets/nextjs/shims/publish-os-theme-provider.tsx +76 -15
  25. package/compiler-assets/nextjs/shims/root-page.tsx +7 -1
  26. package/compiler-assets/nextjs/shims/site-fenced-code-block.tsx +34 -0
  27. package/compiler-assets/nextjs/shims/site-heading-level.tsx +14 -0
  28. package/compiler-assets/nextjs/shims/site-mdx-components.tsx +100 -0
  29. package/compiler-assets/nextjs/shims/site-mdx.tsx +12 -0
  30. package/compiler-assets/nextjs/shims/theme-defaults.css +41 -25
  31. package/dist/build-operation-2JAYBSKF.js +83 -0
  32. package/dist/button-CtzUJB8u.d.ts +11 -0
  33. package/dist/{button-CFdx-ohV.d.ts → button-c3WGnxIj.d.ts} +3 -3
  34. package/dist/chunk-535TM4A5.js +1455 -0
  35. package/dist/chunk-726GV2NF.js +2344 -0
  36. package/dist/chunk-7OGTPSKI.js +156 -0
  37. package/dist/chunk-AI2UULNX.js +9 -0
  38. package/dist/chunk-B2V22WRT.js +297 -0
  39. package/dist/chunk-DLDFHU7Y.js +191 -0
  40. package/dist/chunk-DWA3ZRGW.js +142 -0
  41. package/dist/chunk-GGKFQ3MA.js +621 -0
  42. package/dist/chunk-H6XILMZX.js +6154 -0
  43. package/dist/chunk-NECL5IBJ.js +318 -0
  44. package/dist/chunk-SVYGXXYR.js +36 -0
  45. package/dist/chunk-UF6ATARM.js +1100 -0
  46. package/dist/chunk-VO2I7MTM.js +56 -0
  47. package/dist/chunk-WDY4IK6Z.js +128 -0
  48. package/dist/chunk-WKTEO6RS.js +586 -0
  49. package/dist/chunk-YLTOLYWM.js +92 -0
  50. package/dist/compiler.d.ts +41 -7
  51. package/dist/compiler.js +48 -13143
  52. package/dist/compose-NN23UJGC.js +13 -0
  53. package/dist/config/createNextConfig.js +13 -0
  54. package/dist/dev-operation-TZDVJCN7.js +1055 -0
  55. package/dist/framework-selection-X4U6Y6QC.js +10 -0
  56. package/dist/ide-support-ZNW7ODFY.js +15 -0
  57. package/dist/index.d.ts +15 -3
  58. package/dist/kernel/components/blocks/AgentActions.js +71 -61
  59. package/dist/kernel/components/blocks/design-system-preview.d.ts +1 -1
  60. package/dist/kernel/components/blocks/design-system-preview.js +32 -13
  61. package/dist/kernel/components/blocks/link-playground.js +2 -2
  62. package/dist/kernel/components/blocks/logo-block.js +2 -2
  63. package/dist/kernel/components/blocks/nav-breadcrumb.d.ts +10 -2
  64. package/dist/kernel/components/blocks/nav-breadcrumb.js +20 -5
  65. package/dist/kernel/components/blocks/navbar.d.ts +2 -2
  66. package/dist/kernel/components/blocks/navbar.js +401 -60
  67. package/dist/kernel/components/blocks/navigation-progress-playground.js +4 -4
  68. package/dist/kernel/components/blocks/navigation-progress.js +4 -1
  69. package/dist/kernel/components/blocks/next-prev.d.ts +1 -1
  70. package/dist/kernel/components/blocks/next-prev.js +11 -23
  71. package/dist/kernel/components/blocks/page-list.d.ts +1 -1
  72. package/dist/kernel/components/blocks/page-list.js +5 -4
  73. package/dist/kernel/components/blocks/search.d.ts +83 -58
  74. package/dist/kernel/components/blocks/search.js +553 -426
  75. package/dist/kernel/components/blocks/sidebar-node.d.ts +2 -2
  76. package/dist/kernel/components/blocks/sidebar-node.js +11 -9
  77. package/dist/kernel/components/blocks/sidebar.d.ts +3 -3
  78. package/dist/kernel/components/blocks/sidebar.js +18 -11
  79. package/dist/kernel/components/blocks/theme-toggle.d.ts +2 -2
  80. package/dist/kernel/components/blocks/theme-toggle.js +48 -2
  81. package/dist/kernel/components/blocks/toc.js +3 -3
  82. package/dist/kernel/components/mdx/components/blockquote.js +1 -1
  83. package/dist/kernel/components/mdx/components/callout.d.ts +5 -7
  84. package/dist/kernel/components/mdx/components/callout.js +66 -35
  85. package/dist/kernel/components/mdx/components/code-block.d.ts +5 -2
  86. package/dist/kernel/components/mdx/components/code-block.js +106 -5
  87. package/dist/kernel/components/mdx/components/copy-code-button.d.ts +8 -0
  88. package/dist/kernel/components/mdx/components/copy-code-button.js +60 -0
  89. package/dist/kernel/components/mdx/components/figure-caption.js +1 -1
  90. package/dist/kernel/components/mdx/components/figure.js +1 -1
  91. package/dist/kernel/components/mdx/components/heading-copy-link.js +3 -3
  92. package/dist/kernel/components/mdx/components/image-zoom.js +1 -1
  93. package/dist/kernel/components/mdx/components/image.js +77 -56
  94. package/dist/kernel/components/mdx/components/inline-code.js +1 -1
  95. package/dist/kernel/components/mdx/components/kbd.d.ts +2 -2
  96. package/dist/kernel/components/mdx/components/kbd.js +3 -2
  97. package/dist/kernel/components/mdx/components/link.d.ts +35 -4
  98. package/dist/kernel/components/mdx/components/link.js +1 -1
  99. package/dist/kernel/components/mdx/components/paragraph.js +1 -4
  100. package/dist/kernel/components/mdx/components/task-list.js +1 -1
  101. package/dist/kernel/components/mdx/components/video-player.js +2 -2
  102. package/dist/kernel/components/mdx/components/video.js +1 -1
  103. package/dist/kernel/components/mdx/error-boundary.d.ts +1 -1
  104. package/dist/kernel/components/mdx/mdx.d.ts +12 -8
  105. package/dist/kernel/components/mdx/mdx.js +115 -39
  106. package/dist/kernel/components/mdx/rehype-heading-ids.js +1 -1
  107. package/dist/kernel/components/mdx/types.d.ts +1 -1
  108. package/dist/kernel/components/ui/breadcrumb-trail.d.ts +6 -23
  109. package/dist/kernel/components/ui/breadcrumb.d.ts +11 -3
  110. package/dist/kernel/components/ui/breadcrumb.js +6 -4
  111. package/dist/kernel/components/ui/button-group.d.ts +1 -1
  112. package/dist/kernel/components/ui/button-group.js +5 -4
  113. package/dist/kernel/components/ui/button.d.ts +2 -2
  114. package/dist/kernel/components/ui/button.js +6 -6
  115. package/dist/kernel/components/ui/card.d.ts +2 -2
  116. package/dist/kernel/components/ui/drawer.d.ts +3 -1
  117. package/dist/kernel/components/ui/drawer.js +5 -1
  118. package/dist/kernel/components/ui/input.d.ts +4 -4
  119. package/dist/kernel/components/ui/kbd.d.ts +1 -1
  120. package/dist/kernel/components/ui/kbd.js +9 -16
  121. package/dist/kernel/components/ui/link.d.ts +5 -3
  122. package/dist/kernel/components/ui/link.js +10 -9
  123. package/dist/kernel/components/ui/menu.js +5 -5
  124. package/dist/kernel/components/ui/mod-key.d.ts +10 -0
  125. package/dist/kernel/components/ui/mod-key.js +36 -0
  126. package/dist/kernel/components/ui/navigation-menu.js +2 -2
  127. package/dist/kernel/components/ui/progress.js +1 -1
  128. package/dist/kernel/components/ui/scroll-area.d.ts +8 -8
  129. package/dist/kernel/components/ui/scroll-area.js +4 -4
  130. package/dist/kernel/components/ui/select.js +7 -10
  131. package/dist/kernel/components/ui/text.d.ts +2 -2
  132. package/dist/kernel/components/ui/theme-toggle.d.ts +3 -3
  133. package/dist/kernel/components/ui/theme-toggle.js +4 -9
  134. package/dist/kernel/defaults.registry.d.ts +9 -9
  135. package/dist/kernel/defaults.registry.js +9 -9
  136. package/dist/kernel/routes/agent/markdown.d.ts +0 -1
  137. package/dist/kernel/routes/layouts/group.d.ts +1 -1
  138. package/dist/kernel/routes/metadata/feed.d.ts +1 -1
  139. package/dist/kernel/routes/metadata/feed.js +13 -9
  140. package/dist/kernel/routes/metadata/llms.d.ts +2 -1
  141. package/dist/kernel/routes/metadata/llms.js +4 -4
  142. package/dist/kernel/routes/metadata/robots.d.ts +1 -1
  143. package/dist/kernel/routes/metadata/robots.js +2 -2
  144. package/dist/kernel/routes/metadata/sitemap.d.ts +6 -3
  145. package/dist/kernel/routes/metadata/sitemap.js +48 -20
  146. package/dist/kernel/routes/pages/group.d.ts +1 -1
  147. package/dist/kernel/routes/pages/group.js +20 -12
  148. package/dist/kernel/routes/pages/leaf.d.ts +2 -2
  149. package/dist/kernel/routes/pages/leaf.js +20 -12
  150. package/dist/kernel/routes/pages/root.d.ts +2 -2
  151. package/dist/kernel/routes/pages/root.js +18 -12
  152. package/dist/resolvers/manifest-resolver.js +29 -6
  153. package/dist/search.d.ts +6 -7
  154. package/dist/search.js +47 -22
  155. package/dist/{sidebar-types-Bgchdfd6.d.ts → sidebar-types-DKpR27II.d.ts} +1 -1
  156. package/dist/{types-CIwwieNw.d.ts → types-BzkZty3r.d.ts} +23 -3
  157. package/dist/{types-DHBm7-j6.d.ts → types-CJWRG05y.d.ts} +20 -1
  158. package/dist/{types-B_b3C0U6.d.ts → types-CUtrppxt.d.ts} +8 -2
  159. package/dist/types.d.ts +14 -6
  160. package/dist/validate-local-source-XRL2Y7XE.js +14 -0
  161. package/dist/validate-publish-os-source-DAPZ4QGE.js +12 -0
  162. package/init/seed/minimal/.gitignore +1 -0
  163. package/init/seed/minimal/publish.os/assets/favicon/favicon.dark.png +0 -0
  164. package/init/seed/minimal/publish.os/assets/favicon/favicon.png +0 -0
  165. package/init/seed/minimal/publish.os/docs/content/get-started.mdx +6 -10
  166. package/init/seed/minimal/publish.os/docs/content/groups.mdx +1 -1
  167. package/init/seed/minimal/publish.os/docs/content/{theme.mdx → themes.mdx} +14 -12
  168. package/init/seed/minimal/publish.os/publish.os.config.json +3 -7
  169. package/init/seed/minimal/publish.os/theme.css +52 -24
  170. package/init/seed/minimal/publish.os/tsconfig.json +3 -0
  171. package/init/seed/minimal/seed.json +4 -1
  172. package/init/seed/scratch/publish.os/docs/content/get-started.mdx +6 -0
  173. package/init/seed/scratch/publish.os/publish.os.config.json +4 -0
  174. package/init/seed/scratch/seed.json +5 -0
  175. package/package.json +12 -26
  176. package/release/app/phosphor/package.json +3 -3
  177. package/release/app/phosphor/pnpm-lock.yaml.template +523 -360
  178. package/release/app/phosphor/pnpm-workspace.yaml +3 -0
  179. package/release/compiler/package.json +1 -1
  180. package/release/compiler/pnpm-lock.yaml.template +439 -286
  181. package/release/compiler/pnpm-workspace.yaml +3 -0
  182. package/release/release.json +7 -36
  183. package/compiler-assets/nextjs/defaults/group/leaf.tsx +0 -48
  184. package/dist/icons.d.ts +0 -1
  185. package/dist/icons.js +0 -2
  186. package/dist/kernel/components/mdx/components/pre.d.ts +0 -6
  187. package/dist/kernel/components/mdx/components/pre.js +0 -18
  188. package/dist/kernel/components/ui/meta-key.d.ts +0 -17
  189. package/dist/kernel/components/ui/meta-key.js +0 -9
  190. package/dist/kernel/framework-icons.d.ts +0 -1
  191. package/dist/kernel/framework-icons.js +0 -39
  192. package/dist/link-CYaCRDb6.d.ts +0 -35
  193. package/dist/route-metadata-og-image-BeKKFHg7.d.ts +0 -58
  194. package/dist/types-sM2g9nW0.d.ts +0 -12
  195. package/release/app/lucide/package.json +0 -30
  196. package/release/app/lucide/pnpm-lock.yaml.template +0 -2905
  197. package/release/app/lucide/pnpm-workspace.yaml +0 -11
  198. package/release/app/tabler/package.json +0 -30
  199. package/release/app/tabler/pnpm-lock.yaml.template +0 -2911
  200. package/release/app/tabler/pnpm-workspace.yaml +0 -11
@@ -22,6 +22,7 @@
22
22
  --color-focus-stroke: var(--focus-stroke);
23
23
 
24
24
  --color-accent-1: var(--accent-1);
25
+ --color-accent-text-foreground: var(--accent-text-foreground);
25
26
  --color-accent-1-foreground: var(--accent-1-foreground);
26
27
  --color-accent-1-stroke: var(--accent-1-stroke);
27
28
  --color-accent-1-hover: var(--accent-1-hover);
@@ -157,6 +158,23 @@
157
158
  }
158
159
 
159
160
  @layer base {
161
+ /* Keep light-dark() tokens valid when Next.js lowers them for older browsers. */
162
+ :root {
163
+ color-scheme: light;
164
+ }
165
+
166
+ :root,
167
+ .light {
168
+ --logo-block-light-display: block;
169
+ --logo-block-dark-display: none;
170
+ }
171
+
172
+ .dark {
173
+ color-scheme: dark;
174
+ --logo-block-light-display: none;
175
+ --logo-block-dark-display: block;
176
+ }
177
+
160
178
  *,
161
179
  *::before,
162
180
  *::after {
@@ -188,4 +206,11 @@
188
206
  background-color: var(--selection-surface);
189
207
  color: var(--selection-foreground);
190
208
  }
209
+
210
+ @media (forced-colors: active) {
211
+ ::selection {
212
+ background-color: Highlight;
213
+ color: HighlightText;
214
+ }
215
+ }
191
216
  }
@@ -22,9 +22,13 @@ export default function HomePage(props: RootPageProps) {
22
22
  const navNodes = rootNavNodes(tree);
23
23
 
24
24
  return (
25
- <section className="mx-auto grid min-h-[calc(100vh_-_var(--layout-global-nav-height))] w-full max-w-(--layout-site-max-width) grid-cols-1 border-x border-stroke-3 bg-surface-1 md:grid-cols-[var(--layout-sidebar-width)_minmax(0,1fr)_var(--layout-toc-width)] supports-[height:100dvh]:min-h-[calc(100dvh_-_var(--layout-global-nav-height))] supports-[top:anchor-size(--global-navbar_height)]:min-h-[calc(100vh_-_anchor-size(--global-navbar_height,3.5rem))] supports-[top:anchor-size(--global-navbar_height)]:supports-[height:100dvh]:min-h-[calc(100dvh_-_anchor-size(--global-navbar_height,3.5rem))]">
25
+ <section className="mx-auto grid min-h-[calc(100vh-var(--layout-global-nav-height))] w-full max-w-(--layout-site-max-width) grid-cols-1 border-x border-stroke-3 bg-surface-1 md:grid-cols-[var(--layout-sidebar-width)_minmax(0,1fr)_var(--layout-toc-width)] supports-[height:100dvh]:min-h-[calc(100dvh-var(--layout-global-nav-height))] supports-[top:anchor-size(--global-navbar_height)]:min-h-[calc(100vh-anchor-size(--global-navbar_height,3.5rem))] supports-[top:anchor-size(--global-navbar_height)]:supports-[height:100dvh]:min-h-[calc(100dvh-anchor-size(--global-navbar_height,3.5rem))]">
26
26
  <div className="hidden md:block" />
27
- <main className="flex min-w-0 items-start justify-center px-6 pb-16 pt-[clamp(5rem,18vh,10rem)] text-foreground-1 lg:px-10">
27
+ <main
28
+ className="flex min-w-0 scroll-mt-[calc(var(--layout-global-nav-height)+1rem)] items-start justify-center px-6 pb-16 pt-[clamp(5rem,18vh,10rem)] text-foreground-1 lg:px-10"
29
+ id="main-content"
30
+ tabIndex={-1}
31
+ >
28
32
  <div className="w-full max-w-md">
29
33
  <p className="mb-6 text-sm font-semibold text-foreground-1">
30
34
  {site.name}
@@ -12,8 +12,14 @@ export default function DefaultRootLayout(props: RootLayoutProps) {
12
12
 
13
13
  return (
14
14
  <div className="flex min-h-dvh flex-col bg-surface-2">
15
+ <a
16
+ className="sr-only focus:not-sr-only focus:fixed focus:start-4 focus:top-2 focus:z-toast focus:rounded-button focus:bg-surface-1 focus:px-4 focus:py-3 focus:text-sm focus:text-foreground-1 focus:outline-2 focus:outline-focus-stroke"
17
+ href="#main-content"
18
+ >
19
+ Skip to content
20
+ </a>
15
21
  <Navbar logo={faviconsAndLogos.logo} site={site} tree={tree} />
16
- <main className="flex w-full flex-1 flex-col">{children}</main>
22
+ <div className="flex w-full flex-1 flex-col">{children}</div>
17
23
  </div>
18
24
  );
19
25
  }
@@ -3,18 +3,18 @@
3
3
  :root {
4
4
  --theme-radius: var(--theme-radius-md);
5
5
 
6
- --gray-100: #ffffff;
7
- --gray-200: #fafafa;
8
- --gray-300: #f5f5f5;
9
- --gray-400: #eeeeee;
10
- --gray-500: #e5e5e5;
11
- --gray-600: #d4d4d4;
12
- --gray-700: #a3a3a3;
6
+ --gray-100: #fcfcfc;
7
+ --gray-200: #f9f9f9;
8
+ --gray-300: #f3f3f3;
9
+ --gray-400: #f0f0f0;
10
+ --gray-500: #e0e0e0;
11
+ --gray-600: #c6c6c6;
12
+ --gray-700: #999999;
13
13
  --gray-800: #737373;
14
- --gray-900: #525252;
15
- --gray-1000: #404040;
16
- --gray-1100: #262626;
17
- --gray-1200: #000000;
14
+ --gray-900: #5d5d5d;
15
+ --gray-1000: #464646;
16
+ --gray-1100: #2e2e2e;
17
+ --gray-1200: #171717;
18
18
 
19
19
  --neutral-100: var(--gray-100);
20
20
  --neutral-200: var(--gray-200);
@@ -33,18 +33,18 @@
33
33
  }
34
34
 
35
35
  .dark {
36
- --gray-100: #000000;
37
- --gray-200: #0a0a0a;
38
- --gray-300: #171717;
39
- --gray-400: #262626;
40
- --gray-500: #404040;
36
+ --gray-100: #131313;
37
+ --gray-200: #1a1a1a;
38
+ --gray-300: #212121;
39
+ --gray-400: #292929;
40
+ --gray-500: #383838;
41
41
  --gray-600: #525252;
42
- --gray-700: #737373;
43
- --gray-800: #a3a3a3;
44
- --gray-900: #d4d4d4;
45
- --gray-1000: #e5e5e5;
46
- --gray-1100: #f5f5f5;
47
- --gray-1200: #ffffff;
42
+ --gray-700: #767676;
43
+ --gray-800: #b3b3b3;
44
+ --gray-900: #c4c4c4;
45
+ --gray-1000: #d2d2d2;
46
+ --gray-1100: #e6e6e6;
47
+ --gray-1200: #f5f5f5;
48
48
 
49
49
  --accent: var(--gray-1200);
50
50
  }
@@ -1,10 +1,12 @@
1
+ import { PublishOSThemeScope } from "@app/publish-os-theme-provider";
1
2
  import manifest from "@manifest";
2
3
  import {
3
4
  createGroupMetadata,
4
5
  createGroupPage,
5
6
  } from "@publish.os/nextjs/kernel/routes/pages/group";
6
7
  import { ogManifest } from "@source/.publishos/og-manifest.generated";
7
- import IndexPage from "@source/__PUBLISHOS_OWNER_KEY__/page";
8
+ import { groupThemes } from "@source/.theme.generated";
9
+ import IndexPage from "@source/__PUBLISHOS_OWNER_KEY__/index";
8
10
  import type { Metadata } from "next";
9
11
 
10
12
  const route = {
@@ -14,5 +16,20 @@ const route = {
14
16
  IndexPage,
15
17
  } as const;
16
18
 
17
- export default createGroupPage(route);
19
+ const GroupPage = createGroupPage(route);
20
+
21
+ export default function GeneratedGroupPage() {
22
+ return (
23
+ <PublishOSThemeScope group={groupThemes[route.group].group}>
24
+ <GroupPage />
25
+ </PublishOSThemeScope>
26
+ );
27
+ }
28
+
18
29
  export const metadata: Metadata = createGroupMetadata(route);
30
+
31
+ export const ensureStatic = "navigation";
32
+
33
+ // Next 16.4 still misclassifies redirect() during instant validation.
34
+ // Keep its HTTP response; ensureStatic independently validates prerendering.
35
+ export const instant = false;
@@ -1,3 +1,4 @@
1
+ import { PublishOSThemeScope } from "@app/publish-os-theme-provider";
1
2
  import manifest from "@manifest";
2
3
  import {
3
4
  createLeafMetadata,
@@ -5,7 +6,8 @@ import {
5
6
  createLeafStaticParams,
6
7
  } from "@publish.os/nextjs/kernel/routes/pages/leaf";
7
8
  import { ogManifest } from "@source/.publishos/og-manifest.generated";
8
- import Leaf from "@source/__PUBLISHOS_OWNER_KEY__/leaf";
9
+ import { groupThemes } from "@source/.theme.generated";
10
+ import Leaf from "@source/__PUBLISHOS_OWNER_KEY__/page";
9
11
  import { PoweredByFooter } from "@source/_internal/powered-by-footer.generated";
10
12
 
11
13
  const route = {
@@ -16,6 +18,18 @@ const route = {
16
18
  PoweredByFooter,
17
19
  } as const;
18
20
 
19
- export default createLeafPage(route);
21
+ const LeafPage = createLeafPage(route);
22
+
23
+ export default function GeneratedLeafPage(props: {
24
+ params: Promise<{ slug: string[] }>;
25
+ }) {
26
+ return (
27
+ <PublishOSThemeScope group={groupThemes[route.group].group}>
28
+ <LeafPage {...props} />
29
+ </PublishOSThemeScope>
30
+ );
31
+ }
32
+
20
33
  export const generateMetadata = createLeafMetadata(route);
21
34
  export const generateStaticParams = createLeafStaticParams(route);
35
+ export const ensureStatic = "navigation";
@@ -3,7 +3,6 @@ import "@source/.fonts.generated.css";
3
3
  import { Providers } from "@app/providers";
4
4
  import { preloadRootFonts, rootFontClassName } from "@source/.fonts.generated";
5
5
  import { faviconsAndLogos } from "@source/.publishos/favicons-and-logos.generated";
6
- import { ogManifest } from "@source/.publishos/og-manifest.generated";
7
6
  import RootLayout from "@source/layout";
8
7
  import type { Metadata } from "next";
9
8
  import type { ReactNode } from "react";
@@ -14,15 +13,15 @@ const siteDescription =
14
13
  typeof manifest.site.description === "string"
15
14
  ? manifest.site.description
16
15
  : undefined;
17
- const siteUrl =
18
- "url" in manifest.site && typeof manifest.site.url === "string"
19
- ? manifest.site.url
16
+ const siteOrigin =
17
+ "origin" in manifest.site && typeof manifest.site.origin === "string"
18
+ ? manifest.site.origin
20
19
  : undefined;
21
20
  const isDevelopment = process.env.NODE_ENV === "development";
22
21
  const metadataBase = isDevelopment
23
22
  ? new URL(`http://localhost:${process.env.PORT ?? "3000"}`)
24
- : siteUrl
25
- ? new URL(siteUrl)
23
+ : siteOrigin
24
+ ? new URL(siteOrigin)
26
25
  : undefined;
27
26
  type GeneratedMetadataIcons = {
28
27
  icon?: readonly {
@@ -50,39 +49,6 @@ const metadataIcons: Metadata["icons"] | undefined =
50
49
  : {}),
51
50
  }
52
51
  : undefined;
53
- const rootOgKey = ogManifest.routes["/"];
54
- const rootOgImage = rootOgKey ? ogManifest.entries[rootOgKey] : undefined;
55
- const absoluteUrl = (path: string): string => {
56
- if (/^https:\/\//.test(path)) return path;
57
- if (isDevelopment || !siteUrl) return path;
58
- return new URL(path, siteUrl).toString();
59
- };
60
- const rootSocialMetadata: Pick<Metadata, "openGraph" | "twitter"> | undefined =
61
- rootOgImage
62
- ? {
63
- openGraph: {
64
- description: siteDescription,
65
- images: [
66
- {
67
- alt: rootOgImage.alt,
68
- height: rootOgImage.height,
69
- url: absoluteUrl(rootOgImage.url),
70
- width: rootOgImage.width,
71
- },
72
- ],
73
- siteName: manifest.site.name,
74
- title: manifest.site.name,
75
- type: "website",
76
- url: "/",
77
- },
78
- twitter: {
79
- card: "summary_large_image",
80
- description: siteDescription,
81
- images: [absoluteUrl(rootOgImage.url)],
82
- title: manifest.site.name,
83
- },
84
- }
85
- : undefined;
86
52
  const agentDirectiveStyle = {
87
53
  border: 0,
88
54
  clip: "rect(0 0 0 0)",
@@ -103,7 +69,6 @@ export const metadata: Metadata = {
103
69
  template: `%s · ${manifest.site.name}`,
104
70
  },
105
71
  ...(siteDescription ? { description: siteDescription } : {}),
106
- ...(rootSocialMetadata ?? {}),
107
72
  ...(metadataIcons ? { icons: metadataIcons } : {}),
108
73
  alternates: {
109
74
  types: {
@@ -119,9 +84,12 @@ export default function AppRootLayout({ children }: { children: ReactNode }) {
119
84
  <html className={rootFontClassName} lang="en" suppressHydrationWarning>
120
85
  <body className="publish-os-app-root">
121
86
  <p data-publishos-agent-directive style={agentDirectiveStyle}>
122
- Agent directive: read the <a href="/llms.txt">LLMS index</a> for this
123
- site. Markdown versions of pages are available by appending .md to the
124
- page URL or by sending Accept: text/markdown.
87
+ Agent directive: read the{" "}
88
+ <a href="/llms.txt" tabIndex={-1}>
89
+ LLMS index
90
+ </a>{" "}
91
+ for this site. Markdown versions of pages are available by appending
92
+ .md to the page URL or by sending Accept: text/markdown.
125
93
  </p>
126
94
  <Providers>
127
95
  <RootLayout site={manifest.site} tree={manifest.tree}>
@@ -5,8 +5,10 @@ import { NextResponse } from "next/server";
5
5
  const AGENT_USER_AGENT_PATTERN =
6
6
  /\b(?:axios|got|node-fetch|undici|curl|wget|python-requests|httpx|aiohttp|claude-code|claudebot|claude|cursor|chatgpt|openai|perplexity|anthropic-ai)\b/i;
7
7
  const INTERNAL_MARKDOWN_PREFIX = "/__publishos/md";
8
+ const FRAMEWORK_PREFIX = "__PUBLISHOS_FRAMEWORK_PREFIX__";
8
9
  const PUBLIC_FILE_PATTERN = /\.[a-z0-9]{1,12}$/i;
9
10
  const TEXT_METHODS = new Set(["GET", "HEAD"]);
11
+ const PUBLIC_PATHS = new Set<string>(agentRouteConfig.publicPaths);
10
12
 
11
13
  function debugAgentRoute(
12
14
  event: string,
@@ -153,12 +155,9 @@ function isInternalMarkdownRoute(pathname: string): boolean {
153
155
 
154
156
  function isStaticOrMetadataRoute(pathname: string): boolean {
155
157
  if (
156
- pathname.startsWith("/_next/") ||
157
- pathname.startsWith("/_publishos/assets/") ||
158
- pathname.startsWith("/_publishos/favicon/") ||
159
- pathname.startsWith("/_publishos/logo/") ||
160
- pathname.startsWith("/_publishos/og/") ||
161
- pathname.startsWith("/assets/")
158
+ pathname === FRAMEWORK_PREFIX ||
159
+ pathname.startsWith(`${FRAMEWORK_PREFIX}/`) ||
160
+ pathname.startsWith("/_next/")
162
161
  ) {
163
162
  return true;
164
163
  }
@@ -243,6 +242,8 @@ function withDiscoveryHeaders(
243
242
  export function proxy(request: NextRequest) {
244
243
  const { pathname } = request.nextUrl;
245
244
 
245
+ if (pathname === "/_not-found") return NextResponse.next();
246
+
246
247
  if (isInternalMarkdownRoute(pathname)) {
247
248
  if (request.headers.get("x-publishos-agent-route") === "markdown") {
248
249
  return NextResponse.next();
@@ -288,6 +289,19 @@ export function proxy(request: NextRequest) {
288
289
  return rewriteToMarkdown(request, markdownReason);
289
290
  }
290
291
 
292
+ let pagePath: string;
293
+ try {
294
+ pagePath = normalizePagePath(decodeURIComponent(pathname));
295
+ } catch {
296
+ pagePath = pathname;
297
+ }
298
+ if (!PUBLIC_PATHS.has(pagePath)) {
299
+ // Use Next's static 404 before an unknown catch-all reaches error recovery.
300
+ const url = request.nextUrl.clone();
301
+ url.pathname = "/_not-found";
302
+ return NextResponse.rewrite(url, { status: 404 });
303
+ }
304
+
291
305
  if (!agentRouteConfig.enabled || !agentRouteConfig.discoveryHeaders) {
292
306
  debugAgentRoute("pass", {
293
307
  method: request.method,
@@ -311,7 +325,5 @@ export function proxy(request: NextRequest) {
311
325
  }
312
326
 
313
327
  export const config = {
314
- matcher: [
315
- "/((?!_next/static|_next/image|_next/webpack-hmr|_publishos/assets|_publishos/favicon|_publishos/logo|_publishos/og|assets).*)",
316
- ],
328
+ matcher: ["/__PUBLISHOS_MATCHER__"],
317
329
  };
@@ -6,9 +6,9 @@ import {
6
6
  ROOT_FORCED_MODE,
7
7
  routeThemes,
8
8
  } from "@source/.theme.generated";
9
- import { useSelectedLayoutSegments } from "next/navigation";
9
+ import { usePathname } from "next/navigation";
10
10
  import { ThemeProvider as NextThemes } from "next-themes";
11
- import type { ReactNode } from "react";
11
+ import { type ReactNode, Suspense, useLayoutEffect, useState } from "react";
12
12
 
13
13
  type ForcedMode = "dark" | "light" | "none";
14
14
  type RouteTheme = {
@@ -16,24 +16,20 @@ type RouteTheme = {
16
16
  forcedMode?: ForcedMode;
17
17
  };
18
18
 
19
- function selectedPathFromSegments(segments: readonly string[]): string {
20
- const path = segments
21
- .filter((segment) => segment !== "" && !segment.startsWith("("))
22
- .join("/");
23
-
24
- return path ? `/${path}` : "/";
25
- }
26
-
27
19
  function resolveRouteTheme(
28
- segments: readonly string[],
20
+ pathname: string,
21
+ themes: Readonly<Record<string, RouteTheme>>,
29
22
  ): RouteTheme | undefined {
30
- const selectedPath = selectedPathFromSegments(segments);
31
23
  let match: RouteTheme | undefined;
32
24
  let matchLength = 0;
33
25
 
34
- const entries = Object.entries(routeThemes) as Array<[string, RouteTheme]>;
26
+ const entries = Object.entries(themes);
35
27
  for (const [basePath, routeTheme] of entries) {
36
- if (selectedPath === basePath || selectedPath.startsWith(`${basePath}/`)) {
28
+ if (
29
+ basePath === "/" ||
30
+ pathname === basePath ||
31
+ pathname.startsWith(`${basePath}/`)
32
+ ) {
37
33
  if (basePath.length > matchLength) {
38
34
  match = routeTheme;
39
35
  matchLength = basePath.length;
@@ -50,8 +46,57 @@ function effectiveForcedMode(routeTheme: RouteTheme | undefined): ForcedMode {
50
46
  return ROOT_FORCED_MODE ?? "none";
51
47
  }
52
48
 
49
+ function RouteThemeSync({
50
+ onChange,
51
+ }: {
52
+ onChange: (theme: RouteTheme | undefined) => void;
53
+ }) {
54
+ const routeTheme = resolveRouteTheme(usePathname(), routeThemes);
55
+ useLayoutEffect(() => onChange(routeTheme), [onChange, routeTheme]);
56
+ return null;
57
+ }
58
+
59
+ function initialRouteTheme(): RouteTheme | undefined {
60
+ return typeof window === "undefined"
61
+ ? undefined
62
+ : resolveRouteTheme(window.location.pathname, routeThemes);
63
+ }
64
+
65
+ // Both bootstrap and hydration use the same compiler-generated route mapping.
66
+ const initialThemeScript = `{
67
+ const route = (${resolveRouteTheme.toString()})(location.pathname, ${JSON.stringify(routeThemes).replaceAll("<", "\\u003c")});
68
+ document.currentScript.parentElement.setAttribute("data-group", route?.group ?? "");
69
+ const forcedMode = route?.forcedMode ?? ${JSON.stringify(ROOT_FORCED_MODE) ?? "undefined"};
70
+ let mode = ${JSON.stringify(ROOT_DEFAULT_MODE)};
71
+ try { mode = localStorage.getItem("theme") || mode; } catch {}
72
+ if (forcedMode && forcedMode !== "none") mode = forcedMode;
73
+ if (mode === "system") mode = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
74
+ if (mode === "light" || mode === "dark") {
75
+ const root = document.documentElement;
76
+ root.classList.remove("light", "dark");
77
+ root.classList.add(mode);
78
+ root.style.colorScheme = mode;
79
+ }
80
+ }`;
81
+
82
+ export function PublishOSThemeScope({
83
+ children,
84
+ group,
85
+ }: {
86
+ children: ReactNode;
87
+ group: string;
88
+ }) {
89
+ return (
90
+ <ThemeScopeProvider group={group}>
91
+ <div className="contents" data-theme-root="" data-group={group}>
92
+ {children}
93
+ </div>
94
+ </ThemeScopeProvider>
95
+ );
96
+ }
97
+
53
98
  export function PublishOSThemeProvider({ children }: { children: ReactNode }) {
54
- const routeTheme = resolveRouteTheme(useSelectedLayoutSegments());
99
+ const [routeTheme, setRouteTheme] = useState(initialRouteTheme);
55
100
  const forcedMode = effectiveForcedMode(routeTheme);
56
101
  const forcedTheme = forcedMode === "none" ? undefined : forcedMode;
57
102
 
@@ -62,13 +107,29 @@ export function PublishOSThemeProvider({ children }: { children: ReactNode }) {
62
107
  disableTransitionOnChange
63
108
  enableSystem
64
109
  forcedTheme={forcedTheme}
110
+ scriptProps={{
111
+ type: typeof window === "undefined" ? "text/javascript" : "text/plain",
112
+ }}
65
113
  >
66
114
  <ThemeScopeProvider group={routeTheme?.group}>
67
115
  <div
68
116
  className="contents"
117
+ id="publish-os-theme-root"
69
118
  data-theme-root=""
70
119
  data-group={routeTheme?.group ?? ""}
120
+ suppressHydrationWarning
71
121
  >
122
+ <script
123
+ type={
124
+ typeof window === "undefined" ? "text/javascript" : "text/plain"
125
+ }
126
+ suppressHydrationWarning
127
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: Compiler-only route metadata escapes HTML; the script must run before paint.
128
+ dangerouslySetInnerHTML={{ __html: initialThemeScript }}
129
+ />
130
+ <Suspense fallback={null}>
131
+ <RouteThemeSync onChange={setRouteTheme} />
132
+ </Suspense>
72
133
  {children}
73
134
  </div>
74
135
  </ThemeScopeProvider>
@@ -4,10 +4,16 @@ import {
4
4
  createRootPage,
5
5
  } from "@publish.os/nextjs/kernel/routes/pages/root";
6
6
  import { ogManifest } from "@source/.publishos/og-manifest.generated";
7
- import RootPage from "@source/page";
7
+ import RootPage from "@source/index";
8
8
  import type { Metadata } from "next";
9
9
 
10
10
  const route = { manifest, ogManifest, RootPage } as const;
11
11
 
12
12
  export default createRootPage(route);
13
13
  export const metadata: Metadata = createRootMetadata(route);
14
+
15
+ export const ensureStatic = "navigation";
16
+
17
+ // Next 16.4 still misclassifies redirect() during instant validation.
18
+ // Keep its HTTP response; ensureStatic independently validates prerendering.
19
+ export const instant = false;
@@ -0,0 +1,34 @@
1
+ import { CodeBlock } from "@components/mdx/code-block";
2
+ import {
3
+ type ComponentPropsWithoutRef,
4
+ isValidElement,
5
+ type ReactNode,
6
+ } from "react";
7
+
8
+ type CodeChildProps = {
9
+ children?: ReactNode;
10
+ className?: string;
11
+ title?: string;
12
+ };
13
+
14
+ export function FencedCodeBlock({
15
+ children,
16
+ title,
17
+ ...props
18
+ }: ComponentPropsWithoutRef<"pre">) {
19
+ const codeElement = isValidElement<CodeChildProps>(children)
20
+ ? children
21
+ : null;
22
+ const code = codeElement?.props.children ?? children;
23
+ const language = /(?:^|\s)language-([^\s]+)/.exec(
24
+ codeElement?.props.className ?? "",
25
+ )?.[1];
26
+ return (
27
+ <CodeBlock
28
+ {...props}
29
+ code={typeof code === "string" ? code : ""}
30
+ language={language}
31
+ title={title ?? codeElement?.props.title}
32
+ />
33
+ );
34
+ }
@@ -0,0 +1,14 @@
1
+ import type { ComponentType, HTMLAttributes } from "react";
2
+
3
+ type HeadingTag = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
4
+
5
+ export function headingWithLevel(
6
+ Heading: ComponentType<
7
+ { as: HeadingTag } & HTMLAttributes<HTMLHeadingElement>
8
+ >,
9
+ as: HeadingTag,
10
+ ) {
11
+ return function HeadingLevel(props: HTMLAttributes<HTMLHeadingElement>) {
12
+ return <Heading as={as} {...props} />;
13
+ };
14
+ }
@@ -0,0 +1,100 @@
1
+ import type { MDXComponents } from "@publish.os/nextjs/mdx/mdx";
2
+ import {
3
+ Figure as PublishOsFigure,
4
+ Image as PublishOsImage,
5
+ Link as PublishOsLink,
6
+ Video as PublishOsVideo,
7
+ } from "@publish.os/nextjs/mdx/mdx";
8
+ import { mediaManifest } from "@source/.publishos/media-manifest.generated";
9
+ import {
10
+ mediaOverrides,
11
+ sourceMdxComponents,
12
+ } from "@source/_internal/mdx-source-components.generated";
13
+ import type { ComponentProps } from "react";
14
+
15
+ type ImageProps = ComponentProps<typeof PublishOsImage>;
16
+ type LinkProps = ComponentProps<typeof PublishOsLink>;
17
+ type VideoProps = ComponentProps<typeof PublishOsVideo>;
18
+ type ResolvedMediaAsset = {
19
+ aspect?: number;
20
+ height?: number;
21
+ intrinsicHeight?: number;
22
+ intrinsicWidth?: number;
23
+ mediaType: string;
24
+ mimeType?: string;
25
+ optimized?: boolean;
26
+ outputPath: string;
27
+ width?: number;
28
+ };
29
+
30
+ const sourceMedia = mediaOverrides as MDXComponents;
31
+ const LinkComponent = (sourceMedia.link ??
32
+ PublishOsLink) as typeof PublishOsLink;
33
+ const ImageComponent = (sourceMedia.image ??
34
+ PublishOsImage) as typeof PublishOsImage;
35
+ const VideoComponent = (sourceMedia.video ??
36
+ PublishOsVideo) as typeof PublishOsVideo;
37
+ const FigureComponent = (sourceMedia.figure ??
38
+ PublishOsFigure) as typeof PublishOsFigure;
39
+
40
+ function resolveMediaAsset(src: unknown): ResolvedMediaAsset | undefined {
41
+ if (typeof src !== "string" || !src.startsWith("/assets/")) return undefined;
42
+ return mediaManifest.assets[src as keyof typeof mediaManifest.assets] as
43
+ | ResolvedMediaAsset
44
+ | undefined;
45
+ }
46
+
47
+ function Link(props: LinkProps) {
48
+ const asset = resolveMediaAsset(props.href);
49
+ const shouldDownload =
50
+ asset?.mediaType === "document" || asset?.mediaType === "file";
51
+ return (
52
+ <LinkComponent
53
+ {...props}
54
+ download={props.download ?? (shouldDownload ? "" : undefined)}
55
+ href={asset?.outputPath ?? props.href}
56
+ />
57
+ );
58
+ }
59
+
60
+ function Image(props: ImageProps) {
61
+ const asset = resolveMediaAsset(props.src);
62
+ return (
63
+ <ImageComponent
64
+ {...props}
65
+ aspect={props.aspect ?? asset?.aspect}
66
+ height={props.height ?? asset?.height}
67
+ intrinsicHeight={asset?.intrinsicHeight ?? asset?.height}
68
+ intrinsicWidth={asset?.intrinsicWidth ?? asset?.width}
69
+ optimized={asset?.optimized}
70
+ src={asset?.outputPath ?? props.src}
71
+ width={props.width ?? asset?.width}
72
+ />
73
+ );
74
+ }
75
+
76
+ function Video(props: VideoProps) {
77
+ const asset = resolveMediaAsset(props.src);
78
+ const poster = resolveMediaAsset(props.poster);
79
+ return (
80
+ <VideoComponent
81
+ {...props}
82
+ height={props.height ?? asset?.height}
83
+ poster={poster?.outputPath ?? props.poster}
84
+ src={asset?.outputPath ?? props.src}
85
+ width={props.width ?? asset?.width}
86
+ />
87
+ );
88
+ }
89
+
90
+ export const mdxComponents = {
91
+ ...sourceMdxComponents,
92
+ a: Link,
93
+ Link,
94
+ img: Image,
95
+ Image,
96
+ figure: FigureComponent,
97
+ Figure: FigureComponent,
98
+ video: Video,
99
+ Video,
100
+ } satisfies MDXComponents;