diff --git a/.vitepress/config/constants.ts b/.vitepress/config/constants.ts index de5a1ef..eee2b0e 100644 --- a/.vitepress/config/constants.ts +++ b/.vitepress/config/constants.ts @@ -22,14 +22,11 @@ export const navItemOrder: Record = { reference: ["api", "platform", "concepts", "infrastructure"], "getting-started": ["quickstart", "core-concepts", "console"], "app-shell": [ - "index", + "introduction", + "quickstart", + "concepts", + "components", "api", - "authentication", - "file-based-routing", - "module-resource-definition", - "routing-and-navigation", - "sidebar-navigation", - "styles", "changelog", ], }; @@ -90,13 +87,11 @@ export const defaultSidebarOrder: string[] = ["overview", "quickstart"]; export const sidebarItemOrder: Record = { function: ["overview", "builtin-interfaces"], "app-shell": [ + "introduction", + "quickstart", + "concepts", + "components", "api", - "authentication", - "file-based-routing", - "module-resource-definition", - "routing-and-navigation", - "sidebar-navigation", - "styles", "changelog", ], }; diff --git a/.vitepress/config/sidebar.ts b/.vitepress/config/sidebar.ts index 69ba1e4..5d9c458 100644 --- a/.vitepress/config/sidebar.ts +++ b/.vitepress/config/sidebar.ts @@ -47,6 +47,10 @@ export function generateSidebar( // Sort function that respects custom order const sortItems = (items: string[]) => { return items.sort((a, b) => { + // Always put "overview" first + if (a.toLowerCase() === "overview") return -1; + if (b.toLowerCase() === "overview") return 1; + if (customOrder) { const indexA = customOrder.indexOf(a); const indexB = customOrder.indexOf(b); @@ -74,8 +78,9 @@ export function generateSidebar( // Process files first for (const fileName of sortedFiles) { + const filePath = path.join(fullPath, fileName + ".md"); items.push({ - text: toTitle(fileName + ".md"), + text: toTitle(fileName + ".md", { filePath }), link: `${basePath}/${fileName}`, }); } @@ -87,8 +92,10 @@ export function generateSidebar( const subItems = generateSidebar(docsDir, subdirPath, subdirBasePath, depth + 1); if (subItems.length > 0) { + // Check if directory has an index.md to read title from + const dirIndexPath = path.join(fullPath, dirName, "index.md"); items.push({ - text: toTitle(dirName), + text: toTitle(dirName, { filePath: fs.existsSync(dirIndexPath) ? dirIndexPath : undefined }), collapsed: depth > 0, items: subItems, }); @@ -102,11 +109,11 @@ export function generateSidebar( export function generateSectionSidebar(docsDir: string, section: string): SidebarItem[] { const items = generateSidebar(docsDir, section, `/${section}`); - // Check if there's an index.md to add as overview + // Check if there's an index.md to add as overview (skip for app-shell) const indexPath = path.join(docsDir, section, "index.md"); - if (fs.existsSync(indexPath)) { + if (fs.existsSync(indexPath) && section !== "app-shell") { items.unshift({ - text: "Overview", + text: toTitle("index.md", { filePath: indexPath }), link: `/${section}/`, }); } diff --git a/.vitepress/config/utils.ts b/.vitepress/config/utils.ts index 5cca111..51779ea 100644 --- a/.vitepress/config/utils.ts +++ b/.vitepress/config/utils.ts @@ -4,8 +4,28 @@ import fs from "node:fs"; export type ToTitleOptions = { acronyms?: Record; // token -> desired form keepLowercase?: Set; // optional: "and", "of", etc. + filePath?: string; // optional: full file path to read frontmatter }; +// Extract title from markdown frontmatter +function extractFrontmatterTitle(filePath: string): string | null { + try { + if (!fs.existsSync(filePath)) return null; + + const content = fs.readFileSync(filePath, "utf-8"); + const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/); + + if (!frontmatterMatch) return null; + + const frontmatter = frontmatterMatch[1]; + const titleMatch = frontmatter.match(/^title:\s*(.+)$/m); + + return titleMatch ? titleMatch[1].trim() : null; + } catch { + return null; + } +} + // Helper to get a readable title from filename or directory name export function toTitle(input: string, options: ToTitleOptions = {}): string { // Check for custom title override first @@ -14,6 +34,14 @@ export function toTitle(input: string, options: ToTitleOptions = {}): string { return customTitles[normalizedInput]; } + // Try to read title from frontmatter if file path is provided + if (options.filePath) { + const frontmatterTitle = extractFrontmatterTitle(options.filePath); + if (frontmatterTitle) { + return frontmatterTitle; + } + } + const allAcronyms: Record = { ...acronyms, ...options.acronyms, diff --git a/docs/app-shell/index.md b/docs/app-shell/index.md index f6c2525..2256fd8 100644 --- a/docs/app-shell/index.md +++ b/docs/app-shell/index.md @@ -1,7 +1,43 @@ -# AppShell +--- +title: Introduction +description: AppShell is an opinionated React application framework for creating applications on Tailor Platform with built-in authentication, routing, and beautiful UI components. +--- -AppShell is a UI framework for building interfaces on Tailor Platform. +# Introduction -## Overview +> **AppShell gives you everything you need to build production-ready ERP applications on Tailor Platform with minimal configuration.** -AppShell provides components and utilities for building modern admin panels and dashboards. +## What is AppShell? + +AppShell is a React-based framework that provides the foundation for building custom ERP applications on Tailor Platform. It handles the complex infrastructure so you can focus on building business-level screens and features. + +## Out of the Box + +When you integrate AppShell into your React application, you get: + +- ✅ **Simple composition** - Compose custom Tailor-powered, React-based ERP applications with minimal code +- ✅ **User authentication** - Complete OAuth2 flow with token management and session persistence +- ✅ **Authorization** - Route guards for permission-based and role-based access control +- ✅ **File-based routing** - Define pages through directory structure with auto-generated navigation +- ✅ **Beautiful layouts** - Responsive, opinionated layouts with sidebar navigation and breadcrumbs +- ✅ **Convenience hooks** - Access application, module, and user contexts with React hooks + +## Key Features + +### 🚀 File-based Routing + +Define pages through directory structure with automatic sidebar navigation and breadcrumbs. No manual route configuration needed. + +→ [Learn about File-Based Routing](concepts/file-based-routing) + +### 🔒 Built-in Authentication + +OAuth2/OIDC integration with Tailor Platform's Auth service. Supports any configured IdP including Google, Okta, and Auth0. + +→ [Authentication Guide](concepts/authentication) + +### 🎨 Beautiful UI Components + +Responsive layouts, description cards, command palette, and more. Built with Tailwind CSS v4 and shadcn/ui principles. + +→ [Quick Start](quickstart) diff --git a/docs/app-shell/introduction.md b/docs/app-shell/introduction.md deleted file mode 100644 index 2256fd8..0000000 --- a/docs/app-shell/introduction.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Introduction -description: AppShell is an opinionated React application framework for creating applications on Tailor Platform with built-in authentication, routing, and beautiful UI components. ---- - -# Introduction - -> **AppShell gives you everything you need to build production-ready ERP applications on Tailor Platform with minimal configuration.** - -## What is AppShell? - -AppShell is a React-based framework that provides the foundation for building custom ERP applications on Tailor Platform. It handles the complex infrastructure so you can focus on building business-level screens and features. - -## Out of the Box - -When you integrate AppShell into your React application, you get: - -- ✅ **Simple composition** - Compose custom Tailor-powered, React-based ERP applications with minimal code -- ✅ **User authentication** - Complete OAuth2 flow with token management and session persistence -- ✅ **Authorization** - Route guards for permission-based and role-based access control -- ✅ **File-based routing** - Define pages through directory structure with auto-generated navigation -- ✅ **Beautiful layouts** - Responsive, opinionated layouts with sidebar navigation and breadcrumbs -- ✅ **Convenience hooks** - Access application, module, and user contexts with React hooks - -## Key Features - -### 🚀 File-based Routing - -Define pages through directory structure with automatic sidebar navigation and breadcrumbs. No manual route configuration needed. - -→ [Learn about File-Based Routing](concepts/file-based-routing) - -### 🔒 Built-in Authentication - -OAuth2/OIDC integration with Tailor Platform's Auth service. Supports any configured IdP including Google, Okta, and Auth0. - -→ [Authentication Guide](concepts/authentication) - -### 🎨 Beautiful UI Components - -Responsive layouts, description cards, command palette, and more. Built with Tailwind CSS v4 and shadcn/ui principles. - -→ [Quick Start](quickstart) diff --git a/scripts/docs-sync/main.ts b/scripts/docs-sync/main.ts index 831abbf..8ec18b4 100644 --- a/scripts/docs-sync/main.ts +++ b/scripts/docs-sync/main.ts @@ -130,8 +130,8 @@ for (const file of config.removeFiles) { fs.rmSync(path.join(config.dst, file), { force: true }); } -// 4. Restore index.md -if (indexBackup) { +// 4. Restore index.md (skip for app-shell as we'll use introduction.md) +if (indexBackup && configName !== "app-shell") { fs.writeFileSync(indexBackupPath, indexBackup, "utf8"); } @@ -158,4 +158,17 @@ walk(config.dst) fs.writeFileSync(f, content); }); +// 7. For app-shell: rename introduction.md to index.md +if (configName === "app-shell") { + const introPath = path.join(config.dst, "introduction.md"); + const indexPath = path.join(config.dst, "index.md"); + + if (fs.existsSync(introPath)) { + fs.renameSync(introPath, indexPath); + console.log("Renamed introduction.md to index.md"); + } else { + console.warn("introduction.md not found, skipping rename"); + } +} + console.log(`${config.label} docs synced`);