UNPKG

@isthatuzii/create-nano-app

Version:

Desktop application scaffolding tool for the Nano Framework

465 lines (389 loc) 13.2 kB
# Tauri SolidJS Framework Pattern This document defines the complete development framework for creating Tauri applications using SolidJS with a specific architectural pattern. Use this as a reference for AI assistants to maintain consistency across projects. ## Core Technology Stack ### Frontend - **Framework**: SolidJS (reactive UI library) - **Build Tool**: Vite with `vite-plugin-solid` - **Package Manager**: pnpm (required - faster than npm) - **Entry Point**: `src/index.jsx` - **Main Component**: `src/App.jsx` ### Backend - **Language**: Rust - **Framework**: Tauri v2 - **Main Entry**: `src-tauri/src/main.rs` (calls lib.rs) - **Library**: `src-tauri/src/lib.rs` (contains app setup and commands) - **Configuration**: `src-tauri/tauri.conf.json` ## Project Structure ``` project-root/ ├── src/ # Frontend source │ ├── index.jsx # App entry point │ ├── App.jsx # Main component │ ├── App.functions.js # App-level functions │ ├── App.css # App-level styles │ ├── api/ # Centralized API layer │ │ ├── api.js # Main API export │ │ └── modules/ # Tauri command modules │ │ ├── feature.module.js # Per-feature API modules │ │ └── ... │ ├── components/ # UI components │ │ └── feature/ # Feature-based grouping │ │ ├── Component.jsx # Component UI │ │ ├── Component.functions.js # Component logic │ │ ├── Component.css # Component styles │ │ └── ... │ ├── styles/ │ │ └── global.css # Global theme variables │ └── assets/ # Static assets ├── src-tauri/ # Backend source │ ├── src/ │ │ ├── main.rs # Entry point │ │ ├── lib.rs # App setup & command registration │ │ └── modules/ # Rust modules │ │ ├── mod.rs # Module declarations │ │ └── feature.rs # Feature implementations │ ├── Cargo.toml # Rust dependencies │ ├── tauri.conf.json # Tauri configuration │ └── build.rs # Build configuration ├── package.json # Frontend dependencies ├── vite.config.js # Vite configuration └── pnpm-lock.yaml # pnpm lockfile ``` ## Component Architecture Pattern ### Three-File Component Pattern Every component MUST follow this exact structure: #### 1. `Component.jsx` (UI Layer) ```javascript import { createSignal, onMount } from "solid-js"; import "./Component.css"; import { createComponentFunctions } from "./Component.functions.js"; import api from "../../api/api.js"; function Component(props) { // State management with SolidJS signals const [state, setState] = createSignal(initialValue); // Create component functions const functions = createComponentFunctions( state, setState, props, api ); // Lifecycle hooks onMount(() => { functions.initialize(); }); return ( <div class="component"> {/* JSX content */} </div> ); } export default Component; ``` #### 2. `Component.functions.js` (Logic Layer) ```javascript // Component business logic and functions export const createComponentFunctions = ( state, setState, props, api ) => { const functionName = async () => { // Business logic here try { const result = await api.modules.feature_module.apiCall(); setState(result); } catch (error) { console.error('Error:', error); } }; const initialize = () => { // Initialization logic }; return { functionName, initialize }; }; ``` #### 3. `Component.css` (Styles Layer) ```css /* Component specific styles */ @import "../../styles/global.css"; .component { /* Use CSS variables from global.css */ background: var(--sc-bg-secondary); color: var(--sc-text-primary); padding: var(--sc-spacing-md); border-radius: var(--sc-radius-md); } .component-element { /* Component-specific styling */ } ``` ## API Architecture ### Centralized API Pattern #### `src/api/api.js` (Main API Export) ```javascript import { invoke } from "@tauri-apps/api/core"; import { listen } from "@tauri-apps/api/event"; // Import all feature modules import featureModule from "./modules/feature.module.js"; import anotherModule from "./modules/another.module.js"; const api = { tauri_api: { invoke, listen }, modules: { feature_module: { functionName: featureModule.functionName, anotherFunction: featureModule.anotherFunction }, another_module: { someFunction: anotherModule.someFunction } } }; export default api; ``` #### `src/api/modules/feature.module.js` (Feature Module) ```javascript import { invoke } from "@tauri-apps/api/core"; const functions = { functionName: async (params) => { return await invoke("rust_command_name", params); }, anotherFunction: async (data) => { return await invoke("another_rust_command", { data }); } }; export default functions; ``` ## Backend (Rust) Pattern ### `src-tauri/src/lib.rs` ```rust use tauri::Manager; // Import modules mod modules; use modules::feature; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_opener::init()) .invoke_handler(tauri::generate_handler![ feature::rust_command_name, feature::another_rust_command, ]) .setup(|app| { // App initialization Ok(()) }) .run(tauri::generate_context!()) .expect("error while running tauri application"); } ``` ### `src-tauri/src/modules/mod.rs` ```rust pub mod feature; // Add other modules here ``` ### `src-tauri/src/modules/feature.rs` ```rust use tauri::State; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct FeatureData { pub field: String, } #[tauri::command] pub async fn rust_command_name(params: FeatureData) -> Result<String, String> { // Implementation Ok("Success".to_string()) } #[tauri::command] pub async fn another_rust_command(data: String) -> Result<FeatureData, String> { // Implementation Ok(FeatureData { field: data, }) } ``` ## Configuration Files ### `package.json` ```json { "name": "project-name", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vite build", "serve": "vite preview", "tauri": "tauri" }, "dependencies": { "solid-js": "^1.8.0", "@tauri-apps/api": "^2.0.0", "@tauri-apps/plugin-opener": "^2.0.0" }, "devDependencies": { "vite": "^5.0.0", "vite-plugin-solid": "^2.8.0" } } ``` ### `vite.config.js` ```javascript import { defineConfig } from "vite"; import solid from "vite-plugin-solid"; export default defineConfig(async () => ({ plugins: [solid()], clearScreen: false, server: { port: 1420, strictPort: true, host: "0.0.0.0", hmr: { port: 1421, }, }, envPrefix: ["VITE_", "TAURI_"], build: { target: ["es2021", "chrome100", "safari13"], minify: !process.env.TAURI_DEBUG && "esbuild", sourcemap: !!process.env.TAURI_DEBUG, }, })); ``` ### `src-tauri/Cargo.toml` ```toml [package] name = "project-name" version = "0.0.0" description = "Project description" authors = ["you"] edition = "2021" [lib] name = "project_name_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] tauri-build = { version = "2.0", features = [] } [dependencies] tauri = { version = "2.0", features = ["macos-private-api"] } tauri-plugin-opener = "2.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" ``` ## Styling System ### Global CSS Variables (`src/styles/global.css`) ```css :root { /* Primary Colors */ --sc-primary-blue: #569cd6; /* VS Code blue */ --sc-primary-orange: #d7ba7d; /* orange/yellow used for variables */ --sc-primary-gold: #c586c0; /* closer to VS Code's purple for highlighting */ /* Background Colors */ --sc-bg-primary: #1e1e1e; /* editor background */ --sc-bg-secondary: #252526; /* sidebar */ --sc-bg-tertiary: #2d2d2d; /* panel background */ --sc-bg-overlay: rgba(30, 30, 30, 0.95); --sc-bg-glass: rgba(37, 37, 38, 0.8); /* Text Colors */ --sc-text-primary: #d4d4d4; /* main text */ --sc-text-secondary: #9cdcfe; /* secondary / variable name color */ --sc-text-muted: #808080; /* comments */ --sc-text-accent: var(--sc-primary-blue); /* Border Colors */ --sc-border-primary: #3c3c3c; --sc-border-accent: var(--sc-primary-blue); --sc-border-warning: #ce9178; /* string color, reads as a warning nicely */ --sc-border-success: #b5cea8; /* light green used for success-like things */ /* Spacing */ --sc-spacing-xs: 4px; --sc-spacing-sm: 8px; --sc-spacing-md: 16px; --sc-spacing-lg: 24px; --sc-spacing-xl: 32px; /* Border Radius */ --sc-radius-sm: 4px; --sc-radius-md: 8px; --sc-radius-lg: 12px; /* Transitions */ --sc-transition-fast: 0.15s ease; --sc-transition-normal: 0.3s ease; --sc-transition-slow: 0.5s ease; /* Shadows */ --sc-shadow-soft: 0 4px 12px rgba(0, 0, 0, 0.3); --sc-shadow-hard: 0 2px 8px rgba(0, 0, 0, 0.6); --sc-shadow-glow: 0 0 20px rgba(86, 156, 214, 0.3); } /* Global base styles */ * { box-sizing: border-box; } body { margin: 0; padding: 0; font-family: system-ui, -apple-system, sans-serif; background: var(--sc-bg-primary); color: var(--sc-text-primary); } ``` ## Development Commands ```bash # Frontend development pnpm dev # Start Vite dev server (port 1420) pnpm build # Build frontend for production pnpm serve # Preview production build # Tauri development pnpm tauri dev # Start Tauri app in development mode pnpm tauri build # Build Tauri app for production # Combined development (recommended) pnpm tauri dev # Automatically runs pnpm dev first ``` ## Key Principles ### 1. Separation of Concerns - **JSX files**: Only UI rendering and SolidJS reactive logic - **Functions files**: All business logic and state manipulation - **CSS files**: Only styling, always import global.css - **Module files**: Only Tauri API invocations ### 2. Consistent Naming - Components: PascalCase (`ChatWindow.jsx`) - Functions: camelCase (`sendMessage`) - CSS classes: kebab-case (`chat-window`) - Rust commands: snake_case (`send_message`) ### 3. Error Handling - Always wrap Tauri invocations in try-catch blocks - Return structured results from Rust commands - Handle loading states in UI components ### 4. State Management - Use SolidJS signals for reactive state - Pass state and setters to function creators - Keep state minimal and derived when possible ## Implementation Checklist When creating a new project following this pattern: - [ ] Set up project structure with correct folder hierarchy - [ ] Configure package.json with pnpm and correct scripts - [ ] Set up vite.config.js with proper Tauri integration - [ ] Create global.css with complete CSS variable system - [ ] Implement centralized API system in src/api/ - [ ] Follow three-file pattern for all components - [ ] Set up Rust modules with proper command registration - [ ] Use consistent naming conventions throughout - [ ] Implement proper error handling in all layers - [ ] Test development and build commands work correctly ## Notes for AI Assistants 1. **ALWAYS** follow the three-file component pattern - never deviate 2. **NEVER** put business logic directly in JSX components 3. **ALWAYS** import global.css in component CSS files 4. **ALWAYS** use the centralized API pattern - never invoke Tauri directly from components 5. **ALWAYS** use pnpm as the package manager 6. **ALWAYS** use async/await for Tauri command invocations 7. **ALWAYS** implement proper error handling with try-catch blocks 8. **NEVER** mix concerns between files - keep strict separation 9. **ALWAYS** use SolidJS patterns (signals, createEffect, etc.) not React patterns 10. **ALWAYS** follow the exact project structure shown above This framework ensures consistency, maintainability, and scalability across all Tauri SolidJS projects.