Skip to content

feat(electron): resizable Settings popover with persist + restore default size - #95

Merged
ryota-murakami merged 6 commits into
mainfrom
feat/settings-popover-resize
Jun 16, 2026
Merged

feat(electron): resizable Settings popover with persist + restore default size#95
ryota-murakami merged 6 commits into
mainfrom
feat/settings-popover-resize

Conversation

@ryota-murakami

@ryota-murakami ryota-murakami commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds drag-to-resize for the Settings tray popover and a "Restore default size" button. The window remembers its size across sessions by persisting it to configManager, with clamping on load to guard against corrupted values.

feat: resizable Settings popover + restore default size

  • WindowManager.createSettingsWindow(): resizable: true, min/max constraints, persisted size loaded+clamped at creation
  • will-resize + resize (200 ms debounce): persist width/height to configManager
  • Blur guard: settingsWindowIsResizing flag skips hide-on-blur during drag
  • Failsafe timer: if will-resize fires without a following resize (user clicks handle but doesn't drag), the latch resets after 500 ms — prevents the popover being permanently stuck open
  • resetSettingsPopoverSize(): reverts to 360×380 via setBounds() + config update
  • Resize grip visual (dot pattern, pointer-events-none) in bottom-right corner

refactor: fix review findings

  • Magic numbers extracted to electron/constants.ts
  • clampDimension() extracted to electron/utils/clampDimension.ts with JSDoc
  • useState(false)useTransition for reset button pending state
  • Preload version-skew guard: capture resetFn ref before entering transition
  • will-resize latch bug fixed: 500 ms failsafe timer
  • Test names renamed to observable behavior (DAMP)

fix: design-review — remove dead cursor: se-resize

  • pointer-events-none element ignores cursor CSS; removed the property

test: coverage for settings-popover-resize UI and utilities

  • ElectronSettingsPage.test.tsx: 4 tests (Settings Window card render, button click→IPC, preload skew guard, null return off-Electron)
  • clampDimension.test.ts: 9 unit tests covering all invalid categories (NaN, zero, negative, Infinity, undefined) + undersized/oversized/fractional
  • ConfigManager.settings-popover.test.ts: +1 partial-key migration test (width-only config → height backfilled)

Test Coverage

FILE                                         COVERED           GAPS
────────────────────────────────────────────────────────────────────────
electron/constants.ts                        100%              none
electron/utils/clampDimension.ts             100%              all 9 categories covered
electron/ConfigManager.ts                    100%              incl. partial-key migration
electron/WindowManager.ts                    ~95%              resize-destroyed-window debounce guard
electron/types/ipc.ts                        100%
electron/ipc/ipc-schemas.ts                  100%
electron/main.ts (IPC handler)               ~10%              null-windowManager / throw paths
electron/preload.ts (resetPopoverSize)       ~10%              catch block
ElectronSettingsPage.tsx (new UI)            100%              card, button, click→IPC, skew guard
────────────────────────────────────────────────────────────────────────
OVERALL ESTIMATE: ~90%  |  14 new tests committed

Tests: 622 → 626 unit (+4), 298 → 308 Electron (+10)

Pre-Landing Review

/review ran on the full diff before the design-fix commit. All findings addressed:

  • Magic numbers → constants.ts
  • clampDimension extracted to utils/ with JSDoc ✅
  • useState(false)useTransition
  • will-resize latch bug (permanent stuck-open) fixed with 500 ms failsafe ✅
  • Test names → observable behavior ✅

No new findings on the subsequent test-only commits.

Design Review

/design-review ran: 1 finding auto-fixed (cursor: se-resize on pointer-events-none element — dead CSS). Committed e76eeb4.

Plan Completion

No plan file — feature was implemented autonomously per /goal.

TODOS

No TODO items completed in this PR.

Test plan

  • Unit tests: 626 pass (0 failures)
  • Electron tests: 308 pass (0 failures)
  • Web E2E: unrunnable locally (localhost IPv6/loopback mismatch — verify on CI)
  • Electron E2E: covered by CI
  • Native macOS QA owed: Settings popover resize drag + restore button (requires packaged build + computer-use)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Settings popover window is now resizable, with size changes automatically persisted between sessions.
    • "Restore default size" button added to reset the popover window to its original dimensions.
  • Documentation

    • Strengthened QA governance with explicit post-implementation verification rules, expanded release checklist, and detailed Electron testing guidance including macOS manual QA procedures.

- Add 'QA after implementing, before ship' to Industry Standards table
- Add 'Ship before QA passes' to Violations table
- Add Definition of Done section (QA non-negotiable policy)
- Rename 'smoke test/smoke' → 'QA' throughout (2 occurrences)
- Add Dev vs Production Electron comparison table (bundle ID, app name,
  renderer URL, app.isPackaged, NODE_ENV, computer-use grantability)
- WindowManager: resizable:true, min/max constraints, load/clamp persisted
  size from ConfigManager, 200ms debounce-persist on resize, will-resize flag
  to guard blur→hide mid-drag, resetSettingsPopoverSize() re-anchors to tray
- ConfigManager: SettingsPopoverConfig interface + settingsPopover defaults
- IPC: settings:resetPopoverSize channel (types/ipc.ts + ipc-schemas.ts)
- main.ts: typedHandle for resetPopoverSize
- preload.ts: settings.resetPopoverSize() exposed with version-skew guard
- electron-api.d.ts: resetPopoverSize() type declaration
- ElectronSettingsPage: Settings Window Card + resize grip + Restore button
- Tests: WindowManager.settings-resize (20 cases), ConfigManager.settings-popover
  (3 cases), ipc-contract extension (1 case)
- pnpm validate green (622 unit + 297 electron + build + lint + typecheck)
…sion to utils, useTransition, will-resize failsafe, observable test names
- ElectronSettingsPage: 4 tests for Settings Window card render, button
  click→IPC call, preload version-skew guard (method absent → no-op)
- clampDimension: 9 unit tests covering all invalid input categories
  (NaN, zero, negative, Infinity, undefined) + undersized, oversized,
  fractional rounding
- ConfigManager.settings-popover: partial-key migration scenario (width
  only in config.json → height backfilled to default 380)
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
corelive Ready Ready Preview, Comment Jun 16, 2026 3:45pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

📝 Walkthrough

Walkthrough

Adds user-resizable Settings popover with config-backed dimension persistence, a clampDimension utility, resize lifecycle event handling in WindowManager, a settings:resetPopoverSize IPC channel wired through schema, main-process handler, and preload bridge, and a renderer "Restore default size" button in ElectronSettingsPage. Also updates CLAUDE.md with QA governance rules and Electron native testing guidance.

Changes

Settings Popover Resize Persistence & Reset Feature

Layer / File(s) Summary
Constants, clampDimension utility, and IPC/API type contracts
electron/constants.ts, electron/utils/clampDimension.ts, electron/__tests__/clampDimension.test.ts, electron/ConfigManager.ts, electron/types/ipc.ts, electron/types/electron-api.d.ts
Exports sizing bounds and debounce constants, introduces clampDimension with edge-case tests, defines SettingsPopoverConfig interface and AppConfig.settingsPopover field, and declares settings:resetPopoverSize in IPCChannels and ElectronAPI.settings.
ConfigManager settingsPopover defaults and migration
electron/ConfigManager.ts, electron/__tests__/ConfigManager.settings-popover.test.ts
Imports default dimension constants into ConfigManager, populates settingsPopover in getDefaultConfig(), and tests fresh-install defaults, legacy config backfill via mergeWithDefaults, persisted-value preservation, and partial-key migration.
WindowManager resize lifecycle and resetSettingsPopoverSize
electron/WindowManager.ts, electron/__tests__/WindowManager.settings-resize.test.ts
Updates imports and private state fields, derives clamped initial dimensions from config in createSettingsWindow, enables resizing with min/max constraints, implements will-resize guard with 500ms failsafe, debounced resize config persistence, blur-during-resize protection, closed cleanup, and the public resetSettingsPopoverSize() method. Test suite covers all resize scenarios.
IPC schema, main-process handler, and preload bridge
electron/ipc/ipc-schemas.ts, electron/main.ts, electron/preload.ts, electron/__tests__/ipc-contract.test.ts
Registers the settings:resetPopoverSize Zod schema, adds the main-process handler returning true/false, exposes settings.resetPopoverSize() on the preload bridge via typedInvoke, and validates the schema in the IPC contract test.
ElectronSettingsPage Settings Window card and tests
src/components/electron/ElectronSettingsPage.tsx, src/components/electron/ElectronSettingsPage.test.tsx
Adds useTransition-backed handleResetPopoverSize with version-skew guard, renders a "Settings Window" card with a "Restore default size" button disabled during transition, and covers Electron/non-Electron rendering, button click IPC invocation, and missing-method guard in tests.

CLAUDE.md QA Governance and Electron Testing Documentation

Layer / File(s) Summary
QA governance rules and Electron native QA guidance
CLAUDE.md
Adds "QA after implementing" rule, expands Definition of Done with implement→QA→ship sequence and E2E/local QA steps, adds Electron Native QA section explaining Cocoa chrome invisibility to Playwright, and introduces a Dev vs Production identification table with bundle ID/proc/renderer URL/app.isPackaged/NODE_ENV signals.

Sequence Diagram

sequenceDiagram
  participant Renderer as ElectronSettingsPage
  participant preload as preload.ts
  participant main as main.ts (IPC handler)
  participant WindowManager
  participant ConfigManager

  rect rgba(70, 130, 180, 0.5)
    Note over Renderer,preload: User clicks "Restore default size"
    Renderer->>preload: settings.resetPopoverSize()
    preload->>main: typedInvoke("settings:resetPopoverSize")
    main->>WindowManager: resetSettingsPopoverSize()
    WindowManager->>ConfigManager: update(settingsPopover {width:360, height:380})
    WindowManager->>WindowManager: setBounds to default + re-anchor to tray
    WindowManager-->>main: void
    main-->>preload: true
    preload-->>Renderer: Promise(true)
  end

  rect rgba(60, 179, 113, 0.5)
    Note over Renderer,WindowManager: User drags resize edge
    WindowManager->>WindowManager: will-resize → isResizingSettingsPopover=true
    WindowManager->>WindowManager: resize → debounce 200ms
    WindowManager->>ConfigManager: update(settingsPopover {width, height})
    WindowManager->>WindowManager: isResizingSettingsPopover=false
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • laststance/corelive#23: Both PRs extend the typed IPC infrastructure — this PR adds the settings:resetPopoverSize channel end-to-end (schema in ipc-schemas.ts, handler in main.ts, typedInvoke in preload.ts) on top of the typed IPC framework introduced by #23.
  • laststance/corelive#20: Both PRs modify the Settings popover in electron/WindowManager.ts#20 adds tray-bounds positioning, while this PR adds config-backed size persistence, resizing, and the reset IPC that re-anchors the same popover.
  • laststance/corelive#94: Both PRs modify createSettingsWindow() in electron/WindowManager.ts#94 adds macOS Space-following behavior while this PR changes the same function to use clamped config-backed dimensions and resize persistence.

Poem

🐇 Hop, hop, the popover grows,
I dragged the edge — the config knows!
A reset button, neat and small,
Brings it back from edge to wall.
clampDimension guards the bounds,
No runaway window ever astounds.
This bunny ships only after QA rounds!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main feature: resizable Settings popover with persistence and restore functionality, matching the core changes across WindowManager, ConfigManager, and UI components.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/settings-popover-resize

Warning

Review ran into problems

🔥 Problems

Stopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a @coderabbit review after the pipeline has finished.


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

@codecov-commenter

codecov-commenter commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.50000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.83%. Comparing base (8ebfe05) to head (b411b5e).

Files with missing lines Patch % Lines
src/components/electron/ElectronSettingsPage.tsx 77.77% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #95      +/-   ##
==========================================
- Coverage   69.35%   66.83%   -2.53%     
==========================================
  Files         125      128       +3     
  Lines        4004     4215     +211     
  Branches     1039     1115      +76     
==========================================
+ Hits         2777     2817      +40     
- Misses       1058     1195     +137     
- Partials      169      203      +34     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/WindowManager.ts`:
- Around line 1451-1457: The `settingsResizeDebounceTimer` field is being used
for two independent purposes: managing the resize blur-guard timeout (500ms) and
managing persistence debounce (200ms). This causes the blur-guard logic to
interfere with persistence by clearing the persistence timer before it fires,
dropping unsaved window sizes. Split this into two separate timer fields: one
dedicated to managing the resize blur-guard state (settingsResizeDebounceTimer
for the 500ms timeout that controls settingsWindowIsResizing) and another
dedicated to persistence debounce (create a new field like
settingsPersistDebounceTimer for the 200ms persistence logic). Update all
references throughout the code to use the appropriate timer field for each
concern.

In `@src/components/electron/ElectronSettingsPage.tsx`:
- Around line 191-193: The async callback passed to startResetSizeTransition
calls resetFn() without checking its return value. Since resetFn() (the
resetPopoverSize() function) returns false on IPC failure, capture the return
value from the await resetFn() call and add error handling logic to notify the
user when the reset fails, preventing silent no-op failures where the button
appears to do nothing.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bb48a062-8f13-485c-8bbb-559bacd6cd8e

📥 Commits

Reviewing files that changed from the base of the PR and between 8ebfe05 and 3e99a8d.

📒 Files selected for processing (16)
  • CLAUDE.md
  • electron/ConfigManager.ts
  • electron/WindowManager.ts
  • electron/__tests__/ConfigManager.settings-popover.test.ts
  • electron/__tests__/WindowManager.settings-resize.test.ts
  • electron/__tests__/clampDimension.test.ts
  • electron/__tests__/ipc-contract.test.ts
  • electron/constants.ts
  • electron/ipc/ipc-schemas.ts
  • electron/main.ts
  • electron/preload.ts
  • electron/types/electron-api.d.ts
  • electron/types/ipc.ts
  • electron/utils/clampDimension.ts
  • src/components/electron/ElectronSettingsPage.test.tsx
  • src/components/electron/ElectronSettingsPage.tsx

Comment thread electron/WindowManager.ts
Comment thread src/components/electron/ElectronSettingsPage.tsx
- Split settingsResizeDebounceTimer (500ms blur-guard) and settingsPersistDebounceTimer (200ms persist debounce) into separate fields so will-resize guard can never cancel a pending size write
- Handle resetPopoverSize() false return in handleResetPopoverSize to surface IPC failures in dev instead of silently no-op
@ryota-murakami
ryota-murakami merged commit a6ad47f into main Jun 16, 2026
24 checks passed
@ryota-murakami
ryota-murakami deleted the feat/settings-popover-resize branch June 16, 2026 15:55
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.

2 participants