Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,5 @@ __pycache__/
*.log
*.log.[0-9]
/dist/

# Claude Code local files
.claude/
scripts/
# Scratch config created by scripts/run-dev-ui
.devdata/
76 changes: 76 additions & 0 deletions scripts/run-dev-ui.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# run-dev-ui.ps1 - start the multiACE web UI locally, against a real
# Moonraker if one is reachable and against tests\fixtures\*.json if not.
#
# .\scripts\run-dev-ui.ps1
# $env:MOONRAKER_URL = "http://printer:7125"; .\scripts\run-dev-ui.ps1
# $env:MULTIACE_MOCK_MODE = "1"; .\scripts\run-dev-ui.ps1 # force mock
# $env:MULTIACE_MOCK_STATE_FILE = "mock_state_head.json"; .\scripts\run-dev-ui.ps1
# # dashboard in head mode instead of the default multi-ACE fixture
#
# Mock mode also unlocks POST /api/debug/simulate (inject a fake error
# notification) and GET /api/debug/sample-gcode (a small 4-filament
# sample print, tests\fixtures\sample_4color.gcode).

$ErrorActionPreference = "Stop"

$Port = if ($env:PORT) { $env:PORT } else { "7126" }
$MoonrakerUrl = if ($env:MOONRAKER_URL) { $env:MOONRAKER_URL } else { "http://127.0.0.1:7125" }
$RepoRoot = Split-Path -Parent $PSScriptRoot
$BackendDir = Join-Path $RepoRoot "multiace\web\backend"

if ($env:MULTIACE_MOCK_MODE) {
Write-Host "-> mock mode forced by MULTIACE_MOCK_MODE=$($env:MULTIACE_MOCK_MODE)"
} else {
try {
Invoke-WebRequest -Uri "$MoonrakerUrl/printer/info" -TimeoutSec 2 -UseBasicParsing | Out-Null
Write-Host "OK Moonraker found at $MoonrakerUrl - using real data"
$env:MULTIACE_MOCK_MODE = "0"
} catch {
Write-Host "-- Moonraker not reachable at $MoonrakerUrl - using mock data"
$env:MULTIACE_MOCK_MODE = "1"
}
}

$env:MOONRAKER_URL = $MoonrakerUrl
if (-not $env:MULTIACE_MOCK_DIR) {
$env:MULTIACE_MOCK_DIR = Join-Path $RepoRoot "tests\fixtures"
}
# The installer copies the catalogs next to the backend (web\i18n); in a
# checkout they live at multiace\i18n. Without this every label in the UI
# renders as its raw key.
if (-not $env:MULTIACE_I18N_DIR) {
$env:MULTIACE_I18N_DIR = Join-Path $RepoRoot "multiace\i18n"
}
# A dev box has no /home/lava; point the config editor at a scratch copy
# so saving from the UI cannot 404 (and cannot touch a real printer).
if (-not $env:MULTIACE_CFG_PATH) {
$devCfg = Join-Path $RepoRoot ".devdata\ace.cfg"
$devDir = Split-Path -Parent $devCfg
if (-not (Test-Path $devDir)) { New-Item -ItemType Directory -Path $devDir | Out-Null }
if (-not (Test-Path $devCfg)) {
Copy-Item (Join-Path $RepoRoot "multiace\config\extended\ace.cfg") $devCfg
}
$env:MULTIACE_CFG_PATH = $devCfg
}
Write-Host " config: $($env:MULTIACE_CFG_PATH)"

$Python = if ($env:PYTHON) { $env:PYTHON } else { "python" }
$Url = "http://127.0.0.1:$Port/"

Start-Job -ScriptBlock {
param($u)
Start-Sleep -Seconds 2
Start-Process $u
} -ArgumentList $Url | Out-Null

if ($env:MULTIACE_MOCK_MODE -eq "1") {
Write-Host " mock: sample print at http://127.0.0.1:$Port/api/debug/sample-gcode"
}

Write-Host "Dev server on $Url (Ctrl+C to stop)"
Push-Location $BackendDir
try {
& $Python -m uvicorn main:app --host 127.0.0.1 --port $Port --reload
} finally {
Pop-Location
}
84 changes: 84 additions & 0 deletions scripts/run-dev-ui.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/bin/bash
# run-dev-ui.sh - start the multiACE web UI locally, against a real
# Moonraker if one is reachable and against tests/fixtures/*.json if not.
#
# ./scripts/run-dev-ui.sh # auto-detect
# MOONRAKER_URL=http://printer:7125 ./scripts/run-dev-ui.sh
# MULTIACE_MOCK_MODE=1 ./scripts/run-dev-ui.sh # force mock
# MULTIACE_MOCK_STATE_FILE=mock_state_head.json ./scripts/run-dev-ui.sh
# # dashboard in head mode instead of the default multi-ACE fixture
#
# Mock mode also unlocks POST /api/debug/simulate (inject a fake error
# notification) and GET /api/debug/sample-gcode (a small 4-filament
# sample print, tests/fixtures/sample_4color.gcode).
set -e

PORT="${PORT:-7126}"
MOONRAKER_URL="${MOONRAKER_URL:-http://127.0.0.1:7125}"
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BACKEND_DIR="$REPO_ROOT/multiace/web/backend"

# Git Bash / MSYS hands POSIX paths ("/c/Users/…") to a NATIVE Windows
# Python, which reads them as drive-relative and silently finds nothing -
# every label in the UI then renders as its raw i18n key. Convert every
# path we export. No-op on Linux/macOS, where cygpath does not exist.
winpath() {
if command -v cygpath >/dev/null 2>&1; then cygpath -w "$1"; else printf '%s' "$1"; fi
}

if [ -n "$MULTIACE_MOCK_MODE" ]; then
echo "-> mock mode forced by MULTIACE_MOCK_MODE=$MULTIACE_MOCK_MODE"
elif curl -fsS --max-time 2 "$MOONRAKER_URL/printer/info" >/dev/null 2>&1; then
echo "OK Moonraker found at $MOONRAKER_URL - using real data"
MULTIACE_MOCK_MODE=0
else
echo "-- Moonraker not reachable at $MOONRAKER_URL - using mock data"
MULTIACE_MOCK_MODE=1
fi

export MULTIACE_MOCK_MODE
export MOONRAKER_URL
export MULTIACE_MOCK_DIR="${MULTIACE_MOCK_DIR:-$(winpath "$REPO_ROOT/tests/fixtures")}"
# The installer copies the catalogs next to the backend (web/i18n); in a
# checkout they live at multiace/i18n. Without this every label in the UI
# renders as its raw key.
export MULTIACE_I18N_DIR="${MULTIACE_I18N_DIR:-$(winpath "$REPO_ROOT/multiace/i18n")}"
# A dev box has no /home/lava; point the config editor at a scratch copy
# so saving from the UI cannot 404 (and cannot touch a real printer).
if [ -z "$MULTIACE_CFG_PATH" ]; then
MULTIACE_CFG_PATH="$REPO_ROOT/.devdata/ace.cfg"
mkdir -p "$(dirname "$MULTIACE_CFG_PATH")"
[ -f "$MULTIACE_CFG_PATH" ] || cp "$REPO_ROOT/multiace/config/extended/ace.cfg" "$MULTIACE_CFG_PATH"
MULTIACE_CFG_PATH="$(winpath "$MULTIACE_CFG_PATH")"
export MULTIACE_CFG_PATH
fi
echo " config: $MULTIACE_CFG_PATH"

# Pick an interpreter that actually RUNS: a python3 that merely exists on
# PATH is not enough (a broken Windows Store / stale install shim is on
# most dev boxes, and it fails only once uvicorn is already "started").
PY=""
for cand in "$PYTHON" python3 python py; do
[ -n "$cand" ] || continue
if "$cand" -c 'import sys' >/dev/null 2>&1; then PY="$cand"; break; fi
done
if [ -z "$PY" ]; then
echo "No working Python found - set PYTHON=/path/to/python" >&2
exit 1
fi

URL="http://127.0.0.1:$PORT/"
(
sleep 2
if command -v xdg-open >/dev/null 2>&1; then xdg-open "$URL"
elif command -v open >/dev/null 2>&1; then open "$URL"
fi
) &

if [ "$MULTIACE_MOCK_MODE" = "1" ]; then
echo " mock: sample print at http://127.0.0.1:$PORT/api/debug/sample-gcode"
fi

echo "Dev server on $URL (Ctrl+C to stop)"
cd "$BACKEND_DIR"
exec "$PY" -m uvicorn main:app --host 127.0.0.1 --port "$PORT" --reload
Loading