diff --git a/skills/macos-selfcontained-webapp/SKILL.md b/skills/macos-selfcontained-webapp/SKILL.md new file mode 100644 index 0000000..26e6806 --- /dev/null +++ b/skills/macos-selfcontained-webapp/SKILL.md @@ -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/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 `~/./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. diff --git a/skills/macos-selfcontained-webapp/agents/openai.yaml b/skills/macos-selfcontained-webapp/agents/openai.yaml new file mode 100644 index 0000000..3921dcd --- /dev/null +++ b/skills/macos-selfcontained-webapp/agents/openai.yaml @@ -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." diff --git a/skills/macos-selfcontained-webapp/references/implementation-blueprint.md b/skills/macos-selfcontained-webapp/references/implementation-blueprint.md new file mode 100644 index 0000000..fb77de3 --- /dev/null +++ b/skills/macos-selfcontained-webapp/references/implementation-blueprint.md @@ -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/:" \ + "$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: `~/./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//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. diff --git a/skills/macos-selfcontained-webapp/references/layout-and-naming.md b/skills/macos-selfcontained-webapp/references/layout-and-naming.md new file mode 100644 index 0000000..6c49801 --- /dev/null +++ b/skills/macos-selfcontained-webapp/references/layout-and-naming.md @@ -0,0 +1,48 @@ +# Layout And Naming + +## Canonical Structure + +```text +/ + web/ + app.py + + requirements.txt + mac/ + Mac/ + Mac.xcodeproj/ + Mac/ + ContentView.swift + MacApp.swift + EmbeddedBackend/ + 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` diff --git a/skills/macos-selfcontained-webapp/scripts/scaffold_web_mac_layout.sh b/skills/macos-selfcontained-webapp/scripts/scaffold_web_mac_layout.sh new file mode 100755 index 0000000..0557bd9 --- /dev/null +++ b/skills/macos-selfcontained-webapp/scripts/scaffold_web_mac_layout.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -lt 2 ]]; then + echo "Usage: $0 " + 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" < "$REPO_ROOT/scripts/README.build.md" <