-
Notifications
You must be signed in to change notification settings - Fork 6k
Expand file tree
/
Copy pathlayout.tsx
More file actions
165 lines (159 loc) · 5.92 KB
/
Copy pathlayout.tsx
File metadata and controls
165 lines (159 loc) · 5.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import type { Metadata, Viewport } from "next";
import Script from "next/script";
import { Inter, Geist_Mono, Source_Serif_4 } from "next/font/google";
import { ThemeProvider } from "@/components/theme-provider";
import { Toaster } from "@multica/ui/components/ui/sonner";
import { cn } from "@multica/ui/lib/utils";
import { WebProviders } from "@/components/web-providers";
import type { SupportedLocale } from "@multica/core/i18n";
import { RESOURCES } from "@multica/views/locales";
import { getRequestLocale } from "@/lib/request-locale";
import {
resolveBrowserApiBaseUrl,
resolveBrowserWsUrl,
} from "@/config/runtime-urls";
import "./globals.css";
// Inter is the Latin UI face. next/font produces a hashed family (`__Inter_xxx`)
// plus a synthetic size-adjusted fallback face to prevent FOUT layout shift —
// both are exposed under the `--font-inter` CSS variable.
//
// The full `--font-sans` stack (Inter + the per-locale CJK fallback chain) is
// assembled in static CSS in ./globals.css, not here: it must be overridable per
// `<html lang>` (Japanese Kanji are Han ideographs and need a Japanese-first CJK
// stack), and a hashed family name can only be referenced from CSS via a variable.
// Keeping the CJK chain in CSS also keeps it CSP-safe and in sync with the desktop
// app, which defines the same chain in apps/desktop/src/renderer/src/globals.css.
//
// Italic is loaded explicitly: `style` defaults to `["normal"]`, and without a real
// italic face the ~20 semantic italic labels (chat empty states, model-picker's
// "Managed by runtime", dashboard/squad placeholders) plus every markdown <em> and
// blockquote rendered as browser-synthesized oblique. Keep in sync with desktop's
// `@fontsource-variable/inter/wght-italic.css` import.
const inter = Inter({
subsets: ["latin"],
style: ["normal", "italic"],
variable: "--font-inter",
});
// Mono font has no explicit CJK fallback: CJK chars in code blocks are inherently
// non-aligned with a mono grid (Chinese is proportional), so listing CJK fonts
// here would falsely signal alignment guarantees. Browser default fallback handles
// the rare mixed case correctly.
const geistMono = Geist_Mono({
subsets: ["latin"],
variable: "--font-mono",
fallback: ["ui-monospace", "SFMono-Regular", "Menlo", "Consolas", "monospace"],
});
// Editorial serif used for onboarding headlines. Italic support for h1 em
// accents (e.g. "...on one shared board."). Only loaded on routes that
// render the font; layout-shift-prevention handled by next/font's synthetic
// fallback metrics, same as Inter.
const sourceSerif = Source_Serif_4({
subsets: ["latin"],
style: ["normal", "italic"],
variable: "--font-serif",
fallback: [
"ui-serif",
"Iowan Old Style",
"Apple Garamond",
"Baskerville",
"Times New Roman",
"serif",
],
});
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "#ffffff" },
{ media: "(prefers-color-scheme: dark)", color: "#05070b" },
],
};
export const metadata: Metadata = {
metadataBase: new URL("https://www.multica.ai"),
title: {
default: "Project workspace",
template: "%s",
},
description:
"Open-source platform that turns coding agents into real teammates. Assign tasks, track progress, compound skills.",
icons: {
icon: [{ url: "/favicon.svg", type: "image/svg+xml" }],
shortcut: ["/favicon.svg"],
},
openGraph: {
type: "website",
siteName: "Multica",
locale: "en_US",
},
twitter: {
card: "summary_large_image",
site: "@multica_hq",
creator: "@multica_hq",
},
alternates: {
canonical: "/",
},
robots: {
index: true,
follow: true,
},
};
// HTML lang attribute uses BCP-47 region tags that screen readers and font
// stacks recognize widely. i18next keeps `zh-Hans` as its internal locale
// (script subtag is what we actually translate against), but the html element
// expects a region-flavoured tag for accessibility tooling and CJK fallback.
const HTML_LANG: Record<SupportedLocale, string> = {
en: "en",
"zh-Hans": "zh-CN",
ko: "ko-KR",
ja: "ja-JP",
};
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const locale = await getRequestLocale();
const resources = { [locale]: RESOURCES[locale] };
const apiBaseUrl = resolveBrowserApiBaseUrl(process.env);
const wsUrl = resolveBrowserWsUrl(process.env);
return (
<html
lang={HTML_LANG[locale]}
suppressHydrationWarning
className={cn("antialiased font-sans h-full", inter.variable, geistMono.variable, sourceSerif.variable)}
>
<body className="h-full overflow-hidden">
{/*
react-grab: dev-only element inspector. Hold ⌘C (Mac) / Ctrl+C and click
any element to copy its source path + line + component stack for pasting
to an AI. Opt-in per developer: only loads when VITE_REACT_GRAB is set in
a local, gitignored apps/web/.env.local — it never activates for anyone
else. Both guards are read server-side, so the <Script> is omitted from
the HTML entirely unless you opted in. The VITE_ prefix is shared with the
desktop renderer (apps/desktop/src/renderer/src/main.tsx), where Vite only
exposes VITE_-prefixed vars to client code, so one var name covers both
apps. See https://www.react-grab.com/
*/}
{process.env.NODE_ENV === "development" && process.env.VITE_REACT_GRAB && (
<Script
src="//unpkg.com/react-grab/dist/index.global.js"
crossOrigin="anonymous"
strategy="beforeInteractive"
/>
)}
<ThemeProvider>
<WebProviders
locale={locale}
resources={resources}
apiBaseUrl={apiBaseUrl}
wsUrl={wsUrl}
>
{children}
</WebProviders>
<Toaster />
</ThemeProvider>
</body>
</html>
);
}