For Developers
A little about the technical details of the UAC Launch Control app — if you want to contribute, or build for yourself, read more below.
You can also check out the more detailed explanation about the project and its structure on DeepWiki.
Note: This document gets updated frequently during early development. If you run into any trouble, please open an issue or reach out.
Architecture
Section titled “Architecture”Technology Stack
Section titled “Technology Stack”- Frontend: React 19 + TypeScript + TailwindCSS v3
- Backend: Express.js API server
- Desktop: Tauri (Rust) — bootstraps and proxies the Node.js backend
- Build Tool: Vite (for the renderer)
- File Watching: Chokidar for real-time WAD directory synchronization
- State Management: TanStack Query (React Query)
- UI Components: Radix UI + shadcn/ui
- Routing: wouter (path-only, no query param tracking)
- Icons: Lucide React + Nerd Font (Atkinson Mono Nerd Font)
Application Structure
Section titled “Application Structure”uaclaunchcontrol/├── client/ # React frontend│ └── src/│ ├── api.ts # Fetch-based API client (localhost:7666)│ ├── App.tsx # Root: router, auto-updater, modals│ ├── pages/ # GamesPage (/), InstallPage (/install), not-found│ ├── components/ # Custom components (CatalogManager, GameCard, etc.)│ │ └── ui/ # shadcn/ui primitives (40+ components)│ ├── hooks/ # useAutoUpdater, useToast, useMobile│ ├── lib/ # gameService, fileService, queryClient, utils│ ├── icons/ # DoomVersionIcon + PNG assets│ └── assets/ # Fonts, images, logos├── server/ # Express.js API backend│ ├── index.ts # Server setup (port 7666), CORS, logging middleware│ ├── routes.ts # All REST API endpoints│ ├── storage.ts # JSON file-based persistence + WAD file watcher│ └── services/│ ├── fileService.ts # File system operations│ └── gameService.ts # Game config management├── src-tauri/ # Rust/Tauri desktop wrapper│ ├── src/│ │ ├── main.rs # Bootstraps Node.js server, proxy, window management│ │ └── ...│ └── ...└── shared/ # Shared TypeScript types └── schema.ts # All interfaces: IMod, IModFile, IDoomVersion, etc.Data Flow
Section titled “Data Flow”- Tauri (Rust) starts and spawns the Node.js Express API server on port
7666as a background process. - Express Server manages JSON file storage at
~/.config/uac/:settings.json— App settings (paths, preferences)doomVersions.json— Configured Doom versions/WADsmodFileCatalogue.json— Catalog of available mod filesmods/— Individual mod configurations as JSON files
- Chokidar watches the WAD files directory for changes and syncs doom versions automatically.
- Renderer (React app) communicates with the API server via HTTP fetch.
- Media Proxy: Images served via both
/api/media?path=and/images/:fileNameto bypass Tauri’stauri://security restrictions. - Auto-Update: Uses
electron-updater(legacy) with GitHub releases. New Tauri-based builds use Tauri’s built-in updater.
Getting Started
Section titled “Getting Started”Prerequisites
Section titled “Prerequisites”- Node.js 18+
- npm or pnpm
- GZDoom (or similar source port) installed on your system
# Clone the repositorygit clone https://github.com/mikkelrask/uaclaunchcontrol.gitcd uaclaunchcontrol
# Install dependenciesnpm installDevelopment
Section titled “Development”# Run in development mode (with hot reload)npm run devThis will:
- Start the Vite dev server for the renderer (with proxy to Express on port 7666)
- Start the Tauri development environment
- Start the Chokidar WAD watcher for real-time sync
# TypeScript type checkingnpm run typecheck
# Lint and formatnpm run lintnpm run format
# Build for your current platformnpm run buildPlatform-specific builds
Section titled “Platform-specific builds”npm run build:win # Windowsnpm run build:mac # macOSnpm run build:linux # Linuxnpm run build:unpack # Build + unpacked directory (for testing)Built applications will be in the dist/ directory.
Configuration
Section titled “Configuration”Done via the application settings (cog icon in the top right corner), but everything is also stored in plain text JSON files, so you can edit them manually if you want to. All paths support tilde (~) expansion.
Storage Location
Section titled “Storage Location”All application data is stored in ~/.config/uac/:
~/.config/uac/├── settings.json # App settings (paths, preferences)├── doomVersions.json # Configured Doom versions/WADs├── modFileCatalogue.json # Catalog of available mod files├── mods/ # Individual mod configurations│ ├── 1.json│ ├── 2.json│ └── ...├── images/ # Screenshots and downloaded images└── saves/ # Game save files (per-mod, optional)Default Settings
Section titled “Default Settings”{ "sourcePortPath": "", "theme": "dark", "savegamesPath": "~/.config/uac/saves", "modsDirectory": "~/.config/uac/mods", "screenshotsPath": "~/Pictures/UAC Launch Control/screenshots", "wadFilesDirectory": "~/.config/uac/wads", "autoUpdateEnabled": true, "registryLookupEnabled": false}API Endpoints
Section titled “API Endpoints”The Express server exposes the following REST API on localhost:7666:
GET /api/mods— List all mods (optional?version=and?search=query params)GET /api/mods/:id— Get specific mod with its filesPOST /api/mods— Create new modPUT /api/mods/:id— Update modDELETE /api/mods/:id— Delete modPOST /api/mods/:id/launch— Launch a mod (spawns source port)
Doom Versions / WADs
Section titled “Doom Versions / WADs”GET /api/versions— List all Doom versionsPUT /api/versions— Save all Doom versions (full array replace)GET /api/versions/:slug— Get Doom version by slugPUT /api/versions/:id— Update a single Doom versionPOST /api/wads/import— Import a .wad file with MD5-based renaming
Mod File Catalog
Section titled “Mod File Catalog”GET /api/mod-files/catalog— List catalog filesPOST /api/mod-files/catalog— Add file to catalogPUT /api/mod-files/catalog/:id— Update catalog entryDELETE /api/mod-files/catalog/:id— Delete catalog entryPOST /api/mod-files/hash— Compute MD5 hash of a filePOST /api/mod-files/move— Move file to mod folder with hash-based naming
Settings
Section titled “Settings”GET /api/settings— Get expanded application settingsPUT /api/settings— Update settings
Media & Files
Section titled “Media & Files”GET /api/media?path=— Proxy for serving local files safelyGET /images/:fileName— Serve images from the images directoryPOST /api/screenshots/upload— Upload/copy a screenshot to images dirPOST /api/mod/download-image— Download an image from a URLPOST /api/move-file— Move a file from one path to anotherPOST /api/dialog/open— Open native file/folder picker (supports tilde)
Migration
Section titled “Migration”GET /api/migration/check— Check for legacy config from previous versionsPOST /api/migration/execute— Execute legacy migration
Path Aliases
Section titled “Path Aliases”Defined in electron.vite.config.ts (legacy) or Vite config:
@/— Maps toclient/src/(e.g.@/components/ui/button)@shared/— Maps toshared/(e.g.@shared/schema)
Styling
Section titled “Styling”- TailwindCSS v3 with class-based dark mode
- Custom app utility classes:
bg-app-primary,bg-app-secondary,text-app-primary,text-app-muted,border-app,bg-accent-highlight - Font: Aldrich (sans, bundled via
@font-face), with Atkinson Mono Nerd Font as fallback for icon glyphs - UI components in
client/src/components/ui/(40+ shadcn/ui primitives)
Dialog Pattern
Section titled “Dialog Pattern”All dialogs follow a consistent pattern modeled on SettingsDialog.tsx:
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}> <DialogContent className="bg-app-primary shadow-2xl border-app max-w-4xl p-0 overflow-hidden flex flex-col"> {/* Full-bleed header bar */} <div className="flex items-center justify-between p-4 border-b border-app bg-app-secondary"> <div className="flex items-center gap-3"> <div className="p-2 bg-accent-highlight/10 rounded-md"> <Icon className="w-5 h-5 text-accent-highlight" /> </div> <div> <DialogTitle className="text-xl font-bold tracking-tight text-app-primary lowercase"> title_text </DialogTitle> <DialogDescription className="text-xs font-semibold font-mono text-app-muted uppercase tracking-widest opacity-80"> UAC Launch Control // Description </DialogDescription> </div> </div> </div>
{/* Body */} <div className="flex-1 overflow-y-auto p-4">...</div>
{/* Full-bleed footer bar */} <DialogFooter className="bg-app-secondary border-t border-app p-4 shrink-0"> <Button variant="outline" onClick={onClose}>Cancel</Button> <Button className="bg-accent-highlight text-white">Confirm</Button> </DialogFooter> </DialogContent></Dialog>Keyboard Shortcuts
Section titled “Keyboard Shortcuts”Defined in Header.tsx. All single-letter shortcuts are lowercase and disabled when typing in an input/textarea:
| Key | Action |
|---|---|
/ | Focus search input |
i | Go to Install > Configuration tab |
m | Go to Install > Mod Files tab |
w | Go to Install > WAD Files tab |
l | Go to Launch page |
Ctrl+. | Open Settings |
? | Show keyboard shortcuts modal |
Global shortcuts use URL query params (/install?tab=files) + custom DOM events (uac:switch-tab) since wouter only tracks pathname.
Shared Types
Section titled “Shared Types”All interfaces in shared/schema.ts use I prefix:
- IMod — Mod/game instance (title, description, files, source port, etc.)
- IModFile — Individual mod file entry (path, hash, type, load order)
- IDoomVersion — Base game/WAD configuration (name, slug, executable, icon)
- IAppSettings — Application settings (paths, theme, toggles, database links)
- IUpdateInfo — Auto-update status (version, release notes, download progress)
- IResponseMessage — Generic API response wrapper
Development Notes
Section titled “Development Notes”TypeScript Configuration
Section titled “TypeScript Configuration”The project uses separate TypeScript configurations:
tsconfig.node.json— Server codetsconfig.web.json— Renderer process (includessharedvia composite)
Code Style
Section titled “Code Style”- TypeScript strict mode, files:
.ts/.tsx - No semicolons, single quotes, 100 char print width, no trailing commas (Prettier)
- PascalCase for components, camelCase for hooks (
useprefix), kebab-case for files - PascalCase with
Iprefix for types (IMod,IModFile) - Use
cn()from@/lib/utilsfor conditional class merging - Errors displayed via
useToast()hook
State Management
Section titled “State Management”- Server state: TanStack Query (
useQuery,useMutation) withstaleTime: Infinity - Local state:
useState - Global UI: React Context (rare)
- Query keys: Use URL paths as keys:
['/api/mods'],['/api/mod-files/catalog'],['/api/settings']
Recommended IDE Setup
Section titled “Recommended IDE Setup”- VSCode
- Extensions: