src/main/index.ts → main.ts and src/preload/index.ts → preload.ts, so the process entries stop occupying the barrel-reserved index filename (§6.4) without needing an exemption list. The rename is wired through electron.vite (main build.lib.entry — rollupOptions.input would bypass electron-vite's output-format detection — and the preload input key), package.json main (out/main/main.js), WindowManager's default preload filename, MainWindowService's webview preload path, and DbService's slow-query stack filter, which matches the bundled artifact name. preload.d.ts becomes globals.d.ts: sharing preload.ts's basename makes TypeScript drop the .d.ts as that file's presumed output declaration, and tsgo only applies its global Window augmentation when the declaration sorts before its importer, so the file keeps a distinct, alphabetically-early name. Entry-path mentions in code comments and reference docs follow.
Move useWindowInitData out of the single-file src/renderer/core/ tree into the canonical src/renderer/hooks/ bucket and remove the now-empty core/ directory. The renderer core/ tree was pre-created to mirror src/main/core/ but only ever held this one cross-window hook, violating the naming convention against pre-creating subdirectories for anticipated growth.
Update all three import sites plus the doc and comment references (windows/README, preload comment, window-manager README and usage guide) to the new path.
Move all renderer source from src/renderer/src/* up one level to
src/renderer/*, removing the redundant nested src directory.
- Update path aliases (@renderer, @types, @logger, @data) and TanStack
Router paths in electron.vite.config.ts; update tsconfig.{json,web,node}
path mappings and include globs.
- Fix Vite root-relative script paths in the 8 renderer HTML entries.
- Update cross-process relative imports in main/preload (language,
apiServer models, preload index) to drop the /src segment.
- Switch renderer test imports of the logger mock to the @test-mocks alias.
- Update hardcoded renderer paths in scripts and their fixtures, lint
configs (eslint/oxlint/biome), CODEOWNERS, docs, and the data-classify tool.
- Convert deep (../../+) relative imports within the renderer to the
@renderer alias (69 files, 108 imports); keep single-level relatives.
- Fix doc links broken by the move and correct one pre-existing broken
link in naming-conventions.md.
WindowTypeMetadata now declares per-type config across three orthogonal
layers, chosen by what goes wrong if misconfigured:
- windowOptions: BrowserWindow constructor parameters (Electron-native).
- behavior: cross-platform declarative WM behavior that the constructor
cannot express — hideOnBlur, alwaysOnTop level/relativeLevel,
visibleOnAllWorkspaces options, macShowInDock.
- quirks: OS-specific monkey-patches around hide/show/close.
`macReapplyAlwaysOnTop` is now a pure boolean — the level it re-applies
reads from `behavior.alwaysOnTop`, eliminating the previous mixing of
hack flag and semantic value.
Field renames in the registry:
- defaultConfig → windowOptions
- show → showMode (also gains 'immediate' | 'manual' string variants
in place of true/false; 'auto' unchanged)
- showInDock (top-level) → behavior.macShowInDock
- preload now accepts a plain filename ('index.js' / 'simplest.js'),
mirroring htmlPath; empty string disables, omit defaults to
'index.js'. Removes the variant→file mapping switch.
- mergeWindowConfig → mergeWindowOptions
Two runtime setters added on WindowManager:
- setHideOnBlur(id, enabled): runtime override on behavior.hideOnBlur.
Override map is cleared on destroy and on releaseToPool, so pool
consumers re-applying after open() see a clean default.
- setAlwaysOnTop(id, enabled): toggles using level/relativeLevel from
behavior — registry is the single source of truth.
setVisibleOnAllWorkspaces intentionally has no WM setter: its options
differ per call in real usage (SelectionAction's full-screen show
sequence) and WM has no state to maintain.
Types derived from Electron's signatures so they stay in sync with
@types/electron:
- AlwaysOnTopLevel = NonNullable<Parameters<BW['setAlwaysOnTop']>[1]>
- VisibleOnAllWorkspacesOptions imported directly
applyWindowQuirks is split into applyWindowBehavior (new, behavior.ts)
+ applyWindowQuirks. Behavior runs first so monkey-patches wrap any
subsequent show/hide rather than the initial setter calls. Behavior
takes a closure for reading the override map, avoiding a reverse
WindowManager dependency.
Consumer changes:
- SelectionToolbar: declares behavior.hideOnBlur; mouse-key hook
lifecycle moves from showToolbarAtPosition/hideToolbar call sites
to window 'show'/'hide' event listeners, so any hide path (WM-
driven blur included) triggers cleanup symmetrically.
- SelectionAction: pinActionWindow routes through wm.setAlwaysOnTop
via getWindowId. Show-sequence setVisibleOnAllWorkspaces calls
stay direct (true/false options differ per call). Hardcoded
'floating' level dropped from the show sequence; Electron's
default is identical.
- QuickAssistant: removes the inline setVisibleOnAllWorkspaces and
setAlwaysOnTop calls (now declared in registry behavior). The
blur handler stays in the service because hideQuickAssistant() is
a platform-specific business flow (Windows minimize+setOpacity,
macOS<26 app.hide()) that a generic window.hide() cannot express.
QuickAssistant pin/blur on macOS NSPanel — separate concern, addressed
in the same change because the diagnosis surfaced during refactor:
Electron's blur tracker can get stuck after (outside-click → intra-
panel pin/unpin click → outside-click). Cocoa fires
windowDidResignKey: for the second outside-click but Electron filters
it — its tracker thinks the window was already blurred (the intra-
panel click silently re-keyed the panel without firing
windowDidBecomeKey:). Upstream marked wontfix (electron/electron#3222).
Workaround: after un-pinning, poll window.isFocused() at 200ms,
capped at 30s. isFocused() reads Cocoa state directly, bypassing
the broken Electron tracker. Triggered only when hasBlurredSinceShow
is true (the failure precondition), so the common open→pin→unpin
path runs no timer.
Renderer side: HomeWindow's pin sync moves from useEffect (deferred
by one render cycle) to a setter wrapper, so IPC fires synchronously
inside the click handler — closes a small race where a fast outside
click could see a stale main flag.
Tests: 15 new specs covering behavior layer (declarative + runtime
override, pool recycle reset, level derivation, initial
setVisibleOnAllWorkspaces application, macReapplyAlwaysOnTop reading
from behavior). Existing fixtures mechanically renamed.
Docs: 6 window-manager docs synchronized; README adds sections on
configuration layers, "WM does not know pin", behavior/quirks API,
when to provide a runtime setter, type derivation convention, and
Electron edge cases.
Add thin wrapper methods on WindowManager that subscribe to a specific
WindowType, eliminating the boilerplate `if (managed.type !== X) return`
guard at every consumer call site. The underlying `onWindowCreated` /
`onWindowDestroyed` events remain available for the rare "observe all
windows" use case.
The new variants are additive and non-breaking — same Disposable return
contract, same ManagedWindow callback payload, just with an up-front type
filter baked in.
Documentation updates landing in the same commit:
- window-manager-usage.md: recommend the byType variants as the canonical
single-type subscription pattern; add a "Callback styles" section covering
destructuring vs `mw` shorthand for accessing ManagedWindow fields.
- window-manager-api-reference.md: list the byType variants in the Events
table; update `create`'s rationale to point at byType.
- window-manager-migration-guide.md: Step 3 example uses byType +
destructuring in the After block.
- docs README and window-manager-overview.md: anti-pattern and feature
rows point to the byType variants as the consumer-facing primary form.
Move the 756-line src/main/core/window/README.md into a dedicated
docs/references/window-manager/ directory organized by concern
(overview, usage, pool mechanics, platform, API reference, migration),
following the docs/references/lifecycle/ pattern. The in-source README
shrinks to a 14-line pointer.
Along the way:
- Reframe onWindowCreated + open()/close() as the canonical consumer
pattern, and demote create()/destroy() to internal primitives with an
explicit anti-pattern section for direct-ID attachment at call sites.
- Add a previously-undocumented "Renderer IPC Surface" section covering
WindowManager_Open/Close/Show/Hide/etc., with target-resolution rules
(bare sender vs singleton type).
- Make the pool-recycle-does-not-re-fire-onWindowCreated consequence
explicit in the Event Timing Contract guarantees.
- Update @see links in WindowManager.ts and types.ts to point at the new
doc locations directly instead of bouncing through the in-source README.