2026-07-12 08:57:10 -04:00
|
|
|
#!/usr/bin/env bash
|
2026-07-14 15:03:07 -04:00
|
|
|
# Compose the Souveraine quickshell config (`qs -c souveraine`).
|
2026-07-12 08:57:10 -04:00
|
|
|
#
|
2026-07-14 15:03:07 -04:00
|
|
|
# Model: ~/.config/quickshell/souveraine is BUILT by this script —
|
|
|
|
|
# - our files (this repo) are symlinked in, repo stays source of truth
|
|
|
|
|
# - untouched upstream directories are borrowed as whole-dir symlinks
|
2026-07-21 17:30:32 -04:00
|
|
|
# into the ii tree (so upstream updates flow through)
|
2026-07-14 15:03:07 -04:00
|
|
|
# - directories where we override any file are composed file-by-file
|
2026-07-21 17:30:32 -04:00
|
|
|
# The ii tree itself is DEPLOYED from this repo (ii-base/, the pinned base —
|
|
|
|
|
# vendored 2026-07-21 after laptop/phone drifted ~900 files): every run
|
|
|
|
|
# rsyncs ii-base -> ~/.config/quickshell/ii, so "borrowed from ii" means
|
|
|
|
|
# borrowed from the same pin on every device. On the phone (aarch64) the
|
|
|
|
|
# ii-phone/ overlay is applied on top — the declared home for phone-only
|
|
|
|
|
# files (Cellular, mobile bar/OSK/wallpaper behavior). Never hand-edit
|
|
|
|
|
# ~/.config/quickshell/ii; change ii-base/ (or ii-phone/) and redeploy.
|
2026-07-14 15:03:07 -04:00
|
|
|
#
|
|
|
|
|
# deploy.sh compose ~/.config/quickshell/souveraine
|
|
|
|
|
# deploy.sh -u remove the souveraine config dir (ii untouched)
|
|
|
|
|
# deploy.sh --legacy-clean remove the OLD overlay symlinks from ii and
|
|
|
|
|
# restore its .upstream backups (one-time migration)
|
2026-07-31 12:58:52 -04:00
|
|
|
# deploy.sh --phone copy this surface to the phone and deploy there.
|
|
|
|
|
# NEVER deletes on the device: it snapshots the phone's
|
|
|
|
|
# tree first, copies by checksum, and only *reports*
|
|
|
|
|
# files that exist solely on the phone.
|
2026-07-12 08:57:10 -04:00
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
2026-08-09 19:31:07 -04:00
|
|
|
REPO_ROOT="$(cd "$SRC/../.." && pwd)"
|
|
|
|
|
FACE_SRC="$REPO_ROOT/assets/face"
|
2026-07-12 08:57:10 -04:00
|
|
|
QS="${HOME}/.config/quickshell"
|
|
|
|
|
II="${QS}/ii"
|
2026-07-14 15:03:07 -04:00
|
|
|
SV="${QS}/souveraine"
|
2026-07-21 18:55:02 -04:00
|
|
|
TARGET_ARCH="${SOUVERAINE_TARGET_ARCH:-$(uname -m)}"
|
2026-07-12 08:57:10 -04:00
|
|
|
|
2026-08-12 13:24:34 -04:00
|
|
|
# ── Which edits are live, and which are inert ────────────────────────────
|
|
|
|
|
# Our manifest entries are composed as SYMLINKS into this repo, so the repo IS
|
|
|
|
|
# the running shell for every file listed below. quickshell reloads on write.
|
|
|
|
|
# The consequence, and it is the thing to know before touching anything here:
|
|
|
|
|
#
|
|
|
|
|
# - Adding a NEW file + its MANIFEST line is INERT. The composed tree gains
|
|
|
|
|
# nothing until the next deploy.sh run. Build whole modules this way.
|
|
|
|
|
# - Editing a file ALREADY in the manifest is LIVE, immediately, in the
|
|
|
|
|
# running shell — no deploy needed, no confirmation, no undo.
|
|
|
|
|
#
|
|
|
|
|
# So registration is the dangerous act, not creation. A QML file that fails to
|
|
|
|
|
# compile takes its whole module with it (a QtObject root with no default
|
|
|
|
|
# property failed all of qs.services once, 29ec9fe), and quickshell will not
|
|
|
|
|
# restart a config that crashes within 10s of launch — the bar simply stays
|
|
|
|
|
# gone. Restarting the shell also re-locks the session via PAM, so it costs
|
|
|
|
|
# the human a password.
|
|
|
|
|
#
|
|
|
|
|
# Practice: write patches into surfaces/quickshell/patches/ and let the human
|
|
|
|
|
# apply and reload on their own beat; edit in a detached git worktree, never in
|
|
|
|
|
# this tree. TASK-72's AgentPaneHost makes this structural — a candidate pane
|
|
|
|
|
# loads beside the active one and only swaps on its ready signal — after which
|
|
|
|
|
# a bad edit leaves the working pane drawn instead of taking the surface down.
|
|
|
|
|
#
|
2026-07-12 08:57:10 -04:00
|
|
|
# Manifest: "<repo-relative> <target-relative-to-~/.config/quickshell>"
|
|
|
|
|
MANIFEST="
|
2026-07-14 15:03:07 -04:00
|
|
|
shell.qml souveraine/shell.qml
|
|
|
|
|
GlobalStates.qml souveraine/GlobalStates.qml
|
shell: dock reorder, fullscreen detection fix, idle-power, sessiond, misc shell work
- Dock drag-to-reorder for pinned apps (insertion gap, quick-slide vs dwell)
- Fullscreen detection: scan all windows via HyprlandData.windowList
- IdleCoordinator, GlobalStates, Session.qml updates
- Deploy script, qmldir, settings, wallpaper, visualizer fixes
- sessiond server, memory module updates
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 22:18:20 -04:00
|
|
|
SettingsWindow.qml souveraine/SettingsWindow.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
panelFamilies/SouveraineFamily.qml souveraine/panelFamilies/SouveraineFamily.qml
|
|
|
|
|
services/Souveraine.qml souveraine/services/Souveraine.qml
|
2026-08-13 15:27:51 -04:00
|
|
|
services/AccessoryPresentation.qml souveraine/services/AccessoryPresentation.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
services/Ai.qml souveraine/services/Ai.qml
|
2026-07-21 18:55:02 -04:00
|
|
|
services/Cellular.qml souveraine/services/Cellular.qml
|
2026-07-26 16:56:39 -04:00
|
|
|
services/ChargeRate.qml souveraine/services/ChargeRate.qml
|
2026-07-26 20:36:07 -04:00
|
|
|
services/Haptics.qml souveraine/services/Haptics.qml
|
2026-08-09 19:31:07 -04:00
|
|
|
services/HidController.qml souveraine/services/HidController.qml
|
2026-07-29 17:47:20 -04:00
|
|
|
services/Selection.qml souveraine/services/Selection.qml
|
|
|
|
|
services/DeviceEvidence.qml souveraine/services/DeviceEvidence.qml
|
2026-08-07 14:48:07 -04:00
|
|
|
services/UsbState.qml souveraine/services/UsbState.qml
|
2026-07-26 21:01:11 -04:00
|
|
|
services/Gestures.qml souveraine/services/Gestures.qml
|
2026-07-26 20:36:07 -04:00
|
|
|
modules/souveraine/dial/RadialDial.qml souveraine/modules/souveraine/dial/RadialDial.qml
|
|
|
|
|
modules/souveraine/dial/DialHost.qml souveraine/modules/souveraine/dial/DialHost.qml
|
|
|
|
|
modules/souveraine/dial/qmldir souveraine/modules/souveraine/dial/qmldir
|
2026-07-29 17:47:20 -04:00
|
|
|
modules/souveraine/selection/SelectionHost.qml souveraine/modules/souveraine/selection/SelectionHost.qml
|
|
|
|
|
modules/souveraine/selection/SelectionChip.qml souveraine/modules/souveraine/selection/SelectionChip.qml
|
|
|
|
|
modules/souveraine/selection/SelectionActions.qml souveraine/modules/souveraine/selection/SelectionActions.qml
|
|
|
|
|
modules/souveraine/selection/SelectionAction.qml souveraine/modules/souveraine/selection/SelectionAction.qml
|
|
|
|
|
modules/souveraine/selection/SelectionSeparator.qml souveraine/modules/souveraine/selection/SelectionSeparator.qml
|
|
|
|
|
modules/souveraine/selection/qmldir souveraine/modules/souveraine/selection/qmldir
|
2026-08-05 14:12:22 -04:00
|
|
|
modules/souveraine/windowSheet/WindowSheet.qml souveraine/modules/souveraine/windowSheet/WindowSheet.qml
|
|
|
|
|
modules/souveraine/windowSheet/SheetButton.qml souveraine/modules/souveraine/windowSheet/SheetButton.qml
|
2026-08-17 10:28:26 -04:00
|
|
|
modules/souveraine/windowSheet/WindowResize.qml souveraine/modules/souveraine/windowSheet/WindowResize.qml
|
|
|
|
|
modules/souveraine/windowSheet/PoseRamp.qml souveraine/modules/souveraine/windowSheet/PoseRamp.qml
|
2026-08-05 14:37:56 -04:00
|
|
|
modules/souveraine/windowSheet/PowerMenu.qml souveraine/modules/souveraine/windowSheet/PowerMenu.qml
|
2026-08-07 14:48:07 -04:00
|
|
|
modules/souveraine/windowSheet/PowerOptionRow.qml souveraine/modules/souveraine/windowSheet/PowerOptionRow.qml
|
2026-08-05 14:12:22 -04:00
|
|
|
modules/souveraine/windowSheet/qmldir souveraine/modules/souveraine/windowSheet/qmldir
|
2026-08-13 15:27:51 -04:00
|
|
|
modules/souveraine/airpods/AirPodsSurface.qml souveraine/modules/souveraine/airpods/AirPodsSurface.qml
|
|
|
|
|
modules/souveraine/airpods/qmldir souveraine/modules/souveraine/airpods/qmldir
|
2026-08-05 19:39:50 -04:00
|
|
|
services/Face.qml souveraine/services/Face.qml
|
2026-08-10 21:24:23 -04:00
|
|
|
services/Lens.qml souveraine/services/Lens.qml
|
2026-08-10 20:46:28 -04:00
|
|
|
services/FingerprintPreview.qml souveraine/services/FingerprintPreview.qml
|
2026-08-05 14:12:22 -04:00
|
|
|
services/ViewtopControl.qml souveraine/services/ViewtopControl.qml
|
2026-08-07 12:26:12 -04:00
|
|
|
services/ZoneTransition.qml souveraine/services/ZoneTransition.qml
|
2026-08-06 18:57:01 -04:00
|
|
|
services/Hyprsunset.qml souveraine/services/Hyprsunset.qml
|
2026-07-30 15:26:20 -04:00
|
|
|
modules/souveraine/subconscious/SubconsciousTicker.qml souveraine/modules/souveraine/subconscious/SubconsciousTicker.qml
|
|
|
|
|
modules/souveraine/subconscious/SubconsciousEventPanel.qml souveraine/modules/souveraine/subconscious/SubconsciousEventPanel.qml
|
|
|
|
|
modules/souveraine/subconscious/qmldir souveraine/modules/souveraine/subconscious/qmldir
|
2026-07-21 18:55:02 -04:00
|
|
|
services/Network.qml souveraine/services/Network.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
services/TaskbarApps.qml souveraine/services/TaskbarApps.qml
|
|
|
|
|
services/GlobalFocusGrab.qml souveraine/services/GlobalFocusGrab.qml
|
|
|
|
|
services/Idle.qml souveraine/services/Idle.qml
|
quickshell: first-party lock/nav/session layer, retire the pill
Grows Souveraine's own surfaces on top of the borrowed ii shell and drops
the separate pill shell in favor of one integrated navigation rail.
Session arbiter (functions/Session.qml): probe logind's Can* methods over
busctl instead of guessing from installed binaries -- the answer carries the
polkit tier (yes/challenge/na), so a swapless phone reports hibernate as na
and refuses honestly rather than firing a verb that no-ops. Verbs run through
a Process that logs exit codes and tracks lastAction; refusals log too. The
busctl output is parsed with awk, not a sed regex buried under four escaping
layers -- the sed version returned nothing on the phone and left every
capability stuck at "unknown" (invisible on the laptop, where timing masked
it). Every structured result is JSON-over-string; quickshell maps a var
return to void.
Lock trust: screenLocked (the shell's lock request) is now distinct from
screenLockSecure (WlSessionLock.secure, the compositor's acknowledgement,
mirrored from LockScreen). Cards that disclose personal data gate on secure,
not on a button press. LockContentPolicy centralizes the ambient/personal/
step-up tiers so no card grows its own private rule.
New first-party namespace modules/souveraine/: LockMediaCard, LockSurfaceHost,
SystemGestureRail -- owned surfaces, not ii patches. IdleCoordinator gives one
staged idle vocabulary (dim/lock) gated behind nativeCoordinatorEnabled, off
until the native Wayland idle-notify is verified on the Pixel compositor;
hypridle stays the adapter. WallpaperAssets selects aspect-aware variants for
phone-vs-laptop display shapes.
Pill retired: pill/shell.qml and PillConfig gone, replaced by NavigationConfig
and the gesture rail. Hyprland starts qs -c souveraine directly; no secondary
shell, no qsConfig flip.
Verified on the phone: session.* reports challenge/na correctly, hibernate
and unlock refuse, inhibit round-trips with its reason.
2026-07-14 20:00:57 -04:00
|
|
|
services/IdleCoordinator.qml souveraine/services/IdleCoordinator.qml
|
|
|
|
|
services/LockContentPolicy.qml souveraine/services/LockContentPolicy.qml
|
|
|
|
|
services/WallpaperAssets.qml souveraine/services/WallpaperAssets.qml
|
2026-07-21 17:30:32 -04:00
|
|
|
services/WallpaperDownload.qml souveraine/services/WallpaperDownload.qml
|
|
|
|
|
scripts/wallpaper/download_wallhaven.sh souveraine/scripts/wallpaper/download_wallhaven.sh
|
2026-07-14 15:03:07 -04:00
|
|
|
services/ConflictKiller.qml souveraine/services/ConflictKiller.qml
|
2026-08-11 14:40:07 -04:00
|
|
|
services/AgentSessions.qml souveraine/services/AgentSessions.qml
|
|
|
|
|
scripts/agent/agent-sessions.sh souveraine/scripts/agent/agent-sessions.sh
|
|
|
|
|
modules/souveraine/island/Island.qml souveraine/modules/souveraine/island/Island.qml
|
|
|
|
|
modules/souveraine/island/IslandExpansion.qml souveraine/modules/souveraine/island/IslandExpansion.qml
|
|
|
|
|
modules/souveraine/island/qmldir souveraine/modules/souveraine/island/qmldir
|
2026-08-12 12:55:11 -04:00
|
|
|
modules/souveraine/agent/ToolVocabulary.qml souveraine/modules/souveraine/agent/ToolVocabulary.qml
|
|
|
|
|
modules/souveraine/agent/ToolCard.qml souveraine/modules/souveraine/agent/ToolCard.qml
|
2026-08-12 13:11:19 -04:00
|
|
|
modules/souveraine/agent/ThinkingCard.qml souveraine/modules/souveraine/agent/ThinkingCard.qml
|
|
|
|
|
modules/souveraine/agent/AgentMessage.qml souveraine/modules/souveraine/agent/AgentMessage.qml
|
2026-08-17 11:51:43 -04:00
|
|
|
modules/souveraine/agent/AgentPaneMenu.qml souveraine/modules/souveraine/agent/AgentPaneMenu.qml
|
|
|
|
|
modules/souveraine/agent/ConversationMenu.qml souveraine/modules/souveraine/agent/ConversationMenu.qml
|
|
|
|
|
modules/souveraine/agent/ItineraryRibbon.qml souveraine/modules/souveraine/agent/ItineraryRibbon.qml
|
2026-08-12 12:55:11 -04:00
|
|
|
modules/souveraine/agent/qmldir souveraine/modules/souveraine/agent/qmldir
|
2026-07-14 20:48:46 -04:00
|
|
|
services/SessionEvents.qml souveraine/services/SessionEvents.qml
|
2026-07-16 20:32:46 -04:00
|
|
|
services/SessiondBridge.qml souveraine/services/SessiondBridge.qml
|
2026-07-25 18:53:19 -04:00
|
|
|
services/SessiondPolicy.qml souveraine/services/SessiondPolicy.qml
|
2026-07-14 20:48:46 -04:00
|
|
|
services/StepUpAuth.qml souveraine/services/StepUpAuth.qml
|
2026-07-14 21:31:23 -04:00
|
|
|
services/SessionAudit.qml souveraine/services/SessionAudit.qml
|
2026-07-21 13:28:47 -04:00
|
|
|
services/Speech.qml souveraine/services/Speech.qml
|
2026-07-21 08:17:54 -04:00
|
|
|
services/NotifyEvents.qml souveraine/services/NotifyEvents.qml
|
2026-07-21 08:40:00 -04:00
|
|
|
services/CrashReporter.qml souveraine/services/CrashReporter.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
modules/ii/sidebarLeft/SidebarLeft.qml souveraine/modules/ii/sidebarLeft/SidebarLeft.qml
|
|
|
|
|
modules/ii/sidebarLeft/AiChat.qml souveraine/modules/ii/sidebarLeft/AiChat.qml
|
|
|
|
|
modules/ii/sidebarRight/SidebarRight.qml souveraine/modules/ii/sidebarRight/SidebarRight.qml
|
shell: dock reorder, fullscreen detection fix, idle-power, sessiond, misc shell work
- Dock drag-to-reorder for pinned apps (insertion gap, quick-slide vs dwell)
- Fullscreen detection: scan all windows via HyprlandData.windowList
- IdleCoordinator, GlobalStates, Session.qml updates
- Deploy script, qmldir, settings, wallpaper, visualizer fixes
- sessiond server, memory module updates
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 22:18:20 -04:00
|
|
|
modules/ii/sidebarRight/SidebarRightContent.qml souveraine/modules/ii/sidebarRight/SidebarRightContent.qml
|
2026-07-21 18:55:02 -04:00
|
|
|
modules/ii/sidebarRight/QuickSliders.qml souveraine/modules/ii/sidebarRight/QuickSliders.qml
|
2026-08-10 21:24:23 -04:00
|
|
|
modules/ii/sidebarRight/CenterWidgetGroup.qml souveraine/modules/ii/sidebarRight/CenterWidgetGroup.qml
|
|
|
|
|
modules/ii/sidebarRight/lens/LensWidget.qml souveraine/modules/ii/sidebarRight/lens/LensWidget.qml
|
2026-07-14 20:48:46 -04:00
|
|
|
modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml souveraine/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml
|
2026-07-18 15:05:17 -04:00
|
|
|
modules/ii/bar/UtilButtons.qml souveraine/modules/ii/bar/UtilButtons.qml
|
2026-08-11 14:40:07 -04:00
|
|
|
modules/ii/bar/BarContent.qml souveraine/modules/ii/bar/BarContent.qml
|
|
|
|
|
modules/ii/bar/ClockWidget.qml souveraine/modules/ii/bar/ClockWidget.qml
|
2026-08-11 18:40:35 -04:00
|
|
|
modules/ii/bar/Resources.qml souveraine/modules/ii/bar/Resources.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
modules/common/Config.qml souveraine/modules/common/Config.qml
|
|
|
|
|
modules/common/ShellModel.qml souveraine/modules/common/ShellModel.qml
|
quickshell: session arbiter + fix IPC returns silently dropping payloads
Fork ii's Session singleton and add a sessionctl.* surface.
The IPC bug is the important half. Quickshell marshals exactly five types
(string/int/bool/double/color) and maps a `var` return to VOID, discarding
the payload with no error -- src/io/ipc.cpp, "void and var get mixed by qml
engine". dock.*, shell.* and apps.* were all declared `: var`, so they
registered as `(): void` and returned nothing at all. The {ok, reason}
contract has never once reached a caller. All of them now return JSON as a
string, which is what actually crosses the socket.
Session: upstream fires `systemctl X || loginctl X` detached and throws the
exit code away. Fine on a desktop with someone at the keyboard, not fine on
a phone where the shell is the session manager and a verb that silently does
nothing leaves you believing the machine is suspending when it isn't. So:
probe loginctl/systemctl/hibernate once instead of assuming, run verbs
through a Process that logs the exit code, and refuse honestly when the
machine can't do the thing (the phone has no swap -- hibernate now says so
instead of no-opping). Every upstream verb keeps its name and call sites.
Inhibits carry a mandatory reason and get a cookie; state() lists who is
holding the machine awake and why. "Why didn't it sleep" is now answerable.
unlock() is refused by design -- the lock is the credential gate, so no IPC
caller routes around the PIN pad.
Named sessionctl, not session: ii's SessionScreen already owns "session",
and quickshell drops duplicate targets silently rather than erroring.
Idle: drop the 2>/dev/null and run hypridle through a Process, so a unit
that fails to come back is a log line instead of a flat battery.
Verified on the laptop: inhibit stops hypridle, uninhibit brings it back.
2026-07-14 18:31:59 -04:00
|
|
|
modules/common/functions/Session.qml souveraine/modules/common/functions/Session.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
modules/common/widgets/ContentPage.qml souveraine/modules/common/widgets/ContentPage.qml
|
2026-07-26 17:39:38 -04:00
|
|
|
modules/common/widgets/FullscreenPolkitWindow.qml souveraine/modules/common/widgets/FullscreenPolkitWindow.qml
|
2026-08-05 14:12:22 -04:00
|
|
|
modules/common/widgets/FloatingActionButton.qml souveraine/modules/common/widgets/FloatingActionButton.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
modules/common/widgets/StyledToolTip.qml souveraine/modules/common/widgets/StyledToolTip.qml
|
|
|
|
|
modules/settings/DeviceConfig.qml souveraine/modules/settings/DeviceConfig.qml
|
shell: dock reorder, fullscreen detection fix, idle-power, sessiond, misc shell work
- Dock drag-to-reorder for pinned apps (insertion gap, quick-slide vs dwell)
- Fullscreen detection: scan all windows via HyprlandData.windowList
- IdleCoordinator, GlobalStates, Session.qml updates
- Deploy script, qmldir, settings, wallpaper, visualizer fixes
- sessiond server, memory module updates
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 22:18:20 -04:00
|
|
|
modules/settings/NetworkConfig.qml souveraine/modules/settings/NetworkConfig.qml
|
|
|
|
|
modules/settings/DisplayConfig.qml souveraine/modules/settings/DisplayConfig.qml
|
|
|
|
|
modules/settings/SoundConfig.qml souveraine/modules/settings/SoundConfig.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
modules/settings/LockConfig.qml souveraine/modules/settings/LockConfig.qml
|
2026-07-19 22:46:01 -04:00
|
|
|
modules/settings/WallpaperConfig.qml souveraine/modules/settings/WallpaperConfig.qml
|
2026-08-05 14:12:22 -04:00
|
|
|
modules/settings/OverviewConfig.qml souveraine/modules/settings/OverviewConfig.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
modules/settings/DockConfig.qml souveraine/modules/settings/DockConfig.qml
|
quickshell: first-party lock/nav/session layer, retire the pill
Grows Souveraine's own surfaces on top of the borrowed ii shell and drops
the separate pill shell in favor of one integrated navigation rail.
Session arbiter (functions/Session.qml): probe logind's Can* methods over
busctl instead of guessing from installed binaries -- the answer carries the
polkit tier (yes/challenge/na), so a swapless phone reports hibernate as na
and refuses honestly rather than firing a verb that no-ops. Verbs run through
a Process that logs exit codes and tracks lastAction; refusals log too. The
busctl output is parsed with awk, not a sed regex buried under four escaping
layers -- the sed version returned nothing on the phone and left every
capability stuck at "unknown" (invisible on the laptop, where timing masked
it). Every structured result is JSON-over-string; quickshell maps a var
return to void.
Lock trust: screenLocked (the shell's lock request) is now distinct from
screenLockSecure (WlSessionLock.secure, the compositor's acknowledgement,
mirrored from LockScreen). Cards that disclose personal data gate on secure,
not on a button press. LockContentPolicy centralizes the ambient/personal/
step-up tiers so no card grows its own private rule.
New first-party namespace modules/souveraine/: LockMediaCard, LockSurfaceHost,
SystemGestureRail -- owned surfaces, not ii patches. IdleCoordinator gives one
staged idle vocabulary (dim/lock) gated behind nativeCoordinatorEnabled, off
until the native Wayland idle-notify is verified on the Pixel compositor;
hypridle stays the adapter. WallpaperAssets selects aspect-aware variants for
phone-vs-laptop display shapes.
Pill retired: pill/shell.qml and PillConfig gone, replaced by NavigationConfig
and the gesture rail. Hyprland starts qs -c souveraine directly; no secondary
shell, no qsConfig flip.
Verified on the phone: session.* reports challenge/na correctly, hibernate
and unlock refuse, inhibit round-trips with its reason.
2026-07-14 20:00:57 -04:00
|
|
|
modules/settings/NavigationConfig.qml souveraine/modules/settings/NavigationConfig.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
modules/settings/KeyboardConfig.qml souveraine/modules/settings/KeyboardConfig.qml
|
2026-07-14 20:17:18 -04:00
|
|
|
modules/settings/IdleConfig.qml souveraine/modules/settings/IdleConfig.qml
|
2026-07-21 09:58:09 -04:00
|
|
|
modules/settings/SpeechConfig.qml souveraine/modules/settings/SpeechConfig.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
modules/ii/polkit/Polkit.qml souveraine/modules/ii/polkit/Polkit.qml
|
2026-08-10 20:46:28 -04:00
|
|
|
modules/ii/polkit/PolkitContent.qml souveraine/modules/ii/polkit/PolkitContent.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
modules/ii/dock/Dock.qml souveraine/modules/ii/dock/Dock.qml
|
|
|
|
|
modules/ii/dock/DockManifest.qml souveraine/modules/ii/dock/DockManifest.qml
|
|
|
|
|
modules/ii/dock/DockApps.qml souveraine/modules/ii/dock/DockApps.qml
|
|
|
|
|
modules/ii/dock/DockAppButton.qml souveraine/modules/ii/dock/DockAppButton.qml
|
|
|
|
|
modules/ii/dock/DockButton.qml souveraine/modules/ii/dock/DockButton.qml
|
|
|
|
|
modules/ii/dock/DockSeparator.qml souveraine/modules/ii/dock/DockSeparator.qml
|
|
|
|
|
modules/ii/dock/DockStack.qml souveraine/modules/ii/dock/DockStack.qml
|
|
|
|
|
modules/ii/appInventory/AppInventory.qml souveraine/modules/ii/appInventory/AppInventory.qml
|
|
|
|
|
modules/ii/appInventory/AppInventoryScope.qml souveraine/modules/ii/appInventory/AppInventoryScope.qml
|
|
|
|
|
modules/ii/overview/Overview.qml souveraine/modules/ii/overview/Overview.qml
|
2026-08-05 14:12:22 -04:00
|
|
|
modules/ii/overview/AppGrid.qml souveraine/modules/ii/overview/AppGrid.qml
|
|
|
|
|
modules/ii/background/widgets/clock/CookieClock.qml souveraine/modules/ii/background/widgets/clock/CookieClock.qml
|
her face on the glass: it draws, it listens, and it gets out of the way
The rig never drew. live2d.js is not only the Cubism runtime — it carries the
reference's character layer, which reads #live_talk every update as its
motion-state slot. Our page omitted the element, so it threw on the first
frame after the model and all four textures had loaded: assets 200, WebGL
live, no error reaching Rust, blank canvas.
--app-id was a no-op. set_prgname ran after GTK had already created the
surface, so the compositor saw souveraine-web and window rules could only
match the title. It has to run before the event loop.
Press and hold her to speak: pw-record at 16k mono s16, release transcribes
through souveraine-stt --file and goes out over Souveraine.send, so the
sidebar logs it. Whisper wraps with newlines and the transcript arrives
through a SplitParser, so it is collapsed here or one utterance becomes four
turns. Double tap dismisses; the clock and quote fade out when she is up and
the clock stops being a target, because a double tap on an invisible object
is not a way back.
The page reports its silhouette and the host installs it as the input region.
Claiming the GL context for preserveDrawingBuffer must happen after the
canvas is sized — before it, the context is born 300x150 and she renders as a
thumbnail in the corner.
Console and errors are forwarded to the host's stdout. Without that a rig
that fails to draw is silent in every direction, which is what cost this
the afternoon.
2026-08-07 00:24:12 -04:00
|
|
|
modules/ii/background/widgets/clock/CookieQuote.qml souveraine/modules/ii/background/widgets/clock/CookieQuote.qml
|
2026-07-21 18:55:02 -04:00
|
|
|
modules/ii/screenCorners/ScreenCorners.qml souveraine/modules/ii/screenCorners/ScreenCorners.qml
|
2026-07-14 15:03:07 -04:00
|
|
|
modules/ii/onScreenKeyboard/OnScreenKeyboard.qml souveraine/modules/ii/onScreenKeyboard/OnScreenKeyboard.qml
|
|
|
|
|
modules/common/Persistent.qml souveraine/modules/common/Persistent.qml
|
|
|
|
|
modules/common/panels/lock/LockScreen.qml souveraine/modules/common/panels/lock/LockScreen.qml
|
|
|
|
|
modules/ii/lock/Lock.qml souveraine/modules/ii/lock/Lock.qml
|
|
|
|
|
modules/ii/lock/TouchLockSurface.qml souveraine/modules/ii/lock/TouchLockSurface.qml
|
quickshell: first-party lock/nav/session layer, retire the pill
Grows Souveraine's own surfaces on top of the borrowed ii shell and drops
the separate pill shell in favor of one integrated navigation rail.
Session arbiter (functions/Session.qml): probe logind's Can* methods over
busctl instead of guessing from installed binaries -- the answer carries the
polkit tier (yes/challenge/na), so a swapless phone reports hibernate as na
and refuses honestly rather than firing a verb that no-ops. Verbs run through
a Process that logs exit codes and tracks lastAction; refusals log too. The
busctl output is parsed with awk, not a sed regex buried under four escaping
layers -- the sed version returned nothing on the phone and left every
capability stuck at "unknown" (invisible on the laptop, where timing masked
it). Every structured result is JSON-over-string; quickshell maps a var
return to void.
Lock trust: screenLocked (the shell's lock request) is now distinct from
screenLockSecure (WlSessionLock.secure, the compositor's acknowledgement,
mirrored from LockScreen). Cards that disclose personal data gate on secure,
not on a button press. LockContentPolicy centralizes the ambient/personal/
step-up tiers so no card grows its own private rule.
New first-party namespace modules/souveraine/: LockMediaCard, LockSurfaceHost,
SystemGestureRail -- owned surfaces, not ii patches. IdleCoordinator gives one
staged idle vocabulary (dim/lock) gated behind nativeCoordinatorEnabled, off
until the native Wayland idle-notify is verified on the Pixel compositor;
hypridle stays the adapter. WallpaperAssets selects aspect-aware variants for
phone-vs-laptop display shapes.
Pill retired: pill/shell.qml and PillConfig gone, replaced by NavigationConfig
and the gesture rail. Hyprland starts qs -c souveraine directly; no secondary
shell, no qsConfig flip.
Verified on the phone: session.* reports challenge/na correctly, hibernate
and unlock refuse, inhibit round-trips with its reason.
2026-07-14 20:00:57 -04:00
|
|
|
modules/souveraine/lock/LockMediaCard.qml souveraine/modules/souveraine/lock/LockMediaCard.qml
|
2026-07-16 21:16:28 -04:00
|
|
|
modules/souveraine/lock/LockAgentCard.qml souveraine/modules/souveraine/lock/LockAgentCard.qml
|
2026-07-21 08:17:54 -04:00
|
|
|
modules/souveraine/lock/LockNotifyCard.qml souveraine/modules/souveraine/lock/LockNotifyCard.qml
|
quickshell: first-party lock/nav/session layer, retire the pill
Grows Souveraine's own surfaces on top of the borrowed ii shell and drops
the separate pill shell in favor of one integrated navigation rail.
Session arbiter (functions/Session.qml): probe logind's Can* methods over
busctl instead of guessing from installed binaries -- the answer carries the
polkit tier (yes/challenge/na), so a swapless phone reports hibernate as na
and refuses honestly rather than firing a verb that no-ops. Verbs run through
a Process that logs exit codes and tracks lastAction; refusals log too. The
busctl output is parsed with awk, not a sed regex buried under four escaping
layers -- the sed version returned nothing on the phone and left every
capability stuck at "unknown" (invisible on the laptop, where timing masked
it). Every structured result is JSON-over-string; quickshell maps a var
return to void.
Lock trust: screenLocked (the shell's lock request) is now distinct from
screenLockSecure (WlSessionLock.secure, the compositor's acknowledgement,
mirrored from LockScreen). Cards that disclose personal data gate on secure,
not on a button press. LockContentPolicy centralizes the ambient/personal/
step-up tiers so no card grows its own private rule.
New first-party namespace modules/souveraine/: LockMediaCard, LockSurfaceHost,
SystemGestureRail -- owned surfaces, not ii patches. IdleCoordinator gives one
staged idle vocabulary (dim/lock) gated behind nativeCoordinatorEnabled, off
until the native Wayland idle-notify is verified on the Pixel compositor;
hypridle stays the adapter. WallpaperAssets selects aspect-aware variants for
phone-vs-laptop display shapes.
Pill retired: pill/shell.qml and PillConfig gone, replaced by NavigationConfig
and the gesture rail. Hyprland starts qs -c souveraine directly; no secondary
shell, no qsConfig flip.
Verified on the phone: session.* reports challenge/na correctly, hibernate
and unlock refuse, inhibit round-trips with its reason.
2026-07-14 20:00:57 -04:00
|
|
|
modules/souveraine/lock/LockSurfaceHost.qml souveraine/modules/souveraine/lock/LockSurfaceHost.qml
|
|
|
|
|
modules/souveraine/lock/qmldir souveraine/modules/souveraine/lock/qmldir
|
|
|
|
|
modules/souveraine/navigation/SystemGestureRail.qml souveraine/modules/souveraine/navigation/SystemGestureRail.qml
|
2026-08-04 09:39:00 -04:00
|
|
|
modules/souveraine/navigation/WindowOverview.qml souveraine/modules/souveraine/navigation/WindowOverview.qml
|
2026-08-05 19:16:41 -04:00
|
|
|
modules/souveraine/navigation/ZoneOverview.qml souveraine/modules/souveraine/navigation/ZoneOverview.qml
|
quickshell: first-party lock/nav/session layer, retire the pill
Grows Souveraine's own surfaces on top of the borrowed ii shell and drops
the separate pill shell in favor of one integrated navigation rail.
Session arbiter (functions/Session.qml): probe logind's Can* methods over
busctl instead of guessing from installed binaries -- the answer carries the
polkit tier (yes/challenge/na), so a swapless phone reports hibernate as na
and refuses honestly rather than firing a verb that no-ops. Verbs run through
a Process that logs exit codes and tracks lastAction; refusals log too. The
busctl output is parsed with awk, not a sed regex buried under four escaping
layers -- the sed version returned nothing on the phone and left every
capability stuck at "unknown" (invisible on the laptop, where timing masked
it). Every structured result is JSON-over-string; quickshell maps a var
return to void.
Lock trust: screenLocked (the shell's lock request) is now distinct from
screenLockSecure (WlSessionLock.secure, the compositor's acknowledgement,
mirrored from LockScreen). Cards that disclose personal data gate on secure,
not on a button press. LockContentPolicy centralizes the ambient/personal/
step-up tiers so no card grows its own private rule.
New first-party namespace modules/souveraine/: LockMediaCard, LockSurfaceHost,
SystemGestureRail -- owned surfaces, not ii patches. IdleCoordinator gives one
staged idle vocabulary (dim/lock) gated behind nativeCoordinatorEnabled, off
until the native Wayland idle-notify is verified on the Pixel compositor;
hypridle stays the adapter. WallpaperAssets selects aspect-aware variants for
phone-vs-laptop display shapes.
Pill retired: pill/shell.qml and PillConfig gone, replaced by NavigationConfig
and the gesture rail. Hyprland starts qs -c souveraine directly; no secondary
shell, no qsConfig flip.
Verified on the phone: session.* reports challenge/na correctly, hibernate
and unlock refuse, inhibit round-trips with its reason.
2026-07-14 20:00:57 -04:00
|
|
|
modules/souveraine/navigation/qmldir souveraine/modules/souveraine/navigation/qmldir
|
2026-07-20 17:51:23 -04:00
|
|
|
modules/souveraine/boot/BootBloom.qml souveraine/modules/souveraine/boot/BootBloom.qml
|
|
|
|
|
modules/souveraine/boot/BootBloom.frag.qsb souveraine/modules/souveraine/boot/BootBloom.frag.qsb
|
|
|
|
|
modules/souveraine/boot/souvie.png souveraine/modules/souveraine/boot/souvie.png
|
|
|
|
|
modules/souveraine/boot/qmldir souveraine/modules/souveraine/boot/qmldir
|
quickshell: first-party lock/nav/session layer, retire the pill
Grows Souveraine's own surfaces on top of the borrowed ii shell and drops
the separate pill shell in favor of one integrated navigation rail.
Session arbiter (functions/Session.qml): probe logind's Can* methods over
busctl instead of guessing from installed binaries -- the answer carries the
polkit tier (yes/challenge/na), so a swapless phone reports hibernate as na
and refuses honestly rather than firing a verb that no-ops. Verbs run through
a Process that logs exit codes and tracks lastAction; refusals log too. The
busctl output is parsed with awk, not a sed regex buried under four escaping
layers -- the sed version returned nothing on the phone and left every
capability stuck at "unknown" (invisible on the laptop, where timing masked
it). Every structured result is JSON-over-string; quickshell maps a var
return to void.
Lock trust: screenLocked (the shell's lock request) is now distinct from
screenLockSecure (WlSessionLock.secure, the compositor's acknowledgement,
mirrored from LockScreen). Cards that disclose personal data gate on secure,
not on a button press. LockContentPolicy centralizes the ambient/personal/
step-up tiers so no card grows its own private rule.
New first-party namespace modules/souveraine/: LockMediaCard, LockSurfaceHost,
SystemGestureRail -- owned surfaces, not ii patches. IdleCoordinator gives one
staged idle vocabulary (dim/lock) gated behind nativeCoordinatorEnabled, off
until the native Wayland idle-notify is verified on the Pixel compositor;
hypridle stays the adapter. WallpaperAssets selects aspect-aware variants for
phone-vs-laptop display shapes.
Pill retired: pill/shell.qml and PillConfig gone, replaced by NavigationConfig
and the gesture rail. Hyprland starts qs -c souveraine directly; no secondary
shell, no qsConfig flip.
Verified on the phone: session.* reports challenge/na correctly, hibernate
and unlock refuse, inhibit round-trips with its reason.
2026-07-14 20:00:57 -04:00
|
|
|
modules/ii/sessionScreen/SessionScreen.qml souveraine/modules/ii/sessionScreen/SessionScreen.qml
|
2026-07-21 13:05:41 -04:00
|
|
|
qmldir souveraine/qmldir
|
|
|
|
|
panelFamilies/qmldir souveraine/panelFamilies/qmldir
|
|
|
|
|
services/qmldir souveraine/services/qmldir
|
|
|
|
|
modules/common/qmldir souveraine/modules/common/qmldir
|
|
|
|
|
modules/common/functions/qmldir souveraine/modules/common/functions/qmldir
|
|
|
|
|
modules/common/panels/lock/qmldir souveraine/modules/common/panels/lock/qmldir
|
|
|
|
|
modules/common/widgets/qmldir souveraine/modules/common/widgets/qmldir
|
|
|
|
|
modules/ii/appInventory/qmldir souveraine/modules/ii/appInventory/qmldir
|
|
|
|
|
modules/ii/bar/qmldir souveraine/modules/ii/bar/qmldir
|
|
|
|
|
modules/ii/dock/qmldir souveraine/modules/ii/dock/qmldir
|
|
|
|
|
modules/ii/lock/qmldir souveraine/modules/ii/lock/qmldir
|
|
|
|
|
modules/ii/onScreenKeyboard/qmldir souveraine/modules/ii/onScreenKeyboard/qmldir
|
|
|
|
|
modules/ii/overview/qmldir souveraine/modules/ii/overview/qmldir
|
|
|
|
|
modules/ii/polkit/qmldir souveraine/modules/ii/polkit/qmldir
|
2026-07-21 18:55:02 -04:00
|
|
|
modules/ii/screenCorners/qmldir souveraine/modules/ii/screenCorners/qmldir
|
2026-07-21 13:05:41 -04:00
|
|
|
modules/ii/sessionScreen/qmldir souveraine/modules/ii/sessionScreen/qmldir
|
|
|
|
|
modules/ii/sidebarLeft/qmldir souveraine/modules/ii/sidebarLeft/qmldir
|
|
|
|
|
modules/ii/sidebarRight/qmldir souveraine/modules/ii/sidebarRight/qmldir
|
|
|
|
|
modules/ii/sidebarRight/volumeMixer/qmldir souveraine/modules/ii/sidebarRight/volumeMixer/qmldir
|
|
|
|
|
modules/settings/qmldir souveraine/modules/settings/qmldir
|
2026-07-12 08:57:10 -04:00
|
|
|
"
|
|
|
|
|
|
2026-07-21 17:30:32 -04:00
|
|
|
# wifi fallback: PHONE_HOST=casey@10.10.20.234 ./deploy.sh --phone
|
|
|
|
|
PHONE_USB="${PHONE_HOST:-casey@172.16.42.1}"
|
2026-07-12 08:57:10 -04:00
|
|
|
PHONE_DEST="souveraine-surfaces/quickshell"
|
|
|
|
|
|
2026-07-14 15:03:07 -04:00
|
|
|
manifest_lines() { printf '%s\n' "$MANIFEST" | sed '/^[[:space:]]*$/d'; }
|
|
|
|
|
|
2026-07-20 17:51:23 -04:00
|
|
|
# Compile the boot bloom shader to .qsb (Qt RHI) if the source is newer. The
|
|
|
|
|
# .qsb is architecture-independent (SPIR-V + reflection), so we build it once
|
|
|
|
|
# here and the manifest symlink / phone rsync carries it — no qsb on the phone.
|
|
|
|
|
compile_shaders() {
|
|
|
|
|
local frag="$SRC/modules/souveraine/boot/BootBloom.frag"
|
|
|
|
|
local qsb_out="$SRC/modules/souveraine/boot/BootBloom.frag.qsb"
|
|
|
|
|
[[ -f "$frag" ]] || return 0
|
|
|
|
|
if [[ ! -f "$qsb_out" || "$frag" -nt "$qsb_out" ]]; then
|
|
|
|
|
local qsb
|
|
|
|
|
qsb="$(command -v qsb || echo /usr/lib/qt6/bin/qsb)"
|
|
|
|
|
if [[ ! -x "$qsb" ]]; then
|
|
|
|
|
echo "ERROR: qsb not found (need qt6-shadertools) to build BootBloom.frag.qsb" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
# --glsl 100es,120,150 + --hlsl/--msl targets so it runs on the Adreno
|
|
|
|
|
# GLES backend the phone uses; -O optimizes.
|
|
|
|
|
"$qsb" --glsl "100es,120,150" --hlsl 50 --msl 12 -O -o "$qsb_out" "$frag"
|
|
|
|
|
echo "compiled BootBloom.frag -> BootBloom.frag.qsb"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
compile_shaders
|
|
|
|
|
|
2026-07-13 13:01:31 -04:00
|
|
|
if [[ "${1:-}" == "--manifest" ]]; then
|
2026-07-14 15:03:07 -04:00
|
|
|
manifest_lines
|
2026-07-13 13:01:31 -04:00
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
2026-07-12 08:57:10 -04:00
|
|
|
if [[ "${1:-}" == "--phone" ]]; then
|
2026-07-21 19:11:51 -04:00
|
|
|
ssh_i=(ssh -F /dev/null -i "$HOME/.ssh/ani" -o ConnectTimeout=5)
|
shell: dock reorder, fullscreen detection fix, idle-power, sessiond, misc shell work
- Dock drag-to-reorder for pinned apps (insertion gap, quick-slide vs dwell)
- Fullscreen detection: scan all windows via HyprlandData.windowList
- IdleCoordinator, GlobalStates, Session.qml updates
- Deploy script, qmldir, settings, wallpaper, visualizer fixes
- sessiond server, memory module updates
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 22:18:20 -04:00
|
|
|
maintenance_active=0
|
|
|
|
|
hypridle_was_active=0
|
|
|
|
|
|
|
|
|
|
phone_lock_state() {
|
|
|
|
|
local state last_secure
|
|
|
|
|
state="$("${ssh_i[@]}" "$PHONE_USB" \
|
|
|
|
|
"qs -c souveraine ipc --any-display call session state 2>/dev/null" || true)"
|
|
|
|
|
if grep -Eq '"locked"[[:space:]]*:[[:space:]]*false' <<< "$state" \
|
|
|
|
|
&& grep -Eq '"lockRequested"[[:space:]]*:[[:space:]]*false' <<< "$state"; then
|
|
|
|
|
printf '%s\n' unlocked
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
if grep -Eq '"locked"[[:space:]]*:[[:space:]]*true|"lockRequested"[[:space:]]*:[[:space:]]*true' <<< "$state"; then
|
|
|
|
|
printf '%s\n' locked
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Bootstrap for a phone that predates the session IPC handler. Every
|
|
|
|
|
# compositor-acknowledged lock edge is journaled by LockScreen.qml.
|
|
|
|
|
last_secure="$("${ssh_i[@]}" "$PHONE_USB" \
|
|
|
|
|
"journalctl --user -u souveraine-shell.service -b --no-pager | sed -n 's/.*\\[lock\\] session lock secure=\\(true\\|false\\).*/\\1/p' | tail -n 1" || true)"
|
|
|
|
|
[[ "$last_secure" == "false" ]] && printf '%s\n' unlocked || printf '%s\n' locked
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
finish_phone_maintenance() {
|
|
|
|
|
(( maintenance_active )) || return 0
|
|
|
|
|
if (( hypridle_was_active )); then
|
|
|
|
|
"${ssh_i[@]}" "$PHONE_USB" \
|
|
|
|
|
"systemctl --user restart hypridle.service; systemctl --user stop souveraine-deploy-lease.timer 2>/dev/null || true; systemctl --user reset-failed souveraine-deploy-lease.service 2>/dev/null || true" \
|
|
|
|
|
>/dev/null || true
|
|
|
|
|
fi
|
|
|
|
|
maintenance_active=0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if [[ "$(phone_lock_state)" != "unlocked" ]]; then
|
|
|
|
|
echo "ERROR: phone session is locked; refusing a live shell deploy" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if "${ssh_i[@]}" "$PHONE_USB" "systemctl --user is-active --quiet hypridle.service"; then
|
|
|
|
|
hypridle_was_active=1
|
|
|
|
|
# Schedule recovery before stopping idle handling. If this deploy or
|
|
|
|
|
# its SSH connection dies, systemd restores hypridle within five
|
|
|
|
|
# minutes. Manual lock remains available throughout the lease.
|
|
|
|
|
"${ssh_i[@]}" "$PHONE_USB" \
|
|
|
|
|
"systemctl --user stop souveraine-deploy-lease.timer 2>/dev/null || true; systemd-run --user --unit=souveraine-deploy-lease --on-active=5min --timer-property=AccuracySec=1s --collect /usr/bin/systemctl --user restart hypridle.service >/dev/null; systemctl --user stop hypridle.service"
|
|
|
|
|
fi
|
|
|
|
|
maintenance_active=1
|
|
|
|
|
trap finish_phone_maintenance EXIT
|
|
|
|
|
|
|
|
|
|
# Close the small check→inhibit race. A manual lock always wins.
|
|
|
|
|
if [[ "$(phone_lock_state)" != "unlocked" ]]; then
|
|
|
|
|
echo "ERROR: phone locked while acquiring the maintenance lease" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
2026-07-21 18:55:02 -04:00
|
|
|
deployed_revision="unknown"
|
|
|
|
|
if repo_root="$(git -C "$SRC" rev-parse --show-toplevel 2>/dev/null)"; then
|
|
|
|
|
deployed_revision="$(git -C "$repo_root" rev-parse HEAD)"
|
|
|
|
|
if ! git -C "$repo_root" diff --quiet -- "$SRC" \
|
|
|
|
|
|| [[ -n "$(git -C "$repo_root" ls-files --others --exclude-standard -- "$SRC")" ]]; then
|
|
|
|
|
deployed_revision="${deployed_revision}-dirty"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
2026-07-12 08:57:10 -04:00
|
|
|
"${ssh_i[@]}" "$PHONE_USB" "mkdir -p ~/$PHONE_DEST"
|
2026-07-31 12:58:52 -04:00
|
|
|
|
|
|
|
|
# NEVER --delete into the phone's tree.
|
|
|
|
|
#
|
|
|
|
|
# `~/souveraine-surfaces` on the device is not a git repo — nothing there has
|
|
|
|
|
# history — and files get edited directly on the phone. This path used to be
|
|
|
|
|
# `rsync -a --delete` with no snapshot, so an on-device edit was destroyed
|
|
|
|
|
# with no record that it had existed. It cost real work more than once, and
|
|
|
|
|
# it is the root of both the ~900-file ii drift and the stevia pkgrels that
|
|
|
|
|
# only ever existed on the phone.
|
|
|
|
|
#
|
|
|
|
|
# Note the asymmetry that made it survive: the LOCAL compose path below
|
|
|
|
|
# snapshots to .ii-previous/.souveraine-previous before it switches
|
|
|
|
|
# anything. Only the phone — the one machine that is a daily driver and has
|
|
|
|
|
# no history — was handled without one.
|
|
|
|
|
#
|
|
|
|
|
# So: snapshot first, copy without deleting, and REPORT what a --delete
|
|
|
|
|
# would have taken. Divergence becomes visible instead of being silently
|
|
|
|
|
# resolved in the laptop's favour.
|
|
|
|
|
PHONE_SNAPSHOT="souveraine-surfaces/.quickshell-previous"
|
|
|
|
|
if ! "${ssh_i[@]}" "$PHONE_USB" \
|
|
|
|
|
"mkdir -p ~/$PHONE_SNAPSHOT && rsync -a --delete ~/$PHONE_DEST/ ~/$PHONE_SNAPSHOT/"; then
|
|
|
|
|
echo "ERROR: could not snapshot the phone's surface tree — refusing to write to it" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
echo "phone tree snapshotted to ~/$PHONE_SNAPSHOT"
|
|
|
|
|
|
|
|
|
|
# What a --delete would have destroyed. Listed, never removed.
|
|
|
|
|
phone_only="$(rsync -ain --delete -e "ssh -F /dev/null -i $HOME/.ssh/ani" \
|
|
|
|
|
"$SRC/" "$PHONE_USB:$PHONE_DEST/" 2>/dev/null | sed -n 's/^deleting //p')"
|
|
|
|
|
if [[ -n "$phone_only" ]]; then
|
|
|
|
|
echo "phone-only files LEFT IN PLACE (a --delete would have destroyed these):" >&2
|
|
|
|
|
printf '%s\n' "$phone_only" | sed 's/^/ /' >&2
|
|
|
|
|
echo " → if any of that is work you want, copy it back into the repo now." >&2
|
|
|
|
|
fi
|
|
|
|
|
|
2026-08-09 19:31:07 -04:00
|
|
|
# The face host reads its page and rig from ~/.souveraine/face, outside
|
|
|
|
|
# the composed QML tree. Stage that tracked limb before the QML switch so
|
|
|
|
|
# a newly-added control cannot arrive without the page that implements it.
|
|
|
|
|
# Preserve the prior phone copy first; this is the daily driver and the
|
|
|
|
|
# destination has no git history of its own.
|
|
|
|
|
[[ -f "$FACE_SRC/index.html" && -d "$FACE_SRC/live2d" ]] || {
|
|
|
|
|
echo "ERROR: tracked face assets are incomplete at $FACE_SRC" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
"${ssh_i[@]}" "$PHONE_USB" \
|
|
|
|
|
"mkdir -p ~/.souveraine/.face-previous ~/.souveraine/face; rsync -a --delete ~/.souveraine/face/ ~/.souveraine/.face-previous/"
|
|
|
|
|
rsync -ac -e "ssh -F /dev/null -i $HOME/.ssh/ani" \
|
|
|
|
|
"$FACE_SRC/" "$PHONE_USB:.souveraine/face/"
|
|
|
|
|
|
2026-07-31 12:58:52 -04:00
|
|
|
# Checksum, not timestamp. Plain -a reports files that differ only in mtime
|
|
|
|
|
# from earlier hand copies, which lies about what is actually changing.
|
|
|
|
|
rsync -ac -e "ssh -F /dev/null -i $HOME/.ssh/ani" "$SRC/" "$PHONE_USB:$PHONE_DEST/"
|
2026-07-21 18:55:02 -04:00
|
|
|
printf '%s\n' "$deployed_revision" \
|
|
|
|
|
| "${ssh_i[@]}" "$PHONE_USB" "cat > ~/$PHONE_DEST/DEPLOYED-REVISION"
|
2026-07-12 08:57:10 -04:00
|
|
|
"${ssh_i[@]}" "$PHONE_USB" "bash ~/$PHONE_DEST/deploy.sh"
|
2026-07-23 16:47:00 -04:00
|
|
|
# Phone-only: install mobile launchers into the app grid.
|
|
|
|
|
"${ssh_i[@]}" "$PHONE_USB" "mkdir -p ~/.local/share/applications && ln -sf ~/$PHONE_DEST/souveraine-settings.desktop ~/.local/share/applications/souveraine-settings.desktop && ln -sf ~/$PHONE_DEST/ii-base/modules/common/workspace-selector.desktop ~/.local/share/applications/workspace-selector.desktop"
|
shell: dock reorder, fullscreen detection fix, idle-power, sessiond, misc shell work
- Dock drag-to-reorder for pinned apps (insertion gap, quick-slide vs dwell)
- Fullscreen detection: scan all windows via HyprlandData.windowList
- IdleCoordinator, GlobalStates, Session.qml updates
- Deploy script, qmldir, settings, wallpaper, visualizer fixes
- sessiond server, memory module updates
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 22:18:20 -04:00
|
|
|
finish_phone_maintenance
|
|
|
|
|
trap - EXIT
|
quickshell: first-party lock/nav/session layer, retire the pill
Grows Souveraine's own surfaces on top of the borrowed ii shell and drops
the separate pill shell in favor of one integrated navigation rail.
Session arbiter (functions/Session.qml): probe logind's Can* methods over
busctl instead of guessing from installed binaries -- the answer carries the
polkit tier (yes/challenge/na), so a swapless phone reports hibernate as na
and refuses honestly rather than firing a verb that no-ops. Verbs run through
a Process that logs exit codes and tracks lastAction; refusals log too. The
busctl output is parsed with awk, not a sed regex buried under four escaping
layers -- the sed version returned nothing on the phone and left every
capability stuck at "unknown" (invisible on the laptop, where timing masked
it). Every structured result is JSON-over-string; quickshell maps a var
return to void.
Lock trust: screenLocked (the shell's lock request) is now distinct from
screenLockSecure (WlSessionLock.secure, the compositor's acknowledgement,
mirrored from LockScreen). Cards that disclose personal data gate on secure,
not on a button press. LockContentPolicy centralizes the ambient/personal/
step-up tiers so no card grows its own private rule.
New first-party namespace modules/souveraine/: LockMediaCard, LockSurfaceHost,
SystemGestureRail -- owned surfaces, not ii patches. IdleCoordinator gives one
staged idle vocabulary (dim/lock) gated behind nativeCoordinatorEnabled, off
until the native Wayland idle-notify is verified on the Pixel compositor;
hypridle stays the adapter. WallpaperAssets selects aspect-aware variants for
phone-vs-laptop display shapes.
Pill retired: pill/shell.qml and PillConfig gone, replaced by NavigationConfig
and the gesture rail. Hyprland starts qs -c souveraine directly; no secondary
shell, no qsConfig flip.
Verified on the phone: session.* reports challenge/na correctly, hibernate
and unlock refuse, inhibit round-trips with its reason.
2026-07-14 20:00:57 -04:00
|
|
|
echo "Deployed to phone. Hyprland starts qs -c souveraine; no secondary shell is required."
|
2026-07-12 08:57:10 -04:00
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ "${1:-}" == "-u" || "${1:-}" == "--uninstall" ]]; then
|
2026-07-14 15:03:07 -04:00
|
|
|
rm -rf "$SV"
|
|
|
|
|
echo "removed $SV (ii tree untouched)"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ "${1:-}" == "--legacy-clean" ]]; then
|
|
|
|
|
# One-time migration: strip the old overlay out of ii.
|
|
|
|
|
# 1. remove any symlink in ii that points into a souveraine checkout
|
|
|
|
|
find "$II" -type l | while read -r l; do
|
|
|
|
|
case "$(readlink "$l")" in
|
|
|
|
|
*souveraine*) rm -f "$l"; echo "unlinked ${l#$QS/}" ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
# 2. restore upstream backups
|
|
|
|
|
find "$II" -name '*.upstream' | while read -r u; do
|
|
|
|
|
mv -f "$u" "${u%.upstream}"
|
|
|
|
|
echo "restored ${u%.upstream}"
|
|
|
|
|
done
|
2026-07-12 08:57:10 -04:00
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
2026-07-21 17:30:32 -04:00
|
|
|
# --- Sync the pinned ii base --------------------------------------------
|
2026-07-21 19:11:51 -04:00
|
|
|
# Build both trees away from their live paths, then converge the stable live
|
|
|
|
|
# directories with delayed renames. Quickshell's watcher follows the config
|
|
|
|
|
# directory inode and does not tolerate replacing that root, even atomically.
|
|
|
|
|
# The three-phase order keeps every symlink valid throughout the update:
|
|
|
|
|
# add/update ii files, switch the composed surface, then delete stale ii files.
|
2026-07-21 18:55:02 -04:00
|
|
|
mkdir -p "$QS"
|
2026-07-21 19:11:51 -04:00
|
|
|
|
|
|
|
|
clear_generated_tree() {
|
|
|
|
|
local path="$1"
|
|
|
|
|
case "$path" in
|
|
|
|
|
"$QS/.ii-previous"|"$QS/.souveraine-previous"|"$QS"/.ii-next.*|"$QS"/.souveraine-next.*)
|
|
|
|
|
[[ ! -e "$path" && ! -L "$path" ]] || find "$path" -depth -delete
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
echo "refusing to clear unexpected rollback path: $path" >&2
|
|
|
|
|
return 1
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snapshot_tree() { # live rollback
|
|
|
|
|
local live="$1" rollback="$2"
|
|
|
|
|
clear_generated_tree "$rollback"
|
|
|
|
|
if [[ -e "$live" || -L "$live" ]]; then
|
|
|
|
|
mkdir -p "$rollback"
|
|
|
|
|
rsync -a --delete --exclude='.git' "$live/" "$rollback/"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
II_NEXT="$QS/.ii-next.$$"
|
|
|
|
|
SV_NEXT="$QS/.souveraine-next.$$"
|
|
|
|
|
[[ ! -e "$II_NEXT" && ! -e "$SV_NEXT" ]] || {
|
|
|
|
|
echo "staging path already exists; refusing: $II_NEXT or $SV_NEXT" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
cleanup_staging() {
|
|
|
|
|
clear_generated_tree "$II_NEXT"
|
|
|
|
|
clear_generated_tree "$SV_NEXT"
|
|
|
|
|
}
|
|
|
|
|
trap cleanup_staging EXIT
|
|
|
|
|
mkdir -p "$II_NEXT"
|
|
|
|
|
rsync -a --delete --exclude='.git' "$SRC/ii-base/" "$II_NEXT/"
|
2026-07-21 18:55:02 -04:00
|
|
|
if [[ "$TARGET_ARCH" == "aarch64" ]]; then
|
2026-07-21 19:11:51 -04:00
|
|
|
rsync -a "$SRC/ii-phone/" "$II_NEXT/"
|
|
|
|
|
ii_description="pin + phone overlay"
|
2026-07-21 17:30:32 -04:00
|
|
|
else
|
2026-07-21 19:11:51 -04:00
|
|
|
ii_description="pin"
|
2026-07-21 17:30:32 -04:00
|
|
|
fi
|
2026-07-21 19:11:51 -04:00
|
|
|
install -m 0644 "$SRC/ii-base.pin" "$II_NEXT/.souveraine-upstream-pin"
|
|
|
|
|
snapshot_tree "$II" "$QS/.ii-previous"
|
|
|
|
|
mkdir -p "$II"
|
|
|
|
|
# Phase 1: make every path the new composition can reference available, but
|
|
|
|
|
# retain old extras until their Souveraine override/symlink has switched.
|
|
|
|
|
rsync -a --delay-updates "$II_NEXT/" "$II/"
|
2026-07-12 08:57:10 -04:00
|
|
|
|
2026-07-14 15:03:07 -04:00
|
|
|
# --- Compose -------------------------------------------------------------
|
|
|
|
|
# Targets under souveraine/, relative to $SV
|
|
|
|
|
SV_TARGETS="$(manifest_lines | awk '$2 ~ /^souveraine\// {sub(/^souveraine\//, "", $2); print $2}')"
|
|
|
|
|
|
|
|
|
|
is_replaced() { # exact file override
|
|
|
|
|
grep -qxF "$1" <<< "$SV_TARGETS"
|
|
|
|
|
}
|
|
|
|
|
is_touched() { # dir contains an override somewhere below
|
|
|
|
|
grep -q "^$1/" <<< "$SV_TARGETS"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
compose_dir() { # $1 = path relative to ii root ("" for root)
|
|
|
|
|
local rel="$1" entry name erel
|
2026-07-21 19:11:51 -04:00
|
|
|
mkdir -p "$SV_NEXT${rel:+/$rel}"
|
|
|
|
|
for entry in "$II_NEXT${rel:+/$rel}"/*; do
|
2026-07-14 15:03:07 -04:00
|
|
|
[[ -e "$entry" ]] || continue
|
|
|
|
|
name="$(basename "$entry")"
|
|
|
|
|
[[ "$name" == *.upstream ]] && continue
|
|
|
|
|
erel="${rel:+$rel/}$name"
|
|
|
|
|
if [[ -d "$entry" ]]; then
|
|
|
|
|
if is_touched "$erel"; then
|
|
|
|
|
compose_dir "$erel"
|
|
|
|
|
else
|
2026-07-21 19:11:51 -04:00
|
|
|
ln -sfn "$II/$erel" "$SV_NEXT/$erel"
|
2026-07-14 15:03:07 -04:00
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
# root files and files inside touched dirs: link individually,
|
|
|
|
|
# skipping ones our manifest replaces
|
|
|
|
|
is_replaced "$erel" && continue
|
2026-07-21 19:11:51 -04:00
|
|
|
ln -sf "$II/$erel" "$SV_NEXT/$erel"
|
2026-07-14 15:03:07 -04:00
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:11:51 -04:00
|
|
|
# Build from scratch at the staging path: cheap, with no stale links.
|
2026-07-14 15:03:07 -04:00
|
|
|
compose_dir ""
|
|
|
|
|
|
|
|
|
|
# Our files on top (this also creates dirs that exist only in our tree,
|
|
|
|
|
# e.g. brand-new settings pages)
|
|
|
|
|
mkdir -p "$QS/pill"
|
2026-07-12 08:57:10 -04:00
|
|
|
while read -r rel target; do
|
|
|
|
|
[[ -z "$rel" ]] && continue
|
2026-07-21 19:11:51 -04:00
|
|
|
src="$SRC/$rel"; t="$SV_NEXT/${target#souveraine/}"
|
2026-07-12 08:57:10 -04:00
|
|
|
[[ -f "$src" ]] || { echo "missing $src" >&2; exit 1; }
|
2026-07-14 15:03:07 -04:00
|
|
|
mkdir -p "$(dirname "$t")"
|
2026-07-12 08:57:10 -04:00
|
|
|
ln -sf "$src" "$t"
|
2026-07-14 15:03:07 -04:00
|
|
|
done < <(manifest_lines)
|
2026-07-12 08:57:10 -04:00
|
|
|
|
2026-07-21 19:11:51 -04:00
|
|
|
snapshot_tree "$SV" "$QS/.souveraine-previous"
|
|
|
|
|
mkdir -p "$SV"
|
|
|
|
|
# Phase 2: switch the composed tree using delayed per-entry renames while its
|
|
|
|
|
# root directory remains stable for Quickshell's recursive watcher.
|
|
|
|
|
rsync -a --delete-delay --delay-updates "$SV_NEXT/" "$SV/"
|
|
|
|
|
# Phase 3: the composition no longer points at obsolete ii-only paths, so it
|
|
|
|
|
# is now safe to remove them and finish exact convergence with the pin.
|
|
|
|
|
rsync -a --delete-delay --delay-updates "$II_NEXT/" "$II/"
|
|
|
|
|
clear_generated_tree "$II_NEXT"
|
|
|
|
|
clear_generated_tree "$SV_NEXT"
|
|
|
|
|
trap - EXIT
|
|
|
|
|
echo "ii synced from $ii_description (rollback: $QS/.ii-previous)"
|
|
|
|
|
|
2026-07-14 15:03:07 -04:00
|
|
|
n_ours=$(manifest_lines | grep -c .)
|
|
|
|
|
n_borrowed_dirs=$(find "$SV" -maxdepth 3 -type l -xtype d | wc -l)
|
|
|
|
|
echo "souveraine config composed at $SV ($n_ours files ours, $n_borrowed_dirs dirs borrowed from ii)"
|
2026-07-27 09:15:48 -04:00
|
|
|
|
|
|
|
|
# Every qmldir entry must resolve to a file that exists in the composed tree.
|
|
|
|
|
#
|
|
|
|
|
# This is the failure that has bitten three times (ReloadPopup 2026-07-22,
|
|
|
|
|
# SessiondPolicy 07-25, ChargeRate 07-26). A qmldir naming a file that is not
|
|
|
|
|
# there does not fail that one type: Quickshell fails the whole MODULE, and
|
|
|
|
|
# `qs.services` failing takes GlobalStates with it, which takes shell.qml.
|
|
|
|
|
# The shell then refuses every reload and keeps serving the last scene that
|
|
|
|
|
# loaded — silently, for days. On 2026-07-27 the laptop was found running a
|
|
|
|
|
# 49-hour-old scene for exactly this reason.
|
|
|
|
|
#
|
|
|
|
|
# Loud, not fatal: the tree is already switched by this point, and the old
|
|
|
|
|
# tree is no better. Say it clearly and exit non-zero so a caller notices.
|
|
|
|
|
qmldir_broken=0
|
|
|
|
|
while IFS= read -r qd; do
|
|
|
|
|
dir=$(dirname "$qd")
|
|
|
|
|
# Field 2 of `singleton Name 1.0 File.qml` / `Name 1.0 File.qml` is the
|
|
|
|
|
# version for singletons and the file for plain types; take the last
|
|
|
|
|
# field, which is the filename in both shapes.
|
|
|
|
|
while read -r file; do
|
|
|
|
|
[[ -n "$file" ]] || continue
|
|
|
|
|
[[ -e "$dir/$file" ]] && continue
|
|
|
|
|
echo "BROKEN qmldir: $qd names '$file' which is not in the composed tree" >&2
|
|
|
|
|
qmldir_broken=1
|
|
|
|
|
done < <(awk '/\.qml[[:space:]]*$/ { print $NF }' "$qd")
|
|
|
|
|
done < <(find "$SV" -name qmldir -type f -o -name qmldir -type l)
|
|
|
|
|
|
|
|
|
|
if (( qmldir_broken )); then
|
|
|
|
|
echo "REFUSING TO CALL THIS DEPLOYED: the shell will fail every reload and" >&2
|
|
|
|
|
echo "keep serving its last good scene. Add the missing file to the deploy" >&2
|
|
|
|
|
echo "manifest above, or remove its qmldir line." >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
2026-07-31 12:51:10 -04:00
|
|
|
|
|
|
|
|
# The phone's quick-toggle chooser is a WHOLE-FILE override of the base one, so
|
|
|
|
|
# a choice added to the base never reaches the phone unless it is added there
|
|
|
|
|
# too. That drift is silent by construction: the panel derives what it can build
|
|
|
|
|
# from the chooser, so a missing choice is not an error, it is a toggle that
|
|
|
|
|
# quietly stops existing on one device. Base must be a subset of phone.
|
|
|
|
|
#
|
|
|
|
|
# This is the same class as the qmldir check above and it has already cost a
|
|
|
|
|
# session: five configured toggles rendered nothing for a day because a second
|
|
|
|
|
# enumeration had drifted from this one.
|
|
|
|
|
chooser_rel="modules/ii/sidebarRight/quickToggles/androidStyle/AndroidToggleDelegateChooser.qml"
|
|
|
|
|
base_chooser="$SRC/ii-base/$chooser_rel"
|
|
|
|
|
phone_chooser="$SRC/ii-phone/$chooser_rel"
|
|
|
|
|
if [[ -f "$base_chooser" && -f "$phone_chooser" ]]; then
|
|
|
|
|
role_values() { grep -o 'roleValue: "[A-Za-z]*"' "$1" | sed 's/.*"\(.*\)"/\1/' | sort -u; }
|
|
|
|
|
missing_on_phone="$(comm -23 <(role_values "$base_chooser") <(role_values "$phone_chooser") | tr '\n' ' ')"
|
|
|
|
|
if [[ -n "${missing_on_phone// /}" ]]; then
|
|
|
|
|
echo "BROKEN toggle chooser overlay: ii-phone is missing base choices: $missing_on_phone" >&2
|
|
|
|
|
echo "The phone chooser overrides the base file wholesale — add the same" >&2
|
|
|
|
|
echo "DelegateChoice blocks to ii-phone/$chooser_rel or those toggles will" >&2
|
|
|
|
|
echo "silently not exist on the phone." >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
2026-07-14 15:03:07 -04:00
|
|
|
if grep -q 'qsConfig", "ii"' "$HOME/.config/hypr/hyprland/variables.lua" 2>/dev/null; then
|
|
|
|
|
echo "NOTE: hyprland qsConfig is still 'ii' — flip variables.lua to 'souveraine' to switch."
|
|
|
|
|
fi
|
2026-08-11 14:02:02 -04:00
|
|
|
if [[ -x "$REPO_ROOT/tools/stage-face.sh" ]]; then
|
|
|
|
|
"$REPO_ROOT/tools/stage-face.sh" >/dev/null
|
|
|
|
|
elif [[ -f "$HOME/.souveraine/face/index.html" \
|
|
|
|
|
&& -d "$HOME/.souveraine/face/live2d" ]]; then
|
|
|
|
|
# `--phone` stages these assets before it invokes this copied compositor
|
|
|
|
|
# script. That copy lives below ~/souveraine-surfaces rather than a full
|
|
|
|
|
# checkout, so there is intentionally no repository-level helper beside
|
|
|
|
|
# it. The old unconditional call failed after the QML tree had already
|
|
|
|
|
# switched, making a successful remote composition report as a failure.
|
|
|
|
|
:
|
|
|
|
|
else
|
|
|
|
|
echo "ERROR: face assets are not staged and stage-face.sh is unavailable" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
2026-08-09 19:31:07 -04:00
|
|
|
echo "face assets staged at $HOME/.souveraine/face"
|
2026-07-14 15:03:07 -04:00
|
|
|
echo "Run with: qs -c souveraine (restart: pkill -f 'qs -c souveraine')"
|