KDE's Backend took and returned bare u32 ids and answered
window_workspace and current_workspaces without a Result. The operations
that the shared trait has now use komorebi_backend's WindowId and
MonitorId, window_workspace and current_workspaces return its Result, and
the look/drag/wallpaper methods that only KDE has take the same id types.
So the Shared wrapper no longer converts ids or results: it is down to
the window info mapping.
The window manager core keeps its u32 ids (Window { id: u32 }); the
conversion is at the backend call (Window::window_id, From<WindowId> for
Window, WindowId::from_u32 / to_u32 at the remaining call sites). KWin's
table stays keyed by u32; an id too wide for a u32 is a window it never
had (WindowGone, or None for a query). Test call sites got .into() on the
ids and unwrap on the two results. No behaviour change.
Verified: KDE cargo test --workspace --no-fail-fast (unit, integration,
doc) 881 passed, 0 failed, native with FakeBackend. Not run: KDE VM
suites.
KDE's Backend trait returned eyre reports and the Shared wrapper turned
them into komorebi_backend::Error (guessing WindowGone from is_window
afterwards). The 16 operations that the shared trait has now return
komorebi_backend::Result themselves: KWinBackend says WindowGone for an
id it has no uuid for and Platform for everything else, and the fake
passes the shared fake's errors through. Window's commands (set_position,
focus, raise, close, minimize, ...) return the shared Result too. KDE-only
methods (look, border colours, wallpaper) keep eyre.
Only the text of a failure changes ("no such window: 9" instead of
"there is no such window" in logs); no control flow does.
Verified: KDE cargo test --workspace --no-fail-fast, 881 passed, 0 failed
(native, FakeBackend). Not run: KDE VM suites.
Regenerated komorebi-for-kde/nix/komorebi-options.nix from schema.json with schemars-nixgen (50ce286: sorted output, hash first line) and nix fmt. The checked-in file was stale after steps 2 and 3 changed description lines in the schema. Verified: nix build of kfde-schema-fresh and kfde-nix-options-validation from the root flake both succeed.
Regenerated komorebi-options.nix and komorebi-bar-options.nix from schema.json and schema.bar.json with schemars-nixgen (50ce286: sorted output, hash first line), then nix fmt from the root flake's formatter because the macOS flake has none on Linux. The generator was not run from the macOS tree's own just recipe. Verified: not built or evaluated on macOS; the macOS flake has no schema-fresh check, so the hash line is informational.
schemars-nixgen now writes the `# schema-sha256:` first line itself (schemars-nixgen 50ce286), so the justfile's printf and mv step would add a second one. Removes them and updates the comment. Verified: 'just nixgen' itself was not run (the recipe also builds a check); the same steps by hand gave a first line equal to `nix hash file --type sha256 --base16 schema.json`.
The doc of Backend::focus said Windows and macOS do focus and then center_cursor_in; writing the adapters showed that Window::focus on both takes mouse_follows_focus and centres the pointer itself (Windows also defers the focus while the window slides in). Doc comment only.
Verified: see the gates below; native cargo test -p komorebi-backend passes (doctests unchanged).
Appends section 9 to docs/dev/seam.md: for each method of the shared Backend, what WindowsBackend and MacosBackend do and where that differs from what the daemon calls today (windows() and macOS's registry cannot be written faithfully; set_rect on Windows drops the top flag and keeps animation and guards; hide/restore results are ignored on Windows; macOS hide needs a corner and a display; raise on macOS also focuses; macOS monitors() has to be a main-thread-refreshed cache; set_opacity on Windows is the plain write). Concludes that the trait did not have to change and records three proposals (rect as Option, hide's hidden inputs, windows() as start-up operation).
Verified: docs only, not built. Every claim is by reading the code while writing the adapters; what could not be settled by reading is marked 'not verified' in the text.
komorebi::macos_backend::MacosBackend implements komorebi_backend::Backend over the existing MacosApi, Window and AdhocWindow functions. It is NOT used by the daemon (no call site changed; that is step 11). No capability is supported (workspaces, maximize, opacity and focus-follows-mouse answer Unsupported).
Because nothing on macOS acts from a bare id, the adapter owns the id registry of seam.md consequence (1): the daemon would register(Window) and forget(id), every method looks the Window up by id and an unknown id is WindowGone. monitors() reads a cache that refresh_monitors(MainThreadMarker) fills on the main thread (the TARGET rule: no Backend method waits for main; the live NSScreen query panics off main). begin_batch/end_batch hold a stack of SkyLight BatchedUpdates guards. The parts that cannot be written faithfully (windows() is the registry, hide needs a corner and a display the trait has no place for, raise also focuses, no minimized/alive query that does not round-trip to the app, ...) are commented where they are and listed in seam.md section 9 (next revision).
komorebi-for-mac gains the komorebi-backend path dependency (workspace dependency, komorebi/Cargo.toml, Cargo.lock).
Verified: macOS fake-CC cargo check --locked --target aarch64-apple-darwin --workspace --all-targets: finished, 0 errors, no warning in macos_backend.rs. Not run: unit tests (macOS code cannot run here).
komorebi::windows_backend::WindowsBackend implements komorebi_backend::Backend over the existing static WindowsApi and Window methods. It is NOT used by the daemon (no call site changed; that is step 8). A window is WindowId::from_hwnd(hwnd) (every bit kept), a monitor MonitorId::from_hmonitor; maximize, opacity and focus-follows-mouse are supported, the five workspace methods answer Unsupported, batches are no-ops.
Methods that cannot be written without a difference in semantics are commented where they are and will be listed in docs/dev/seam.md section 9 (next revision): set_rect (the top flag is dropped; Window::set_position keeps its animation, slide-in, live-resize guard and same-frame skip), hide/restore/minimize/unminimize/maximize (ShowWindow results are ignored, so only an IsWindow pre-check can say WindowGone; hide and restore also drive the border and HIDDEN_HWNDS), windows() (alt-tab list, not the core's filtered candidates), set_opacity (plain write, not the animated, globally-configured Window::transparent), monitor_from_point (nearest monitor, never None).
Verified: nix develop cargo clippy --workspace --locked --all-targets -- -D warnings finished with no warnings or errors. Not run: unit tests (Windows code cannot run here).
komorebi::backend::shared::Shared<B> wraps any KDE Backend (the KWin one, the fake, a dyn Backend) and implements komorebi_backend::Backend over it, so KDE is the first implementation of the shared trait without any change to its own trait or call sites. Ids are WindowId::from_u32 (an id wider than u32 is a window KDE never had); a failed window command becomes Error::WindowGone when the window is no longer alive and Error::Platform(report) otherwise; every optional field is Some; every Capability is supported; batches are no-ops (KDE batches by construction).
Tests (5, in backend/shared.rs): the KDE fake, through Shared, passes the shared contract (check_all); the error mapping; wide ids; every capability supported; fields coming through.
Verified: komorebi-for-kde cargo test --workspace --no-fail-fast: 881 passed, 0 failed over 80 binaries (876 baseline + these 5). clippy -D warnings on komorebi-for-kde reports 3 ptr_arg errors in static_config.rs that predate this work; none in backend/.
komorebi::backend::fake::FakeBackend (and Command) keep their paths, signatures (u32 ids, eyre errors, the 18-variant Command) and behaviour, but the desktop is now komorebi_backend::fake::FakeBackend. The KDE type wraps it and translates ids and errors ('no such window: <id>' text is unchanged); the commands and state that only KDE has (apply_look, set_border_colours, set_wallpaper, drag_end and their getters) stay in the wrapper, and their commands are logged in the shared fake's log (Command::Ext) so their order among the others is kept. KDE's own trait Backend is untouched: that is the least invasive route, no call site and no test body changes (the 26 unit tests of fake.rs stay in the file byte for byte and now test the wrapper).
Cargo: komorebi-for-kde gains the komorebi-backend path dependency (workspace dependency plus komorebi/Cargo.toml; Cargo.lock updated).
Verified: in komorebi-for-kde, cargo test --workspace --no-fail-fast: 876 passed, 0 failed over 80 binaries (identical to the baseline; counts unchanged because the fake.rs tests were kept and the crate's tests are additional). The shared crate's own tests are counted separately.
komorebi_backend::contract: a Harness trait (how a test puts a live window on the desktop of the backend under test) and 14 checks, run by check_all::<H>(): windows listed by id; window agrees with windows/is_window/window_rect; an unknown window is not a window; every window command on an unknown or on a destroyed window fails with WindowGone naming that window; a destroyed window is neither alive nor listed; set_rect on a live window succeeds and the frame stays readable (a request: the value read is not checked); hide/restore tolerate repeats; focus names a known window; capabilities and Unsupported agree (supported means never Unsupported, unsupported means always Unsupported, with a live or unknown window); monitors are sane and found by point; Batch scopes nest around writes; the backend works from another thread; it is usable as a trait object.
Only what the Backend documentation promises is checked, because a write is a request and platforms act at different speeds. The fake passes under three configurations (plain, with a frame offset, with no capability at all, as macOS would be) and a deliberately broken backend (set_rect answers Ok for a gone window) is shown to fail exactly the two checks that cover it.
Verified: native cargo test -p komorebi-backend: 29 unit tests passed, 6 doctests passed, 1 compile_fail doctest passed, 0 failed; clippy --all-targets -D warnings clean.
The in-memory desktop of komorebi-for-kde (backend/fake.rs) moved into the shared crate, rewritten against WindowId/MonitorId/Result and the generic Backend only (look, border colours, wallpaper and drag frames stay out: they are KDE's).
Same behaviour as the KDE fake (permissive mode, minimum sizes, user_* helpers, maximize, fullscreen, workspaces, the ordered Command log with 15 of KDE's 18 variants; close() leaves the foreground window alone, as KDE's did), plus:
- failure injection: fail_next(Op, Error) and fail_next_for(Op, WindowId, Error), oldest first, one per call; the failed call is still logged as a command and changes nothing else (none existed on KDE);
- frame offsets: set_frame_offset / set_window_frame_offset make window_rect differ from the request by a constant, as the Windows shadow border does;
- capabilities: all four on by default, set_supported switches one off and its operations return Unsupported without being logged;
- batches: begin/end are counted (open_batches) and logged as BatchBegin/BatchEnd;
- FakeBackend<X>: a platform extension logs its own commands in order with record_ext (Command::Ext(X)); the shared fake is FakeBackend<NoExt> (new/permissive/with_one_monitor), an extension uses empty/permissive_ext/one_monitor because default type parameters are not inferred.
Verified: native cargo test -p komorebi-backend: 24 unit tests and 6 doctests passed, 0 failed (the KDE fake tests were ported and 6 failure/offset/capability/batch/extension tests added); clippy --all-targets -D warnings clean; cargo fmt applied.
New crate crates/komorebi-backend (edition 2024, root workspace member, depends only on komorebi-layouts for Rect).
- WindowId and MonitorId: opaque u64 newtypes. KDE and macOS ids convert with from_u32/to_u32; a Windows HWND/HMONITOR goes through from_hwnd/to_hwnd, which keep the bits of the isize unchanged (isize as usize as u64), so no handle is lost whether or not it fits in 32 bits (the e2e samples stay below 2^32, which is an observation, not a guarantee).
- WindowInfo and MonitorInfo: plain data; only id, pid, rect, minimized and alive are required, the rest (title, class, exe, path, fullscreen, maximized, normal; name and serial id of a monitor) are Option (seam.md consequence 6).
- Error: WindowGone(id) (finding 9: macOS uses a failed write as its signal that a window died), Unsupported(Capability) and Platform(cause).
- Backend: only the operations of seam.md section 2.1 that at least two platforms have (5 queries, 9 commands), a Capability query for workspaces, maximize, opacity and focus-follows-mouse (their methods default to Unsupported), begin_batch/end_batch with a no-op default plus the Batch guard (finding 8), the documented set_rect contract (a request; reads after a write return what the platform defines, consequence 2 as corrected) and the thread contract (any non-main thread, no waiting for the platform's main thread: consequences 3 and 4, written as TARGET rules). Every doc comment says which platforms implement the method.
Verified: native cargo test -p komorebi-backend (2 unit tests, 3 doctests, 0 failed) and cargo clippy -p komorebi-backend --all-targets -- -D warnings clean. The Windows/KDE/macOS gates are not affected yet (new crate, nothing depends on it).
bar.rs of the macOS bar still carries the Windows code it was copied from as comments: the PowerShell session (start/stop/exec), the InputConfig scroll state and its initialisation, the mouse configuration in apply_config, the hwnd field and its use, the CJK font fallbacks and the whole mouse handler of update. Those blocks (and the comments that only introduce them) are deleted; every live comment stays. The shared bar.rs of the later steps would otherwise inherit them.
Removed: 258 lines (the 269 of the digest included live comments that were kept).
Verified: macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors (6 bar warnings, unchanged); rustfmt --check clean on bar.rs; harness regeneration byte-identical for both. Not verified: rendering.
komorebi-for-mac/komorebi-bar/src/widgets/keyboard.rs (177 lines, still written against the windows crate) and media.rs (122 lines) are not declared as modules (their mod lines in widgets/mod.rs are comments), so they are not part of the build; the widget variants that would use them are commented out in widget.rs. The files and the two commented-out mod lines are removed. The macOS bar has no keyboard or media widget; the config types of both live in the Windows bar and move to komorebi-bar-core with the platform seam. No behaviour change, no schema change.
Verified: macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors (6 bar warnings, unchanged); harness regeneration byte-identical for both. Not verified: rendering.
Pure move of the three widget files that are byte-identical in the two bars since the previous revision. Each bar's widgets/mod.rs re-exports them (pub use komorebi_bar_core::widgets::memory / date / update). The core takes chrono, chrono-tz, reqwest (blocking) and serde_json (now a normal dependency, it was only a dev-dependency).
Removed: 541 lines from each bar (138 + 245 + 158), 541 added once.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors; native cargo test -p komorebi-bar-core 3 unit + 2 oracle passed; harness regeneration byte-identical for both (macOS 3,774 lines, Windows 9,091 lines). Not verified: rendering.
The three widgets that differ between the bars only by a platform fact now read it from the hook, in both trees identically: memory opens the task manager through open_system_tool(TaskManager) (was cmd.exe /C taskmgr.exe on Windows, open -a 'Activity Monitor' on macOS: the same commands, now in each platform.rs); date takes its calendar glyph from calendar_icon() (CALENDAR_DOTS on Windows, CALENDAR on macOS); update polls latest_release_api_url() (the komorebi and komorebi-for-mac release APIs) and opens the release page through open_system_tool(Url(..)) (explorer.exe on Windows; on macOS the same explorer.exe it spawns today, broken there, unchanged). After this the memory.rs, date.rs and update.rs of the two bars are byte-identical.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors; memory.rs, date.rs and update.rs byte-identical across the trees (diff). Note kept as it was: update.rs has a second GitHub API URL (the periodic refresh thread, line 85) that is the komorebi repo in both bars, also on macOS; only the first check goes through the hook. Not verified: clicks and the update check (no screen, no daemon).
The click on the cpu widget (task manager) and on the battery widget (power settings) now calls platform().open_system_tool(...) instead of spawning cmd.exe in shared code. The Windows hook spawns the same commands as before; the macOS hook still spawns the same (broken there) cmd.exe commands, so neither platform's behaviour changes. Fixing macOS is now a change to one function of komorebi-for-mac/komorebi-bar/src/platform.rs.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors; native cargo test -p komorebi-bar-core 3 unit + 2 oracle passed; harness regeneration byte-identical for both. Not verified: that a click still opens the tool (no screen).
komorebi_bar_core::platform holds what the widgets need from the operating system and differs between the bars: BarPlatform { open_system_tool, latest_release_api_url, calendar_icon }, the SystemTool enum (task manager, power settings, network settings, a URL) and install()/platform(), a once-set global the shell fills first thing in main() (a widget calls platform() when it is clicked or built, never before main). Each bar gets an os.rs with its implementation (main.rs re-exports the core module as crate::platform, so shared files can say crate::platform::platform()): Windows spawns exactly the commands the widgets spawn today (cmd.exe /C taskmgr.exe, cmd.exe /C start ms-settings:batterysaver, cmd.exe /C ncpa, explorer.exe <url>); macOS maps TaskManager to open -a 'Activity Monitor' and network settings to open x-apple.systempreferences:... as memory.rs and network.rs do today, and keeps the commands the shared files spawn on macOS today for the rest (cmd.exe taskmgr.exe for cpu, cmd.exe start ms-settings:batterysaver for battery, explorer.exe for a URL), which are broken there: a one-line fix per tool in os.rs, not part of this change.
Nothing calls the hooks yet: the next revisions switch the widgets over, one concern each.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors (6 bar warnings, unchanged); native cargo test -p komorebi-bar-core 3 unit + 2 oracle passed; rustfmt --check clean on the new files and both main.rs. No widget calls the hooks yet, so no behaviour changes.
Pure move of the two widget files that are byte-identical in the Windows and macOS bars. Each bar's widgets/mod.rs re-exports them (pub use komorebi_bar_core::widgets::battery / cpu), so crate::widgets::battery::... resolves as before; the files themselves are unchanged.
Dependencies: the core takes sysinfo from the root workspace (0.39) and starship-battery 0.11, the versions the Windows bar already used. The macOS bar therefore now gets these two widgets built against sysinfo 0.39 (its own workspace pins 0.38, still used by its other widgets until they move) and starship-battery 0.11 (was 0.10; its own dependency on the crate is removed). Both compile unchanged; behaviour of the libraries is not verified on macOS.
Not changed on purpose: the macOS bug that these two files spawn cmd.exe (taskmgr.exe, explorer.exe) on click is still there, identically; it is fixed behind the platform hook, not inside a move.
Removed: 325 lines from each bar (191 + 134), 325 added once.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors (bar warnings 6, unchanged; macOS Cargo.lock: starship-battery 0.10.3 -> 0.11.1 with its dependencies (uom, plist, nix), sysinfo 0.39.6 added next to 0.38.4); native cargo test -p komorebi-bar-core 3 unit + 2 oracle passed; harness regeneration (S/bar2/regen.sh, which now fails when a harness does not build): macOS byte-identical (3,774 lines), Windows byte-identical (9,091 lines). Not verified: rendering, sysinfo and starship-battery behaviour on macOS.
The three-line trait every widget implements is split off widgets/widget.rs (the WidgetConfig enum stays there until every widget it names has moved) and becomes komorebi_bar_core::widgets::widget::BarWidget; each bar's widget.rs re-exports it, so crate::widgets::widget::BarWidget resolves as before. The core's config module re-exports the leaf types (crate::config::LabelPrefix) so that the widgets can later move without a text edit.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors; native cargo test -p komorebi-bar-core 3 unit + 2 oracle passed. No schema type changed. Not verified: rendering.
Pure move of render.rs (RenderConfig, Grouping, GroupingConfig, GroupingStyle, RoundingConfig and the helpers), byte-identical in both bars since the three previous revisions, with the one path edit the move needs: Alignment is imported from komorebi_bar_core::alignment instead of crate::bar. Each bar's main.rs re-exports the module (pub use komorebi_bar_core::render), so crate::render::... resolves as before.
Removed: 382 lines from each bar, 382 added once.
Note: the one added line is #[allow(clippy::new_without_default)] on RenderConfig::new, which clippy now asks for because the type is public API of a library crate.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors; native cargo test -p komorebi-bar-core 3 unit + 2 oracle passed; harness regeneration: macOS byte-identical (3,774 lines), Windows byte-identical (9,091 lines). Not verified: rendering.
render.rs is about to move into komorebi-bar-core and must not know KomobarConfig. The only place it did was impl RenderExt for &KomobarConfig (the trait and RenderConfig stay in render.rs); that block, byte-identical in both bars, becomes the last item of config.rs with its imports. No behaviour change.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors; rustfmt --check clean on both config.rs and render.rs. Not verified: rendering.
The two render.rs differed in two places. The Windows bar documented transparency_alpha with '(default: 200)' in its text; the macOS bar documented it without and put the default into the schema (schemars extend default 200), so the macOS nix options file gets default = 200. The macOS spelling is taken for both, because a change to the macOS schema.bar.json would also change the generated nix/komorebi-bar-options.nix, which cannot be regenerated here. Windows: the description loses the '(default: 200)' text and the property gains "default": 200 (reviewed diff of schema.bar.json below, pin updated in tests/schema_oracle.rs); the one comment on RenderConfig that only Windows had ('/// Render configuration') is added to the macOS copy. The two render.rs are then byte-identical.
The schema diff is the two lines of the property: description 'Alpha value for the color transparency [[0-255]] (default: 200)' -> 'Alpha value for the color transparency [[0-255]]', and one added "default": 200. macOS schema.bar.json is untouched (its harness regeneration is byte-identical).
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors; native cargo test -p komorebi-bar-core: 3 unit + 2 oracle passed, 0 failed; harness regeneration: macOS byte-identical (3,774 lines), Windows byte-identical to the updated schema.bar.json (9,091 lines, diff exactly the two lines above). Not verified: rendering.
komorebi_client::Colour and ::Rgb are re-exports of komorebi_themes::colour::Colour and ::Rgb in both trees; naming the crate that defines them lets render.rs move into komorebi-bar-core, which must not depend on komorebi_client (D25). Both bars' render.rs change identically. No behaviour change.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors. Types are the same re-exports, so no schema change. Not verified: rendering.
Pure move of the tail of config.rs, which both bars have byte-identical: Position (with its Vec2 and Pos2 conversions), LabelPrefix, DisplayFormat, the extend_enum! macro, WorkspacesDisplayFormat, MediaDisplayFormat and the three extend_* unit tests, to komorebi_bar_core::config::types. Each config.rs re-exports the module (pub use komorebi_bar_core::config::types::*), so crate::config::LabelPrefix and the others resolve as before; the Windows config.rs keeps its own pub use of KomobarTheme.
Two differences, neither visible in a schema: the macOS bar had no MediaDisplayFormat (it has no media widget), so it now has the unused type; the unit tests count as 3 in komorebi-bar-core instead of 3 in each bar, and run natively on Linux. The bars take the crate's schemars feature from their own (komorebi-bar-core/schemars).
Removed: 170 lines from the Windows config.rs and 149 from the macOS one; 174 added once (170 moved, 4 import lines).
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors (bar warnings 6, unchanged); native cargo test -p komorebi-bar-core: 3 unit tests (the moved extend_* tests) + 2 schema oracle passed, 0 failed; harness regeneration (S/bar2/regen.sh): macOS byte-identical (3,774 lines), Windows byte-identical (9,090 lines). Not verified: rendering.
Pure move of what every bar module reads and nothing in it depends on: the monitor and label statics of main.rs (MAX_LABEL_WIDTH, MONITOR_LEFT/TOP/RIGHT/INDEX, BAR_HEIGHT, DEFAULT_PADDING, AUTO_SELECT_FILL/TEXT_COLOUR, and the Windows WIDGET_CLICKED flag with its two functions) and the three-variant Alignment of bar.rs. Each bar re-exports them (pub use komorebi_bar_core::statics::*, pub use komorebi_bar_core::alignment::Alignment), so crate::MAX_LABEL_WIDTH and crate::bar::Alignment resolve as before.
The only difference between the trees is that the macOS bar now also has the WIDGET_CLICKED flag and its two functions; nothing in the macOS bar calls them (only the Windows systray widget does), so there is no behaviour change.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors; native cargo test -p komorebi-bar-core 2 passed, 0 failed. Not verified: rendering.
Pure move of the two files of the bar that depend on nothing else in it, byte-identical in the Windows and macOS bars (selected_frame.rs since the previous revision). Each bar keeps its paths: main.rs re-exports the modules (pub use komorebi_bar_core::selected_frame / ui), so every crate::selected_frame::... and crate::ui::... in the widgets and bar.rs resolves as before. The crate takes eframe from the workspace; the macOS bar takes the crate by path.
Removed: 111 lines (89 + 22) from each of the two bars, 111 added once.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors (bar warnings 10 -> 6, the four strokes of the previous revision); native cargo test -p komorebi-bar-core 2 passed, 0 failed; harness schema regeneration (S/bar2/regen.sh): macOS byte-identical to its schema.bar.json (3,774 lines), Windows byte-identical to the regenerated schema.bar.json (9,090 lines). Not verified: rendering.
The four Stroke::new(1.0, ...) calls become 1.0_f32, which is what the Windows copy has and what the compiler falls back to anyway (the f32: From<f64> fallback lint). Converge step before moving selected_frame.rs into komorebi-bar-core: afterwards both copies are byte-identical. No behaviour change.
Verified: macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors; the bar's warnings went from 10 to 6 (the four f32 literal fallbacks in this file are gone); macOS schema regeneration (harness) still byte-identical. Not verified: rendering.
Skeleton of the shared bar crate (D25/D26): an empty library and the oracle that every later move of bar code is held to. tests/schema_oracle.rs pins the checked-in schema.bar.json of the Windows bar (9,090 lines, FNV-1a 0x557301341968bff9) and of the macOS bar (3,774 lines, 0xc99e02949424e5d7), so any change to either file, intended or not, fails the test until the pin is updated in a reviewed revision. Once config.rs moves into the crate the test generates the schema and compares it with the file instead.
The pins are of the files as they are after the previous revision (the regenerated Windows file) and are the values the out-of-tree harness (S/bar2/regen.sh over the real sources) reproduces: macOS byte-identical, Windows byte-identical to the regenerated file.
Verified: native cargo test -p komorebi-bar-core: 2 passed, 0 failed; Windows cargo clippy --workspace --locked --all-targets -D warnings clean; macOS fake-CC cargo check --locked --workspace --all-targets finished, 0 errors.
Step 2 put SocketMessage in komorebi-protocol, so the Windows bar schema (reached through MouseConfig.message) changed. The file was regenerated with the harness that reproduced the old file byte for byte except for the State closure (S/bar2/regen.sh: real config.rs, render.rs, selected_frame.rs, ui.rs and widgets over the real komorebi-protocol, komorebi-layouts and komorebi-themes with the Windows cfgs on).
Diff, 10,552 -> 9,090 lines, all from step 2 (D15, D16, D27):
- State is now the opaque StateBlob (object, no properties) and the 31 defs only reachable through it are gone: Column, ColumnSplit, ColumnSplitWithCapacity, ColumnWidth, Container, CustomLayout, FloatingLayerBehaviour, GridLayoutOptions, IdWithIdentifier, InitialWindowPlacementRules, Layout, LayoutOptions, MatchingRule, Monitor, PlacementMatchingRules, PlacementTarget, Ring, Ring2-4, ScrollingCentering, ScrollingLayoutOptions, ThemeOptions, ThemeVariant, Wallpaper, Window, WindowContainerBehaviour, WindowPlacement, Workspace, WorkspaceConfig, WorkspaceLayer.
- SocketMessage gains ReloadHotkeys and HotkeysState (the unified enum) and a description; DebugWindow is int64 (was int).
- Descriptions added to State, StateQuery, SubscribeOptions; WindowKind description reworded.
Nothing in the bar's own types changed. The macOS file regenerates byte-identical (3,774 lines) and is not touched.
Verified: S/bar2/regen.sh (macOS cmp identical; Windows differs only by the items above, checked with jq after deleting the 31 defs from the old file). Not verified: against a real Windows komorebi-bar --schema run (cannot run here).
Merge revision: wip-main (history merges, fixes, step 0, steps 1, 2, 4 and 5, flake work, seam document) and wip-step3 (komorebi-config: matching types, behaviour enums, AspectRatio/PerAnimationPrefixConfig, asc and the application configuration data, placement types, the one StaticConfig with per-platform schema views, the daemons wired to it). Conflicts between step 3's edits and step 4's restructuring of komorebic (the schema-printing arms moved into the WindowsCommands/MacosCommands/KdeCommands matches) and in the Cargo.lock files are resolved here once, instead of through 35 separate rebases.
Renders the macOS command line through komorebic-core (it needs no macOS to do it) and compares every page with komorebi-for-mac/docs/cli after the page transform macOS uses (a heading, 'Usage: komorebic <name>'), and the number of pages. It found the four macOS pages that step 2 left stale (the previous revision regenerates them) and keeps all 158 in step from now on. The test returns early when the macOS tree is not in the source (the nix build of the Windows workspace).
Verified: native cargo test -p komorebic-core --test docs_cli: 1 passed.
platforms::reads(platform, key) says whether a platform reads a top-level key and platforms::unsupported_keys(raw, platform) lists the keys of a configuration text that are keys of komorebi.json but that the platform does not read, which is what a daemon can say at load time instead of dropping them silently. The table is the x-komorebi-platforms markers themselves, so a key cannot be added without saying who reads it. The daemons do not call it yet: reporting at load time is a visible change of behaviour for the user's decision.
The oracle test checks the table against the three checked-in schemas (every key of a tree's schema.json is read by that platform) and on examples.
Verified: native cargo test -p komorebi-config: 4 unit + 9 schemas + 1 doctest passed, 0 failed (tier 1).
Step 2 moved SocketMessage into komorebi-protocol and unified its doc comments, so four macOS pages under komorebi-for-mac/docs/cli changed (animation, border-colour and two more; 26 insertions, 10 deletions). Regenerated with the docgen oracle (S/step4/oracle-check.sh) so the checked-in pages match what the command line renders. Verified: oracle-check reports the macOS pages identical.
static_config re-exports StaticConfig, WorkspaceConfig, MonitorConfig, BorderColours, ThemeOptions, Wallpaper, AnimationsConfig, AppSpecificConfigurationPath, TabsConfig and StackbarConfig from komorebi-config and loses their definitions, the end_of_life/aliases/deprecated notices and read_raw/read (about 650 lines). The inherent impl StaticConfig cannot stay in the daemon (orphan rule): apply_globals is a private trait (ApplyGlobals) and preload, postload and reload are the public extension trait StaticConfigExt, bodies unchanged; process_command, window_manager and main import the trait (komorebic only calls read and the notices, which are inherent in the crate, so it is untouched). The From<&WindowManager> and From<&Monitor> conversions fill the keys Windows never writes (border_radius, titleless_applications, tabbed_applications, window_hiding_position) with None or Default. The five #[deprecated] attributes of the old fields are gone (the schema marker replaced them; see the crate).
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings: clean (tier 2); the oracle test proves the Windows view of this StaticConfig equals schema.json byte for byte. Not verified: Windows unit tests (cannot run), running komorebic.exe or the daemon.
docs/cli of the three trees for the commands the previous revision unified, written the way each tree writes its pages: komorebi-for-kde 6 pages (colour/behaviour spelling), komorebi-for-mac 7 (the shorter scrolling descriptions), the Windows tree 2 (eager-focus, promote-focus). The Windows docs/cli is otherwise stale (older clap output, three pages missing; found in step 4 prep) and the other pages are left alone.
Verified: the KDE pages are byte-identical to komorebic docgen (166 of 166 against the real binary); the macOS and Windows pages are the docgen-all output with the same page transform that made 158 of 158 macOS pages identical before. Diffs are two-line spelling or description changes (see the previous revision).
core re-exports WindowPlacement, PlacementTarget, PlacementMatchingRules and InitialWindowPlacementRules from komorebi-config and loses the local definitions and impls (about 230 lines) with the imports they alone needed. PlacementTarget is now the crate's copy (the KDE one): it parses the same values (a name, a number, or numeric text) and only the wording of its error for a bad value differs; the schema text Windows checked in for it is part of the Windows view of komorebi-config (changes.rs), which printing already uses.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings: clean (tier 2). Not verified: Windows unit tests (cannot run).
border-colour, monocle-focus-behaviour, toggle-monocle-focus-behaviour, toggle-window-container-behaviour, toggle-workspace-window-container-behaviour, unmanaged-window-operation-behaviour, scrolling-layout-columns, scrolling-layout-full-width, expel, retile, center-focus, consume, toggle-column-stack, eager-focus and promote-focus had the same arguments, flags and messages in the three trees and only their help text differed. They move into Messages (and out of WindowsCommands, MacosCommands and the KDE komorebic with their arms and argument structs) with ONE text each: the Windows text, except for eager-focus and promote-focus, where the wording two of the trees shared wins ('executable', 'largest window container').
The page diff against the pre-move output is docs only (reviewed, kept in the progress log): KDE says colour and behaviour instead of color and behavior in six pages; Windows eager-focus and promote-focus change a phrase; macOS loses the longer descriptions it had for center-focus, consume, expel, retile and toggle-column-stack and the three of its scrolling pages that differ from the Windows wording get the Windows text. Nothing that changes behaviour (flags, defaults, possible values, aliases) differs. The longer macOS descriptions make claims about the other layouts and about retile that were not checked for the Windows and KDE daemons, so the shorter text was kept; restoring them is a one-revision change to messages.rs. data-directory, disable-autostart, log (platform facts: paths, shortcuts, komorebi.exe), enable-autostart, start, stop and pan stay in the platform enums.
Verified: oracle compare: only the pages above differ (windows 2, macos 7, kde 6 of 187/158/166); native cargo test -p komorebic-core 34 passed; KDE cargo test -p komorebic --locked 80 passed; macOS fake-CC cargo check --locked 0 errors; Windows clippy --workspace --locked --all-targets -D warnings clean.
static_config re-exports StaticConfig, WorkspaceConfig, MonitorConfig, BorderColours, ThemeOptions, Wallpaper, AnimationsConfig and AppSpecificConfigurationPath from komorebi-config and loses their definitions, read_raw and read (about 410 lines). The inherent impl StaticConfig cannot stay in the daemon (orphan rule): apply_globals is a private trait (ApplyGlobals) and preload, postload and reload are the public extension trait StaticConfigExt (the macOS preload keeps its run_loop parameter), bodies unchanged; process_command, window_manager and main import the trait. The From conversions stay and fill the keys macOS never writes with None or Default.
Verified: macOS fake-CC cargo check --locked --target aarch64-apple-darwin --workspace --all-targets: finished, 0 errors, no warnings in komorebi (tier 2). Not verified: macOS unit tests (cannot run), running komorebic.
The three animation subcommands were listed as differing in arguments between the trees because the possible values of --animation-type came from three different AnimationPrefix enums. Since step 2 there is one, and the pages of the three trees are now the same text (checked by comparing the rendered pages without fences and widths), so the Windows definitions move into Messages and the copies in WindowsCommands, MacosCommands and the KDE komorebic go, with their arms. The pages are byte-identical to the oracle: this is a move, not a docs change.
Verified: oracle: windows 187, macos 158, kde 166 pages byte-identical; native cargo test -p komorebic-core: 31 + 3 passed; KDE cargo test -p komorebic --locked: 80 passed; macOS fake-CC cargo check --locked: 0 errors; Windows clippy --workspace --locked --all-targets -D warnings clean.
static_config re-exports StaticConfig, WorkspaceConfig, MonitorConfig, BorderColours, ThemeOptions, Wallpaper, AnimationsConfig and AppSpecificConfigurationPath from komorebi-config and loses their definitions, read_raw and read (about 480 lines). The inherent impl StaticConfig can no longer exist in the daemon (orphan rule): its apply_globals is a private trait (ApplyGlobals) and preload, postload and reload are the public extension trait StaticConfigExt, with the bodies unchanged; callers import the trait (process_command, window_manager, main and the integration tests that call StaticConfig::preload, postload or reload: one added use line each, no assertion touched). The From<&WindowManager> and From<&Workspace> conversions stay (a reference to a local type is local) and fill the keys KDE never writes with None or Default.
Two places read the schema of StaticConfig to know which keys are read; the one type now carries every platform's keys, so they ask for the KDE view: the unknown-key diagnostic (check.rs known_keys) and the test config_parity (keys_that_are_read: only its source of the key list changed, the assertions are the same).
Verified: KDE cargo test --workspace --no-fail-fast -- --test-threads=1: 876 passed, 0 failed, 80 binaries (baseline 891: 4 unit tests moved to komorebi-config with the matching types, doc tests 45 -> 41 as examples moved; the other 7 are not itemised); komorebic static-config-schema and application-specific-configuration-schema byte-identical to the checked-in files. Windows and macOS untouched by this revision.
start becomes a WindowsLauncher (the PowerShell script, its printing of the script or the error, the process lookup by name komorebi.exe, the run in front of the user) around komorebic_core::launcher::start; the preflight for whkd, masir and ahk, the script building, the companions and the validation epilogue stay in the arm, and stop sends launcher::stop_message (its companions and force-quit are untouched). 'Waiting for komorebi.exe to start...' now has a trailing space and is flushed, as KDE's was.
Verified: Windows cargo clippy --workspace --locked --all-targets -D warnings clean. Not verified: running komorebic start on Windows (no Windows guest run).
read_raw and read are identical in the three daemons (KDE's docs and doctest are taken); end_of_life, aliases and deprecated are the Windows pure string checks that print what a configuration file should change. None of them touches a window manager, so they live next to the type; the daemons' own inherent impls cannot (the orphan rule), they become an extension trait in the wiring revisions.
Verified: native cargo test -p komorebi-config: 4 unit + 8 schemas + 1 doctest passed, 0 failed (tier 1).
start becomes a MacosLauncher around komorebic_core::launcher::start; the companion bar and the licence text after it stay as they were, and stop sends launcher::stop_message. The odd '--config' arguments macOS passes are kept as they were. 'Waiting for komorebi to start... ' is now flushed as KDE's was (the text is the same).
Verified: macOS fake-CC cargo check --locked --workspace --all-targets: 0 errors. Not verified: running komorebic start on macOS.
KomorebiThemeBase16, KomorebiThemeCatppuccin and KomorebiThemeCustom carried stackbar_focused_text, stackbar_unfocused_text and stackbar_background under cfg(target_os = "windows"), so what a Linux or macOS build accepted (and what its schema said) depended on the compile target: a Linux build could not even generate the Windows schema. komorebi-config needs one set of types, so the nine cfg attributes are removed. The KDE and macOS schemas keep their bytes because their views remove the fields again (changes.rs, applied after pruning); the Windows tree is unchanged.
Consequences, all mechanical: the places in KDE and macOS that build a KomorebiThemeCustom, KomorebiThemeCatppuccin or ThemeOptions by name (workspace.rs in both, two KDE tests and a komorebi-client test) now say stackbar_*: None, which keeps what they meant. A KDE or macOS komorebi.json that sets one of the three keys in a theme is now accepted and ignored instead of ignored as an unknown key. The macOS bar's own schema (komorebi-for-mac/schema.bar.json) will gain the three fields when it is next generated; it cannot be regenerated on this host and is left for step 4b, whose per-platform view takes care of it.
The oracle test now compares the Windows view too: all three views of StaticConfig are byte-identical to the checked-in schema.json files (Windows 141169, KDE 127164, macOS 120057 bytes).
Verified: native cargo test -p komorebi-config: 4 unit + 8 schemas passed, 0 failed; Windows cargo clippy --workspace --locked --all-targets -D warnings clean; KDE cargo check --workspace --all-targets clean (after the wallpaper test fix), komorebic output byte-identical to schema.json and schema.asc.json; macOS fake-CC cargo check --workspace --all-targets: 0 errors. Not verified: macOS and Windows unit tests, schema.bar.json of either bar.
start becomes a KdeLauncher (the Command for komorebi, the process lookup by exact name, the run in front of the user) around komorebic_core::launcher::start, which has the attempt loop; stop sends launcher::stop_message. The text printed and the commands run are the same.
Verified: KDE cargo test -p komorebic --locked: 10 + 12 + 49 + 9 passed, 0 failed; oracle kde 166 of 166 pages. Not verified: an actual komorebic start against komorebi (no test starts the daemon).
komorebic static-config-schema and the daemon's StaticConfigSchema answer print schema::view::<StaticConfig>(Platform::CURRENT) of the shared crate instead of the schema derived from the daemon's own StaticConfig; the oracle test proves the macOS view equals schema.json byte for byte.
Verified: macOS fake-CC cargo check --locked --target aarch64-apple-darwin --workspace --all-targets: finished, 0 errors (tier 2). Not verified: running komorebic or the macOS tests.
start is the same dance in all three komorebic: look whether komorebi is running, start it, wait three seconds and look again, give up after three attempts by running it in front of the user so its stderr can be read. launcher::start runs that loop over a Launcher (daemon_name, spawn, is_running, diagnose) that each platform implements by hand; what follows a successful start (companions, the licence text) stays in each komorebic, as does stop's handling of companions, whose order differs per platform; only stop_message(ignore_restore) is shared. The loop flushes standard output after 'Waiting for ... to start...' (KDE did, macOS and Windows did not) and puts the same space after it; both are invisible in the text printed. Nothing uses it yet.
Verified: native cargo test -p komorebic-core: 31 passed, 0 failed (4 new launcher tests with a fake launcher: already running, second try, three tries then diagnose, stop message).