@objectdocs/site 0.2.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # @objectdocs/site
2
+
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Bug fixes and improvements for ObjectDocs
8
+
9
+ ## 0.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - Initial release of ObjectDocs - A modern documentation engine built on Next.js 14 and Fumadocs
14
+
15
+ ## 0.1.0
16
+
17
+ ### Minor Changes
18
+
19
+ - Initial release of ObjectDocs
20
+
21
+ - Modern documentation engine built on Next.js 14 and Fumadocs
22
+ - Metadata-driven architecture with configuration as code
23
+ - Support for low-code component embedding
24
+ - Enterprise-grade UI with dark mode support
25
+ - Multi-product documentation support
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # @objectdocs/site
2
+
3
+ The Next.js application foundation for the ObjectStack documentation.
4
+
5
+ ## Overview
6
+
7
+ This package provides the actual Next.js application structure, configuration, and UI components used to render the documentation. It is designed to be encapsulated and run via the `@objectdocs/cli`.
8
+
9
+ ## Tech Stack
10
+
11
+ - **Concepts**: Next.js 16 (App Router)
12
+ - **Documentation**: Fumadocs
13
+ - **Styling**: Tailwind CSS
14
+ - **Deployment**: Static Export (`output: 'export'`)
15
+
16
+ ## Development
17
+
18
+ Typically you won't run this package directly. Instead, you should use the CLI from the root workspace:
19
+
20
+ ```bash
21
+ pnpm dev
22
+ # or
23
+ pnpm objectdocs dev
24
+ ```
25
+
26
+ However, for internal development of the site theme or logic, you can run:
27
+
28
+ ```bash
29
+ cd packages/site
30
+ pnpm dev
31
+ ```
32
+
33
+ ## Structure
34
+
35
+ - `app/`: Next.js App Router pages and layouts.
36
+ - `lib/`: Utility functions (i18n, etc).
37
+ - `source.config.ts`: Fumadocs configuration.
38
+ - `next.config.mjs`: Next.js configuration (configured for static export).
39
+
40
+ ## Build output
41
+
42
+ When built, the static assets are generated in the `out/` directory.
@@ -0,0 +1,84 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { source } from '@/lib/source';
10
+ import type { Metadata } from 'next';
11
+ import { DocsPage, DocsBody } from 'fumadocs-ui/page';
12
+ import { notFound } from 'next/navigation';
13
+ import { siteConfig } from '@/lib/site-config';
14
+ import defaultComponents from 'fumadocs-ui/mdx';
15
+ import { Callout } from 'fumadocs-ui/components/callout';
16
+ import { Card, Cards } from 'fumadocs-ui/components/card';
17
+ import { Steps, Step } from 'fumadocs-ui/components/steps';
18
+
19
+ const components = {
20
+ ...defaultComponents,
21
+ Callout,
22
+ Card,
23
+ Cards,
24
+ Steps,
25
+ Step,
26
+ };
27
+
28
+ interface PageProps {
29
+ params: Promise<{
30
+ lang: string;
31
+ slug?: string[];
32
+ }>;
33
+ }
34
+
35
+ export default async function Page({ params }: PageProps) {
36
+ const { lang, slug = [] } = await params;
37
+
38
+ const page = source.getPage(slug, lang);
39
+
40
+ if (!page) {
41
+ notFound();
42
+ }
43
+
44
+ const MDX = page.data.body as any;
45
+
46
+ return (
47
+ <DocsPage
48
+ toc={page.data.toc as any}
49
+ full={page.data.full as any}
50
+ lastUpdate={siteConfig.page.showLastUpdate ? (page.data as any).lastModified : undefined}
51
+ tableOfContent={{
52
+ enabled: siteConfig.layout.toc.enabled,
53
+ style: siteConfig.layout.toc.depth > 2 ? 'clerk' : 'normal',
54
+ }}
55
+ editOnGithub={siteConfig.page.showEditLink ? {
56
+ owner: siteConfig.page.repoBaseUrl.split('/')[3],
57
+ repo: siteConfig.page.repoBaseUrl.split('/')[4],
58
+ sha: 'main', // Defaulting to main, could be extracted
59
+ path: siteConfig.page.repoBaseUrl.split('/').slice(7).join('/') // simplistic parsing
60
+ } : undefined}
61
+ >
62
+ <DocsBody>
63
+ <MDX components={components} />
64
+ </DocsBody>
65
+ </DocsPage>
66
+ );
67
+ }
68
+
69
+
70
+ export async function generateStaticParams() {
71
+ return source.generateParams();
72
+ }
73
+
74
+ export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
75
+ const { lang, slug = [] } = await params;
76
+ const page = source.getPage(slug, lang);
77
+
78
+ if (!page) notFound();
79
+
80
+ return {
81
+ title: page.data.title,
82
+ description: page.data.description,
83
+ };
84
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { source } from '@/lib/source';
10
+ import { DocsLayout } from 'fumadocs-ui/layouts/docs';
11
+ import type { ReactNode } from 'react';
12
+ import { baseOptions } from '@/app/layout.config';
13
+ import { siteConfig } from '@/lib/site-config';
14
+
15
+ export default async function Layout({
16
+ params,
17
+ children,
18
+ }: {
19
+ params: Promise<{ lang: string }>;
20
+ children: ReactNode;
21
+ }) {
22
+ const { lang } = await params;
23
+
24
+ return (
25
+ <DocsLayout
26
+ tree={source.getPageTree(lang)}
27
+ {...baseOptions}
28
+ sidebar={{
29
+ enabled: siteConfig.layout.sidebar.enabled,
30
+ prefetch: siteConfig.layout.sidebar.prefetch,
31
+ collapsible: siteConfig.layout.sidebar.collapsible,
32
+ defaultOpenLevel: siteConfig.layout.sidebar.defaultOpenLevel,
33
+ footer: siteConfig.layout.sidebar.footer ? (
34
+ <div className="p-4 text-xs text-muted-foreground border-t">
35
+ {siteConfig.layout.sidebar.footer.html ? (
36
+ <div dangerouslySetInnerHTML={{ __html: siteConfig.layout.sidebar.footer.html }} />
37
+ ) : (
38
+ siteConfig.layout.sidebar.footer.text
39
+ )}
40
+ </div>
41
+ ) : undefined,
42
+ tabs: siteConfig.layout.sidebar.tabs && siteConfig.layout.sidebar.tabs.length > 0 ?
43
+ siteConfig.layout.sidebar.tabs.map(tab => ({
44
+ title: tab.title,
45
+ url: tab.url,
46
+ description: tab.description,
47
+ })) : undefined,
48
+ banner: siteConfig.layout.sidebar.banner ? (
49
+ <div className="-mx-2 -mt-2 rounded-lg border bg-card p-4 transition-colors hover:bg-accent/50 text-card-foreground text-sm">
50
+ <a href={siteConfig.layout.sidebar.banner.url} className="block font-medium">
51
+ {siteConfig.layout.sidebar.banner.text}
52
+ </a>
53
+ </div>
54
+ ) : undefined
55
+ }}
56
+ >
57
+ {children}
58
+ </DocsLayout>
59
+ );
60
+ }
61
+
@@ -0,0 +1,35 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import 'fumadocs-ui/style.css';
10
+ import { RootProvider } from 'fumadocs-ui/provider/next';
11
+ import { defineI18nUI } from 'fumadocs-ui/i18n';
12
+ import { i18n } from '@/lib/i18n';
13
+ import { getTranslations } from '@/lib/translations';
14
+
15
+
16
+ const { provider } = defineI18nUI(i18n, {
17
+ translations: getTranslations(),
18
+ });
19
+
20
+ export default async function Layout({ params, children }: LayoutProps<'/[lang]'>) {
21
+ const { lang } = await params;
22
+ return (
23
+ <html lang={lang} suppressHydrationWarning>
24
+ <body
25
+ style={{
26
+ display: 'flex',
27
+ flexDirection: 'column',
28
+ minHeight: '100vh',
29
+ }}
30
+ >
31
+ <RootProvider i18n={provider(lang)}>{children}</RootProvider>
32
+ </body>
33
+ </html>
34
+ );
35
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { redirect, notFound } from 'next/navigation';
10
+
11
+
12
+ export default async function LangPage({
13
+ params,
14
+ }: {
15
+ params: Promise<{ lang: string }>;
16
+ }) {
17
+ const { lang } = await params;
18
+
19
+ redirect(`/${lang}/docs`);
20
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { source } from '@/lib/source';
10
+ import { createSearchAPI } from 'fumadocs-core/search/server';
11
+
12
+ export const { GET } = createSearchAPI('advanced', {
13
+ indexes: source.getPages().map((page) => ({
14
+ title: page.data.title,
15
+ description: page.data.description,
16
+ url: page.url,
17
+ id: page.url,
18
+ structuredData: page.data.structuredData,
19
+ })),
20
+ });
@@ -0,0 +1,49 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { type BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
10
+ import { siteConfig } from '@/lib/site-config';
11
+ import Image from 'next/image';
12
+
13
+ export const baseOptions: BaseLayoutProps = {
14
+ nav: {
15
+ title: (
16
+ <div className="flex items-center gap-2">
17
+ {siteConfig.branding.logo.light && (
18
+ <Image
19
+ src={siteConfig.branding.logo.light}
20
+ alt={siteConfig.branding.logo.text || 'Logo'}
21
+ width={30}
22
+ height={30}
23
+ className={siteConfig.branding.logo.dark ? 'dark:hidden' : ''}
24
+ />
25
+ )}
26
+ {siteConfig.branding.logo.dark && (
27
+ <Image
28
+ src={siteConfig.branding.logo.dark}
29
+ alt={siteConfig.branding.logo.text || 'Logo'}
30
+ width={30}
31
+ height={30}
32
+ className="hidden dark:block"
33
+ />
34
+ )}
35
+ <span className="font-bold">{siteConfig.branding.logo.text || 'ObjectStack'}</span>
36
+ </div>
37
+ ),
38
+ transparentMode: siteConfig.layout.navbar.transparentMode,
39
+ },
40
+ links: siteConfig.layout.navbar.links.map(link => ({
41
+ text: link.text,
42
+ url: link.url,
43
+ active: link.active || 'nested-url',
44
+ external: link.external,
45
+ })),
46
+ githubUrl: siteConfig.layout.navbar.socials.find(s => s.platform === 'github')?.url,
47
+ i18n: siteConfig.i18n.enabled,
48
+ };
49
+
package/app/layout.tsx ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import 'fumadocs-ui/style.css';
10
+ import { RootProvider } from 'fumadocs-ui/provider/next';
11
+ import { defineI18nUI } from 'fumadocs-ui/i18n';
12
+ import { i18n } from '@/lib/i18n';
13
+ import { getTranslations } from '@/lib/translations';
14
+
15
+
16
+ const { provider } = defineI18nUI(i18n, {
17
+ translations: getTranslations(),
18
+ });
19
+
20
+ export default function Layout({ children }: { children: React.ReactNode }) {
21
+ return (
22
+ <html lang="en" suppressHydrationWarning>
23
+ <body
24
+ style={{
25
+ display: 'flex',
26
+ flexDirection: 'column',
27
+ minHeight: '100vh',
28
+ }}
29
+ >
30
+ {children}
31
+ </body>
32
+ </html>
33
+ );
34
+ }
package/app/page.tsx ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { redirect } from 'next/navigation';
10
+
11
+ /**
12
+ * Root page - redirects are handled by proxy.ts middleware
13
+ * This page should never actually render as the middleware intercepts and redirects
14
+ * But Next.js requires a page component for the route to be recognized
15
+ */
16
+ export default function RootPage() {
17
+ // Fallback redirect if middleware didn't handle it
18
+ redirect('/en/docs');
19
+ }
package/docs.site.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "meta": {
3
+ "title": "ObjectDocs",
4
+ "description": "The Metadata-Driven Documentation Engine for the Low-Code Era.",
5
+ "url": "https://docs.objectstack.ai",
6
+ "favicon": "/favicon.ico"
7
+ },
8
+ "i18n": {
9
+ "enabled": true,
10
+ "defaultLanguage": "en",
11
+ "languages": ["en", "cn"
12
+ ]
13
+ },
14
+ "branding": {
15
+ "logo": {
16
+ "text": "ObjectDocs",
17
+ "light": "/logo.svg",
18
+ "dark": "/logo.svg"
19
+ },
20
+ "theme": {
21
+ "accentColor": "blue",
22
+ "radius": "0.5rem"
23
+ }
24
+ },
25
+ "layout": {
26
+ "navbar": {
27
+ "enabled": true,
28
+ "transparentMode": "top",
29
+ "links": [
30
+ {
31
+ "text": "Home",
32
+ "url": "https://www.objectstack.ai",
33
+ "external": true
34
+ }
35
+ ],
36
+ "socials": [
37
+ { "platform": "github", "url": "https://github.com/objectstack-ai" }
38
+ ]
39
+ },
40
+ "sidebar": {
41
+ "enabled": true,
42
+ "prefetch": true,
43
+ "defaultOpenLevel": 1,
44
+ "collapsible": true,
45
+ "tabs": []
46
+ },
47
+ "toc": {
48
+ "enabled": true,
49
+ "depth": 3
50
+ },
51
+ "footer": {
52
+ "enabled": false,
53
+ "copyright": "© 2026 ObjectStack Inc."
54
+ }
55
+ },
56
+ "page": {
57
+ "showLastUpdate": true,
58
+ "showEditLink": true,
59
+ "repoBaseUrl": "https://github.com/objectstack-ai/objectdocs"
60
+ },
61
+ "content": {
62
+ "math": false,
63
+ "imageZoom": true,
64
+ "codeBlock": {
65
+ "theme": "vesper",
66
+ "showLineNumbers": true
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ export function isObject(item: any): item is Record<string, any> {
10
+ return (item && typeof item === 'object' && !Array.isArray(item));
11
+ }
12
+
13
+ export function deepMerge<T extends Record<string, any>>(target: T, source: Record<string, any>): T {
14
+ const output = { ...target };
15
+
16
+ if (isObject(target) && isObject(source)) {
17
+ Object.keys(source).forEach(key => {
18
+ if (isObject(source[key])) {
19
+ if (!(key in target)) {
20
+ Object.assign(output, { [key]: source[key] });
21
+ } else {
22
+ (output as any)[key] = deepMerge(target[key], source[key]);
23
+ }
24
+ } else {
25
+ Object.assign(output, { [key]: source[key] });
26
+ }
27
+ });
28
+ }
29
+
30
+ return output;
31
+ }
package/lib/i18n.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { siteConfig } from './site-config';
10
+ import { defineI18n } from 'fumadocs-core/i18n';
11
+
12
+ export const i18n = defineI18n({
13
+ defaultLanguage: siteConfig.i18n.defaultLanguage,
14
+ languages: siteConfig.i18n.languages,
15
+ parser: 'dot',
16
+ });
@@ -0,0 +1,154 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { deepMerge } from './deep-merge';
10
+ import objectDocsConfig from '@/docs.site.json';
11
+
12
+ export interface SiteConfig {
13
+ meta: {
14
+ title: string;
15
+ description: string;
16
+ url: string;
17
+ favicon: string;
18
+ };
19
+ i18n: {
20
+ enabled: boolean;
21
+ defaultLanguage: string;
22
+ languages: Array<string>;
23
+ };
24
+ build?: {
25
+ output?: 'export' | 'standalone' | undefined;
26
+ };
27
+ branding: {
28
+ logo: {
29
+ light?: string;
30
+ dark?: string;
31
+ text?: string;
32
+ };
33
+ theme: {
34
+ accentColor: string;
35
+ radius: string;
36
+ };
37
+ };
38
+ layout: {
39
+ navbar: {
40
+ enabled: boolean;
41
+ transparentMode?: 'none' | 'top' | 'always';
42
+ links: Array<{
43
+ text: string;
44
+ url: string;
45
+ active?: 'url' | 'nested-url';
46
+ external?: boolean;
47
+ }>;
48
+ socials: Array<{ platform: string; url: string }>;
49
+ };
50
+ sidebar: {
51
+ enabled?: boolean;
52
+ defaultOpenLevel: number;
53
+ collapsible: boolean;
54
+ prefetch?: boolean;
55
+ banner?: {
56
+ text: string;
57
+ url: string;
58
+ };
59
+ footer?: {
60
+ text?: string;
61
+ html?: string;
62
+ };
63
+ tabs?: Array<{
64
+ title: string;
65
+ url: string;
66
+ description?: string;
67
+ }>;
68
+ };
69
+ toc: {
70
+ enabled: boolean;
71
+ depth: number;
72
+ };
73
+ footer: {
74
+ copyright: string;
75
+ };
76
+ };
77
+ page: {
78
+ showLastUpdate: boolean;
79
+ showEditLink: boolean;
80
+ repoBaseUrl: string;
81
+ };
82
+ content: {
83
+ math: boolean;
84
+ imageZoom: boolean;
85
+ codeBlock: {
86
+ theme: string;
87
+ showLineNumbers: boolean;
88
+ };
89
+ };
90
+ }
91
+
92
+ const defaultConfig: SiteConfig = {
93
+ meta: {
94
+ title: 'ObjectStack ',
95
+ description: 'Documentation',
96
+ url: 'https://objectstack.com',
97
+ favicon: '/favicon.ico',
98
+ },
99
+ i18n: {
100
+ enabled: true,
101
+ defaultLanguage: 'en',
102
+ languages: ["en", "cn"
103
+ ],
104
+ },
105
+ branding: {
106
+ logo: {
107
+ text: 'ObjectStack',
108
+ },
109
+ theme: {
110
+ accentColor: 'blue',
111
+ radius: '0.5rem',
112
+ },
113
+ },
114
+ layout: {
115
+ navbar: {
116
+ enabled: true,
117
+ transparentMode: 'top',
118
+ links: [],
119
+ socials: [],
120
+ },
121
+ sidebar: {
122
+ enabled: true,
123
+ defaultOpenLevel: 1,
124
+ collapsible: true,
125
+ prefetch: true,
126
+ },
127
+ toc: {
128
+ enabled: true,
129
+ depth: 3,
130
+ },
131
+ footer: {
132
+ copyright: '© 2026',
133
+ },
134
+ },
135
+ page: {
136
+ showLastUpdate: true,
137
+ showEditLink: true,
138
+ repoBaseUrl: '',
139
+ },
140
+ content: {
141
+ math: false,
142
+ imageZoom: true,
143
+ codeBlock: {
144
+ theme: 'vesper',
145
+ showLineNumbers: true,
146
+ },
147
+ },
148
+ };
149
+
150
+ export function getSiteConfig(): SiteConfig {
151
+ return deepMerge(defaultConfig, objectDocsConfig);
152
+ }
153
+
154
+ export const siteConfig = getSiteConfig();
package/lib/source.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { loader } from 'fumadocs-core/source';
10
+ import { docs, meta } from '@/.source/server';
11
+ import { toFumadocsSource } from 'fumadocs-mdx/runtime/server';
12
+ import { i18n } from './i18n';
13
+
14
+ const mainSource = toFumadocsSource(docs, meta);
15
+
16
+ export const source = loader({
17
+ baseUrl: '/docs',
18
+ source: {
19
+ files: [
20
+ ...mainSource.files,
21
+ ],
22
+ },
23
+ i18n,
24
+ });
@@ -0,0 +1,110 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { siteConfig } from './site-config';
10
+
11
+ /**
12
+ * Translation strings for different languages
13
+ * These are the UI strings used by fumadocs-ui
14
+ */
15
+ interface LanguageTranslations {
16
+ displayName: string;
17
+ toc?: string;
18
+ search?: string;
19
+ lastUpdate?: string;
20
+ searchNoResult?: string;
21
+ previousPage?: string;
22
+ nextPage?: string;
23
+ chooseLanguage?: string;
24
+ }
25
+
26
+ /**
27
+ * Default translations for supported languages
28
+ * Add new language translations here as needed
29
+ */
30
+ const defaultTranslations: Record<string, LanguageTranslations> = {
31
+ en: {
32
+ displayName: 'English',
33
+ },
34
+ cn: {
35
+ displayName: '简体中文',
36
+ toc: '目录',
37
+ search: '搜索文档',
38
+ lastUpdate: '最后更新于',
39
+ searchNoResult: '没有结果',
40
+ previousPage: '上一页',
41
+ nextPage: '下一页',
42
+ chooseLanguage: '选择语言',
43
+ },
44
+ ja: {
45
+ displayName: '日本語',
46
+ toc: '目次',
47
+ search: 'ドキュメントを検索',
48
+ lastUpdate: '最終更新',
49
+ searchNoResult: '結果がありません',
50
+ previousPage: '前のページ',
51
+ nextPage: '次のページ',
52
+ chooseLanguage: '言語を選択',
53
+ },
54
+ fr: {
55
+ displayName: 'Français',
56
+ toc: 'Table des matières',
57
+ search: 'Rechercher dans la documentation',
58
+ lastUpdate: 'Dernière mise à jour',
59
+ searchNoResult: 'Aucun résultat',
60
+ previousPage: 'Page précédente',
61
+ nextPage: 'Page suivante',
62
+ chooseLanguage: 'Choisir la langue',
63
+ },
64
+ de: {
65
+ displayName: 'Deutsch',
66
+ toc: 'Inhaltsverzeichnis',
67
+ search: 'Dokumentation durchsuchen',
68
+ lastUpdate: 'Zuletzt aktualisiert',
69
+ searchNoResult: 'Keine Ergebnisse',
70
+ previousPage: 'Vorherige Seite',
71
+ nextPage: 'Nächste Seite',
72
+ chooseLanguage: 'Sprache wählen',
73
+ },
74
+ es: {
75
+ displayName: 'Español',
76
+ toc: 'Tabla de contenidos',
77
+ search: 'Buscar documentación',
78
+ lastUpdate: 'Última actualización',
79
+ searchNoResult: 'Sin resultados',
80
+ previousPage: 'Página anterior',
81
+ nextPage: 'Página siguiente',
82
+ chooseLanguage: 'Elegir idioma',
83
+ },
84
+ };
85
+
86
+ /**
87
+ * Get translations for configured languages
88
+ * Returns only the translations for languages specified in docs.site.json
89
+ */
90
+ export function getTranslations(): Record<string, LanguageTranslations> {
91
+ const configuredLanguages = siteConfig.i18n.languages;
92
+ const translations: Record<string, LanguageTranslations> = {};
93
+
94
+ for (const lang of configuredLanguages) {
95
+ if (defaultTranslations[lang]) {
96
+ translations[lang] = defaultTranslations[lang];
97
+ } else {
98
+ // If no translation exists for a configured language, provide a minimal fallback
99
+ // Only log warning in development
100
+ if (process.env.NODE_ENV === 'development') {
101
+ console.warn(`Warning: No translations found for language "${lang}". Using minimal fallback.`);
102
+ }
103
+ translations[lang] = {
104
+ displayName: lang.toUpperCase(),
105
+ };
106
+ }
107
+ }
108
+
109
+ return translations;
110
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import defaultComponents from 'fumadocs-ui/mdx';
10
+ import type { MDXComponents } from 'mdx/types';
11
+ import { Steps, Step } from 'fumadocs-ui/components/steps';
12
+
13
+ export function useMDXComponents(components: MDXComponents): MDXComponents {
14
+ return {
15
+ ...defaultComponents,
16
+ Steps,
17
+ Step,
18
+ ...components,
19
+ };
20
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { createMDX } from 'fumadocs-mdx/next';
10
+
11
+ const withMDX = createMDX();
12
+
13
+ /** @type {import('next').NextConfig} */
14
+ const nextConfig = {
15
+ reactStrictMode: true,
16
+ distDir: '.next',
17
+ images: { unoptimized: true },
18
+ };
19
+
20
+ export default withMDX(nextConfig);
@@ -0,0 +1,69 @@
1
+ {
2
+ "meta": {
3
+ "title": "ObjectStack Docs",
4
+ "description": "Enterprise-grade low-code platform documentation",
5
+ "url": "https://docs.objectstack.ai",
6
+ "favicon": "/favicon.ico"
7
+ },
8
+ "i18n": {
9
+ "enabled": true,
10
+ "defaultLanguage": "en",
11
+ "languages": ["en", "cn"
12
+ ]
13
+ },
14
+ "branding": {
15
+ "logo": {
16
+ "text": "ObjectStack",
17
+ "light": "/logo.svg",
18
+ "dark": "/logo.svg"
19
+ },
20
+ "theme": {
21
+ "accentColor": "blue",
22
+ "radius": "0.5rem"
23
+ }
24
+ },
25
+ "layout": {
26
+ "navbar": {
27
+ "enabled": true,
28
+ "transparentMode": "top",
29
+ "links": [
30
+ {
31
+ "text": "Home",
32
+ "url": "https://www.objectstack.ai",
33
+ "external": true
34
+ }
35
+ ],
36
+ "socials": [
37
+ { "platform": "github", "url": "https://github.com/objectstack-ai/" }
38
+ ]
39
+ },
40
+ "sidebar": {
41
+ "enabled": true,
42
+ "prefetch": true,
43
+ "defaultOpenLevel": 1,
44
+ "collapsible": true,
45
+ "tabs": []
46
+ },
47
+ "toc": {
48
+ "enabled": true,
49
+ "depth": 3
50
+ },
51
+ "footer": {
52
+ "enabled": false,
53
+ "copyright": "© 2026 ObjectStack Inc."
54
+ }
55
+ },
56
+ "page": {
57
+ "showLastUpdate": true,
58
+ "showEditLink": true,
59
+ "repoBaseUrl": "https://github.com/objectstack-ai/docs"
60
+ },
61
+ "content": {
62
+ "math": false,
63
+ "imageZoom": true,
64
+ "codeBlock": {
65
+ "theme": "vesper",
66
+ "showLineNumbers": true
67
+ }
68
+ }
69
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@objectdocs/site",
3
+ "version": "0.2.1",
4
+ "dependencies": {
5
+ "@fumadocs/ui": "^16.4.7",
6
+ "@tailwindcss/postcss": "^4.1.18",
7
+ "@types/mdx": "^2.0.13",
8
+ "@types/node": "^25.0.8",
9
+ "@types/react": "^19.2.8",
10
+ "autoprefixer": "^10.4.23",
11
+ "client-only": "^0.0.1",
12
+ "dotenv": "^16.4.5",
13
+ "lucide-react": "^0.562.0",
14
+ "fumadocs-core": "^16.4.7",
15
+ "fumadocs-mdx": "^14.2.5",
16
+ "fumadocs-ui": "^16.4.7",
17
+ "next": "^16.1.2",
18
+ "openai": "^4.0.0",
19
+ "postcss": "^8.5.6",
20
+ "react": "^19.2.3",
21
+ "react-dom": "^19.2.3",
22
+ "remark-directive": "^4.0.0",
23
+ "server-only": "^0.0.1",
24
+ "tailwindcss": "^4.1.18",
25
+ "typescript": "^5.9.3"
26
+ },
27
+ "scripts": {
28
+ "dev": "next dev",
29
+ "build": "next build",
30
+ "start": "next start",
31
+ "lint": "next lint",
32
+ "test": "echo \"No test specified\" && exit 0"
33
+ }
34
+ }
package/proxy.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { createI18nMiddleware } from 'fumadocs-core/i18n/middleware';
10
+ import { i18n } from '@/lib/i18n';
11
+ import { NextResponse, NextRequest, NextFetchEvent } from 'next/server';
12
+
13
+ // Create fumadocs middleware
14
+ const fumadocsMiddleware = createI18nMiddleware(i18n);
15
+
16
+ export default function proxy(request: NextRequest, event: NextFetchEvent) {
17
+ const path = request.nextUrl.pathname;
18
+
19
+ // Handle root path separately with custom language detection
20
+ if (path === '/') {
21
+ const acceptLanguage = request.headers.get('accept-language') || '';
22
+
23
+ // Simple language detection: check if zh is in Accept-Language
24
+ if (acceptLanguage.toLowerCase().includes('zh')) {
25
+ // Redirect to Chinese docs
26
+ const url = request.nextUrl.clone();
27
+ url.pathname = '/cn/docs';
28
+ return NextResponse.redirect(url);
29
+ } else {
30
+ // Redirect to default language (English)
31
+ const url = request.nextUrl.clone();
32
+ url.pathname = '/en/docs';
33
+ return NextResponse.redirect(url);
34
+ }
35
+ }
36
+
37
+ // For all other paths, pass through to fumadocs middleware
38
+ return fumadocsMiddleware(request, event);
39
+ }
40
+
41
+ export const config = {
42
+ // Matcher ignoring `/_next/` and `/api/`
43
+ matcher: ['/((?!api|_next/static|_next/image|favicon.ico|logo.svg).*)'],
44
+ };
File without changes
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" width="512" height="512">
2
+ <defs>
3
+ <linearGradient id="logo-gradient" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#818cf8" />
5
+ <stop offset="100%" stop-color="#c084fc" />
6
+ </linearGradient>
7
+ </defs>
8
+ <path d="M12 22.5L2 17.5L12 12.5L22 17.5L12 22.5Z" fill="url(#logo-gradient)" fill-opacity="0.6" />
9
+ <path d="M12 17.5L2 12.5L12 7.5L22 12.5L12 17.5Z" fill="url(#logo-gradient)" fill-opacity="0.8" />
10
+ <path d="M12 12.5L2 7.5L12 2.5L22 7.5L12 12.5Z" fill="url(#logo-gradient)" fill-opacity="1" />
11
+ </svg>
@@ -0,0 +1,43 @@
1
+ /**
2
+ * ObjectDocs
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import { defineConfig, defineDocs } from 'fumadocs-mdx/config';
10
+ import { siteConfig } from './lib/site-config';
11
+ import path from 'node:path';
12
+ import fs from 'node:fs';
13
+ import remarkDirective from 'remark-directive';
14
+ import { remarkDirectiveAdmonition } from 'fumadocs-core/mdx-plugins';
15
+
16
+ function resolveContentDir(dir: string) {
17
+ if (process.env.DOCS_DIR && dir === 'content/docs') return process.env.DOCS_DIR;
18
+
19
+ // Try local first (Root deployment)
20
+ if (fs.existsSync(path.resolve(dir))) return dir;
21
+
22
+ // Try parent (Monorepo/Vercel deployment where CWD is packages/site)
23
+ const parentDir = path.join('../..', dir);
24
+ if (fs.existsSync(path.resolve(parentDir))) return parentDir;
25
+
26
+ return dir;
27
+ }
28
+
29
+ const docsDir = resolveContentDir('content/docs');
30
+
31
+ export const { docs, meta } = defineDocs({
32
+ dir: docsDir,
33
+ });
34
+
35
+ export default defineConfig({
36
+ mdxOptions: {
37
+ remarkPlugins: [remarkDirective, remarkDirectiveAdmonition],
38
+ rehypeCodeOptions: {
39
+ theme: siteConfig.content.codeBlock.theme,
40
+ }
41
+ }
42
+ });
43
+
package/tsconfig.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": [
4
+ "dom",
5
+ "dom.iterable",
6
+ "esnext"
7
+ ],
8
+ "allowJs": true,
9
+ "skipLibCheck": true,
10
+ "strict": true,
11
+ "noEmit": true,
12
+ "esModuleInterop": true,
13
+ "module": "esnext",
14
+ "moduleResolution": "bundler",
15
+ "resolveJsonModule": true,
16
+ "isolatedModules": true,
17
+ "jsx": "react-jsx",
18
+ "incremental": true,
19
+ "plugins": [
20
+ {
21
+ "name": "next"
22
+ }
23
+ ],
24
+ "paths": {
25
+ "@/*": [
26
+ "./*"
27
+ ]
28
+ },
29
+ "target": "ES2017"
30
+ },
31
+ "include": [
32
+ "next-env.d.ts",
33
+ "**/*.ts",
34
+ "**/*.tsx",
35
+ ".next/types/**/*.ts",
36
+ ".next/dev/types/**/*.ts",
37
+ "/Users/steedos/Documents/GitHub/docs/packages/site/.next/types/**/*.ts",
38
+ "/Users/steedos/Documents/GitHub/docs/packages/site/.next/dev/types/**/*.ts"
39
+ ],
40
+ "exclude": [
41
+ "node_modules"
42
+ ]
43
+ }