Thank you for your interest in contributing to julIDE! This guide will help you get set up and understand the project structure.
- Code of Conduct
- Getting Started
- Development Setup
- Project Structure
- Making Changes
- Coding Standards
- Submitting a Pull Request
- Issue Guidelines
- Architecture Overview
Be respectful, constructive, and inclusive. We welcome contributors of all experience levels. Harassment or discrimination of any kind is not tolerated.
| Tool | Version | Installation |
|---|---|---|
| Rust | Latest stable | rustup.rs |
| Bun | Latest | bun.sh |
| Julia | 1.6+ | julialang.org |
| Tauri CLI | v2 | cargo install tauri-cli --version "^2" |
Linux only — install system dependencies:
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf \
libgtk-3-dev \
libsoup-3.0-dev \
javascriptcoregtk-4.1-devgit clone https://github.com/sinisterMage/JulIde.git
cd JulIde
bun install
bun run tauri devThis starts the dev server with hot reload on both the frontend (Vite) and backend (Rust).
# Full development mode (recommended)
bun run tauri dev
# Frontend only (for UI work without the native shell)
bun run dev
# Rust type-checking only
cd src-tauri && cargo check
# TypeScript type-checking only
bun run tsc --noEmit# Production build
bun run tauri build
# Frontend build only
bun run build
# Rust build only
cd src-tauri && cargo build --releasesrc/ # Frontend (React + TypeScript)
├── components/ # React components, one folder per feature
│ ├── ActivityBar/ # Sidebar view switcher
│ ├── CommandPalette/ # Cmd+Shift+P command search
│ ├── Container/ # Dev container panel and container logs
│ ├── Debugger/ # Debug panel (variables, call stack)
│ ├── Editor/ # MonacoEditor, EditorTabs, Breadcrumb, etc.
│ ├── FileExplorer/ # File tree with drag-and-drop
│ ├── Git/ # Source control panel, diff viewer, PRs/Issues tabs
│ ├── Outline/ # LSP document symbol outline sidebar panel
│ ├── OutputPanel/ # Script output with MIME rendering
│ ├── PackageManager/ # Julia package management UI
│ ├── PlotPane/ # Plot output gallery (bottom panel)
│ ├── Plugin/ # Plugin management panel
│ ├── QuickOpen/ # Fuzzy file finder (Cmd+P)
│ ├── SearchPanel/ # Global file search (Cmd+Shift+F)
│ ├── Settings/ # Preferences panel
│ ├── StatusBar/ # Bottom status indicators
│ ├── Terminal/ # Multi-terminal with xterm.js
│ ├── TestRunner/ # Julia test execution with @testset result parsing
│ ├── Toolbar/ # Run, debug, Revise, Pluto buttons
│ ├── Variables/ # Variable explorer with DataFrame viewer
│ └── Welcome/ # Welcome screen with recent projects
├── stores/ # Zustand state stores
│ ├── useIdeStore.ts # Main IDE state (tabs, panels, workspace, container, git)
│ ├── useSettingsStore.ts # Persisted user settings
│ └── usePluginStore.ts # Plugin contribution registry (commands, panels, etc.)
├── lsp/ # LSP client and Monaco providers
├── themes/ # Theme definitions
├── services/ # Keybinding service, plugin host, builtin contributions
│ ├── keybindings.ts # Keyboard shortcut manager
│ ├── builtinContributions.ts # Built-in sidebar/bottom panels and commands
│ ├── pluginHost.ts # Plugin discovery, loading, and lifecycle
│ └── pluginContext.ts # Sandboxed plugin API context factory
├── types/ # TypeScript interfaces
├── App.tsx # Root layout
└── App.css # All styles (single file)
src-tauri/src/ # Backend (Rust)
├── lib.rs # Tauri builder, command registration
├── julia.rs # Julia process management
├── lsp.rs # LSP server bridge
├── pty.rs # Terminal PTY sessions
├── git.rs # Git operations (libgit2)
├── git_auth.rs # PAT token storage via OS keychain
├── git_provider.rs # Git provider trait and dispatch commands for PRs/issues/CI
├── git_github.rs # GitHub REST API provider implementation
├── git_gitlab.rs # GitLab REST API provider implementation
├── git_gitea.rs # Gitea REST API provider implementation
├── container.rs # Docker/Podman and devcontainer management
├── plugins.rs # Plugin directory scanning and manifest loading
├── fs.rs # File system operations
├── search.rs # Workspace file search
├── watcher.rs # File change detection
├── settings.rs # Settings persistence
├── debugger.rs # Debugger.jl integration
└── pluto.rs # Pluto.jl integration
- Single CSS file: All styles are in
src/App.cssusing CSS custom properties for theming. This is intentional — it keeps theming centralized and avoids CSS-in-JS overhead. - Zustand stores: State is split into
useIdeStore(runtime state),useSettingsStore(persisted settings), andusePluginStore(plugin contribution registry). Zustand with Immer middleware allows mutable-style updates. - Tauri invoke: Frontend communicates with Rust via
invoke()calls (JSON-RPC over IPC). Events flow from Rust to the frontend viaemit(). - No Electron: julIDE uses Tauri 2, which bundles to ~10MB instead of ~150MB.
- Components go in
src/components/<FeatureName>/. - If your feature needs state, add it to
useIdeStore.ts(oruseSettingsStore.tsfor persisted settings). - If your feature needs a Rust backend command, add it to the appropriate
src-tauri/src/*.rsmodule and register it inlib.rs. - Add styles to
src/App.cssunder a clearly marked section header.
- Add new commands as
#[tauri::command]functions. - Register them in the
invoke_handlerarray insrc-tauri/src/lib.rs. - If you add a new module, declare it with
mod <name>;inlib.rs. - New Cargo dependencies go in
src-tauri/Cargo.toml.
-
Rust: Define the command in the appropriate module:
#[tauri::command] pub fn my_command(arg: String) -> Result<String, String> { Ok(format!("Hello {}", arg)) }
-
Rust: Register in
lib.rs:.invoke_handler(tauri::generate_handler![ // ... existing commands my_module::my_command, ])
-
TypeScript: Call from the frontend:
import { invoke } from "@tauri-apps/api/core"; const result = await invoke<string>("my_command", { arg: "world" });
- Use functional components with hooks.
- Use
useCallbackanduseMemofor expensive operations. - Subscribe to Zustand stores with selectors:
useIdeStore((s) => s.specificField). - No class components or HOCs.
- Types go in
src/types/index.tsfor shared interfaces. - Use Lucide React for icons.
- Follow standard Rust formatting (
cargo fmt). - Use
anyhoworStringfor error types in commands. - Use
serdefor all types that cross the IPC boundary. - Async commands should use
tokiowhere appropriate. - Keep modules focused — one concern per file.
- Use CSS custom properties (e.g.,
var(--bg-primary)) for all colors. - Add a section header comment when adding new styles.
- Test both dark and light themes when modifying styles.
- Use imperative mood: "Add feature", not "Added feature".
- Keep the first line under 72 characters.
- Reference issue numbers when applicable:
Fix #42.
-
Fork the repository and create a branch from
master:git checkout -b feature/my-feature
-
Make your changes following the coding standards above.
-
Verify your changes compile:
bun run tsc --noEmit # TypeScript cd src-tauri && cargo build # Rust
-
Test manually by running
bun run tauri devand verifying your feature works. -
Push your branch and open a PR against
master. -
PR description should include:
- A summary of what changed and why.
- Screenshots or screen recordings for UI changes.
- Steps to test the change.
- TypeScript compiles without errors (
bun run tsc --noEmit) - Rust compiles without errors (
cargo build) - Tested in both dark and light themes (if UI changes)
- No hardcoded colors (use CSS variables)
- New Tauri commands are registered in
lib.rs - New state is added to the appropriate Zustand store
Please include:
- OS and version (e.g., macOS 14.2, Ubuntu 22.04, Windows 11)
- Julia version (
julia --version) - Steps to reproduce
- Expected behavior vs actual behavior
- Logs from the terminal or dev console (Cmd/Ctrl+Shift+I in the app)
- Describe the problem you're trying to solve, not just the solution.
- Include mockups or examples from other tools if helpful.
- Check existing issues to avoid duplicates.
React Component
│
├── invoke("command_name", { args }) ──→ Rust #[tauri::command]
│ │
│ ├── Returns Result<T, String>
│ │
└── listen("event-name", callback) ←── app.emit("event-name", payload)
| Feature | Frontend → Backend | Backend → Frontend |
|---|---|---|
| Run Julia | julia_run(filePath, projectPath) |
julia-output events (stdout/stderr/done) |
| Terminal | pty_write(sessionId, data) |
pty-output events |
| LSP | lsp_send_request(method, params) |
lsp-notification events |
| File ops | fs_read_file, fs_write_file, etc. |
Direct return values |
| Git | git_status, git_commit, git_push, etc. |
Direct return values |
| Git Providers | git_provider_list_prs, git_provider_list_issues, etc. |
Direct return values (async) |
| Container | devcontainer_up, container_start, etc. |
container-status, container-output events |
| Plugins | plugin_scan, plugin_read_entry |
Direct return values |
| File watch | watcher_start(workspacePath) |
fs-changed events |
useIdeStore— Main IDE state: workspace path, open tabs, active panel, breakpoints, debug state, terminal sessions, LSP status, container state, git provider, etc.useSettingsStore— Persisted settings loaded from disk: font size, theme, tab size, container runtime preferences, recent workspaces, etc.usePluginStore— Plugin contribution registry: commands, sidebar panels, bottom panels, status bar items, toolbar buttons. Used by builtinContributions and third-party plugins.
All stores use Zustand with Immer middleware for immutable updates with mutable syntax.
If something is unclear, open an issue or start a discussion. We're happy to help you get started.