aboutsummaryrefslogtreecommitdiff
path: root/docs/design/2026-07-02-file-manager-swallow-spec.org
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-04 12:38:10 -0500
committerCraig Jennings <c@cjennings.net>2026-07-04 12:38:10 -0500
commit140e3b9f0757b8bc413c4b13f060f24f5eebd03c (patch)
tree91a305410cd89455c10f1a130ef536ddbc38ed53 /docs/design/2026-07-02-file-manager-swallow-spec.org
parent6d25b93d442c1ae74bf1bdc9e7aecc2374868b87 (diff)
downloadarchsetup-140e3b9f0757b8bc413c4b13f060f24f5eebd03c.tar.gz
archsetup-140e3b9f0757b8bc413c4b13f060f24f5eebd03c.zip
docs(spec): sort formal specs into docs/specs/ with lifecycle status
Moved the seven formal specs from docs/design/ into docs/specs/, each stamped with a lifecycle status heading: four IMPLEMENTED (bluetooth, net-other-interfaces, audio, instrument-console), one CANCELLED (file-manager-swallow), two DRAFT (desktop-settings, timer). Rewrote the seven todo.org links to the new paths. The two -spec.org files without the spec spine (waybar-network-module, waybar-timer-module) stayed in docs/design/ as notes.
Diffstat (limited to 'docs/design/2026-07-02-file-manager-swallow-spec.org')
-rw-r--r--docs/design/2026-07-02-file-manager-swallow-spec.org141
1 files changed, 0 insertions, 141 deletions
diff --git a/docs/design/2026-07-02-file-manager-swallow-spec.org b/docs/design/2026-07-02-file-manager-swallow-spec.org
deleted file mode 100644
index 4c61be1..0000000
--- a/docs/design/2026-07-02-file-manager-swallow-spec.org
+++ /dev/null
@@ -1,141 +0,0 @@
-#+TITLE: File-Manager Swallow Pattern
-#+AUTHOR: Craig Jennings
-#+DATE: 2026-07-02
-#+TODO: TODO | DONE
-#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED
-
-* CANCELLED Status
-:PROPERTIES:
-:ID: d92e0074-f594-4e83-81a0-faf282e15ed0
-:END:
-- [2026-07-02 Thu] CANCELLED — targeted the wrong file manager. Craig's ask
- is about the dirvish popup (Super+F, an Emacs frame), not nautilus (the
- Super+Shift+F bind that misled the grounding). For dirvish the right
- design is elisp-side and strictly better: Emacs is the launcher, so it
- can spawn the handler directly (=start-process=), hide the popup frame,
- and restore it from a process sentinel — exact exit tracking plus a
- failure notify, no window-event heuristics. Reassigned to .emacs.d via
- its inbox (2026-07-02-2231-from-archsetup-dirvish-popup-swallow-handoff).
- The gio double-fork finding below stands for any gio-launching file
- manager; the daemon design is kept for reference only.
-- [2026-07-02 Thu] DRAFT — initial spec from Craig's roam capture ("when the
- file manager launches another app, it should hide and return when that
- process ends"). Feasibility ground truth sampled live on velox same
- evening: Hyprland's native swallow cannot work here (see Problem), so the
- design is an event-listener daemon.
-
-* Metadata
-
-| Field | Value |
-|--------+----------------------------------------------------|
-| Status | cancelled |
-|--------+----------------------------------------------------|
-| Owner | Craig Jennings |
-|--------+----------------------------------------------------|
-| Repo | dotfiles (daemon + config); archsetup (none) |
-|--------+----------------------------------------------------|
-| Kin | touchpad-auto (socket-listener donor), |
-| | hypr-refocus-scratchpad (event-daemon sibling) |
-|--------+----------------------------------------------------|
-
-* Problem
-
-Opening a file from nautilus (Super+Shift+F, tiled, class
-=org.gnome.Nautilus=) spawns a viewer window while nautilus stays in the
-layout. The wanted behavior is the swallow pattern: the file manager hides
-while the app it launched runs, and returns when that app exits. Today
-there's no signal connecting the two windows — the viewer lands wherever
-the layout puts it, nautilus lingers, and quitting is manual.
-
-*Hyprland's native swallow is ruled out — measured, not assumed.*
-=misc:enable_swallow= + =swallow_regex= would be exactly this feature in two
-config lines, but it matches by walking the new window's PID ancestry to
-the swallow candidate's PID. Nautilus launches handlers through GLib
-(=g_app_info_launch_default_for_uri=), and that path orphans the child:
-reproduced live on velox 2026-07-02 with a python-gi launcher — feh came up
-with PPID 1 (reparented to init) while the launcher was still alive. The
-ancestry walk hits init before it hits nautilus, every time, for every
-handler. Any design that depends on PID parentage is dead on arrival; the
-signal has to come from window events instead.
-
-Ground truth on handlers (velox, 2026-07-02): pdf → zathura, image → feh,
-video → mpv, text/code → emacsclient (window belongs to the emacs daemon).
-Side-note, out of scope here: feh is X11 — an XWayland viewer on a
-no-XWayland-by-preference setup; a default-handler review is its own task.
-
-* Goals
-
-- Double-click a file in nautilus → the viewer takes its place; nautilus is
- gone (special workspace, not killed — state and tabs survive).
-- Quit the viewer → nautilus returns and has focus.
-- Nothing else changes: terminals, scratchpads, and every other window keep
- their current behavior.
-- Config-driven, testable logic, one small daemon — the touchpad-auto shape.
-
-* Design sketch
-
-A =hypr-swallow= daemon (dotfiles, =hyprland/.local/bin/=) listening on the
-Hyprland IPC event socket (socket2), same as =touchpad-auto=:
-
-- Track the active window (=activewindow>>= events carry class + title;
- =activewindowv2>>= carries the address).
-- On =openwindow>>= (address, workspace, class, title) while the active
- window's class is a configured *parent* (nautilus): dispatch
- =movetoworkspacesilent special:swallow,address:0x<parent>=, record
- child-address → {parent-address, origin workspace}.
-- On =closewindow>>= of a recorded child: bring the parent back
- (=movetoworkspace=) and focus it; drop the record.
-- On =closewindow>>= of a hidden parent (nautilus quit while hidden): drop
- the record, nothing to restore.
-- Exception classes (fuzzel, dunst, scratchpad classes, the panels) never
- trigger a swallow even when they open over nautilus.
-- Pure event-machine core (parse lines → state transitions → dispatch list),
- unit-tested against recorded event streams; a thin socket loop around it.
-
-Known edge, handled: Super+Shift+F while nautilus is hidden re-runs
-=nautilus=, which activates the existing (hidden) instance instead of
-opening a window. The daemon (or the bind) must restore-and-untrack in that
-case so the bind never appears dead.
-
-Known limitation, accepted: the emacsclient case never swallows — the
-window belongs to the long-running emacs daemon and =closewindow= for it
-means a frame closed, not "the file is done." The parent-class trigger plus
-exception list naturally leaves it alone only if we exclude it explicitly —
-see decision 2.
-
-* Decisions (Craig)
-
-** TODO Trigger breadth: any new window while nautilus is active, or an allowlist of viewer classes?
-"Any window" is simple and catches every handler, but a false positive
-exists: an app you launched seconds earlier from elsewhere finishes starting
-while you're focused on nautilus → nautilus gets swallowed by an unrelated
-window. An allowlist (zathura, mpv, imv, feh, …) can't be surprised but
-needs maintaining. Recommendation: any-window + exception list — the false
-positive is rare and self-healing (close the window or refocus).
-
-** TODO The emacs frame case: swallow or exempt?
-Opening a text file from nautilus raises/creates an emacs frame. Swallowing
-nautilus under it "works" going in, but the restore fires when *any* frame
-closes, which may be much later or never. Recommendation: exempt =emacs= —
-text files just open, nautilus stays.
-
-** TODO Restore destination: the workspace nautilus came from, or the one you're on when the viewer closes?
-If you move the viewer to another workspace and quit it there, "origin"
-teleports you back; "current" brings nautilus to you. Recommendation:
-current workspace — the restore should land where your attention is.
-
-** TODO Multiple children: refcount or single-slot?
-You can only launch a second file after restoring nautilus manually, so
-overlap is rare — but a fast double-launch can record two children.
-Recommendation: refcount — restore when the last tracked child closes.
-
-* Implementation phases
-
-1. =hypr-swallow= core: pure event-machine (TDD over recorded event
- streams; fake hyprctl for dispatch assertions), config block at the top
- (parent classes, exception classes), unittest suite in =tests/=.
-2. Socket loop + wiring: exec-once in hyprland.conf, the Super+Shift+F
- restore-if-hidden interplay, daemon single-instance guard.
-3. Live verification on velox (zathura + mpv round-trips, the emacs case,
- the false-positive probe) + manual-testing entries; ratio rides the
- dotfiles pull.