diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e713b03..c1d9904 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,7 +54,8 @@ jobs: file "$APP_PATH/Contents/MacOS/Space" | grep -q "$WANT_ARCH" || { echo "ERROR: wrong architecture"; file "$APP_PATH/Contents/MacOS/Space"; exit 1; } /usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$APP_PATH/Contents/Info.plist" | grep -qx "de.studiofritzgnad.space" || { echo "ERROR: bundle id not patched"; exit 1; } grep -q "hasVisibleWindows && mainWindow" "$APP_PATH/Contents/Resources/app/lib/main.js" || { echo "ERROR: activate-handler guard not patched"; exit 1; } - echo "Verified: arch, bundle id, and activate-handler patch OK" + grep -q "Reset Cache and Cookies" "$APP_PATH/Contents/Resources/app/lib/main.js" || { echo "ERROR: app-menu reset item not patched"; exit 1; } + echo "Verified: arch, bundle id, activate-handler, and app-menu patches OK" - name: Import signing certificate (optional) if: env.MAC_CERT_P12_BASE64 != '' run: | diff --git a/README.md b/README.md index 64e5428..9edf39f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Create Nativefier desktop apps for `https://space.studiofritzgnad.de` for: - **macOS Apple Silicon (arm64)** - **macOS Intel (x64)** -**Current Version**: 1.8.5 +**Current Version**: 1.8.6 Icons live in `assets/`: @@ -22,12 +22,21 @@ All builds use persistent session storage so you stay logged in across app resta **Important**: Cookies persist automatically - no special configuration needed. The app uses Electron's built-in cookie storage which saves cookies to disk in the user data directory. -To clear login data and cookies, delete the respective directory: -- macOS: `rm -rf ~/Library/Application\ Support/Space` -- Windows: Delete `%APPDATA%\Space` folder - The app will remember your login as long as the website sets persistent cookies (typically when "Remember me" is checked during login). +#### Resetting cache and cookies + +On macOS, **Space → Reset Cache and Cookies…** clears the HTTP cache, deletes cookies (which signs you out), and reloads the window. It asks for confirmation first. Use it when the app serves stale assets or a login gets stuck. + +The menu item is injected into the nativefier-built app by `scripts/patch-app-menu.mjs`, which runs from `scripts/patch-mac-plist.sh` during the macOS build. Nativefier is archived upstream, so the bundled `main.js` is patched post-build rather than forked; the patch fails the build if nativefier's menu template ever moves. + +Other ways to clear state, on either platform: + +- **Edit → Clear App Data** (built into nativefier) wipes *everything* — cookies, local storage, IndexedDB, service workers — after a confirmation prompt. +- Delete the user data directory: + - macOS: `rm -rf ~/Library/Application\ Support/Space` + - Windows: Delete `%APPDATA%\Space` folder + ### Camera & microphone (macOS) The build patches the app's `Info.plist` with `NSCameraUsageDescription`/`NSMicrophoneUsageDescription`, sets the stable bundle id `de.studiofritzgnad.space`, and re-signs the bundle ad-hoc (`scripts/patch-mac-plist.sh`). All three steps are required: without a valid signature covering the patched plist, macOS silently denies camera/mic access without ever showing a prompt, and the app never appears under System Settings → Privacy & Security → Camera/Microphone. diff --git a/package.json b/package.json index 6617fe1..ab49fcc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "space-nativefier", - "version": "1.8.5", + "version": "1.8.6", "private": true, "description": "Nativefier desktop apps for space.studiofritzgnad.de", "license": "MIT", diff --git a/scripts/patch-app-menu.mjs b/scripts/patch-app-menu.mjs new file mode 100755 index 0000000..febff7f --- /dev/null +++ b/scripts/patch-app-menu.mjs @@ -0,0 +1,83 @@ +#!/usr/bin/env node +// Adds a "Reset Cache and Cookies…" item to the top of the macOS app menu +// (the "Space" menu) in a built app's bundled main.js. +// +// Nativefier only offers Edit → Clear App Data, which wipes *all* site data +// (local storage, IndexedDB, service workers, …) and leaves the page running +// on the now-stale state. The item added here does the narrower, far more +// common thing: drop the HTTP cache, delete cookies (signing the user out), +// and reload. Nativefier is archived upstream, so we patch its bundled +// main.js after the build rather than carrying a fork. +// +// Usage: node scripts/patch-app-menu.mjs + +import { readFile, writeFile } from 'node:fs/promises'; + +// Also grepped for by the CI "Verify app arch and patches" step, so a silently +// unpatched build can never ship. +const MARKER = 'Reset Cache and Cookies'; + +// Nativefier builds the macOS app menu as `electronMenu`; macOS renders its +// label as the app name, which is why this shows up as the "Space" menu. +const ANCHOR = + /(const electronMenu\s*=\s*\{\s*label:\s*'E&lectron',\s*submenu:\s*\[)/; + +// Sits above Services, where macOS apps put app-level actions. `mainWindow`, +// `electron_1` and `log` are all in scope at this point in generateMenu(). +const MENU_ITEM = ` + { + label: 'Reset Cache and Cookies…', + click: (item, focusedWindow) => { + const targetWindow = focusedWindow || mainWindow; + if (!targetWindow) { + log.error('Reset Cache and Cookies: no window available'); + return; + } + (async () => { + const { response } = await electron_1.dialog.showMessageBox(targetWindow, { + type: 'warning', + buttons: ['Reset', 'Cancel'], + defaultId: 1, + cancelId: 1, + title: 'Reset cache and cookies', + message: 'Reset cache and delete cookies?', + detail: 'This clears cached files and signs you out of Space. The window reloads afterwards.', + }); + if (response !== 0) { + return; + } + const { session } = targetWindow.webContents; + await session.clearCache(); + await session.clearStorageData({ storages: ['cookies'] }); + targetWindow.webContents.reloadIgnoringCache(); + })().catch((err) => log.error('Reset Cache and Cookies ERROR', err)); + }, + }, + { + type: 'separator', + },`; + +const mainJsPath = process.argv[2]; +if (!mainJsPath) { + console.error('Usage: patch-app-menu.mjs '); + process.exit(1); +} + +const source = await readFile(mainJsPath, 'utf8'); + +if (source.includes(MARKER)) { + console.log(`App menu already patched, nothing to do: ${mainJsPath}`); + process.exit(0); +} + +if (!ANCHOR.test(source)) { + console.error( + `ERROR: macOS app menu template not found in ${mainJsPath}. ` + + 'Nativefier likely changed its menu code — update the anchor in scripts/patch-app-menu.mjs.', + ); + process.exit(1); +} + +await writeFile(mainJsPath, source.replace(ANCHOR, `$1${MENU_ITEM}`), 'utf8'); + +console.log(`Added "Reset Cache and Cookies…" to the app menu in: ${mainJsPath}`); diff --git a/scripts/patch-mac-plist.sh b/scripts/patch-mac-plist.sh index e4f0153..cc18701 100755 --- a/scripts/patch-mac-plist.sh +++ b/scripts/patch-mac-plist.sh @@ -15,6 +15,7 @@ set -euo pipefail # Usage: patch-mac-plist.sh /path/to/Space-darwin- APP_PARENT="${1:?Usage: patch-mac-plist.sh }" +SCRIPT_DIR="$(dirname "$(realpath "$0")")" # Locate the .app bundle inside the given directory. APP_BUNDLE=$(find "${APP_PARENT}" -maxdepth 1 -name "*.app" -type d | head -n 1) @@ -60,6 +61,10 @@ if [[ -f "${MAIN_JS}" ]]; then echo "ERROR: activate-handler pattern not found in ${MAIN_JS}" exit 1 fi + + # Add "Reset Cache and Cookies…" to the Space menu. Exits non-zero if the + # menu template moved, so an unpatched build fails here instead of shipping. + node "${SCRIPT_DIR}/patch-app-menu.mjs" "${MAIN_JS}" fi # Re-sign the whole bundle ad-hoc so the signature covers the patched plist. diff --git a/scripts/version.sh b/scripts/version.sh index a50a492..a160e26 100644 --- a/scripts/version.sh +++ b/scripts/version.sh @@ -1,3 +1,3 @@ # Single source of truth for the app version. # Sourced by build.sh and the per-platform build scripts. -VERSION="1.8.5" +VERSION="1.8.6"