How to run, build, debug, and extend DevStackBox without breaking things.
Read this before writing any new code.
| Tool | Version | Install |
|---|---|---|
| Node.js | 18+ | nodejs.org |
| pnpm | latest | npm install -g pnpm |
| Rust | stable | rustup.rs |
| Tauri CLI | 2.x | bundled in devDependencies |
| Git | any | git-scm.com |
Verify your setup:
node --version # 18+
pnpm --version
rustc --version
cargo --versiongit clone https://github.com/ProgrammerNomad/DevStackBox.git
cd DevStackBox
pnpm installThe service binaries (MySQL, Apache, PHP) must be present in their directories:
mysql/bin/mysqld.exeapache/bin/httpd.exephp/8.3/php.exe
These are NOT committed to git (too large). Either:
- Copy them from a working XAMPP installation
- Run
scripts/prepare-binaries.ps1
pnpm tauri dev
# or
npm run tauri:devThis starts:
- Vite dev server on
http://localhost:1420 - Tauri shell with the Rust backend
Hot reload works for frontend changes. Rust changes require a full rebuild (happens automatically).
Browser-only mode (no Tauri):
pnpm devOpens just the frontend in a browser. All Tauri commands return null (safeInvoke handles this). Useful for fast UI development.
pnpm tauri:build
# or
npm run tauri:buildProduces:
src-tauri/target/release/bundle/msi/DevStackBox_0.1.6_x64_en-US.msisrc-tauri/target/release/bundle/nsis/DevStackBox_0.1.6_x64-setup.exe
Pre-build check:
- Version must be clean semver in ALL THREE places:
package.json,src-tauri/Cargo.toml,src-tauri/tauri.conf.json - No emoji in GitHub Actions
.ymlfiles (causes PowerShell encoding errors)
Read this before designing or modifying any UI component.
DevStackBox must feel like a native desktop utility - like Docker Desktop, GitHub Desktop, or Warp.
It must NOT look like: a web admin panel, a cPanel clone, analytics dashboard, or enterprise CMS.
This mindset changes everything. When in doubt, ask: "does this look like a desktop app or a website?"
This is the single most important UI rule:
Default (beginner) view: Start, Stop, Running status only.
Expanded (advanced) view: Logs, config, port, PID, process arguments.
Do NOT show everything at once. Overloaded UIs drive users away.
- Two-panel layout: fixed sidebar (220-260px) + main content area
- Top bar: app title, version badge, auto updater, language switcher, theme toggle
- Command palette: keyboard-driven via
Ctrl+Pin the current implementation - Sidebar: EXACTLY 6 items in the current app - Dashboard, Services, Projects, Logs, Settings, About
- Main area: card-based, spacious, clean - no dense tables, no charts
See ARCHITECTURE.md for the full layout diagram.
Each service card follows this exact structure:
[ Apache ] Running ●
Port: 80 PID: 1234
[ Start ] [ Stop ] [ Logs ] [ Config ]
Colors: green = running, red = stopped, yellow = warning/starting.
- Do NOT add nested sidebar items or collapsible sidebar trees
- Do NOT put logs, configs, services, and stats all on one screen at once
- Do NOT use rainbow colors, gradients, neon accents, or gaming aesthetics
- Do NOT use mobile-style UI patterns (bottom nav, full-screen modals for simple actions)
- Do NOT add charts or analytics - this is a utility, not a dashboard
- Do NOT design for web browsers - design for a desktop window
The command palette (Ctrl+P, component: command-palette.tsx) is a first-class feature.
Add any new quick action to the command palette. Example entries:
> Start Apache
> Stop MySQL
> Open php.ini
> Switch PHP 8.4
> Open www/ folder
Reuse-first workflow:
- Check
docs/COMPONENTS.mdbefore creating any component, page section, dialog, or status widget. - If an existing component is close, extend it with props, variants, or children instead of copying markup.
- If the same command, type, or UI pattern appears in more than one place, centralize it immediately.
- Keep the same action looking and behaving the same everywhere in the app.
Styling:
- Tailwind CSS classes ONLY
- shadcn/ui components for all UI elements
- Framer Motion for all animations
- NO custom CSS files (except
globals.csswhich has only Tailwind base imports) - Dark/light mode via Tailwind
dark:prefix or shadcn/ui components
State:
- Local
useStatefor component state - No global state manager (no Redux, no Zustand) unless it becomes necessary
- Pass callbacks down via props for parent-child communication
Tauri calls:
- Always use
safeInvoke()fromsrc/lib/tauri.ts - Always use command name from
TAURI_COMMANDSinsrc/lib/commands.ts - Never hardcode command name strings in components
Text / i18n:
- All user-visible text uses
t('key')fromuseTranslation() - Add EN key to
locales/en.jsonand HI key tolocales/hi.jsonat the same time
Types:
- Shared types live in
src/types/services.tsONLY - Do not duplicate type definitions across files
Commands:
- All commands go in
src-tauri/src/lib.rs - Do NOT add code to
service_manager.rs(it is dead code, see KNOWN_ISSUES.md) - Commands must return
Result<T, String> - Long-running operations must be
async - Add every new command to
invoke_handlerinrun()at the bottom oflib.rs
Paths:
- Never hardcode absolute paths
- Use
get_installation_path()to get the base path - Prefer
base_path.join("subdir").join("file")over string concatenation
Errors:
- Return descriptive
Err(format!("..."))strings - they show to the user - Do not
unwrap()orexpect()in production code paths
-
Create
src/pages/my-page.tsx:import { useTranslation } from "react-i18next"; import { motion } from "framer-motion"; export function MyPage() { const { t } = useTranslation(); return ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}> <h1>{t("myPage.title")}</h1> </motion.div> ); }
-
Export from
src/pages/index.ts:export { MyPage } from "./my-page";
-
Add to sidebar in
src/components/sidebar.tsx(menuItemsarray). -
Add to router in
src/App.tsx(therenderPage()function or the page switch). -
Add translation keys to
locales/en.jsonandlocales/hi.json. -
Update
docs/COMPONENTS.mdwith the new page. -
If the page is mostly composed of existing cards, dialogs, or sections, reuse those components instead of creating page-specific duplicates.
-
Add the Rust function in
src-tauri/src/lib.rs(NOT in service_manager.rs):#[tauri::command] async fn my_command(service: String) -> Result<String, String> { let base_path = get_installation_path(); // ... your logic ... Ok("done".to_string()) }
-
Register it in the
invoke_handlerinsiderun()at the bottom oflib.rs. -
Add the constant to
src/lib/commands.ts. -
Call from frontend with
safeInvoke. -
Update
docs/TAURI_COMMANDS.md.
npx shadcn add <component-name>Example:
npx shadcn add tabs
npx shadcn add select
npx shadcn add alertThis generates the component in src/components/ui/. Do NOT manually edit these files. Document it in docs/COMPONENTS.md.
Do not generate a new shadcn component if an existing one plus composition solves the problem. Prefer reuse over surface-area growth.
Open both locales/en.json and locales/hi.json and add matching keys at the same time.
// locales/en.json
{
"services": {
"startButton": "Start"
}
}
// locales/hi.json
{
"services": {
"startButton": "शुरू करें"
}
}Use in component:
const { t } = useTranslation();
<Button>{t("services.startButton")}</Button>;Version must be identical in all three files. Change all three at once:
package.json-"version": "0.1.7"src-tauri/Cargo.toml-version = "0.1.7"src-tauri/tauri.conf.json-"version": "0.1.7"
Rule: Version must be clean semver only: X.Y.Z - no hyphens, no letters (MSI requirement).
Frontend debugging:
- Open DevTools in the Tauri window: Right-click -> Inspect
- Or use
Ctrl+Shift+I - Console logs from
safeInvokeshow which commands were called
Rust debugging:
- Use
println!()- output appears in the terminal wherepnpm tauri devis running - Use
DebugPanel.tsxcomponent - it callsdebug_pathsanddebug_installationcommands to show resolved paths
Service path issues:
- Call
debug_installationfromDebugPanelto see where Rust is looking for binaries - Most bugs are path resolution failures - check
get_installation_path()logic inlib.rs
Common errors:
binary not found at ...- service binary missing from the expected pathport X is not listening- service started but crashed immediately; check logsArchitecture mismatch- Apache 32-bit on 64-bit build; download 64-bit Apache
- Does a component with the same shape already exist in
docs/COMPONENTS.md? - Can the existing component accept props, children, or a variant instead?
- Is there already a shared Tauri command name in
src/lib/commands.ts? - Is there already a shared type in
src/types/services.ts? - Will the new UI look and behave the same as the rest of the app?
If any answer is yes, extend the shared path instead of creating a duplicate implementation.
| File/Dir | Why |
|---|---|
src/globals.css |
Tailwind base only - adding custom CSS here breaks the build |
src/main.tsx |
React entry point - do not modify |
src/components/ui/*.tsx |
shadcn/ui generated - use npx shadcn add to update |
src-tauri/src/main.rs |
Tauri entry point - do not modify |
src-tauri/target/ |
Build output - never commit this |
src-tauri/gen/ |
Tauri generated - never commit this |
- Never commit directly to
main. - Branch:
git checkout -b feature/my-feature - Build and test locally before pushing.
- Open a PR.
Do NOT push with --force unless you know exactly what you are doing.
pnpm install # Install all dependencies
pnpm dev # Frontend only (browser)
pnpm tauri dev # Full app (Tauri + frontend)
pnpm tauri:build # Production build
pnpm tauri -- --version # Check Tauri CLI version