Compare commits
1 Commits
mimo/code/
...
mimo/code/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
536744ec21 |
110
app.js
110
app.js
@@ -2516,46 +2516,24 @@ function populateAtlas() {
|
||||
const grid = document.getElementById('atlas-grid');
|
||||
grid.innerHTML = '';
|
||||
|
||||
// Counters by status
|
||||
let onlineCount = 0;
|
||||
let standbyCount = 0;
|
||||
let rebuildingCount = 0;
|
||||
let localCount = 0;
|
||||
let blockedCount = 0;
|
||||
|
||||
// Group portals by environment
|
||||
const byEnv = { production: [], staging: [], local: [] };
|
||||
|
||||
portals.forEach(portal => {
|
||||
const config = portal.config;
|
||||
const status = config.status || 'online';
|
||||
const env = config.environment || 'production';
|
||||
if (config.status === 'online') onlineCount++;
|
||||
if (config.status === 'standby') standbyCount++;
|
||||
|
||||
// Count statuses
|
||||
if (status === 'online' || status === 'active') onlineCount++;
|
||||
else if (status === 'standby') standbyCount++;
|
||||
else if (status === 'rebuilding') rebuildingCount++;
|
||||
else if (status === 'local-only') localCount++;
|
||||
else if (status === 'blocked') blockedCount++;
|
||||
|
||||
// Group by environment
|
||||
if (byEnv[env]) {
|
||||
byEnv[env].push({ config, status });
|
||||
} else {
|
||||
byEnv['production'].push({ config, status });
|
||||
}
|
||||
|
||||
// Create atlas card
|
||||
const card = document.createElement('div');
|
||||
card.className = 'atlas-card';
|
||||
card.style.setProperty('--portal-color', config.color);
|
||||
|
||||
const statusClass = `status-${status}`;
|
||||
const statusClass = `status-${config.status || 'online'}`;
|
||||
|
||||
card.innerHTML = `
|
||||
<div class="atlas-card-header">
|
||||
<div class="atlas-card-name">${config.name}</div>
|
||||
<div class="atlas-card-status ${statusClass}">${status.toUpperCase()}</div>
|
||||
<div class="atlas-card-status ${statusClass}">${config.status || 'ONLINE'}</div>
|
||||
</div>
|
||||
<div class="atlas-card-desc">${config.description}</div>
|
||||
<div class="atlas-card-footer">
|
||||
@@ -2572,18 +2550,9 @@ function populateAtlas() {
|
||||
grid.appendChild(card);
|
||||
});
|
||||
|
||||
// Update footer counts
|
||||
document.getElementById('atlas-online-count').textContent = onlineCount;
|
||||
document.getElementById('atlas-standby-count').textContent = standbyCount;
|
||||
document.getElementById('atlas-rebuilding-count').textContent = rebuildingCount;
|
||||
document.getElementById('atlas-local-count').textContent = localCount;
|
||||
document.getElementById('atlas-blocked-count').textContent = blockedCount;
|
||||
|
||||
// Populate status wall by environment
|
||||
populateStatusWallEnv('production', byEnv.production);
|
||||
populateStatusWallEnv('staging', byEnv.staging);
|
||||
populateStatusWallEnv('local', byEnv.local);
|
||||
|
||||
|
||||
// Update Bannerlord HUD status
|
||||
const bannerlord = portals.find(p => p.config.id === 'bannerlord');
|
||||
if (bannerlord) {
|
||||
@@ -2592,75 +2561,6 @@ function populateAtlas() {
|
||||
}
|
||||
}
|
||||
|
||||
function populateStatusWallEnv(envName, portalList) {
|
||||
const container = document.getElementById(`${envName}-portals`);
|
||||
const summary = document.getElementById(`${envName}-summary`);
|
||||
container.innerHTML = '';
|
||||
|
||||
if (portalList.length === 0) {
|
||||
container.innerHTML = '<div class="status-portal-row"><span class="status-portal-name" style="font-style: italic; color: rgba(160,184,208,0.4);">No worlds</span></div>';
|
||||
summary.textContent = 'No portals in this environment';
|
||||
return;
|
||||
}
|
||||
|
||||
// Count statuses in this environment
|
||||
const statusCounts = {};
|
||||
portalList.forEach(({ config, status }) => {
|
||||
statusCounts[status] = (statusCounts[status] || 0) + 1;
|
||||
});
|
||||
|
||||
// Create portal rows
|
||||
portalList.forEach(({ config, status }) => {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'status-portal-row';
|
||||
|
||||
const indicator = document.createElement('span');
|
||||
indicator.className = `status-portal-indicator status-dot ${status}`;
|
||||
|
||||
const nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'status-portal-name';
|
||||
nameSpan.textContent = config.name;
|
||||
|
||||
const statusSpan = document.createElement('span');
|
||||
statusSpan.style.fontSize = '9px';
|
||||
statusSpan.style.textTransform = 'uppercase';
|
||||
statusSpan.style.marginLeft = '8px';
|
||||
statusSpan.style.color = getStatusColor(status);
|
||||
statusSpan.textContent = status;
|
||||
|
||||
row.appendChild(nameSpan);
|
||||
row.appendChild(statusSpan);
|
||||
row.appendChild(indicator);
|
||||
container.appendChild(row);
|
||||
});
|
||||
|
||||
// Create summary
|
||||
const summaryParts = Object.entries(statusCounts).map(([status, count]) =>
|
||||
`${count} ${status}`
|
||||
);
|
||||
summary.textContent = summaryParts.join(' · ');
|
||||
}
|
||||
|
||||
function getStatusColor(status) {
|
||||
switch (status) {
|
||||
case 'online':
|
||||
case 'active':
|
||||
return 'var(--color-primary)';
|
||||
case 'standby':
|
||||
return 'var(--color-gold)';
|
||||
case 'rebuilding':
|
||||
return '#ffa500';
|
||||
case 'local-only':
|
||||
return '#00ff88';
|
||||
case 'blocked':
|
||||
return '#ff0000';
|
||||
case 'offline':
|
||||
return 'var(--color-danger)';
|
||||
default:
|
||||
return 'var(--color-text-muted)';
|
||||
}
|
||||
}
|
||||
|
||||
function focusPortal(portal) {
|
||||
// Teleport player to a position in front of the portal
|
||||
const offset = new THREE.Vector3(0, 0, 6).applyEuler(new THREE.Euler(0, portal.config.rotation?.y || 0, 0));
|
||||
|
||||
46
index.html
46
index.html
@@ -264,57 +264,11 @@
|
||||
<div class="atlas-grid" id="atlas-grid">
|
||||
<!-- Portals will be injected here -->
|
||||
</div>
|
||||
<!-- Portal Status Wall -->
|
||||
<div class="atlas-status-wall">
|
||||
<div class="status-wall-header">
|
||||
<span class="status-wall-title">WORLD STATUS WALL</span>
|
||||
<span class="status-wall-subtitle">Real-time portal health</span>
|
||||
</div>
|
||||
<div class="status-wall-grid">
|
||||
<div class="status-wall-env" id="status-wall-production">
|
||||
<div class="status-env-header">
|
||||
<span class="status-env-dot production"></span>
|
||||
<span class="status-env-label">PRODUCTION</span>
|
||||
</div>
|
||||
<div class="status-env-portals" id="production-portals"></div>
|
||||
<div class="status-env-summary" id="production-summary"></div>
|
||||
</div>
|
||||
<div class="status-wall-env" id="status-wall-staging">
|
||||
<div class="status-env-header">
|
||||
<span class="status-env-dot staging"></span>
|
||||
<span class="status-env-label">STAGING</span>
|
||||
</div>
|
||||
<div class="status-env-portals" id="staging-portals"></div>
|
||||
<div class="status-env-summary" id="staging-summary"></div>
|
||||
</div>
|
||||
<div class="status-wall-env" id="status-wall-local">
|
||||
<div class="status-env-header">
|
||||
<span class="status-env-dot local"></span>
|
||||
<span class="status-env-label">LOCAL</span>
|
||||
</div>
|
||||
<div class="status-env-portals" id="local-portals"></div>
|
||||
<div class="status-env-summary" id="local-summary"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-wall-legend">
|
||||
<div class="legend-item"><span class="status-dot online"></span> Online</div>
|
||||
<div class="legend-item"><span class="status-dot rebuilding"></span> Rebuilding</div>
|
||||
<div class="legend-item"><span class="status-dot local-only"></span> Local-only</div>
|
||||
<div class="legend-item"><span class="status-dot blocked"></span> Blocked</div>
|
||||
<div class="legend-item"><span class="status-dot offline"></span> Offline</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="atlas-footer">
|
||||
<div class="atlas-status-summary">
|
||||
<span class="status-indicator online"></span> <span id="atlas-online-count">0</span> ONLINE
|
||||
|
||||
<span class="status-indicator standby"></span> <span id="atlas-standby-count">0</span> STANDBY
|
||||
|
||||
<span class="status-indicator rebuilding"></span> <span id="atlas-rebuilding-count">0</span> REBUILDING
|
||||
|
||||
<span class="status-indicator local-only"></span> <span id="atlas-local-count">0</span> LOCAL
|
||||
|
||||
<span class="status-indicator blocked"></span> <span id="atlas-blocked-count">0</span> BLOCKED
|
||||
</div>
|
||||
<div class="atlas-hint">Click a portal to focus or teleport</div>
|
||||
</div>
|
||||
|
||||
112
portal/bannerlord/INSTALL.md
Normal file
112
portal/bannerlord/INSTALL.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Bannerlord Local Install Guide (macOS / Apple Silicon)
|
||||
|
||||
## Goal
|
||||
Run the GOG Mount & Blade II: Bannerlord build natively on Alexander's Mac (arm64, macOS Sequoia+).
|
||||
|
||||
## Prerequisites
|
||||
- macOS 14+ on Apple Silicon (arm64)
|
||||
- ~60 GB free disk space (game + Wine prefix)
|
||||
- GOG installer files in `~/Downloads/`:
|
||||
- `setup_mount__blade_ii_bannerlord_1.3.15.109797_(64bit)_(89124).exe`
|
||||
- `setup_mount__blade_ii_bannerlord_1.3.15.109797_(64bit)_(89124)-1.bin` through `-13.bin`
|
||||
|
||||
## Step 1: Install Porting Kit
|
||||
|
||||
Porting Kit (free) wraps Wine/GPTK for macOS. It has a GUI but we automate what we can.
|
||||
|
||||
```bash
|
||||
brew install --cask porting-kit
|
||||
```
|
||||
|
||||
Launch it once to complete first-run setup:
|
||||
```bash
|
||||
open -a "Porting Kit"
|
||||
```
|
||||
|
||||
## Step 2: Create Wine Prefix + Install Game
|
||||
|
||||
**Option A: Via Porting Kit GUI (recommended)**
|
||||
|
||||
1. Open Porting Kit
|
||||
2. Click "Install Game" → "Custom Port" or search for Bannerlord
|
||||
3. Point it at: `~/Downloads/setup_mount__blade_ii_bannerlord_1.3.15.109797_(64bit)_(89124).exe`
|
||||
4. Follow the GOG installer wizard
|
||||
5. Install to default path inside the Wine prefix
|
||||
6. When done, note the prefix path (usually `~/Library/Application Support/PortingKit/...`)
|
||||
|
||||
**Option B: Manual Wine prefix (advanced)**
|
||||
|
||||
If you have Homebrew Wine (or GPTK) installed:
|
||||
|
||||
```bash
|
||||
# Create prefix
|
||||
export WINEPREFIX="$HOME/Games/Bannerlord"
|
||||
wine64 boot /init
|
||||
|
||||
# Run the GOG installer (it auto-chains the .bin files)
|
||||
cd ~/Downloads
|
||||
wine64 setup_mount__blade_ii_bannerlord_1.3.15.109797_\(64bit\)_\(89124\).exe
|
||||
```
|
||||
|
||||
Follow the GOG installer wizard. Default install path is fine.
|
||||
|
||||
## Step 3: Locate the Game Binary
|
||||
|
||||
After installation, the game executable is at:
|
||||
```
|
||||
$WINEPREFIX/drive_c/GOG Games/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe
|
||||
```
|
||||
|
||||
Or inside Porting Kit's prefix at:
|
||||
```
|
||||
~/Library/Application Support/PortingKit/<prefix-name>/drive_c/GOG Games/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe
|
||||
```
|
||||
|
||||
## Step 4: First Launch
|
||||
|
||||
```bash
|
||||
# Find the actual path first, then:
|
||||
cd "$HOME/Games/Bannerlord/drive_c/GOG Games/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client"
|
||||
wine64 Bannerlord.exe
|
||||
```
|
||||
|
||||
Or use the launcher script:
|
||||
```bash
|
||||
./portal/bannerlord/launch.sh
|
||||
```
|
||||
|
||||
## Step 5: Proof (Operator Checklist)
|
||||
|
||||
- [ ] Game window opens and is visible on screen
|
||||
- [ ] At least the main menu renders (TaleWorlds logo, "Campaign", "Custom Battle", etc.)
|
||||
- [ ] Screenshot taken: save to `portal/bannerlord/proof/`
|
||||
- [ ] Launch command recorded below for repeatability
|
||||
|
||||
**Launch command (fill in after install):**
|
||||
```
|
||||
# Repeatable launch:
|
||||
./portal/bannerlord/launch.sh
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Black screen on launch:**
|
||||
- Try: `wine64 Bannerlord.exe -force-d3d11` or `-force-vulkan`
|
||||
- Set Windows version: `winecfg` → set to Windows 10
|
||||
|
||||
**Missing DLLs:**
|
||||
- Install DirectX runtime: `winetricks d3dx9 d3dx10 d3dx11 vcrun2019`
|
||||
|
||||
**Performance:**
|
||||
- GPTK/Rosetta overhead is expected; 30-60 FPS is normal on M1/M2
|
||||
- Lower in-game graphics settings to "Medium" for first run
|
||||
|
||||
**Installer won't chain .bin files:**
|
||||
- Make sure all .bin files are in the same directory as the .exe
|
||||
- Verify with: `ls -la ~/Downloads/setup_mount__blade_ii_bannerlord_*`
|
||||
|
||||
## References
|
||||
- GamePortal Protocol: `GAMEPORTAL_PROTOCOL.md`
|
||||
- Portal config: `portals.json` (entry: "bannerlord")
|
||||
- GOG App ID: Mount & Blade II: Bannerlord
|
||||
- Steam App ID: 261550 (for Steam stats integration)
|
||||
115
portal/bannerlord/launch.sh
Executable file
115
portal/bannerlord/launch.sh
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env bash
|
||||
# Bannerlord Launcher for macOS (Apple Silicon via Wine/GPTK)
|
||||
# Usage: ./portal/bannerlord/launch.sh [--wine-prefix PATH] [--exe PATH]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# Defaults — override with flags or environment
|
||||
WINEPREFIX="${WINEPREFIX:-$HOME/Games/Bannerlord}"
|
||||
BANNERLORD_EXE=""
|
||||
WINE_CMD=""
|
||||
|
||||
# Parse args
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--wine-prefix) WINEPREFIX="$2"; shift 2 ;;
|
||||
--exe) BANNERLORD_EXE="$2"; shift 2 ;;
|
||||
--help)
|
||||
echo "Usage: $0 [--wine-prefix PATH] [--exe PATH]"
|
||||
echo ""
|
||||
echo "Defaults:"
|
||||
echo " Wine prefix: $WINEPREFIX"
|
||||
echo " Auto-discovers Bannerlord.exe in the prefix"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown arg: $1"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Find wine command
|
||||
find_wine() {
|
||||
if command -v wine64 &>/dev/null; then
|
||||
echo "wine64"
|
||||
elif command -v wine &>/dev/null; then
|
||||
echo "wine"
|
||||
elif [ -f "/Applications/Whisky.app/Contents/Resources/WhiskyCmd" ]; then
|
||||
echo "/Applications/Whisky.app/Contents/Resources/WhiskyCmd"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
WINE_CMD="$(find_wine)"
|
||||
if [ -z "$WINE_CMD" ]; then
|
||||
echo "ERROR: No Wine runtime found."
|
||||
echo "Install one of:"
|
||||
echo " brew install --cask porting-kit"
|
||||
echo " brew install --cask crossover"
|
||||
echo " brew tap apple/apple && brew install game-porting-toolkit"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Wine runtime: $WINE_CMD"
|
||||
echo "Wine prefix: $WINEPREFIX"
|
||||
|
||||
# Find Bannerlord.exe if not specified
|
||||
if [ -z "$BANNERLORD_EXE" ]; then
|
||||
# Search common GOG install paths
|
||||
SEARCH_PATHS=(
|
||||
"$WINEPREFIX/drive_c/GOG Games/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe"
|
||||
"$WINEPREFIX/drive_c/GOG Games/Mount Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe"
|
||||
"$WINEPREFIX/drive_c/Program Files/Mount & Blade II Bannerlord/bin/Win64_Shipping_Client/Bannerlord.exe"
|
||||
)
|
||||
|
||||
# Also search PortingKit prefixes
|
||||
while IFS= read -r -d '' exe; do
|
||||
SEARCH_PATHS+=("$exe")
|
||||
done < <(find "$HOME/Library/Application Support/PortingKit" -name "Bannerlord.exe" -print0 2>/dev/null || true)
|
||||
|
||||
for path in "${SEARCH_PATHS[@]}"; do
|
||||
if [ -f "$path" ]; then
|
||||
BANNERLORD_EXE="$path"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -z "$BANNERLORD_EXE" ] || [ ! -f "$BANNERLORD_EXE" ]; then
|
||||
echo "ERROR: Bannerlord.exe not found."
|
||||
echo "Searched:"
|
||||
echo " $WINEPREFIX/drive_c/GOG Games/"
|
||||
echo " ~/Library/Application Support/PortingKit/"
|
||||
echo ""
|
||||
echo "Run the install first. See: portal/bannerlord/INSTALL.md"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Game binary: $BANNERLORD_EXE"
|
||||
echo "Launching..."
|
||||
echo ""
|
||||
|
||||
# Log the launch for proof
|
||||
LAUNCH_LOG="$SCRIPT_DIR/proof/launch_$(date +%Y%m%d_%H%M%S).log"
|
||||
mkdir -p "$SCRIPT_DIR/proof"
|
||||
{
|
||||
echo "=== Bannerlord Launch ==="
|
||||
echo "Date: $(date -Iseconds)"
|
||||
echo "Wine: $WINE_CMD"
|
||||
echo "Prefix: $WINEPREFIX"
|
||||
echo "Binary: $BANNERLORD_EXE"
|
||||
echo "User: $(whoami)"
|
||||
echo "macOS: $(sw_vers -productVersion)"
|
||||
echo "Arch: $(uname -m)"
|
||||
echo "========================="
|
||||
} > "$LAUNCH_LOG"
|
||||
echo "Launch log: $LAUNCH_LOG"
|
||||
echo ""
|
||||
|
||||
# Set the prefix and launch
|
||||
export WINEPREFIX
|
||||
EXE_DIR="$(dirname "$BANNERLORD_EXE")"
|
||||
cd "$EXE_DIR"
|
||||
exec "$WINE_CMD" "Bannerlord.exe" "$@"
|
||||
16
portal/bannerlord/proof/README.md
Normal file
16
portal/bannerlord/proof/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Bannerlord Proof
|
||||
|
||||
Screenshots and launch logs proving the game runs locally on the Mac.
|
||||
|
||||
## How to capture proof
|
||||
|
||||
1. Launch the game: `./portal/bannerlord/launch.sh`
|
||||
2. Wait for main menu to render
|
||||
3. Take screenshot: `screencapture -x portal/bannerlord/proof/main_menu_$(date +%Y%m%d).png`
|
||||
4. Save launch log (auto-generated by launch.sh)
|
||||
|
||||
## Expected proof files
|
||||
|
||||
- `main_menu_*.png` — screenshot of game main menu
|
||||
- `launch_*.log` — launch command + environment details
|
||||
- `ingame_*.png` — optional in-game screenshots
|
||||
36
portals.json
36
portals.json
@@ -4,7 +4,6 @@
|
||||
"name": "Morrowind",
|
||||
"description": "The Vvardenfell harness. Ash storms and ancient mysteries.",
|
||||
"status": "online",
|
||||
"environment": "production",
|
||||
"color": "#ff6600",
|
||||
"position": { "x": 15, "y": 0, "z": -10 },
|
||||
"rotation": { "y": -0.5 },
|
||||
@@ -18,24 +17,27 @@
|
||||
"id": "bannerlord",
|
||||
"name": "Bannerlord",
|
||||
"description": "Calradia battle harness. Massive armies, tactical command.",
|
||||
"status": "online",
|
||||
"environment": "production",
|
||||
"status": "active",
|
||||
"color": "#ffd700",
|
||||
"position": { "x": -15, "y": 0, "z": -10 },
|
||||
"rotation": { "y": 0.5 },
|
||||
"portal_type": "game-world",
|
||||
"world_category": "strategy-rpg",
|
||||
"environment": "local",
|
||||
"access_mode": "operator",
|
||||
"readiness_state": "active",
|
||||
"telemetry_source": "hermes-harness:bannerlord",
|
||||
"telemetry_source": "local-desktop:bannerlord",
|
||||
"owner": "Timmy",
|
||||
"app_id": 261550,
|
||||
"window_title": "Mount & Blade II: Bannerlord",
|
||||
"install_source": "gog",
|
||||
"gog_version": "1.3.15.109797",
|
||||
"launcher_script": "portal/bannerlord/launch.sh",
|
||||
"install_guide": "portal/bannerlord/INSTALL.md",
|
||||
"destination": {
|
||||
"url": "https://bannerlord.timmy.foundation",
|
||||
"type": "harness",
|
||||
"type": "local-launch",
|
||||
"action_label": "Enter Calradia",
|
||||
"params": { "world": "calradia" }
|
||||
"params": { "world": "calradia", "runtime": "wine/gptk" }
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -43,7 +45,6 @@
|
||||
"name": "Workshop",
|
||||
"description": "The creative harness. Build, script, and manifest.",
|
||||
"status": "online",
|
||||
"environment": "production",
|
||||
"color": "#4af0c0",
|
||||
"position": { "x": 0, "y": 0, "z": -20 },
|
||||
"rotation": { "y": 0 },
|
||||
@@ -58,7 +59,6 @@
|
||||
"name": "Archive",
|
||||
"description": "The repository of all knowledge. History, logs, and ancient data.",
|
||||
"status": "online",
|
||||
"environment": "production",
|
||||
"color": "#0066ff",
|
||||
"position": { "x": 25, "y": 0, "z": 0 },
|
||||
"rotation": { "y": -1.57 },
|
||||
@@ -73,7 +73,6 @@
|
||||
"name": "Chapel",
|
||||
"description": "A sanctuary for reflection and digital peace.",
|
||||
"status": "online",
|
||||
"environment": "production",
|
||||
"color": "#ffd700",
|
||||
"position": { "x": -25, "y": 0, "z": 0 },
|
||||
"rotation": { "y": 1.57 },
|
||||
@@ -88,7 +87,6 @@
|
||||
"name": "Courtyard",
|
||||
"description": "The open nexus. A place for agents to gather and connect.",
|
||||
"status": "online",
|
||||
"environment": "production",
|
||||
"color": "#4af0c0",
|
||||
"position": { "x": 15, "y": 0, "z": 10 },
|
||||
"rotation": { "y": -2.5 },
|
||||
@@ -103,7 +101,6 @@
|
||||
"name": "Gate",
|
||||
"description": "The transition point. Entry and exit from the Nexus core.",
|
||||
"status": "standby",
|
||||
"environment": "staging",
|
||||
"color": "#ff4466",
|
||||
"position": { "x": -15, "y": 0, "z": 10 },
|
||||
"rotation": { "y": 2.5 },
|
||||
@@ -112,20 +109,5 @@
|
||||
"type": "harness",
|
||||
"params": { "mode": "transit" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dev",
|
||||
"name": "Dev Sandbox",
|
||||
"description": "Local development world. Unstable, experimental, honest.",
|
||||
"status": "local-only",
|
||||
"environment": "local",
|
||||
"color": "#00ff88",
|
||||
"position": { "x": 0, "y": 0, "z": 20 },
|
||||
"rotation": { "y": 0 },
|
||||
"destination": {
|
||||
"url": "http://localhost:3000",
|
||||
"type": "local",
|
||||
"params": { "mode": "dev" }
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
169
style.css
169
style.css
@@ -365,11 +365,7 @@ canvas#nexus-canvas {
|
||||
}
|
||||
|
||||
.status-online { background: rgba(74, 240, 192, 0.2); color: var(--color-primary); border: 1px solid var(--color-primary); }
|
||||
.status-active { background: rgba(74, 240, 192, 0.2); color: var(--color-primary); border: 1px solid var(--color-primary); }
|
||||
.status-standby { background: rgba(255, 215, 0, 0.2); color: var(--color-gold); border: 1px solid var(--color-gold); }
|
||||
.status-rebuilding { background: rgba(255, 165, 0, 0.2); color: #ffa500; border: 1px solid #ffa500; }
|
||||
.status-local-only { background: rgba(0, 255, 136, 0.2); color: #00ff88; border: 1px solid #00ff88; }
|
||||
.status-blocked { background: rgba(255, 0, 0, 0.2); color: #ff0000; border: 1px solid #ff0000; }
|
||||
.status-offline { background: rgba(255, 68, 102, 0.2); color: var(--color-danger); border: 1px solid var(--color-danger); }
|
||||
|
||||
.atlas-card-desc {
|
||||
@@ -414,165 +410,6 @@ canvas#nexus-canvas {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Portal Status Wall */
|
||||
.atlas-status-wall {
|
||||
padding: 20px 30px;
|
||||
border-top: 1px solid var(--color-border);
|
||||
background: rgba(10, 15, 40, 0.5);
|
||||
}
|
||||
|
||||
.status-wall-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.status-wall-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: var(--color-primary);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.status-wall-subtitle {
|
||||
font-family: var(--font-body);
|
||||
font-size: 11px;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.status-wall-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.status-wall-env {
|
||||
background: rgba(20, 30, 60, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.status-env-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.status-env-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.status-env-dot.production {
|
||||
background: var(--color-primary);
|
||||
box-shadow: 0 0 5px var(--color-primary);
|
||||
}
|
||||
|
||||
.status-env-dot.staging {
|
||||
background: var(--color-gold);
|
||||
box-shadow: 0 0 5px var(--color-gold);
|
||||
}
|
||||
|
||||
.status-env-dot.local {
|
||||
background: #00ff88;
|
||||
box-shadow: 0 0 5px #00ff88;
|
||||
}
|
||||
|
||||
.status-env-label {
|
||||
font-family: var(--font-display);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.status-env-portals {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
margin-bottom: 10px;
|
||||
max-height: 120px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.status-portal-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-family: var(--font-body);
|
||||
font-size: 11px;
|
||||
color: var(--color-text-muted);
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.status-portal-name {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status-portal-indicator {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
margin-left: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.status-env-summary {
|
||||
font-family: var(--font-body);
|
||||
font-size: 10px;
|
||||
color: var(--color-text-muted);
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.status-wall-legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-family: var(--font-body);
|
||||
font-size: 10px;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.status-dot.online { background: var(--color-primary); box-shadow: 0 0 4px var(--color-primary); }
|
||||
.status-dot.active { background: var(--color-primary); box-shadow: 0 0 4px var(--color-primary); }
|
||||
.status-dot.standby { background: var(--color-gold); box-shadow: 0 0 4px var(--color-gold); }
|
||||
.status-dot.rebuilding { background: #ffa500; box-shadow: 0 0 4px #ffa500; }
|
||||
.status-dot.local-only { background: #00ff88; box-shadow: 0 0 4px #00ff88; }
|
||||
.status-dot.blocked { background: #ff0000; box-shadow: 0 0 4px #ff0000; }
|
||||
.status-dot.offline { background: var(--color-danger); box-shadow: 0 0 4px var(--color-danger); }
|
||||
|
||||
/* Additional status indicators for footer */
|
||||
.status-indicator.rebuilding { background: #ffa500; box-shadow: 0 0 5px #ffa500; }
|
||||
.status-indicator.local-only { background: #00ff88; box-shadow: 0 0 5px #00ff88; }
|
||||
.status-indicator.blocked { background: #ff0000; box-shadow: 0 0 5px #ff0000; }
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
@@ -586,12 +423,6 @@ canvas#nexus-canvas {
|
||||
.atlas-content {
|
||||
max-height: 90vh;
|
||||
}
|
||||
.status-wall-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.atlas-status-wall {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Debug overlay */
|
||||
|
||||
Reference in New Issue
Block a user