A desktop application built with Electron and Angular 22 that reads a JSON file from disk and displays it as plain text. The project is scaffolded with modern Angular practices β standalone components, signals, zoneless change detection, lazy-loaded routes, and a clean folder structure ready to grow over time.
- Features
- Tech stack
- Prerequisites
- Project structure
- Getting started
- Running the app
- Development workflow
- Building for production
- Publishing installers
- Scripts reference
- Architecture notes
- Troubleshooting
- License
- π Opens any
.jsonfile via the native OS dialog. - π§Ύ Displays the file content as plain text (pretty-printed with 2-space indentation).
- β‘ Built with Angular signals and zoneless change detection.
- π₯οΈ Runs inside Electron with a secure
contextBridgepreload (no Node in the renderer). - π¨ Light/dark theme via
prefers-color-scheme. - π§© Standard Angular folder layout (
core,features,shared,environments) ready to scale.
| Layer | Technology |
|---|---|
| Shell | Electron 33 (production-ready stable line) |
| UI | Angular 22 (standalone, signals, zoneless) |
| Language | TypeScript 5.6 |
| Build (web) | @angular/build:application (esbuild-based) |
| Build (app) | electron-builder |
| Dev tooling | Concurrently, wait-on, cross-env, Prettier |
- Node.js β₯ 22.22.3 LTS (Angular 22 requires Node 22, 24, or 26) β https://nodejs.org
- npm β₯ 10 (ships with Node 22)
- For publishing macOS/Windows installers from non-native OSes, see the electron-builder docs.
Verify your environment:
node --version # v22.22.3 or newer
npm --version # 10.x or newerdocker-project-manager/
βββ electron/ # Electron main process (Node context)
β βββ main.ts # BrowserWindow, IPC handlers, app menu
β βββ preload.ts # contextBridge β exposes window.electronAPI
β βββ tsconfig.json # Separate CJS tsconfig for the main process
βββ src/
β βββ app/
β β βββ core/ # Singletons: services, models, providers
β β β βββ electron/ # Electron bridge (service + types + provider)
β β βββ features/ # Feature areas (one folder per feature)
β β β βββ json-viewer/ # The "read & show JSON" feature
β β β βββ json-viewer.component.ts
β β β βββ json-viewer.component.html
β β β βββ json-viewer.component.scss
β β β βββ json-viewer.model.ts
β β βββ shared/ # (Reserved) reusable pipes, directives, utils
β β βββ app.component.ts # Root shell component
β β βββ app.config.ts # bootstrapApplication providers
β β βββ app.routes.ts # Top-level lazy routes
β βββ environments/ # environment.ts / environment.development.ts
β βββ index.html
β βββ main.ts # Angular bootstrap entry
β βββ styles.scss # Global styles + CSS variables
βββ public/ # Static assets copied as-is (sample.json)
βββ .vscode/ # Recommended extensions + editor settings
βββ angular.json # Angular CLI workspace config
βββ electron-builder.json # Packaging & publish config
βββ package.json
βββ tsconfig.json # Base TS config (shared by app/spec/electron)
βββ tsconfig.app.json # App compilation
βββ tsconfig.spec.json # Test compilation
βββ README.md
The
core / features / sharedsplit is the recommended Angular layout for apps that will grow over time. Add new screens undersrc/app/features/<name>/and register them as lazy routes inapp.routes.ts.
# 1. Install dependencies
npm install
# 2. Run the desktop app in dev mode (Angular + Electron together)
npm run devThe first run compiles the Electron main process to dist-electron/, starts the Angular dev server on http://localhost:4200, and launches Electron once the dev server is ready.
Electron desktop mode
npm run dev- Angular dev server runs with live reload on port
4200. - Electron loads
http://localhost:4200and opens DevTools detached. - The Electron main process is recompiled automatically when
electron/**/*.tschanges (vianpm run watch:electron).
To run a production build of the app inside Electron (no dev server):
npm run start:electron| Task | Command |
|---|---|
| Start dev (Angular + Electron) | npm run dev |
| Angular dev server only | npm start |
| Watch-compile Electron main | npm run watch:electron |
| Run unit tests | npm test |
| Format the codebase | npm run format |
| Lint | npm run lint |
- Create a folder under
src/app/features/<feature-name>/. - Generate a standalone component (OnPush + signals):
npx ng generate component features/<feature-name> --standalone --change-detection=OnPush
- Register a lazy route in
src/app/app.routes.ts:{ path: '<feature-name>', loadComponent: () => import('./features/<feature-name>/<feature-name>.component').then( (m) => m.<FeatureName>Component, ), }
- Put shared utilities in
src/app/shared/and singletons insrc/app/core/.
- The app uses
provideExperimentalZonelessChangeDetection()inapp.config.ts. Prefer signals (signal,computed,effect) overBehaviorSubjectfor component state. JsonViewerComponentis a reference implementation: state is held in private writable signals and exposed as read-only views.
# 1. Build the Angular app (production) and the Electron main process
npm run build:prod
npm run build:electron
# 2. Package installers for the current OS
npm run electron:buildOutputs land in release/ (e.g. release/Docker Project Manager-0.1.0.exe on Windows).
Platform-specific shortcuts:
npm run electron:build:win # Windows NSIS installer
npm run electron:build:mac # macOS dmg + zip
npm run electron:build:linux # Linux AppImage + debPublishing is configured for GitHub Releases in electron-builder.json:
"publish": {
"provider": "github",
"owner": "your-org",
"repo": "docker-project-manager"
}Steps:
-
Update
versioninpackage.json(semver). -
Create a git tag:
git tag v0.1.0 && git push origin v0.1.0. -
Set the
GH_TOKENenvironment variable with a GitHub personal access token that hasreposcope. -
Publish:
# macOS / Linux export GH_TOKEN="ghp_xxx" # Windows (PowerShell) $env:GH_TOKEN="ghp_xxx" npm run publish
electron-builder will build, upload the artifacts to the GitHub release matching the tag, and produce auto-update latest.yml/latest-mac.yml/latest-linux.yml manifests.
To switch to a different provider (S3, generic HTTP, Snap Store, etc.), update the
publishblock inelectron-builder.jsonβ see https://www.electron.build/configuration/publish.
| Script | Description |
|---|---|
ng |
Raw Angular CLI passthrough. |
start |
ng serve β Angular dev server only. |
dev |
Angular dev server + Electron watcher + launch (recommended). |
build |
ng build (development config). |
build:prod |
ng build --configuration production. |
build:electron |
Compiles electron/**/*.ts β dist-electron/ (CommonJS). |
watch:electron |
Same as build:electron but in watch mode. |
start:electron |
Builds Electron main and launches the packaged app against dist/. |
test |
Runs unit tests via Karma + Jasmine. |
lint |
ng lint. |
format |
Prettier write across src/ and electron/. |
electron:build |
Production build + packaging for the current OS. |
electron:build:<os> |
Cross-compile for win / mac / linux. |
publish |
Build + publish installers to the configured publish provider. |
ββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββ
β Main process (Node) β IPC β Renderer process (Angular) β
β electron/main.ts β ββββββΆ β src/ β
β - BrowserWindow β β - standalone components β
β - dialog.showOpenDialog β β - signals / zoneless β
β - fs.readFile + JSON.parseβ β - ElectronService (typed bridge) β
ββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββ
β² β²
β contextBridge β window.electronAPI
β (electron/preload.ts) β (typed via ElectronAPI)
- Main process owns all Node APIs (
fs,dialog,Menu). It validates JSON before sending it to the renderer. - Preload uses
contextBridgewithcontextIsolation: trueandnodeIntegration: false, so the renderer never touches Node directly. - Renderer talks to the bridge through
ElectronService, which is fully typed viaElectronAPIincore/electron/electron-api.model.ts.
| Channel | Direction | Purpose |
|---|---|---|
dialog:open-json |
renderer β main | Open the native file dialog. |
file:read-json |
renderer β main | Read + validate a JSON file. |
menu:open-json |
main β renderer | Triggered by the "Open JSONβ¦" menu. |
Angular's application code targets ES2022 modules and is bundled by esbuild. Electron's main process must run as CommonJS (Node's require), so electron/tsconfig.json overrides module/moduleResolution and emits to dist-electron/.
Electron bridge is not availableβ you rannpm start(web only). Usenpm run devfor desktop features.- Blank Electron window β the Angular dev server isn't ready yet.
npm run devuseswait-onto handle this; if you launched Electron manually, ensurehttp://localhost:4200is up first. Cannot find module 'dist-electron/main.js'β runnpm run build:electrononce beforenpm run start:electron.- Build errors after upgrading Electron/Angular β delete
node_modules,dist,dist-electron, and~/.angularcache, thennpm installagain. - Code-signing errors on macOS/Windows β see https://www.electron.build/code-signing.
MIT β see LICENSE.