···6677The intent is to add deeper integration with the desktop app. Eventually, it will be submitted to the Chrome Web Store and Mozilla.
8899-## Native Messaging Support
99+## Desktop Integration
10101111The Switchyard extension can also show your installed browsers directly in the popup, letting you send the current tab to a specific browser in one click. This requires installing a native messaging host on your system.
12121313-Please read [`scripts/install-native-host.sh`](../scripts/install-native-host.sh) before running this command:
1313+Please read [`scripts/install-desktop-integration.sh`](../scripts/install-desktop-integration.sh) before running this command:
14141515```bash
1616-curl -fsSL https://raw.githubusercontent.com/alyraffauf/Switchyard/master/scripts/install-native-host.sh | bash
1616+curl -fsSL https://raw.githubusercontent.com/alyraffauf/Switchyard/master/scripts/install-desktop-integration.sh | bash
1717```
18181919After installation, restart your browser(s). The extension popup will list your installed browsers.
···2121### Uninstall
22222323```bash
2424-bash install-native-host.sh --uninstall
2424+bash install-desktop-integration.sh --uninstall
2525```
26262727This removes all manifests and wrappers, but the `flatpak override` permissions granted during install are left in place. It's up to the user to decide what to do with them.
+185
scripts/install-desktop-integration.sh
···11+#!/usr/bin/env bash
22+# SPDX-License-Identifier: GPL-3.0-or-later
33+# Switchyard desktop integration installer — https://github.com/alyraffauf/Switchyard
44+#
55+# Installs the desktop integration that lets the browser extension list your installed browsers.
66+#
77+# For Flatpak browsers this grants --talk-name=org.freedesktop.Flatpak, which
88+# lets code inside the browser sandbox run arbitrary host commands via
99+# flatpak-spawn --host. This weakens the Flatpak sandbox for those browsers.
1010+# Read and understand this before running it.
1111+#
1212+# Usage: bash install-native-host.sh [--uninstall]
1313+1414+set -euo pipefail
1515+1616+# ── Find Switchyard ────────────────────────────────────────────────────────────
1717+1818+CMD=""
1919+for id in io.github.alyraffauf.Switchyard io.github.alyraffauf.Switchyard.Devel; do
2020+ if flatpak info "$id" &>/dev/null; then
2121+ CMD="/usr/bin/flatpak run --command=switchyard $id --native-host"
2222+ break
2323+ fi
2424+done
2525+if [[ -z $CMD ]]; then
2626+ bin=$(command -v switchyard 2>/dev/null) || {
2727+ echo "error: Switchyard not found" >&2
2828+ exit 1
2929+ }
3030+ CMD="$bin --native-host"
3131+fi
3232+3333+# ── Config ─────────────────────────────────────────────────────────────────────
3434+3535+NAME="io.github.alyraffauf.switchyard"
3636+WRAPPER="$HOME/.local/share/switchyard/native-host-wrapper.sh"
3737+CHROME_ORIGIN="chrome-extension://gmdmmjfmpfbmddgphjbkbbmdolkifloi/"
3838+FF_EXT="switchyard@alyraffauf.github.io"
3939+4040+NATIVE_CHROMIUM=(net.imput.helium google-chrome chromium BraveSoftware/Brave-Browser microsoft-edge vivaldi)
4141+NATIVE_FIREFOX=(.mozilla .librewolf .zen)
4242+4343+# (appId:configSubdir) — configSubdir maps to ~/.config/<sub>/ inside the sandbox
4444+FLATPAK_CHROMIUM=(
4545+ com.google.Chrome:google-chrome
4646+ com.google.ChromeDev:google-chrome-unstable
4747+ org.chromium.Chromium:chromium
4848+ com.brave.Browser:BraveSoftware/Brave-Browser
4949+ com.microsoft.Edge:microsoft-edge
5050+ com.vivaldi.Vivaldi:vivaldi
5151+ io.github.ungoogled_software.ungoogled_chromium:chromium
5252+ net.imput.helium:net.imput.helium
5353+)
5454+5555+# (appId:profileDir) — profileDir is persisted as ~/<sub>/ inside the sandbox
5656+FLATPAK_FIREFOX=(
5757+ org.mozilla.firefox:.mozilla
5858+ io.gitlab.librewolf-community:.librewolf
5959+ io.github.zen_browser.zen:.zen
6060+)
6161+6262+# ── Helpers ────────────────────────────────────────────────────────────────────
6363+6464+N=0
6565+6666+chromium_manifest() {
6767+ printf '{"name":"%s","description":"Switchyard browser selector","path":"%s","type":"stdio","allowed_origins":["%s"]}\n' \
6868+ "$NAME" "$1" "$CHROME_ORIGIN"
6969+}
7070+7171+firefox_manifest() {
7272+ printf '{"name":"%s","description":"Switchyard browser selector","path":"%s","type":"stdio","allowed_extensions":["%s"]}\n' \
7373+ "$NAME" "$1" "$FF_EXT"
7474+}
7575+7676+# ── Uninstall ──────────────────────────────────────────────────────────────────
7777+7878+if [[ ${1-} == "--uninstall" ]]; then
7979+ echo "Removing Switchyard native messaging host..."
8080+8181+ for sub in "${NATIVE_CHROMIUM[@]}"; do
8282+ f="$HOME/.config/$sub/NativeMessagingHosts/$NAME.json"
8383+ [[ -f $f ]] && rm -f "$f" && echo " removed $f" && N=$((N + 1)) || true
8484+ done
8585+8686+ for sub in "${NATIVE_FIREFOX[@]}"; do
8787+ f="$HOME/$sub/native-messaging-hosts/$NAME.json"
8888+ [[ -f $f ]] && rm -f "$f" && echo " removed $f" && N=$((N + 1)) || true
8989+ done
9090+9191+ for entry in "${FLATPAK_CHROMIUM[@]}"; do
9292+ appId="${entry%%:*}"
9393+ sub="${entry##*:}"
9494+ dir="$HOME/.var/app/$appId/config/$sub/NativeMessagingHosts"
9595+ f="$dir/$NAME.json"
9696+ [[ -f $f ]] && rm -f "$f" "$dir/switchyard-native-host.sh" && echo " removed $f" && N=$((N + 1)) || true
9797+ done
9898+9999+ for entry in "${FLATPAK_FIREFOX[@]}"; do
100100+ appId="${entry%%:*}"
101101+ sub="${entry##*:}"
102102+ dir="$HOME/.var/app/$appId/$sub/native-messaging-hosts"
103103+ f="$dir/$NAME.json"
104104+ [[ -f $f ]] && rm -f "$f" "$dir/switchyard-native-host.sh" && echo " removed $f" && N=$((N + 1)) || true
105105+ done
106106+107107+ [[ -f $WRAPPER ]] && rm -f "$WRAPPER" && echo " removed $WRAPPER" || true
108108+109109+ echo
110110+ echo "Removed $N manifest(s)."
111111+ exit 0
112112+fi
113113+114114+# ── Install ────────────────────────────────────────────────────────────────────
115115+116116+echo "Installing Switchyard native messaging host..."
117117+118118+mkdir -p "$(dirname "$WRAPPER")"
119119+printf '#!/bin/sh\nexec %s "$@"\n' "$CMD" >"$WRAPPER"
120120+chmod +x "$WRAPPER"
121121+echo "Wrapper: $WRAPPER"
122122+123123+# Flatpak browsers need a different wrapper: they detect the sandbox via
124124+# $container and use flatpak-spawn --host to escape it. This requires the
125125+# org.freedesktop.Flatpak talk-name, granted per-browser below.
126126+FP_WRAPPER="#!/bin/sh
127127+if [ \"\${container-}\" = flatpak ]; then
128128+ exec /usr/bin/flatpak-spawn --host $CMD
129129+else
130130+ exec $CMD
131131+fi"
132132+133133+for sub in "${NATIVE_CHROMIUM[@]}"; do
134134+ [[ -d "$HOME/.config/$sub" ]] || continue
135135+ target="$HOME/.config/$sub/NativeMessagingHosts/$NAME.json"
136136+ mkdir -p "$(dirname "$target")"
137137+ chromium_manifest "$WRAPPER" >"$target"
138138+ echo " $target"
139139+ N=$((N + 1))
140140+done
141141+142142+for sub in "${NATIVE_FIREFOX[@]}"; do
143143+ [[ -d "$HOME/$sub" ]] || continue
144144+ target="$HOME/$sub/native-messaging-hosts/$NAME.json"
145145+ mkdir -p "$(dirname "$target")"
146146+ firefox_manifest "$WRAPPER" >"$target"
147147+ echo " $target"
148148+ N=$((N + 1))
149149+done
150150+151151+for entry in "${FLATPAK_CHROMIUM[@]}"; do
152152+ appId="${entry%%:*}"
153153+ sub="${entry##*:}"
154154+ [[ -d "$HOME/.var/app/$appId" ]] || continue
155155+ dir="$HOME/.var/app/$appId/config/$sub/NativeMessagingHosts"
156156+ wp="$dir/switchyard-native-host.sh"
157157+ mkdir -p "$dir"
158158+ printf '%s\n' "$FP_WRAPPER" >"$wp" && chmod +x "$wp"
159159+ chromium_manifest "$wp" >"$dir/$NAME.json"
160160+ flatpak override --user --talk-name=org.freedesktop.Flatpak "$appId"
161161+ echo " $dir/$NAME.json [Flatpak]"
162162+ N=$((N + 1))
163163+done
164164+165165+for entry in "${FLATPAK_FIREFOX[@]}"; do
166166+ appId="${entry%%:*}"
167167+ sub="${entry##*:}"
168168+ [[ -d "$HOME/.var/app/$appId" ]] || continue
169169+ dir="$HOME/.var/app/$appId/$sub/native-messaging-hosts"
170170+ wp="$dir/switchyard-native-host.sh"
171171+ mkdir -p "$dir"
172172+ printf '%s\n' "$FP_WRAPPER" >"$wp" && chmod +x "$wp"
173173+ firefox_manifest "$wp" >"$dir/$NAME.json"
174174+ flatpak override --user --talk-name=org.freedesktop.Flatpak "$appId"
175175+ echo " $dir/$NAME.json [Flatpak]"
176176+ N=$((N + 1))
177177+done
178178+179179+[[ $N -gt 0 ]] || {
180180+ echo
181181+ echo "No supported browsers found."
182182+ exit 0
183183+}
184184+echo
185185+echo "Installed $N manifest(s). Restart your browser to activate."
-185
scripts/install-native-host.sh
···11-#!/usr/bin/env bash
22-# SPDX-License-Identifier: GPL-3.0-or-later
33-# Switchyard native messaging host installer — https://github.com/alyraffauf/Switchyard
44-#
55-# Installs manifests that let the browser extension list your installed browsers.
66-#
77-# For Flatpak browsers this grants --talk-name=org.freedesktop.Flatpak, which
88-# lets code inside the browser sandbox run arbitrary host commands via
99-# flatpak-spawn --host. This weakens the Flatpak sandbox for those browsers.
1010-# Read and understand this before running it.
1111-#
1212-# Usage: bash install-native-host.sh [--uninstall]
1313-1414-set -euo pipefail
1515-1616-# ── Find Switchyard ────────────────────────────────────────────────────────────
1717-1818-CMD=""
1919-for id in io.github.alyraffauf.Switchyard io.github.alyraffauf.Switchyard.Devel; do
2020- if flatpak info "$id" &>/dev/null; then
2121- CMD="/usr/bin/flatpak run --command=switchyard $id --native-host"
2222- break
2323- fi
2424-done
2525-if [[ -z $CMD ]]; then
2626- bin=$(command -v switchyard 2>/dev/null) || {
2727- echo "error: Switchyard not found" >&2
2828- exit 1
2929- }
3030- CMD="$bin --native-host"
3131-fi
3232-3333-# ── Config ─────────────────────────────────────────────────────────────────────
3434-3535-NAME="io.github.alyraffauf.switchyard"
3636-WRAPPER="$HOME/.local/share/switchyard/native-host-wrapper.sh"
3737-CHROME_ORIGIN="chrome-extension://gmdmmjfmpfbmddgphjbkbbmdolkifloi/"
3838-FF_EXT="switchyard@alyraffauf.github.io"
3939-4040-NATIVE_CHROMIUM=(net.imput.helium google-chrome chromium BraveSoftware/Brave-Browser microsoft-edge vivaldi)
4141-NATIVE_FIREFOX=(.mozilla .librewolf .zen)
4242-4343-# (appId:configSubdir) — configSubdir maps to ~/.config/<sub>/ inside the sandbox
4444-FLATPAK_CHROMIUM=(
4545- com.google.Chrome:google-chrome
4646- com.google.ChromeDev:google-chrome-unstable
4747- org.chromium.Chromium:chromium
4848- com.brave.Browser:BraveSoftware/Brave-Browser
4949- com.microsoft.Edge:microsoft-edge
5050- com.vivaldi.Vivaldi:vivaldi
5151- io.github.ungoogled_software.ungoogled_chromium:chromium
5252- net.imput.helium:net.imput.helium
5353-)
5454-5555-# (appId:profileDir) — profileDir is persisted as ~/<sub>/ inside the sandbox
5656-FLATPAK_FIREFOX=(
5757- org.mozilla.firefox:.mozilla
5858- io.gitlab.librewolf-community:.librewolf
5959- io.github.zen_browser.zen:.zen
6060-)
6161-6262-# ── Helpers ────────────────────────────────────────────────────────────────────
6363-6464-N=0
6565-6666-chromium_manifest() {
6767- printf '{"name":"%s","description":"Switchyard browser selector","path":"%s","type":"stdio","allowed_origins":["%s"]}\n' \
6868- "$NAME" "$1" "$CHROME_ORIGIN"
6969-}
7070-7171-firefox_manifest() {
7272- printf '{"name":"%s","description":"Switchyard browser selector","path":"%s","type":"stdio","allowed_extensions":["%s"]}\n' \
7373- "$NAME" "$1" "$FF_EXT"
7474-}
7575-7676-# ── Uninstall ──────────────────────────────────────────────────────────────────
7777-7878-if [[ ${1-} == "--uninstall" ]]; then
7979- echo "Removing Switchyard native messaging host..."
8080-8181- for sub in "${NATIVE_CHROMIUM[@]}"; do
8282- f="$HOME/.config/$sub/NativeMessagingHosts/$NAME.json"
8383- [[ -f $f ]] && rm -f "$f" && echo " removed $f" && N=$((N + 1)) || true
8484- done
8585-8686- for sub in "${NATIVE_FIREFOX[@]}"; do
8787- f="$HOME/$sub/native-messaging-hosts/$NAME.json"
8888- [[ -f $f ]] && rm -f "$f" && echo " removed $f" && N=$((N + 1)) || true
8989- done
9090-9191- for entry in "${FLATPAK_CHROMIUM[@]}"; do
9292- appId="${entry%%:*}"
9393- sub="${entry##*:}"
9494- dir="$HOME/.var/app/$appId/config/$sub/NativeMessagingHosts"
9595- f="$dir/$NAME.json"
9696- [[ -f $f ]] && rm -f "$f" "$dir/switchyard-native-host.sh" && echo " removed $f" && N=$((N + 1)) || true
9797- done
9898-9999- for entry in "${FLATPAK_FIREFOX[@]}"; do
100100- appId="${entry%%:*}"
101101- sub="${entry##*:}"
102102- dir="$HOME/.var/app/$appId/$sub/native-messaging-hosts"
103103- f="$dir/$NAME.json"
104104- [[ -f $f ]] && rm -f "$f" "$dir/switchyard-native-host.sh" && echo " removed $f" && N=$((N + 1)) || true
105105- done
106106-107107- [[ -f $WRAPPER ]] && rm -f "$WRAPPER" && echo " removed $WRAPPER" || true
108108-109109- echo
110110- echo "Removed $N manifest(s)."
111111- exit 0
112112-fi
113113-114114-# ── Install ────────────────────────────────────────────────────────────────────
115115-116116-echo "Installing Switchyard native messaging host..."
117117-118118-mkdir -p "$(dirname "$WRAPPER")"
119119-printf '#!/bin/sh\nexec %s "$@"\n' "$CMD" >"$WRAPPER"
120120-chmod +x "$WRAPPER"
121121-echo "Wrapper: $WRAPPER"
122122-123123-# Flatpak browsers need a different wrapper: they detect the sandbox via
124124-# $container and use flatpak-spawn --host to escape it. This requires the
125125-# org.freedesktop.Flatpak talk-name, granted per-browser below.
126126-FP_WRAPPER="#!/bin/sh
127127-if [ \"\${container-}\" = flatpak ]; then
128128- exec /usr/bin/flatpak-spawn --host $CMD
129129-else
130130- exec $CMD
131131-fi"
132132-133133-for sub in "${NATIVE_CHROMIUM[@]}"; do
134134- [[ -d "$HOME/.config/$sub" ]] || continue
135135- target="$HOME/.config/$sub/NativeMessagingHosts/$NAME.json"
136136- mkdir -p "$(dirname "$target")"
137137- chromium_manifest "$WRAPPER" >"$target"
138138- echo " $target"
139139- N=$((N + 1))
140140-done
141141-142142-for sub in "${NATIVE_FIREFOX[@]}"; do
143143- [[ -d "$HOME/$sub" ]] || continue
144144- target="$HOME/$sub/native-messaging-hosts/$NAME.json"
145145- mkdir -p "$(dirname "$target")"
146146- firefox_manifest "$WRAPPER" >"$target"
147147- echo " $target"
148148- N=$((N + 1))
149149-done
150150-151151-for entry in "${FLATPAK_CHROMIUM[@]}"; do
152152- appId="${entry%%:*}"
153153- sub="${entry##*:}"
154154- [[ -d "$HOME/.var/app/$appId" ]] || continue
155155- dir="$HOME/.var/app/$appId/config/$sub/NativeMessagingHosts"
156156- wp="$dir/switchyard-native-host.sh"
157157- mkdir -p "$dir"
158158- printf '%s\n' "$FP_WRAPPER" >"$wp" && chmod +x "$wp"
159159- chromium_manifest "$wp" >"$dir/$NAME.json"
160160- flatpak override --user --talk-name=org.freedesktop.Flatpak "$appId"
161161- echo " $dir/$NAME.json [Flatpak]"
162162- N=$((N + 1))
163163-done
164164-165165-for entry in "${FLATPAK_FIREFOX[@]}"; do
166166- appId="${entry%%:*}"
167167- sub="${entry##*:}"
168168- [[ -d "$HOME/.var/app/$appId" ]] || continue
169169- dir="$HOME/.var/app/$appId/$sub/native-messaging-hosts"
170170- wp="$dir/switchyard-native-host.sh"
171171- mkdir -p "$dir"
172172- printf '%s\n' "$FP_WRAPPER" >"$wp" && chmod +x "$wp"
173173- firefox_manifest "$wp" >"$dir/$NAME.json"
174174- flatpak override --user --talk-name=org.freedesktop.Flatpak "$appId"
175175- echo " $dir/$NAME.json [Flatpak]"
176176- N=$((N + 1))
177177-done
178178-179179-[[ $N -gt 0 ]] || {
180180- echo
181181- echo "No supported browsers found."
182182- exit 0
183183-}
184184-echo
185185-echo "Installed $N manifest(s). Restart your browser to activate."
+10-1
webextension/src/App.tsx
···9494 <div className="separator" />
9595 )}
9696 {loaded && browsers.length === 0 && (
9797- <div className="hint">Install the native host to list browsers</div>
9797+ <div className="hint">
9898+ <a
9999+ href="https://github.com/alyraffauf/Switchyard/blob/master/docs/WebExtension.md#desktop-integration"
100100+ target="_blank"
101101+ rel="noreferrer"
102102+ >
103103+ Set up desktop integration
104104+ </a>{" "}
105105+ to list browsers
106106+ </div>
98107 )}
99108 {browsers.map((b) => (
100109 <button