Skip to content

feat: per-panel zoom system#9

Merged
Cheezeiii365 merged 62 commits into
mainfrom
dev
Mar 27, 2026
Merged

feat: per-panel zoom system#9
Cheezeiii365 merged 62 commits into
mainfrom
dev

Conversation

@Cheezeiii365
Copy link
Copy Markdown
Owner

@Cheezeiii365 Cheezeiii365 commented Mar 26, 2026

Summary

  • Add per-panel zoom (0.5×–2×) controlled via menu bar Cmd+=/Cmd+-/Cmd+0, targeting the active Dockview panel
  • Editor & terminal panes scale font size dynamically (CodeMirror compartment + xterm.js fontSize)
  • Browser panes zoom via native webContents.setZoomFactor() with toolbar +/-/readout controls and dedicated IPC channels
  • CSS-based panes (welcome, find-in-files, markdown preview, placeholder) use --panel-zoom CSS variable with calc() scaling
  • Shared zoom helpers in @aide/shared (adjustZoomFactor, clampZoomFactor, resetZoomFactor, zoomFactorToPercent)
  • Zoom state persisted per-panel in workspace runtime snapshots
  • Tightened tab bar and sidebar font sizes for better density
  • Updated tests and IDE_BUILD_PLAN.md

Issues

Test plan

  • Cmd+= / Cmd+- / Cmd+0 zooms the active panel (editor, terminal, browser, welcome, find-in-files, markdown preview)
  • Browser pane toolbar zoom +/-/readout buttons work correctly and readout resets on click
  • Zoom persists across workspace switches and app restarts
  • Zoom clamps to 0.5x-2x range
  • Terminal resizes PTY correctly after zoom change
  • Unit tests pass (pnpm test)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Browser panes: embed web content in editor panels with address bar, navigation (back/forward/reload), title/loading status, and a “New Browser Pane” modal.
    • Per-panel zoom: independent zoom per panel (0.5×–2×) with menu/toolbar shortcuts and persisted zoom per panel.
    • Session modes: choose shared-auth, workspace, or temporary session scopes for browser panes.
  • Documentation

    • Added detailed plans for browser panes and language-support (LSP).

Cheezeiii365 and others added 30 commits March 25, 2026 09:25
…d panel close

Replace scattered keydown listeners with a ShortcutManager singleton that
handles platform-aware modifier normalization (Cmd/Ctrl) and provides a
useShortcut React hook for component-scoped registration. Migrate existing
Cmd+Shift+T (new terminal) shortcut and add Cmd+B (sidebar toggle) and
Cmd+W (close active panel). Also add @electron/rebuild with postinstall
script to fix node-pty posix_spawnp errors from ABI mismatch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… tree

Replace planned chokidar integration with @parcel/watcher (same library
VS Code uses) for reliable macOS file watching. Native C++ FSEvents
integration with ignore patterns processed at the OS level — no more
CPU spikes or crashes from scanning node_modules/.git.

Adds right-click context menu on file tree items with New File, New
Folder, Rename (inline input), Delete (with confirmation), Copy Path,
and Reveal in Finder. File watcher provides incremental tree updates
with debounced event batching (150ms normal, 500ms for bulk operations).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Note cmux-inspired panel header buttons for new terminal, new browser,
and split operations in the Tiling Pane System section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix three production-only issues where the packaged .dmg rendered
differently from pnpm dev:

1. CSP under file:// protocol — move Content-Security-Policy from HTML
   meta tag to Electron session headers so 'self' resolves correctly
   for both http:// and file:// origins.

2. Dockview CSS cascade — strip dockview's runtime styleInject() via a
   Vite plugin so the manually imported CSS (with our theme overrides)
   is the single source of truth. Dockview's JS was injecting a <style>
   tag after our <link>, causing equal-specificity rules to win and
   override .dockview-theme-aide variables.

3. CodeMirror light theme — add a complete Atom One Light theme
   (editor chrome + syntax highlighting) so the editor matches the
   shell UI in light mode instead of falling back to bare defaults.

Also adds error handling and validation to theme IPC in useTheme.ts.

Closes #2, closes #3

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move all state reads into the setNodes functional updater so toggleExpand
never references the captured nodes closure. Add a loadingPaths ref to
guard against concurrent directory loads on rapid clicks, and remove
nodes from the dependency array since it is no longer referenced.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The @parcel/watcher native addon was ABI-incompatible with Electron 41,
causing napi_fatal_error on startup that crashed the main process and
broke terminal spawning. Replace with Node.js built-in fs.watch
(recursive mode) which uses macOS FSEvents natively without a native
addon. Also add cwd existence check in ptyManager to prevent
posix_spawnp failures from stale workspace paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…hting

Implement milestone 2.5 — side-by-side markdown preview in Dockview with
live editor content via editorContentBus pub/sub, marked v17 + DOMPurify
for XSS-safe rendering, and highlight.js for code block syntax highlighting
mapped to aIDE theme CSS variables. Includes Cmd+Shift+V toggle shortcut,
toast notification on .md file open, and auto-close on editor tab close.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… filter

Integrate simple-git to poll git status every 3s and display colored
file status badges (M/A/U/D) in the file tree with filename tinting.
Replace hardcoded branch name in StatusBar with live git branch.
Add search/filter input to sidebar that narrows the file tree by
substring match with ancestor directory auto-expansion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…imming

Virtualize file tree with @tanstack/react-virtual for large directory
performance (22px fixed rows, 15-item overscan, memoized DFS into
VirtualRow[] union type). Replace generic file/folder icons with
Seti-style colored SVG icons (~30 file types, ~15 special folders).
Add gitignored file dimming via git ls-files with 0.4 opacity and
italic text styling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…egration

Add worktree sidebar panel to create, remove, and switch between git
worktrees. File tree re-roots on worktree switch, terminals can switch
cwd via right-click context menu, and externally created worktrees are
auto-detected via 5s polling. Sidebar refactored into collapsible
SidebarSection components. New IPC channels and worktreeManager backend
following the gitStatus.ts polling pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…segmented toggle

Add VS Code-style active item accent border, inline hover action buttons
(terminal + more), "M" dirty badge with pulse animation, context menu
icons, segmented toggle replacing checkbox in create modal, CSS spinner
and success flash on submit, entry slide-in animations, empty state
guidance, and list-integrated add button.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…batching

React 18 may defer functional updaters when other state updates are
batched, causing needsLoad/nodeDepth variables set inside setNodes()
to be unreliable when checked outside. Read node state directly instead.

Also fix dev tools menu crash with BaseWindow + WebContentsView by
replacing role: 'toggleDevTools' with a manual handler.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Conflicted files were incorrectly mapped to 'M' (modified), hiding merge
conflict state. Add distinct 'C' status to GitFileStatus union and update
the file tree UI to display conflicts with error styling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add --text-on-accent CSS variable to both theme definitions and replace
all hardcoded #fff color values on accent backgrounds across modal,
toast, context-menu, and worktree-panel styles to support future custom
themes with light accent colors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Check that saved workspace and worktree paths exist on disk before
starting file watcher and git polling at launch. Clears stale paths
from the store if the directory no longer exists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
clearContent was deleting the listener set along with cached content,
which could orphan active subscriptions (e.g. markdown preview) and
prevent them from receiving future updates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…preview

Add cancellation guard and re-check in-memory content before applying
disk-read result, avoiding a race where a slow readFile response could
overwrite newer editor updates.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevent anchor clicks in the markdown preview from navigating the
Electron renderer. Links are now intercepted and routed through the
app actions openUrl system, ready for future browser pane integration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Docstrings generation was requested by @Cheezeiii365.

* #5 (comment)

The following files were modified:

* `packages/main/src/gitStatus.ts`
* `packages/main/src/index.ts`
* `packages/main/src/worktreeManager.ts`
* `packages/renderer/src/components/AppShell.tsx`
* `packages/renderer/src/components/DockviewContainer.tsx`
* `packages/renderer/src/components/FileTree/FileTree.tsx`
* `packages/renderer/src/components/FileTree/FileTreeItem.tsx`
* `packages/renderer/src/components/FileTree/FileTypeIcon.tsx`
* `packages/renderer/src/components/FileTree/fileIcons.ts`
* `packages/renderer/src/components/Sidebar.tsx`
* `packages/renderer/src/components/SidebarSection.tsx`
* `packages/renderer/src/components/StatusBar.tsx`
* `packages/renderer/src/components/Toast.tsx`
* `packages/renderer/src/components/WorktreePanel/CreateWorktreeModal.tsx`
* `packages/renderer/src/components/WorktreePanel/WorktreeItem.tsx`
* `packages/renderer/src/components/WorktreePanel/WorktreePanel.tsx`
* `packages/renderer/src/components/panes/EditorPane.tsx`
* `packages/renderer/src/components/panes/MarkdownPreviewPane.tsx`
* `packages/renderer/src/components/panes/TerminalPane.tsx`
* `packages/renderer/src/hooks/useWorktrees.ts`
* `packages/renderer/src/lib/editorContentBus.ts`

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… and worktree ideas

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nd shortcut wiring

Central CommandRegistry replaces ad-hoc shortcuts as the single source of
truth for all IDE actions. ShortcutManager enhanced with chord support and
conflict detection. Reusable SearchPanel powers both the command palette
(Cmd+Shift+P) and quick open (Cmd+P). Find-in-files (Cmd+Shift+F) uses
bundled ripgrep with streaming results, replace mode, and jump-to-line.
Remaining shortcuts wired: split editor, workspace placeholders, symbol
search placeholder.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… resolution

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
File watcher was single-rooted — switching worktrees killed the old watcher,
making changes in the original repo root invisible. EditorPane never subscribed
to file watch events, so even detected changes were ignored.

Rewrites fileWatcher.ts with a scopeId-keyed Map of watcher groups so multiple
roots can be watched simultaneously. Worktree switching now watches both repo
root and active worktree. Adds external change detection to EditorPane (auto-
reload clean files, toast prompt for dirty files, deletion detection). The
scopeId infrastructure prepares for Phase 4 cross-workspace file watching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…it, task system

Phase A: .aide folder infrastructure
- aideInit.ts: idempotent .aide/ creation, project type detection, default settings
- settingsResolver.ts: three-layer settings cascade (project > user > defaults)
- Shared types: AideProjectSettings, ResolvedSettings, AideLocalWorkspace, ProjectType

Phase B: Gitignore security audit
- gitignoreAudit.ts: parse gitignore, check 22 security patterns, append with confirmation
- GitignoreReviewModal: grouped checkbox UI for selecting patterns to add
- Command palette integration and dismissal tracking

Phase B.5: Task system
- taskRunner.ts: load/validate tasks.json, spawn via node-pty, dependsOn graph,
  compound tasks (parallel/sequence), auto-restart, timeout, user input prompts
- taskVariableResolver.ts: resolve ${workspaceRoot}, ${file}, ${branch}, ${input:id}, etc.
- problemMatcher.ts: 7 built-in matchers (tsc, eslint, python, gcc, go, pytest, generic)
- taskAutoDetect.ts: scan package.json/Cargo.toml/go.mod/etc. and offer to generate tasks.json
- useTasks hook, TaskInputModal, StatusBar task indicator, command palette commands

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cheezeiii365 and others added 21 commits March 26, 2026 16:29
Extract workspace serialization into a dedicated workspaceRuntimeSnapshots module
that captures and caches layout, terminal, sidebar, and active panel state in memory.
This enables instant restore on workspace switch without disk reads, consolidates
save logic from AppShell/workspaceSwitcher/DockviewContainer, and fixes workspace
close/remove to properly clean up task runners, watchers, and terminal state. Also
adds resolveAppDefaults for blank workspaces without a rootPath.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Docstrings generation was requested by @Cheezeiii365.

The following files were modified:

* `packages/main/src/aideInit.ts`
* `packages/main/src/fileWatcher.ts`
* `packages/main/src/gitStatus.ts`
* `packages/main/src/gitignoreAudit.ts`
* `packages/main/src/index.ts`
* `packages/main/src/problemMatcher.ts`
* `packages/main/src/ptyManager.ts`
* `packages/main/src/ripgrepSearch.ts`
* `packages/main/src/settingsResolver.ts`
* `packages/main/src/stateSerializer.ts`
* `packages/main/src/taskAutoDetect.ts`
* `packages/main/src/taskVariableResolver.ts`
* `packages/main/src/worktreeManager.ts`
* `packages/renderer/src/components/AppShell.tsx`
* `packages/renderer/src/components/CommandPalette.tsx`
* `packages/renderer/src/components/DockviewContainer.tsx`
* `packages/renderer/src/components/FileTree/FileTree.tsx`
* `packages/renderer/src/components/FileTree/FileTreeItem.tsx`
* `packages/renderer/src/components/GitignoreReviewModal.tsx`
* `packages/renderer/src/components/QuickOpen.tsx`
* `packages/renderer/src/components/SearchPanel.tsx`
* `packages/renderer/src/components/SidebarSection.tsx`
* `packages/renderer/src/components/StatusBar.tsx`
* `packages/renderer/src/components/TaskInputModal.tsx`
* `packages/renderer/src/components/Toast.tsx`
* `packages/renderer/src/components/WorkspaceContextMenu.tsx`
* `packages/renderer/src/components/WorkspaceRibbon.tsx`
* `packages/renderer/src/components/panes/EditorPane.tsx`
* `packages/renderer/src/components/panes/FindInFilesPane.tsx`
* `packages/renderer/src/components/panes/TerminalPane.tsx`
* `packages/renderer/src/components/panes/WelcomePane.tsx`
* `packages/renderer/src/hooks/useTasks.ts`
* `packages/renderer/src/hooks/useWorkspaces.ts`
* `packages/renderer/src/lib/CommandRegistry.ts`
* `packages/renderer/src/lib/ContextKeys.ts`
* `packages/renderer/src/lib/ShortcutManager.ts`
* `packages/renderer/src/lib/editorContentBus.ts`
* `packages/renderer/src/lib/editorDirtyState.ts`
* `packages/renderer/src/lib/editorStateCache.ts`
* `packages/renderer/src/lib/terminalState.ts`
* `packages/renderer/src/lib/workspaceRuntimeSnapshots.ts`
* `packages/renderer/src/lib/workspaceStateSerializer.ts`
* `packages/renderer/src/lib/workspaceSwitcher.ts`

These files were kept as they were:
* `packages/renderer/src/components/FileTree/FileTypeIcon.tsx`
* `packages/renderer/src/components/FileTree/fileIcons.ts`
* `packages/renderer/src/components/Sidebar.tsx`
* `packages/renderer/src/components/WorktreePanel/CreateWorktreeModal.tsx`
* `packages/renderer/src/components/WorktreePanel/WorktreeItem.tsx`
* `packages/renderer/src/components/WorktreePanel/WorktreePanel.tsx`
* `packages/renderer/src/components/panes/MarkdownPreviewPane.tsx`
* `packages/renderer/src/hooks/useWorktrees.ts`
* `packages/renderer/src/lib/defaultCommands.ts`

These files were ignored:
* `tests/unit/app.test.tsx`

These file types are not supported:
* `.aide/.gitignore`
* `.aide/settings.json`
* `.aide/tasks.json`
* `.gitignore`
* `IDE_BUILD_PLAN.md`
* `README.md`
* `WORKSPACE_PLAN.md`
* `electron-builder.yml`
* `package.json`
* `packages/renderer/src/styles/app-shell.css`
* `packages/renderer/src/styles/find-in-files.css`
* `packages/renderer/src/styles/gitignore-modal.css`
* `packages/renderer/src/styles/search-panel.css`
* `packages/renderer/src/styles/welcome-pane.css`
The settings resolver already merged exclude patterns from project,
user, and built-in defaults, but the ripgrep search never read them.
Now the SEARCH_START handler resolves settings and converts enabled
exclude entries into --glob '!pattern' args so Find in Files actually
respects filesExclude and searchExclude from .aide/settings.json.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a sequence-number cancellation pattern to prevent race conditions
when rapid workspace switches cause overlapping activateWorkspace calls,
which could leave watchers/pollers pointed at the wrong workspace root.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The pytest problem matcher was registered with line: 0, but
createMatcherFromDef() returned null whenever lineNum === 0, making the
matcher dead code. Make line optional in MatcherDef and TaskDiagnostic,
and relax the guard to only require a non-empty file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents the pty onExit callback from overwriting 'killed' status with
'succeeded'/'failed' and from auto-restarting tasks that were intentionally
terminated (e.g. workspace switch, app quit, timeout).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verify that the text at each recorded position still matches before
splicing, skipping stale hits instead of silently corrupting the file.
Also fix handleReplaceAll to check per-file results instead of
unconditionally clearing state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…failures

onExit removed executions from `this.running` before waitForCompletion could
poll the terminal state, causing all finished tasks to resolve as exit code 0.
This broke dependency chains and sequential compounds that rely on exit codes
to abort on failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a user cancels an input prompt or promptBefore confirmation,
runSingle() returns a killed execution with an empty ptyId. Both the
dependency loop and sequential compound loop only checked ptyId for
failure, silently treating cancellations as success and continuing to
run subsequent tasks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Files starting with a dot (e.g. .gitignore) returned the entire
filename as the extension because lastIndexOf('.') returned 0.
Change the guard from >= 0 to > 0 so dotfiles without a secondary
extension correctly return an empty string.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The totalFiles count was always reported as 0 because the Set was
created empty at close time after fileMap had already been cleared
by flush(). Use a running Set that accumulates file paths as matches
arrive.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two fixes in taskAutoDetect:
1. Skip plain 'python' detection when 'python-pyproject' is also
   found, preventing duplicate task IDs (python:test, python:run).
2. Create .aide/ directory in generateTasksFile before writing, so
   callers that skip ensureAideFolder() (like TASK_GENERATE) don't
   hit ENOENT.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Confirm dialogs rendered both a Cancel and a No button, both calling
handleCancel. Now confirm shows No/Yes and other types show Cancel/OK.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Check for 'error' in result before accessing result.missing, matching
the pattern used by the aideInit and generateTasks handlers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
If appendToGitignore throws, the modal was stuck with a disabled
button. Wrap in try/catch and reset submitting state on error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a unified zoom system that lets each Dockview panel independently control its zoom level (0.5×–2×). Menu bar Cmd+=/Cmd+-/Cmd+0 zooms the active panel. Editor and terminal panes scale font size via CodeMirror compartment and xterm.js options. Browser panes use native webContents.setZoomFactor() with toolbar ±/readout controls. CSS-based panes (welcome, find-in-files, markdown preview, placeholder) use a --panel-zoom CSS variable. Zoom state is persisted per-panel in workspace runtime snapshots.

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

coderabbitai Bot commented Mar 26, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 00d2fd92-392c-4854-9fb2-94823381cf5c

📥 Commits

Reviewing files that changed from the base of the PR and between 2b78b25 and 3bfdaa7.

📒 Files selected for processing (1)
  • packages/renderer/src/lib/panelZoom.ts

📝 Walkthrough

Walkthrough

Adds per-panel zoom and browser-pane support: new main-process BrowserPaneManager and IPC, renderer BrowserPane UI and modal, zoom utilities and propagation to editor/terminal/CSS panes, workspace serialization of browser panes, tests, and related styles and shared types.

Changes

Cohort / File(s) Summary
Documentation
IDE_BUILD_PLAN.md, LSPPLAN.md, browserPanes.md
Added/updated design and roadmap docs: per-panel zoom notes, full LSP plan, and detailed Browser Panes design (IPC contract, session modes, lifecycle, open questions).
Main Process - Browser Pane Manager
packages/main/src/browserPaneManager.ts
New exported BrowserPaneManager class: create/destroy panes, navigate/back/forward/reload, zoom get/set/adjust, host updates, visibility/overlay suppression, and event forwarding to renderer.
Main Process - IPC & Menu
packages/main/src/index.ts, packages/main/src/preload.ts
Wired BrowserPaneManager into main, replaced role-based zoom menu with IPC APP_ZOOM_COMMAND, added BROWSER_* and BROWSER_ZOOM_* IPC handlers and exposed browser/zoom APIs in preload.
Renderer - App Integration
packages/renderer/src/components/AppShell.tsx
Integrated browser-pane lifecycle and modal, workspace cleanup, overlay suppression, focus tracking, zoom command handling, new browser commands, and persistence wiring.
Renderer - Dockview Registration
packages/renderer/src/components/DockviewContainer.tsx
Registered BrowserPane under browserPane in Dockview components map.
Renderer - Browser Pane UI & Modal
packages/renderer/src/components/panes/BrowserPane.tsx, packages/renderer/src/components/NewBrowserPaneModal.tsx
Added BrowserPane component (nav chrome, URL input, zoom, host element, ResizeObserver, IPC sync) and NewBrowserPaneModal component for pane creation.
Renderer - Per-pane Zoom Propagation
packages/renderer/src/components/panes/...
Added optional zoomFactor param across panes; Editor uses CodeMirror editorMetricsCompartment, Terminal sets xterm font size and fit, CSS-based panes use --panel-zoom.
Renderer - State & Utilities
packages/renderer/src/lib/browserState.ts, packages/renderer/src/lib/panelZoom.ts, packages/renderer/src/lib/editorTheme.ts, packages/renderer/src/lib/terminalState.ts, packages/renderer/src/lib/defaultCommands.ts
New browserState types/helpers, panel zoom helpers, editor metrics compartment and helpers, terminal default zoomFactor, and minor defaultCommands simplification.
Renderer - Workspace Serialization
packages/renderer/src/lib/workspaceRuntimeSnapshots.ts, packages/renderer/src/lib/workspaceStateSerializer.ts
Pass workspaceId into serialization and include browserPanes in serialized workspace state via serializeBrowserPaneState.
Renderer - Styles
packages/renderer/src/styles/browser-pane.css, .../app-shell.css, .../dockview-theme.css, .../file-tree.css, .../find-in-files.css, .../markdown-preview.css, .../welcome-pane.css
Added browser pane CSS; introduced --panel-zoom and scaled typography via calc() across multiple panes; small dockview and file-tree size tweaks.
Shared Types & Zoom Utilities
packages/shared/src/index.ts, packages/shared/src/zoom.ts
New zoom utilities/constants and exports, added IPC channel names and browser-pane payload/types, extended AideLocalState with browserPanes, and expanded WindowApi with browser/zoom methods/events.
Tests & Test Harness
tests/unit/*, packages/shared/src/index.test.ts, tests/unit/app.test.tsx
Updated test harness to async renderApp, expanded mock API for browser/zoom, and added tests for browserState, editor metrics, panelZoom, workspace serialization, and zoom helpers.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AppShell as AppShell (Renderer)
    participant Modal as NewBrowserPaneModal (Renderer)
    participant Preload as Preload API
    participant Main as Main Process
    participant BPM as BrowserPaneManager
    participant WebView as WebContentsView

    User->>AppShell: Click "New Browser Pane"
    AppShell->>Modal: show modal
    User->>Modal: submit(sessionMode, url)
    Modal->>AppShell: onSubmit(sessionMode, url)
    AppShell->>Preload: browserCreate(paneId, workspaceId, sessionMode)
    Preload->>Main: IPC BROWSER_CREATE
    Main->>BPM: create(paneId, workspaceId, sessionMode)
    BPM->>WebView: instantiate WebContentsView(session)
    BPM-->>Main: create success
    Main-->>Preload: IPC response
    Preload-->>AppShell: promise resolves
    AppShell->>AppShell: add Dockview panel (BrowserPane)
    AppShell->>Preload: browserNavigate(paneId, url)
    Preload->>Main: IPC BROWSER_NAVIGATE
    Main->>BPM: navigate(paneId, url)
    BPM->>WebView: loadURL(normalizedUrl)
    WebView-->>BPM: navigation events (title, loading, did-navigate)
    BPM->>Main: emit IPC events (BROWSER_DID_NAVIGATE, PAGE_TITLE_UPDATED, LOADING_CHANGED)
    Main-->>Preload: IPC forward
    Preload-->>AppShell: onBrowserDidNavigate/onBrowserTitleUpdated...
    AppShell->>BrowserPane: update UI/state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped in with a panel and zoom,

A tiny webview found its room.
Editors, terminals, and previews align,
Fonts stretch snug — one zoom at a time.
Rabbit cheers: panes scaled and in bloom!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: per-panel zoom system' accurately summarizes the main change—adding a comprehensive per-panel zoom feature across all pane types with persistence and zoom helpers.
Docstring Coverage ✅ Passed Docstring coverage is 97.44% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/renderer/src/lib/terminalState.ts (1)

54-68: ⚠️ Potential issue | 🟠 Major

Restored terminal params currently reset zoom to 1×

Line 67 forces zoomFactor: 1 during restore, so restored terminal params cannot preserve a previously saved zoom value.

Suggested fix
 export function createRestoredTerminalPanelParams(
   terminalId: string,
   workspaceId?: string,
   worktreePath?: string,
   title?: string,
   shell?: string,
+  zoomFactor = 1,
 ): TerminalPanelParams {
   return {
     terminalId,
     workspaceId,
     worktreePath,
     title,
     shell,
-    zoomFactor: 1,
+    zoomFactor,
   }
 }

You should also include zoomFactor in serialized terminal state and in the shared TerminalState type so this value can round-trip.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/renderer/src/lib/terminalState.ts` around lines 54 - 68,
createRestoredTerminalPanelParams currently forces zoomFactor: 1 so restored
terminals lose saved zoom; update the createRestoredTerminalPanelParams
signature and return to accept and propagate a zoomFactor (do not hardcode 1)
and ensure TerminalPanelParams includes zoomFactor, then add zoomFactor to the
serialized terminal state and the shared TerminalState type so the value is
included when serializing/deserializing/restoring terminals (update any
serialize/deserialize or toJSON/fromJSON logic that handles terminal state to
round-trip zoomFactor).
packages/shared/src/index.ts (1)

321-329: ⚠️ Potential issue | 🟠 Major

Browser pane state is serialized but restoration logic is missing.

AideLocalState.browserPanes is persisted via serializeBrowserPaneState() in workspaceStateSerializer.ts (line 78), but the workspace restoration flow in workspaceSwitcher.ts has no corresponding logic to read this field and recreate browser panels. The restoration process (lines 99-129) handles layout, sidebar, and active tab, but savedState.browserPanes is never accessed. This means browser panes won't survive app restarts even though their state is saved.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/src/index.ts` around lines 321 - 329, Saved browser pane
state (AideLocalState.browserPanes) is serialized by serializeBrowserPaneState()
but never restored; update the workspace restoration in workspaceSwitcher.ts to
read savedState.browserPanes, iterate each BrowserPaneState and recreate the
browser panels (via the existing panel creation/dispatch API used elsewhere) so
persisted panes are rehydrated at startup; use the inverse of
serializeBrowserPaneState (or a deserializer/helper) to convert stored entries
back to BrowserPaneState and call the appropriate recreate/open function for
each pane.
🧹 Nitpick comments (8)
tests/unit/panelZoom.test.ts (1)

1-16: Good test coverage for core functionality.

The tests verify the essential behavior of getPanelZoomFactor and updatePanelZoomParams. Consider adding edge case tests for boundary values (0.5, 2.0 clamp limits) and overwriting an existing zoomFactor.

💡 Optional: Additional test cases
it('extracts existing zoom factor from params', () => {
  expect(getPanelZoomFactor({ zoomFactor: 1.5 })).toBe(1.5)
})

it('overwrites existing zoom factor', () => {
  expect(updatePanelZoomParams({ zoomFactor: 1.0 }, 1.5)).toEqual({
    zoomFactor: 1.5,
  })
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/panelZoom.test.ts` around lines 1 - 16, Add unit tests to cover
boundary clamping and overwriting behavior: add cases in
tests/unit/panelZoom.test.ts that assert getPanelZoomFactor({ zoomFactor: 0.5 })
returns 0.5 and getPanelZoomFactor({ zoomFactor: 2.0 }) returns 2.0 (or that
inputs outside allowed range are clamped if implementation does that), add a
test that getPanelZoomFactor({ zoomFactor: 1.5 }) returns 1.5, and add a test
that updatePanelZoomParams({ zoomFactor: 1.0 }, 1.5) returns an object with
zoomFactor: 1.5 (confirming overwrite) as well as a test that
updatePanelZoomParams({ filePath: '/tmp/test.ts', zoomFactor: 0.8 }, 2.0)
updates zoomFactor to 2.0 while preserving other fields.
tests/unit/workspaceStateSerializer.test.ts (1)

11-25: Consider adding test coverage for browser pane serialization.

The test validates sidebar state but doesn't exercise the new browserPanes serialization path added in this PR. Given that serializeBrowserPaneState is now called within serializeWorkspaceState, a test case with mock browser pane panels would strengthen confidence in the integration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/workspaceStateSerializer.test.ts` around lines 11 - 25, Add a unit
test that supplies a dockviewApi with one or more panels representing browser
panes (e.g., panels containing type or view id that serializeBrowserPaneState
expects) and an activePanel as needed, then call serializeWorkspaceState and
assert that the returned object includes a browserPanes key with the expected
serialized structure; specifically target serializeWorkspaceState and
serializeBrowserPaneState so the test verifies browser pane serialization is
exercised and the browserPanes shape matches the output from
serializeBrowserPaneState.
packages/renderer/src/lib/workspaceStateSerializer.ts (1)

15-27: Update JSDoc to document the new workspaceId parameter.

The JSDoc block lists parameters but omits the newly added workspaceId. This creates documentation drift.

📝 Proposed JSDoc update
 /**
  * Build an AideLocalState representing the current workspace layout, open editor tabs, active tab, and sidebar settings.
  *
  * `@param` dockviewApi - The Dockview API instance to read panels and layout from, or `null` if unavailable
+ * `@param` workspaceId - The workspace ID to filter browser panes by, or `null` if unavailable
  * `@param` sidebarWidth - The current sidebar width in pixels
  * `@param` sidebarCollapsed - Whether the sidebar is collapsed
- * `@returns` The serialized AideLocalState with `layout`, `openTabs`, `activeTabPath`, `sidebarWidth`, `sidebarCollapsed`, and `sidebarSections`
+ * `@returns` The serialized AideLocalState with `layout`, `openTabs`, `activeTabPath`, `sidebarWidth`, `sidebarCollapsed`, `sidebarSections`, and `browserPanes`
  */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/renderer/src/lib/workspaceStateSerializer.ts` around lines 15 - 27,
The JSDoc for serializeWorkspaceState is missing documentation for the newly
added workspaceId parameter; update the comment block above function
serializeWorkspaceState to include a new `@param` entry describing workspaceId
(type and meaning—e.g., the current workspace identifier or null when
unavailable) and ensure the `@returns` and existing param descriptions remain
accurate and consistent with the function signature.
packages/renderer/src/components/NewBrowserPaneModal.tsx (1)

40-48: Consider deriving session mode options from the type definition.

The select options are hardcoded strings that must stay in sync with the BrowserSessionMode type definition in packages/shared/src/index.ts. If the type is extended with new modes, this component won't show them and TypeScript won't catch the omission.

This is a minor maintainability concern for future changes.

♻️ Optional: Extract options to a shared constant
// In packages/shared/src/index.ts
export const BROWSER_SESSION_MODES: readonly BrowserSessionMode[] = [
  'shared-auth',
  'workspace', 
  'temporary',
] as const

// In NewBrowserPaneModal.tsx
import { BROWSER_SESSION_MODES } from '@aide/shared'
// ...
{BROWSER_SESSION_MODES.map((mode) => (
  <option key={mode} value={mode}>{formatSessionMode(mode)}</option>
))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/renderer/src/components/NewBrowserPaneModal.tsx` around lines 40 -
48, The select options in NewBrowserPaneModal are hardcoded and can get out of
sync with the BrowserSessionMode type; introduce or import a single
source-of-truth array (e.g., export const BROWSER_SESSION_MODES: readonly
BrowserSessionMode[] from the shared package) and replace the hardcoded <option>
elements by mapping over BROWSER_SESSION_MODES in NewBrowserPaneModal, using
each mode as key/value and a small formatter (e.g., formatSessionMode) for
display; update imports to bring in BROWSER_SESSION_MODES (and the formatter if
created) and remove the literal strings so new modes added to BrowserSessionMode
automatically appear.
packages/shared/src/index.test.ts (1)

31-45: Consider adding edge case tests for zoom helpers.

The current tests cover basic functionality, but consider adding tests for edge cases:

  • Non-finite values (NaN, Infinity)
  • Exact boundary values (0.5, 2.0)
  • Negative zoom factors
  • Step direction at boundaries (e.g., stepZoomFactor(0.5, -1) should stay at 0.5)
🧪 Suggested additional test cases
 describe('zoom helpers', () => {
   it('clamps zoom factors into the supported range', () => {
     expect(clampZoomFactor(0.1)).toBe(0.5)
     expect(clampZoomFactor(3)).toBe(2)
+    expect(clampZoomFactor(0.5)).toBe(0.5)  // exact min
+    expect(clampZoomFactor(2)).toBe(2)      // exact max
+    expect(clampZoomFactor(NaN)).toBe(1)    // non-finite fallback
+    expect(clampZoomFactor(-1)).toBe(0.5)   // negative
   })

   it('adjusts zoom factors in 10 percent increments', () => {
     expect(adjustZoomFactor(1, 0.1)).toBe(1.1)
     expect(stepZoomFactor(1.1, -1)).toBe(1)
+    expect(stepZoomFactor(0.5, -1)).toBe(0.5)  // clamped at min
+    expect(stepZoomFactor(2, 1)).toBe(2)       // clamped at max
   })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/src/index.test.ts` around lines 31 - 45, Add unit tests for
edge cases in the zoom helpers: add assertions for non-finite inputs (NaN and
Infinity) passed to clampZoomFactor/adjustZoomFactor/zoomFactorToPercent to
ensure they behave predictably, add tests using exact boundary values 0.5 and
2.0 for clampZoomFactor and stepZoomFactor to confirm they clamp correctly, add
a test for a negative zoom factor (e.g., clampZoomFactor(-1) and
adjustZoomFactor(-1, 0.1)) to verify handling, and add step-direction boundary
tests such as stepZoomFactor(0.5, -1) and stepZoomFactor(2.0, 1) to assert they
remain within bounds; reference the functions clampZoomFactor, adjustZoomFactor,
stepZoomFactor, and zoomFactorToPercent when adding these cases.
packages/renderer/src/components/panes/BrowserPane.tsx (2)

44-66: Unused api in dependency array.

The pushHostUpdate callback includes api in its dependency array (line 66) but doesn't use it in the function body. This causes unnecessary re-creation of the callback.

♻️ Proposed fix
-  }, [api, paneId, workspaceId])
+  }, [paneId, workspaceId])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/renderer/src/components/panes/BrowserPane.tsx` around lines 44 - 66,
The useCallback pushHostUpdate re-creates unnecessarily because its dependency
array includes api which is not referenced inside the function; remove api from
the dependency list so the hook depends only on paneId and workspaceId (and any
refs) or alternatively use the api variable consistently inside pushHostUpdate
instead of window.api to justify keeping it—update the dependency array for
pushHostUpdate (function name) accordingly and ensure lastHostUpdateRef, hostRef
and chromeRef remain accessed as refs rather than added to the dependencies.

119-129: Unused api in dependency array.

Similar issue: requestInitialLoad includes api in dependencies but only uses commitNavigation.

♻️ Proposed fix
-  }, [api, commitNavigation])
+  }, [commitNavigation])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/renderer/src/components/panes/BrowserPane.tsx` around lines 119 -
129, The requestInitialLoad callback lists an unused dependency `api`, which can
cause unnecessary re-creations; update the dependency array of
requestInitialLoad to only include the actually used symbol(s) (e.g., change
from [api, commitNavigation] to [commitNavigation]) so that requestInitialLoad
(which references hostRef, paramsRef, browserReadyRef, initialLoadRequestedRef
and calls commitNavigation) only re-renders when commitNavigation changes.
packages/shared/src/index.ts (1)

346-353: zoomFactor is optional but always serialized.

BrowserPaneState.zoomFactor is marked optional (zoomFactor?: number), but serializeBrowserPaneState() in packages/renderer/src/lib/browserState.ts always includes it. Consider making it required for consistency, or document why it's optional (e.g., backwards compatibility with older persisted state).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/src/index.ts` around lines 346 - 353, The BrowserPaneState
interface declares zoomFactor as optional but serializeBrowserPaneState() always
writes it; update one side for consistency: either make zoomFactor required on
BrowserPaneState (remove the ? from zoomFactor) so serializeBrowserPaneState()
and consumers can assume it exists, or change serializeBrowserPaneState() to
only include zoomFactor when defined (and add a comment explaining
backward-compatibility if needed); reference BrowserPaneState and
serializeBrowserPaneState in your change so callers and persisted state handling
are updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@browserPanes.md`:
- Around line 124-133: The fenced ASCII-art block is missing a language tag;
update the fence that begins with the ┌ box (the ASCII-art placeholder div
block) to include a language identifier (e.g., add ```text instead of ``` ) so
the Markdown linter rule MD040 is satisfied and the block is treated as plain
text.

In `@packages/main/src/browserPaneManager.ts`:
- Around line 32-41: The normalizeUrl function can still throw when the fallback
new URL(`https://${trimmed}`) is given a completely malformed input like "://";
update normalizeUrl to catch errors from the fallback attempt as well and return
a safe default (e.g., empty string) instead of letting an exception propagate.
Specifically, keep the initial trim and empty check, attempt new URL(trimmed) in
a try, and if that fails attempt new URL(`https://${trimmed}`) inside a second
try/catch (or a single try with nested catches) and return '' on any remaining
error; refer to the normalizeUrl function and the trimmed variable to locate the
change.

In `@packages/renderer/src/components/panes/EditorPane.tsx`:
- Around line 151-159: When restoring a cached editor state in the view.dispatch
block, detect whether the cached state already contains the
editorMetricsCompartment and use StateEffect.reconfigure (or the
compartment.reconfigure effect) in that case, otherwise use
StateEffect.appendConfig.of(...) as current code does; specifically, inspect the
cached state's extensions for editorMetricsCompartment (the compartment symbol)
and then call either a reconfigure effect with
getEditorMetricsExtension(Math.round(EDITOR_BASE_FONT_SIZE *
getPanelZoomFactor(params))) or
appendConfig.of([...editorMetricsCompartment.of(getEditorMetricsExtension(...))])
to avoid duplicating compartments or failing for older cached states.

In `@packages/renderer/src/lib/panelZoom.ts`:
- Around line 5-14: getPanelZoomFactor and updatePanelZoomParams currently
accept any numeric value; clamp zoom values to the 0.5–2.0 range and reject
non-finite numbers. In getPanelZoomFactor (function name) validate the extracted
zoomFactor is a finite number and return Math.min(2, Math.max(0.5, zoomFactor))
(or 1 if invalid). In updatePanelZoomParams (function name) clamp the provided
zoomFactor parameter to the same 0.5–2.0 bounds before merging it into the
returned object so saved/externally supplied values cannot break rendering.

---

Outside diff comments:
In `@packages/renderer/src/lib/terminalState.ts`:
- Around line 54-68: createRestoredTerminalPanelParams currently forces
zoomFactor: 1 so restored terminals lose saved zoom; update the
createRestoredTerminalPanelParams signature and return to accept and propagate a
zoomFactor (do not hardcode 1) and ensure TerminalPanelParams includes
zoomFactor, then add zoomFactor to the serialized terminal state and the shared
TerminalState type so the value is included when
serializing/deserializing/restoring terminals (update any serialize/deserialize
or toJSON/fromJSON logic that handles terminal state to round-trip zoomFactor).

In `@packages/shared/src/index.ts`:
- Around line 321-329: Saved browser pane state (AideLocalState.browserPanes) is
serialized by serializeBrowserPaneState() but never restored; update the
workspace restoration in workspaceSwitcher.ts to read savedState.browserPanes,
iterate each BrowserPaneState and recreate the browser panels (via the existing
panel creation/dispatch API used elsewhere) so persisted panes are rehydrated at
startup; use the inverse of serializeBrowserPaneState (or a deserializer/helper)
to convert stored entries back to BrowserPaneState and call the appropriate
recreate/open function for each pane.

---

Nitpick comments:
In `@packages/renderer/src/components/NewBrowserPaneModal.tsx`:
- Around line 40-48: The select options in NewBrowserPaneModal are hardcoded and
can get out of sync with the BrowserSessionMode type; introduce or import a
single source-of-truth array (e.g., export const BROWSER_SESSION_MODES: readonly
BrowserSessionMode[] from the shared package) and replace the hardcoded <option>
elements by mapping over BROWSER_SESSION_MODES in NewBrowserPaneModal, using
each mode as key/value and a small formatter (e.g., formatSessionMode) for
display; update imports to bring in BROWSER_SESSION_MODES (and the formatter if
created) and remove the literal strings so new modes added to BrowserSessionMode
automatically appear.

In `@packages/renderer/src/components/panes/BrowserPane.tsx`:
- Around line 44-66: The useCallback pushHostUpdate re-creates unnecessarily
because its dependency array includes api which is not referenced inside the
function; remove api from the dependency list so the hook depends only on paneId
and workspaceId (and any refs) or alternatively use the api variable
consistently inside pushHostUpdate instead of window.api to justify keeping
it—update the dependency array for pushHostUpdate (function name) accordingly
and ensure lastHostUpdateRef, hostRef and chromeRef remain accessed as refs
rather than added to the dependencies.
- Around line 119-129: The requestInitialLoad callback lists an unused
dependency `api`, which can cause unnecessary re-creations; update the
dependency array of requestInitialLoad to only include the actually used
symbol(s) (e.g., change from [api, commitNavigation] to [commitNavigation]) so
that requestInitialLoad (which references hostRef, paramsRef, browserReadyRef,
initialLoadRequestedRef and calls commitNavigation) only re-renders when
commitNavigation changes.

In `@packages/renderer/src/lib/workspaceStateSerializer.ts`:
- Around line 15-27: The JSDoc for serializeWorkspaceState is missing
documentation for the newly added workspaceId parameter; update the comment
block above function serializeWorkspaceState to include a new `@param` entry
describing workspaceId (type and meaning—e.g., the current workspace identifier
or null when unavailable) and ensure the `@returns` and existing param
descriptions remain accurate and consistent with the function signature.

In `@packages/shared/src/index.test.ts`:
- Around line 31-45: Add unit tests for edge cases in the zoom helpers: add
assertions for non-finite inputs (NaN and Infinity) passed to
clampZoomFactor/adjustZoomFactor/zoomFactorToPercent to ensure they behave
predictably, add tests using exact boundary values 0.5 and 2.0 for
clampZoomFactor and stepZoomFactor to confirm they clamp correctly, add a test
for a negative zoom factor (e.g., clampZoomFactor(-1) and adjustZoomFactor(-1,
0.1)) to verify handling, and add step-direction boundary tests such as
stepZoomFactor(0.5, -1) and stepZoomFactor(2.0, 1) to assert they remain within
bounds; reference the functions clampZoomFactor, adjustZoomFactor,
stepZoomFactor, and zoomFactorToPercent when adding these cases.

In `@packages/shared/src/index.ts`:
- Around line 346-353: The BrowserPaneState interface declares zoomFactor as
optional but serializeBrowserPaneState() always writes it; update one side for
consistency: either make zoomFactor required on BrowserPaneState (remove the ?
from zoomFactor) so serializeBrowserPaneState() and consumers can assume it
exists, or change serializeBrowserPaneState() to only include zoomFactor when
defined (and add a comment explaining backward-compatibility if needed);
reference BrowserPaneState and serializeBrowserPaneState in your change so
callers and persisted state handling are updated accordingly.

In `@tests/unit/panelZoom.test.ts`:
- Around line 1-16: Add unit tests to cover boundary clamping and overwriting
behavior: add cases in tests/unit/panelZoom.test.ts that assert
getPanelZoomFactor({ zoomFactor: 0.5 }) returns 0.5 and getPanelZoomFactor({
zoomFactor: 2.0 }) returns 2.0 (or that inputs outside allowed range are clamped
if implementation does that), add a test that getPanelZoomFactor({ zoomFactor:
1.5 }) returns 1.5, and add a test that updatePanelZoomParams({ zoomFactor: 1.0
}, 1.5) returns an object with zoomFactor: 1.5 (confirming overwrite) as well as
a test that updatePanelZoomParams({ filePath: '/tmp/test.ts', zoomFactor: 0.8 },
2.0) updates zoomFactor to 2.0 while preserving other fields.

In `@tests/unit/workspaceStateSerializer.test.ts`:
- Around line 11-25: Add a unit test that supplies a dockviewApi with one or
more panels representing browser panes (e.g., panels containing type or view id
that serializeBrowserPaneState expects) and an activePanel as needed, then call
serializeWorkspaceState and assert that the returned object includes a
browserPanes key with the expected serialized structure; specifically target
serializeWorkspaceState and serializeBrowserPaneState so the test verifies
browser pane serialization is exercised and the browserPanes shape matches the
output from serializeBrowserPaneState.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9e4e26b6-ed5a-4a50-864b-296ffcc6471c

📥 Commits

Reviewing files that changed from the base of the PR and between bbbdcaa and a77015b.

📒 Files selected for processing (40)
  • IDE_BUILD_PLAN.md
  • LSPPLAN.md
  • browserPanes.md
  • packages/main/src/browserPaneManager.ts
  • packages/main/src/index.ts
  • packages/main/src/preload.ts
  • packages/renderer/src/components/AppShell.tsx
  • packages/renderer/src/components/DockviewContainer.tsx
  • packages/renderer/src/components/FileTree/FileTypeIcon.tsx
  • packages/renderer/src/components/NewBrowserPaneModal.tsx
  • packages/renderer/src/components/Sidebar.tsx
  • packages/renderer/src/components/panes/BrowserPane.tsx
  • packages/renderer/src/components/panes/EditorPane.tsx
  • packages/renderer/src/components/panes/FindInFilesPane.tsx
  • packages/renderer/src/components/panes/MarkdownPreviewPane.tsx
  • packages/renderer/src/components/panes/PlaceholderPane.tsx
  • packages/renderer/src/components/panes/TerminalPane.tsx
  • packages/renderer/src/components/panes/WelcomePane.tsx
  • packages/renderer/src/lib/browserState.ts
  • packages/renderer/src/lib/defaultCommands.ts
  • packages/renderer/src/lib/editorTheme.ts
  • packages/renderer/src/lib/panelZoom.ts
  • packages/renderer/src/lib/terminalState.ts
  • packages/renderer/src/lib/workspaceRuntimeSnapshots.ts
  • packages/renderer/src/lib/workspaceStateSerializer.ts
  • packages/renderer/src/styles/app-shell.css
  • packages/renderer/src/styles/browser-pane.css
  • packages/renderer/src/styles/dockview-theme.css
  • packages/renderer/src/styles/file-tree.css
  • packages/renderer/src/styles/find-in-files.css
  • packages/renderer/src/styles/markdown-preview.css
  • packages/renderer/src/styles/welcome-pane.css
  • packages/shared/src/index.test.ts
  • packages/shared/src/index.ts
  • packages/shared/src/zoom.ts
  • tests/unit/app.test.tsx
  • tests/unit/browserState.test.ts
  • tests/unit/editorTheme.test.ts
  • tests/unit/panelZoom.test.ts
  • tests/unit/workspaceStateSerializer.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{css,scss,tsx,ts}

📄 CodeRabbit inference engine (CLAUDE.md)

Use CSS variable token system with data-theme attribute for theming, supporting dark and light mode

Files:

  • packages/renderer/src/lib/workspaceRuntimeSnapshots.ts
  • packages/renderer/src/components/FileTree/FileTypeIcon.tsx
  • packages/renderer/src/components/DockviewContainer.tsx
  • packages/renderer/src/styles/dockview-theme.css
  • packages/renderer/src/lib/defaultCommands.ts
  • packages/renderer/src/components/panes/PlaceholderPane.tsx
  • packages/renderer/src/components/Sidebar.tsx
  • tests/unit/panelZoom.test.ts
  • tests/unit/editorTheme.test.ts
  • packages/renderer/src/styles/file-tree.css
  • packages/renderer/src/styles/welcome-pane.css
  • packages/renderer/src/styles/app-shell.css
  • packages/renderer/src/components/panes/TerminalPane.tsx
  • tests/unit/browserState.test.ts
  • packages/renderer/src/styles/markdown-preview.css
  • packages/renderer/src/components/panes/EditorPane.tsx
  • packages/renderer/src/lib/workspaceStateSerializer.ts
  • packages/renderer/src/styles/browser-pane.css
  • tests/unit/workspaceStateSerializer.test.ts
  • packages/renderer/src/lib/terminalState.ts
  • packages/renderer/src/lib/panelZoom.ts
  • packages/shared/src/index.test.ts
  • tests/unit/app.test.tsx
  • packages/renderer/src/styles/find-in-files.css
  • packages/renderer/src/components/NewBrowserPaneModal.tsx
  • packages/renderer/src/lib/editorTheme.ts
  • packages/main/src/index.ts
  • packages/renderer/src/components/panes/MarkdownPreviewPane.tsx
  • packages/shared/src/zoom.ts
  • packages/renderer/src/lib/browserState.ts
  • packages/renderer/src/components/panes/FindInFilesPane.tsx
  • packages/renderer/src/components/panes/WelcomePane.tsx
  • packages/renderer/src/components/panes/BrowserPane.tsx
  • packages/main/src/browserPaneManager.ts
  • packages/renderer/src/components/AppShell.tsx
  • packages/main/src/preload.ts
  • packages/shared/src/index.ts
**/*editor*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use CodeMirror 6 for editor implementation (not Monaco)

Files:

  • tests/unit/editorTheme.test.ts
  • packages/renderer/src/lib/editorTheme.ts
🧠 Learnings (6)
📚 Learning: 2026-03-25T11:00:59.388Z
Learnt from: Cheezeiii365
Repo: Cheezeiii365/aIDE PR: 4
File: packages/renderer/src/components/FileTree/ContextMenu.tsx:16-20
Timestamp: 2026-03-25T11:00:59.388Z
Learning: Since aIDE does not support Windows, path-handling utilities and related code should assume POSIX-style separators (`/`) (e.g., helpers like `dirname`/`basename` that split on `/`). During review, do not require Windows-style backslash (`\\`) support or `path.sep`/cross-platform normalization unless the project explicitly adds Windows support.

Applied to files:

  • packages/renderer/src/lib/workspaceRuntimeSnapshots.ts
  • packages/renderer/src/components/FileTree/FileTypeIcon.tsx
  • packages/renderer/src/components/DockviewContainer.tsx
  • packages/renderer/src/lib/defaultCommands.ts
  • packages/renderer/src/components/panes/PlaceholderPane.tsx
  • packages/renderer/src/components/Sidebar.tsx
  • packages/renderer/src/components/panes/TerminalPane.tsx
  • packages/renderer/src/components/panes/EditorPane.tsx
  • packages/renderer/src/lib/workspaceStateSerializer.ts
  • packages/renderer/src/lib/terminalState.ts
  • packages/renderer/src/lib/panelZoom.ts
  • packages/shared/src/index.test.ts
  • packages/renderer/src/components/NewBrowserPaneModal.tsx
  • packages/renderer/src/lib/editorTheme.ts
  • packages/main/src/index.ts
  • packages/renderer/src/components/panes/MarkdownPreviewPane.tsx
  • packages/shared/src/zoom.ts
  • packages/renderer/src/lib/browserState.ts
  • packages/renderer/src/components/panes/FindInFilesPane.tsx
  • packages/renderer/src/components/panes/WelcomePane.tsx
  • packages/renderer/src/components/panes/BrowserPane.tsx
  • packages/main/src/browserPaneManager.ts
  • packages/renderer/src/components/AppShell.tsx
  • packages/main/src/preload.ts
  • packages/shared/src/index.ts
📚 Learning: 2026-03-25T11:11:52.630Z
Learnt from: CR
Repo: Cheezeiii365/aIDE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T11:11:52.630Z
Learning: Applies to **/*layout*.{ts,tsx} : Use Dockview 5.x for layout and tiling panes

Applied to files:

  • packages/renderer/src/components/DockviewContainer.tsx
  • packages/renderer/src/styles/dockview-theme.css
  • packages/renderer/src/components/panes/PlaceholderPane.tsx
  • packages/renderer/src/components/panes/TerminalPane.tsx
  • packages/renderer/src/components/panes/EditorPane.tsx
  • packages/renderer/src/lib/workspaceStateSerializer.ts
  • IDE_BUILD_PLAN.md
  • packages/renderer/src/lib/browserState.ts
  • packages/renderer/src/components/panes/WelcomePane.tsx
  • packages/renderer/src/components/panes/BrowserPane.tsx
  • packages/renderer/src/components/AppShell.tsx
📚 Learning: 2026-03-25T11:11:52.630Z
Learnt from: CR
Repo: Cheezeiii365/aIDE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T11:11:52.630Z
Learning: Always check against the .gitignore file when committing for security and performance reasons

Applied to files:

  • packages/renderer/src/lib/defaultCommands.ts
📚 Learning: 2026-03-25T11:11:52.630Z
Learnt from: CR
Repo: Cheezeiii365/aIDE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T11:11:52.630Z
Learning: Applies to **/*.{css,scss,tsx,ts} : Use CSS variable token system with `data-theme` attribute for theming, supporting dark and light mode

Applied to files:

  • packages/renderer/src/styles/welcome-pane.css
  • packages/renderer/src/styles/markdown-preview.css
  • packages/renderer/src/styles/find-in-files.css
  • packages/renderer/src/lib/editorTheme.ts
📚 Learning: 2026-03-25T11:11:52.630Z
Learnt from: CR
Repo: Cheezeiii365/aIDE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T11:11:52.630Z
Learning: Applies to **/*editor*.{ts,tsx} : Use CodeMirror 6 for editor implementation (not Monaco)

Applied to files:

  • packages/renderer/src/components/panes/EditorPane.tsx
  • packages/renderer/src/lib/editorTheme.ts
📚 Learning: 2026-03-25T11:11:52.630Z
Learnt from: CR
Repo: Cheezeiii365/aIDE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-25T11:11:52.630Z
Learning: Always update IDE_BUILD_PLAN.md when making changes to the codebase

Applied to files:

  • IDE_BUILD_PLAN.md
  • LSPPLAN.md
🪛 LanguageTool
LSPPLAN.md

[style] ~47-~47: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...esses should not run in the renderer. - LSP server runtime should be workspace-scop...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[grammar] ~195-~195: Ensure spelling is correct
Context: ...tion Use a plain folder format with an aIDE-specific manifest. Example shape: ```...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[style] ~568-~568: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: .... Add LSP-backed hover integration. 16. Add definition/symbol request plumbing. 17....

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~569-~569: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...definition/symbol request plumbing. 17. Add a shared diagnostics store and editor d...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[grammar] ~698-~698: Ensure spelling is correct
Context: ...tory be on each platform? 2. How should aIDE represent pack health states in the UI ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.22.0)
browserPanes.md

[warning] 124-124: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Stylelint (17.5.0)
packages/renderer/src/styles/welcome-pane.css

[error] 3-3: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

packages/renderer/src/styles/app-shell.css

[error] 319-319: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

packages/renderer/src/styles/markdown-preview.css

[error] 3-3: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

packages/renderer/src/styles/find-in-files.css

[error] 5-5: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🔇 Additional comments (50)
packages/renderer/src/lib/defaultCommands.ts (1)

123-131: LGTM! Correctly removes dead code.

The previous 'error' in result check was unreachable since GitignoreAuditResult (defined in packages/shared/src/index.ts) only contains missing and total properties—never an error field. The backend auditGitignore throws on I/O failures rather than returning error objects, so this simplification correctly aligns the runtime behavior with the type system.

LSPPLAN.md (2)

1-717: Well-structured architecture plan with clear phased execution.

This is a strong planning document: clear boundaries (renderer/main/shared), good rollout sequencing, and explicit risk tracking.


1-1: No action needed—no files were changed in this PR.

The git diff shows zero changed files, so the requirement to update IDE_BUILD_PLAN.md does not apply. Please verify that the correct branch/diff was provided for review.

			> Likely an incorrect or invalid review comment.
packages/renderer/src/components/panes/TerminalPane.tsx (1)

161-172: Zoom application flow in terminal pane looks solid

The update order here (font size update, fit, then PTY resize) is correct and keeps terminal geometry in sync after zoom changes.

packages/renderer/src/components/FileTree/FileTypeIcon.tsx (1)

6-59: Type update for iconPath is appropriate

Using ReactNode here is a good fit for the mixed SVG return shapes in the switch cases.

packages/renderer/src/components/DockviewContainer.tsx (1)

10-22: Browser pane registration is correctly wired

browserPane is registered consistently with the panel component key used in panel creation.

packages/renderer/src/lib/workspaceRuntimeSnapshots.ts (1)

40-40: Passing workspaceId into serialization is the right move

This makes snapshot capture workspace-aware at serialization time and matches the new serializer signature.

tests/unit/editorTheme.test.ts (1)

4-16: Good targeted coverage for editor metric scaling

This test directly validates the font-size to line-height mapping used by zoom-driven editor metrics.

packages/renderer/src/styles/dockview-theme.css (1)

21-27: LGTM!

The tighter tab bar sizing (28px height, 10px font, 8px padding) is a reasonable density adjustment. The file consistently uses CSS variable tokens for theming.

packages/renderer/src/styles/find-in-files.css (1)

4-10: LGTM!

The --panel-zoom CSS custom property with default 1 and calc() font scaling is correctly implemented. This pattern is consistent with other zoom-aware panes in the PR.

packages/renderer/src/components/panes/FindInFilesPane.tsx (2)

6-9: LGTM!

The zoomFactor optional param follows the established pattern used across other panes.


178-179: LGTM!

The inline style correctly sets --panel-zoom with a fallback to 1, matching the pattern in PlaceholderPane and MarkdownPreviewPane.

packages/renderer/src/components/panes/MarkdownPreviewPane.tsx (2)

38-41: LGTM!

The zoomFactor optional param is correctly added to the interface.


117-122: LGTM!

The --panel-zoom CSS variable is correctly applied via inline style with appropriate fallback.

packages/renderer/src/styles/app-shell.css (2)

257-263: LGTM!

Adding min-width: 0 to flex containers is a standard fix to allow proper shrinking behavior, preventing overflow issues in the status bar.


316-326: LGTM!

The .placeholder-pane correctly implements the --panel-zoom pattern with default 1 and calc() font scaling, consistent with other zoom-aware panes.

packages/renderer/src/styles/file-tree.css (3)

48-53: LGTM!

The font size reduction from 12px to 11px aligns with the PR's UI density adjustments.


87-100: LGTM!

The filter input font size reduction to 10px is consistent with the overall UI density tightening.


137-141: LGTM!

The empty hint font size reduction is consistent with other sidebar typography changes.

packages/renderer/src/styles/markdown-preview.css (2)

1-11: LGTM!

The --panel-zoom custom property with default 1 and calc() scaling for the base font size is correctly implemented, following the established pattern.


50-60: LGTM!

Code block font size correctly scales with --panel-zoom, ensuring consistent zoom behavior across all text elements in the preview.

packages/renderer/src/components/Sidebar.tsx (1)

30-36: LGTM!

The multi-line destructuring improves readability with no functional changes.

packages/renderer/src/components/panes/PlaceholderPane.tsx (1)

3-5: LGTM!

The zoom factor integration follows the established pattern: typed panel params, inline CSS custom property with sensible default, consistent with other CSS-based panes.

packages/renderer/src/styles/welcome-pane.css (1)

2-10: LGTM!

The --panel-zoom CSS variable with calc() font scaling is well-implemented. The default value of 1 on the selector allows the inline style from WelcomePane.tsx to override it, and all typography elements scale consistently.

packages/renderer/src/components/panes/WelcomePane.tsx (1)

8-13: LGTM!

The zoom integration is consistent with other CSS-based panes. The params?.zoomFactor ?? 1 fallback correctly handles the initial params: {} state (per context snippet at AppShell.tsx:150-151) until a zoom command updates the panel parameters.

tests/unit/browserState.test.ts (1)

11-37: LGTM!

Good coverage of the happy path. The test validates that browser pane state is correctly extracted from Dockview panels.

Consider adding a test case verifying that panels with a different workspaceId are filtered out by serializeBrowserPaneState.

IDE_BUILD_PLAN.md (1)

1217-1217: LGTM!

The progress tracker entry comprehensively documents the per-panel zoom implementation, covering all key aspects: shared utilities, IPC channels, per-pane-type scaling mechanisms, and state persistence. This aligns with the retrieved learning to keep IDE_BUILD_PLAN.md updated with codebase changes.

packages/renderer/src/components/panes/EditorPane.tsx (3)

1-26: LGTM!

Imports and interface extension are well-structured. The EDITOR_BASE_FONT_SIZE constant appropriately centralizes the base value for zoom calculations. Based on learnings, CodeMirror 6 usage is correct per the coding guidelines.


88-91: LGTM!

The metrics compartment is correctly added to the initial extensions list for new editor states, properly incorporating the zoom factor into font size calculations.


263-272: LGTM!

The effect correctly reconfigures the metrics compartment when params change and calls requestMeasure() to ensure CodeMirror updates its internal measurements after the font size adjustment.

packages/renderer/src/styles/browser-pane.css (1)

1-87: LGTM!

The CSS correctly uses CSS variable tokens throughout for theming support. The structure is clean and the focus state styling (lines 48-50) maintains accessibility after removing the default outline. As per coding guidelines, this follows the CSS variable token system with data-theme attribute support.

tests/unit/app.test.tsx (1)

18-21: LGTM!

The mock implementations correctly match the WindowApi interface signatures. The zoom methods return appropriate default values (1 for zoom factor), and browser pane lifecycle mocks properly return the expected types. The async renderApp with act wrapper correctly handles React effects that fire on mount.

Also applies to: 112-126

packages/shared/src/zoom.ts (1)

1-38: LGTM!

Clean, pure utility functions with good defensive coding. The Number.isFinite check in clampZoomFactor properly handles edge cases like NaN and Infinity. The rounding strategy prevents floating-point precision issues.

packages/main/src/index.ts (3)

30-33: LGTM!

The BrowserPaneManager instantiation correctly uses getter functions to lazily resolve mainWindow and contentView?.webContents, handling the case where these don't exist at module initialization time.


79-93: LGTM!

The custom zoom menu items correctly replace the built-in role-based zoom (which would affect the entire window) with per-panel zoom commands routed through IPC. Platform-specific accelerators are properly configured.


444-483: LGTM!

The browser pane IPC handlers correctly distinguish between ipcMain.handle (for operations requiring a return value like BROWSER_CREATE and BROWSER_NAVIGATE) and ipcMain.on (for fire-and-forget operations like BROWSER_DESTROY and navigation controls). All handlers properly delegate to browserPaneManager.

packages/renderer/src/lib/editorTheme.ts (1)

22-52: LGTM!

The getEditorMetrics function provides a clean abstraction for calculating font metrics, and getEditorMetricsExtension comprehensively applies these to all relevant CodeMirror selectors (content, lines, gutters, gutter elements). The 1.6× line-height ratio is a standard choice for code readability. Based on learnings, the CodeMirror 6 Compartment pattern is correctly applied per the coding guidelines.

packages/renderer/src/lib/browserState.ts (1)

1-59: LGTM!

The browser state utilities are well-structured:

  • randomId() has a sensible fallback for environments without crypto.randomUUID
  • createBrowserPanelParams initializes defaults correctly
  • isBrowserPanel provides a proper type guard checking essential fields
  • serializeBrowserPaneState correctly filters by workspace and extracts the relevant state
packages/main/src/browserPaneManager.ts (2)

52-174: Well-structured pane lifecycle and event handling.

The create() method properly:

  • Handles existing pane re-creation with workspace/session validation
  • Configures secure webPreferences (sandbox, contextIsolation, no nodeIntegration)
  • Sets up comprehensive event handlers for navigation, title, loading, and focus state
  • Handles external link opening appropriately

260-285: No DPR scaling issue exists. getBoundingClientRect() returns device-independent pixels (CSS pixels), which is the correct coordinate system that WebContentsView.setBounds() expects. Both APIs use the same DIP standard, so the bounds values flow through without needing any scaling adjustment.

			> Likely an incorrect or invalid review comment.
packages/renderer/src/components/panes/BrowserPane.tsx (1)

256-332: Browser chrome UI implementation looks solid.

The component correctly:

  • Provides accessible navigation buttons with aria-label attributes
  • Uses controlled form submission for URL navigation
  • Integrates zoom controls using shared utilities (adjustZoomFactor, resetZoomFactor, zoomFactorToPercent)
  • Shows loading state and page title appropriately
packages/main/src/preload.ts (2)

27-35: LGTM!

Zoom control IPC methods follow established patterns with proper event subscription cleanup.


228-273: LGTM!

Browser pane IPC surface is well-structured:

  • Lifecycle methods (browserCreate, browserDestroy, browserDestroyWorkspace) properly use invoke for async operations and send for fire-and-forget
  • Navigation and state methods follow existing PTY/terminal patterns
  • Event subscriptions return cleanup functions consistently
packages/renderer/src/components/AppShell.tsx (4)

337-344: Good overlay suppression coordination.

The effect correctly suppresses browser overlays when any modal is open (command palette, quick open, gitignore modal, task input, new browser modal), preventing browser panes from obscuring modal content.


362-391: Well-implemented per-panel zoom handling.

The zoom implementation correctly:

  • Distinguishes browser panes from other panel types
  • Uses shared zoom utilities (adjustZoomFactor, resetZoomFactor, getPanelZoomFactor)
  • Persists zoom state to both the panel parameters and workspace runtime
  • Applies zoom via appropriate API (setBrowserZoom for browser, updateParameters for others)

708-723: LGTM!

Browser pane creation follows established Dockview patterns with proper parameter generation and positioning relative to the active panel.


453-460: The race condition concern is not substantiated by evidence in the codebase. The current guard adequately protects against synchronous events (the expected behavior), as the isSwitchingWorkspaceRef remains true throughout the synchronous fromJSON()onAfterRestorePanels() sequence. No TODOs, warnings, or issues in the codebase indicate a known async event problem with Dockview's fromJSON().

packages/shared/src/index.ts (3)

7-16: LGTM!

Clean re-export of zoom utilities from the dedicated module.


389-394: LGTM!

ZoomCommandPayload is well-typed with a clear discriminator (target: 'panel') for future extensibility.


616-631: LGTM!

Browser pane WindowApi methods are comprehensive and match the preload implementation exactly.

Comment thread browserPanes.md
Comment on lines +124 to +133
```
┌─────────────────────────────┐
│ ◀ ▶ ↻ [ URL bar ] │ ← nav chrome (React, measured height = chromeHeight)
├─────────────────────────────┤
│ │
│ placeholder div │ ← colored div, native overlay covers this area
│ (ResizeObserver) │
│ │
└─────────────────────────────┘
```
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.

⚠️ Potential issue | 🟡 Minor

Add a language tag to the fenced code block

The fence starting at Line 124 is missing a language identifier (MD040).

Suggested fix
-```
+```text
 ┌─────────────────────────────┐
 │ ◀ ▶ ↻  [ URL bar         ] │  ← nav chrome (React, measured height = chromeHeight)
 ├─────────────────────────────┤
 │                             │
 │    placeholder div          │  ← colored div, native overlay covers this area
 │    (ResizeObserver)         │
 │                             │
 └─────────────────────────────┘
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.22.0)</summary>

[warning] 124-124: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @browserPanes.md around lines 124 - 133, The fenced ASCII-art block is
missing a language tag; update the fence that begins with the ┌ box (the
ASCII-art placeholder div block) to include a language identifier (e.g., add
text instead of ) so the Markdown linter rule MD040 is satisfied and the
block is treated as plain text.


</details>

<!-- fingerprinting:phantom:poseidon:hawk -->

<!-- This is an auto-generated comment by CodeRabbit -->

Comment on lines +32 to +41
function normalizeUrl(input: string): string {
const trimmed = input.trim()
if (!trimmed) return ''

try {
return new URL(trimmed).toString()
} catch {
return new URL(`https://${trimmed}`).toString()
}
}
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.

⚠️ Potential issue | 🟡 Minor

URL normalization can throw on malformed input.

If trimmed is truly malformed (e.g., "://"), the fallback new URL(\https://${trimmed}`)` will also throw, causing an unhandled exception.

🛡️ Proposed fix to handle malformed URLs
 function normalizeUrl(input: string): string {
   const trimmed = input.trim()
   if (!trimmed) return ''

   try {
     return new URL(trimmed).toString()
   } catch {
-    return new URL(`https://${trimmed}`).toString()
+    try {
+      return new URL(`https://${trimmed}`).toString()
+    } catch {
+      return ''
+    }
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/main/src/browserPaneManager.ts` around lines 32 - 41, The
normalizeUrl function can still throw when the fallback new
URL(`https://${trimmed}`) is given a completely malformed input like "://";
update normalizeUrl to catch errors from the fallback attempt as well and return
a safe default (e.g., empty string) instead of letting an exception propagate.
Specifically, keep the initial trim and empty check, attempt new URL(trimmed) in
a try, and if that fails attempt new URL(`https://${trimmed}`) inside a second
try/catch (or a single try with nested catches) and return '' on any remaining
error; refer to the normalizeUrl function and the trimmed variable to locate the
change.

Comment on lines +151 to +159
if (cached) {
view.dispatch({
effects: StateEffect.appendConfig.of([
editorMetricsCompartment.of(
getEditorMetricsExtension(Math.round(EDITOR_BASE_FONT_SIZE * getPanelZoomFactor(params))),
),
]),
})
}
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how compartments are handled elsewhere in the codebase for cached states
rg -n "appendConfig|reconfigure" --type=ts -C3 packages/renderer/src

Repository: Cheezeiii365/aIDE

Length of output: 2623


🏁 Script executed:

#!/bin/bash
# Find where compartments are created and initialized
rg -n "editorMetricsCompartment|wrapCompartment|themeCompartment" --type=ts packages/renderer/src/components/panes/EditorPane.tsx | head -20

Repository: Cheezeiii365/aIDE

Length of output: 702


🏁 Script executed:

#!/bin/bash
# Search for where initial extensions are constructed (likely where compartments are added)
rg -n "extensions.*=.*\[|getEditorExtensions" --type=ts packages/renderer/src -B2 -A5

Repository: Cheezeiii365/aIDE

Length of output: 817


🏁 Script executed:

#!/bin/bash
# Check StateEffect.appendConfig documentation and usage patterns
rg -n "StateEffect.appendConfig" --type=ts packages/renderer/src -B3 -A3

Repository: Cheezeiii365/aIDE

Length of output: 721


Use conditional logic to handle both pre- and post-feature cached states when restoring editorMetricsCompartment.

Using StateEffect.appendConfig unconditionally on cached states creates duplicate editorMetricsCompartment instances when restoring states cached after this PR (which already contain the compartment). Conversely, directly using reconfigure would fail on states cached before this feature existed.

Check if the compartment exists in the cached state and use reconfigure if present, or appendConfig if absent. Alternatively, consider whether stripping and rebuilding the extension is viable for your backward compatibility requirements.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/renderer/src/components/panes/EditorPane.tsx` around lines 151 -
159, When restoring a cached editor state in the view.dispatch block, detect
whether the cached state already contains the editorMetricsCompartment and use
StateEffect.reconfigure (or the compartment.reconfigure effect) in that case,
otherwise use StateEffect.appendConfig.of(...) as current code does;
specifically, inspect the cached state's extensions for editorMetricsCompartment
(the compartment symbol) and then call either a reconfigure effect with
getEditorMetricsExtension(Math.round(EDITOR_BASE_FONT_SIZE *
getPanelZoomFactor(params))) or
appendConfig.of([...editorMetricsCompartment.of(getEditorMetricsExtension(...))])
to avoid duplicating compartments or failing for older cached states.

Comment thread packages/renderer/src/lib/panelZoom.ts
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 27, 2026

Note

Docstrings generation - SUCCESS
Generated docstrings and committed to branch dev (commit: 2b78b25934019e9ce9f9e53336aa5d823dd33c6a)

coderabbitai Bot and others added 3 commits March 27, 2026 10:44
Docstrings generation was requested by @Cheezeiii365.

The following files were modified:

* `packages/main/src/browserPaneManager.ts`
* `packages/renderer/src/components/FileTree/FileTypeIcon.tsx`
* `packages/renderer/src/components/NewBrowserPaneModal.tsx`
* `packages/renderer/src/components/Sidebar.tsx`
* `packages/renderer/src/components/panes/BrowserPane.tsx`
* `packages/renderer/src/components/panes/EditorPane.tsx`
* `packages/renderer/src/components/panes/MarkdownPreviewPane.tsx`
* `packages/renderer/src/components/panes/PlaceholderPane.tsx`
* `packages/renderer/src/components/panes/WelcomePane.tsx`
* `packages/renderer/src/lib/browserState.ts`
* `packages/renderer/src/lib/editorTheme.ts`
* `packages/renderer/src/lib/panelZoom.ts`
* `packages/renderer/src/lib/terminalState.ts`
* `packages/renderer/src/lib/workspaceRuntimeSnapshots.ts`
* `packages/renderer/src/lib/workspaceStateSerializer.ts`
* `packages/shared/src/zoom.ts`

These files were kept as they were:
* `packages/main/src/index.ts`
* `packages/renderer/src/components/AppShell.tsx`
* `packages/renderer/src/components/panes/FindInFilesPane.tsx`
* `packages/renderer/src/components/panes/TerminalPane.tsx`
* `packages/renderer/src/lib/defaultCommands.ts`

These files were ignored:
* `packages/shared/src/index.test.ts`
* `tests/unit/app.test.tsx`
* `tests/unit/browserState.test.ts`
* `tests/unit/editorTheme.test.ts`
* `tests/unit/panelZoom.test.ts`
* `tests/unit/workspaceStateSerializer.test.ts`

These file types are not supported:
* `IDE_BUILD_PLAN.md`
* `LSPPLAN.md`
* `browserPanes.md`
* `packages/renderer/src/styles/app-shell.css`
* `packages/renderer/src/styles/browser-pane.css`
* `packages/renderer/src/styles/dockview-theme.css`
* `packages/renderer/src/styles/file-tree.css`
* `packages/renderer/src/styles/find-in-files.css`
* `packages/renderer/src/styles/markdown-preview.css`
* `packages/renderer/src/styles/welcome-pane.css`
Clamp values through clampZoomFactor in getPanelZoomFactor and
updatePanelZoomParams to prevent invalid zoom from persisted or
externally mutated params.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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