Skip to content

feat(frontend): redesign Electron app UI with a clean Lucide-icon system#156

Open
ashish921998 wants to merge 1 commit into
mainfrom
feat/electron-ui-redesign
Open

feat(frontend): redesign Electron app UI with a clean Lucide-icon system#156
ashish921998 wants to merge 1 commit into
mainfrom
feat/electron-ui-redesign

Conversation

@ashish921998
Copy link
Copy Markdown
Collaborator

What

Redesigns the placeholder Electron desktop shell into a clean, minimal, Codex-style UI. All changes are in frontend/src/main.ts — the single-file renderer that generates the entire HTML/CSS/JS. This rewrites only the three render functions (buildAppHTML, appCSS, appJS); the data model and Electron lifecycle are untouched (redesign → preserve).

Why

The shell looked unfinished, primarily because every icon was a placeholder Unicode glyph ( folder, sidebar toggle, search, a literal S for settings, stop, × close, chevron). The goal was a clean, minimal desktop UI with real icons, matching the project's stated frontend direction.

Changes

  • Real icons. Every glyph is replaced with inlined Lucide SVGs (ISC) via a small icon registry + icon() helper at a uniform 1.75 stroke. Inlined as raw markup because the renderer runs from a data: URL with no bundler.
  • Visual system locked. Cool-neutral zinc palette, semantic green/amber/red status, near-black primary button, blue reserved for focus rings only, and one documented radius scale (--r-sm/md/lg).
  • Detail. Per-worker status dots; titles ellipsize cleanly; removed dead cruft (three invisible window-dot buttons) and dev-jargon copy that was leaking into the terminal placeholder; dropped Inter from the font stack in favor of native SF Pro.

Bugs fixed (found while verifying the running app)

  1. Sidebar labels clipped — a CSS grid min-width: auto issue meant long worker titles never truncated, cutting off trailing labels like "Needs input" / "18m". Tracks now use grid-template-columns: minmax(0, 1fr).
  2. Settings modal clipped on the left — the 760px settings panel overflowed its fixed 620px <dialog> (latent in the original). The dialog now sizes to its content and width lives on each panel.

Reviewer notes

  • frontend/tsconfig.json is included: it excludes src/landing from the frontend TypeScript program so the Electron shell typecheck/build don't pull in the separate Next.js landing app. (This change predated the redesign in the working tree but is required for npm run typecheck/build to pass for the shell.)
  • The icon set is inlined rather than added as an npm dependency by design — the renderer has no build step.

Verification

  • Launched the built app and visually checked all four surfaces: orchestrator view, worker view, New Task modal, Settings modal.
  • npm run typecheck and npm run build pass. Source audit confirms zero em-dashes and zero remaining glyph icons.

🤖 Generated with Claude Code

Rework the placeholder Electron shell into a clean, minimal Codex-style
desktop UI. The single-file renderer (src/main.ts) builds the whole
HTML/CSS/JS, so this rewrites only the three render functions; the data
model and Electron lifecycle are untouched.

- Replace every placeholder glyph (folder, sidebar toggle, search, the
  literal "S" settings icon, stop, close, chevron) with real inlined
  Lucide SVGs via a small icon registry + icon() helper at a uniform
  1.75 stroke. No more programmer-art glyphs.
- Lock a cool-neutral zinc palette, semantic green/amber/red status, a
  near-black primary button, and one documented radius scale.
- Add per-worker status dots; long titles now ellipsize cleanly.
- Drop dead cruft (invisible window-dot buttons) and dev-jargon copy
  leaking into the terminal placeholder text.
- Drop Inter from the font stack in favor of the native SF Pro system.

Fixes two layout bugs found while verifying the running app:
- Sidebar worker labels were clipped (CSS grid min-width:auto); list
  tracks now use minmax(0, 1fr) so titles truncate and trailing status
  labels stay visible.
- Settings modal overflowed its fixed-width <dialog>; the dialog now
  sizes to its content and width lives on each panel.

Also exclude src/landing from the frontend tsconfig so the Electron
shell typecheck/build do not pull in the separate Next.js landing app.

Verified in the running app across the orchestrator, worker, new-task,
and settings surfaces; npm run typecheck and npm run build pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 8, 2026

Greptile Summary

This PR rewrites the Electron shell's three render functions (buildAppHTML, appCSS, appJS) into a polished Codex-style UI using inlined Lucide SVG icons, a zinc/semantic-color palette, and CSS grid layout fixes for sidebar truncation and the settings dialog. It also adds new Electron lifecycle code alongside a tsconfig.json update that excludes the Next.js landing app from the shell build.

  • UI redesign: All Unicode glyphs replaced with real Lucide SVGs via an inline icon registry; visual tokens documented in CSS custom properties; two latent CSS layout bugs fixed.
  • Lifecycle additions: A before-quit async guard that shows a native confirmation dialog before quitting, and a window-all-closed handler for non-macOS platforms.
  • Dead IPC registration: ipcMain.handle(\"app:confirm-stop-sessions\", ...) is registered but the renderer can never invoke it (no preload script, contextIsolation: true).

Confidence Score: 3/5

The UI and CSS changes are clean, but the new before-quit lifecycle handler can permanently trap the app if the native dialog ever rejects.

The async before-quit handler calls event.preventDefault() synchronously then awaits confirmStopSessions() with no error handling. A dialog failure leaves the app stuck in an un-quittable state requiring a force-kill. The dead ipcMain registration and cycling worker-ID scheme are minor but warrant cleanup.

frontend/src/main.ts — specifically the before-quit event handler and the ipcMain.handle registration

Important Files Changed

Filename Overview
frontend/src/main.ts Full rewrite of the Electron renderer into a clean Lucide-icon UI; introduces new lifecycle code with a missing error handler in the async before-quit path that can permanently trap the app, plus a dead ipcMain registration and a fragile worker-ID scheme.
frontend/tsconfig.json Adds src/landing to the TypeScript exclude list so the Electron shell build does not pull in the Next.js landing app; straightforward and correct.

Sequence Diagram

sequenceDiagram
    participant User
    participant Renderer as Renderer (data: URL)
    participant Main as main.ts (Main Process)
    participant Electron as Electron / OS

    User->>Renderer: Clicks New task
    Renderer->>Renderer: openNewTask() modal.showModal()
    User->>Renderer: Submits form
    Renderer->>Renderer: createWorker() workspace.workers.unshift()
    Renderer->>Renderer: renderWorkspaceList() + showWorker()

    User->>Electron: Cmd+Q or window close
    Electron->>Main: before-quit event
    Main->>Main: event.preventDefault()
    Main->>Electron: dialog.showMessageBox()
    Electron-->>Main: response 0 or 1
    alt User confirms
        Main->>Main: "quitting = true"
        Main->>Electron: app.quit()
    else User cancels or dialog throws
        Main-->>Electron: app stays open
    end

    Note over Main: ipcMain.handle registered but unreachable, no preload script
Loading

Reviews (1): Last reviewed commit: "feat(frontend): redesign Electron app UI..." | Re-trigger Greptile

Comment thread frontend/src/main.ts
Comment on lines +832 to +838
app.on("before-quit", async (event) => {
if (quitting) return;
event.preventDefault();
if (await confirmStopSessions()) {
quitting = true;
app.quit();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unhandled rejection in before-quit leaves app permanently unquittable

event.preventDefault() fires synchronously, then confirmStopSessions() is awaited — but there is no try/catch. If dialog.showMessageBox ever rejects (e.g., dialog is destroyed mid-show, Electron internal error), the async handler silently swallows the rejection, app.quit() is never called, and quitting stays false. Every subsequent quit attempt hits the same path and fails identically, leaving the user unable to close the app without force-killing it.

Comment thread frontend/src/main.ts
Comment on lines +822 to 823
ipcMain.handle("app:confirm-stop-sessions", confirmStopSessions);
createWindow();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The "app:confirm-stop-sessions" IPC channel is registered but can never be invoked: contextIsolation: true is set and no preload script is configured, so the renderer has no access to ipcRenderer. The handler is dead code; confirmStopSessions is only actually exercised via the before-quit event handler.

Suggested change
ipcMain.handle("app:confirm-stop-sessions", confirmStopSessions);
createWindow();
createWindow();

Comment thread frontend/src/main.ts
const agent = document.getElementById("task-agent").value;
const branch = document.getElementById("task-branch").value;
const prompt = promptBox.value.trim();
const id = workspace.id.slice(0, 4) + "-" + agent.replace("-", "").slice(0, 5) + "-" + String(Date.now()).slice(-4);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Using String(Date.now()).slice(-4) for ID uniqueness recycles every ~10 seconds (the last four decimal digits of the millisecond epoch roll over every 10,000 ms). Two workers created for the same workspace 10 seconds apart will get identical IDs, causing showWorker to silently select the wrong session after renderWorkspaceList re-renders.

Suggested change
const id = workspace.id.slice(0, 4) + "-" + agent.replace("-", "").slice(0, 5) + "-" + String(Date.now()).slice(-4);
const id = workspace.id.slice(0, 4) + "-" + agent.replace("-", "").slice(0, 5) + "-" + Math.random().toString(36).slice(2, 6);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant