From bc94128c2bb924ef82c8542f687eef0cae26a989 Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Fri, 24 Apr 2026 10:44:13 -0500 Subject: [PATCH] Add Zen Browser as a macOS FMA (#44126) This pull request adds support for the Zen Browser across the application, including its metadata, installation scripts, and UI icon. The changes ensure Zen Browser is now recognized as a maintained app, can be installed/uninstalled via scripts, and displays its icon in the frontend. **Zen Browser Integration** * Added Zen Browser metadata to the maintained apps input (`zen.json`) and output (`apps.json`) files, making it available in the maintained apps list. [[1]](diffhunk://#diff-1227fd3d4a73fdd49df2d7e2977fc94f56c8fe606a444ae5d995916abbbccdb5R1-R8) [[2]](diffhunk://#diff-4c1446cfc02c6bb0bda874481e333c65b84e184fcea52f656b49a6489f73c9c2R1957-R1963) * Created a new versioned output file for Zen Browser (`zen/darwin.json`), including installer and uninstaller scripts, version information, and download details. **Frontend/UI Updates** * Added a new React SVG icon component for Zen Browser (`Zen.tsx`). * Registered the Zen icon in the icon index and mapped the "zen" software name to the new icon, enabling its display in the UI. [[1]](diffhunk://#diff-628095892e1d16090be1db6cc1a5c9cebc65248c32a8b1312385394818f2907bR249) [[2]](diffhunk://#diff-628095892e1d16090be1db6cc1a5c9cebc65248c32a8b1312385394818f2907bR514) --- ee/maintained-apps/inputs/homebrew/zen.json | 8 +++++++ ee/maintained-apps/outputs/apps.json | 7 ++++++ ee/maintained-apps/outputs/zen/darwin.json | 22 ++++++++++++++++++ .../SoftwarePage/components/icons/Zen.tsx | 14 +++++++++++ .../SoftwarePage/components/icons/index.ts | 2 ++ .../assets/images/app-icon-zen-60x60@2x.png | Bin 0 -> 11125 bytes 6 files changed, 53 insertions(+) create mode 100644 ee/maintained-apps/inputs/homebrew/zen.json create mode 100644 ee/maintained-apps/outputs/zen/darwin.json create mode 100644 frontend/pages/SoftwarePage/components/icons/Zen.tsx create mode 100644 website/assets/images/app-icon-zen-60x60@2x.png diff --git a/ee/maintained-apps/inputs/homebrew/zen.json b/ee/maintained-apps/inputs/homebrew/zen.json new file mode 100644 index 0000000000..719645dc46 --- /dev/null +++ b/ee/maintained-apps/inputs/homebrew/zen.json @@ -0,0 +1,8 @@ +{ + "name": "Zen Browser", + "unique_identifier": "app.zen-browser.zen", + "token": "zen", + "installer_format": "dmg", + "slug": "zen/darwin", + "default_categories": ["Browsers"] +} diff --git a/ee/maintained-apps/outputs/apps.json b/ee/maintained-apps/outputs/apps.json index 83f80d61ad..d796b7329e 100644 --- a/ee/maintained-apps/outputs/apps.json +++ b/ee/maintained-apps/outputs/apps.json @@ -1954,6 +1954,13 @@ "unique_identifier": "dev.zed.Zed", "description": "Zed is a multiplayer code editor." }, + { + "name": "Zen Browser", + "slug": "zen/darwin", + "platform": "darwin", + "unique_identifier": "app.zen-browser.zen", + "description": "Zen Browser is a Gecko based web browser." + }, { "name": "Zeplin", "slug": "zeplin/darwin", diff --git a/ee/maintained-apps/outputs/zen/darwin.json b/ee/maintained-apps/outputs/zen/darwin.json new file mode 100644 index 0000000000..1e09fcf9c0 --- /dev/null +++ b/ee/maintained-apps/outputs/zen/darwin.json @@ -0,0 +1,22 @@ +{ + "versions": [ + { + "version": "1.19.8b", + "queries": { + "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'app.zen-browser.zen';", + "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM apps WHERE bundle_identifier = 'app.zen-browser.zen' AND version_compare(bundle_short_version, '1.19.8b') < 0);" + }, + "installer_url": "https://github.com/zen-browser/desktop/releases/download/1.19.8b/zen.macos-universal.dmg", + "install_script_ref": "b195d3bf", + "uninstall_script_ref": "045f019e", + "sha256": "08b06881fea899fc4f2adac93232c028a71334bbba6945a00268279e31108355", + "default_categories": [ + "Browsers" + ] + } + ], + "refs": { + "045f019e": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nsudo rm -rf \"$APPDIR/Zen.app\"\nsudo rmdir '~/Library/Caches/Mozilla'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Zen'\ntrash $LOGGED_IN_USER '~/Library/Caches/Mozilla/updates/Applications/Zen Browser'\ntrash $LOGGED_IN_USER '~/Library/Caches/Mozilla/updates/Applications/Zen'\ntrash $LOGGED_IN_USER '~/Library/Caches/Zen'\ntrash $LOGGED_IN_USER '~/Library/Preferences/app.zen-browser.zen.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/org.mozilla.com.zen.browser.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/app.zen-browser.zen.savedState'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/org.mozilla.com.zen.browser.savedState'\n", + "b195d3bf": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Launch the app in the logged-in user's GUI session. Apps launched by root\n # won't register with the user's Dock/GUI, so run 'open' as the console user.\n # Use 'launchctl asuser' to bootstrap into the console user's Mach namespace\n # and GUI session — 'sudo -u' alone doesn't do this, which can cause\n # LSOpenURLsWithRole() failures even when 'open' exits 0.\n local open_status=0\n if [[ $EUID -eq 0 ]]; then\n local console_uid\n console_uid=$(id -u \"$console_user\")\n /bin/launchctl asuser \"$console_uid\" sudo -u \"$console_user\" open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n else\n open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n fi\n\n if [[ $open_status -eq 0 ]]; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nhdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\n# copy to the applications folder\nquit_and_track_application 'app.zen-browser.zen'\nif [ -d \"$APPDIR/Zen.app\" ]; then\n\tsudo mv \"$APPDIR/Zen.app\" \"$TMPDIR/Zen.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Zen.app\" \"$APPDIR\"\nrelaunch_application 'app.zen-browser.zen'\n" + } +} diff --git a/frontend/pages/SoftwarePage/components/icons/Zen.tsx b/frontend/pages/SoftwarePage/components/icons/Zen.tsx new file mode 100644 index 0000000000..992a83546c --- /dev/null +++ b/frontend/pages/SoftwarePage/components/icons/Zen.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +import type { SVGProps } from "react"; + +const Zen = (props: SVGProps) => ( + + + +); +export default Zen; diff --git a/frontend/pages/SoftwarePage/components/icons/index.ts b/frontend/pages/SoftwarePage/components/icons/index.ts index 243c85c645..5c02153152 100644 --- a/frontend/pages/SoftwarePage/components/icons/index.ts +++ b/frontend/pages/SoftwarePage/components/icons/index.ts @@ -246,6 +246,7 @@ import WrikeForMac from "./WrikeForMac"; import YubicoAuthenticator from "./YubicoAuthenticator"; import YubikeyManager from "./YubikeyManager"; import Zed from "./Zed"; +import Zen from "./Zen"; import Zeplin from "./Zeplin"; import ZeroOneZeroEditor from "./010Editor"; import Zoom from "./Zoom"; @@ -510,6 +511,7 @@ export const SOFTWARE_NAME_TO_ICON_MAP = { "yubico authenticator": YubicoAuthenticator, "yubikey manager": YubikeyManager, zed: Zed, + zen: Zen, zeplin: Zeplin, zotero: Zotero, } as const; diff --git a/website/assets/images/app-icon-zen-60x60@2x.png b/website/assets/images/app-icon-zen-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cf9060d15c7cfcff65c67be092fe4239e91a1ccf GIT binary patch literal 11125 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