Launchers: a neutral example app in docs and tests

The Nix-package launcher example in docs and option examples is now
Signal (pkgs.signal-desktop, desktop ID "signal", sgnl:// and
signalcaptcha:// handlers). The check uses a made-up org.example.App
from a fake package (fake store path), covering the same Exec, action,
MimeType, source and missing-entry cases.

Screenshots: placeholder Steam account IDs in the docs, option example
and check.
This commit is contained in:
Pierre Kisters committed 2026-10-01 18:06:50 +02:00
1 parent a01a49da45
commit 92e9d17e09
11 files changed
+83 -83

No files matched your search

+5 -5
View File
@@ -27,7 +27,7 @@ the Frame's [two sessions](../README.md#two-sessions):
reads through the Secret Service `org.freedesktop.secrets` (KRDC: "Password
not found").
- **Login callbacks:** SSO logins come back through a URL scheme
(`io.element.desktop://`, `claude://`) opened by the portal. Unless the app
(`io.element.desktop://`, `signalcaptcha://`) opened by the portal. Unless the app
is the default and a recommended handler, the portal opens an app chooser,
which isn't shown in VR.
@@ -80,11 +80,11 @@ A program from a Nix package, from the package's own entry
```nix
{ pkgs, ... }: {
home.packages = [ pkgs.claude-desktop ];
steamFrame.launchers."com.anthropic.Claude" = {
source.package = pkgs.claude-desktop;
home.packages = [ pkgs.signal-desktop ];
steamFrame.launchers.signal = {
source.package = pkgs.signal-desktop;
keyring = { enable = true; electron = true; };
defaultFor = [ "x-scheme-handler/claude" ]; # login callback
defaultFor = [ "x-scheme-handler/sgnl" "x-scheme-handler/signalcaptcha" ];
};
}
```
+1 -1
View File
@@ -25,7 +25,7 @@ in `…/760/screenshots.vdf`). The JPG is the only copy that stays.
```nix
steamFrame.screenshots.enable = true;
# steamFrame.screenshots.steamUserId = "80511808"; # optional
# steamFrame.screenshots.steamUserId = "12345678"; # optional
```
The account ID is found at runtime: the account last logged in to Steam
+6 -6
View File
@@ -41,10 +41,10 @@ let
keyring = { enable = true; electron = true; };
defaultFor = [ "x-scheme-handler/element" "x-scheme-handler/io.element.desktop" ];
};
steamFrame.launchers."com.anthropic.Claude" = {
source.package = pkgs.claude-desktop;
steamFrame.launchers.signal = {
source.package = pkgs.signal-desktop;
keyring = { enable = true; electron = true; };
defaultFor = [ "x-scheme-handler/claude" ];
defaultFor = [ "x-scheme-handler/sgnl" ];
};
(schemeHandlers = [ "x" ] -> defaultFor = [ "x-scheme-handler/x" ];
flatpakArgs, args, mimeTypes, settings keep their meaning; name, icon,
@@ -63,10 +63,10 @@ in {
defaultFor = [ "x-scheme-handler/element" "x-scheme-handler/io.element.desktop" ];
};
"org.kde.krdc".keyring.enable = true;
"com.anthropic.Claude" = {
source.package = pkgs.claude-desktop;
signal = {
source.package = pkgs.signal-desktop;
keyring = { enable = true; electron = true; };
defaultFor = [ "x-scheme-handler/claude" ];
defaultFor = [ "x-scheme-handler/sgnl" ];
};
}
'';
+14 -14
View File
@@ -1,8 +1,8 @@
# Checks of steamFrame.launchers (lib.nix, rewrite.awk, generate.sh) on
# copies of real entries (fixtures/sources: Element, KRDC, Firefox, gedit
# (DBusActivatable) from /var/lib/flatpak, Jellyfin from the user
# installation, Claude from the claude-desktop package) and two made-up ones
# (quoting, an Exec without anchor):
# installation) and three made-up ones (quoting, an Exec without anchor, an
# Electron app from a fake package):
# - the rewritten entries equal fixtures/expected (X-SteamFrameNix-Source
# compared separately) and pass desktop-file-validate;
# - the generator: unchanged sources change nothing (mtimes kept, no touch of
@@ -19,9 +19,9 @@ let
src = ./fixtures/sources;
shim = "/nix/store/00000000000000000000000000000000-mpv-hwdec-shim";
fakeClaude = pkgs.runCommand "claude-desktop-fixture" { } ''
fakeApp = pkgs.runCommand "example-app-fixture" { } ''
mkdir -p $out/share/applications
cp ${src}/com.anthropic.Claude.desktop $out/share/applications/
cp ${src}/org.example.App.desktop $out/share/applications/
'';
noEntry = pkgs.runCommand "no-entry-fixture" { } "mkdir -p $out/share/applications";
@@ -58,10 +58,10 @@ let
args = [ "a b" "50%" "q\"uote" "$HOME" "back\\slash" "back`tick" "" ];
settings.Keywords = "x;";
};
"com.anthropic.Claude" = {
source.package = fakeClaude;
"org.example.App" = {
source.package = fakeApp;
keyring = { enable = true; electron = true; };
defaultFor = [ "x-scheme-handler/claude" ];
defaultFor = [ "x-scheme-handler/example" ];
};
};
} ];
@@ -81,14 +81,14 @@ let
applicationsDir = "/nonexistent";
exportDirs = [ "/nonexistent" ];
};
claude = L.packageEntry all."com.anthropic.Claude";
missing = pkgs.testers.testBuildFailure (L.packageEntry (all."com.anthropic.Claude" // {
source = all."com.anthropic.Claude".source // { package = noEntry; };
app = L.packageEntry all."org.example.App";
missing = pkgs.testers.testBuildFailure (L.packageEntry (all."org.example.App" // {
source = all."org.example.App".source // { package = noEntry; };
}));
in
pkgs.runCommand "launchers-check" {
nativeBuildInputs = [ generator pkgs.desktop-file-utils pkgs.diffutils ];
passthru = { inherit generator claude; };
passthru = { inherit generator app; };
} ''
set -euo pipefail
fail() { echo "FAIL: $*" >&2; exit 1; }
@@ -127,12 +127,12 @@ pkgs.runCommand "launchers-check" {
[ $id = org.example.NoAnchor ] && continue
cp $out_dir/$id.desktop $out/actual/
done
cp ${claude} $out/actual/com.anthropic.Claude.desktop
cp ${app} $out/actual/org.example.App.desktop
grep -qx "X-SteamFrameNix-Source=$root/user/org.jellyfin.JellyfinDesktop.desktop" $out/actual/org.jellyfin.JellyfinDesktop.desktop \
|| fail "Jellyfin not from the user installation"
grep -qx "X-SteamFrameNix-Source=$root/system/org.kde.krdc.desktop" $out/actual/org.kde.krdc.desktop || fail "KRDC source"
grep -qx "X-SteamFrameNix-Source=${src}/org.example.Quoting.desktop" $out/actual/org.example.Quoting.desktop || fail "file source"
grep -qx "X-SteamFrameNix-Source=${fakeClaude}/share/applications/com.anthropic.Claude.desktop" $out/actual/com.anthropic.Claude.desktop || fail "package source"
grep -qx "X-SteamFrameNix-Source=${fakeApp}/share/applications/org.example.App.desktop" $out/actual/org.example.App.desktop || fail "package source"
sed -i 's|^X-SteamFrameNix-Source=.*|X-SteamFrameNix-Source=@SOURCE@|' $out/actual/*.desktop
desktop-file-validate --no-hints $out/actual/*.desktop || fail "desktop-file-validate"
for f in ${./fixtures/expected}/*.desktop; do
@@ -185,7 +185,7 @@ pkgs.runCommand "launchers-check" {
[ "$(mt $root/apps)" != 1000000000 ] || fail "removal didn't touch the applications dir"
# a package without the entry: the build fails
[ -e ${missing}/testBuildFailure.log ] && grep -q "has no share/applications/com.anthropic.Claude.desktop" ${missing}/testBuildFailure.log \
[ -e ${missing}/testBuildFailure.log ] && grep -q "has no share/applications/org.example.App.desktop" ${missing}/testBuildFailure.log \
|| fail "package without the entry built"
echo ok
''
@@ -1,26 +0,0 @@
[Desktop Entry]
Name=Claude
Comment=Desktop application for Claude.ai
GenericName=AI Assistant
Keywords=AI;Chat;Assistant;Claude;Code;LLM;
Exec=env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus /nix/store/8cc1cplpbdlsqy4x0v1c9w66w3dq80w4-claude-desktop-2.7032.0/bin/claude-desktop --password-store=kwallet6 %U
Icon=claude-desktop
Type=Application
StartupNotify=true
# Matches the Wayland app_id / X11 WM_CLASS Chromium derives from
# package.json desktopName, so docks group windows under this entry.
StartupWMClass=com.anthropic.Claude
# second-instance just focuses mainWindow; suppress GNOME's default "New Window" item
SingleMainWindow=true
Categories=Utility;Development;
MimeType=x-scheme-handler/claude;
Actions=NewChat;NewCode;
X-SteamFrameNix-Source=@SOURCE@
[Desktop Action NewChat]
Name=New Chat
Exec=env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus /nix/store/8cc1cplpbdlsqy4x0v1c9w66w3dq80w4-claude-desktop-2.7032.0/bin/claude-desktop --password-store=kwallet6 "claude://claude.ai/new?surface=chat&source=desktop_action"
[Desktop Action NewCode]
Name=New Claude Code Session
Exec=env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus /nix/store/8cc1cplpbdlsqy4x0v1c9w66w3dq80w4-claude-desktop-2.7032.0/bin/claude-desktop --password-store=kwallet6 "claude://code/new?source=desktop_action"
@@ -0,0 +1,26 @@
[Desktop Entry]
Name=Example App
Comment=A made-up Electron app from a Nix package
GenericName=Example
Keywords=Example;Test;
Exec=env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus /nix/store/00000000000000000000000000000000-example-app-1.0/bin/example-app --password-store=kwallet6 %U
Icon=example-app
Type=Application
StartupNotify=true
# Matches the Wayland app_id / X11 WM_CLASS Electron derives from
# package.json desktopName, so docks group windows under this entry.
StartupWMClass=org.example.App
# second-instance just focuses the main window
SingleMainWindow=true
Categories=Utility;Development;
MimeType=x-scheme-handler/example;
Actions=NewWindow;NewSession;
X-SteamFrameNix-Source=@SOURCE@
[Desktop Action NewWindow]
Name=New Window
Exec=env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus /nix/store/00000000000000000000000000000000-example-app-1.0/bin/example-app --password-store=kwallet6 "example://app/new?surface=window&source=desktop_action"
[Desktop Action NewSession]
Name=New Example Session
Exec=env DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus /nix/store/00000000000000000000000000000000-example-app-1.0/bin/example-app --password-store=kwallet6 "example://session/new?source=desktop_action"
@@ -1,25 +0,0 @@
[Desktop Entry]
Name=Claude
Comment=Desktop application for Claude.ai
GenericName=AI Assistant
Keywords=AI;Chat;Assistant;Claude;Code;LLM;
Exec=/nix/store/8cc1cplpbdlsqy4x0v1c9w66w3dq80w4-claude-desktop-2.7032.0/bin/claude-desktop %U
Icon=claude-desktop
Type=Application
StartupNotify=true
# Matches the Wayland app_id / X11 WM_CLASS Chromium derives from
# package.json desktopName, so docks group windows under this entry.
StartupWMClass=com.anthropic.Claude
# second-instance just focuses mainWindow; suppress GNOME's default "New Window" item
SingleMainWindow=true
Categories=Utility;Development;
MimeType=x-scheme-handler/claude;
Actions=NewChat;NewCode;
[Desktop Action NewChat]
Name=New Chat
Exec=/nix/store/8cc1cplpbdlsqy4x0v1c9w66w3dq80w4-claude-desktop-2.7032.0/bin/claude-desktop "claude://claude.ai/new?surface=chat&source=desktop_action"
[Desktop Action NewCode]
Name=New Claude Code Session
Exec=/nix/store/8cc1cplpbdlsqy4x0v1c9w66w3dq80w4-claude-desktop-2.7032.0/bin/claude-desktop "claude://code/new?source=desktop_action"
@@ -0,0 +1,25 @@
[Desktop Entry]
Name=Example App
Comment=A made-up Electron app from a Nix package
GenericName=Example
Keywords=Example;Test;
Exec=/nix/store/00000000000000000000000000000000-example-app-1.0/bin/example-app %U
Icon=example-app
Type=Application
StartupNotify=true
# Matches the Wayland app_id / X11 WM_CLASS Electron derives from
# package.json desktopName, so docks group windows under this entry.
StartupWMClass=org.example.App
# second-instance just focuses the main window
SingleMainWindow=true
Categories=Utility;Development;
MimeType=x-scheme-handler/example;
Actions=NewWindow;NewSession;
[Desktop Action NewWindow]
Name=New Window
Exec=/nix/store/00000000000000000000000000000000-example-app-1.0/bin/example-app "example://app/new?surface=window&source=desktop_action"
[Desktop Action NewSession]
Name=New Example Session
Exec=/nix/store/00000000000000000000000000000000-example-app-1.0/bin/example-app "example://session/new?source=desktop_action"
+1 -1
View File
@@ -45,7 +45,7 @@ in rec {
package = lib.mkOption {
type = lib.types.nullOr lib.types.package;
default = null;
example = lib.literalExpression "pkgs.claude-desktop";
example = lib.literalExpression "pkgs.signal-desktop";
description = ''
Nix package whose `share/applications/<desktop ID>.desktop` is
rewritten at build time (the build fails if it has none).
+1 -1
View File
@@ -25,7 +25,7 @@ in {
steamUserId = lib.mkOption {
type = lib.types.nullOr (lib.types.strMatching "[1-9][0-9]*");
default = null;
example = "80511808";
example = "12345678";
description = ''
Steam account ID: the folder name in ~/.local/share/Steam/userdata.
`null`: the account last logged in, found at runtime.
+4 -4
View File
@@ -19,16 +19,16 @@ pkgs.runCommand "screenshots-check" { nativeBuildInputs = [ script ]; } ''
steam-frame-nix-screenshots 2>&1 | grep -q "doesn't exist" || fail "no runtime dir"
[ ! -e run ] || fail "runtime dir created"
mkdir -p run $S/config $S/userdata/0 $S/userdata/80511808
mkdir -p run $S/config $S/userdata/0 $S/userdata/12345678
# the only account folder ("0" is not an account)
steam-frame-nix-screenshots; expect 80511808 "userdata"
steam-frame-nix-screenshots; expect 12345678 "userdata"
# MostRecent wins over a later timestamp
cat > $S/config/loginusers.vdf <<'VDF'
"users"
{
"76561198040777536"
"76561197972611406"
{
"AccountName" "a"
"MostRecent" "1"
@@ -42,7 +42,7 @@ pkgs.runCommand "screenshots-check" { nativeBuildInputs = [ script ]; } ''
}
}
VDF
steam-frame-nix-screenshots; expect 80511808 "MostRecent"
steam-frame-nix-screenshots; expect 12345678 "MostRecent"
# unchanged: not rewritten
res=$(steam-frame-nix-screenshots 2>&1); [ -z "$res" ] || fail "rewrote: $res"