diff options
| -rw-r--r-- | KEYBINDS.md | 6 | ||||
| -rw-r--r-- | docs/canonical-laptop.md | 32 | ||||
| -rw-r--r-- | dot_local/lib/dotfiles/canonical_desktop.py | 71 | ||||
| -rw-r--r-- | meta/canonical/extensions.txt | 2 | ||||
| -rw-r--r-- | scripts/canonical-vm-guest.sh | 4 | ||||
| -rw-r--r-- | tests/test_canonical_desktop.py | 46 |
6 files changed, 118 insertions, 43 deletions
diff --git a/KEYBINDS.md b/KEYBINDS.md index a5794f1..ee60159 100644 --- a/KEYBINDS.md +++ b/KEYBINDS.md @@ -423,7 +423,7 @@ in `~/.XCompose`. Press and release Compose, then the sequence. # Canonical GNOME Role -These keys apply only to the `canonical` role with PaperWM. Company-locked +These keys apply only to the `canonical` role with O-Tiling. Company-locked settings remain unchanged. | Keys | Action | @@ -434,6 +434,8 @@ settings remain unchanged. | Super+T | Thunderbird, normal window | | Super+Shift+Q | Close window | | Super+F | Fullscreen | +| Super+Shift+Space | Toggle floating | +| Super+Period | Emoji picker | | Super+H/J/K/Right | Focus left/down/up/right | | Super+Shift+H/J/K/L | Move window left/down/up/right | | Super+I | Toggle dictation; copy text to clipboard | @@ -443,7 +445,7 @@ settings remain unchanged. | Caps Lock | Escape | | Right Ctrl | Compose | -Use GNOME's screenshot UI and Emoji Copy's configured shortcut. PaperWM's +Use GNOME's screenshot UI. O-Tiling's other default shortcuts remain active. Sway modes and recovery keys do not apply to this role. Super+L remains the GNOME lock shortcut. Super+D opens the application launcher. diff --git a/docs/canonical-laptop.md b/docs/canonical-laptop.md index 8b60195..a03db70 100644 --- a/docs/canonical-laptop.md +++ b/docs/canonical-laptop.md @@ -125,8 +125,14 @@ just canonical-check The extension schemas can be unavailable until the first new login. Repeat `canonical-desktop` after that login if it reported missing schemas. Keep Ubuntu -AppIndicator enabled for tray icons. Disable Ubuntu Tiling Assistant in the -Extensions app if it conflicts with PaperWM. Do not override a policy lock. +AppIndicator enabled for tray icons. The desktop recipe disables PaperWM, +Ubuntu Tiling Assistant, Ubuntu Dock, and the custom corporate status extension. +Company-locked settings remain unchanged. Super+D opens the app grid; Alt+Tab +switches windows. GNOME's overview remains available without the dock extension. + +O-Tiling splits the focused area along its longer dimension. To replace an +existing PaperWM installation, run `just canonical-extensions`, log out and back +in, then run `just canonical-desktop`. No `just apply` is needed for these settings. Ghostty starts Nix zsh as a login shell. It does not change the authd account's shell. GNOME keeps its own desktop environment and GNOME Keyring. `pass` remains @@ -286,7 +292,7 @@ Restore skips keys changed since deployment. It does not reset all of dconf. Check these operations both docked and undocked: -- PaperWM focus, window movement, terminal and browser launch. +- O-Tiling focus, window movement, terminal and browser launch. - Lock, unlock, suspend, resume, lid state, and external monitors. - HP webcam selection with `rqr`; the integrated camera when undocked. - A Google Meet camera, microphone, speaker, and screen-share session. @@ -309,24 +315,10 @@ Public-IP lookup is disabled. Sensor names and network totals come from Vitals; select a specific temperature sensor or network device in its preferences if the defaults are not useful on the laptop. -The repo-owned `corporate-panel@dotfiles` extension adds compact status text: - -- `EXT`: connected external display connectors, not a dock identity. -- `APT`: upgrades listed in the local apt cache, excluding Snap and Nix. -- `FAIL`: failed system and user units. A failed query displays `?`, not zero. -- `REBOOT`: `/run/reboot-required` exists. The panel does not reboot the laptop. - -The menu opens display settings, `htop`, `pulsemixer`, Thunderbird, failed-unit -details, available apt/Snap updates, and reboot details. Its update action runs -`just update` in the active chezmoi source directory in a terminal. Status checks -run each minute without sudo, package installation, or apt metadata refresh. -The custom labels use the personal panel's green, yellow, and red colours. - GNOME retains the clock, notifications, privacy indicators, and network/audio -controls. Ubuntu AppIndicators supplies the tray. PaperWM retains workspace -navigation. The custom panel does not copy Sway IPC modules, personal VPN -controls, Arch checks, or the webcam process scanner. Module placement and -actions are not identical to Waybar; custom actions are in a menu. +controls. Ubuntu AppIndicators supplies the tray. O-Tiling provides window +tiling and movement. Emoji Copy uses Super+Period without a persistent panel +icon. The custom `EXT`, `APT`, `FAIL`, and `REBOOT` status labels are disabled. On an existing corporate installation, run outside the sandbox: diff --git a/dot_local/lib/dotfiles/canonical_desktop.py b/dot_local/lib/dotfiles/canonical_desktop.py index cbda77f..7269a3f 100644 --- a/dot_local/lib/dotfiles/canonical_desktop.py +++ b/dot_local/lib/dotfiles/canonical_desktop.py @@ -10,13 +10,18 @@ from pathlib import Path HOME = Path.home() STATE = HOME / ".local/state/dotfiles/gnome-settings.json" EXTENSIONS = [ - "paperwm@paperwm.github.com", + "o-tiling@oliwebd.github.com", "copyous@boerdereinar.dev", "emoji-copy@felipeftn", "Vitals@CoreCoding.com", - "corporate-panel@dotfiles", "ubuntu-appindicators@ubuntu.com", ] +DISABLED_EXTENSIONS = [ + "paperwm@paperwm.github.com", + "ubuntu-dock@ubuntu.com", + "tiling-assistant@ubuntu.com", + "corporate-panel@dotfiles", +] def save_state(saved: dict) -> None: @@ -119,8 +124,20 @@ def shortcuts(saved: dict) -> None: merge_key(schema, "custom-keybindings", paths, saved) +def extensions(saved: dict) -> None: + schema = "org.gnome.shell" + settings = settings_object(schema) + if settings is None: + return + for key, added, removed in [ + ("enabled-extensions", EXTENSIONS, DISABLED_EXTENSIONS), + ("disabled-extensions", DISABLED_EXTENSIONS, EXTENSIONS), + ]: + values = [value for value in settings.get_strv(key) if value not in removed] + write_key(schema, key, list(dict.fromkeys([*values, *added])), saved) + + def apply_settings(saved: dict) -> None: - merge_key("org.gnome.shell", "enabled-extensions", EXTENSIONS, saved) merge_key( "org.gnome.desktop.input-sources", "xkb-options", @@ -138,26 +155,47 @@ def apply_settings(saved: dict) -> None: write_key( "org.gnome.desktop.wm.keybindings", "toggle-fullscreen", ["<Super>f"], saved ) - paper = "org.gnome.shell.extensions.paperwm.keybindings" + tiling(saved) + shortcuts(saved) + workspaces(saved) + panel(saved) + extensions(saved) + + +def tiling(saved: dict) -> None: + schema = "org.gnome.shell.extensions.o-tiling" + for key, value in { + "tile-by-default": True, + "new-window-placement": "focused", + "active-hint-overlay-enabled": False, + "workspace-switcher-style": False, + "workspace-number-indicator": False, + "panel-transparency": False, + "mouse-cursor-follows-active-window": False, + "toggle-floating": ["<Super><Shift>space"], + }.items(): + write_key(schema, key, value, saved) for key in [ - "new-window", - "take-window", - "toggle-maximize-width", - "slurp-in", - "barf-out-active", - "cycle-height", + "tile-enter", + "toggle-tiling", + "tile-orientation", + "pop-workspace-up", + "pop-workspace-down", ]: - write_key(paper, key, [], saved) + write_key(schema, key, [], saved) for direction, letter in zip(["left", "down", "up", "right"], "hjkl"): binding = "<Super>Right" if direction == "right" else f"<Super>{letter}" - write_key(paper, f"switch-{direction}", [binding], saved) - write_key(paper, f"move-{direction}", [f"<Super><Shift>{letter}"], saved) - shortcuts(saved) - workspaces(saved) - panel(saved) + write_key(schema, f"focus-{direction}", [binding], saved) + write_key( + schema, f"tile-move-{direction}-global", [f"<Super><Shift>{letter}"], saved + ) def panel(saved: dict) -> None: + emoji = "org.gnome.shell.extensions.emoji-copy" + write_key(emoji, "always-show", False, saved) + write_key(emoji, "active-keybind", True, saved) + write_key(emoji, "emoji-keybind", ["<Super>period"], saved) schema = "org.gnome.shell.extensions.vitals" monitor = shlex.join( ["/snap/bin/ghostty", "-e", str(HOME / ".nix-profile/bin/htop")] @@ -183,7 +221,6 @@ def panel(saved: dict) -> None: def workspaces(saved: dict) -> None: write_key("org.gnome.mutter", "dynamic-workspaces", False, saved) write_key("org.gnome.desktop.wm.preferences", "num-workspaces", 10, saved) - write_key("org.gnome.shell.extensions.dash-to-dock", "hot-keys", False, saved) for number in range(1, 11): key = str(number % 10) for action, modifier in [("switch", ""), ("move", "<Shift>")]: diff --git a/meta/canonical/extensions.txt b/meta/canonical/extensions.txt index 7844496..dab1321 100644 --- a/meta/canonical/extensions.txt +++ b/meta/canonical/extensions.txt @@ -1,4 +1,4 @@ -paperwm@paperwm.github.com +o-tiling@oliwebd.github.com copyous@boerdereinar.dev emoji-copy@felipeftn Vitals@CoreCoding.com diff --git a/scripts/canonical-vm-guest.sh b/scripts/canonical-vm-guest.sh index 29caa7d..647fd0a 100644 --- a/scripts/canonical-vm-guest.sh +++ b/scripts/canonical-vm-guest.sh @@ -12,11 +12,11 @@ cd "$HOME/dotfiles" check_extensions() { test "$(/usr/bin/gsettings get org.gnome.shell disable-user-extensions)" = false - for extension in corporate-panel@dotfiles $(sed '/^#/d; /^$/d' meta/canonical/extensions.txt); do + while IFS= read -r extension; do gnome-extensions info "$extension" gnome-extensions list --enabled | grep -Fx "$extension" gnome-extensions info "$extension" | grep -Eq 'State: (ACTIVE|ENABLED)$' - done + done < <(sed '/^#/d; /^$/d' meta/canonical/extensions.txt) } check_mattermost_keyring() { diff --git a/tests/test_canonical_desktop.py b/tests/test_canonical_desktop.py index 9bdace1..fac6b8e 100644 --- a/tests/test_canonical_desktop.py +++ b/tests/test_canonical_desktop.py @@ -14,6 +14,46 @@ SPEC.loader.exec_module(desktop) class DesktopTests(unittest.TestCase): + def test_extensions_replace_tiler_and_hide_dock_and_status(self): + settings = MagicMock() + settings.get_strv.side_effect = [ + ["company", "paperwm@paperwm.github.com", "corporate-panel@dotfiles"], + ["unrelated", "o-tiling@oliwebd.github.com"], + ] + with ( + patch.object(desktop, "settings_object", return_value=settings), + patch.object(desktop, "write_key") as write, + ): + desktop.extensions({}) + write.assert_any_call( + "org.gnome.shell", + "enabled-extensions", + ["company", *desktop.EXTENSIONS], + {}, + ) + write.assert_any_call( + "org.gnome.shell", + "disabled-extensions", + ["unrelated", *desktop.DISABLED_EXTENSIONS], + {}, + ) + + def test_tiling_shortcuts_do_not_claim_application_keys(self): + with patch.object(desktop, "write_key") as write: + desktop.tiling({}) + schema = "org.gnome.shell.extensions.o-tiling" + for key in [ + "tile-enter", + "toggle-tiling", + "tile-orientation", + "pop-workspace-up", + "pop-workspace-down", + ]: + write.assert_any_call(schema, key, [], {}) + write.assert_any_call(schema, "new-window-placement", "focused", {}) + write.assert_any_call(schema, "focus-right", ["<Super>Right"], {}) + write.assert_any_call(schema, "tile-move-down-global", ["<Super><Shift>j"], {}) + def test_clipboard_shortcut_preserves_other_display_bindings(self): settings = MagicMock() settings.get_strv.return_value = ["<Super>p", "XF86Display", "<Super>x"] @@ -35,8 +75,11 @@ class DesktopTests(unittest.TestCase): write.assert_any_call( "org.gnome.shell.extensions.vitals", "include-public-ip", False, {} ) - self.assertIn("corporate-panel@dotfiles", desktop.EXTENSIONS) + self.assertNotIn("corporate-panel@dotfiles", desktop.EXTENSIONS) self.assertIn("ubuntu-appindicators@ubuntu.com", desktop.EXTENSIONS) + write.assert_any_call( + "org.gnome.shell.extensions.emoji-copy", "always-show", False, {} + ) def test_unknown_keys_fail_without_writing(self): settings = MagicMock() @@ -50,6 +93,7 @@ class DesktopTests(unittest.TestCase): def test_launcher_uses_gnome_application_view_key(self): with ( + patch.object(desktop, "extensions"), patch.object(desktop, "merge_key"), patch.object(desktop, "shortcuts"), patch.object(desktop, "workspaces"), |
