Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions assets/desktop-contract.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"version": 1,
"shell": {
"font": "mono",
"wall": "snow-capped-mountains-with-full-moon-lo.jpg",
"wallDir": "~/Pictures/Wallpapers"
},
Expand Down
14 changes: 14 additions & 0 deletions assets/scripts/cybexos-runtime
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,20 @@ exec_runtime() {
"$selected" >&2
return 1
}
# The authentication UI belongs to the selected shell. Stop the old
# agent synchronously before QML registers its listener; this also
# handles an upgrade while the previous agent is still running.
# Older releases remain usable when rolling back or leaving dev mode.
if [[ ${CYBEXOS_RUNTIME_TESTING:-0} != 1 ]] && {
systemctl --user is-active --quiet hyprpolkitagent.service ||
[[ $(systemctl --user show hyprpolkitagent.service -p LoadState --value 2>/dev/null) == loaded ]];
}; then
if [[ -f $selected/PolkitWindow.qml ]]; then
systemctl --user stop hyprpolkitagent.service
else
systemctl --user start --no-block hyprpolkitagent.service
fi
fi
export CYBEXOS_USER_CONFIG_ROOT=$config_root
export CYBEXOS_THEME_ROOT=$data_root/themes
export CYBEXOS_PLUGIN_ROOT=$data_root/plugins
Expand Down
75 changes: 75 additions & 0 deletions docs/authentication-dialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Authentication dialog

CybexOS handles Polkit requests in the managed Quickshell process. The built-in
`PolkitWindow.qml` owns one `Quickshell.Services.Polkit.PolkitAgent`, including
when a plugin replaces the bar. `PolkitPrompt.qml` presents its current flow.
Polkit and the system's PAM configuration still decide which identities may
authenticate and whether their responses succeed; the shell changes no policy.

The compact card uses `Common/Theme.qml` fonts, density, light/dark colors and
wallpaper palette. It opens on the focused monitor and stays there until the
request ends, with a fallback if that output disappears. Account names wrap,
multiple eligible identities get a selector, and the action ID is available
under Show details. The card scrolls on small outputs or with large text.

Enter submits the current response, Escape or Cancel aborts the request, and
Tab reaches the controls. Clicking the dimmed background does not dismiss it.
The launcher, drawers and shortcut sheet close during authentication so they
cannot take the password field's keyboard focus. The Network overlay temporarily
hides and releases its focus grab while keeping its requesting helper alive;
it returns when the authentication flow ends. A normal settings window can
remain open behind a request it initiated.

Passwords start masked, never echo the last character, and can be revealed
with the eye button. PAM prompts that request visible responses are supported,
as are informational messages while waiting for fingerprint or other methods.
The response is cleared on submission, cancellation, account/prompt changes,
completion and replacement by another request. Closing the window destroys
the input. Responses go directly to the backend; no shell command, IPC method,
log or persistent setting carries them. This does not promise secure erasure
of Qt's or the authentication library's internal memory.

## Startup and rollback

`cybexos-runtime exec quickshell` stops a loaded or active
`hyprpolkitagent.service` before registering the integrated agent. Source
deployment removes the old session enablement and managed unit; new images
neither require nor start the standalone agent. Existing RPMs are left
installed. Selecting an older runtime without `PolkitWindow.qml` starts the
legacy agent when its unit is available, including after a deployment rollback.

`cybexos-runtime ipc polkit status` returns only `registered` and `active`.
There is no diagnostic method to read or submit a response. Registration must
be true before considering a deployment healthy. After switching from an old
installed runtime resolver to development source, use the checkout's resolver
for the managed service too, or stop the old agent before restarting Quickshell.
Do not start a second `qs` instance to test a dialog.

## Verification

`tests/run` checks account-label handling, QML, startup integration and icon
coverage. `tests/ownership-layering.py` verifies the agent transition and legacy
fallback with isolated service/executable fixtures. The production-component
lifecycle harness exercises submission, duplicate-submit prevention, retry,
account switching, visible PAM responses, local/remote cancellation, completion
and clearing state between requests. Its Polkit fixture never authenticates
against the host. The real-engine harness runs in CI without an active shell;
on a workstation use `tests/lib/quickshell-live` before and after any controlled
test that stops and restores the service.

For live acceptance, verify registration, trigger a real Polkit authorization
check, cancel with the keyboard and confirm the requesting process is denied.
Also check details expansion, password visibility, Tab order, background clicks,
blocked competing overlays, large text, both color modes and output removal.
Successful authentication and fingerprint/other PAM methods require an operator
with the relevant credential or hardware; never collect their password through
agent tools.

On 2026-09-26 the installed Quickshell 0.2.1 build registered successfully and
displayed a real `com.1password.1Password.unlock` authorization check. Escape
returned a dismissed result to `pkcheck`; Tab navigation, visibility, details
and blocking the competing launcher were exercised. The isolated real-engine
conversation fixture and the image's 29 installed-policy/user-parity tests
passed. The managed service finished as the sole `qs` process, with no QML
errors in its current invocation and the old agent inactive. Successful real
authentication, fingerprint hardware and monitor removal were not exercised.
4 changes: 2 additions & 2 deletions docs/omarchy-plugin-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,10 @@ accessibility scale, spacing density, border and corner settings described in
[Shell appearance](shell-appearance.md). Appearance settings apply individually;
there are no appearance presets.

The defaults use JetBrainsMono Nerd Font at 12px, standard spacing, no panel
The defaults use JetBrainsMono Nerd Font at 14px, standard spacing, no panel
border and 16px panel corners. At the default accessibility scale,
Model Usage's `Style.space(420)`
is 420 logical pixels (840 image pixels on a 200% output). Qt applies monitor
is 490 logical pixels (980 image pixels on a 200% output). Qt applies monitor
scaling; the shell never multiplies geometry by monitor scale itself.

Settings → Appearance → Plugins provides an additional interface scale (75–200%). Border mode
Expand Down
4 changes: 4 additions & 0 deletions docs/quickshell-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ you need the reasoning behind a particular change; `git log --oneline

## Testing without a GUI

The built-in [authentication dialog](authentication-dialog.md) uses Quickshell's
Polkit service. Its presentation and startup checks are covered below; its
authentication backend remains the system Polkit/PAM stack.

Run `./tests/run` first; it needs no live shell. External widget tests require
`sway` for a disposable headless Wayland compositor (also installed by CI);
this is a test dependency, not a change to the desktop's compositor.
Expand Down
99 changes: 99 additions & 0 deletions docs/remote-server-widget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Remote Server widget

Add **Remote Server** from Settings → Menubar → Widgets, then open its options.
Set **SSH host** to an existing SSH alias or `user@hostname`, and optionally set
a display name. For example: `john@10.10.0.7`, **The Beast**. These are personal
settings, not distribution defaults; new installations leave the widget disabled
and its host empty, and its dashboard offers **Choose SSH host** until one is set.

The default menubar statistic is CPU utilization. Options include load average,
memory used percent/bytes or available bytes, filesystem used percent/free bytes,
network receive/transmit rate, and the hottest reported temperature. The server
label can be hidden or compacted away on a crowded bar. A healthy server shows
only its reading; the reading turns amber or red past its warning threshold
(85%/95% for percentages and per-CPU load, 75/90 °C for temperature), and the
server mark gains an amber badge while readings are stale or a red one once the
connection is lost.

Click the widget for its dashboard. The header names the server with its SSH
destination, uptime and a Live/Stale/Offline/Connecting status. Four tiles show
CPU, memory, the selected filesystem and the hottest sensor, each with a meter;
the tiles are also tabs for the detail beneath them, which opens on whatever the
menubar shows:

- **CPU**: usage history, one bar per logical CPU (hover for its reading), the
CPU model and 1/5/15-minute load, including load per thread.
- **Memory**: usage history with used, available, total and swap use.
- **Storage**: local filesystems, fullest first. Bind mounts of one device
collapse into its shortest path and firmware variable stores are hidden.
Selecting a row makes it the filesystem the tile and menubar report.
- **Temp**: history of the hottest sensor and the hottest sensors by name;
repeated chip names (one per NVMe drive) are numbered.

Network download/upload rates and their history stay in view below. **Change**
lists the default route, then addressed or active interfaces (idle container
links on request); choosing one pins it, and Automatic follows the default
route. Automatic networking prefers the default route, then the busiest
interface with an address. It never sums bridges, bonds and members together.

Charts cover the history collected so far, from two up to ten minutes, and
scroll with time between samples; network charts scale to the next binary unit
above their peak. Missing sensors and first-sample rates are unavailable, never
zero. Disconnections keep the last readings, dimmed, with the reason, a Retry
action and the time since the last reading. Long lists scroll inside the
dashboard while its header and footer stay fixed.

## Connection requirements

- Linux with readable `/proc` and Python **3.9+** on the server.
- OpenSSH and Python 3 on the desktop. GNU `df` provides local filesystem stats;
`ip` provides optional addresses/default-route discovery on the server.
- Working noninteractive SSH key/agent authentication. First connect in a
terminal, for example `ssh john@10.10.0.7`, to verify its host key. Unknown or
changed keys are rejected; the widget never accepts them automatically.
- Configure ports, identities and jump hosts in `~/.ssh/config`. The host field
accepts a destination, not SSH options or a shell command.

No root access, remote installation, remote file writes or remote service is
needed. Temperature sensors depend on the host's drivers and permissions.
Physical DIMM type/speed, SMART health and privileged hardware information are
outside the current collector. Network filesystems and temporary/container
overlay mounts are excluded from capacity collection.

## Implementation and lifecycle

`Common/RemoteServer.qml` owns a single `Process` for the whole shell, independent
of monitor count. `scripts/remote-server.py` validates the destination and execs
SSH, sending the self-contained `remote_server_probe.py` as a quoted Python
program. One persistent SSH channel streams newline-delimited JSON. Its stdin
accepts cadence/refresh messages, and EOF terminates the probe. The connection
does not create a background SSH master or forward an agent/ports.

The default interval is five seconds; opening the dashboard or its settings
claims two-second sampling. Closing the last view returns to the configured
interval. Disabling the widget and closing its views stops the connection.
CPU and network rates use counter deltas over actual monotonic elapsed time,
with unknown rates on initial/reset samples. Memory usage uses `MemAvailable`,
not `MemFree`; filesystem free space is what an unprivileged user can use.
Hardware details, addresses and filesystems refresh every minute, or on Refresh;
optional `df`/`ip` commands have bounded execution time.

History stays in memory (up to ten minutes/300 points) and resets on host change,
reboot or a long sampling gap. Reconnection backs off from five to sixty seconds.
A heartbeat timeout catches a stalled stream. Host changes discard old data and
ignore the previous connection's late output. SSH failures appear in the view.

Read-only diagnostics and manual refresh use the normal runtime IPC entrypoint:

```sh
cybexos-runtime ipc remoteServer status
cybexos-runtime ipc remoteServer refresh
cybexos-runtime ipc remoteServer configure
cybexos-runtime ipc popouts open remote
```

Tests cover counter resets, memory semantics, cache lifetime, transport quoting,
SSH restrictions, stream cadence/EOF, malformed samples, interface selection,
metric formatting, warning levels, chart windows and scales, filesystem and
sensor presentation, history bounds and settings migration. Run `./tests/run`.
For live checks, use `tests/lib/quickshell-live` begin/end and the managed service.
17 changes: 11 additions & 6 deletions docs/shell-appearance.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,23 @@ settings. Existing explicit plugin border overrides remain valid; choose
surface borders.

The defaults use Dark mode, a Hug bar, wallpaper colors, opaque surfaces and
numbered workspaces. Typography uses JetBrainsMono Nerd Font at 12px, 100% UI
numbered workspaces. Typography uses JetBrainsMono Nerd Font at 14px, 100% UI
scale and standard spacing. Panels have 16px corners and no border (width 0);
plugins inherit the shared appearance. Appearance has no preset actions.
Existing saved preferences remain in effect; section resets use these defaults.
New ISO-installed accounts explicitly seed the same JetBrainsMono font choice.
Empty or older settings without a font also inherit this default; the historical
Google Sans migration applies only to a stored pre-schema-7 Urbanist value.

The shared library and usage rules are documented in [Shell typography](shell-typography.md).

Typography follows [Omarchy's default scale](https://github.com/omacom/omarchy/blob/quattro/default/themed/shell.toml.tpl)
and [default monospace family](https://github.com/omacom/omarchy/blob/quattro/default/fontconfig/conf.avail/50-omarchy.conf):
10px captions, 11px secondary copy, 12px body/control/bar text, 14px titles,
16px headings, and 24/28px display values. Settings labels, inputs, pickers,
and actions use the same body role as plugin controls. Regular copy uses
Typography derives from [Omarchy's scale](https://github.com/omacom/omarchy/blob/quattro/default/themed/shell.toml.tpl)
and [default monospace family](https://github.com/omacom/omarchy/blob/quattro/default/fontconfig/conf.avail/50-omarchy.conf),
with a larger default base: 12px captions, 13px secondary copy,
14px body/control/bar text, 16px titles, 18px headings, and 28/33px display
values. The heading multiplier is tuned to 18px at the new base.
Settings labels, inputs, pickers and actions use the same body role as plugin
controls. Regular copy uses
weight 400; headings can use medium, semibold or bold.

Native body/caption and plugin body/caption share the same reference sizes.
Expand Down
46 changes: 37 additions & 9 deletions docs/shell-typography.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,24 @@ All three resolve through the same library. Existing `Theme.fontBody` and
similar aliases, `Style.font.body`, and `api.theme.fontSize` remain compatible.
New native views must use the named roles below, not the old aliases.

## Comparison with Omarchy
## Readable defaults

Since 2026-09-26 the default base is **14 logical pixels**. Normal labels,
controls, navigation and bar readings are 14px; secondary descriptions and
tooltips are 13px; captions, section labels and metadata are 12px. Titles,
notifications and OSD text are 16px, and headings (including launcher queries
and results) are 18px. These are the sizes at 100% interface scale and default
text size; explicit smaller settings and plugin overrides remain available.

Native and plugin surfaces use the same resolver. The Omarchy multipliers
below are retained except for `heading`, which uses 18/14 of the effective
base. Geometry retains its 12px reference so panels and spacing grow with the
larger text; wrapping rows grow to their content and panels clamp to the output.
Fresh installations and appearance resets use 14px. Existing saved font sizes
are preserved; select 14px under Appearance → Advanced text options to adopt
the new size on an existing installation.

## Historical comparison with Omarchy (12px base)

Audited on 2026-09-21 against Omarchy commit
[`961ec7f39fd0d70c7d2944c5b80585a86713693d`](https://github.com/omacom/omarchy/tree/961ec7f39fd0d70c7d2944c5b80585a86713693d).
Expand Down Expand Up @@ -45,8 +62,8 @@ Sources at the audited revision:
[OSD](https://github.com/omacom/omarchy/blob/961ec7f39fd0d70c7d2944c5b80585a86713693d/shell/plugins/osd/Osd.qml),
[clock hero](https://github.com/omacom/omarchy/blob/961ec7f39fd0d70c7d2944c5b80585a86713693d/shell/plugins/panels/clock/Panel.qml).

The core scale is caption 10, body-small 11, body 12, subtitle 13, title 14,
heading 16, display 24 and display-large 28. The `clock` role explicitly
At the September 21 audit the core scale was caption 10, body-small 11, body 12,
subtitle 13, title 14, heading 16, display 24 and display-large 28. The `clock` role explicitly
centralizes Omarchy's exceptional 52px date treatment for our date/time hero;
it is not a general heading. Icons keep their separate optical sizes.
Omarchy uses Liberation Sans for notifications; we retain the user's shared
Expand All @@ -56,7 +73,7 @@ shared size/usage contract, not a claim of identical layout or rendering.
## Implementation contract

`ShellMetrics.calculate()` combines base size, UI scale and accessibility
scale once. `Typography.resolve()` applies Omarchy's multipliers and rounds
scale once. `Typography.resolve()` applies the shared multipliers and rounds
once per token. Density affects spacing, not font size; Qt applies monitor
scaling. Native and plugin adapters no longer maintain independent type
scales. At default plugin settings their role values are identical, including
Expand Down Expand Up @@ -84,15 +101,26 @@ because the available row is short. Reserve `section` for group labels and
wrapping, scrolling or elision when space is constrained; do not invent a
local smaller size. New exceptional sizes need a documented shared token.

The launcher deliberately uses `heading` (16px at the default base) for its
search query and primary result labels, matching Omarchy v4.0.4's menu.
The launcher deliberately uses `heading` (18px at the default base) for its
search query and primary result labels, following Omarchy v4.0.4's menu hierarchy.
The query is regular weight and result labels are medium weight. Provider tabs
use `title` (14px) at medium weight with 16px icons. This prominent search field
is an exception to the ordinary `control` input role and still follows the
shared accessibility scale.
use `title` (16px) at medium weight with icons from the shared scale. This
prominent search field is an exception to the ordinary `control` input role
and still follows the shared accessibility scale.

## Verification

The 2026-09-26 readability update passed 1,163 unit tests and the required
repository check stages, with the updated typography expectations rerun.
Managed-service checks confirmed identical native/plugin role maps for all
nine text-size/density combinations, using effective bases of 14, 16 and 18px.
Overview (including the growing media row), sound, Appearance and the launcher
were inspected at the new default, and Overview at Larger text with Compact
spacing. The service finished as the sole Quickshell process with no QML
errors in its current invocation. Both outputs used 2× device scaling;
fractional output scaling and the isolated full-shell lifecycle harness were
not exercised on this live desktop.

`tests/quickshell/typography-scale.test.cjs` checks reference values, usage
roles, native/plugin adapter wiring, the accessibility/density matrix, plugin
overrides and icon fallbacks. Source checks reject pixel literals, point
Expand Down
Loading
Loading