From 71b6f5a1741444a7d020bc3a24e0c6878527d311 Mon Sep 17 00:00:00 2001 From: TrueNine Date: Tue, 24 Mar 2026 21:00:18 +0800 Subject: [PATCH 1/4] Overhaul workspace packaging and release workflows --- doc/app/globals.css | 268 +++++++++++++++ doc/components/doc-widgets.tsx | 318 ++++++++++++++++++ doc/content/mcp/server-tools.mdx | 34 +- doc/content/technical-details/_meta.ts | 1 + .../documentation-components.mdx | 160 +++++++++ doc/mdx-components.tsx | 5 + 6 files changed, 780 insertions(+), 6 deletions(-) create mode 100644 doc/components/doc-widgets.tsx create mode 100644 doc/content/technical-details/documentation-components.mdx diff --git a/doc/app/globals.css b/doc/app/globals.css index 8c9f7876..b6a323a9 100644 --- a/doc/app/globals.css +++ b/doc/app/globals.css @@ -818,6 +818,197 @@ samp { padding: 0 18px 18px; } +.nextra-body-typesetting-article .docs-widget { + margin: 1.5rem 0; +} + +.nextra-body-typesetting-article .docs-widget-header { + margin-bottom: 0.9rem; +} + +.nextra-body-typesetting-article .docs-widget-header h3 { + margin: 0; + font-size: 1rem; +} + +.nextra-body-typesetting-article .docs-widget-header p { + margin: 0.45rem 0 0; + color: var(--page-fg-soft); +} + +.nextra-body-typesetting-article .docs-table-shell { + overflow-x: auto; + border: 1px solid var(--surface-border); + border-radius: 20px; + background: color-mix(in srgb, var(--surface-strong) 92%, transparent); + box-shadow: var(--shadow-sm); +} + +.nextra-body-typesetting-article .docs-widget-table { + width: 100%; + min-width: 720px; + margin: 0; + border: 0; + border-collapse: separate; + border-spacing: 0; + background: transparent; +} + +.nextra-body-typesetting-article .docs-widget-table thead { + background: color-mix(in srgb, var(--surface-subtle) 88%, transparent); +} + +.nextra-body-typesetting-article .docs-widget-table :where(th, td) { + vertical-align: top; + border-right: 1px solid var(--surface-border); + border-bottom: 1px solid var(--surface-border); +} + +.nextra-body-typesetting-article .docs-widget-table :where(th, td):last-child { + border-right: 0; +} + +.nextra-body-typesetting-article .docs-widget-table tbody tr:last-child td { + border-bottom: 0; +} + +.nextra-body-typesetting-article .docs-cell-heading { + display: flex; + flex-direction: column; + gap: 0.45rem; +} + +.nextra-body-typesetting-article .docs-muted { + color: var(--page-fg-muted); +} + +.nextra-body-typesetting-article .docs-table-list, +.nextra-body-typesetting-article .docs-platform-card__highlights { + margin: 0; + padding-left: 1.1rem; +} + +.nextra-body-typesetting-article .docs-table-list li, +.nextra-body-typesetting-article .docs-platform-card__highlights li { + margin: 0.15rem 0; + color: var(--page-fg-soft); +} + +.nextra-body-typesetting-article .docs-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 26px; + padding: 0 0.7rem; + border: 1px solid var(--surface-border); + border-radius: 999px; + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.nextra-body-typesetting-article .docs-badge--stable, +.nextra-body-typesetting-article .docs-badge--full { + border-color: rgba(20, 132, 86, 0.22); + background: rgba(20, 132, 86, 0.1); + color: #116149; +} + +.nextra-body-typesetting-article .docs-badge--partial, +.nextra-body-typesetting-article .docs-badge--beta { + border-color: rgba(180, 83, 9, 0.2); + background: rgba(180, 83, 9, 0.1); + color: #92400e; +} + +.nextra-body-typesetting-article .docs-badge--planned, +.nextra-body-typesetting-article .docs-badge--experimental, +.nextra-body-typesetting-article .docs-badge--info { + border-color: rgba(37, 99, 235, 0.18); + background: rgba(37, 99, 235, 0.08); + color: #1d4ed8; +} + +.nextra-body-typesetting-article .docs-badge--deprecated, +.nextra-body-typesetting-article .docs-badge--unsupported { + border-color: rgba(185, 28, 28, 0.18); + background: rgba(185, 28, 28, 0.08); + color: #b91c1c; +} + +.nextra-body-typesetting-article .docs-platform-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 14px; +} + +.nextra-body-typesetting-article .docs-platform-card { + position: relative; + overflow: hidden; + padding: 20px; + border: 1px solid var(--surface-border); + border-radius: 22px; + background: + linear-gradient(180deg, color-mix(in srgb, var(--surface-strong) 94%, transparent), var(--surface)), + var(--hero-glow); + box-shadow: var(--shadow-sm); +} + +.nextra-body-typesetting-article .docs-platform-card::before { + content: ''; + position: absolute; + inset: 0 auto auto 0; + width: 100%; + height: 1px; + background: linear-gradient(90deg, transparent, var(--surface-border-strong), transparent); +} + +.nextra-body-typesetting-article .docs-platform-card__top { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 14px; +} + +.nextra-body-typesetting-article .docs-platform-card__family { + display: inline-flex; + margin-bottom: 0.5rem; + color: var(--page-fg-muted); + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.nextra-body-typesetting-article .docs-platform-card h3 { + margin: 0; + font-size: 1.02rem; +} + +.nextra-body-typesetting-article .docs-platform-card p { + margin: 0.75rem 0 0; +} + +.nextra-body-typesetting-article .docs-command-stack { + display: flex; + flex-direction: column; + gap: 0.45rem; +} + +.nextra-body-typesetting-article .docs-command-chip { + display: block; + width: fit-content; + max-width: min(100%, 36rem); + padding: 0.45rem 0.6rem; + border: 1px solid var(--surface-border); + border-radius: 12px; + background: color-mix(in srgb, var(--surface-subtle) 90%, transparent); + color: var(--page-fg); + white-space: pre-wrap; + word-break: break-word; +} + html.dark .home-topbar-nav a:hover, html.dark .docs-nav-link:hover, html.dark .docs-navbar-action:hover { @@ -1082,6 +1273,79 @@ html.dark .nextra-body-typesetting-article .mermaid-diagram__fallback { background: color-mix(in srgb, var(--surface-overlay) 96%, transparent) !important; } +html.dark .nextra-body-typesetting-article .docs-table-shell { + border-color: var(--surface-border); + background: + linear-gradient( + 180deg, + color-mix(in srgb, var(--surface-overlay) 96%, transparent), + color-mix(in srgb, var(--surface) 92%, transparent) + ); + box-shadow: + inset 0 1px 0 var(--surface-highlight), + var(--shadow-sm); +} + +html.dark .nextra-body-typesetting-article .docs-widget-table thead { + background: color-mix(in srgb, var(--surface-highlight-strong) 100%, var(--surface-elevated)); +} + +html.dark .nextra-body-typesetting-article .docs-widget-table :where(th, td) { + border-color: var(--surface-separator); +} + +html.dark .nextra-body-typesetting-article .docs-badge--stable, +html.dark .nextra-body-typesetting-article .docs-badge--full { + border-color: rgba(74, 222, 128, 0.18); + background: rgba(74, 222, 128, 0.1); + color: #86efac; +} + +html.dark .nextra-body-typesetting-article .docs-badge--partial, +html.dark .nextra-body-typesetting-article .docs-badge--beta { + border-color: rgba(251, 191, 36, 0.16); + background: rgba(251, 191, 36, 0.1); + color: #fcd34d; +} + +html.dark .nextra-body-typesetting-article .docs-badge--planned, +html.dark .nextra-body-typesetting-article .docs-badge--experimental, +html.dark .nextra-body-typesetting-article .docs-badge--info { + border-color: rgba(96, 165, 250, 0.16); + background: rgba(96, 165, 250, 0.1); + color: #93c5fd; +} + +html.dark .nextra-body-typesetting-article .docs-badge--deprecated, +html.dark .nextra-body-typesetting-article .docs-badge--unsupported { + border-color: rgba(248, 113, 113, 0.16); + background: rgba(248, 113, 113, 0.1); + color: #fca5a5; +} + +html.dark .nextra-body-typesetting-article .docs-platform-card { + border-color: var(--surface-border); + background: + linear-gradient( + 180deg, + color-mix(in srgb, var(--surface-overlay) 94%, transparent), + color-mix(in srgb, var(--surface) 92%, transparent) + ); + box-shadow: + inset 0 1px 0 var(--surface-highlight), + var(--shadow-sm); +} + +html.dark .nextra-body-typesetting-article .docs-platform-card::before { + background: linear-gradient(90deg, transparent, var(--surface-separator), transparent); +} + +html.dark .nextra-body-typesetting-article .docs-command-chip { + border-color: var(--surface-border); + background: color-mix(in srgb, var(--surface-highlight) 100%, var(--surface-elevated)); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03); +} + .nextra-footer { color: var(--page-fg-muted); } @@ -1150,6 +1414,10 @@ html.dark .nextra-body-typesetting-article .mermaid-diagram__fallback { grid-template-columns: minmax(0, 1fr); } + .nextra-body-typesetting-article .docs-platform-grid { + grid-template-columns: 1fr; + } + .home-hero h1 { max-width: 13ch; font-size: clamp(1.8rem, 10vw, 2.45rem); diff --git a/doc/components/doc-widgets.tsx b/doc/components/doc-widgets.tsx new file mode 100644 index 00000000..05a00031 --- /dev/null +++ b/doc/components/doc-widgets.tsx @@ -0,0 +1,318 @@ +import type {ReactNode} from 'react' + +type BadgeTone + = | 'stable' + | 'full' + | 'partial' + | 'planned' + | 'beta' + | 'experimental' + | 'deprecated' + | 'unsupported' + | 'info' + +type Badge + = | BadgeTone + | { + label: string + tone?: BadgeTone + } + +interface SharedProps { + title?: string + description?: ReactNode +} + +interface FeatureMatrixItem { + tool: string + summary?: ReactNode + capabilities?: ReactNode | ReactNode[] + surfaces?: ReactNode | ReactNode[] + notes?: ReactNode + status?: Badge +} + +interface SupportMatrixItem { + system: string + support: Badge + coverage?: ReactNode | ReactNode[] + notes?: ReactNode +} + +interface PlatformGridItem { + name: string + family?: string + description: ReactNode + highlights?: ReactNode[] + support?: Badge +} + +interface CommandReferenceItem { + stack: string + task: string + command: string | string[] + runtime?: ReactNode + notes?: ReactNode +} + +interface FeatureMatrixProps extends SharedProps { + items: FeatureMatrixItem[] +} + +interface SupportMatrixProps extends SharedProps { + items: SupportMatrixItem[] +} + +interface PlatformGridProps extends SharedProps { + items: PlatformGridItem[] +} + +interface CommandReferenceProps extends SharedProps { + items: CommandReferenceItem[] +} + +function hasText(value?: string): value is string { + return value !== void 0 && value.length > 0 +} + +function hasNode(value?: ReactNode): value is Exclude { + return value !== void 0 && value !== null && value !== false +} + +function renderBadge(badge?: Badge): ReactNode { + if (badge === void 0) { + return null + } + + const tone = typeof badge === 'string' ? badge : badge.tone ?? 'info' + const label = typeof badge === 'string' ? formatToneLabel(badge) : badge.label + + return ( + + {label} + + ) +} + +function renderContent(value?: ReactNode | ReactNode[]): ReactNode { + if (value === void 0) { + return - + } + + if (!Array.isArray(value)) { + return value + } + + if (value.length === 0) { + return - + } + + return ( + + ) +} + +function renderCommand(command: string | string[]): ReactNode { + const lines = Array.isArray(command) ? command : [command] + + return ( +
+ {lines.map((line, index) => ( + + {line} + + ))} +
+ ) +} + +function renderSectionHeader(title?: string, description?: ReactNode): ReactNode { + const hasTitle = hasText(title) + const hasDescription = hasNode(description) + + if (!hasTitle && !hasDescription) { + return null + } + + return ( +
+ {hasTitle ?

{title}

: null} + {hasDescription ?

{description}

: null} +
+ ) +} + +function renderOptionalNode(value?: ReactNode): ReactNode { + return hasNode(value) ? value : - +} + +function formatToneLabel(tone: BadgeTone): string { + switch (tone) { + case 'stable': + return 'Stable' + case 'full': + return 'Full' + case 'partial': + return 'Partial' + case 'planned': + return 'Planned' + case 'beta': + return 'Beta' + case 'experimental': + return 'Experimental' + case 'deprecated': + return 'Deprecated' + case 'unsupported': + return 'Unsupported' + default: + return 'Info' + } +} + +function getListKey(item: ReactNode, index: number): string | number { + if (typeof item === 'string' || typeof item === 'number') { + return `${item}-${index}` + } + + return index +} + +export function FeatureMatrix({title, description, items}: FeatureMatrixProps): ReactNode { + return ( +
+ {renderSectionHeader(title, description)} +
+ + + + + + + + + + + {items.map(item => ( + + + + + + + ))} + +
ToolCore CapabilityEntry / SurfaceNotes
+
+ {item.tool} + {item.status !== void 0 ?
{renderBadge(item.status)}
: null} +
+ {hasNode(item.summary) ?
{item.summary}
: null} +
{renderContent(item.capabilities)}{renderContent(item.surfaces)}{renderOptionalNode(item.notes)}
+
+
+ ) +} + +export function SupportMatrix({title, description, items}: SupportMatrixProps): ReactNode { + return ( +
+ {renderSectionHeader(title, description)} +
+ + + + + + + + + + + {items.map(item => ( + + + + + + + ))} + +
SystemSupportCoverageNotes
+ {item.system} + {renderBadge(item.support)}{renderContent(item.coverage)}{renderOptionalNode(item.notes)}
+
+
+ ) +} + +export function PlatformGrid({title, description, items}: PlatformGridProps): ReactNode { + return ( +
+ {renderSectionHeader(title, description)} +
+ {items.map(item => { + const hasFamily = hasText(item.family) + const hasSupport = item.support !== void 0 + const highlights = item.highlights ?? [] + const hasHighlights = highlights.length > 0 + + return ( +
+
+
+ {hasFamily ? {item.family} : null} +

{item.name}

+
+ {hasSupport ? renderBadge(item.support) : null} +
+

{item.description}

+ {hasHighlights + ? ( +
    + {highlights.map((highlight, index) =>
  • {highlight}
  • )} +
+ ) + : null} +
+ ) + })} +
+
+ ) +} + +export function CommandReference({title, description, items}: CommandReferenceProps): ReactNode { + return ( +
+ {renderSectionHeader(title, description)} +
+ + + + + + + + + + + + {items.map((item, index) => ( + + + + + + + + ))} + +
StackTaskCommandRuntimeNotes
+ {item.stack} + {item.task}{renderCommand(item.command)}{renderOptionalNode(item.runtime)}{renderOptionalNode(item.notes)}
+
+
+ ) +} diff --git a/doc/content/mcp/server-tools.mdx b/doc/content/mcp/server-tools.mdx index b670cadf..ef5a0f2d 100644 --- a/doc/content/mcp/server-tools.mdx +++ b/doc/content/mcp/server-tools.mdx @@ -27,12 +27,34 @@ status: stable ## 每个工具大致负责什么 -| 工具 | 职责 | -| --- | --- | -| `list_prompts` | 列出受管 prompt 记录及 zh/en/dist 状态 | -| `get_prompt` | 读取单个 prompt 的 source 与 dist 产物 | -| `upsert_prompt_src` | 更新 zh 或 en 源文件,不直接碰 dist | -| `apply_prompt_translation` | 写入外部生成的 en 或 dist 内容 | + ## `workspaceDir` 的作用 diff --git a/doc/content/technical-details/_meta.ts b/doc/content/technical-details/_meta.ts index 768826ee..38b7a67f 100644 --- a/doc/content/technical-details/_meta.ts +++ b/doc/content/technical-details/_meta.ts @@ -3,6 +3,7 @@ export default { 'architecture': '架构边界', 'pipeline': '同步管线', 'source-of-truth': '单一真源模型', + 'documentation-components': '文档组件', 'global-and-workspace-prompts': '全局与工作区 Prompt', 'skills': 'Skills', 'commands': 'Commands', diff --git a/doc/content/technical-details/documentation-components.mdx b/doc/content/technical-details/documentation-components.mdx new file mode 100644 index 00000000..d0a2aab0 --- /dev/null +++ b/doc/content/technical-details/documentation-components.mdx @@ -0,0 +1,160 @@ +--- +title: 文档组件 +description: 为 MDX 提供可复用的能力矩阵、系统支持表、平台卡片与命令参考组件。 +sidebarTitle: 文档组件 +status: stable +--- + +# 文档组件 + +`doc/` 现在可以直接在 MDX 里使用一组文档组件,不必继续手写大段 Markdown 表格。它们已经通过 `doc/mdx-components.tsx` 全局注册,所以任意 `.mdx` 页面都能直接写 JSX 标签。 + +## 1. 多工具功能矩阵 + + + +## 2. 多系统支持矩阵 + + + +## 3. 系统展示卡片 + + + +## 4. 技术栈命令参考 + + + +## 使用方式 + +在任意 MDX 页面中直接写: + +```tsx + + + + +``` + +如果后面还要继续扩展,建议沿着这个方向做,而不是回到散乱的原生 Markdown 表格: + +- 表格类信息继续复用 `FeatureMatrix`、`SupportMatrix`、`CommandReference` +- 偏展示型内容复用 `PlatformGrid` +- 需要完全自由布局时,再单独加新的卡片或对比组件 diff --git a/doc/mdx-components.tsx b/doc/mdx-components.tsx index 720b3f5d..b43c3c4f 100644 --- a/doc/mdx-components.tsx +++ b/doc/mdx-components.tsx @@ -1,6 +1,7 @@ import type {ComponentPropsWithoutRef, ReactElement, ReactNode} from 'react' import {useMDXComponents as getDocsMDXComponents} from 'nextra-theme-docs' import {isValidElement} from 'react' +import {CommandReference, FeatureMatrix, PlatformGrid, SupportMatrix} from './components/doc-widgets' import {Mermaid} from './components/mermaid' const docsComponents = getDocsMDXComponents() @@ -45,7 +46,11 @@ function MermaidPre(props: MermaidPreProps) { export function useMDXComponents(components: MDXComponents = {}): MDXComponents { return { ...docsComponents, + CommandReference, + FeatureMatrix, Mermaid, + PlatformGrid, + SupportMatrix, pre: MermaidPre, ...components } From fc68e037ea228f527ca28f833f254c1adf9c5157 Mon Sep 17 00:00:00 2001 From: TrueNine Date: Wed, 25 Mar 2026 21:40:05 +0800 Subject: [PATCH 2/4] fix(doc): force dark theme on first paint --- doc/app/docs/layout.tsx | 2 ++ doc/app/globals.css | 64 ++++++++++++++++++++++------------------- doc/app/layout.tsx | 12 ++++---- 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/doc/app/docs/layout.tsx b/doc/app/docs/layout.tsx index 172a0dd5..786d3d7a 100644 --- a/doc/app/docs/layout.tsx +++ b/doc/app/docs/layout.tsx @@ -57,6 +57,7 @@ export default async function DocsLayout({children}: {readonly children: ReactNo link: siteConfig.issueUrl, labels: 'documentation' }} + darkMode={false} sidebar={{ autoCollapse: false, defaultMenuCollapseLevel: 99, @@ -77,6 +78,7 @@ export default async function DocsLayout({children}: {readonly children: ReactNo attribute: 'class', defaultTheme: 'dark', disableTransitionOnChange: true, + forcedTheme: 'dark', storageKey: 'memory-sync-docs-theme' }} > diff --git a/doc/app/globals.css b/doc/app/globals.css index b6a323a9..9a3776fb 100644 --- a/doc/app/globals.css +++ b/doc/app/globals.css @@ -1,34 +1,7 @@ -:root { - --nextra-content-width: 1380px; - --page-bg: #ffffff; - --page-fg: #111111; - --page-fg-soft: #5f6670; - --page-fg-muted: #7b818b; - --surface: rgba(255, 255, 255, 0.9); - --surface-strong: #ffffff; - --surface-muted: #f6f7f8; - --surface-subtle: #f3f4f6; - --surface-elevated: #f8f9fb; - --surface-overlay: rgba(255, 255, 255, 0.94); - --surface-border: rgba(17, 17, 17, 0.08); - --surface-border-strong: rgba(17, 17, 17, 0.14); - --surface-separator: rgba(17, 17, 17, 0.06); - --surface-highlight: rgba(17, 17, 17, 0.035); - --surface-highlight-strong: rgba(17, 17, 17, 0.055); - --inline-code-bg: rgba(17, 17, 17, 0.035); - --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05); - --shadow-md: 0 12px 40px rgba(15, 23, 42, 0.06); - --hero-glow: radial-gradient(circle at top, rgba(0, 0, 0, 0.06), transparent 58%); - --page-gradient: - radial-gradient(circle at top, rgba(0, 0, 0, 0.04), transparent 30%), - linear-gradient(180deg, #ffffff 0%, #fbfbfc 100%); - --button-primary-bg: #111111; - --button-primary-fg: #ffffff; - --button-secondary-bg: rgba(255, 255, 255, 0.82); - --button-secondary-fg: #111111; -} - +:root, html.dark { + color-scheme: dark; + --nextra-content-width: 1380px; --page-bg: #0b0c10; --page-fg: #fafafa; --page-fg-soft: #b8bec7; @@ -57,6 +30,37 @@ html.dark { --button-secondary-fg: #fafafa; } +html.light { + color-scheme: light; + --nextra-content-width: 1380px; + --page-bg: #ffffff; + --page-fg: #111111; + --page-fg-soft: #5f6670; + --page-fg-muted: #7b818b; + --surface: rgba(255, 255, 255, 0.9); + --surface-strong: #ffffff; + --surface-muted: #f6f7f8; + --surface-subtle: #f3f4f6; + --surface-elevated: #f8f9fb; + --surface-overlay: rgba(255, 255, 255, 0.94); + --surface-border: rgba(17, 17, 17, 0.08); + --surface-border-strong: rgba(17, 17, 17, 0.14); + --surface-separator: rgba(17, 17, 17, 0.06); + --surface-highlight: rgba(17, 17, 17, 0.035); + --surface-highlight-strong: rgba(17, 17, 17, 0.055); + --inline-code-bg: rgba(17, 17, 17, 0.035); + --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05); + --shadow-md: 0 12px 40px rgba(15, 23, 42, 0.06); + --hero-glow: radial-gradient(circle at top, rgba(0, 0, 0, 0.06), transparent 58%); + --page-gradient: + radial-gradient(circle at top, rgba(0, 0, 0, 0.04), transparent 30%), + linear-gradient(180deg, #ffffff 0%, #fbfbfc 100%); + --button-primary-bg: #111111; + --button-primary-fg: #ffffff; + --button-secondary-bg: rgba(255, 255, 255, 0.82); + --button-secondary-fg: #111111; +} + *, *::before, *::after { diff --git a/doc/app/layout.tsx b/doc/app/layout.tsx index 0fb0d53d..021acfb3 100644 --- a/doc/app/layout.tsx +++ b/doc/app/layout.tsx @@ -9,14 +9,16 @@ const docsThemeStorageKey = 'memory-sync-docs-theme' const docsThemeBootstrapScript = ` try { const storageKey = '${docsThemeStorageKey}'; - const storedTheme = window.localStorage.getItem(storageKey); - const normalizedTheme = storedTheme === 'light' ? 'light' : 'dark'; + const root = document.documentElement; + const normalizedTheme = 'dark'; - if (storedTheme !== normalizedTheme) { + if (window.localStorage.getItem(storageKey) !== normalizedTheme) { window.localStorage.setItem(storageKey, normalizedTheme); } - document.documentElement.classList.toggle('dark', normalizedTheme === 'dark'); + root.classList.remove('light', 'dark'); + root.classList.add(normalizedTheme); + root.style.colorScheme = normalizedTheme; } catch {} ` @@ -62,7 +64,7 @@ export const metadata: Metadata = { export default function RootLayout({children}: {readonly children: React.ReactNode}) { return ( - +