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
└── .gitignore

Key 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:

  • LinchDesktopProvider wraps the entire app, providing configuration and initialization
  • Shell serves 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 database
  • tauri-plugin-updater - Auto-updates
  • tauri-plugin-dialog - Dialogs
  • tauri-plugin-shell - Shell operations
  • tauri-plugin-fs - File system
  • tauri-plugin-opener - Open external links
  • tauri-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 bar
  • withGlobalTauri: 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 plugins

Development Workflow

Adding New Pages

  1. Create page component in src/pages/
  2. Add route in src/App.tsx
  3. Add navigation item in src/config.ts (optional)
  4. Add translations in i18n.resources (if using i18n)

Adding Rust Features

  1. Add dependency in src-tauri/Cargo.toml
  2. Write Rust code in src-tauri/src/
  3. Export command with #[tauri::command]
  4. Call from frontend using invoke()

Next Steps