CrashReportModal crashed on dumps.map because ipc.crash.read returns
{ ok, data } and the consumer was assigning the wrapper to state. Now
unwraps .data and falls back to [] on error.
ipc.file.search was miswired to ipc.file.pickFile — clicking 'Search'
popped a file picker instead of searching. Add real 'search-in-files'
handler in src/main/files/search-in-files.js with regex support,
case-sensitivity toggle, .git/node_modules/dist skip, 2MB file cap, and
1000-result limit. Wire preload bridge + TS declaration + allowlist.
Also:
- Drop dead ipc.file.open wrapper that was miswired to pickFile
- Rename FileEntry field 'modified' to 'modifiedAt' (number) so the
IPC payload matches the declared type
- Update CrashReportModal tests to use the safeCall envelope shape
The list-directory handler returned { path, entries } while the
renderer typed result.data as FileEntry[]; the file-store had to fall
back to raw.entries via Array.isArray. Extract the entries builder into
src/main/files/list-directory.js so it can be unit-tested, return a
plain array, and skip entries that can't be stat'd (broken symlinks,
permission errors) instead of throwing.
Also tighten jest config so dist/*.snap electron-builder artifacts are
not matched as test suites.
GitOperations.getStatus returns { files: [...] } but the renderer's
ipc.file.gitStatus type declares Array<...> and calls result.data.map().
The mismatch made GitStatusPanel.tsx throw 'n.map is not a function'
on mount, which blanked the entire React tree.
Unwrap result.files in the IPC handler so it returns the array the
renderer expects. Add tests covering the success, empty, and non-git
branches so this regression cannot recur.
Refs: blank-screen-on-md-open
Two related bugs surfaced during end-to-end verification:
1. The 'isAlreadyV5' short-circuit in both the main-side and renderer-side
v4-to-v5 transforms returned the persisted object as-is when a
migration.version=5 marker was present. A previously-shipped v4 build
had written theme: 'ayu-light' (and similar) under the v5 marker;
the transform trusted the marker and passed the invalid theme through,
which then failed the renderer's zod schema on every launch and
reset user settings to defaults.
Fix: in both transforms, when isAlreadyV5 matches, normalize theme
against the v5 enum (light/dark/system) and validate the full result
with the v5 schema before returning. Out-of-range values are replaced
with the v5 default ('system').
2. The renderer's settings-store onRehydrateStorage callback called
useSettingsStore.setState() to reset the store on validation failure.
At that moment the store is still being constructed, and setState
could hit a TDZ ReferenceError (the one we already wrapped in a
try/catch in v5.0.0, which only hid the symptom).
Fix: return the normalized state object from onRehydrateStorage
instead. Zustand's persist middleware applies the returned value
*after* construction completes, so there is no TDZ.
Tests: 334 vitest + 208 jest = 542 passing.
E2E: 12/12 verify-features.mjs steps green; no console errors.
Amit Haridas
1. ThemeSettings: change 'auto' radio value to 'system' to match
the v5 settingsSchema enum. Storing 'auto' silently wipes all
settings on next launch.
2. UpdateBanner: render an 'available' branch so users see a CTA
when a new version is detected (the store already had this state,
but the banner was silent). Added a test.
3. feed-config: remove the unused resolveFeedUrl / FEEDS exports.
feedConfigFor is the actual implementation used by the IPC
handler. Updated tests to match.
4. main/index.js: tighten app:open-external regex to https:// only.