diff --git a/.github/release-notes/v0.1.1-beta.16.1.md b/.github/release-notes/v0.1.1-beta.16.1.md new file mode 100644 index 0000000..0d42aec --- /dev/null +++ b/.github/release-notes/v0.1.1-beta.16.1.md @@ -0,0 +1,67 @@ +# 0.1.1-beta.16.1 + +Beta.16.1 fixes Bluetooth power and the raised date, restores tray and launcher +icons, and simplifies the update and tray count presentation. + +## Fixes + +- Bluetooth power uses Omarchy's power helper to turn a soft-blocked radio on + and to turn it off again (#68). Repeat clicks while a power request is pending + do not dispatch a reverse action; the display follows observed BlueZ state. +- The smaller date is raised beside the clock again, as before Beta.16. The + clock keeps its shared baseline. +- Show the update count beside the update icon instead of in a badge, and remove + the tray count badge. Both counts remain in the tooltips; vertical bars show + the update count only there. +- Restore application icons for pinned tray apps, with a fallback when no icon is available. +- Restore the selected launcher logo, including Omarchy, Shibumi, and Dragon, without symbol remapping. +- Render the OpenCode usage mark crisply at the size of the other provider icons, so its usage fill reads clearly. + +## Compatibility + +The 24-plugin suite and transactional installation model are unchanged. +Beta.16.1 retains Beta.16's pinned Omarchy and Omarchy Settings `4.0.4-1` +package and Omarchy `v4.0.4` source baselines with Quickshell `0.3.1-1`. +It does not update Omarchy. + +## Install and update + +For a new source installation, use the release tag: + +```bash +sudo pacman -S --needed python jq curl networkmanager power-profiles-daemon upower xdg-utils libnotify wl-clipboard ttf-material-symbols-variable ttf-jetbrains-mono-nerd-basic noto-fonts-cjk adwaita-fonts +git clone https://github.com/HANCORE-linux/Shibumi-Shell.git +cd Shibumi-Shell +git checkout v0.1.1-beta.16.1 +./scripts/shibumi-suite install --yes +``` + +Update an admitted Beta.16 installation from its existing checkout: + +```bash +git fetch --tags +git checkout v0.1.1-beta.16.1 +./scripts/shibumi-suite update --dry-run +./scripts/shibumi-suite update --yes +./scripts/shibumi-suite status +``` + +The exact Beta.16 tag and release-merge checkouts are admitted predecessors. +Older supported identities retain their exact revision and payload checks; +an arbitrary checkout with the same version is not admitted. Do not install +or update from `main`, or delete lifecycle state to bypass admission. See the +[installation and recovery guide](https://github.com/HANCORE-linux/Shibumi-Shell/blob/v0.1.1-beta.16.1/docs/install.md). +AUR publication remains deferred; use the source workflow above. + +## Downgrade warning + +**Downgrade:** Beta.15.4 and older cannot read V1 layouts with parked widgets +(`order.parked`). They reset the V1 order, roles and splits to defaults; other +settings and V2 stay. `uninstall --keep-settings` does not prevent this. Before +downgrading, back up `~/.config/omarchy/shell.json` and `~/.local/state/shibumi/`. +Details: [Backup before downgrade](https://github.com/HANCORE-linux/Shibumi-Shell/blob/v0.1.1-beta.16.1/docs/install.md#backup-before-downgrade). + +## Known limits + +- The power helper affects all Bluetooth radios, not only the displayed + adapter, and cannot clear a hardware rfkill block. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index d2a7a1f..accdfe9 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -947,22 +947,33 @@ Current Phase 2 foundation: `BluetoothBackendAdapter`. The adapter keeps native BlueZ device QObjects private and publishes detached primitive records carrying the device path, address, adapter identity, and monotonic device/adapter incarnations. Every - mutation resolves exactly one current entity immediately before dispatch; - stale, malformed, ambiguous, unavailable, or state-conflicting requests - return typed failures and dispatch nothing. Beta.13 deliberately retains one - `omarchy-bluetooth-device` helper path per device action and never invokes a - native device method in parallel. Because the 4.0.2 helper accepts only an - action/address pair, production fails closed with more than one adapter; the - residual replacement race after dispatch remains explicit Step-4B helper - debt. Callers inspect `.ok` explicitly. The - adapter also owns pending state and Bluetooth-audio handoff; no complete - Omarchy Bluetooth UI component is instantiated as a backend; + device mutation resolves exactly one current entity immediately before dispatch; + stale, malformed, ambiguous, unavailable, or state-conflicting device requests + return typed failures and dispatch nothing. Device actions retain one + `omarchy-bluetooth-device` helper path and never invoke a native device method + in parallel. Because the helper accepts only an action/address pair, device + actions fail closed with more than one production adapter; the residual + replacement race after dispatch remains explicit Step-4B helper debt. + Device callers inspect `.ok` explicitly. The adapter also owns pending state + and Bluetooth-audio handoff; no complete Omarchy Bluetooth UI component is + instantiated as a backend. Global radio power is a separate host action: + one argv-only detached `omarchy-bluetooth-power on|off` request applies + Omarchy's rfkill persistence policy to all Bluetooth radios. It can clear a + soft block, not a hardware block. Bar, panel, and IPC share this power path; + no direct `adapter.enabled` write or helper `toggle` is used. BlueZ's observed + `adapter.enabled` remains authoritative for the displayed state, not dispatch + success. Repeat clicks are ignored while the adapter-bound target is pending; + observing that target, adapter loss/replacement, a dispatch exception, or the + deadline clears it. Power-off stops owned discovery; - each output owns only its V1 Bluetooth presentation and lazy Shibumi device panel. The process-wide service leases discovery across open panels and owns one symmetric six-method `omarchy.bluetooth` IPC target. Presentation has no Bluetooth/PipeWire import, process, timer, or file watcher; the service facade - owns one bounded discovery-reconciliation timer and the adapter owns four - bounded lifecycle timers. While a requested Discovery start is still pending + owns one bounded discovery-reconciliation timer and the adapter owns five + bounded lifecycle timers, including a separate one-shot 20-second power-state + deadline. This clears pending state, not the detached helper process; late + helper effects remain possible. It is not a process timeout or cancellation. + While a requested Discovery start is still pending during teardown, at most one temporary 30-second retry/expiry timer per native adapter survives the facade. Top mapping, adapter/radio reactivity, discovery ownership/teardown, both backend load orders, and IPC diff --git a/CHANGELOG.md b/CHANGELOG.md index e772e80..5aca984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## [0.1.1-beta.16.1] - 2026-10-04 + +### Fixed + +- Turn Bluetooth on after an rfkill soft block, and off again, through + Omarchy's power helper (#68). Ignore repeat clicks while a power request + is pending instead of dispatching a reverse action. +- Raise the smaller date beside the clock again, as before Beta.16. +- Show the update count beside the update icon instead of in a badge, and remove + the tray count badge. Both counts remain in the tooltips; vertical bars show + the update count only there. +- Restore application icons for pinned tray apps, with a fallback when no icon is available. +- Restore the selected launcher logo, including Omarchy, Shibumi, and Dragon, without symbol remapping. +- Render the OpenCode usage mark crisply at the size of the other provider icons, so its usage fill reads clearly. + +### Changed + +- Admit the exact Beta.16 tag and merge checkouts as update predecessors, + retaining strict payload and lifecycle checks. + ## [0.1.1-beta.16] - 2026-10-02 ### Added diff --git a/README.md b/README.md index fce15be..4cc50ac 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ process.
[Documentation](docs/README.md) · [Release status](docs/release-readiness.md) -`0.1.1-beta.16` is published as a GitHub prerelease. AUR publication is +`0.1.1-beta.16.1` is published as a GitHub prerelease. AUR publication is deferred; use the source installation below. See the -[release notes](.github/release-notes/v0.1.1-beta.16.md) for changes and the +[release notes](.github/release-notes/v0.1.1-beta.16.1.md) for changes and the downgrade warning. The current pinned host contracts use Omarchy and Omarchy Settings `4.0.4-1` (source tag `v4.0.4`, revision `c668141e`) with Quickshell `0.3.1-1`; the @@ -82,19 +82,21 @@ Install from source using the release tag: sudo pacman -S --needed python jq curl networkmanager power-profiles-daemon upower xdg-utils libnotify wl-clipboard ttf-material-symbols-variable ttf-jetbrains-mono-nerd-basic noto-fonts-cjk adwaita-fonts git clone https://github.com/HANCORE-linux/Shibumi-Shell.git cd Shibumi-Shell -git checkout v0.1.1-beta.16 +git checkout v0.1.1-beta.16.1 ./scripts/shibumi-suite install --yes ``` -Update an admitted source installation, including Beta.15.4, to Beta.16. +Update an admitted Beta.16 source installation to Beta.16.1. Exact predecessor identities, not a version range, govern admission; see the [update guide](docs/install.md#update). ```sh cd Shibumi-Shell git fetch --tags -git checkout v0.1.1-beta.16 +git checkout v0.1.1-beta.16.1 +./scripts/shibumi-suite update --dry-run ./scripts/shibumi-suite update --yes +./scripts/shibumi-suite status ``` Do not install or update from `main`. If you previously installed from `main`, run `./scripts/shibumi-suite uninstall --keep-settings --yes` from that exact checkout before switching to a tag. diff --git a/VERSION b/VERSION index 9ae57e5..a607da6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.1-beta.16 +0.1.1-beta.16.1 diff --git a/contracts/backend-boundary-v1.json b/contracts/backend-boundary-v1.json index 7e5a4f1..9de84f3 100644 --- a/contracts/backend-boundary-v1.json +++ b/contracts/backend-boundary-v1.json @@ -395,6 +395,7 @@ {"value":"omarchy-audio-output-sink","count":1,"category":"omarchyBackend","owner":"hancore.shibumi.audio","capability":"audio","backend":"omarchy.audio","removalStep":"Step 4A","reason":"Resolve the effective physical output before native volume and mute mutations."}, {"value":"omarchy-battery-status","count":1,"category":"omarchyBackend","owner":"hancore.shibumi.power-state","capability":"battery-ac","backend":"omarchy.power","removalStep":"Step 6","reason":"Remove the Omarchy battery helper after UPower is authoritative."}, {"value":"omarchy-bluetooth-device","count":1,"category":"omarchyBackend","owner":"hancore.shibumi.bluetooth","capability":"bluetooth","backend":"omarchy.bluetooth","removalStep":"Step 4B","reason":"Beta.13 resolves the current device and adapter incarnation immediately before this single helper dispatch and rejects a multi-adapter production inventory because the Omarchy 4.0.2 helper carries only action/address, not controller path; closing the residual post-dispatch helper race remains a separately scoped native-action cutover."}, + {"value":"omarchy-bluetooth-power","count":1,"category":"omarchyAction","owner":"hancore.shibumi.bluetooth","capability":"bluetooth","reason":"Explicit global radio power requests use the host rfkill persistence policy through one argv-only detached action; BlueZ remains the native state source."}, {"value":"omarchy-capture-screenrecording","count":1,"category":"omarchyBackend","owner":"hancore.shibumi.status","capability":"recording-voxtype","backend":"omarchy.recording","removalStep":"Step 8","reason":"Move recording actions behind the typed Shibumi status service."}, {"value":"omarchy-cmd-missing","count":1,"category":"hostIntegration","owner":"quattro.host","capability":"bar-host-routing"}, {"value":"omarchy-cmd-present","count":4,"category":"hostIntegration","owner":"quattro.host","capability":"bar-host-routing"}, diff --git a/contracts/lifecycle-predecessors-v1.json b/contracts/lifecycle-predecessors-v1.json index f1c0637..d5942ff 100644 --- a/contracts/lifecycle-predecessors-v1.json +++ b/contracts/lifecycle-predecessors-v1.json @@ -751,9 +751,72 @@ ], "settingsStorageVersion": 1, "sourceRevisions": [ + "4840482ec0e06a2ebd478120833d4fae1f30889e", + "05548c8d9ffdf0d37962d6e423e2a0dc21b8cd91", "package:0.1.1-beta.16" ], "suiteVersion": "0.1.1-beta.16" + }, + { + "id": "public-beta.16.1", + "payloadDigest": "46f72fd02b00dbe34326dbbdf6e95556e7e35641a4d597c5281bc2beed94a974", + "pluginDigests": { + "hancore.shibumi.bar": "8bd1ac4ce7ac2eea83f83c1e2a1e87257d1db41e06ce93f17c88849e04939cb4", + "hancore.shibumi.state": "3aed3c7a3cb92afb66d5f12d7d5c773f1988d5f11c933da187f8b31c68179da8", + "hancore.shibumi.control-center": "730ab1517cf0fe52ec5409203bdeb03661f8440c50a6b8ba8baf6563a4f23ec8", + "hancore.shibumi.reactor": "347d5997e664581da56104a636af44d4675f37d311bb058b9624d8c9f6121322", + "hancore.shibumi.telemetry": "f52aff2aa784a11eab6edb12b5c3610986a01284f98844293e5cc8e1bb2963b0", + "hancore.shibumi.power-state": "f2f664b00436695dbd47edf9b8d7da46d0f4f7fbe740045023479ed707872a50", + "hancore.shibumi.workspaces": "de2a198c2a1fcbada535f046b91f37e5f5ca517ef4a0e3c6f570baf7930699c5", + "hancore.shibumi.update-center": "4f028d0c82cbe0d85c74799a77c12e7e26acc9bd4dcda6f7c14fc40a5f991a86", + "hancore.shibumi.status": "1f16174c157c70e6b05a7ff7c00339090a1d4c2d051a4bd389cde1de6e296fe0", + "hancore.shibumi.memory": "f6fbd0838180fac0740fd27d22f9fe1812528f45210e51fa9984f479f42da8f9", + "hancore.shibumi.cpu": "bb0d1df3404525a45c55fffaa2091a8340ae3d9536f7edab6e6bd56759f7999e", + "hancore.shibumi.audio": "20a3f204e0c55b6c9aff18d12e9627b91f3710596e3d1c2e00b08aaa0f3e3e4c", + "hancore.shibumi.ai": "38e41e443b34c888107bc64e654adef9982130067c52702660f3960450a50959", + "hancore.shibumi.center": "fe792ff696628b9b6ce324e0767d6ccabc6bca98a0529f9c2d7d2a58f36761a6", + "hancore.shibumi.media": "e67e51d2629520b6e7a4578940885b8ff2245798254af4f8ea39ce7b8d2d8c82", + "hancore.shibumi.quick-access": "4ce7d5575e758ddb3cec856672e0717f95dd494c5a4488f61e0269e534697502", + "hancore.shibumi.network": "9ee7d1725749da03e9480529bbfb22c557f6f7da534900c280f0cb031492a41d", + "hancore.shibumi.battery": "59f67a3358c5e419bb98e474a867b53630cf1ee0bcd61c44d59c5775d8c6933f", + "hancore.shibumi.brightness": "80ba42a406f9a24b675ed2daf83555e7f1da6e0442246ae093bb7800be1a8b91", + "hancore.shibumi.power-profile": "6d71528dc88132dfc2c33b22437427c9210df74cdc123dfcdd574b4a3a07113e", + "hancore.shibumi.bluetooth": "f61a18cd1a47be8c820b731ddb9156c6311c02dcafe46533e798f5adba147fbb", + "hancore.shibumi.temperature": "72ac530bd97cf092349351a6c63e2ef78a9446053e7add71cfe72282704a76b4", + "hancore.shibumi.gpu": "a460b94a8f0142ce6da9318493a6343f7bdefcbd1999db3fe73c46d5ff59fa9e", + "hancore.shibumi.storage": "eb97c939b77fc88a17066bfa72ea4a1f9a0c7279d164afbc15b176f767837299" + }, + "pluginIds": [ + "hancore.shibumi.bar", + "hancore.shibumi.state", + "hancore.shibumi.control-center", + "hancore.shibumi.reactor", + "hancore.shibumi.telemetry", + "hancore.shibumi.power-state", + "hancore.shibumi.workspaces", + "hancore.shibumi.update-center", + "hancore.shibumi.status", + "hancore.shibumi.memory", + "hancore.shibumi.cpu", + "hancore.shibumi.audio", + "hancore.shibumi.ai", + "hancore.shibumi.center", + "hancore.shibumi.media", + "hancore.shibumi.quick-access", + "hancore.shibumi.network", + "hancore.shibumi.battery", + "hancore.shibumi.brightness", + "hancore.shibumi.power-profile", + "hancore.shibumi.bluetooth", + "hancore.shibumi.temperature", + "hancore.shibumi.gpu", + "hancore.shibumi.storage" + ], + "settingsStorageVersion": 1, + "sourceRevisions": [ + "package:0.1.1-beta.16.1" + ], + "suiteVersion": "0.1.1-beta.16.1" } ] } diff --git a/contracts/package-runtime-v1.json b/contracts/package-runtime-v1.json index 7870d58..c5bf2a8 100644 --- a/contracts/package-runtime-v1.json +++ b/contracts/package-runtime-v1.json @@ -37,6 +37,7 @@ "wl-copy": "wl-clipboard" }, "requiredHostCommands": { + "omarchy-bluetooth-power": "Bluetooth radio power and persistent rfkill policy", "omarchy-bluetooth-device": "Retained Beta.13 Bluetooth device mutation path", "omarchy-audio-output-set-default": "Retained Bluetooth audio-route compatibility path" }, diff --git a/contracts/plugin-suite-v1.json b/contracts/plugin-suite-v1.json index 1cca875..5c6d7b6 100644 --- a/contracts/plugin-suite-v1.json +++ b/contracts/plugin-suite-v1.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "suiteId": "hancore.shibumi", - "suiteVersion": "0.1.1-beta.16", + "suiteVersion": "0.1.1-beta.16.1", "settingsStorageVersion": 1, "hostContract": "contracts/host-facade-v1.json", "retiredPlugins": [ diff --git a/contracts/v2-feature-evidence.json b/contracts/v2-feature-evidence.json index ac78e92..d056917 100644 --- a/contracts/v2-feature-evidence.json +++ b/contracts/v2-feature-evidence.json @@ -34,7 +34,7 @@ {"id":"tray.drawer","implementation":["hancore.shibumi.status/TrayDrawerPanel.qml"],"tests":["tests/status-plugin-smoke.qml","tests/status-plugin-regression.sh"]}, {"id":"tray.item-menu","implementation":["hancore.shibumi.status/TrayAppMenuPanel.qml"],"tests":["tests/tray-app-menu-smoke.qml","tests/status-plugin-regression.sh"]}, {"id":"launcher.logo-variants","implementation":["hancore.shibumi.state/ShibumiConfig.js","hancore.shibumi.control-center/BarWidget.qml"],"tests":["tests/shibumi-config-regression.qml","tests/control-center-smoke.qml"]}, - {"id":"updates.badge-controls","implementation":["hancore.shibumi.update-center/UpdateCenterPanel.qml"],"tests":["tests/update-center-ui-smoke.qml","tests/update-center-regression.sh"]}, + {"id":"updates.badge-controls","implementation":["hancore.shibumi.update-center/UpdateCenterPanel.qml","hancore.shibumi.update-center/BarWidget.qml"],"tests":["tests/update-center-ui-smoke.qml","tests/update-center-regression.sh"]}, {"id":"updates.shell-lifecycle","implementation":["scripts/shibumi_suite/runtime.py"],"tests":["tests/shibumi-suite-quattro-runtime.sh","tests/test_shibumi_suite.py"]}, {"id":"control-center.v2-capabilities","implementation":["hancore.shibumi.control-center/ControlSettings.qml","hancore.shibumi.control-center/BarFunctionsPage.qml"],"tests":["tests/control-center-smoke.qml","tests/control-center-regression.sh"]}, {"id":"output.lifecycle-recovery","implementation":["hancore.shibumi.bar/core/WindowRecovery.qml","hancore.shibumi.bar/core/BarPanel.qml"],"tests":["tests/bar-host-registry-smoke.qml","tests/contract-regression.sh"]}, diff --git a/contracts/v2-feature-port.json b/contracts/v2-feature-port.json index 254b9a7..1ee864b 100644 --- a/contracts/v2-feature-port.json +++ b/contracts/v2-feature-port.json @@ -40,7 +40,7 @@ {"id":"tray.drawer","decision":"port","owner":"hancore.shibumi.status","strategy":"official-tray-owner-presentation","phase":4}, {"id":"tray.item-menu","decision":"port","owner":"hancore.shibumi.status","strategy":"official-tray-owner-presentation","phase":4}, {"id":"launcher.logo-variants","decision":"port","owner":"hancore.shibumi.control-center","strategy":"presentation","phase":5}, - {"id":"updates.badge-controls","decision":"port","owner":"hancore.shibumi.update-center","strategy":"official-update-actions","phase":4}, + {"id":"updates.badge-controls","decision":"port","owner":"hancore.shibumi.update-center","strategy":"bar-count-controls","phase":4}, {"id":"updates.shell-lifecycle","decision":"port","owner":"shibumi-suite","strategy":"transactional-source-update","phase":4}, {"id":"control-center.v2-capabilities","decision":"port","owner":"hancore.shibumi.control-center","strategy":"user-directed-redesign","phase":5}, {"id":"output.lifecycle-recovery","decision":"port","owner":"hancore.shibumi.bar","strategy":"native-quattro-lifecycle","phase":6}, diff --git a/docs/architecture/quattro-compatibility.md b/docs/architecture/quattro-compatibility.md index 73f202e..257f559 100644 --- a/docs/architecture/quattro-compatibility.md +++ b/docs/architecture/quattro-compatibility.md @@ -8,7 +8,7 @@ by this evidence. ## Current pinned host -Beta.16 targets the exact official Omarchy 4.0.4 baseline. These pins are not +Beta.16.1 retains Beta.16's exact official Omarchy 4.0.4 baseline. These pins are not physical release acceptance. The historical Beta.13 physical host acceptance below applies to its recorded single-output Omarchy 4.0.3 setup: diff --git a/docs/development/packaging.md b/docs/development/packaging.md index 01b5181..2fcd71b 100644 --- a/docs/development/packaging.md +++ b/docs/development/packaging.md @@ -1,6 +1,6 @@ # Arch packaging and AUR publication -Status: `0.1.1-beta.16` is published as a GitHub prerelease; AUR publication +Status: `0.1.1-beta.16.1` is published as a GitHub prerelease; AUR publication is deferred. Shibumi ships one versioned suite containing 24 separately validated Omarchy @@ -74,7 +74,7 @@ and lifecycle code before the user-level update runs, so Beta.12's storage guard cannot reject that package afterward. There is no reverse migration to legacy storage. -The additional [Beta.16 downgrade warning and backup requirements](../install.md#backup-before-downgrade) +The additional [Beta.16.1 downgrade warning and backup requirements](../install.md#backup-before-downgrade) apply even to readers using canonical State storage: Beta.15.4 and older cannot preserve a V1 layout containing `order.parked`. `--keep-settings` does not prevent this. Restore the settings with a matching payload. @@ -136,10 +136,11 @@ result. It requires: `--nodeps` is used only by this controlled rehearsal because dependency resolution is verified separately against the validation system. The package -continues to depend on Omarchy's `omarchy-bluetooth-device` and -`omarchy-audio-output-set-default` helpers for the Beta.12 hotfix; those are -explicit compatibility debt, not undeclared native replacements. A clean-chroot -build must perform normal dependency resolution before publication. +requires Omarchy's `omarchy-bluetooth-device`, `omarchy-bluetooth-power`, and +`omarchy-audio-output-set-default` helpers. Device/audio mutation paths retain +their declared compatibility boundaries; global Bluetooth power delegates +rfkill persistence to the host while BlueZ remains authoritative for state. +A clean-chroot build must perform normal dependency resolution before publication. ## Publication gates diff --git a/docs/install.md b/docs/install.md index c27cef7..64f4fc0 100644 --- a/docs/install.md +++ b/docs/install.md @@ -6,7 +6,7 @@ Shibumi installs 24 independent plugin roots into Omarchy's normal plugin directory. The repository root is a suite source, not a single installable Omarchy plugin. -`0.1.1-beta.16` is published as a GitHub prerelease. AUR publication is +`0.1.1-beta.16.1` is published as a GitHub prerelease. AUR publication is deferred; install from the release tag using the source workflow below. Do not install or update from `main`. @@ -51,7 +51,7 @@ For an intentional non-interactive installation: sudo pacman -S --needed python jq curl networkmanager power-profiles-daemon upower xdg-utils libnotify wl-clipboard ttf-material-symbols-variable ttf-jetbrains-mono-nerd-basic noto-fonts-cjk adwaita-fonts git clone https://github.com/HANCORE-linux/Shibumi-Shell.git cd Shibumi-Shell -git checkout v0.1.1-beta.16 +git checkout v0.1.1-beta.16.1 ./scripts/shibumi-suite install --yes ``` @@ -66,7 +66,7 @@ To inspect the transaction before installing: ```bash git clone https://github.com/HANCORE-linux/Shibumi-Shell.git cd Shibumi-Shell -git checkout v0.1.1-beta.16 +git checkout v0.1.1-beta.16.1 ./scripts/shibumi-suite install --dry-run ./scripts/shibumi-suite install ``` @@ -146,15 +146,17 @@ Pacman updating `/usr/share/shibumi-shell` does not silently change a running desktop. The explicit update validates, stages, reloads, and verifies all 24 plugins as one transaction. -For an admitted trusted source checkout, including the published Beta.15.4 -tag, its release merge, and its documentation merge: +To update an admitted Beta.16 source installation to Beta.16.1, use its +trusted checkout. The exact published Beta.16 tag and release merge are +admitted predecessors: ```bash cd Shibumi-Shell git fetch --tags -git checkout v0.1.1-beta.16 +git checkout v0.1.1-beta.16.1 ./scripts/shibumi-suite update --dry-run -./scripts/shibumi-suite update +./scripts/shibumi-suite update --yes +./scripts/shibumi-suite status ``` An update requires a suite-managed Shibumi installation. It stages all 24 @@ -204,7 +206,7 @@ authoritative origin. The old checkout is not deleted or modified. ### Roll back a package version > [!WARNING] -> **Beta.16 downgrade warning.** +> **Beta.16.1 downgrade warning.** > Beta.15.4 and older cannot read V1 layouts containing > `order.parked`. They reset V1 order, roles, and splits to defaults; other > settings and V2 remain. `uninstall --keep-settings` does not prevent this. @@ -404,7 +406,7 @@ then install the published tag. For example, only if the recorded revision is ```bash git checkout e92d8ee1ce9821d0b203a9d4d5dd9932d2e460fb ./scripts/shibumi-suite uninstall --keep-settings --yes -git checkout v0.1.1-beta.16 +git checkout v0.1.1-beta.16.1 ./scripts/shibumi-suite install --yes ``` diff --git a/docs/plugins/README.md b/docs/plugins/README.md index 5e5b302..173fd50 100644 --- a/docs/plugins/README.md +++ b/docs/plugins/README.md @@ -2,7 +2,7 @@ Status: reference -Shibumi `0.1.1-beta.16` contains 24 independently registered Omarchy plugins. The +Shibumi `0.1.1-beta.16.1` contains 24 independently registered Omarchy plugins. The default suite profile installs all of them as one verified transaction. The authoritative inventory is @@ -52,9 +52,19 @@ required device or service is unavailable. Shibumi uses native Quattro owners where they provide sufficient state and actions, including media, notifications, tray, background, monitor, weather, idle, and system-update services. Shibumi owns native Audio and Network state. -Its Bluetooth adapter keeps BlueZ QObjects private, publishes detached -identity-bound records, and retains one declared Omarchy helper mutation path -per device action for Beta.14. +Its Bluetooth adapter keeps BlueZ QObjects private and publishes detached +identity-bound records. Device actions retain one `omarchy-bluetooth-device` +helper path with exact device-identity checks. Global radio power instead uses +one explicit `omarchy-bluetooth-power on|off` request for all Bluetooth radios; +it follows the host's rfkill persistence policy and cannot clear a hardware +block. BlueZ remains authoritative for the displayed radio state. Bar, panel, +and IPC share the same adapter-bound pending target and ignore repeat clicks. + +The Bluetooth adapter owns five bounded lifecycle timers. Its separate +one-shot 20-second power-state deadline clears pending state; it neither stops +the detached helper nor prevents later effects. The service facade retains +one discovery-reconciliation timer and the bounded native-adapter discovery +teardown guard. See the [architecture contract](../../ARCHITECTURE.md). Shibumi owns its presentation and adds a narrow adapter only where the host contract cannot produce the approved behavior. It must not create a duplicate diff --git a/docs/release-readiness.md b/docs/release-readiness.md index de41823..c8ece07 100644 --- a/docs/release-readiness.md +++ b/docs/release-readiness.md @@ -1,45 +1,54 @@ -# Shibumi 0.1.1-beta.16 release readiness +# Shibumi 0.1.1-beta.16.1 release readiness > **Document status: Revision-bound validation record.** This page separates > recorded product evidence from release, package, and physical acceptance. > It cannot override [`../ARCHITECTURE.md`](../ARCHITECTURE.md). -## Release and published predecessor - -`0.1.1-beta.16` is published as a GitHub prerelease. The validated product -revision recorded here is `30746f56f61f08e7cf665e7d1fc894711b563fa2`, with -24 plugins and suite payload digest -`f08b4ec613f248fda62d7dec9865ea46a846fa2e91b2723a186adc0ab0bffc41`. -This product record does not substitute for the exact release commit's archive, -checksum, package, or complete release-evidence records. - -Beta.15.4 was published on 2026-09-28 as a GitHub prerelease from -`6594b989c9f5edfe557669b0413a9bd3e50f8e81`. It is the frozen rolling +## Candidate and published predecessor + +The Beta.16.1 product, expectation, and payload-pin revision is +`9f1fc90c4f9414286969cb039445bb91301701b6` (tree +`0660d7e2cb97c2f8286423d356550c880ef7c6f9`), with 24 plugins and suite payload +digest `46f72fd02b00dbe34326dbbdf6e95556e7e35641a4d597c5281bc2beed94a974`. +The complete source contract and isolated Quattro gate below bind exactly that +revision. This is a pre-checksum readiness snapshot, not final release evidence. +Subsequent readiness, changelog-date, and AUR checksum commits do not change the +plugin-only suite payload, but shipped documentation changes the release archive. +Source review, the complete 14-gate collector, and a byte-identical clean-commit +rebuild must bind the final combined release revision before publication. + +Beta.16 was published on 2026-10-02 as a GitHub prerelease from +`4840482ec0e06a2ebd478120833d4fae1f30889e`. It is the frozen rolling predecessor in [`release-predecessor.json`](../tests/fixtures/release-predecessor.json). -Exact tag, merge, and documentation-merge checkout identities are admitted by -[`lifecycle-predecessors-v1.json`](../contracts/lifecycle-predecessors-v1.json). -A version string alone never authorizes recovery or replacement. AUR publication -remains deferred. +That exact tag checkout and release merge +`05548c8d9ffdf0d37962d6e423e2a0dc21b8cd91` are admitted by +[`lifecycle-predecessors-v1.json`](../contracts/lifecycle-predecessors-v1.json), +with the unchanged Beta.16 payload digest +`f08b4ec613f248fda62d7dec9865ea46a846fa2e91b2723a186adc0ab0bffc41`. +Older exact identities remain admitted; a version string alone never authorizes +recovery or replacement. AUR publication remains deferred. -## Evidence at the recorded product revision +## Recorded evidence and outstanding gates | Gate | Evidence and boundary | | --- | --- | -| Complete source contract | Passed on `30746f5`, exit 0; 638.65 seconds | -| Isolated Quattro lifecycle | Passed on `30746f5`, exit 0; 32/32 shell generations; 109.68 seconds | -| Retained runtime paths | Historical Beta.13 → Beta.14.1 package arm; fresh checkout; Beta.15, Beta.15.2, and rolling Beta.15.4 checkout updates | -| Settings round trips | Isolated keep-settings/reinstall retained the complete State entry and host layout, including delayed follow-up | -| Live installations | Reviewer reports `30746f5` installed on two maintainer machines by keep-settings round trip, identical settings and no Shibumi warnings in the new shell logs | -| Scoped visual previews | Reviewed V1/V2 badge, text, bell, battery, and palette comparisons; software-rendered previews do not establish physical acceptance | -| Scoped maintainer live check | Maintainer confirms the battery, charging bolt, and palette; this is not blanket acceptance of all visuals or hardware workflows | - -The contract/runtime passes above are not a full 14-gate collector result. -The historical package arm is not a Beta.16 package-upgrade proof. Private -Wayland/software-rendered previews are not live GPU, layer-shell, hotplug, or -physical multi-output acceptance. The recorded contract run retained six known -PillSurface teardown TypeErrors. Its 590 warning/error lines in 154 normalized -classes matched the preceding product run, as did the runtime warning/error -multiset; these are not warning-free fixture logs. +| Complete source contract | Passed on `9f1fc90`, exit 0 in 637.634 seconds, complete marker, no retry or timeout. Fresh exact-HEAD source; private staged validation passed separately. | +| Isolated Quattro lifecycle | Passed on the same exact HEAD, exit 0, 32/32 generations in 108.747 seconds; no retry or timeout. | +| Retained runtime paths | Historical Beta.13 → Beta.14.1 package arm (5), fresh checkout (6), Beta.15 (7), Beta.15.2 (7), and rolling frozen Beta.16 → candidate (7). Payload, layout, keep-settings, reinstall, and delayed readbacks passed; owned fixtures were cleaned. | +| Status spacing | Private same-render V1/V2 fit × DPR 1/1.6 × update 13/0 × drawer absent/present: all 16 outer-gap cases match `69ea4da`; inner gaps and count spacing retain `deeca9b`. Neighbors move by whole physical pixels. Bell glyph and point RGBA are exactly translated in all 42 main and supplementary comparisons, including G3 in the V1 center at DPR 1.6. | +| Bluetooth and Update input | `deeca9b`'s any-controller power/display/completion fix and whole-widget Update button are byte-identical. Their controlled QML checks are not physical multi-controller, rfkill, or desktop input acceptance. | +| Diagnostics | Retained fixture diagnostics match the accepted reference, including the known VisualTokens color-assignment warning and six PillSurface teardown TypeErrors; not warning-free. No new diagnostic class or native type/reference/binding error was accepted. | +| Live installation and hardware | Separate reviewer live status-spacing measurement remains outside these private fixtures. Multi-controller/rfkill, persistence, real focus/menu/input, physical multi-output and 1366px acceptance are not supplied here. | +| Release/package verification | At this pre-checksum snapshot, dated changelog, two reproducible archive builds, AUR metadata/checksum commit, clean-HEAD source review and rehearsal, final 14-gate collector, matching rebuild, and publication verification remain pending. Final external evidence must identify their exact combined commit. | + +The historical package arm is not a Beta.16.1 installed-package upgrade proof. +Private Wayland/Mesa captures are not live GPU, layer-shell, hotplug, or physical +multi-output acceptance. The private V2/DPR1 separator reference is 24/19 free +columns, not the reviewer's 24/20 live reference; equality is claimed only within +the same controlled render, including the notification point and halo in the +last painted pixel. V1 island backgrounds change as their outer margins +are restored: exact bell paint does not mean exact complete slot backgrounds or +whole-bar RGBA. Earlier release passes do not transfer to this candidate. ## Pinned host contracts @@ -100,9 +109,20 @@ was performed. Each operation needs its own maintainer Go. - Local whole-ink, workspace/Pacman, meter, Calendar overhang, and charge-bolt raster limits are not declared solved by the ordinary-text baseline work. - The pinned host exposes Recent notification history, not Live popup access. +- Bluetooth power affects all radios through the host helper and cannot clear + a hardware block. The 20-second pending deadline does not stop the detached + process or prevent later effects. Real rfkill and persistence acceptance is + separate from controlled native-QML feedback. +- The horizontal update count shares the bar-value baseline and CPU ink spacing. + Its leading reservation grows on an output-scale grid. Status inner-spacing + savings are returned to the group width; centered Fit preserves whole-physical- + pixel movements relative to the previous raster phase. Complete other-widget + rectangles are not all RGBA-identical after translation, and the restored V1 + island edges change slot backgrounds. Exact whole-bar pixel identity and + physical acceptance are not claimed. - **Downgrade:** Beta.15.4 and older cannot read V1 `order.parked`; they reset V1 order, roles, and splits while retaining other settings and V2. - `--keep-settings` does not prevent this. The [release notes](../.github/release-notes/v0.1.1-beta.16.md) + `--keep-settings` does not prevent this. The [release notes](../.github/release-notes/v0.1.1-beta.16.1.md) and [backup guide](install.md#backup-before-downgrade) identify the complete settings and recovery evidence to preserve. Restore the settings with a matching payload. @@ -124,6 +144,9 @@ block updates and deletion. See [packaging](development/packaging.md). Beta.13's single-output Omarchy 4.0.3 acceptance remains historical in [Get started](getting-started.md#know-the-beta-boundary). The paused [Beta.14 validation record](development/beta14-validation.md) is not a current -release checklist. Beta.15.4's published changes and limits are recorded in its -[release notes](../.github/release-notes/v0.1.1-beta.15.4.md). Earlier readiness -snapshots remain in Git history; none confers acceptance on Beta.16. +release checklist. Beta.16's published changes and limits remain in its +[release notes](../.github/release-notes/v0.1.1-beta.16.md). Its earlier product +revision `30746f5` passed the complete contract and 32-generation runtime; +those fixtures retained known teardown warnings and did not establish blanket +hardware acceptance. Earlier readiness snapshots remain in Git history; none +confers acceptance on Beta.16.1. diff --git a/docs/v1-parity-matrix.md b/docs/v1-parity-matrix.md index 4421532..d8e61f9 100644 --- a/docs/v1-parity-matrix.md +++ b/docs/v1-parity-matrix.md @@ -52,7 +52,7 @@ Status values: | Themes/wallpapers | Discover, preview and apply supported sources | Omarchy theme APIs plus current and user background roots through one shared scanner | Partial; discovery and previews pass on the validation system, while apply and failure-path acceptance remain | | AI usage | Claude, Codex and OpenCode quota state and warnings | One process-wide V1 single-provider facade; lazy official Claude/Codex scanners and one cache-free OpenCode SQLite adapter | Partial; OpenCode runtime, legacy IPC, and Top/Bottom product-panel routing are accepted on the validation system; Claude/Codex account-data and physical multi-output gates remain | | Reactor | Approved modes, gating and event behavior | Backend-free Modes 1-6; one lazy root event/quote source for Modes 7-8; style owns only per-output rendering | Partial; all modes are implemented, Mode 0 is zero-work, and Modes 1-8 now have single-output the validation system visual evidence. Mode 7/8 lifecycle and bounded CPU are characterized; physical multi-output acceptance remains. | -| Package/theme updates | User-visible checks, package apply, theme review and apply | Independent G3 Update Center plugin with read-only package/theme scans and actions delegated to visible Omarchy-owned commands | Partial; the compact V1-style package and theme tabs load with real the validation system state, badge preferences, pinned theme review/update/reinstall/remove controls, and no copied shell updater. Real theme apply/failure, package apply, bottom placement, and multi-output remain release gates. | +| Package/theme updates | User-visible checks, package apply, theme review and apply | Independent G3 Update Center plugin with read-only package/theme scans and actions delegated to visible Omarchy-owned commands | Partial; the compact V1-style package and theme tabs load with real the validation system state, bar-count preferences, pinned theme review/update/reinstall/remove controls, and no copied shell updater. Real theme apply/failure, package apply, bottom placement, and multi-output remain release gates. | | Shell self-update | V1 pinned standalone updater and progress UI | Omarchy plugin install/update/rollback lifecycle | Adapt | | Install/uninstall | Standalone scripts and post-boot hooks | Transactional Shibumi suite installer stages independently validated Omarchy plugins; Omarchy owns runtime activation | Adapt | | User configuration | V1 cache/config behavior | Host-owned `shell.json` plus versioned Shibumi feature state | Adapt | diff --git a/hancore.shibumi.ai/BarWidget.qml b/hancore.shibumi.ai/BarWidget.qml index cce70c1..75155e7 100644 --- a/hancore.shibumi.ai/BarWidget.qml +++ b/hancore.shibumi.ai/BarWidget.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import QtQuick +import QtQuick.Effects import qs.Commons as Commons import qs.Ui as Ui import "../hancore.shibumi.state/lib/presentation" as Presentation @@ -56,9 +57,9 @@ Ui.Panel { readonly property int providerIconSlotWidth: 20 readonly property int providerIconSlotHeight: 16 readonly property int claudeGlyphPixelSize: 15 - readonly property int providerGlyphWidth: providerId === "opencode" ? 20 + readonly property int providerGlyphWidth: providerId === "opencode" ? 15 : providerId === "codex" ? 14 : 15 - readonly property int providerGlyphHeight: providerId === "opencode" ? 12 + readonly property int providerGlyphHeight: providerId === "opencode" ? 10 : providerId === "codex" ? 14 : 15 readonly property int providerGlyphHorizontalOffset: 0 readonly property int providerContentHorizontalOffset: @@ -209,10 +210,20 @@ Ui.Panel { } } + OpenCodeUsageIcon { + anchors.fill: parent + visible: root.providerId === "opencode" + baseColor: root.baseIconColor + fillColor: root.usageIconColor + baseOpacity: root.baseIconOpacity + usageFraction: root.steppedPercent > 0 + ? Math.min(1, Math.max(root.steppedPercent / 100, 0.22)) : 0 + } + Item { id: providerImage anchors.fill: parent - visible: root.providerId === "codex" || root.providerId === "opencode" + visible: root.providerId === "codex" Presentation.BarInk { id: imageInk; target: providerImage } transform: Translate { x: imageInk.enabled ? imageInk.snapX(0) : 0 @@ -274,6 +285,60 @@ Ui.Panel { } } + // The original pixel mark, rasterized at its native grid before nearest sampling. + component OpenCodeUsageIcon: Item { + id: iconRoot + required property color baseColor + required property color fillColor + required property real baseOpacity + required property real usageFraction + + Presentation.BarInk { id: placement; target: iconRoot } + Item { + id: paint + width: Math.round(iconRoot.width * placement.dpr) / placement.dpr + height: Math.round(width * placement.dpr * 14 / 22) / placement.dpr + x: placement.snapX((iconRoot.width - width) / 2) + y: placement.rectangleY(height) + + Image { + id: mark + anchors.fill: parent + visible: false + source: Qt.resolvedUrl("assets/opencode-mark.svg") + // Qt scales SVG requests by DPR before rasterizing them. + sourceSize: Qt.size(Math.max(1, Math.round(22 / placement.dpr)), + Math.max(1, Math.round(14 / placement.dpr))) + fillMode: Image.Stretch + smooth: false + mipmap: false + } + MultiEffect { + anchors.fill: parent + source: mark + colorization: 1 + colorizationColor: Qt.rgba(iconRoot.baseColor.r, iconRoot.baseColor.g, iconRoot.baseColor.b, 1) + opacity: iconRoot.baseColor.a * iconRoot.baseOpacity + } + Item { + anchors.bottom: parent.bottom + width: parent.width + height: Math.round(parent.height * iconRoot.usageFraction * placement.dpr) / placement.dpr + clip: true + Behavior on height { NumberAnimation { duration: 600; easing.type: Easing.OutCubic } } + MultiEffect { + anchors.bottom: parent.bottom + width: paint.width + height: paint.height + source: mark + colorization: 1 + colorizationColor: Qt.rgba(iconRoot.fillColor.r, iconRoot.fillColor.g, iconRoot.fillColor.b, 1) + opacity: iconRoot.fillColor.a + } + } + } + } + Loader { id: panelLoader } diff --git a/hancore.shibumi.ai/Service.qml b/hancore.shibumi.ai/Service.qml index 5c9969b..fcf145b 100644 --- a/hancore.shibumi.ai/Service.qml +++ b/hancore.shibumi.ai/Service.qml @@ -19,7 +19,7 @@ Item { SuiteRuntime.HostShell { id: suiteShell; host: root.shell } SuiteRuntime.Provider { pluginId: "hancore.shibumi.ai" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.ai/manifest.json b/hancore.shibumi.ai/manifest.json index c57dd0d..1ae0281 100644 --- a/hancore.shibumi.ai/manifest.json +++ b/hancore.shibumi.ai/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.ai", "name": "Shibumi AI Usage", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi Claude, Codex, and OpenCode usage presentation.", diff --git a/hancore.shibumi.audio/Service.qml b/hancore.shibumi.audio/Service.qml index 4fa3f3f..a9f559a 100644 --- a/hancore.shibumi.audio/Service.qml +++ b/hancore.shibumi.audio/Service.qml @@ -14,7 +14,7 @@ Item { SuiteRuntime.Provider { id: runtimeProvider pluginId: "hancore.shibumi.audio" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.audio/manifest.json b/hancore.shibumi.audio/manifest.json index 36f494e..9f1ae9d 100644 --- a/hancore.shibumi.audio/manifest.json +++ b/hancore.shibumi.audio/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.audio", "name": "Shibumi Audio", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi audio presentation over Omarchy's PipeWire owner.", diff --git a/hancore.shibumi.bar/Bar.qml b/hancore.shibumi.bar/Bar.qml index d864f38..971639e 100644 --- a/hancore.shibumi.bar/Bar.qml +++ b/hancore.shibumi.bar/Bar.qml @@ -27,7 +27,7 @@ Item { SuiteRuntime.Provider { id: barRuntimeProvider pluginId: "hancore.shibumi.bar" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: suiteHostShell.host manifest: root.manifest diff --git a/hancore.shibumi.bar/core/GroupSlot.qml b/hancore.shibumi.bar/core/GroupSlot.qml index 019211a..c901133 100644 --- a/hancore.shibumi.bar/core/GroupSlot.qml +++ b/hancore.shibumi.bar/core/GroupSlot.qml @@ -108,6 +108,8 @@ Item { // that same selection and can oscillate for width-reactive providers. readonly property bool hasLoadedWidgets: contentItem ? contentItem.hasLoadedWidgets === true : false + readonly property real spacingRecovery: contentItem && "spacingRecovery" in contentItem + ? contentItem.spacingRecovery : 0 readonly property bool hasContent: implicitWidth > 0.5 && implicitHeight > 0.5 readonly property real minimumResponsiveWidth: contentItem && "minimumResponsiveWidth" in contentItem @@ -226,6 +228,16 @@ Item { readonly property bool hasLoadedWidgets: root ? root.anyLoadedWidget(moduleRepeater) : false + readonly property real spacingRecovery: { + void(moduleRow.implicitWidth) + let total = 0 + for (let i = 0; i < moduleRepeater.count; i++) { + const slot = moduleRepeater.itemAt(i) + const item = slot && slot.activeItemVisible ? slot.activeItem : null + if (item && "spacingSavings" in item) total += item.spacingSavings + } + return total + } readonly property real minimumResponsiveWidth: { void(moduleRow.implicitWidth) var total = 0 diff --git a/hancore.shibumi.bar/manifest.json b/hancore.shibumi.bar/manifest.json index 4602856..88247d4 100644 --- a/hancore.shibumi.bar/manifest.json +++ b/hancore.shibumi.bar/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.bar", "name": "Shibumi Bar", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "The Shibumi full-bar host for Omarchy Shell.", diff --git a/hancore.shibumi.bar/styles/shibumi/BarSurface.qml b/hancore.shibumi.bar/styles/shibumi/BarSurface.qml index 19bd4ef..3c3c5fd 100644 --- a/hancore.shibumi.bar/styles/shibumi/BarSurface.qml +++ b/hancore.shibumi.bar/styles/shibumi/BarSurface.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import QtQuick +import QtQuick.Window import qs.Commons as Commons import "../../core" as Core import "../../core/RunGeometry.js" as RunGeometry @@ -104,8 +105,19 @@ Item { // Stage against the output capacity; shellWidth remains presentation. readonly property real responsiveCapacity: compactShell ? Math.max(0, width - 2 * frameInset) : shellWidth - readonly property real shellX: shibumiShell ? frameInset - : Math.round((width - shellWidth) / 2) + readonly property real spacingRecovery: leftGroups.spacingRecovery + + centerGroups.spacingRecovery + rightGroups.spacingRecovery + readonly property real pixelRatio: root.Window.window + ? root.Window.window.devicePixelRatio : 1 + readonly property real expandedShellWidth: compactShell + ? Math.min(width - 2 * frameInset, shellWidth + spacingRecovery) : shellWidth + readonly property real expandedShellX: shibumiShell ? frameInset + : Math.round((width - expandedShellWidth) / 2) + // Preserve the old raster phase; recover real width, not outer padding. + readonly property real shellX: compactShell && spacingRecovery > 0 + ? expandedShellX + Math.round((expandedShellWidth - shellWidth) + * pixelRatio / 2) / pixelRatio + : shibumiShell ? frameInset : Math.round((width - shellWidth) / 2) readonly property real shellContentInset: contentInset + (shellStyle === "notch" ? root.bar.visualTokens.shellWingWidth : 0) @@ -144,13 +156,27 @@ Item { readonly property real idealCenterX: Math.round((width - centerRegion.width) / 2) readonly property real minCenterX: Math.round(leftRegion.x + leftRegion.width + centerGap) readonly property real maxCenterX: Math.round(rightRegion.x - centerGap - centerRegion.width) - readonly property real centerTargetX: maxCenterX < minCenterX + readonly property real logicalCenterTargetX: maxCenterX < minCenterX // At an exact fit, pixel rounding can make the two legal limits cross // by one pixel. Falling back to the screen center then overlaps the // asymmetric right run (notably G8 with G9/MPRIS). Split the tiny // deficit between both sides instead and preserve the visible gaps. ? Math.round((minCenterX + maxCenterX) / 2) : Math.max(minCenterX, Math.min(idealCenterX, maxCenterX)) + readonly property real expandedCenterTargetX: { + const centerWidth = centerRegion.width + centerGroups.spacingRecovery + const ideal = Math.round((width - centerWidth) / 2) + const minimum = Math.round(expandedShellX + shellContentInset + + leftRegion.width + leftGroups.spacingRecovery + centerGap) + const maximum = Math.round(width - expandedShellX - shellContentInset + - rightRegion.width - rightGroups.spacingRecovery - centerGap - centerWidth) + return maximum < minimum ? Math.round((minimum + maximum) / 2) + : Math.max(minimum, Math.min(ideal, maximum)) + } + readonly property real centerTargetX: (shibumiShell || compactShell) && spacingRecovery > 0 + ? expandedCenterTargetX + Math.round((logicalCenterTargetX + - expandedCenterTargetX) * pixelRatio) / pixelRatio + : logicalCenterTargetX readonly property var runs: { void(leftGroups.groupGeometry) void(centerGroups.groupGeometry) diff --git a/hancore.shibumi.bar/styles/shibumi/GroupSection.qml b/hancore.shibumi.bar/styles/shibumi/GroupSection.qml index ba3e7d3..bd77f63 100644 --- a/hancore.shibumi.bar/styles/shibumi/GroupSection.qml +++ b/hancore.shibumi.bar/styles/shibumi/GroupSection.qml @@ -62,6 +62,8 @@ Item { readonly property var stateConfig: stateService && stateService.config ? stateService.config : ({}) readonly property var contentItem: content.item + readonly property real spacingRecovery: contentItem && "spacingRecovery" in contentItem + ? contentItem.spacingRecovery : 0 readonly property var groupGeometry: contentItem && contentItem.groupGeometry ? contentItem.groupGeometry : [] readonly property var separatorGeometry: contentItem @@ -393,6 +395,16 @@ Item { return Math.max(1, root.availableWidth - siblings) } + readonly property real spacingRecovery: { + if (!root) return 0 + void(root.groups) + let total = 0 + for (let i = 0; i < horizontalRepeater.count; i++) { + const cell = horizontalRepeater.itemAt(i) + if (cell && cell.contentShown) total += cell.spacingRecovery + } + return total + } readonly property var groupGeometry: { if (!root) return [] void(root.groups) @@ -534,6 +546,7 @@ Item { readonly property real minimumGroupWidth: placeholderSlot ? emptySlotTarget.width : groupHasContent ? groupSlot.minimumResponsiveWidth : measuredMinimumGroupWidth + readonly property real spacingRecovery: groupSlot.spacingRecovery readonly property real contentLeft: targetVisual.x readonly property real contentRight: targetVisual.x + targetVisual.width readonly property real visualRightEdge: groupSlot.x + groupSlot.width diff --git a/hancore.shibumi.battery/manifest.json b/hancore.shibumi.battery/manifest.json index 3944a0b..553c9c6 100644 --- a/hancore.shibumi.battery/manifest.json +++ b/hancore.shibumi.battery/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.battery", "name": "Shibumi Battery", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi battery status and detailed battery panel.", diff --git a/hancore.shibumi.bluetooth/BluetoothBackendAdapter.qml b/hancore.shibumi.bluetooth/BluetoothBackendAdapter.qml index e02ebd4..b737f02 100644 --- a/hancore.shibumi.bluetooth/BluetoothBackendAdapter.qml +++ b/hancore.shibumi.bluetooth/BluetoothBackendAdapter.qml @@ -18,6 +18,7 @@ Item { // Lets the component smoke test drive native device property transitions // without touching the host's real Bluetooth devices. property var nativeDevicesOverride: null + property var nativeAdaptersOverride: null property var pipewireNodesOverride: null property var commandRunnerOverride: null property var audioOutputOverride: null @@ -33,6 +34,8 @@ Item { property var retiredDiscoveryAdapter: null property bool discoveryDesired: false property var nativePendingActions: ({}) + property var pendingPowerTarget: null + property var pendingPowerAdapter: null // Raw native entities remain private here. The registry assigns a monotonic // incarnation whenever a device or adapter QObject is replaced. property var nativeAdapterEntity: null @@ -49,9 +52,17 @@ Item { readonly property var adapter: backendOverride !== null ? ("adapter" in backendOverride ? backendOverride.adapter : null) : (adapterOverride !== null ? adapterOverride : Bluetooth.defaultAdapter) - readonly property var nativeAdapters: backendOverride === null - && adapterOverride === null && Bluetooth.adapters - ? Model.toArray(Bluetooth.adapters.values) : (adapter ? [adapter] : []) + readonly property var nativeAdapters: nativeAdaptersOverride !== null + ? nativeAdaptersOverride : backendOverride === null + && adapterOverride === null && Bluetooth.adapters + ? Model.toArray(Bluetooth.adapters.values) : (adapter ? [adapter] : []) + // The host power helper acts globally; discovery/device actions remain local. + readonly property bool powerEnabled: { + for (let i = 0; i < nativeAdapters.length; i++) { + if (nativeAdapters[i] && nativeAdapters[i].enabled === true) return true + } + return false + } readonly property bool adapterAvailable: adapter !== null readonly property bool radioEnabled: adapterAvailable && adapter.enabled !== undefined && adapter.enabled === true @@ -313,11 +324,29 @@ Item { backendOverride.toggleBluetooth() return true } + if (pendingPowerTarget !== null) return false + pendingPowerAdapter = adapter + pendingPowerTarget = !powerEnabled + powerRequestTimeout.restart() if (radioEnabled) stopDiscovery() - adapter.enabled = !adapter.enabled + try { + executeDeviceCommand([ + "omarchy-bluetooth-power", pendingPowerTarget ? "on" : "off" + ]) + } catch (error) { + clearPendingPower() + return false + } + // Detached dispatch is not success: only observed BlueZ state settles it. return true } + function clearPendingPower() { + pendingPowerTarget = null + pendingPowerAdapter = null + powerRequestTimeout.stop() + } + function setNativePendingAction(address, action) { if (!address) return nativePendingActions = Model.withPendingAction( @@ -711,8 +740,13 @@ Item { syncNativeAudioHandoffIntents() } onDiscoveryDesiredChanged: if (!discoveryDesired) stopDiscovery() + onPowerEnabledChanged: { + if (pendingPowerAdapter === adapter && pendingPowerTarget === powerEnabled) + clearPendingPower() + } onRadioEnabledChanged: if (!radioEnabled) cancelAllAudioHandoffs() onAdapterChanged: { + clearPendingPower() observeNativeAdapter() reconcileNativeEntities() // Ownership is tied to the adapter instance on which Shibumi started the @@ -729,6 +763,17 @@ Item { Component.onCompleted: reconcileNativeEntities() Component.onDestruction: destroyDiscovery() + // A deadline for the desired state, not a timeout/cancellation of the helper. + Timer { + id: powerRequestTimeout + interval: 20000 + repeat: false + onTriggered: { + console.warn("Bluetooth power request did not reach its target") + root.clearPendingPower() + } + } + Timer { id: pendingTimeout interval: 20000 diff --git a/hancore.shibumi.bluetooth/Service.qml b/hancore.shibumi.bluetooth/Service.qml index 2915c3b..bd206c3 100644 --- a/hancore.shibumi.bluetooth/Service.qml +++ b/hancore.shibumi.bluetooth/Service.qml @@ -15,7 +15,7 @@ Item { SuiteRuntime.HostShell { id: suiteShell; host: root.shell } SuiteRuntime.Provider { pluginId: "hancore.shibumi.bluetooth" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest @@ -36,7 +36,8 @@ Item { readonly property bool ready: adapter.ready readonly property bool adapterAvailable: adapter.adapterAvailable - readonly property bool radioEnabled: adapter.radioEnabled + readonly property bool radioEnabled: adapter.powerEnabled + readonly property bool adapterEnabled: adapter.radioEnabled readonly property bool discovering: adapter.discovering readonly property var connectedDevices: adapter.connectedDevices readonly property var knownDevices: adapter.knownDevices @@ -53,7 +54,7 @@ Item { if (!owner) return false if (sessionOwners.indexOf(owner) < 0) sessionOwners = sessionOwners.concat([owner]) - if (sessionOwners.length === 1 && radioEnabled) adapter.startDiscovery() + if (sessionOwners.length === 1 && adapterEnabled) adapter.startDiscovery() return true } @@ -94,8 +95,8 @@ Item { function pendingAction(address) { return adapter.pendingAction(address) } function deviceLabel(device) { return adapter.deviceLabel(device) } - onRadioEnabledChanged: { - if (!radioEnabled) adapter.stopDiscovery() + onAdapterEnabledChanged: { + if (!adapterEnabled) adapter.stopDiscovery() } Component.onDestruction: adapter.stopDiscovery() @@ -135,7 +136,7 @@ Item { repeat: true triggeredOnStart: true running: root.sessionCount > 0 && root.adapterAvailable - && root.radioEnabled && !root.discovering + && root.adapterEnabled && !root.discovering onTriggered: adapter.startDiscovery() } } diff --git a/hancore.shibumi.bluetooth/manifest.json b/hancore.shibumi.bluetooth/manifest.json index 6f8a7ed..e6f52f0 100644 --- a/hancore.shibumi.bluetooth/manifest.json +++ b/hancore.shibumi.bluetooth/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.bluetooth", "name": "Shibumi Bluetooth", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi Bluetooth presentation with a native BlueZ and audio backend.", diff --git a/hancore.shibumi.brightness/Service.qml b/hancore.shibumi.brightness/Service.qml index 044e4e7..667b781 100644 --- a/hancore.shibumi.brightness/Service.qml +++ b/hancore.shibumi.brightness/Service.qml @@ -13,7 +13,7 @@ Item { property var manifest: null SuiteRuntime.Provider { pluginId: "hancore.shibumi.brightness" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.brightness/manifest.json b/hancore.shibumi.brightness/manifest.json index 9047b3c..52f801c 100644 --- a/hancore.shibumi.brightness/manifest.json +++ b/hancore.shibumi.brightness/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.brightness", "name": "Shibumi Brightness", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi brightness and display presentation over Omarchy's monitor owner.", diff --git a/hancore.shibumi.center/BarWidget.qml b/hancore.shibumi.center/BarWidget.qml index 746417b..2e53d59 100644 --- a/hancore.shibumi.center/BarWidget.qml +++ b/hancore.shibumi.center/BarWidget.qml @@ -295,11 +295,15 @@ Ui.Panel { else root.toggle() } - // Keep the native button's reservation and input; only its bar label - // opts into the shared ink alignment. Vertical rendering stays native. - Presentation.IconText { - barText: true - baselineFont: clockWidget.textFont + // Keep the native line-box centring: the smaller date sits raised + // beside the clock. Only paint is snapped; reservation/input stay native. + Presentation.BarInk { id: datePlacement; target: dateLabel } + Text { + id: dateLabel + transform: Translate { + x: datePlacement.snapX(0) + y: datePlacement.snapY(0) + } visible: !dateButton.labelVisible anchors.centerIn: parent textFormat: Text.PlainText diff --git a/hancore.shibumi.center/Service.qml b/hancore.shibumi.center/Service.qml index acd130e..b010832 100644 --- a/hancore.shibumi.center/Service.qml +++ b/hancore.shibumi.center/Service.qml @@ -10,7 +10,7 @@ Item { property var manifest: null SuiteRuntime.Provider { pluginId: "hancore.shibumi.center" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.center/manifest.json b/hancore.shibumi.center/manifest.json index 0214485..eb287cc 100644 --- a/hancore.shibumi.center/manifest.json +++ b/hancore.shibumi.center/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.center", "name": "Shibumi Center", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi clock, calendar, weather, status, and update center.", diff --git a/hancore.shibumi.control-center/BarWidget.qml b/hancore.shibumi.control-center/BarWidget.qml index 4877fc7..2765629 100644 --- a/hancore.shibumi.control-center/BarWidget.qml +++ b/hancore.shibumi.control-center/BarWidget.qml @@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound import QtQuick import QtQuick.Effects import qs.Ui as Ui -import qs.Commons as Commons import "HostIdentity.js" as HostIdentity import "../hancore.shibumi.state/runtime" as SuiteRuntime import "../hancore.shibumi.state/lib/presentation" as Presentation @@ -355,7 +354,7 @@ Ui.Panel { visible: root.archWordmark anchors.fill: parent - Presentation.BarGlyph { + Text { id: archLogo anchors.left: parent.left anchors.leftMargin: root.archWordLeftPad @@ -418,12 +417,43 @@ Ui.Panel { width: root.stockOmarchyHost ? 18 : 16 height: root.tokens ? root.tokens.pillHeight : 24 - Presentation.BarGlyph { - anchors.fill: parent - text: !root.stockOmarchyHost && root.launcherConfig.icon !== "shibumi" - ? root.iconGlyph(root.launcherConfig.icon) : "" + Text { + visible: !root.stockOmarchyHost + && root.launcherConfig.icon !== "shibumi" + anchors.centerIn: parent + anchors.horizontalCenterOffset: root.iconXOffset(root.launcherConfig.icon) + anchors.verticalCenterOffset: root.iconYOffset(root.launcherConfig.icon) + text: root.iconGlyph(root.launcherConfig.icon) color: root.widgetInk - font.family: Commons.Style.font.family + renderType: Text.QtRendering + font.family: root.iconFont(root.launcherConfig.icon) + font.pixelSize: root.iconSize(root.launcherConfig.icon) + } + + Image { + visible: root.stockOmarchyHost + || (root.launcherConfig.icon === "shibumi" && !root.v1CustomFill) + anchors.centerIn: parent + width: 18 + height: 18 + source: Qt.resolvedUrl("assets/shibumi-icon-hikiryo.svg") + fillMode: Image.PreserveAspectFit + sourceSize.width: 24 + sourceSize.height: 24 + smooth: true + mipmap: true + } + + FlatTintedImage { + id: v1TintedLauncherIcon + visible: !root.stockOmarchyHost + && root.launcherConfig.icon === "shibumi" + && root.v1CustomFill + anchors.centerIn: parent + width: 18 + height: 18 + source: Qt.resolvedUrl("assets/shibumi-icon-hikiryo.svg") + tint: root.widgetInk } } diff --git a/hancore.shibumi.control-center/PluginUpdateService.qml b/hancore.shibumi.control-center/PluginUpdateService.qml index abc0aff..abf88d2 100644 --- a/hancore.shibumi.control-center/PluginUpdateService.qml +++ b/hancore.shibumi.control-center/PluginUpdateService.qml @@ -20,7 +20,7 @@ Item { SuiteRuntime.Provider { id: runtimeProvider pluginId: "hancore.shibumi.control-center" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.control-center/manifest.json b/hancore.shibumi.control-center/manifest.json index a41355b..d3f97a3 100644 --- a/hancore.shibumi.control-center/manifest.json +++ b/hancore.shibumi.control-center/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.control-center", "name": "Shibumi Control Center", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi G1 presentation and bar configuration panel.", diff --git a/hancore.shibumi.cpu/Service.qml b/hancore.shibumi.cpu/Service.qml index fca2904..9afd6df 100644 --- a/hancore.shibumi.cpu/Service.qml +++ b/hancore.shibumi.cpu/Service.qml @@ -13,7 +13,7 @@ Item { SuiteRuntime.Provider { id: runtimeProvider pluginId: "hancore.shibumi.cpu" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.cpu/manifest.json b/hancore.shibumi.cpu/manifest.json index 62888c0..ed5d387 100644 --- a/hancore.shibumi.cpu/manifest.json +++ b/hancore.shibumi.cpu/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.cpu", "name": "Shibumi CPU", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi CPU widget, GPU sampler, and detailed usage panel.", diff --git a/hancore.shibumi.gpu/manifest.json b/hancore.shibumi.gpu/manifest.json index 5548422..6ae26bb 100644 --- a/hancore.shibumi.gpu/manifest.json +++ b/hancore.shibumi.gpu/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.gpu", "name": "Shibumi GPU", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Lazy GPU load, hardware, driver, temperature, and VRAM widget.", diff --git a/hancore.shibumi.media/Service.qml b/hancore.shibumi.media/Service.qml index cf501cd..525c8b5 100644 --- a/hancore.shibumi.media/Service.qml +++ b/hancore.shibumi.media/Service.qml @@ -18,7 +18,7 @@ Item { SuiteRuntime.Provider { id: runtimeProvider pluginId: "hancore.shibumi.media" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.media/manifest.json b/hancore.shibumi.media/manifest.json index 4e2248f..dc45ff1 100644 --- a/hancore.shibumi.media/manifest.json +++ b/hancore.shibumi.media/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.media", "name": "Shibumi Media", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi media controls over Omarchy's MPRIS service.", diff --git a/hancore.shibumi.memory/manifest.json b/hancore.shibumi.memory/manifest.json index 234860a..bb02043 100644 --- a/hancore.shibumi.memory/manifest.json +++ b/hancore.shibumi.memory/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.memory", "name": "Shibumi Memory", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi memory widget and detailed memory panel.", diff --git a/hancore.shibumi.network/Service.qml b/hancore.shibumi.network/Service.qml index 833e81a..5d7af9b 100644 --- a/hancore.shibumi.network/Service.qml +++ b/hancore.shibumi.network/Service.qml @@ -15,7 +15,7 @@ Item { property var manifest: null SuiteRuntime.Provider { pluginId: "hancore.shibumi.network" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.network/manifest.json b/hancore.shibumi.network/manifest.json index 0a3f0b9..5635a96 100644 --- a/hancore.shibumi.network/manifest.json +++ b/hancore.shibumi.network/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.network", "name": "Shibumi Network", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Native Shibumi NetworkManager ownership, telemetry, actions, QR, and speed testing.", diff --git a/hancore.shibumi.power-profile/manifest.json b/hancore.shibumi.power-profile/manifest.json index 7571ccb..d53796e 100644 --- a/hancore.shibumi.power-profile/manifest.json +++ b/hancore.shibumi.power-profile/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.power-profile", "name": "Shibumi Power Profile", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi power-profile status and profile selector.", diff --git a/hancore.shibumi.power-state/Service.qml b/hancore.shibumi.power-state/Service.qml index 07fa555..a787714 100644 --- a/hancore.shibumi.power-state/Service.qml +++ b/hancore.shibumi.power-state/Service.qml @@ -23,7 +23,7 @@ Item { SuiteRuntime.Provider { id: runtimeProvider pluginId: "hancore.shibumi.power-state" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.power-state/manifest.json b/hancore.shibumi.power-state/manifest.json index 852d243..8ab5b05 100644 --- a/hancore.shibumi.power-state/manifest.json +++ b/hancore.shibumi.power-state/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.power-state", "name": "Shibumi Power State", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shared battery and power-profile state for Shibumi widgets.", diff --git a/hancore.shibumi.quick-access/Service.qml b/hancore.shibumi.quick-access/Service.qml index a2b3980..9c7e3f5 100644 --- a/hancore.shibumi.quick-access/Service.qml +++ b/hancore.shibumi.quick-access/Service.qml @@ -18,7 +18,7 @@ Item { SuiteRuntime.HostShell { id: suiteShell; host: root.shell } SuiteRuntime.Provider { pluginId: "hancore.shibumi.quick-access" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.quick-access/manifest.json b/hancore.shibumi.quick-access/manifest.json index 879c88b..4f554c4 100644 --- a/hancore.shibumi.quick-access/manifest.json +++ b/hancore.shibumi.quick-access/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.quick-access", "name": "Shibumi Quick Access", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi idle inhibitor and theme, wallpaper, screenshot, and video pickers.", diff --git a/hancore.shibumi.reactor/Service.qml b/hancore.shibumi.reactor/Service.qml index 3f7aea8..26cfd6a 100644 --- a/hancore.shibumi.reactor/Service.qml +++ b/hancore.shibumi.reactor/Service.qml @@ -18,7 +18,7 @@ Item { SuiteRuntime.Provider { id: runtimeProvider pluginId: "hancore.shibumi.reactor" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.reactor/manifest.json b/hancore.shibumi.reactor/manifest.json index 9f041af..6d5fa81 100644 --- a/hancore.shibumi.reactor/manifest.json +++ b/hancore.shibumi.reactor/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.reactor", "name": "Shibumi Reactor", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi event and quote Reactor service for compatible bar styles.", diff --git a/hancore.shibumi.state/Service.qml b/hancore.shibumi.state/Service.qml index 6e5a765..bb16892 100644 --- a/hancore.shibumi.state/Service.qml +++ b/hancore.shibumi.state/Service.qml @@ -16,7 +16,7 @@ Item { SuiteRuntime.Provider { id: runtimeProvider pluginId: "hancore.shibumi.state" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.state/manifest.json b/hancore.shibumi.state/manifest.json index 7d0cc81..4429365 100644 --- a/hancore.shibumi.state/manifest.json +++ b/hancore.shibumi.state/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.state", "name": "Shibumi State", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Normalized Shibumi configuration and theme palette state.", diff --git a/hancore.shibumi.state/runtime/Runtime.qml b/hancore.shibumi.state/runtime/Runtime.qml index a874346..02af8e6 100644 --- a/hancore.shibumi.state/runtime/Runtime.qml +++ b/hancore.shibumi.state/runtime/Runtime.qml @@ -10,7 +10,7 @@ QtObject { id: runtime readonly property int contractVersion: 1 - readonly property string suiteVersion: "0.1.1-beta.16" + readonly property string suiteVersion: "0.1.1-beta.16.1" readonly property var providerIds: [ "hancore.shibumi.state", "hancore.shibumi.control-center", "hancore.shibumi.reactor", "hancore.shibumi.telemetry", diff --git a/hancore.shibumi.status/BarWidget.qml b/hancore.shibumi.status/BarWidget.qml index 8eacbcc..88ad46e 100644 --- a/hancore.shibumi.status/BarWidget.qml +++ b/hancore.shibumi.status/BarWidget.qml @@ -89,6 +89,46 @@ Item { || iconMode && notificationPresented || textMode && notificationService !== null readonly property real childGap: Commons.Style.space(4) + Presentation.BarInk { id: statusPlacement; target: root } + // Quick Access: 22px slots + 4px spacing - 12px glyphs = 14px ink gap. + // Return the interior savings to the owner rather than to its outer gaps. + readonly property real iconGapPixels: Math.round(Commons.Style.space(14) * statusPlacement.dpr) + readonly property rect updateInk: updateWidget && updateWidget.iconInk !== undefined + ? updateWidget.mapToItem(updateSlot, updateWidget.iconInk) : Qt.rect(0, 0, 0, 0) + function inkEdge(slot, ink, end) { + const edge = (statusPlacement.origin.x + horizontalInset + slot.x + + ink.x + (end ? ink.width : 0)) * statusPlacement.dpr + return end ? Math.ceil(edge - 1e-7) : Math.floor(edge + 1e-7) + } + readonly property real directContraction: fullMode && updatePresented + && notificationPresented && !trayPresented && updateInk.width > 0 + ? Math.max(0, inkEdge(notificationView, notificationView.iconInk, false) + - inkEdge(updateSlot, updateInk, true) - iconGapPixels) : 0 + readonly property real updateShift: !fullMode || !updatePresented || updateInk.width <= 0 ? 0 + : (trayPresented ? trayView.pinnedCount > 0 + ? Math.round(Commons.Style.space(3) * statusPlacement.dpr) + : Math.min(Math.floor(childGap * statusPlacement.dpr), + Math.max(0, inkEdge(trayView, trayView.drawerInk, false) + - inkEdge(updateSlot, updateInk, true) - iconGapPixels)) + : Math.ceil(directContraction / 2)) / statusPlacement.dpr + readonly property real bellShift: !fullMode || !notificationPresented ? 0 + : -(trayPresented ? trayView.drawerCount > 0 + ? Math.min(Math.floor(childGap * statusPlacement.dpr), + Math.max(0, inkEdge(notificationView, notificationView.iconInk, false) + - inkEdge(trayView, trayView.drawerInk, true) - iconGapPixels)) + : Math.round(Commons.Style.space(3) * statusPlacement.dpr) + : Math.floor(directContraction / 2)) / statusPlacement.dpr + property real spacingSavings: 0 + property bool spacingActive: false + // Settle after layout: binding width directly to snapped ink feeds the + // parent's new position back into its own reservation. + function syncSpacingSavings() { + if (!root || !spacingActive) return + const saved = Math.max(0, updateShift - bellShift) + if (Math.abs(spacingSavings - saved) > 1e-7) spacingSavings = saved + } + onUpdateShiftChanged: Qt.callLater(syncSpacingSavings) + onBellShiftChanged: Qt.callLater(syncSpacingSavings) readonly property int presentedCount: fullMode ? (updatePresented ? 1 : 0) + (trayPresented ? 1 : 0) + (notificationPresented ? 1 : 0) @@ -96,7 +136,7 @@ Item { readonly property real contentWidth: fullMode ? updateSlotWidth + traySlotWidth + notificationSlotWidth - + Math.max(0, presentedCount - 1) * childGap + + Math.max(0, presentedCount - 1) * childGap - spacingSavings : textMode ? textStatus.implicitWidth : notificationView.implicitWidth readonly property real updateSlotWidth: updatePresented ? updateSlot.implicitWidth : 0 @@ -107,9 +147,6 @@ Item { readonly property real trayPinnedIconOffset: trayView.pinnedIconHorizontalOffset readonly property color trayDrawerIconColor: trayView.drawerIconColor - readonly property color trayDrawerBadgeColor: trayView.drawerBadgeColor - readonly property color trayDrawerBadgeTextColor: - trayView.drawerBadgeTextColor readonly property color notificationBadgeColor: notificationView.badgeFillColor readonly property color notificationBadgeTextColor: @@ -119,9 +156,6 @@ Item { readonly property real updateSlotLayer: updateSlot.z readonly property real traySlotLayer: trayView.z readonly property real notificationSlotLayer: notificationView.z - readonly property real updateBadgeLayer: updateWidget - && "badgeLayer" in updateWidget ? Number(updateWidget.badgeLayer) : 0 - readonly property real trayBadgeLayer: trayView.badgeLayer readonly property real notificationBadgeLayer: notificationView.badgeLayer visible: ready && hasVisibleChild @@ -190,20 +224,12 @@ Item { if ("settings" in item) item.settings = childSettings("hancore.shibumi.update-center") if ("contentColor" in item) item.contentColor = root.widgetInk - if ("customToneActive" in item) - item.customToneActive = root.v1CustomToneActive - if ("badgeContrastColor" in item) - item.badgeContrastColor = root.badgeContrastColor } function syncUpdateInk() { if (!updateWidget) return if ("contentColor" in updateWidget) updateWidget.contentColor = root.widgetInk - if ("customToneActive" in updateWidget) - updateWidget.customToneActive = root.v1CustomToneActive - if ("badgeContrastColor" in updateWidget) - updateWidget.badgeContrastColor = root.badgeContrastColor } function injectChildren() { @@ -470,10 +496,13 @@ Item { onBadgeContrastColorChanged: syncUpdateInk() Component.onCompleted: { + spacingActive = true + Qt.callLater(syncSpacingSavings) lifecycleBar = bar scheduleChildSync() } Component.onDestruction: { + spacingActive = false close() const dyingBar = bar || lifecycleBar if (dyingBar && "tearingDown" in dyingBar @@ -583,15 +612,20 @@ Item { Row { id: statusRow - anchors.centerIn: parent + anchors.verticalCenter: parent.verticalCenter + x: root.horizontalInset + transform: Translate { x: -root.updateShift } spacing: root.childGap z: 2 Item { id: updateSlot + transform: Translate { x: root.updateShift } anchors.verticalCenter: parent.verticalCenter visible: root.fullMode && updateLoader.item !== null - implicitWidth: visible ? root.statusActionSlot : 0 + implicitWidth: visible ? root.statusActionSlot + + (root.updateWidget && "leadingWidth" in root.updateWidget + ? Number(root.updateWidget.leadingWidth) : 0) : 0 implicitHeight: updateLoader.implicitHeight width: implicitWidth height: implicitHeight @@ -609,16 +643,15 @@ Item { visible: root.fullMode && presented bar: root.bar trayBackend: root.trayWidget - customToneActive: root.v1CustomToneActive contentColor: root.v1CustomToneActive ? root.widgetInk : root.bar ? root.bar.foreground : root.widgetInk - badgeContrastColor: root.badgeContrastColor z: 2 onDrawerRequested: root.toggleTrayDrawer() } NotificationStatusView { id: notificationView + transform: Translate { x: root.bellShift } visible: (root.fullMode || root.iconMode) && presented bar: root.bar contentColor: root.widgetInk diff --git a/hancore.shibumi.status/NotificationStatusView.qml b/hancore.shibumi.status/NotificationStatusView.qml index 62e8f4b..9cc1922 100644 --- a/hancore.shibumi.status/NotificationStatusView.qml +++ b/hancore.shibumi.status/NotificationStatusView.qml @@ -15,6 +15,7 @@ Item { ? bar.background : Commons.Color.background property real slotWidth: Commons.Style.space(26) readonly property real iconHorizontalOffset: Commons.Style.space(1) + readonly property rect iconInk: bellIcon.mapToItem(root, bellIcon.symbolInk) property var notificationService: null readonly property int pendingCount: notificationService && notificationService.liveAvailable === true && notificationService.pendingModel diff --git a/hancore.shibumi.status/Service.qml b/hancore.shibumi.status/Service.qml index b93a81f..1188a1f 100644 --- a/hancore.shibumi.status/Service.qml +++ b/hancore.shibumi.status/Service.qml @@ -16,7 +16,7 @@ Item { SuiteRuntime.HostShell { id: suiteShell; host: root.shell } SuiteRuntime.Provider { pluginId: "hancore.shibumi.status" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.status/TrayStatusView.qml b/hancore.shibumi.status/TrayStatusView.qml index d949207..301d355 100644 --- a/hancore.shibumi.status/TrayStatusView.qml +++ b/hancore.shibumi.status/TrayStatusView.qml @@ -9,15 +9,10 @@ Item { required property var bar property var trayBackend: null - property bool customToneActive: false property color contentColor: bar ? bar.foreground : Commons.Color.foreground - property color badgeContrastColor: bar - ? bar.background : Commons.Color.background readonly property color drawerIconColor: moreIcon.color - readonly property color drawerBadgeColor: badgePaint.color - readonly property color drawerBadgeTextColor: badgeValue.color - readonly property real badgeLayer: drawerBadge.z + readonly property rect drawerInk: moreIcon.mapToItem(root, moreIcon.symbolInk) readonly property var pinnedItems: trayBackend && Array.isArray(trayBackend.pinnedItems) ? trayBackend.pinnedItems : [] readonly property int drawerCount: trayBackend @@ -91,14 +86,35 @@ Item { if (root.bar) root.bar.unregisterClickTarget(trayDelegate) } - Presentation.BarGlyph { + Item { + id: trayIconSlot anchors.centerIn: parent anchors.horizontalCenterOffset: root.pinnedIconHorizontalOffset - text: "" - font.family: Commons.Style.font.family - color: root.contentColor width: Commons.Style.space(14) height: Commons.Style.space(14) + + Presentation.BarInk { id: iconPlacement; target: trayIconSlot } + Image { + id: trayIcon + source: String(trayDelegate.modelData.icon || "") + sourceSize: Qt.size(Math.round(parent.width * iconPlacement.dpr), + Math.round(parent.height * iconPlacement.dpr)) + width: sourceSize.width / iconPlacement.dpr + height: sourceSize.height / iconPlacement.dpr + x: iconPlacement.snapX((parent.width - width) / 2) + y: iconPlacement.rectangleY(height) + fillMode: Image.PreserveAspectFit + smooth: true + mipmap: true + } + + Presentation.BarGlyph { + visible: trayIcon.status !== Image.Ready + anchors.fill: parent + text: "" + font.family: Commons.Style.font.family + color: root.contentColor + } } MouseArea { @@ -171,90 +187,6 @@ Item { Behavior on color { ColorAnimation { duration: 150 } } } - Presentation.BarInk { id: badgePlacement; target: drawerBadge } - - Rectangle { - id: drawerBadge - visible: root.drawerCount > 0 - width: Math.floor(Math.max(Commons.Style.space(12), badgeText.implicitWidth + 6)) - height: Commons.Style.space(12) - radius: height / 2 - color: "transparent" - border.width: 0 - border.color: "transparent" - z: 10 - x: moreIcon.x + moreIcon.badgeLeft - y: moreIcon.badgeY(root, drawerBadge) - anchors.verticalCenter: !moreIcon.optical ? moreIcon.verticalCenter : undefined - anchors.verticalCenterOffset: -6 - anchors.horizontalCenter: !moreIcon.optical ? moreIcon.horizontalCenter : undefined - anchors.horizontalCenterOffset: 7 - - Text { - id: badgeText - visible: false - anchors.centerIn: parent - text: root.drawerCount > 99 ? "99" : String(root.drawerCount) - color: root.customToneActive - ? root.badgeContrastColor - : root.bar ? root.bar.background : Commons.Color.background - font.family: root.bar ? root.bar.fontFamily : Commons.Style.font.family - font.pixelSize: 7 - font.weight: Font.Bold - } - - // Paint only; the original badge still reserves its layout and anchor. - Rectangle { - id: badgePaint - readonly property real pixel: 1 / badgePlacement.dpr - readonly property real diameter: Math.max(1, - Math.round(Commons.Style.space(10) * badgePlacement.dpr)) * pixel - width: Math.floor(Math.max(diameter, badgeValue.implicitWidth + Commons.Style.space(6)) / pixel) * pixel - height: diameter - radius: height / 2 - color: root.customToneActive - ? root.contentColor - : root.bar ? root.bar.urgent : Commons.Color.accent - x: { - const origin = badgePlacement.origin - const slot = drawerToggle.mapToItem(drawerBadge, 0, 0) - const first = Math.ceil((origin.x + slot.x) / pixel) * pixel - origin.x - // Keep the single-digit circle anchored; extra width grows right. - const last = Math.floor((origin.x + slot.x + drawerToggle.width) / pixel) * pixel - origin.x - diameter - return Math.max(first, Math.min(last, badgePlacement.snapX(0))) - } - y: { - const origin = badgePlacement.origin - const slot = drawerToggle.mapToItem(drawerBadge, 0, 0) - const first = Math.ceil((origin.y + slot.y) / pixel) * pixel - origin.y - const last = Math.floor((origin.y + slot.y + drawerToggle.height) / pixel) * pixel - origin.y - height - return Math.max(first, Math.min(last, - badgePlacement.snapY((drawerBadge.height - height) / 2))) - } - - Presentation.BarInk { id: valuePlacement; target: badgePaint } - TextMetrics { - id: valueInk - text: badgeValue.text - font: Qt.font({family: badgeValue.font.family, - pixelSize: badgeValue.font.pixelSize * valuePlacement.metricScale, - weight: badgeValue.font.weight, hintingPreference: Font.PreferNoHinting}) - } - Text { - id: badgeValue - text: root.drawerCount > 99 ? "99+" : String(root.drawerCount) - x: valuePlacement.snapX(parent.width / 2 - - (valueInk.tightBoundingRect.x + valueInk.tightBoundingRect.width / 2) / valuePlacement.metricScale) - y: valuePlacement.snapY(parent.height / 2 - - (valueInk.tightBoundingRect.y + valueInk.tightBoundingRect.height / 2) / valuePlacement.metricScale) - baselineOffset - color: badgeText.color - font.family: badgeText.font.family - font.pixelSize: Commons.Style.space(7) - font.bold: true - } - } - } - MouseArea { id: drawerMouse anchors.fill: parent diff --git a/hancore.shibumi.status/manifest.json b/hancore.shibumi.status/manifest.json index f12bc7f..56ec825 100644 --- a/hancore.shibumi.status/manifest.json +++ b/hancore.shibumi.status/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.status", "name": "Shibumi Status", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shibumi tray and notification composition with shared desktop status state.", diff --git a/hancore.shibumi.storage/Service.qml b/hancore.shibumi.storage/Service.qml index beeb4f0..bcc8e49 100644 --- a/hancore.shibumi.storage/Service.qml +++ b/hancore.shibumi.storage/Service.qml @@ -13,7 +13,7 @@ Item { SuiteRuntime.Provider { id: runtimeProvider pluginId: "hancore.shibumi.storage" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.storage/manifest.json b/hancore.shibumi.storage/manifest.json index 57a72e0..a8da307 100644 --- a/hancore.shibumi.storage/manifest.json +++ b/hancore.shibumi.storage/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.storage", "name": "Shibumi Storage", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Read-only root usage and physical drive inventory.", diff --git a/hancore.shibumi.telemetry/Service.qml b/hancore.shibumi.telemetry/Service.qml index 04877df..807a6e7 100644 --- a/hancore.shibumi.telemetry/Service.qml +++ b/hancore.shibumi.telemetry/Service.qml @@ -15,7 +15,7 @@ Item { SuiteRuntime.Provider { id: runtimeProvider pluginId: "hancore.shibumi.telemetry" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.telemetry/manifest.json b/hancore.shibumi.telemetry/manifest.json index 64c8b58..4ab3418 100644 --- a/hancore.shibumi.telemetry/manifest.json +++ b/hancore.shibumi.telemetry/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.telemetry", "name": "Shibumi Telemetry", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Demand-driven CPU and memory telemetry shared by Shibumi widgets.", diff --git a/hancore.shibumi.temperature/manifest.json b/hancore.shibumi.temperature/manifest.json index 9b88dd5..e4ead09 100644 --- a/hancore.shibumi.temperature/manifest.json +++ b/hancore.shibumi.temperature/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.temperature", "name": "Shibumi Temperature", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Demand-driven hardware temperature widget and sensor panel.", diff --git a/hancore.shibumi.update-center/BarWidget.qml b/hancore.shibumi.update-center/BarWidget.qml index 5fc8a84..e0e0860 100644 --- a/hancore.shibumi.update-center/BarWidget.qml +++ b/hancore.shibumi.update-center/BarWidget.qml @@ -44,20 +44,50 @@ Ui.Panel { readonly property color activeColor: bar ? bar.urgent : Commons.Color.bar.active property color contentColor: activeColor - property bool customToneActive: false - property color badgeContrastColor: bar - ? bar.background : Commons.Color.background - readonly property color badgeFillColor: badgePaint.color - readonly property color badgeTextColor: badgeValue.color - readonly property real badgeLayer: updateBadge.z readonly property string fontFamily: bar ? String(bar.fontFamily || Commons.Style.font.family) : Commons.Style.font.family readonly property bool hasMaterialSymbols: Qt.fontFamilies().indexOf("Material Symbols Rounded") !== -1 readonly property bool panelLoaded: panelLoader.item !== null + readonly property rect iconInk: { + for (let item = updateIcon; item && item !== root; item = item.parent) { + void(item.x); void(item.width) + } + return updateIcon.mapToItem(root, updateIcon.symbolInk) + } - implicitWidth: button.implicitWidth + Presentation.HostTokens { id: hostTokens; bar: root.bar } + readonly property var tokens: bar && "visualTokens" in bar + && bar.visualTokens ? bar.visualTokens : hostTokens + readonly property bool countVisible: !vertical && updateCount > 0 + Presentation.BarInk { id: countPlacement; target: root } + // Fit rounds its origin in logical pixels. Keep both the full expansion and + // its centered half integral in logical AND physical pixels (Wayland /120). + readonly property real countQuantum: { + let numerator = Math.max(1, Math.round(countPlacement.dpr * 120)) + let denominator = 120 + while (denominator !== 0) { + const remainder = numerator % denominator + numerator = denominator + denominator = remainder + } + return 240 / numerator + } + readonly property real countGap: Commons.Style.space(6) + readonly property real leadingWidth: countVisible + ? Math.ceil((Math.ceil(updateValue.implicitWidth) + countGap + - ((button.implicitWidth - updateIcon.width) / 2 + updateIcon.inkLeft)) + / countQuantum) * countQuantum : 0 + TextMetrics { + id: countInk + text: updateValue.text + font: Qt.font({ family: updateValue.font.family, + pixelSize: updateValue.font.pixelSize * countPlacement.metricScale, + weight: updateValue.font.weight, hintingPreference: Font.PreferNoHinting }) + } + + implicitWidth: button.implicitWidth + leadingWidth implicitHeight: button.implicitHeight function tooltipText() { @@ -106,7 +136,7 @@ Ui.Panel { panelLoader.source = "" if (!opened || !updateService) return panelLoader.setSource(panelSource, { - anchorItem: button, + anchorItem: panelAnchor, bar: root.bar, open: true, ownerWidget: root, @@ -137,6 +167,37 @@ Ui.Panel { registeredBar.unregisterClickTarget(root) } + Presentation.IconText { + id: updateValue + visible: root.countVisible + barText: true + x: { + const iconLeft = root.iconInk.x + const first = Math.floor((countPlacement.origin.x + iconLeft) + * countPlacement.dpr + 1e-7) + const end = Math.ceil((countInk.tightBoundingRect.x + + countInk.tightBoundingRect.width) * countPlacement.dpr + / countPlacement.metricScale) + return (first - Math.round(root.countGap * countPlacement.dpr) - end) + / countPlacement.dpr - countPlacement.origin.x + } + anchors.verticalCenter: parent.verticalCenter + text: root.updateCount > 99 ? "99+" : String(root.updateCount) + color: root.contentColor + font.family: root.fontFamily + font.pixelSize: root.tokens.labelSize + renderType: Text.NativeRendering + } + + // Keep the panel/caret on the original icon slot while input spans the count. + Item { + id: panelAnchor + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + width: button.implicitWidth + height: parent.height + } + Ui.WidgetButton { id: button anchors.fill: parent @@ -154,7 +215,10 @@ Ui.Panel { onPressed: function(mouseButton) { root.triggerPress(mouseButton) } Item { - anchors.centerIn: parent + anchors.right: parent.right + anchors.rightMargin: button.implicitWidth - width + - Math.round((button.implicitWidth - width) / 2) + anchors.verticalCenter: parent.verticalCenter width: Commons.Style.space(20) height: Commons.Style.space(20) @@ -172,89 +236,6 @@ Ui.Panel { renderType: root.hasMaterialSymbols ? Text.QtRendering : Text.NativeRendering } - - Presentation.BarInk { id: badgePlacement; target: updateBadge } - - Rectangle { - id: updateBadge - visible: root.updateCount > 0 - x: updateIcon.x + updateIcon.badgeLeft - y: updateIcon.badgeY(root, updateBadge) - anchors.verticalCenter: !updateIcon.optical ? updateIcon.verticalCenter : undefined - anchors.verticalCenterOffset: -Commons.Style.space(6) - anchors.horizontalCenter: !updateIcon.optical ? updateIcon.horizontalCenter : undefined - anchors.horizontalCenterOffset: Commons.Style.space(7) - width: Math.floor(Math.max(Commons.Style.space(12), - badgeText.implicitWidth + Commons.Style.space(6))) - height: Commons.Style.space(12) - radius: height / 2 - color: "transparent" - border.width: 0 - border.color: "transparent" - z: 10 - - Text { - id: badgeText - visible: false - anchors.centerIn: parent - text: root.updateCount > 99 ? "99+" : String(root.updateCount) - color: root.customToneActive - ? root.badgeContrastColor : Commons.Color.background - font.family: root.fontFamily - font.pixelSize: Math.max(Commons.Style.space(7), - Commons.Style.font.caption - 3) - font.bold: true - } - - // Paint only; the original badge still reserves its layout and anchor. - Rectangle { - id: badgePaint - readonly property real pixel: 1 / badgePlacement.dpr - readonly property real diameter: Math.max(1, - Math.round(Commons.Style.space(10) * badgePlacement.dpr)) * pixel - width: Math.floor(Math.max(diameter, badgeValue.implicitWidth + Commons.Style.space(6)) / pixel) * pixel - height: diameter - radius: height / 2 - color: root.contentColor - x: { - const origin = badgePlacement.origin - const slot = root.mapToItem(updateBadge, 0, 0) - const first = Math.ceil((origin.x + slot.x) / pixel) * pixel - origin.x - // Keep the single-digit circle anchored; extra width grows right. - const last = Math.floor((origin.x + slot.x + root.width) / pixel) * pixel - origin.x - diameter - return Math.max(first, Math.min(last, badgePlacement.snapX(0))) - } - y: { - const origin = badgePlacement.origin - const slot = root.mapToItem(updateBadge, 0, 0) - const first = Math.ceil((origin.y + slot.y) / pixel) * pixel - origin.y - const last = Math.floor((origin.y + slot.y + root.height) / pixel) * pixel - origin.y - height - return Math.max(first, Math.min(last, - badgePlacement.snapY((updateBadge.height - height) / 2))) - } - - Presentation.BarInk { id: valuePlacement; target: badgePaint } - TextMetrics { - id: valueInk - text: badgeValue.text - font: Qt.font({family: badgeValue.font.family, - pixelSize: badgeValue.font.pixelSize * valuePlacement.metricScale, - weight: badgeValue.font.weight, hintingPreference: Font.PreferNoHinting}) - } - Text { - id: badgeValue - text: root.updateCount > 99 ? "99+" : String(root.updateCount) - x: valuePlacement.snapX(parent.width / 2 - - (valueInk.tightBoundingRect.x + valueInk.tightBoundingRect.width / 2) / valuePlacement.metricScale) - y: valuePlacement.snapY(parent.height / 2 - - (valueInk.tightBoundingRect.y + valueInk.tightBoundingRect.height / 2) / valuePlacement.metricScale) - baselineOffset - color: badgeText.color - font.family: badgeText.font.family - font.pixelSize: Commons.Style.space(7) - font.bold: true - } - } - } } } diff --git a/hancore.shibumi.update-center/Service.qml b/hancore.shibumi.update-center/Service.qml index adbfd37..2a106d9 100644 --- a/hancore.shibumi.update-center/Service.qml +++ b/hancore.shibumi.update-center/Service.qml @@ -12,7 +12,7 @@ Item { property var manifest: null SuiteRuntime.Provider { pluginId: "hancore.shibumi.update-center" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.update-center/UpdateCenterPanel.qml b/hancore.shibumi.update-center/UpdateCenterPanel.qml index 36b0f8e..5b51a48 100644 --- a/hancore.shibumi.update-center/UpdateCenterPanel.qml +++ b/hancore.shibumi.update-center/UpdateCenterPanel.qml @@ -102,8 +102,8 @@ ShibumiPanel { hoverTextColor: panel.controlMutedHigh hoverBorderColor: panel.controlHoverBorderColor tooltipText: selected - ? "Package count is shown in the badge" - : "Package count is hidden from the badge" + ? "Package count is shown in the bar" + : "Package count is hidden from the bar" onClicked: panel.setBadgePreference( "packageBadge", !panel.packageBadgeEnabled) } @@ -121,8 +121,8 @@ ShibumiPanel { hoverTextColor: panel.controlMutedHigh hoverBorderColor: panel.controlHoverBorderColor tooltipText: selected - ? "Theme count is shown in the badge" - : "Theme count is hidden from the badge" + ? "Theme count is shown in the bar" + : "Theme count is hidden from the bar" onClicked: panel.setBadgePreference( "themeBadge", !panel.themeBadgeEnabled) } diff --git a/hancore.shibumi.update-center/manifest.json b/hancore.shibumi.update-center/manifest.json index 4c112da..320f8d5 100644 --- a/hancore.shibumi.update-center/manifest.json +++ b/hancore.shibumi.update-center/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.update-center", "name": "Shibumi Update Center", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Safe repository package and user theme update status for Shibumi.", diff --git a/hancore.shibumi.workspaces/WorkspaceService.qml b/hancore.shibumi.workspaces/WorkspaceService.qml index 1f31231..4ec8f38 100644 --- a/hancore.shibumi.workspaces/WorkspaceService.qml +++ b/hancore.shibumi.workspaces/WorkspaceService.qml @@ -14,7 +14,7 @@ Item { SuiteRuntime.Provider { id: runtimeProvider pluginId: "hancore.shibumi.workspaces" - implementationVersion: "0.1.1-beta.16" + implementationVersion: "0.1.1-beta.16.1" owner: root host: root.shell manifest: root.manifest diff --git a/hancore.shibumi.workspaces/manifest.json b/hancore.shibumi.workspaces/manifest.json index 602cab8..7f40b6f 100644 --- a/hancore.shibumi.workspaces/manifest.json +++ b/hancore.shibumi.workspaces/manifest.json @@ -2,7 +2,7 @@ "schemaVersion": 1, "id": "hancore.shibumi.workspaces", "name": "Shibumi Workspaces", - "version": "0.1.1-beta.16", + "version": "0.1.1-beta.16.1", "author": "HANCORE", "license": "MIT", "description": "Shared Hyprland workspace state with the Shibumi workspace presentation.", diff --git a/packaging/aur/.SRCINFO b/packaging/aur/.SRCINFO index de877d4..5c97fc3 100644 --- a/packaging/aur/.SRCINFO +++ b/packaging/aur/.SRCINFO @@ -1,6 +1,6 @@ pkgbase = shibumi-shell pkgdesc = Transactional Shibumi desktop shell plugins for Omarchy Quattro - pkgver = 0.1.1beta.16 + pkgver = 0.1.1beta.16.1 pkgrel = 1 url = https://github.com/HANCORE-linux/Shibumi-Shell arch = any @@ -30,7 +30,7 @@ pkgbase = shibumi-shell optdepends = btop: battery panel system monitor action optdepends = nvidia-utils: NVIDIA GPU telemetry optdepends = git: source-checkout diagnostics and migration - source = shibumi-shell-0.1.1-beta.16.tar.gz::https://github.com/HANCORE-linux/Shibumi-Shell/releases/download/v0.1.1-beta.16/shibumi-shell-0.1.1-beta.16.tar.gz - sha256sums = 3e1a2de055a070916e16cb7de51fa3922db8ecf8363bc6976e1b297f1dffc367 + source = shibumi-shell-0.1.1-beta.16.1.tar.gz::https://github.com/HANCORE-linux/Shibumi-Shell/releases/download/v0.1.1-beta.16.1/shibumi-shell-0.1.1-beta.16.1.tar.gz + sha256sums = 8209b53464b5deeb4025c9ab45038946d2e5a794ae7f368da8bee679fed2f30d pkgname = shibumi-shell diff --git a/packaging/aur/PKGBUILD b/packaging/aur/PKGBUILD index d8d498d..e5640b7 100644 --- a/packaging/aur/PKGBUILD +++ b/packaging/aur/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: HANCORE Linux