Project Structure
Understand the directory structure and key files of a Linch Desktop project
Directory Structure
Projects created with the scaffolding tool have the following structure:
my-app/
├── src/ # Frontend source directory
│ ├── main.tsx # React app entry point
│ ├── App.tsx # Routing and Provider setup
│ ├── config.ts # Core configuration
│ ├── index.css # Global styles
│ ├── vite-env.d.ts # Vite type declarations
│ └── pages/ # Page components directory
│ ├── Dashboard.tsx # Home page
│ └── Settings.tsx # Settings page
│
├── src-tauri/ # Tauri/Rust source directory
│ ├── src/
│ │ ├── lib.rs # Rust entry (plugin initialization)
│ │ └── main.rs # Program main entry
│ ├── capabilities/
│ │ └── default.json # Permission configuration
│ ├── icons/ # App icons (various sizes)
│ ├── Cargo.toml # Rust dependencies
│ ├── build.rs # Build script
│ └── tauri.conf.json # Tauri configuration
│
├── index.html # HTML template
├── package.json # Node dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── vite.config.ts # Vite configuration
└── .gitignoreKey Files Explained
src/main.tsx
React app entry file, responsible for rendering the root component:
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>
);src/App.tsx
Core application component, configures Provider and routing:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import {
LinchDesktopProvider,
Shell,
} from '@linch-tech/desktop-core';
import { config } from './config';
import { Dashboard } from './pages/Dashboard';
import { Settings } from './pages/Settings';
export default function App() {
return (
<LinchDesktopProvider config={config}>
<BrowserRouter>
<Routes>
<Route element={<Shell />}>
<Route path="/" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Route>
</Routes>
</BrowserRouter>
</LinchDesktopProvider>
);
}Key points:
LinchDesktopProviderwraps the entire app, providing configuration and initializationShellserves as the layout component, containing sidebar and title bar- Pages are rendered as child routes of
Shell
src/config.ts
Application configuration file, controls various features of the core:
import { Home, Settings } from 'lucide-react';
import type { LinchDesktopConfig } from '@linch-tech/desktop-core';
export const config: Partial<LinchDesktopConfig> = {
brand: {
name: 'app.name', // App name (i18n key)
version: `v${__APP_VERSION__}`,
},
nav: [
{ title: 'app.home', path: '/', icon: Home },
{ title: 'settings.title', path: '/settings', icon: Settings },
],
features: {
updater: true, // Enable auto-update
database: true, // Enable database
sentry: false, // Disable Sentry
},
i18n: {
defaultLanguage: 'zh',
supportedLanguages: ['zh', 'en'],
resources: { ... },
},
};src/pages/
Page components directory, each page is a React component:
// src/pages/Dashboard.tsx
import { useTranslation } from 'react-i18next';
import { PageHeader, ScrollArea } from '@linch-tech/desktop-core';
export function Dashboard() {
const { t } = useTranslation();
return (
<ScrollArea className="flex-1">
<div className="p-6">
<PageHeader
title={t('app.welcome')}
description={t('app.description')}
/>
{/* Page content */}
</div>
</ScrollArea>
);
}src-tauri/src/lib.rs
Rust entry file, initializes Tauri plugins:
use linch_tech_desktop_core::{LinchDesktopExt, LinchConfig};
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let config = LinchConfig::from_env();
tauri::Builder::default()
.with_linch_desktop(config)
.run(tauri::generate_context!())
.expect("error while running tauri application");
}with_linch_desktop() automatically registers the following Tauri plugins:
tauri-plugin-sql- SQLite databasetauri-plugin-updater- Auto-updatestauri-plugin-dialog- Dialogstauri-plugin-shell- Shell operationstauri-plugin-fs- File systemtauri-plugin-opener- Open external linkstauri-plugin-process- Process control
src-tauri/tauri.conf.json
Tauri core configuration file:
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "My App",
"version": "0.1.0",
"identifier": "com.mycompany.myapp",
"build": {
"beforeDevCommand": "pnpm dev",
"devUrl": "http://localhost:1450",
"beforeBuildCommand": "pnpm build",
"frontendDist": "../dist"
},
"app": {
"withGlobalTauri": true,
"windows": [
{
"title": "My App",
"width": 1200,
"height": 800,
"minWidth": 800,
"minHeight": 600,
"decorations": false,
"resizable": true
}
]
},
"bundle": {
"active": true,
"targets": "all",
"icon": [...]
}
}Important configuration items:
decorations: false- Hides system window decorations, uses custom title barwithGlobalTauri: true- Exposes Tauri API on the window object
vite.config.ts
Vite build configuration:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
define: {
'__APP_VERSION__': JSON.stringify(pkg.version),
},
plugins: [react(), tailwindcss()],
server: {
port: 1450,
strictPort: true,
},
});src-tauri/Cargo.toml
Rust dependencies configuration:
[dependencies]
linch_tech_desktop_core = "0.1"
tauri = { version = "2", features = [] }
tauri-plugin-sql = { version = "2", features = ["sqlite"] }
tauri-plugin-updater = "2"
# ... other pluginsDevelopment Workflow
Adding New Pages
- Create page component in
src/pages/ - Add route in
src/App.tsx - Add navigation item in
src/config.ts(optional) - Add translations in
i18n.resources(if using i18n)
Adding Rust Features
- Add dependency in
src-tauri/Cargo.toml - Write Rust code in
src-tauri/src/ - Export command with
#[tauri::command] - Call from frontend using
invoke()