100 lines
2.8 KiB
Markdown
100 lines
2.8 KiB
Markdown
# Implementation Blueprint
|
|
|
|
## Backend Build Script (Python Example)
|
|
|
|
```bash
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
WEB_SRC_DIR="$ROOT_DIR/web/src"
|
|
MAC_SRC_DIR="${MAC_SRC_DIR:-$ROOT_DIR/mac/src}"
|
|
PYTHON_BIN="$ROOT_DIR/.venv/bin/python"
|
|
BACKEND_BIN_NAME="${BACKEND_BIN_NAME:-WebBackend}"
|
|
BUILD_ROOT="$ROOT_DIR/dist-backend-build"
|
|
DIST_PATH="$BUILD_ROOT/dist"
|
|
WORK_PATH="$BUILD_ROOT/build"
|
|
SPEC_PATH="$BUILD_ROOT/spec"
|
|
PROJECT_PATH="${MAC_PROJECT_PATH:-}"
|
|
SCHEME="${MAC_SCHEME:-}"
|
|
TARGET_DIR="${EMBEDDED_BACKEND_DIR:-}"
|
|
|
|
if [[ -z "$PROJECT_PATH" ]]; then
|
|
PROJECT_PATH="$(find "$MAC_SRC_DIR" -maxdepth 4 -name "*.xcodeproj" | sort | head -n 1)"
|
|
fi
|
|
PROJECT_DIR="$(dirname "$PROJECT_PATH")"
|
|
if [[ -z "$TARGET_DIR" ]]; then
|
|
TARGET_DIR="$(find "$PROJECT_DIR" -maxdepth 4 -type d -name EmbeddedBackend | head -n 1)"
|
|
fi
|
|
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 "$BACKEND_BIN_NAME" \
|
|
--distpath "$DIST_PATH" \
|
|
--workpath "$WORK_PATH" \
|
|
--specpath "$SPEC_PATH" \
|
|
--add-data "$WEB_SRC_DIR/app.py:." \
|
|
--add-data "$WEB_SRC_DIR/<module_dir>:<module_dir>" \
|
|
"$WEB_SRC_DIR/backend_embedded_launcher.py"
|
|
|
|
cp "$DIST_PATH/$BACKEND_BIN_NAME" "$TARGET_DIR/$BACKEND_BIN_NAME"
|
|
chmod +x "$TARGET_DIR/$BACKEND_BIN_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`.
|
|
- Add toolbar Help action that opens a bundled local `help.html` in a sheet/webview.
|
|
|
|
## 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.
|
|
- Keep a legacy fallback path for migration when needed.
|
|
|
|
## 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
|
|
```
|
|
|
|
Expected DMG output:
|
|
|
|
```text
|
|
build/dmg/<AppName>-<timestamp>.dmg
|
|
```
|
|
|
|
## Git Ignore Baseline
|
|
|
|
```gitignore
|
|
build/
|
|
*.dmg
|
|
rw.*.dmg
|
|
```
|
|
|
|
## 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.
|
|
- Native Help popup renders bundled content.
|
|
- Web-only run has sidebar help fallback.
|
|
- DMG installs and runs on a second machine.
|
|
- DMG is produced under `build/dmg/` and repo root stays clean.
|