@isthatuzii/create-nano-app
Version:
Desktop application scaffolding tool for the Nano Framework
465 lines (389 loc) • 13.2 kB
Markdown
# 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.