Files
komorebi/docs/dev/bar-unification.md
T
LGUG2Z e0e0f49714 docs(dev): old paths in the analysis notes carry the new ones
seam, core-model, settings, rule-matching, bar-unification, e2e-to-unit and known-issues are
measurements and decision logs written for the old directories. They keep their history; each
gets a "Paths (cutover stack)" note under the title that maps the old names (komorebi/,
komorebi-for-kde/komorebi/, komorebi-for-mac/komorebi/, nix/, docs/, schema files) to the new
ones and says the 0.1.x numbers are history. The seam.md tree table, the test file paths in
e2e-to-unit.md and known-issues.md (files that exist and are named as current) and the
bar-e2e.md schema path now name the files where they are.

Not rewritten: the line numbers, measurements and "at the time" statements. Not touched:
backend-switch.md (no old paths), build-and-test.md and flake.md (flake-one).

Verified: the files named as current exist (komorebi-linux/tests/*.rs); relative links resolve.
2026-11-05 15:03:27 -08:00

185 lines
15 KiB
Markdown

# One `komorebi-bar` for Windows and macOS
> **Status (wip-cand4):** B0-B3 done (one `komorebi-bar` package, a module per platform, schema views); B4 (merging the rendering code) not done and cannot be shown mechanical without a screen; macOS bar never rendered by these changes.
> **Paths (cutover stack):** this document was written before the directories were renamed and keeps the paths of its time. Now: `komorebi/` is `komorebi-windows/`, `komorebi-for-kde/komorebi/` is `komorebi-linux/`, `komorebi-for-mac/komorebi/` is `komorebi-macos/`, `komorebi-for-kde/komorebi-hotkeys/` is `komorebi-hotkeys/`, `komorebi-for-kde/nix/` and `komorebi-for-mac/nix/` are `nix/linux/` and `nix/macos/`, `komorebi-for-kde/docs/` and `komorebi-for-mac/docs/` are `docs/linux/` and `docs/macos/`, `schema*.json` are in `schemas/{windows,linux,macos}/`, and every package is at version 0.2.0 (the 0.1.x numbers below are history).
Status at the revision that adds this file. Everything below was measured on this tree with
`diff` (counts are lines that differ, `<`/`>` lines of `diff`, hunks in parentheses). Nothing
here was rendered: the bars cannot run on Linux. Gates that did run are named; the ones that
did not are listed at the end.
KDE has no bar: `komorebi-for-kde` has no `komorebi-bar` package and no `schema.bar.json`.
The plan below is for two platforms; a KDE bar would add a third shell and a third schema view.
## What exists
- `crates/komorebi-bar-core` (4,592 lines): the code both bars share, one copy each.
`selected_frame`, `ui`, `statics`, `alignment`, `render`, `config` (the leaf types and the whole
`KomobarConfig` with the mouse options), the widgets `applications`, `battery`, `cpu`, `date`,
`memory`, `storage`, `time`, `update`, `komorebi_layout`, the icon cache (`widgets/icons.rs`),
all widget configuration types (`komorebi_config`, `network_config`, `platform_config` for
keyboard, media and systray), `WidgetConfig` with a variant for every widget, the `BarWidget`
trait, `platform` (`BarPlatform`, `SystemTool`, a once-installed global) and `schema`
(per-platform views, see below).
- `BarPlatform` hooks: `open_system_tool`, `latest_release_api_url`, `calendar_icon`,
`command_icon`, `exec_shell_command`, `send_komorebi_message`, `send_komorebi_batch`.
- Two shells, `komorebi-bar` (Windows, root workspace, version 0.1.42) and
`komorebi-for-mac/komorebi-bar` (own workspace, own `Cargo.lock`, own flake, version 0.1.1).
Each keeps `main.rs`, `bar.rs`, `os.rs`, the one-line `config.rs`, and the widgets whose
runtime differs: `komorebi.rs`, `network.rs`, and on Windows `keyboard.rs`, `media.rs`,
`systray.rs`. `widgets/widget.rs` is only `WidgetConfigExt` (config -> widget).
- The schema oracle regenerates the macOS file natively
(`tests/schema_oracle.rs`, module `generated`); the Windows file is still checked by the
out-of-tree harness because three descriptions read differently when compiled for Windows.
## Why one package is not possible yet (the structural blocker)
The two trees are two Cargo workspaces with two lockfiles and two flakes. A package belongs to
one workspace, so a single `komorebi-bar` that builds on both platforms needs one workspace
first: the Windows root `Cargo.toml` and `komorebi-for-mac/Cargo.toml` merged, one `Cargo.lock`,
one version (0.1.42 vs 0.1.0), one `komorebi-client` (the two `lib.rs` differ in 150 lines:
Windows re-exports the daemon crate and `State`/`Window`/`Workspace`, macOS has its own
types) and `windows`/`objc2` dependencies under `[target.'cfg(...)'.dependencies]`. That is a
repository-level cutover (the plan's "final naming of the outputs and the unified app/devShell
is a cutover decision"), not a bar change. Until then the bars share the crate and converge,
they do not merge. Everything in this stack is ordered so the merge later is a package move.
## The schema is solved: views of one union
`schema.bar.json` differs between the platforms in 40 hunks (5,381 lines of 9,091 / 3,774).
Analysed by definition (`jq` over `$defs`):
- 42 definitions exist only on Windows: all reachable only through `MouseConfig` (which holds a
`SocketMessage`, hence the closure) and the `Keyboard`/`Media`/`Systray` widgets. Removing the
`mouse` property and the three widget variants and pruning unreachable definitions removes
all 42.
- 14 differences remain in the 55 shared definitions, all text or default: root description
(macOS names its own schema URL), `height` default `50` vs `50.0`, the margin and padding
examples (`10` vs `10.0`, a sentence about defaults, a trailing comma), `show_icon`
(deprecated flag and backticks), `KomorebiLayout` (untagged `anyOf` on Windows, externally
tagged `oneOf` on macOS), `NetworkConfig.default_interface_options` (Windows only),
four storage descriptions (`C:\` vs `/`), `TimeConfig.changing_icon` (`default: false` only
on macOS), `TimeFormat` title of `Custom` (macOS says `Default`, a mislabel), the
`WidgetConfig` variants above.
`komorebi_bar_core::schema` holds this as data (`schema/changes.rs`, 16 changes) in the same
design as `komorebi-config`: the union is the Windows text, `view_of` applies a platform's
changes and prunes. `tests/schema_oracle.rs` proves that the macOS view of the checked-in Windows
file is byte-identical to `komorebi-for-mac/schema.bar.json`, that the Windows view is the
identity, and that views are idempotent. Both bars print `--schema` through their view
(`schema::generated_text`), so the files and `nix/komorebi-bar-options.nix` do not change when
code is converged onto the Windows spelling.
The out-of-tree harness (`S/bar2/regen.sh`, real bar sources over the real
`komorebi-protocol`/`komorebi-config`) regenerates both files on Linux. Since `config.rs` moved, the
in-repo oracle regenerates the macOS file itself (`generated` tests); only Windows needs the harness.
## What is still per platform (measured at this tip)
| file | win / mac lines | differing | what differs and why it is not moved |
|---|---|---|---|
| `widgets/komorebi.rs` | 940 / 986 | 136 (17) | Two things. (1) Real rendering differences: macOS wraps `show_label` and the "all icons" painter in a `Frame` with a margin and a minimum height (`bar.show_label(...)`, a method, vs an associated function on Windows), `1.0` vs `1.0_f32`. Taking either spelling is a visual change on the other platform: needs a screen. (2) The conversion from the daemon state: Windows uses `komorebi_client::{State, Window, Workspace, Container}` (`hwnd`, `title()`, `process_id()`, windows-icons), macOS its own client types (`details.title`, `details.icon_path`, `icns_to_rgba`, `device`, no `Layout::Custom`). A State-to-snapshot split (`WindowInfo`/`MonitorInfo` of `komorebi-backend` as the carrier) is not clean yet: the macOS and Windows `komorebi_client` `State` are different types, the bar subscribes to the daemons with different notification shapes, and I cannot test either subscription here. Not attempted. |
| `widgets/network.rs` | 598 / 578 | 82 (19) | Windows reads `default_interface_options` (friendly name, IPv4, IPv6), macOS does not (the config type is shared and macOS logs a warning); `std::sync::Mutex` (poisoning handled) vs `parking_lot`; `netdev` 0.46 vs 0.40 and a different interface match key (friendly name vs `name`); `cmd.exe /C ncpa` vs `open x-apple...`. Functional on macOS: BEHAVIOUR, needs a Mac. Known difference, left as is. |
| `bar.rs` | 1,399 / 1,105 | 444 (35) | Windows: PowerShell session (`exec_powershell`, behind the hook), `hwnd`, virtual-desktop restore/minimize, `position_bar` through `komorebi_client::Window`, CJK font fallbacks, `scale_factor`, the Windows mouse handling. macOS: unboxed notification type, window shadow and level. Not behaviour-preserving without a screen. |
| `main.rs` | 402 / 388 | 92 (14) | `process_hwnd`/`enum_window`, DPI awareness, `set_read_timeout`; macOS activation policy and window level (AppKit), config dir `~/.config/komorebi`. Window and viewport setup belongs behind `BarPlatform` hooks (`init_process`, `viewport_builder`, `after_creation`), not written: it cannot be exercised here. |
| `os.rs` | 83 / 60 | 71 (14) | the platform implementations themselves; stays per platform (`cfg(target_os)` modules after the merge). |
| `widgets/widget.rs`, `widgets/mod.rs` | 41 / 49, 22 / 19 | 20, 5 | `WidgetConfigExt` (macOS builds a no-op `Unsupported` for the three Windows-only widgets) and the module lists. |
Windows-only and not shared: `widgets/keyboard.rs` (167), `media.rs` (322), `systray.rs` (1,205);
their config types are in the core. The macOS copies of keyboard and media were dead and are deleted.
### Known macOS/Windows differences that were kept (D-coord-1)
- `network.rs`: see above. The macOS bar ignores `default_interface_options`.
- Windows-only config keys are accepted on macOS and skipped with a logged warning (BEHAVIOUR on
macOS, as for `StaticConfig`): `Keyboard`, `Media` and `Systray` widgets
(`widgets::widget::warn_unsupported`), the `mouse` options and the network
`default_interface_options`. Before, the first group failed to deserialize and rejected the whole
config, the other two were ignored silently.
- macOS commands that spawn programs that do not exist there are preserved, not fixed:
`SystemTool::PowerSettings` (battery click) spawns `cmd.exe /C start ms-settings:...`,
`SystemTool::Url` (update click) spawns `explorer.exe`, and `UserCommand::launch_if_ready` of the
applications widget spawns `cmd /C <command>` in both bars. `update.rs` still has a second GitHub
API URL for the Windows repository. The macOS spawns live in
`komorebi-for-mac/komorebi-bar/src/os.rs`; the storage click is `SystemTool::StorageLocation` and
each shell spawns what it spawned before.
- `KomorebiLayout` is `#[serde(untagged)]` in both bars now (the macOS source converged; the macOS
schema view restores the macOS text). Its `Deserialize` is hand-written and nothing in the bar
serializes it, so this changes only the derived schema and `Serialize`. The coordinator said to
leave it alone; it is a separate revision (`mac-bar: converge komorebi.rs config text and
komorebi_layout.rs`) and can be dropped, at the price of keeping `KomorebiLayout` and its
widget in the shells.
## The merged package layout (for the coordinator's workspace merge)
Not done and not prepared in the tree (no workspace or lockfile structure was changed). When the
root workspace and `komorebi-for-mac` are merged the bar should look like this:
- Package `komorebi-bar` (one, in `komorebi-bar/`) plus `crates/komorebi-bar-core` as today.
Version: the workspace version. The macOS tree's `komorebi-bar` 0.1.1 and the `v0.1.0` text in
its schema description are a view concern (`schema/changes.rs`), not a package one.
- `src/main.rs` (shared: the argument parsing, config read, `--schema`, the event loop shell)
and `src/bar.rs` split into `src/bar/mod.rs` (shared `Komobar`) with
`#[cfg(target_os = "windows")] mod windows;` and `#[cfg(target_os = "macos")] mod macos;`
for the window handle, DPI, activation policy, window level, position and the PowerShell
session. `src/os.rs` becomes `src/os/{windows,macos}.rs` (the `BarPlatform` implementations,
selected by `cfg(target_os)` and installed first thing in `main`). Windows-only widgets
(`keyboard`, `media`, `systray`) are `#[cfg(target_os = "windows")]` modules; `komorebi.rs` and
`network.rs` stay two files (`komorebi/{windows,macos}.rs` behind one `widgets::komorebi`
facade) until the snapshot split.
- `Cargo.toml`: `windows`, `windows-core`, `windows-icons*`, `systray-util`, `font-loader` under
`[target.'cfg(windows)'.dependencies]`; `objc2`, `objc2-app-kit`, `raw-window-handle`, `icns`
under `[target.'cfg(target_os = "macos")'.dependencies]`; `netdev` has two versions (0.46,
0.40): pick one when `network.rs` converges, until then a target-specific rename
(`netdev-win`, `netdev-mac`). `komorebi-client`: one crate (the two differ in 150 lines).
Features: `schemars` as now (it forwards to the core). No new feature per platform: the
platform is `cfg(target_os)`.
- Schema: the checked-in files stay (`schema.bar.json`, `komorebi-for-mac/schema.bar.json`, or one
directory for both after the merge). `--schema` prints the view of the running platform
(`SchemaPlatform` from `cfg(target_os)`); a `--schema-for <platform>` flag would let one host
regenerate both, as the Windows union is the Windows text. Windows' three
platform-dependent descriptions (protocol `PathBuf`, themes) need the Windows target to
regenerate, so the Windows file is only verifiable on Windows or with the harness.
- Lock files: the macOS `Cargo.lock` folds into the root one; the lines this stack added to both
(`image`, `komorebi-config`, `komorebi-layouts`, `komorebi-protocol`, `komorebi-themes`,
`tracing`, `lazy_static`, `color-eyre` for bar-core) are the same crates in both, so the merge
adds no new versions beyond `netdev` and `starship-battery`/`sysinfo` (already unified here).
- Nix: `komorebi-for-mac/nix/komorebi-bar-options.nix` is generated from the macOS file and does
not change; the two flakes' `extractBinary "komorebi-bar"` become one derivation per system.
## The plan, in order
1. Done: schema views and oracle; storage, time, applications, komorebi layout widget, icon
cache, all widget config types, `WidgetConfig`, `config.rs` moved; hooks for platform
spawns, the shell session and the komorebi client.
2. Not done, needs a screen or a Mac: `bar.rs`/`main.rs` window code behind `BarPlatform`
(`init_process`, `viewport_builder`, `after_creation`, position/DPI/activation), then
`Komobar<P>` and the shared `app` body into the core.
3. Not done, needs the snapshot split: `komorebi.rs`. It needs `State`-to-snapshot conversion
per platform and one renderer; the frame/margin difference must be decided by looking at both.
4. Decision for `network.rs`: converge on the Windows widget (macOS gains friendly-name,
IPv4/IPv6 and the `netdev` bump) or keep two.
5. Workspace merge (above), then the two shells collapse into one package.
6. The oracle then compares generated text with the files for both platforms.
## What to check by hand
Windows (coordinator, screenshot e2e): the bar starts; the storage, time, applications (icons
from executables), komorebi layout (click opens the options, choosing one changes the layout,
Monocle/Floating toggles, paused) and komorebi workspaces widgets render; a click on storage
opens Explorer on the drive; a config with `mouse` options and a `Systray`/`Keyboard`/`Media`
widget still works (this is the Windows path through the moved `config.rs`, `WidgetConfig` and
the `exec_shell_command` hook: a `Command` mouse message must still reach the PowerShell session);
`komorebi-bar --schema` equals `schema.bar.json`.
macOS (also in the section for `docs/dev/macos-handcheck.md`): the bar starts with your real
config; the time widget renders (format variants, `changing_icon`); clicking a storage widget
opens the mounted volume in Finder (`open <path>`); the komorebi layout widget click works as
before; a config that contains a `Keyboard`, `Media` or `Systray` widget, a `mouse` block, or
`default_interface_options` under `Network` starts the bar and logs one warning per item (before:
the widget case failed to load the config); `komorebi-bar --schema` equals
`komorebi-for-mac/schema.bar.json`; the battery and update clicks still fail the way they did
(preserved, one-function fix in `os.rs`).