Forge - Development Guide
This guide covers setting up the development environment, understanding the codebase structure, and contributing to Forge.
Development Environment Setup
Prerequisites
| Tool | Version | Installation |
|---|---|---|
| Node.js | 18+ | nodejs.org |
| pnpm | 8+ | npm install -g pnpm |
| Rust | 1.70+ | rustup.rs |
| Tauri CLI | 2.x | cargo install tauri-cli |
Platform-Specific Setup
macOS
# Install Xcode Command Line Tools
xcode-select --install
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shWindows
- Install Visual Studio Build Tools
- Install Rust
- Ensure
cargois in your PATH
Linux
# Ubuntu/Debian
sudo apt update
sudo apt install build-essential libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shProject Setup
Clone and Install
# Clone the repository
git clone https://github.com/pktikkani/forge.git
cd forge
# Install frontend dependencies
pnpm installRunning in Development
# Start development server with hot reload
pnpm tauri:devThis starts:
- Vite dev server on
http://localhost:1420 - Tauri app connecting to the dev server
- Hot module replacement for frontend changes
- Auto-rebuild for Rust changes
Development Credentials
For development, you’ll need:
-
Turso Database - Create a development database:
turso db create forge-dev turso db show forge-dev --url turso db tokens create forge-dev -
Azure AD App - Register a dev app with redirect URI
forge://oauth/callback -
LiveKit (optional) - Create a development project
Enter these in the app’s Settings page on first run.
Project Structure
forge/
├── src/ # Frontend (React/TypeScript)
│ ├── components/ # React components
│ │ ├── ui/ # shadcn/ui primitives
│ │ ├── dashboard/ # Dashboard-specific components
│ │ ├── shared/ # Shared components
│ │ └── calling/ # LiveKit voice/video
│ ├── pages/ # Route pages
│ ├── hooks/ # Custom React hooks
│ ├── stores/ # Zustand state stores
│ ├── lib/ # Utilities and API clients
│ │ ├── tauri-api.ts # Tauri command wrappers
│ │ ├── settings-store.ts # Persistent settings
│ │ └── utils.ts # Helper functions
│ ├── types/ # TypeScript definitions
│ ├── App.tsx # App root with routing
│ └── main.tsx # Entry point
│
├── src-tauri/ # Backend (Rust)
│ ├── src/
│ │ ├── lib.rs # Plugin registration, setup
│ │ ├── app_commands.rs # Window and init commands
│ │ ├── commands.rs # Settings, OAuth, LiveKit
│ │ ├── turso_commands.rs # CRUD operations
│ │ ├── shared_commands.rs # Presence and calling
│ │ ├── turso.rs # Database models and queries
│ │ ├── db.rs # Local SQLite
│ │ └── models.rs # Local data models
│ ├── Cargo.toml # Rust dependencies
│ └── tauri.conf.json # Tauri configuration
│
├── scripts/ # Build and utility scripts
│ └── uninstall-macos.sh # macOS uninstaller
│
├── package.json # Node dependencies
├── vite.config.ts # Vite configuration
├── tailwind.config.js # Tailwind CSS config
└── tsconfig.json # TypeScript configFrontend Architecture
Technology Stack
| Library | Purpose |
|---|---|
| React 19 | UI framework |
| TypeScript | Type safety |
| Tailwind CSS | Styling |
| shadcn/ui | Component library |
| React Query | Server state management |
| Zustand | Client state management |
| React Router | Navigation |
| Framer Motion | Animations |
State Management
Zustand Stores (src/stores/):
auth-store.ts- User session and authenticationsettings-store.ts- App settings state (UI)
React Query (src/hooks/):
- Server state (Ideas, Goals, Tasks)
- Automatic caching and refetching
- Optimistic updates
Persistent Storage (src/lib/settings-store.ts):
- Uses
@tauri-apps/plugin-store - Survives app reinstalls
- Stores Turso/LiveKit credentials
Component Patterns
// Example component structure
import { useQuery, useMutation } from "@tanstack/react-query";
import { tursoApi } from "@/lib/tauri-api";
import { Button } from "@/components/ui/button";
export function IdeaList() {
// Fetch data with React Query
const { data: ideas, isLoading } = useQuery({
queryKey: ["ideas"],
queryFn: tursoApi.getIdeas,
});
// Mutation with optimistic update
const createIdea = useMutation({
mutationFn: tursoApi.createIdea,
onSuccess: () => queryClient.invalidateQueries(["ideas"]),
});
if (isLoading) return <Spinner />;
return (
<div>
{ideas?.map((idea) => (
<IdeaCard key={idea.id} idea={idea} />
))}
</div>
);
}Tauri API Integration
Frontend communicates with Rust via the invoke function:
// src/lib/tauri-api.ts
import { invoke } from "@tauri-apps/api/core";
export const tursoApi = {
getIdeas: () => invoke<Idea[]>("get_all_ideas"),
createIdea: (idea: CreateIdeaInput) => invoke<Idea>("create_idea", { idea }),
updateIdea: (idea: UpdateIdeaInput) => invoke<Idea>("update_idea", { idea }),
deleteIdea: (id: string) => invoke<void>("delete_idea", { id }),
};Backend Architecture
Tauri Commands
Commands are defined with the #[tauri::command] attribute:
// src-tauri/src/turso_commands.rs
#[tauri::command]
pub async fn get_all_ideas(
turso_state: State<'_, TursoState>,
) -> Result<Vec<Idea>, String> {
let guard = turso_state.0.read().await;
let db = guard.as_ref().ok_or("Turso not initialized")?;
db.get_all_ideas().await.map_err(|e| e.to_string())
}Commands are registered in lib.rs:
.invoke_handler(tauri::generate_handler![
show_main_window,
initialize_turso,
get_all_ideas,
create_idea,
// ... more commands
])Database Operations
Turso queries use libSQL (SQLite-compatible):
// src-tauri/src/turso.rs
impl TursoDatabase {
pub async fn get_all_ideas(&self) -> Result<Vec<Idea>> {
let mut rows = self.conn.query(
"SELECT id, title, description, status, priority, author_id,
created_at, updated_at
FROM ideas
WHERE author_id IN (SELECT id FROM users WHERE tenant_id = ?)",
[&self.tenant_id],
).await?;
let mut ideas = Vec::new();
while let Some(row) = rows.next().await? {
ideas.push(Idea::from_row(&row)?);
}
Ok(ideas)
}
}State Management
Rust state is managed through Tauri’s state system:
// Lazy-initialized Turso connection
pub struct TursoState(pub Arc<RwLock<Option<TursoDatabase>>>);
// Access in commands
#[tauri::command]
pub async fn some_command(
turso_state: State<'_, TursoState>,
) -> Result<(), String> {
let guard = turso_state.0.read().await;
let db = guard.as_ref().ok_or("Not initialized")?;
// Use db...
}Adding New Features
Adding a New Entity
-
Define the Rust model (
src-tauri/src/turso.rs):#[derive(Debug, Serialize, Deserialize)] pub struct NewEntity { pub id: String, pub title: String, // ... fields } -
Add database operations:
impl TursoDatabase { pub async fn create_new_entity(&self, entity: NewEntity) -> Result<NewEntity> { self.conn.execute( "INSERT INTO new_entities (id, title, ...) VALUES (?, ?, ...)", params![entity.id, entity.title, ...], ).await?; Ok(entity) } } -
Create Tauri commands (
src-tauri/src/turso_commands.rs):#[tauri::command] pub async fn create_new_entity( turso_state: State<'_, TursoState>, entity: NewEntity, ) -> Result<NewEntity, String> { // Implementation } -
Register commands in
lib.rs -
Add TypeScript types (
src/types/):export interface NewEntity { id: string; title: string; // ... fields } -
Add API wrapper (
src/lib/tauri-api.ts):export const newEntityApi = { create: (entity: NewEntity) => invoke<NewEntity>("create_new_entity", { entity }), }; -
Create React components and hooks
Adding a New Page
-
Create the page component (
src/pages/NewPage.tsx) -
Add route in
App.tsx:<Route path="/new-page" element={<NewPage />} /> -
Add navigation in the sidebar component
Testing
Frontend Testing
# Run Jest tests
pnpm test
# Run with coverage
pnpm test:coverageRust Testing
# Run Rust tests
cd src-tauri
cargo test
# Run specific test
cargo test test_nameE2E Testing
# Run Playwright tests
pnpm test:e2eCode Quality
Linting
# Lint frontend
pnpm lint
# Lint Rust
cd src-tauri
cargo clippyFormatting
# Format frontend
pnpm format
# Format Rust
cd src-tauri
cargo fmtType Checking
# TypeScript type check
pnpm typecheck
# Rust type check
cd src-tauri
cargo checkDebugging
Frontend Debugging
- Open DevTools in the app:
Cmd+Option+I(macOS) orCtrl+Shift+I(Windows/Linux) - Use React DevTools extension
- Check Console for errors
- Use Network tab for API calls
Rust Debugging
-
Logging:
use log::{info, debug, error}; info!("Processing request: {:?}", request); -
View logs:
- macOS:
~/Library/Logs/com.forge.desktop/ - Windows:
%APPDATA%\com.forge.desktop\logs\ - Linux:
~/.config/com.forge.desktop/logs/
- macOS:
-
Debug build:
RUST_BACKTRACE=1 pnpm tauri:dev
Database Debugging
# Connect to Turso database
turso db shell forge-dev
# Query tables
.tables
SELECT * FROM ideas LIMIT 5;Common Development Tasks
Adding a New Tauri Plugin
-
Add to
Cargo.toml:[dependencies] tauri-plugin-new = "2" -
Register in
lib.rs:.plugin(tauri_plugin_new::init()) -
Add to
capabilities/default.jsonif needed -
Use in frontend:
import { someFunction } from "@tauri-apps/plugin-new";
Adding a shadcn/ui Component
# Add a new component
pnpm dlx shadcn@latest add button
pnpm dlx shadcn@latest add dialogComponents are added to src/components/ui/.
Updating Dependencies
# Update frontend dependencies
pnpm update
# Update Rust dependencies
cd src-tauri
cargo updateGit Workflow
Branch Naming
feature/description- New featuresfix/description- Bug fixesrefactor/description- Code refactoringdocs/description- Documentation updates
Commit Messages
Follow conventional commits:
feat: add user presence indicator
fix: resolve task drag-drop issue
docs: update deployment guide
refactor: simplify auth flowPull Request Process
- Create a feature branch
- Make changes and test locally
- Push branch and create PR
- Request review
- Address feedback
- Merge after approval
Troubleshooting Development
Build Errors
“Cannot find module” errors:
pnpm installRust compilation errors:
cd src-tauri
cargo clean
cargo buildTauri command not found:
cargo install tauri-cliRuntime Errors
“Turso not initialized”:
- Enter credentials in Settings page
- Check console for connection errors
“Failed to invoke command”:
- Verify command is registered in
lib.rs - Check command name matches exactly
- Verify parameter types match
Hot Reload Not Working
Frontend changes not reflecting:
- Check Vite dev server is running
- Clear browser cache
- Restart dev server
Rust changes not reflecting:
- Rust auto-rebuilds, but may need manual restart
- Run
cargo buildinsrc-tauri/