Add reusable skill for self-contained macOS web app wrappers

This commit is contained in:
Matt Bruce 2026-02-14 10:49:27 -06:00
parent 19fa3d7371
commit 733a038638
5 changed files with 234 additions and 0 deletions

View File

@ -0,0 +1,72 @@
---
name: macos-selfcontained-webapp
description: "Build or refactor a project so a web app runs as a self-contained macOS app: embedded local backend binary + SwiftUI/WKWebView shell + packaging scripts. Use when a user has web logic and wants no external browser, no separate web-code install, local execution from inside .app, and repeatable DMG packaging."
---
# macOS Self-Contained Web App
## Outcome
Deliver one installable macOS app that:
- launches an embedded backend executable from app resources
- opens the web UI only inside `WKWebView`
- persists settings locally and supports native-to-web sync
- can be packaged as a DMG
## Layout Standard
Use this layout for new projects:
- `web/` for web backend source (`app.py`, modules, requirements)
- `mac/<AppName>Mac/` for Xcode project and SwiftUI shell
- `scripts/` for build and packaging automation
- `docs/` for architecture and onboarding docs
For existing repos, avoid destructive renames unless explicitly requested. Add compatibility paths or migration commits in small steps.
Detailed naming guidance: `references/layout-and-naming.md`
## Workflow
1. Inventory existing architecture.
- Identify backend entrypoint, runtime dependencies, and static/resource files.
- Confirm current launch mode and where browser auto-open is happening.
2. Create embedded backend launcher.
- Add a thin launcher (`backend_embedded_launcher.py` or equivalent) that starts the web server on `127.0.0.1` and reads port from env (for example `MANESH_TRADER_PORT`).
- Ensure browser auto-open is disabled.
3. Build backend into a single executable.
- Add `scripts/build_embedded_backend.sh` that compiles backend into a one-file executable and copies it into the mac target resource folder.
- Include all required web files/modules in build inputs.
4. Implement SwiftUI host shell.
- Use `@Observable` host state (no `ObservableObject`/Combine).
- Start/stop backend process, detect available local port, and handle retries.
- Render URL in `WKWebView` only.
- Keep app responsive when backend fails and surface actionable status.
5. Define settings sync contract.
- Use shared settings file (for example `~/.<app>/settings.json`) as source of truth.
- Normalize settings both in web app and native app.
- Pass effective settings via URL query params on launch/reload/restart.
- Keep onboarding limited to starter fields; preserve advanced fields.
6. Automate packaging.
- Add `scripts/build_selfcontained_mac_app.sh` to build embedded backend then Xcode app.
- Add `scripts/create_installer_dmg.sh` for distributable DMG.
7. Validate.
- Python syntax: `python -m py_compile` on web entrypoint.
- Xcode build: `xcodebuild ... build`.
- Runtime check: no external browser opens, webview loads locally, settings persist across relaunch.
## Required Deliverables
- Embedded backend build script
- macOS app host that launches backend + WKWebView
- Shared settings sync path
- README section for local build + DMG workflow
- Verified build commands and final artifact locations
## References
- Layout and migration rules: `references/layout-and-naming.md`
- Implementation blueprint and command templates: `references/implementation-blueprint.md`
## Bundled Script
Use `scripts/scaffold_web_mac_layout.sh` to create a new standardized folder skeleton for fresh projects.

View File

@ -0,0 +1,4 @@
interface:
display_name: "macOS Self-Contained Web App"
short_description: "Embed web backend in local Mac app."
default_prompt: "Create or refactor this project into a self-contained macOS app wrapper for a local web backend running inside WKWebView."

View File

@ -0,0 +1,69 @@
# Implementation Blueprint
## Backend Build Script (Python Example)
```bash
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PYTHON_BIN="$ROOT_DIR/.venv/bin/python"
APP_NAME="TraderBackend"
BUILD_ROOT="$ROOT_DIR/dist-backend-build"
DIST_PATH="$BUILD_ROOT/dist"
WORK_PATH="$BUILD_ROOT/build"
SPEC_PATH="$BUILD_ROOT/spec"
TARGET_DIR="$ROOT_DIR/mac/TraderMac/TraderMac/EmbeddedBackend"
mkdir -p "$DIST_PATH" "$WORK_PATH" "$SPEC_PATH" "$TARGET_DIR"
"$PYTHON_BIN" -m pip install -q pyinstaller
"$PYTHON_BIN" -m PyInstaller \
--noconfirm --clean --onefile \
--name "$APP_NAME" \
--distpath "$DIST_PATH" \
--workpath "$WORK_PATH" \
--specpath "$SPEC_PATH" \
--add-data "$ROOT_DIR/web/app.py:." \
--add-data "$ROOT_DIR/web/<module_dir>:<module_dir>" \
"$ROOT_DIR/web/backend_embedded_launcher.py"
cp "$DIST_PATH/$APP_NAME" "$TARGET_DIR/$APP_NAME"
chmod +x "$TARGET_DIR/$APP_NAME"
```
## SwiftUI Host Requirements
- Use `@Observable` host object.
- Compute `serverURL` from host/port + query items.
- Start backend once on appear; stop on disappear.
- Render backend URL in `WKWebView`.
- Retry provisional load failure after short delay.
- Keep debug controls behind `#if DEBUG`.
## Settings Sync Contract
- Shared path: `~/.<app>/settings.json`
- Normalize every field on web and native sides.
- Load shared file before app launch.
- Push normalized fields as query params when launching/reloading webview.
- Persist setup-sheet changes back to shared file.
## Packaging Commands
- Build app:
```bash
./scripts/build_selfcontained_mac_app.sh
```
- Create DMG:
```bash
APP_BUNDLE_PATH="dist-mac/<timestamp>/<AppName>Mac.app" ./scripts/create_installer_dmg.sh
```
## Verification Checklist
- No external browser window opens.
- App starts with embedded backend from app resources.
- `WKWebView` loads local URL.
- Settings persist across relaunch and remain in sync.
- DMG installs and runs on a second machine.

View File

@ -0,0 +1,48 @@
# Layout And Naming
## Canonical Structure
```text
<repo-root>/
web/
app.py
<web modules>
requirements.txt
mac/
<AppName>Mac/
<AppName>Mac.xcodeproj/
<AppName>Mac/
ContentView.swift
<AppName>MacApp.swift
EmbeddedBackend/
<AppName>Backend
scripts/
build_embedded_backend.sh
build_selfcontained_mac_app.sh
create_installer_dmg.sh
docs/
architecture.md
onboarding.md
```
## Naming Rules
- Repo: lowercase hyphenated (`trader-desktop-shell`).
- macOS app target/project: PascalCase with `Mac` suffix (`TraderMac`).
- Embedded backend binary: PascalCase + `Backend` (`TraderBackend`).
- Settings directory: lowercase snake or kebab (`~/.trader_app`).
- Environment port var: uppercase snake (`TRADER_PORT`).
## Migration Rules For Existing Projects
1. Do not rename everything in one commit.
2. First, add new folders and compatibility references.
3. Move scripts and docs next.
4. Move web source only after build scripts are updated.
5. Rename Xcode project/target last, with a dedicated verification commit.
## Commit Strategy
1. `chore(layout): add canonical folders`
2. `build(backend): add embedded binary build`
3. `feat(mac-shell): host local backend in webview`
4. `feat(sync): add shared settings contract`
5. `build(packaging): add self-contained app + dmg scripts`
6. `chore(rename): finalize naming migration`

View File

@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 2 ]]; then
echo "Usage: $0 <repo-root> <app-name-pascal>"
echo "Example: $0 ~/Code/trader-app Trader"
exit 1
fi
REPO_ROOT="$1"
APP_NAME="$2"
MAC_APP_NAME="${APP_NAME}Mac"
BACKEND_NAME="${APP_NAME}Backend"
mkdir -p "$REPO_ROOT/web"
mkdir -p "$REPO_ROOT/mac/$MAC_APP_NAME/$MAC_APP_NAME/EmbeddedBackend"
mkdir -p "$REPO_ROOT/scripts"
mkdir -p "$REPO_ROOT/docs"
cat > "$REPO_ROOT/docs/architecture.md" <<DOC
# ${APP_NAME} Architecture
- web backend source: ./web
- mac shell source: ./mac/${MAC_APP_NAME}
- embedded backend binary: ./mac/${MAC_APP_NAME}/${MAC_APP_NAME}/EmbeddedBackend/${BACKEND_NAME}
DOC
cat > "$REPO_ROOT/scripts/README.build.md" <<DOC
# Build Script Placeholders
Add:
- build_embedded_backend.sh
- build_selfcontained_mac_app.sh
- create_installer_dmg.sh
DOC
echo "Created layout for ${APP_NAME}:"
echo "- $REPO_ROOT/web"
echo "- $REPO_ROOT/mac/$MAC_APP_NAME"
echo "- $REPO_ROOT/scripts"
echo "- $REPO_ROOT/docs"