@knkcs/anker 1.9.0 → 1.9.2
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/dist/templates/index.d.ts +14 -18
- package/dist/templates/index.js +10 -7
- package/dist/templates/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -30,15 +30,13 @@ interface AppShellProps {
|
|
|
30
30
|
*/
|
|
31
31
|
sidebar: ReactNode;
|
|
32
32
|
/**
|
|
33
|
-
* Context rail element.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* `usePageRail` — when the rail column is disabled, registered content is
|
|
37
|
-
* dropped silently.
|
|
33
|
+
* Context rail element. Acts as a *fallback* for the rail column: it
|
|
34
|
+
* renders when no descendant has registered rail content via
|
|
35
|
+
* `usePageRail`. Registered content always wins over the prop.
|
|
38
36
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
37
|
+
* The rail column is enabled — and a grid track is reserved — when *either*
|
|
38
|
+
* a `rail` prop is supplied *or* a descendant has registered rail content.
|
|
39
|
+
* Omit both (or pass `null`) to drop the rail column entirely.
|
|
42
40
|
*/
|
|
43
41
|
rail?: ReactNode;
|
|
44
42
|
/** Page content. */
|
|
@@ -52,6 +50,11 @@ interface AppShellProps {
|
|
|
52
50
|
* AppShell is layout-only — it does not render a PageHeader, and it does not
|
|
53
51
|
* inject any business chrome. Pages compose `<IndexPageTemplate>`,
|
|
54
52
|
* `<DetailPageTemplate>`, etc. inside `children`.
|
|
53
|
+
*
|
|
54
|
+
* Rail precedence: content registered by a descendant via `usePageRail` wins
|
|
55
|
+
* over the static `rail` prop. The prop is the fallback when no descendant
|
|
56
|
+
* has registered content. Rationale: a descendant explicitly registering
|
|
57
|
+
* content is signaling "show this here", which should trump the static prop.
|
|
55
58
|
*/
|
|
56
59
|
declare function AppShell({ sidebar, rail, children }: AppShellProps): react_jsx_runtime.JSX.Element;
|
|
57
60
|
declare namespace AppShell {
|
|
@@ -111,19 +114,12 @@ interface DetailPageTemplateProps extends Pick<PageHeaderProps, "breadcrumbs" |
|
|
|
111
114
|
tabs?: ReactNode;
|
|
112
115
|
/**
|
|
113
116
|
* Page body — the entity's identity card, tab bodies, edit forms, etc.
|
|
114
|
-
* Rendered
|
|
115
|
-
*
|
|
116
|
-
* passing a `<Box px="0" pt="0">…</Box>` if you need a flush layout.
|
|
117
|
+
* Rendered flush against the canvas. Add internal padding inside
|
|
118
|
+
* `children` if you need it.
|
|
117
119
|
*/
|
|
118
120
|
children: ReactNode;
|
|
119
|
-
/**
|
|
120
|
-
* When `true`, removes the default `px="8" pt="6"` padding around
|
|
121
|
-
* `children`. Use for detail pages whose body is itself a full-bleed
|
|
122
|
-
* surface (e.g. a code editor). @default false
|
|
123
|
-
*/
|
|
124
|
-
flush?: boolean;
|
|
125
121
|
}
|
|
126
|
-
declare function DetailPageTemplate({ breadcrumbs, title, subtitle, eyebrow, actions, tabs, children,
|
|
122
|
+
declare function DetailPageTemplate({ breadcrumbs, title, subtitle, eyebrow, actions, tabs, children, }: DetailPageTemplateProps): react_jsx_runtime.JSX.Element;
|
|
127
123
|
declare namespace DetailPageTemplate {
|
|
128
124
|
var displayName: string;
|
|
129
125
|
}
|
package/dist/templates/index.js
CHANGED
|
@@ -91,10 +91,14 @@ function useRegisteredPageActions() {
|
|
|
91
91
|
}
|
|
92
92
|
function AppShell({ sidebar, rail, children }) {
|
|
93
93
|
const store = useMemo(() => createSlotStore(), []);
|
|
94
|
+
return /* @__PURE__ */ jsx(SlotStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx(AppShellInner, { sidebar, rail, children }) });
|
|
95
|
+
}
|
|
96
|
+
AppShell.displayName = "AppShell";
|
|
97
|
+
function AppShellInner({ sidebar, rail, children }) {
|
|
94
98
|
const railNode = useSlotValue("rail");
|
|
95
|
-
const showRailColumn = rail !== void 0 && rail !== null;
|
|
96
99
|
const renderedRail = railNode ?? rail;
|
|
97
|
-
|
|
100
|
+
const showRailColumn = renderedRail !== void 0 && renderedRail !== null && renderedRail !== false;
|
|
101
|
+
return /* @__PURE__ */ jsxs(
|
|
98
102
|
Grid,
|
|
99
103
|
{
|
|
100
104
|
"data-testid": "app-shell",
|
|
@@ -108,9 +112,9 @@ function AppShell({ sidebar, rail, children }) {
|
|
|
108
112
|
showRailColumn ? /* @__PURE__ */ jsx(Box, { gridColumn: "3", minW: "0", children: renderedRail }) : null
|
|
109
113
|
]
|
|
110
114
|
}
|
|
111
|
-
)
|
|
115
|
+
);
|
|
112
116
|
}
|
|
113
|
-
|
|
117
|
+
AppShellInner.displayName = "AppShellInner";
|
|
114
118
|
function AuthPageTemplate(props) {
|
|
115
119
|
return /* @__PURE__ */ jsx(AuthCard, { ...props });
|
|
116
120
|
}
|
|
@@ -166,8 +170,7 @@ function DetailPageTemplate({
|
|
|
166
170
|
eyebrow,
|
|
167
171
|
actions,
|
|
168
172
|
tabs,
|
|
169
|
-
children
|
|
170
|
-
flush = false
|
|
173
|
+
children
|
|
171
174
|
}) {
|
|
172
175
|
const registered = useRegisteredPageActions();
|
|
173
176
|
const resolvedActions = actions ?? registered;
|
|
@@ -191,7 +194,7 @@ function DetailPageTemplate({
|
|
|
191
194
|
}
|
|
192
195
|
),
|
|
193
196
|
tabs ? /* @__PURE__ */ jsx(Box, { children: tabs }) : null,
|
|
194
|
-
/* @__PURE__ */ jsx(Box, { flex: "1", minH: "0",
|
|
197
|
+
/* @__PURE__ */ jsx(Box, { flex: "1", minH: "0", children })
|
|
195
198
|
]
|
|
196
199
|
}
|
|
197
200
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/templates/app-shell.tsx","../../src/templates/auth-page-template.tsx","../../src/templates/dashboard-page-template.tsx","../../src/templates/detail-page-template.tsx","../../src/templates/error-page.tsx","../../src/templates/index-page-template.tsx","../../src/templates/loading-page.tsx","../../src/templates/maintenance-page.tsx","../../src/templates/marketing-page-template.tsx","../../src/templates/settings-page-template.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["// src/templates/app-shell.tsx\n//\n// AppShell — top-level layout for authenticated knkCMS pages.\n//\n// Composition: a 3-column CSS grid with a fixed-width sidebar, a fluid main\n// content column, and an optional fixed-width context rail.\n//\n// ┌─────────┬───────────────────────────┬─────────┐\n// │ │ │ │\n// │ sidebar │ children │ rail │\n// │ │ (page header / content) │ │\n// │ │ │ │\n// └─────────┴───────────────────────────┴─────────┘\n//\n// Slot mechanism\n// --------------\n// AppShell installs an external slot store on its descendants via context.\n// Two named slots are exposed:\n//\n// - \"actions\" — registered via `usePageActions(node)` — surfaced by page\n// templates inside their <PageHeader actions=…> slot.\n// - \"rail\" — registered via `usePageRail(node)` — surfaced by AppShell\n// as the content of the right rail column.\n//\n// The store uses `useSyncExternalStore` so that producers (deep child\n// components rendered after the consumer) and consumers (AppShell, the page\n// templates) stay decoupled. A naive `useState`-based context would force the\n// consumer to render in the same React commit as the producer, which causes\n// the actions/rail to flicker on route changes (the Path-B fix originally\n// shipped in odon as PR #115). The external-store pattern lets the producer\n// register its content in a `useEffect`, the consumer re-reads on the next\n// browser frame, and React's concurrent renderer is happy.\n\nimport {\n\tcreateContext,\n\ttype ReactNode,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseSyncExternalStore,\n} from \"react\";\nimport { Box, Flex, Grid } from \"../primitives/layout\";\n\ntype SlotName = \"actions\" | \"rail\";\n\ninterface SlotStore {\n\tget(slot: SlotName): ReactNode;\n\tset(slot: SlotName, node: ReactNode): void;\n\tclear(slot: SlotName): void;\n\tsubscribe(slot: SlotName, listener: () => void): () => void;\n}\n\nfunction createSlotStore(): SlotStore {\n\tconst values: Record<SlotName, ReactNode> = {\n\t\tactions: null,\n\t\trail: null,\n\t};\n\tconst listeners: Record<SlotName, Set<() => void>> = {\n\t\tactions: new Set(),\n\t\trail: new Set(),\n\t};\n\n\tfunction notify(slot: SlotName) {\n\t\tfor (const listener of listeners[slot]) listener();\n\t}\n\n\treturn {\n\t\tget(slot) {\n\t\t\treturn values[slot];\n\t\t},\n\t\tset(slot, node) {\n\t\t\tif (values[slot] === node) return;\n\t\t\tvalues[slot] = node;\n\t\t\tnotify(slot);\n\t\t},\n\t\tclear(slot) {\n\t\t\tif (values[slot] === null) return;\n\t\t\tvalues[slot] = null;\n\t\t\tnotify(slot);\n\t\t},\n\t\tsubscribe(slot, listener) {\n\t\t\tlisteners[slot].add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners[slot].delete(listener);\n\t\t\t};\n\t\t},\n\t};\n}\n\nconst SlotStoreContext = createContext<SlotStore | null>(null);\n\n/**\n * Read-side hook used internally by AppShell and the page templates to\n * subscribe to a named slot's currently-registered ReactNode. Returns `null`\n * when no producer has registered content (or when called outside an AppShell).\n */\nfunction useSlotValue(slot: SlotName): ReactNode {\n\tconst store = useContext(SlotStoreContext);\n\tconst subscribe = useCallback(\n\t\t(listener: () => void) => {\n\t\t\tif (!store) return () => undefined;\n\t\t\treturn store.subscribe(slot, listener);\n\t\t},\n\t\t[store, slot],\n\t);\n\tconst getSnapshot = useCallback(() => {\n\t\tif (!store) return null;\n\t\treturn store.get(slot);\n\t}, [store, slot]);\n\treturn useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Register page-action content (typically buttons rendered to the right of\n * the page title) from any descendant of `<AppShell>`. The content is\n * surfaced by the active page template inside its `<PageHeader>` actions\n * slot.\n *\n * Pass `null` (or omit) to clear the registration. The hook is a no-op when\n * called outside an AppShell, which keeps it safe to use in stories and\n * isolated component tests.\n */\nexport function usePageActions(content: ReactNode): void {\n\tconst store = useContext(SlotStoreContext);\n\t// Stash the most recent content in a ref so the effect can run on every\n\t// render without forcing a fresh effect-cleanup cycle for unstable nodes.\n\tconst latest = useRef<ReactNode>(content);\n\tlatest.current = content;\n\tuseEffect(() => {\n\t\tif (!store) return;\n\t\tstore.set(\"actions\", latest.current);\n\t\treturn () => {\n\t\t\tstore.clear(\"actions\");\n\t\t};\n\t}, [store]);\n\t// Re-register on every change of `content` (effect above only runs once\n\t// per mount; this second effect re-pushes the latest value).\n\tuseEffect(() => {\n\t\tif (!store) return;\n\t\tstore.set(\"actions\", content);\n\t}, [store, content]);\n}\n\n/**\n * Register context-rail content from any descendant of `<AppShell>`. The\n * content is rendered in the rail column (assuming the AppShell has a rail\n * slot enabled).\n *\n * Pass `null` (or omit) to clear the registration. The hook is a no-op when\n * called outside an AppShell.\n */\nexport function usePageRail(content: ReactNode): void {\n\tconst store = useContext(SlotStoreContext);\n\tconst latest = useRef<ReactNode>(content);\n\tlatest.current = content;\n\tuseEffect(() => {\n\t\tif (!store) return;\n\t\tstore.set(\"rail\", latest.current);\n\t\treturn () => {\n\t\t\tstore.clear(\"rail\");\n\t\t};\n\t}, [store]);\n\tuseEffect(() => {\n\t\tif (!store) return;\n\t\tstore.set(\"rail\", content);\n\t}, [store, content]);\n}\n\n/**\n * Internal hook used by page templates to read the currently-registered\n * page-actions ReactNode. Page templates fall back to a locally-passed\n * `actions` prop when nothing is registered.\n */\nexport function useRegisteredPageActions(): ReactNode {\n\treturn useSlotValue(\"actions\");\n}\n\nexport interface AppShellProps {\n\t/**\n\t * Sidebar element — the navigation column. Required. Typically an instance\n\t * of `<Sidebar>` from `@knkcs/anker/components`. AppShell does not own the\n\t * sidebar's collapsed-state — pass it through the Sidebar's own props.\n\t */\n\tsidebar: ReactNode;\n\t/**\n\t * Context rail element. Pass an instance of `<ContextRail>` to enable the\n\t * rail column. Pass `null` (or omit) to disable the column entirely (no\n\t * grid track is reserved). Pages can still register rail content via\n\t * `usePageRail` — when the rail column is disabled, registered content is\n\t * dropped silently.\n\t *\n\t * Tip: pass an _empty_ `<ContextRail>` if you want the column to exist\n\t * (so child pages can populate it via `usePageRail`) without rendering\n\t * any default content.\n\t */\n\trail?: ReactNode;\n\t/** Page content. */\n\tchildren: ReactNode;\n}\n\n/**\n * AppShell is the top-level layout for authenticated knkCMS pages. It owns\n * the slot context that powers `usePageActions` and `usePageRail`, and\n * arranges sidebar / main / rail in a 3-column CSS grid.\n *\n * AppShell is layout-only — it does not render a PageHeader, and it does not\n * inject any business chrome. Pages compose `<IndexPageTemplate>`,\n * `<DetailPageTemplate>`, etc. inside `children`.\n */\nexport function AppShell({ sidebar, rail, children }: AppShellProps) {\n\tconst store = useMemo(() => createSlotStore(), []);\n\tconst railNode = useSlotValue(\"rail\");\n\n\t// The rail column is enabled when the consumer passed a `rail` element.\n\t// We use the consumer-supplied element as a *container* for the\n\t// dynamically-registered rail content: anything registered via\n\t// `usePageRail` is appended into that container at render time. This\n\t// keeps the framing chrome (collapse toggle, scrolling, borders) under\n\t// the consumer's control while letting pages contribute body content.\n\t//\n\t// Implementation note: we render `rail` as-is and tee its `children`\n\t// across (a) whatever the consumer passed and (b) whatever a page\n\t// registered via `usePageRail`. The simplest, escape-hatch-friendly\n\t// behavior is: if a page has registered rail content, render that;\n\t// otherwise render the consumer's `rail` as-is. Consumers that want\n\t// \"default + page-specific\" content can wire that themselves.\n\tconst showRailColumn = rail !== undefined && rail !== null;\n\tconst renderedRail = railNode ?? rail;\n\n\treturn (\n\t\t<SlotStoreContext.Provider value={store}>\n\t\t\t<Grid\n\t\t\t\tdata-testid=\"app-shell\"\n\t\t\t\tdata-rail={showRailColumn ? \"true\" : \"false\"}\n\t\t\t\ttemplateColumns={showRailColumn ? \"auto 1fr auto\" : \"auto 1fr\"}\n\t\t\t\tminH=\"100vh\"\n\t\t\t\tbg=\"bg-canvas\"\n\t\t\t>\n\t\t\t\t<Box gridColumn=\"1\" minW=\"0\">\n\t\t\t\t\t{sidebar}\n\t\t\t\t</Box>\n\t\t\t\t<Flex gridColumn=\"2\" direction=\"column\" minW=\"0\" minH=\"100vh\">\n\t\t\t\t\t{children}\n\t\t\t\t</Flex>\n\t\t\t\t{showRailColumn ? (\n\t\t\t\t\t<Box gridColumn=\"3\" minW=\"0\">\n\t\t\t\t\t\t{renderedRail}\n\t\t\t\t\t</Box>\n\t\t\t\t) : null}\n\t\t\t</Grid>\n\t\t</SlotStoreContext.Provider>\n\t);\n}\nAppShell.displayName = \"AppShell\";\n","// src/templates/auth-page-template.tsx\n//\n// AuthPageTemplate — full-bleed centered card on a neutral canvas. No app\n// shell, no sidebar, no rail. Used for login, register, MFA challenge,\n// forgot/reset password, and email-verification screens.\n//\n// Internally this is a thin wrapper around `<AuthCard>` (in\n// `@knkcs/anker/components`) — surfaced under the templates module so\n// consumers can import their layout chrome from one entry point.\n//\n// We re-expose the wrapper rather than re-implementing it because AuthCard\n// is already battle-tested and stable; the templates module is the consumer\n// contract, AuthCard is the implementation.\n\nimport type { ReactNode } from \"react\";\nimport { AuthCard, type AuthCardProps } from \"../components/auth-card\";\n\nexport interface AuthPageTemplateProps extends AuthCardProps {\n\t/**\n\t * Page body — typically a form or a verification CTA. Inherits all\n\t * AuthCard slots (logo, topBarRight, eyebrow, title, subtitle, footer).\n\t */\n\tchildren: ReactNode;\n}\n\n/**\n * Auth-flow page layout. Use for any pre-authentication screen (login,\n * register, MFA, forgot-password, reset-password) and for short\n * confirmation flows (email-verify, account-deleted). For consent\n * dialogs, prefer `<OAuthConsentPageTemplate>` (denser layout).\n *\n * The template supplies a centered card with a dot-grid background, an\n * optional topbar with logo + locale switcher, and a footer slot. To hide\n * the topbar (rare — embedded preview, printable receipts) pass\n * `hideTopBar`. To hide the dot-grid (printable) pass `hideBackground`.\n */\nexport function AuthPageTemplate(props: AuthPageTemplateProps) {\n\treturn <AuthCard {...props} />;\n}\nAuthPageTemplate.displayName = \"AuthPageTemplate\";\n","// src/templates/dashboard-page-template.tsx\n//\n// DashboardPageTemplate — for at-a-glance overview pages composed of a grid\n// of widgets (Stat cards, mini-charts, recent-activity panes, etc.).\n//\n// ┌─────────────────────────────────────────┐\n// │ PageHeader (greeting · range picker) │\n// ├─────────────────────────────────────────┤\n// │ ┌──────┬──────┬──────┬──────┐ │\n// │ │ stat │ stat │ stat │ stat │ │\n// │ ├──────┴──────┼──────┴──────┤ │\n// │ │ widget │ widget │ │\n// │ │ │ │ │\n// │ ├─────────────┴─────────────┤ │\n// │ │ wide widget │ │\n// │ └───────────────────────────┘ │\n// └─────────────────────────────────────────┘\n//\n// The template provides the page chrome and a 12-column responsive grid.\n// Widgets opt into the column count via Chakra's `gridColumn` prop on each\n// child. Inspired by Linear's \"All issues\" overview and GitHub's repo\n// insights — calm surfaces, dense info, clear hierarchy.\n\nimport type { ReactNode } from \"react\";\nimport { PageHeader, type PageHeaderProps } from \"../components/page-header\";\nimport { Box, Flex, Grid } from \"../primitives/layout\";\nimport { useRegisteredPageActions } from \"./app-shell\";\n\nexport interface DashboardPageTemplateProps\n\textends Pick<\n\t\tPageHeaderProps,\n\t\t\"breadcrumbs\" | \"title\" | \"subtitle\" | \"eyebrow\"\n\t> {\n\tactions?: ReactNode;\n\t/**\n\t * Dashboard widgets. Children are placed inside a 12-column responsive\n\t * CSS grid with `gap=\"4\"`. Each child should set `gridColumn` (e.g.\n\t * `\"span 3\"` for a quarter-width tile, `\"span 6\"` for half, `\"span 12\"`\n\t * for full-width). The default per-child column span is `\"span 12\"`.\n\t */\n\tchildren: ReactNode;\n\t/**\n\t * Override the grid `gap` between widgets. @default \"4\" (= 16px)\n\t */\n\tgap?: string;\n}\n\nexport function DashboardPageTemplate({\n\tbreadcrumbs,\n\ttitle,\n\tsubtitle,\n\teyebrow,\n\tactions,\n\tchildren,\n\tgap = \"4\",\n}: DashboardPageTemplateProps) {\n\tconst registered = useRegisteredPageActions();\n\tconst resolvedActions = actions ?? registered;\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"dashboard-page-template\"\n\t\t\tdirection=\"column\"\n\t\t\tflex=\"1\"\n\t\t\tminH=\"0\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t<PageHeader\n\t\t\t\tbreadcrumbs={breadcrumbs}\n\t\t\t\ttitle={title}\n\t\t\t\tsubtitle={subtitle}\n\t\t\t\teyebrow={eyebrow}\n\t\t\t\tactions={resolvedActions}\n\t\t\t/>\n\t\t\t<Box flex=\"1\" minH=\"0\" px=\"8\" py=\"6\">\n\t\t\t\t<Grid\n\t\t\t\t\tdata-testid=\"dashboard-grid\"\n\t\t\t\t\ttemplateColumns=\"repeat(12, minmax(0, 1fr))\"\n\t\t\t\t\tgap={gap}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</Grid>\n\t\t\t</Box>\n\t\t</Flex>\n\t);\n}\nDashboardPageTemplate.displayName = \"DashboardPageTemplate\";\n","// src/templates/detail-page-template.tsx\n//\n// DetailPageTemplate — the canonical \"single entity\" page layout.\n//\n// Composition:\n//\n// ┌─────────────────────────────────────────┐\n// │ PageHeader (breadcrumbs · title · …) │\n// ├─────────────────────────────────────────┤\n// │ Tabs (optional — pinned beneath header) │\n// ├─────────────────────────────────────────┤\n// │ children (identity card · panes · …) │\n// └─────────────────────────────────────────┘\n//\n// Use for pages that show one entity: a single user, a single OAuth client,\n// a single audit-event detail. Detail pages often have an identity Card at\n// the top followed by tabbed sections — DetailPageTemplate is intentionally\n// thin so consumers can compose those freely under `children`.\n\nimport type { ReactNode } from \"react\";\nimport { PageHeader, type PageHeaderProps } from \"../components/page-header\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { useRegisteredPageActions } from \"./app-shell\";\n\nexport interface DetailPageTemplateProps\n\textends Pick<\n\t\tPageHeaderProps,\n\t\t\"breadcrumbs\" | \"title\" | \"subtitle\" | \"eyebrow\"\n\t> {\n\tactions?: ReactNode;\n\t/**\n\t * Tab strip rendered immediately under the PageHeader. Typically a\n\t * `<Tabs.Root>` containing a `<Tabs.List>` and one `<Tabs.Content>` per\n\t * pane. The template does not render `<Tabs.Content>` separately —\n\t * consumers control the tab body composition entirely.\n\t */\n\ttabs?: ReactNode;\n\t/**\n\t * Page body — the entity's identity card, tab bodies, edit forms, etc.\n\t * Rendered with horizontal padding (`px=\"8\"`) and top padding (`pt=\"6\"`)\n\t * so cards inside don't sit flush against the page edges. Override by\n\t * passing a `<Box px=\"0\" pt=\"0\">…</Box>` if you need a flush layout.\n\t */\n\tchildren: ReactNode;\n\t/**\n\t * When `true`, removes the default `px=\"8\" pt=\"6\"` padding around\n\t * `children`. Use for detail pages whose body is itself a full-bleed\n\t * surface (e.g. a code editor). @default false\n\t */\n\tflush?: boolean;\n}\n\nexport function DetailPageTemplate({\n\tbreadcrumbs,\n\ttitle,\n\tsubtitle,\n\teyebrow,\n\tactions,\n\ttabs,\n\tchildren,\n\tflush = false,\n}: DetailPageTemplateProps) {\n\tconst registered = useRegisteredPageActions();\n\tconst resolvedActions = actions ?? registered;\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"detail-page-template\"\n\t\t\tdirection=\"column\"\n\t\t\tflex=\"1\"\n\t\t\tminH=\"0\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t<PageHeader\n\t\t\t\tbreadcrumbs={breadcrumbs}\n\t\t\t\ttitle={title}\n\t\t\t\tsubtitle={subtitle}\n\t\t\t\teyebrow={eyebrow}\n\t\t\t\tactions={resolvedActions}\n\t\t\t/>\n\t\t\t{tabs ? <Box>{tabs}</Box> : null}\n\t\t\t<Box flex=\"1\" minH=\"0\" px={flush ? \"0\" : \"8\"} pt={flush ? \"0\" : \"6\"}>\n\t\t\t\t{children}\n\t\t\t</Box>\n\t\t</Flex>\n\t);\n}\nDetailPageTemplate.displayName = \"DetailPageTemplate\";\n","// src/templates/error-page.tsx\n//\n// ErrorPage — full-bleed error layout for 404 / 403 / 500 / generic\n// failures. Centered status code + message + suggested next step. No app\n// shell, no sidebar.\n//\n// We keep it deliberately spare — an enterprise B2B error screen does not\n// need a 404-illustration of an astronaut. The optional `illustration`\n// slot is there for the rare case where a product wants to reach for one\n// (e.g. a dedicated brand \"OOPS\" SVG).\n\nimport type { ReactNode } from \"react\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { Heading, Text } from \"../primitives/typography\";\n\nexport interface ErrorPageProps {\n\t/**\n\t * Status code (404, 500, 403, …) shown as the page's largest piece of\n\t * type. Pass any string — non-numeric codes like \"OOPS\" work too.\n\t */\n\tstatusCode: ReactNode;\n\t/** Short headline — e.g. \"Page not found\" or \"Something went wrong\". */\n\ttitle: ReactNode;\n\t/**\n\t * One-paragraph explanation. Keep it short — the user is already in a\n\t * frustrated state.\n\t */\n\tdescription?: ReactNode;\n\t/**\n\t * Action(s) — typically one solid button (back home / retry) plus an\n\t * optional secondary link.\n\t */\n\tactions?: ReactNode;\n\t/**\n\t * Optional illustration rendered above the status code. Use sparingly.\n\t */\n\tillustration?: ReactNode;\n\t/**\n\t * Logo or wordmark rendered top-left. Useful for branded error pages\n\t * served from the root domain.\n\t */\n\tlogo?: ReactNode;\n}\n\nexport function ErrorPage({\n\tstatusCode,\n\ttitle,\n\tdescription,\n\tactions,\n\tillustration,\n\tlogo,\n}: ErrorPageProps) {\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"error-page\"\n\t\t\tdirection=\"column\"\n\t\t\tminH=\"100vh\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t{logo && (\n\t\t\t\t<Box px=\"8\" py=\"6\">\n\t\t\t\t\t{logo}\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Flex\n\t\t\t\tflex=\"1\"\n\t\t\t\tdirection=\"column\"\n\t\t\t\talign=\"center\"\n\t\t\t\tjustify=\"center\"\n\t\t\t\tpx=\"8\"\n\t\t\t\tpb=\"16\"\n\t\t\t\ttextAlign=\"center\"\n\t\t\t>\n\t\t\t\t{illustration && <Box mb=\"8\">{illustration}</Box>}\n\t\t\t\t<Text\n\t\t\t\t\tfontSize=\"7xl\"\n\t\t\t\t\tfontWeight=\"semibold\"\n\t\t\t\t\tcolor=\"muted\"\n\t\t\t\t\tletterSpacing=\"-0.04em\"\n\t\t\t\t\tlineHeight=\"1\"\n\t\t\t\t\tmb=\"4\"\n\t\t\t\t>\n\t\t\t\t\t{statusCode}\n\t\t\t\t</Text>\n\t\t\t\t<Heading\n\t\t\t\t\tas=\"h1\"\n\t\t\t\t\tfontSize=\"2xl\"\n\t\t\t\t\tfontWeight=\"semibold\"\n\t\t\t\t\tcolor=\"default\"\n\t\t\t\t\tletterSpacing=\"-0.02em\"\n\t\t\t\t\tmb=\"3\"\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</Heading>\n\t\t\t\t{description && (\n\t\t\t\t\t<Text fontSize=\"md\" color=\"muted\" lineHeight=\"1.6\" maxW=\"lg\" mb=\"8\">\n\t\t\t\t\t\t{description}\n\t\t\t\t\t</Text>\n\t\t\t\t)}\n\t\t\t\t{actions && (\n\t\t\t\t\t<Flex gap=\"3\" align=\"center\" justify=\"center\">\n\t\t\t\t\t\t{actions}\n\t\t\t\t\t</Flex>\n\t\t\t\t)}\n\t\t\t</Flex>\n\t\t</Flex>\n\t);\n}\nErrorPage.displayName = \"ErrorPage\";\n","// src/templates/index-page-template.tsx\n//\n// IndexPageTemplate — the canonical \"list of items\" page layout.\n//\n// Composition (top to bottom):\n//\n// ┌─────────────────────────────────────────┐\n// │ PageHeader (breadcrumbs · title · …) │\n// ├─────────────────────────────────────────┤\n// │ Tabs (optional — under header) │\n// ├─────────────────────────────────────────┤\n// │ Toolbar (search · filters · count) │\n// ├─────────────────────────────────────────┤\n// │ children (DataTable, list, empty state) │\n// └─────────────────────────────────────────┘\n//\n// Use this template for any \"browse a list\" page: users index, OAuth-clients\n// index, audit-log, etc. The template flushes its content (no horizontal\n// padding) so a full-bleed DataTable sits cleanly under the toolbar. Pages\n// that need padded content should wrap children in a `<Box px=\"8\" py=\"6\">`.\n\nimport type { ReactNode } from \"react\";\nimport { PageHeader, type PageHeaderProps } from \"../components/page-header\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { useRegisteredPageActions } from \"./app-shell\";\n\nexport interface IndexPageTemplateProps\n\textends Pick<\n\t\tPageHeaderProps,\n\t\t\"breadcrumbs\" | \"title\" | \"subtitle\" | \"eyebrow\"\n\t> {\n\t/**\n\t * Optional explicit page-action content (rendered inside the PageHeader's\n\t * actions slot). When omitted, the template falls back to whatever a\n\t * descendant has registered via `usePageActions`.\n\t */\n\tactions?: ReactNode;\n\t/**\n\t * Optional tab strip rendered between the PageHeader and the toolbar.\n\t * Pass an instance of `<Tabs.Root>` (with its own `<Tabs.List>` and\n\t * `<Tabs.Content>`s). When omitted, no tab strip is rendered.\n\t */\n\ttabs?: ReactNode;\n\t/**\n\t * Toolbar element rendered between the tabs (if any) and the page body.\n\t * Typically an instance of `<Toolbar>` from `@knkcs/anker/components`.\n\t * Pass `null` to omit the toolbar entirely (rare).\n\t */\n\ttoolbar?: ReactNode;\n\t/**\n\t * Page body — DataTable, list, empty state, or error/loading content.\n\t * Rendered flush against the canvas. Add internal padding inside\n\t * `children` if you need it.\n\t */\n\tchildren: ReactNode;\n}\n\n/**\n * Canonical list-page layout. Renders PageHeader → optional Tabs → optional\n * Toolbar → children, full-bleed against the canvas.\n *\n * Page actions are sourced from (in priority order): `actions` prop →\n * registered slot via `usePageActions`. This lets a tab-pane component deep\n * inside `children` register its own actions without prop-drilling.\n */\nexport function IndexPageTemplate({\n\tbreadcrumbs,\n\ttitle,\n\tsubtitle,\n\teyebrow,\n\tactions,\n\ttabs,\n\ttoolbar,\n\tchildren,\n}: IndexPageTemplateProps) {\n\tconst registered = useRegisteredPageActions();\n\tconst resolvedActions = actions ?? registered;\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"index-page-template\"\n\t\t\tdirection=\"column\"\n\t\t\tflex=\"1\"\n\t\t\tminH=\"0\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t<PageHeader\n\t\t\t\tbreadcrumbs={breadcrumbs}\n\t\t\t\ttitle={title}\n\t\t\t\tsubtitle={subtitle}\n\t\t\t\teyebrow={eyebrow}\n\t\t\t\tactions={resolvedActions}\n\t\t\t/>\n\t\t\t{tabs ? <Box>{tabs}</Box> : null}\n\t\t\t{toolbar ? <Box>{toolbar}</Box> : null}\n\t\t\t<Box flex=\"1\" minH=\"0\">\n\t\t\t\t{children}\n\t\t\t</Box>\n\t\t</Flex>\n\t);\n}\nIndexPageTemplate.displayName = \"IndexPageTemplate\";\n","// src/templates/loading-page.tsx\n//\n// LoadingPage — full-bleed initial-boot loading screen. Used while the\n// authenticated app shell is being hydrated (auth check, theme loading,\n// initial data fetch). For in-page loading states (e.g. tab switch),\n// prefer a centered <Spinner /> inside the page body — this template is\n// only for the very first frame of the app.\n\nimport type { ReactNode } from \"react\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { Spinner } from \"../primitives/spinner\";\nimport { Text } from \"../primitives/typography\";\n\nexport interface LoadingPageProps {\n\t/** Optional logo rendered above the spinner. */\n\tlogo?: ReactNode;\n\t/**\n\t * Optional message rendered below the spinner. Keep it short\n\t * (e.g. \"Loading your workspace…\").\n\t */\n\tmessage?: ReactNode;\n}\n\nexport function LoadingPage({ logo, message }: LoadingPageProps) {\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"loading-page\"\n\t\t\tdirection=\"column\"\n\t\t\talign=\"center\"\n\t\t\tjustify=\"center\"\n\t\t\tminH=\"100vh\"\n\t\t\tbg=\"bg-canvas\"\n\t\t\tgap=\"6\"\n\t\t\ttextAlign=\"center\"\n\t\t>\n\t\t\t{logo && <Box>{logo}</Box>}\n\t\t\t<Spinner size=\"lg\" color=\"accent\" />\n\t\t\t{message && (\n\t\t\t\t<Text fontSize=\"sm\" color=\"muted\">\n\t\t\t\t\t{message}\n\t\t\t\t</Text>\n\t\t\t)}\n\t\t</Flex>\n\t);\n}\nLoadingPage.displayName = \"LoadingPage\";\n","// src/templates/maintenance-page.tsx\n//\n// MaintenancePage — full-bleed maintenance/down-for-upgrade screen. No app\n// shell. Surfaces a clear message, an optional ETA, and an optional link\n// to a status page. Operators serve this from a static asset or a\n// fallback handler when the app is offline.\n\nimport type { ReactNode } from \"react\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { Heading, Text } from \"../primitives/typography\";\n\nexport interface MaintenancePageProps {\n\t/** Logo rendered top-left. */\n\tlogo?: ReactNode;\n\t/** Headline — e.g. \"We'll be right back\". */\n\ttitle?: ReactNode;\n\t/**\n\t * One-paragraph message explaining what's happening and what users\n\t * should do (typically \"wait, we'll be back shortly\").\n\t */\n\tdescription?: ReactNode;\n\t/**\n\t * Estimated-time-of-restoration banner. Pass a string like\n\t * \"Estimated back online: 14:30 UTC\". Rendered below the description.\n\t */\n\teta?: ReactNode;\n\t/**\n\t * Optional link to a status page or twitter status. Pass a fully-\n\t * styled `<Link>` or an `<a>` element.\n\t */\n\tstatusLink?: ReactNode;\n}\n\nexport function MaintenancePage({\n\tlogo,\n\ttitle = \"We'll be right back\",\n\tdescription = \"We're upgrading the service. Please refresh in a few minutes.\",\n\teta,\n\tstatusLink,\n}: MaintenancePageProps) {\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"maintenance-page\"\n\t\t\tdirection=\"column\"\n\t\t\tminH=\"100vh\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t{logo && (\n\t\t\t\t<Box px=\"8\" py=\"6\">\n\t\t\t\t\t{logo}\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Flex\n\t\t\t\tflex=\"1\"\n\t\t\t\tdirection=\"column\"\n\t\t\t\talign=\"center\"\n\t\t\t\tjustify=\"center\"\n\t\t\t\tpx=\"8\"\n\t\t\t\tpb=\"16\"\n\t\t\t\ttextAlign=\"center\"\n\t\t\t>\n\t\t\t\t<Heading\n\t\t\t\t\tas=\"h1\"\n\t\t\t\t\tfontSize=\"3xl\"\n\t\t\t\t\tfontWeight=\"semibold\"\n\t\t\t\t\tcolor=\"default\"\n\t\t\t\t\tletterSpacing=\"-0.02em\"\n\t\t\t\t\tmb=\"4\"\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</Heading>\n\t\t\t\t{description && (\n\t\t\t\t\t<Text fontSize=\"md\" color=\"muted\" lineHeight=\"1.6\" maxW=\"lg\" mb=\"6\">\n\t\t\t\t\t\t{description}\n\t\t\t\t\t</Text>\n\t\t\t\t)}\n\t\t\t\t{eta && (\n\t\t\t\t\t<Box\n\t\t\t\t\t\tpx=\"4\"\n\t\t\t\t\t\tpy=\"2\"\n\t\t\t\t\t\tborderWidth=\"1px\"\n\t\t\t\t\t\tborderColor=\"border\"\n\t\t\t\t\t\tborderRadius=\"md\"\n\t\t\t\t\t\tbg=\"bg-surface\"\n\t\t\t\t\t\tmb=\"6\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Text fontSize=\"sm\" fontWeight=\"medium\" color=\"emphasized\">\n\t\t\t\t\t\t\t{eta}\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t)}\n\t\t\t\t{statusLink && <Box>{statusLink}</Box>}\n\t\t\t</Flex>\n\t\t</Flex>\n\t);\n}\nMaintenancePage.displayName = \"MaintenancePage\";\n","// src/templates/marketing-page-template.tsx\n//\n// MarketingPageTemplate — full-bleed landing-page chrome. No app shell, no\n// sidebar, no rail. Used for product landing pages, \"about us\", coming-\n// soon teasers, and other unauthenticated marketing surfaces.\n//\n// Composition (top to bottom):\n//\n// ┌──────────────────────────────────────────┐\n// │ topbar (logo · nav · CTA) │\n// ├──────────────────────────────────────────┤\n// │ hero (eyebrow · title · subtitle · CTAs) │\n// ├──────────────────────────────────────────┤\n// │ children (feature sections, testimonials) │\n// ├──────────────────────────────────────────┤\n// │ footer (copyright, links) │\n// └──────────────────────────────────────────┘\n//\n// The template aims for the same refined-minimalism aesthetic as the rest\n// of anker — calm surfaces, generous spacing on the hero only, brand\n// colors used as accents rather than full backgrounds. No carousels, no\n// animated parallax, no auto-rotating testimonials.\n\nimport type { ReactNode } from \"react\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { Heading, Text } from \"../primitives/typography\";\n\nexport interface MarketingPageTemplateProps {\n\t/** Logo or wordmark, far-left of the topbar. */\n\tlogo?: ReactNode;\n\t/** Navigation links and/or sign-in CTA, far-right of the topbar. */\n\ttopBarRight?: ReactNode;\n\t/** Hide the topbar (rare). */\n\thideTopBar?: boolean;\n\t/** Eyebrow above the hero title (uppercase, muted). */\n\theroEyebrow?: ReactNode;\n\t/** Hero title — large display text. */\n\theroTitle?: ReactNode;\n\t/** Hero subtitle — one-paragraph value statement. */\n\theroSubtitle?: ReactNode;\n\t/** Hero CTAs — typically one solid button + one ghost link. */\n\theroActions?: ReactNode;\n\t/** Optional visual rendered to the right of the hero copy on wide viewports. */\n\theroVisual?: ReactNode;\n\t/**\n\t * Feature sections, testimonials, etc. Rendered with `maxW=\"6xl\"` and\n\t * `marginInline=\"auto\"` so content tracks a consistent reading column.\n\t */\n\tchildren?: ReactNode;\n\t/** Footer content — copyright, secondary nav, contact. */\n\tfooter?: ReactNode;\n}\n\nexport function MarketingPageTemplate({\n\tlogo,\n\ttopBarRight,\n\thideTopBar,\n\theroEyebrow,\n\theroTitle,\n\theroSubtitle,\n\theroActions,\n\theroVisual,\n\tchildren,\n\tfooter,\n}: MarketingPageTemplateProps) {\n\treturn (\n\t\t<Box data-testid=\"marketing-page-template\" minH=\"100vh\" bg=\"bg-canvas\">\n\t\t\t{!hideTopBar && (\n\t\t\t\t<Flex\n\t\t\t\t\talign=\"center\"\n\t\t\t\t\tjustify=\"space-between\"\n\t\t\t\t\tpx=\"8\"\n\t\t\t\t\tpy=\"4\"\n\t\t\t\t\tbg=\"bg-surface\"\n\t\t\t\t\tborderBottomWidth=\"1px\"\n\t\t\t\t\tborderBottomColor=\"border\"\n\t\t\t\t>\n\t\t\t\t\t<Box>{logo}</Box>\n\t\t\t\t\t<Flex gap=\"6\" align=\"center\" fontSize=\"sm\" color=\"emphasized\">\n\t\t\t\t\t\t{topBarRight}\n\t\t\t\t\t</Flex>\n\t\t\t\t</Flex>\n\t\t\t)}\n\n\t\t\t{(heroEyebrow || heroTitle || heroSubtitle || heroActions) && (\n\t\t\t\t<Box px=\"8\" py=\"20\" bg=\"bg-canvas\">\n\t\t\t\t\t<Flex\n\t\t\t\t\t\tmaxW=\"6xl\"\n\t\t\t\t\t\tmarginInline=\"auto\"\n\t\t\t\t\t\talign=\"center\"\n\t\t\t\t\t\tgap=\"12\"\n\t\t\t\t\t\tdirection={{ base: \"column\", md: \"row\" }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Box flex=\"1\" minW=\"0\">\n\t\t\t\t\t\t\t{heroEyebrow && (\n\t\t\t\t\t\t\t\t<Text\n\t\t\t\t\t\t\t\t\tfontSize=\"2xs\"\n\t\t\t\t\t\t\t\t\tfontWeight=\"semibold\"\n\t\t\t\t\t\t\t\t\tletterSpacing=\"wider\"\n\t\t\t\t\t\t\t\t\ttextTransform=\"uppercase\"\n\t\t\t\t\t\t\t\t\tcolor=\"muted\"\n\t\t\t\t\t\t\t\t\tmb=\"3\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{heroEyebrow}\n\t\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{heroTitle && (\n\t\t\t\t\t\t\t\t<Heading\n\t\t\t\t\t\t\t\t\tas=\"h1\"\n\t\t\t\t\t\t\t\t\tfontSize={{ base: \"4xl\", md: \"6xl\" }}\n\t\t\t\t\t\t\t\t\tfontWeight=\"semibold\"\n\t\t\t\t\t\t\t\t\tcolor=\"default\"\n\t\t\t\t\t\t\t\t\tletterSpacing=\"-0.02em\"\n\t\t\t\t\t\t\t\t\tlineHeight=\"1.05\"\n\t\t\t\t\t\t\t\t\tmb=\"5\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{heroTitle}\n\t\t\t\t\t\t\t\t</Heading>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{heroSubtitle && (\n\t\t\t\t\t\t\t\t<Text\n\t\t\t\t\t\t\t\t\tfontSize=\"lg\"\n\t\t\t\t\t\t\t\t\tcolor=\"muted\"\n\t\t\t\t\t\t\t\t\tlineHeight=\"1.6\"\n\t\t\t\t\t\t\t\t\tmaxW=\"2xl\"\n\t\t\t\t\t\t\t\t\tmb=\"8\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{heroSubtitle}\n\t\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{heroActions && (\n\t\t\t\t\t\t\t\t<Flex gap=\"3\" align=\"center\">\n\t\t\t\t\t\t\t\t\t{heroActions}\n\t\t\t\t\t\t\t\t</Flex>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t\t{heroVisual && (\n\t\t\t\t\t\t\t<Box flex=\"1\" minW=\"0\" display={{ base: \"none\", md: \"block\" }}>\n\t\t\t\t\t\t\t\t{heroVisual}\n\t\t\t\t\t\t\t</Box>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</Flex>\n\t\t\t\t</Box>\n\t\t\t)}\n\n\t\t\t{children && (\n\t\t\t\t<Box px=\"8\" py=\"16\">\n\t\t\t\t\t<Box maxW=\"6xl\" marginInline=\"auto\">\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</Box>\n\t\t\t\t</Box>\n\t\t\t)}\n\n\t\t\t{footer && (\n\t\t\t\t<Box\n\t\t\t\t\tpx=\"8\"\n\t\t\t\t\tpy=\"8\"\n\t\t\t\t\tbg=\"bg-subtle\"\n\t\t\t\t\tborderTopWidth=\"1px\"\n\t\t\t\t\tborderTopColor=\"border\"\n\t\t\t\t>\n\t\t\t\t\t<Box maxW=\"6xl\" marginInline=\"auto\">\n\t\t\t\t\t\t{footer}\n\t\t\t\t\t</Box>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t</Box>\n\t);\n}\nMarketingPageTemplate.displayName = \"MarketingPageTemplate\";\n","// src/templates/settings-page-template.tsx\n//\n// SettingsPageTemplate — for any \"preferences / configuration\" page that\n// uses a tabbed split between mixed form / list content. Visually identical\n// to DetailPageTemplate today, but exposed as a separate template so its\n// authoring rules (tabs are required, body is padded, max-width on forms)\n// can evolve independently.\n//\n// ┌─────────────────────────────────────────┐\n// │ PageHeader (breadcrumbs · title · …) │\n// ├─────────────────────────────────────────┤\n// │ Tabs (required for settings) │\n// ├─────────────────────────────────────────┤\n// │ children (form cards · lists · …) │\n// └─────────────────────────────────────────┘\n//\n// Use this template for: personal-settings (profile, password, MFA tabs),\n// organization settings, identity-provider settings, admin → general.\n\nimport type { ReactNode } from \"react\";\nimport { PageHeader, type PageHeaderProps } from \"../components/page-header\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { useRegisteredPageActions } from \"./app-shell\";\n\nexport interface SettingsPageTemplateProps\n\textends Pick<\n\t\tPageHeaderProps,\n\t\t\"breadcrumbs\" | \"title\" | \"subtitle\" | \"eyebrow\"\n\t> {\n\tactions?: ReactNode;\n\t/**\n\t * Tab strip. Settings pages should always have at least two tabs — if\n\t * you only have one section, use DetailPageTemplate instead.\n\t */\n\ttabs: ReactNode;\n\t/** Page body — typically Card-wrapped forms or DataLists. */\n\tchildren: ReactNode;\n\t/**\n\t * Constrain the body width for readability. Settings forms read better\n\t * at ~720px even on wide viewports. Pass a Chakra width token (`\"3xl\"`,\n\t * `\"4xl\"`) or any CSS length. @default \"3xl\" (= 768px)\n\t *\n\t * Pass `\"full\"` to disable the constraint entirely.\n\t */\n\tmaxBodyWidth?: string;\n}\n\nexport function SettingsPageTemplate({\n\tbreadcrumbs,\n\ttitle,\n\tsubtitle,\n\teyebrow,\n\tactions,\n\ttabs,\n\tchildren,\n\tmaxBodyWidth = \"3xl\",\n}: SettingsPageTemplateProps) {\n\tconst registered = useRegisteredPageActions();\n\tconst resolvedActions = actions ?? registered;\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"settings-page-template\"\n\t\t\tdirection=\"column\"\n\t\t\tflex=\"1\"\n\t\t\tminH=\"0\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t<PageHeader\n\t\t\t\tbreadcrumbs={breadcrumbs}\n\t\t\t\ttitle={title}\n\t\t\t\tsubtitle={subtitle}\n\t\t\t\teyebrow={eyebrow}\n\t\t\t\tactions={resolvedActions}\n\t\t\t/>\n\t\t\t<Box>{tabs}</Box>\n\t\t\t<Box flex=\"1\" minH=\"0\" px=\"8\" pt=\"6\">\n\t\t\t\t<Box\n\t\t\t\t\tmaxW={maxBodyWidth}\n\t\t\t\t\tmarginInline={maxBodyWidth === \"full\" ? \"0\" : \"auto\"}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</Box>\n\t\t\t</Box>\n\t\t</Flex>\n\t);\n}\nSettingsPageTemplate.displayName = \"SettingsPageTemplate\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/templates/app-shell.tsx","../../src/templates/auth-page-template.tsx","../../src/templates/dashboard-page-template.tsx","../../src/templates/detail-page-template.tsx","../../src/templates/error-page.tsx","../../src/templates/index-page-template.tsx","../../src/templates/loading-page.tsx","../../src/templates/maintenance-page.tsx","../../src/templates/marketing-page-template.tsx","../../src/templates/settings-page-template.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["// src/templates/app-shell.tsx\n//\n// AppShell — top-level layout for authenticated knkCMS pages.\n//\n// Composition: a 3-column CSS grid with a fixed-width sidebar, a fluid main\n// content column, and an optional fixed-width context rail.\n//\n// ┌─────────┬───────────────────────────┬─────────┐\n// │ │ │ │\n// │ sidebar │ children │ rail │\n// │ │ (page header / content) │ │\n// │ │ │ │\n// └─────────┴───────────────────────────┴─────────┘\n//\n// Slot mechanism\n// --------------\n// AppShell installs an external slot store on its descendants via context.\n// Two named slots are exposed:\n//\n// - \"actions\" — registered via `usePageActions(node)` — surfaced by page\n// templates inside their <PageHeader actions=…> slot.\n// - \"rail\" — registered via `usePageRail(node)` — surfaced by AppShell\n// as the content of the right rail column.\n//\n// The store uses `useSyncExternalStore` so that producers (deep child\n// components rendered after the consumer) and consumers (AppShell, the page\n// templates) stay decoupled. A naive `useState`-based context would force the\n// consumer to render in the same React commit as the producer, which causes\n// the actions/rail to flicker on route changes (the Path-B fix originally\n// shipped in odon as PR #115). The external-store pattern lets the producer\n// register its content in a `useEffect`, the consumer re-reads on the next\n// browser frame, and React's concurrent renderer is happy.\n\nimport {\n\tcreateContext,\n\ttype ReactNode,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseSyncExternalStore,\n} from \"react\";\nimport { Box, Flex, Grid } from \"../primitives/layout\";\n\ntype SlotName = \"actions\" | \"rail\";\n\ninterface SlotStore {\n\tget(slot: SlotName): ReactNode;\n\tset(slot: SlotName, node: ReactNode): void;\n\tclear(slot: SlotName): void;\n\tsubscribe(slot: SlotName, listener: () => void): () => void;\n}\n\nfunction createSlotStore(): SlotStore {\n\tconst values: Record<SlotName, ReactNode> = {\n\t\tactions: null,\n\t\trail: null,\n\t};\n\tconst listeners: Record<SlotName, Set<() => void>> = {\n\t\tactions: new Set(),\n\t\trail: new Set(),\n\t};\n\n\tfunction notify(slot: SlotName) {\n\t\tfor (const listener of listeners[slot]) listener();\n\t}\n\n\treturn {\n\t\tget(slot) {\n\t\t\treturn values[slot];\n\t\t},\n\t\tset(slot, node) {\n\t\t\tif (values[slot] === node) return;\n\t\t\tvalues[slot] = node;\n\t\t\tnotify(slot);\n\t\t},\n\t\tclear(slot) {\n\t\t\tif (values[slot] === null) return;\n\t\t\tvalues[slot] = null;\n\t\t\tnotify(slot);\n\t\t},\n\t\tsubscribe(slot, listener) {\n\t\t\tlisteners[slot].add(listener);\n\t\t\treturn () => {\n\t\t\t\tlisteners[slot].delete(listener);\n\t\t\t};\n\t\t},\n\t};\n}\n\nconst SlotStoreContext = createContext<SlotStore | null>(null);\n\n/**\n * Read-side hook used internally by AppShell and the page templates to\n * subscribe to a named slot's currently-registered ReactNode. Returns `null`\n * when no producer has registered content (or when called outside an AppShell).\n */\nfunction useSlotValue(slot: SlotName): ReactNode {\n\tconst store = useContext(SlotStoreContext);\n\tconst subscribe = useCallback(\n\t\t(listener: () => void) => {\n\t\t\tif (!store) return () => undefined;\n\t\t\treturn store.subscribe(slot, listener);\n\t\t},\n\t\t[store, slot],\n\t);\n\tconst getSnapshot = useCallback(() => {\n\t\tif (!store) return null;\n\t\treturn store.get(slot);\n\t}, [store, slot]);\n\treturn useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Register page-action content (typically buttons rendered to the right of\n * the page title) from any descendant of `<AppShell>`. The content is\n * surfaced by the active page template inside its `<PageHeader>` actions\n * slot.\n *\n * Pass `null` (or omit) to clear the registration. The hook is a no-op when\n * called outside an AppShell, which keeps it safe to use in stories and\n * isolated component tests.\n */\nexport function usePageActions(content: ReactNode): void {\n\tconst store = useContext(SlotStoreContext);\n\t// Stash the most recent content in a ref so the effect can run on every\n\t// render without forcing a fresh effect-cleanup cycle for unstable nodes.\n\tconst latest = useRef<ReactNode>(content);\n\tlatest.current = content;\n\tuseEffect(() => {\n\t\tif (!store) return;\n\t\tstore.set(\"actions\", latest.current);\n\t\treturn () => {\n\t\t\tstore.clear(\"actions\");\n\t\t};\n\t}, [store]);\n\t// Re-register on every change of `content` (effect above only runs once\n\t// per mount; this second effect re-pushes the latest value).\n\tuseEffect(() => {\n\t\tif (!store) return;\n\t\tstore.set(\"actions\", content);\n\t}, [store, content]);\n}\n\n/**\n * Register context-rail content from any descendant of `<AppShell>`. The\n * content is rendered in the rail column (assuming the AppShell has a rail\n * slot enabled).\n *\n * Pass `null` (or omit) to clear the registration. The hook is a no-op when\n * called outside an AppShell.\n */\nexport function usePageRail(content: ReactNode): void {\n\tconst store = useContext(SlotStoreContext);\n\tconst latest = useRef<ReactNode>(content);\n\tlatest.current = content;\n\tuseEffect(() => {\n\t\tif (!store) return;\n\t\tstore.set(\"rail\", latest.current);\n\t\treturn () => {\n\t\t\tstore.clear(\"rail\");\n\t\t};\n\t}, [store]);\n\tuseEffect(() => {\n\t\tif (!store) return;\n\t\tstore.set(\"rail\", content);\n\t}, [store, content]);\n}\n\n/**\n * Internal hook used by page templates to read the currently-registered\n * page-actions ReactNode. Page templates fall back to a locally-passed\n * `actions` prop when nothing is registered.\n */\nexport function useRegisteredPageActions(): ReactNode {\n\treturn useSlotValue(\"actions\");\n}\n\nexport interface AppShellProps {\n\t/**\n\t * Sidebar element — the navigation column. Required. Typically an instance\n\t * of `<Sidebar>` from `@knkcs/anker/components`. AppShell does not own the\n\t * sidebar's collapsed-state — pass it through the Sidebar's own props.\n\t */\n\tsidebar: ReactNode;\n\t/**\n\t * Context rail element. Acts as a *fallback* for the rail column: it\n\t * renders when no descendant has registered rail content via\n\t * `usePageRail`. Registered content always wins over the prop.\n\t *\n\t * The rail column is enabled — and a grid track is reserved — when *either*\n\t * a `rail` prop is supplied *or* a descendant has registered rail content.\n\t * Omit both (or pass `null`) to drop the rail column entirely.\n\t */\n\trail?: ReactNode;\n\t/** Page content. */\n\tchildren: ReactNode;\n}\n\n/**\n * AppShell is the top-level layout for authenticated knkCMS pages. It owns\n * the slot context that powers `usePageActions` and `usePageRail`, and\n * arranges sidebar / main / rail in a 3-column CSS grid.\n *\n * AppShell is layout-only — it does not render a PageHeader, and it does not\n * inject any business chrome. Pages compose `<IndexPageTemplate>`,\n * `<DetailPageTemplate>`, etc. inside `children`.\n *\n * Rail precedence: content registered by a descendant via `usePageRail` wins\n * over the static `rail` prop. The prop is the fallback when no descendant\n * has registered content. Rationale: a descendant explicitly registering\n * content is signaling \"show this here\", which should trump the static prop.\n */\nexport function AppShell({ sidebar, rail, children }: AppShellProps) {\n\t// AppShell is split into an outer Provider and an inner Renderer so the\n\t// renderer's `useContext(SlotStoreContext)` resolves to *our* store rather\n\t// than the parent context. (A naive single component that both provides\n\t// and consumes the context at the same level reads the parent context —\n\t// the Provider only takes effect for descendants.)\n\tconst store = useMemo(() => createSlotStore(), []);\n\treturn (\n\t\t<SlotStoreContext.Provider value={store}>\n\t\t\t<AppShellInner sidebar={sidebar} rail={rail}>\n\t\t\t\t{children}\n\t\t\t</AppShellInner>\n\t\t</SlotStoreContext.Provider>\n\t);\n}\nAppShell.displayName = \"AppShell\";\n\nfunction AppShellInner({ sidebar, rail, children }: AppShellProps) {\n\tconst railNode = useSlotValue(\"rail\");\n\n\t// Precedence: registered rail content wins, fall back to the `rail` prop.\n\t// The rail column is enabled when *either* the consumer passed a `rail`\n\t// element *or* a descendant registered content via `usePageRail`.\n\tconst renderedRail = railNode ?? rail;\n\tconst showRailColumn =\n\t\trenderedRail !== undefined &&\n\t\trenderedRail !== null &&\n\t\trenderedRail !== false;\n\n\treturn (\n\t\t<Grid\n\t\t\tdata-testid=\"app-shell\"\n\t\t\tdata-rail={showRailColumn ? \"true\" : \"false\"}\n\t\t\ttemplateColumns={showRailColumn ? \"auto 1fr auto\" : \"auto 1fr\"}\n\t\t\tminH=\"100vh\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t<Box gridColumn=\"1\" minW=\"0\">\n\t\t\t\t{sidebar}\n\t\t\t</Box>\n\t\t\t<Flex gridColumn=\"2\" direction=\"column\" minW=\"0\" minH=\"100vh\">\n\t\t\t\t{children}\n\t\t\t</Flex>\n\t\t\t{showRailColumn ? (\n\t\t\t\t<Box gridColumn=\"3\" minW=\"0\">\n\t\t\t\t\t{renderedRail}\n\t\t\t\t</Box>\n\t\t\t) : null}\n\t\t</Grid>\n\t);\n}\nAppShellInner.displayName = \"AppShellInner\";\n","// src/templates/auth-page-template.tsx\n//\n// AuthPageTemplate — full-bleed centered card on a neutral canvas. No app\n// shell, no sidebar, no rail. Used for login, register, MFA challenge,\n// forgot/reset password, and email-verification screens.\n//\n// Internally this is a thin wrapper around `<AuthCard>` (in\n// `@knkcs/anker/components`) — surfaced under the templates module so\n// consumers can import their layout chrome from one entry point.\n//\n// We re-expose the wrapper rather than re-implementing it because AuthCard\n// is already battle-tested and stable; the templates module is the consumer\n// contract, AuthCard is the implementation.\n\nimport type { ReactNode } from \"react\";\nimport { AuthCard, type AuthCardProps } from \"../components/auth-card\";\n\nexport interface AuthPageTemplateProps extends AuthCardProps {\n\t/**\n\t * Page body — typically a form or a verification CTA. Inherits all\n\t * AuthCard slots (logo, topBarRight, eyebrow, title, subtitle, footer).\n\t */\n\tchildren: ReactNode;\n}\n\n/**\n * Auth-flow page layout. Use for any pre-authentication screen (login,\n * register, MFA, forgot-password, reset-password) and for short\n * confirmation flows (email-verify, account-deleted). For consent\n * dialogs, prefer `<OAuthConsentPageTemplate>` (denser layout).\n *\n * The template supplies a centered card with a dot-grid background, an\n * optional topbar with logo + locale switcher, and a footer slot. To hide\n * the topbar (rare — embedded preview, printable receipts) pass\n * `hideTopBar`. To hide the dot-grid (printable) pass `hideBackground`.\n */\nexport function AuthPageTemplate(props: AuthPageTemplateProps) {\n\treturn <AuthCard {...props} />;\n}\nAuthPageTemplate.displayName = \"AuthPageTemplate\";\n","// src/templates/dashboard-page-template.tsx\n//\n// DashboardPageTemplate — for at-a-glance overview pages composed of a grid\n// of widgets (Stat cards, mini-charts, recent-activity panes, etc.).\n//\n// ┌─────────────────────────────────────────┐\n// │ PageHeader (greeting · range picker) │\n// ├─────────────────────────────────────────┤\n// │ ┌──────┬──────┬──────┬──────┐ │\n// │ │ stat │ stat │ stat │ stat │ │\n// │ ├──────┴──────┼──────┴──────┤ │\n// │ │ widget │ widget │ │\n// │ │ │ │ │\n// │ ├─────────────┴─────────────┤ │\n// │ │ wide widget │ │\n// │ └───────────────────────────┘ │\n// └─────────────────────────────────────────┘\n//\n// The template provides the page chrome and a 12-column responsive grid.\n// Widgets opt into the column count via Chakra's `gridColumn` prop on each\n// child. Inspired by Linear's \"All issues\" overview and GitHub's repo\n// insights — calm surfaces, dense info, clear hierarchy.\n\nimport type { ReactNode } from \"react\";\nimport { PageHeader, type PageHeaderProps } from \"../components/page-header\";\nimport { Box, Flex, Grid } from \"../primitives/layout\";\nimport { useRegisteredPageActions } from \"./app-shell\";\n\nexport interface DashboardPageTemplateProps\n\textends Pick<\n\t\tPageHeaderProps,\n\t\t\"breadcrumbs\" | \"title\" | \"subtitle\" | \"eyebrow\"\n\t> {\n\tactions?: ReactNode;\n\t/**\n\t * Dashboard widgets. Children are placed inside a 12-column responsive\n\t * CSS grid with `gap=\"4\"`. Each child should set `gridColumn` (e.g.\n\t * `\"span 3\"` for a quarter-width tile, `\"span 6\"` for half, `\"span 12\"`\n\t * for full-width). The default per-child column span is `\"span 12\"`.\n\t */\n\tchildren: ReactNode;\n\t/**\n\t * Override the grid `gap` between widgets. @default \"4\" (= 16px)\n\t */\n\tgap?: string;\n}\n\nexport function DashboardPageTemplate({\n\tbreadcrumbs,\n\ttitle,\n\tsubtitle,\n\teyebrow,\n\tactions,\n\tchildren,\n\tgap = \"4\",\n}: DashboardPageTemplateProps) {\n\tconst registered = useRegisteredPageActions();\n\tconst resolvedActions = actions ?? registered;\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"dashboard-page-template\"\n\t\t\tdirection=\"column\"\n\t\t\tflex=\"1\"\n\t\t\tminH=\"0\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t<PageHeader\n\t\t\t\tbreadcrumbs={breadcrumbs}\n\t\t\t\ttitle={title}\n\t\t\t\tsubtitle={subtitle}\n\t\t\t\teyebrow={eyebrow}\n\t\t\t\tactions={resolvedActions}\n\t\t\t/>\n\t\t\t<Box flex=\"1\" minH=\"0\" px=\"8\" py=\"6\">\n\t\t\t\t<Grid\n\t\t\t\t\tdata-testid=\"dashboard-grid\"\n\t\t\t\t\ttemplateColumns=\"repeat(12, minmax(0, 1fr))\"\n\t\t\t\t\tgap={gap}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</Grid>\n\t\t\t</Box>\n\t\t</Flex>\n\t);\n}\nDashboardPageTemplate.displayName = \"DashboardPageTemplate\";\n","// src/templates/detail-page-template.tsx\n//\n// DetailPageTemplate — the canonical \"single entity\" page layout.\n//\n// Composition:\n//\n// ┌─────────────────────────────────────────┐\n// │ PageHeader (breadcrumbs · title · …) │\n// ├─────────────────────────────────────────┤\n// │ Tabs (optional — pinned beneath header) │\n// ├─────────────────────────────────────────┤\n// │ children (identity card · panes · …) │\n// └─────────────────────────────────────────┘\n//\n// Use for pages that show one entity: a single user, a single OAuth client,\n// a single audit-event detail. Detail pages often have an identity Card at\n// the top followed by tabbed sections — DetailPageTemplate is intentionally\n// thin so consumers can compose those freely under `children`.\n\nimport type { ReactNode } from \"react\";\nimport { PageHeader, type PageHeaderProps } from \"../components/page-header\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { useRegisteredPageActions } from \"./app-shell\";\n\nexport interface DetailPageTemplateProps\n\textends Pick<\n\t\tPageHeaderProps,\n\t\t\"breadcrumbs\" | \"title\" | \"subtitle\" | \"eyebrow\"\n\t> {\n\tactions?: ReactNode;\n\t/**\n\t * Tab strip rendered immediately under the PageHeader. Typically a\n\t * `<Tabs.Root>` containing a `<Tabs.List>` and one `<Tabs.Content>` per\n\t * pane. The template does not render `<Tabs.Content>` separately —\n\t * consumers control the tab body composition entirely.\n\t */\n\ttabs?: ReactNode;\n\t/**\n\t * Page body — the entity's identity card, tab bodies, edit forms, etc.\n\t * Rendered flush against the canvas. Add internal padding inside\n\t * `children` if you need it.\n\t */\n\tchildren: ReactNode;\n}\n\nexport function DetailPageTemplate({\n\tbreadcrumbs,\n\ttitle,\n\tsubtitle,\n\teyebrow,\n\tactions,\n\ttabs,\n\tchildren,\n}: DetailPageTemplateProps) {\n\tconst registered = useRegisteredPageActions();\n\tconst resolvedActions = actions ?? registered;\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"detail-page-template\"\n\t\t\tdirection=\"column\"\n\t\t\tflex=\"1\"\n\t\t\tminH=\"0\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t<PageHeader\n\t\t\t\tbreadcrumbs={breadcrumbs}\n\t\t\t\ttitle={title}\n\t\t\t\tsubtitle={subtitle}\n\t\t\t\teyebrow={eyebrow}\n\t\t\t\tactions={resolvedActions}\n\t\t\t/>\n\t\t\t{tabs ? <Box>{tabs}</Box> : null}\n\t\t\t<Box flex=\"1\" minH=\"0\">\n\t\t\t\t{children}\n\t\t\t</Box>\n\t\t</Flex>\n\t);\n}\nDetailPageTemplate.displayName = \"DetailPageTemplate\";\n","// src/templates/error-page.tsx\n//\n// ErrorPage — full-bleed error layout for 404 / 403 / 500 / generic\n// failures. Centered status code + message + suggested next step. No app\n// shell, no sidebar.\n//\n// We keep it deliberately spare — an enterprise B2B error screen does not\n// need a 404-illustration of an astronaut. The optional `illustration`\n// slot is there for the rare case where a product wants to reach for one\n// (e.g. a dedicated brand \"OOPS\" SVG).\n\nimport type { ReactNode } from \"react\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { Heading, Text } from \"../primitives/typography\";\n\nexport interface ErrorPageProps {\n\t/**\n\t * Status code (404, 500, 403, …) shown as the page's largest piece of\n\t * type. Pass any string — non-numeric codes like \"OOPS\" work too.\n\t */\n\tstatusCode: ReactNode;\n\t/** Short headline — e.g. \"Page not found\" or \"Something went wrong\". */\n\ttitle: ReactNode;\n\t/**\n\t * One-paragraph explanation. Keep it short — the user is already in a\n\t * frustrated state.\n\t */\n\tdescription?: ReactNode;\n\t/**\n\t * Action(s) — typically one solid button (back home / retry) plus an\n\t * optional secondary link.\n\t */\n\tactions?: ReactNode;\n\t/**\n\t * Optional illustration rendered above the status code. Use sparingly.\n\t */\n\tillustration?: ReactNode;\n\t/**\n\t * Logo or wordmark rendered top-left. Useful for branded error pages\n\t * served from the root domain.\n\t */\n\tlogo?: ReactNode;\n}\n\nexport function ErrorPage({\n\tstatusCode,\n\ttitle,\n\tdescription,\n\tactions,\n\tillustration,\n\tlogo,\n}: ErrorPageProps) {\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"error-page\"\n\t\t\tdirection=\"column\"\n\t\t\tminH=\"100vh\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t{logo && (\n\t\t\t\t<Box px=\"8\" py=\"6\">\n\t\t\t\t\t{logo}\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Flex\n\t\t\t\tflex=\"1\"\n\t\t\t\tdirection=\"column\"\n\t\t\t\talign=\"center\"\n\t\t\t\tjustify=\"center\"\n\t\t\t\tpx=\"8\"\n\t\t\t\tpb=\"16\"\n\t\t\t\ttextAlign=\"center\"\n\t\t\t>\n\t\t\t\t{illustration && <Box mb=\"8\">{illustration}</Box>}\n\t\t\t\t<Text\n\t\t\t\t\tfontSize=\"7xl\"\n\t\t\t\t\tfontWeight=\"semibold\"\n\t\t\t\t\tcolor=\"muted\"\n\t\t\t\t\tletterSpacing=\"-0.04em\"\n\t\t\t\t\tlineHeight=\"1\"\n\t\t\t\t\tmb=\"4\"\n\t\t\t\t>\n\t\t\t\t\t{statusCode}\n\t\t\t\t</Text>\n\t\t\t\t<Heading\n\t\t\t\t\tas=\"h1\"\n\t\t\t\t\tfontSize=\"2xl\"\n\t\t\t\t\tfontWeight=\"semibold\"\n\t\t\t\t\tcolor=\"default\"\n\t\t\t\t\tletterSpacing=\"-0.02em\"\n\t\t\t\t\tmb=\"3\"\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</Heading>\n\t\t\t\t{description && (\n\t\t\t\t\t<Text fontSize=\"md\" color=\"muted\" lineHeight=\"1.6\" maxW=\"lg\" mb=\"8\">\n\t\t\t\t\t\t{description}\n\t\t\t\t\t</Text>\n\t\t\t\t)}\n\t\t\t\t{actions && (\n\t\t\t\t\t<Flex gap=\"3\" align=\"center\" justify=\"center\">\n\t\t\t\t\t\t{actions}\n\t\t\t\t\t</Flex>\n\t\t\t\t)}\n\t\t\t</Flex>\n\t\t</Flex>\n\t);\n}\nErrorPage.displayName = \"ErrorPage\";\n","// src/templates/index-page-template.tsx\n//\n// IndexPageTemplate — the canonical \"list of items\" page layout.\n//\n// Composition (top to bottom):\n//\n// ┌─────────────────────────────────────────┐\n// │ PageHeader (breadcrumbs · title · …) │\n// ├─────────────────────────────────────────┤\n// │ Tabs (optional — under header) │\n// ├─────────────────────────────────────────┤\n// │ Toolbar (search · filters · count) │\n// ├─────────────────────────────────────────┤\n// │ children (DataTable, list, empty state) │\n// └─────────────────────────────────────────┘\n//\n// Use this template for any \"browse a list\" page: users index, OAuth-clients\n// index, audit-log, etc. The template flushes its content (no horizontal\n// padding) so a full-bleed DataTable sits cleanly under the toolbar. Pages\n// that need padded content should wrap children in a `<Box px=\"8\" py=\"6\">`.\n\nimport type { ReactNode } from \"react\";\nimport { PageHeader, type PageHeaderProps } from \"../components/page-header\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { useRegisteredPageActions } from \"./app-shell\";\n\nexport interface IndexPageTemplateProps\n\textends Pick<\n\t\tPageHeaderProps,\n\t\t\"breadcrumbs\" | \"title\" | \"subtitle\" | \"eyebrow\"\n\t> {\n\t/**\n\t * Optional explicit page-action content (rendered inside the PageHeader's\n\t * actions slot). When omitted, the template falls back to whatever a\n\t * descendant has registered via `usePageActions`.\n\t */\n\tactions?: ReactNode;\n\t/**\n\t * Optional tab strip rendered between the PageHeader and the toolbar.\n\t * Pass an instance of `<Tabs.Root>` (with its own `<Tabs.List>` and\n\t * `<Tabs.Content>`s). When omitted, no tab strip is rendered.\n\t */\n\ttabs?: ReactNode;\n\t/**\n\t * Toolbar element rendered between the tabs (if any) and the page body.\n\t * Typically an instance of `<Toolbar>` from `@knkcs/anker/components`.\n\t * Pass `null` to omit the toolbar entirely (rare).\n\t */\n\ttoolbar?: ReactNode;\n\t/**\n\t * Page body — DataTable, list, empty state, or error/loading content.\n\t * Rendered flush against the canvas. Add internal padding inside\n\t * `children` if you need it.\n\t */\n\tchildren: ReactNode;\n}\n\n/**\n * Canonical list-page layout. Renders PageHeader → optional Tabs → optional\n * Toolbar → children, full-bleed against the canvas.\n *\n * Page actions are sourced from (in priority order): `actions` prop →\n * registered slot via `usePageActions`. This lets a tab-pane component deep\n * inside `children` register its own actions without prop-drilling.\n */\nexport function IndexPageTemplate({\n\tbreadcrumbs,\n\ttitle,\n\tsubtitle,\n\teyebrow,\n\tactions,\n\ttabs,\n\ttoolbar,\n\tchildren,\n}: IndexPageTemplateProps) {\n\tconst registered = useRegisteredPageActions();\n\tconst resolvedActions = actions ?? registered;\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"index-page-template\"\n\t\t\tdirection=\"column\"\n\t\t\tflex=\"1\"\n\t\t\tminH=\"0\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t<PageHeader\n\t\t\t\tbreadcrumbs={breadcrumbs}\n\t\t\t\ttitle={title}\n\t\t\t\tsubtitle={subtitle}\n\t\t\t\teyebrow={eyebrow}\n\t\t\t\tactions={resolvedActions}\n\t\t\t/>\n\t\t\t{tabs ? <Box>{tabs}</Box> : null}\n\t\t\t{toolbar ? <Box>{toolbar}</Box> : null}\n\t\t\t<Box flex=\"1\" minH=\"0\">\n\t\t\t\t{children}\n\t\t\t</Box>\n\t\t</Flex>\n\t);\n}\nIndexPageTemplate.displayName = \"IndexPageTemplate\";\n","// src/templates/loading-page.tsx\n//\n// LoadingPage — full-bleed initial-boot loading screen. Used while the\n// authenticated app shell is being hydrated (auth check, theme loading,\n// initial data fetch). For in-page loading states (e.g. tab switch),\n// prefer a centered <Spinner /> inside the page body — this template is\n// only for the very first frame of the app.\n\nimport type { ReactNode } from \"react\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { Spinner } from \"../primitives/spinner\";\nimport { Text } from \"../primitives/typography\";\n\nexport interface LoadingPageProps {\n\t/** Optional logo rendered above the spinner. */\n\tlogo?: ReactNode;\n\t/**\n\t * Optional message rendered below the spinner. Keep it short\n\t * (e.g. \"Loading your workspace…\").\n\t */\n\tmessage?: ReactNode;\n}\n\nexport function LoadingPage({ logo, message }: LoadingPageProps) {\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"loading-page\"\n\t\t\tdirection=\"column\"\n\t\t\talign=\"center\"\n\t\t\tjustify=\"center\"\n\t\t\tminH=\"100vh\"\n\t\t\tbg=\"bg-canvas\"\n\t\t\tgap=\"6\"\n\t\t\ttextAlign=\"center\"\n\t\t>\n\t\t\t{logo && <Box>{logo}</Box>}\n\t\t\t<Spinner size=\"lg\" color=\"accent\" />\n\t\t\t{message && (\n\t\t\t\t<Text fontSize=\"sm\" color=\"muted\">\n\t\t\t\t\t{message}\n\t\t\t\t</Text>\n\t\t\t)}\n\t\t</Flex>\n\t);\n}\nLoadingPage.displayName = \"LoadingPage\";\n","// src/templates/maintenance-page.tsx\n//\n// MaintenancePage — full-bleed maintenance/down-for-upgrade screen. No app\n// shell. Surfaces a clear message, an optional ETA, and an optional link\n// to a status page. Operators serve this from a static asset or a\n// fallback handler when the app is offline.\n\nimport type { ReactNode } from \"react\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { Heading, Text } from \"../primitives/typography\";\n\nexport interface MaintenancePageProps {\n\t/** Logo rendered top-left. */\n\tlogo?: ReactNode;\n\t/** Headline — e.g. \"We'll be right back\". */\n\ttitle?: ReactNode;\n\t/**\n\t * One-paragraph message explaining what's happening and what users\n\t * should do (typically \"wait, we'll be back shortly\").\n\t */\n\tdescription?: ReactNode;\n\t/**\n\t * Estimated-time-of-restoration banner. Pass a string like\n\t * \"Estimated back online: 14:30 UTC\". Rendered below the description.\n\t */\n\teta?: ReactNode;\n\t/**\n\t * Optional link to a status page or twitter status. Pass a fully-\n\t * styled `<Link>` or an `<a>` element.\n\t */\n\tstatusLink?: ReactNode;\n}\n\nexport function MaintenancePage({\n\tlogo,\n\ttitle = \"We'll be right back\",\n\tdescription = \"We're upgrading the service. Please refresh in a few minutes.\",\n\teta,\n\tstatusLink,\n}: MaintenancePageProps) {\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"maintenance-page\"\n\t\t\tdirection=\"column\"\n\t\t\tminH=\"100vh\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t{logo && (\n\t\t\t\t<Box px=\"8\" py=\"6\">\n\t\t\t\t\t{logo}\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Flex\n\t\t\t\tflex=\"1\"\n\t\t\t\tdirection=\"column\"\n\t\t\t\talign=\"center\"\n\t\t\t\tjustify=\"center\"\n\t\t\t\tpx=\"8\"\n\t\t\t\tpb=\"16\"\n\t\t\t\ttextAlign=\"center\"\n\t\t\t>\n\t\t\t\t<Heading\n\t\t\t\t\tas=\"h1\"\n\t\t\t\t\tfontSize=\"3xl\"\n\t\t\t\t\tfontWeight=\"semibold\"\n\t\t\t\t\tcolor=\"default\"\n\t\t\t\t\tletterSpacing=\"-0.02em\"\n\t\t\t\t\tmb=\"4\"\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</Heading>\n\t\t\t\t{description && (\n\t\t\t\t\t<Text fontSize=\"md\" color=\"muted\" lineHeight=\"1.6\" maxW=\"lg\" mb=\"6\">\n\t\t\t\t\t\t{description}\n\t\t\t\t\t</Text>\n\t\t\t\t)}\n\t\t\t\t{eta && (\n\t\t\t\t\t<Box\n\t\t\t\t\t\tpx=\"4\"\n\t\t\t\t\t\tpy=\"2\"\n\t\t\t\t\t\tborderWidth=\"1px\"\n\t\t\t\t\t\tborderColor=\"border\"\n\t\t\t\t\t\tborderRadius=\"md\"\n\t\t\t\t\t\tbg=\"bg-surface\"\n\t\t\t\t\t\tmb=\"6\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Text fontSize=\"sm\" fontWeight=\"medium\" color=\"emphasized\">\n\t\t\t\t\t\t\t{eta}\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t)}\n\t\t\t\t{statusLink && <Box>{statusLink}</Box>}\n\t\t\t</Flex>\n\t\t</Flex>\n\t);\n}\nMaintenancePage.displayName = \"MaintenancePage\";\n","// src/templates/marketing-page-template.tsx\n//\n// MarketingPageTemplate — full-bleed landing-page chrome. No app shell, no\n// sidebar, no rail. Used for product landing pages, \"about us\", coming-\n// soon teasers, and other unauthenticated marketing surfaces.\n//\n// Composition (top to bottom):\n//\n// ┌──────────────────────────────────────────┐\n// │ topbar (logo · nav · CTA) │\n// ├──────────────────────────────────────────┤\n// │ hero (eyebrow · title · subtitle · CTAs) │\n// ├──────────────────────────────────────────┤\n// │ children (feature sections, testimonials) │\n// ├──────────────────────────────────────────┤\n// │ footer (copyright, links) │\n// └──────────────────────────────────────────┘\n//\n// The template aims for the same refined-minimalism aesthetic as the rest\n// of anker — calm surfaces, generous spacing on the hero only, brand\n// colors used as accents rather than full backgrounds. No carousels, no\n// animated parallax, no auto-rotating testimonials.\n\nimport type { ReactNode } from \"react\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { Heading, Text } from \"../primitives/typography\";\n\nexport interface MarketingPageTemplateProps {\n\t/** Logo or wordmark, far-left of the topbar. */\n\tlogo?: ReactNode;\n\t/** Navigation links and/or sign-in CTA, far-right of the topbar. */\n\ttopBarRight?: ReactNode;\n\t/** Hide the topbar (rare). */\n\thideTopBar?: boolean;\n\t/** Eyebrow above the hero title (uppercase, muted). */\n\theroEyebrow?: ReactNode;\n\t/** Hero title — large display text. */\n\theroTitle?: ReactNode;\n\t/** Hero subtitle — one-paragraph value statement. */\n\theroSubtitle?: ReactNode;\n\t/** Hero CTAs — typically one solid button + one ghost link. */\n\theroActions?: ReactNode;\n\t/** Optional visual rendered to the right of the hero copy on wide viewports. */\n\theroVisual?: ReactNode;\n\t/**\n\t * Feature sections, testimonials, etc. Rendered with `maxW=\"6xl\"` and\n\t * `marginInline=\"auto\"` so content tracks a consistent reading column.\n\t */\n\tchildren?: ReactNode;\n\t/** Footer content — copyright, secondary nav, contact. */\n\tfooter?: ReactNode;\n}\n\nexport function MarketingPageTemplate({\n\tlogo,\n\ttopBarRight,\n\thideTopBar,\n\theroEyebrow,\n\theroTitle,\n\theroSubtitle,\n\theroActions,\n\theroVisual,\n\tchildren,\n\tfooter,\n}: MarketingPageTemplateProps) {\n\treturn (\n\t\t<Box data-testid=\"marketing-page-template\" minH=\"100vh\" bg=\"bg-canvas\">\n\t\t\t{!hideTopBar && (\n\t\t\t\t<Flex\n\t\t\t\t\talign=\"center\"\n\t\t\t\t\tjustify=\"space-between\"\n\t\t\t\t\tpx=\"8\"\n\t\t\t\t\tpy=\"4\"\n\t\t\t\t\tbg=\"bg-surface\"\n\t\t\t\t\tborderBottomWidth=\"1px\"\n\t\t\t\t\tborderBottomColor=\"border\"\n\t\t\t\t>\n\t\t\t\t\t<Box>{logo}</Box>\n\t\t\t\t\t<Flex gap=\"6\" align=\"center\" fontSize=\"sm\" color=\"emphasized\">\n\t\t\t\t\t\t{topBarRight}\n\t\t\t\t\t</Flex>\n\t\t\t\t</Flex>\n\t\t\t)}\n\n\t\t\t{(heroEyebrow || heroTitle || heroSubtitle || heroActions) && (\n\t\t\t\t<Box px=\"8\" py=\"20\" bg=\"bg-canvas\">\n\t\t\t\t\t<Flex\n\t\t\t\t\t\tmaxW=\"6xl\"\n\t\t\t\t\t\tmarginInline=\"auto\"\n\t\t\t\t\t\talign=\"center\"\n\t\t\t\t\t\tgap=\"12\"\n\t\t\t\t\t\tdirection={{ base: \"column\", md: \"row\" }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Box flex=\"1\" minW=\"0\">\n\t\t\t\t\t\t\t{heroEyebrow && (\n\t\t\t\t\t\t\t\t<Text\n\t\t\t\t\t\t\t\t\tfontSize=\"2xs\"\n\t\t\t\t\t\t\t\t\tfontWeight=\"semibold\"\n\t\t\t\t\t\t\t\t\tletterSpacing=\"wider\"\n\t\t\t\t\t\t\t\t\ttextTransform=\"uppercase\"\n\t\t\t\t\t\t\t\t\tcolor=\"muted\"\n\t\t\t\t\t\t\t\t\tmb=\"3\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{heroEyebrow}\n\t\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{heroTitle && (\n\t\t\t\t\t\t\t\t<Heading\n\t\t\t\t\t\t\t\t\tas=\"h1\"\n\t\t\t\t\t\t\t\t\tfontSize={{ base: \"4xl\", md: \"6xl\" }}\n\t\t\t\t\t\t\t\t\tfontWeight=\"semibold\"\n\t\t\t\t\t\t\t\t\tcolor=\"default\"\n\t\t\t\t\t\t\t\t\tletterSpacing=\"-0.02em\"\n\t\t\t\t\t\t\t\t\tlineHeight=\"1.05\"\n\t\t\t\t\t\t\t\t\tmb=\"5\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{heroTitle}\n\t\t\t\t\t\t\t\t</Heading>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{heroSubtitle && (\n\t\t\t\t\t\t\t\t<Text\n\t\t\t\t\t\t\t\t\tfontSize=\"lg\"\n\t\t\t\t\t\t\t\t\tcolor=\"muted\"\n\t\t\t\t\t\t\t\t\tlineHeight=\"1.6\"\n\t\t\t\t\t\t\t\t\tmaxW=\"2xl\"\n\t\t\t\t\t\t\t\t\tmb=\"8\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{heroSubtitle}\n\t\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{heroActions && (\n\t\t\t\t\t\t\t\t<Flex gap=\"3\" align=\"center\">\n\t\t\t\t\t\t\t\t\t{heroActions}\n\t\t\t\t\t\t\t\t</Flex>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</Box>\n\t\t\t\t\t\t{heroVisual && (\n\t\t\t\t\t\t\t<Box flex=\"1\" minW=\"0\" display={{ base: \"none\", md: \"block\" }}>\n\t\t\t\t\t\t\t\t{heroVisual}\n\t\t\t\t\t\t\t</Box>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</Flex>\n\t\t\t\t</Box>\n\t\t\t)}\n\n\t\t\t{children && (\n\t\t\t\t<Box px=\"8\" py=\"16\">\n\t\t\t\t\t<Box maxW=\"6xl\" marginInline=\"auto\">\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</Box>\n\t\t\t\t</Box>\n\t\t\t)}\n\n\t\t\t{footer && (\n\t\t\t\t<Box\n\t\t\t\t\tpx=\"8\"\n\t\t\t\t\tpy=\"8\"\n\t\t\t\t\tbg=\"bg-subtle\"\n\t\t\t\t\tborderTopWidth=\"1px\"\n\t\t\t\t\tborderTopColor=\"border\"\n\t\t\t\t>\n\t\t\t\t\t<Box maxW=\"6xl\" marginInline=\"auto\">\n\t\t\t\t\t\t{footer}\n\t\t\t\t\t</Box>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t</Box>\n\t);\n}\nMarketingPageTemplate.displayName = \"MarketingPageTemplate\";\n","// src/templates/settings-page-template.tsx\n//\n// SettingsPageTemplate — for any \"preferences / configuration\" page that\n// uses a tabbed split between mixed form / list content. Visually identical\n// to DetailPageTemplate today, but exposed as a separate template so its\n// authoring rules (tabs are required, body is padded, max-width on forms)\n// can evolve independently.\n//\n// ┌─────────────────────────────────────────┐\n// │ PageHeader (breadcrumbs · title · …) │\n// ├─────────────────────────────────────────┤\n// │ Tabs (required for settings) │\n// ├─────────────────────────────────────────┤\n// │ children (form cards · lists · …) │\n// └─────────────────────────────────────────┘\n//\n// Use this template for: personal-settings (profile, password, MFA tabs),\n// organization settings, identity-provider settings, admin → general.\n\nimport type { ReactNode } from \"react\";\nimport { PageHeader, type PageHeaderProps } from \"../components/page-header\";\nimport { Box, Flex } from \"../primitives/layout\";\nimport { useRegisteredPageActions } from \"./app-shell\";\n\nexport interface SettingsPageTemplateProps\n\textends Pick<\n\t\tPageHeaderProps,\n\t\t\"breadcrumbs\" | \"title\" | \"subtitle\" | \"eyebrow\"\n\t> {\n\tactions?: ReactNode;\n\t/**\n\t * Tab strip. Settings pages should always have at least two tabs — if\n\t * you only have one section, use DetailPageTemplate instead.\n\t */\n\ttabs: ReactNode;\n\t/** Page body — typically Card-wrapped forms or DataLists. */\n\tchildren: ReactNode;\n\t/**\n\t * Constrain the body width for readability. Settings forms read better\n\t * at ~720px even on wide viewports. Pass a Chakra width token (`\"3xl\"`,\n\t * `\"4xl\"`) or any CSS length. @default \"3xl\" (= 768px)\n\t *\n\t * Pass `\"full\"` to disable the constraint entirely.\n\t */\n\tmaxBodyWidth?: string;\n}\n\nexport function SettingsPageTemplate({\n\tbreadcrumbs,\n\ttitle,\n\tsubtitle,\n\teyebrow,\n\tactions,\n\ttabs,\n\tchildren,\n\tmaxBodyWidth = \"3xl\",\n}: SettingsPageTemplateProps) {\n\tconst registered = useRegisteredPageActions();\n\tconst resolvedActions = actions ?? registered;\n\treturn (\n\t\t<Flex\n\t\t\tdata-testid=\"settings-page-template\"\n\t\t\tdirection=\"column\"\n\t\t\tflex=\"1\"\n\t\t\tminH=\"0\"\n\t\t\tbg=\"bg-canvas\"\n\t\t>\n\t\t\t<PageHeader\n\t\t\t\tbreadcrumbs={breadcrumbs}\n\t\t\t\ttitle={title}\n\t\t\t\tsubtitle={subtitle}\n\t\t\t\teyebrow={eyebrow}\n\t\t\t\tactions={resolvedActions}\n\t\t\t/>\n\t\t\t<Box>{tabs}</Box>\n\t\t\t<Box flex=\"1\" minH=\"0\" px=\"8\" pt=\"6\">\n\t\t\t\t<Box\n\t\t\t\t\tmaxW={maxBodyWidth}\n\t\t\t\t\tmarginInline={maxBodyWidth === \"full\" ? \"0\" : \"auto\"}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</Box>\n\t\t\t</Box>\n\t\t</Flex>\n\t);\n}\nSettingsPageTemplate.displayName = \"SettingsPageTemplate\";\n"]}
|