feat: minimize and restore windows the way a desktop does

The scratchpad technically minimized Chrome, but sway hands scratchpad
windows back floating, so restoring gave a draggable window instead of
the maximized one the user put away. Park windows on a hidden workspace
as ordinary tiled containers instead, and move them back to the visible
workspace on restore.

classroom-window carries the whole state machine, so the dock buttons
and Super+M share one implementation:

  not running  -> launch
  unfocused    -> focus
  focused      -> minimize
  minimized    -> restore, tiled and maximized

Fullscreen is dropped on the way out, since a container parked while
still fullscreen keeps drawing over everything else. floating disable on
the way back also rescues windows left floating by the old scratchpad
path.

Super+Shift+M restores the newest minimized window, so nothing can get
stranded on the hidden workspace.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
ekklesia
2026-09-04 17:06:43 -05:00
co-authored by Claude Opus 5
parent d05f5bf909
commit af79aba21b
5 changed files with 366 additions and 41 deletions
+20 -39
View File
@@ -1,44 +1,25 @@
#!/bin/bash
set -e
set -euo pipefail
APP_ID='google-chrome'
HIDDEN_WS='__ekklesia_hidden'
# The dock's Browser button. classroom-window decides whether this is a
# launch, a focus, a minimise or a restore.
#
# Chrome stays a full browser: Wonder Ink is only the starting page, and
# the profile in ~/.config/google-chrome is never touched here so logins
# and cookies survive reboots and updates.
if ! pgrep -x chrome >/dev/null 2>&1 &&
! pgrep -x google-chrome >/dev/null 2>&1; then
exec google-chrome \
HOME_PAGE='https://app.wonderink.org/'
exec /usr/local/bin/classroom-window toggle \
--match 'google-chrome' \
--match 'chrome' \
--match 'chrome-.*' \
--match 'google-chrome-.*' \
-- \
google-chrome \
--ozone-platform=wayland \
--start-maximized \
https://app.wonderink.org/
fi
focused_app="$(
swaymsg -t get_tree -r |
python3 -c '
import json, sys
tree = json.load(sys.stdin)
def find_focused(node):
if node.get("focused"):
return node
for child in node.get("nodes", []) + node.get("floating_nodes", []):
result = find_focused(child)
if result:
return result
return None
node = find_focused(tree)
print((node or {}).get("app_id") or "")
'
)"
if [ "$focused_app" = "$APP_ID" ]; then
swaymsg '[app_id="google-chrome"] move container to workspace __ekklesia_hidden' >/dev/null
exit 0
fi
swaymsg '[app_id="google-chrome"] move container to workspace current' >/dev/null 2>&1 || true
swaymsg '[app_id="google-chrome"] focus' >/dev/null 2>&1 || true
swaymsg '[app_id="google-chrome"] fullscreen disable' >/dev/null 2>&1 || true
swaymsg '[app_id="google-chrome"] floating disable' >/dev/null 2>&1 || true
--no-first-run \
--no-default-browser-check \
--password-store=basic \
"$HOME_PAGE"