diff --git a/.cursor/rules/APP-UI-QUICK-REF.md b/.cursor/rules/APP-UI-QUICK-REF.md new file mode 100644 index 00000000..1609fc93 --- /dev/null +++ b/.cursor/rules/APP-UI-QUICK-REF.md @@ -0,0 +1,98 @@ +# Quick Reference: Archipelago App UI Classes + +## Core CSS Classes + +### Containers + +| Class | Use Case | Features | +|-------|----------|----------| +| `.glass-card` | Main sections, modals, headers | Gradient border, strong blur (24px), inset highlights | +| `.info-card` | Status badges, metric displays | Gradient border, no hover effects | +| `bg-white/5` | Simple info rows | Plain dark background, no borders | + +### Buttons + +| Class | Use Case | Features | +|-------|----------|----------| +| `.info-card-button` | Primary actions (Copy Info, View Logs) | Looks like `.info-card`, lifts and brightens on hover | +| `.glass-button` | Secondary actions (Settings, Close ×) | Simple glass effect, subtle hover | + +--- + +## HTML Snippets + +### Info Card (Display Only) +```html +
+ +
+

Label

+

Value

+
+
+``` + +### Action Button +```html + +``` + +### Info Row +```html +
+
+ + Label +
+ Value +
+``` + +--- + +## Service UI Ports + +| Service | Port | Status | +|---------|------|--------| +| Bitcoin Knots | 8334 | ✅ Live | +| LND | 8081 | ✅ Live | +| Core Lightning | 8082 | 🚧 Planned | +| Mempool | 8083 | 🚧 Planned | + +--- + +## Quick Deploy + +```bash +# From docker/{service}-ui/ directory +sshpass -p "archipelago" rsync -avz --delete ./ archipelago@192.168.1.228:/tmp/{service}-ui-build/ +sshpass -p "archipelago" ssh archipelago@192.168.1.228 \ + "cd /tmp/{service}-ui-build && \ + sudo podman build -t {service}-ui:latest . && \ + sudo podman stop {service}-ui 2>/dev/null || true && \ + sudo podman rm {service}-ui 2>/dev/null || true && \ + sudo podman run -d --name {service}-ui --restart unless-stopped \ + --network=host --label 'com.archipelago.parent-app={service-id}' \ + {service}-ui:latest" +``` + +--- + +## Visual Hierarchy + +``` +┌─────────────────────────────────────┐ +│ .glass-card (Main Container) │ ← Strongest visual weight +│ ┌─────────────────────────────────┐ │ +│ │ .info-card (Status Badge) │ │ ← Medium weight, no hover +│ └─────────────────────────────────┘ │ +│ ┌─────────────────────────────────┐ │ +│ │ bg-white/5 (Info Row) │ │ ← Lightest weight +│ └─────────────────────────────────┘ │ +│ ┌─────────────────────────────────┐ │ +│ │ .info-card-button (Action) │ │ ← Interactive, lifts on hover +│ └─────────────────────────────────┘ │ +└─────────────────────────────────────┘ +``` diff --git a/.cursor/rules/APP-UI-STANDARDS.md b/.cursor/rules/APP-UI-STANDARDS.md new file mode 100644 index 00000000..a478bf02 --- /dev/null +++ b/.cursor/rules/APP-UI-STANDARDS.md @@ -0,0 +1,588 @@ +# Archipelago App UI Standards - For Apps Without Native UIs + +**Version:** 1.0 +**Last Updated:** 2026-02-03 + +## Overview + +This document defines the **standard UI pattern** for containerized applications that don't have their own web interface (e.g., Bitcoin Core, LND, Core Lightning, mempool backend services, etc.). + +These UIs provide a simple, elegant way to: +- Monitor service status and metrics +- View connection information (RPC, REST, gRPC endpoints) +- Access logs and settings +- Copy configuration details for external tools + +--- + +## Architecture + +``` +┌─────────────────────────────────────┐ +│ Nginx Container (Alpine) │ +│ - Serves static HTML/CSS/JS │ +│ - Port 8XXX (unique per service) │ +│ - Optional: Proxies RPC/API calls │ +└─────────────────────────────────────┘ +``` + +### File Structure + +``` +docker/ +├── {service-name}-ui/ +│ ├── index.html # Main UI file +│ ├── Dockerfile # Container build +│ ├── nginx.conf # Nginx config +│ ├── {service-icon} # App icon (svg/webp/png) +│ └── bg-{theme}.jpg # Background image +``` + +--- + +## Standard UI Components + +### 1. **CSS Class System** + +#### `.glass-card` - Main Container Cards +Used for: Header, main sections, modals + +```css +.glass-card { + position: relative; + background: rgba(0, 0, 0, 0.60); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.45), + inset 0 1px 0 rgba(255, 255, 255, 0.22); + border-radius: 1rem; + overflow-x: hidden; + overflow-y: visible; + border: none; +} + +.glass-card::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 2px; + background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), transparent); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + z-index: 1; +} + +.glass-card > * { + position: relative; + z-index: 2; +} +``` + +#### `.info-card` - Stat Display Cards +Used for: Status badges, metric displays (non-interactive) + +```css +.info-card { + position: relative; + background: rgba(0, 0, 0, 0.60); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.45), + inset 0 1px 0 rgba(255, 255, 255, 0.22); + border-radius: 16px; + padding: 12px; + border: none; +} + +.info-card::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 2px; + background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), transparent); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; +} +``` + +**No hover effects** - These are display-only elements. + +#### `.info-card-button` - Interactive Action Buttons +Used for: Primary action buttons (Copy Info, View Logs, etc.) + +```css +.info-card-button { + /* Same base styles as .info-card */ + position: relative; + background: rgba(0, 0, 0, 0.60); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.45), + inset 0 1px 0 rgba(255, 255, 255, 0.22); + border-radius: 16px; + padding: 12px; + transition: all 0.3s ease; + border: none; + cursor: pointer; + color: rgba(255, 255, 255, 0.9); +} + +.info-card-button::before { + /* Same gradient as .info-card */ + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 2px; + background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), transparent); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + transition: all 0.3s ease; +} + +/* Hover state - lifts and brightens */ +.info-card-button:hover { + transform: translateY(-2px); + background: rgba(0, 0, 0, 0.35); + box-shadow: + 0 12px 32px rgba(0, 0, 0, 0.6), + inset 0 1px 0 rgba(255, 255, 255, 0.25); + color: rgba(255, 255, 255, 1); +} + +.info-card-button:hover::before { + background: linear-gradient(135deg, rgba(255, 255, 255, 0.3), transparent); +} + +/* Active state - press down */ +.info-card-button:active { + transform: translateY(1px); +} +``` + +#### `.glass-button` - Secondary Buttons +Used for: Settings, Close (×), secondary actions + +```css +.glass-button { + background-color: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(18px); + -webkit-backdrop-filter: blur(18px); + border: 1px solid rgba(255, 255, 255, 0.18); + color: rgba(255, 255, 255, 0.9); + transition: all 0.3s ease; +} + +.glass-button:hover { + color: white; + background-color: rgba(0, 0, 0, 0.7); +} +``` + +#### Simple Info Rows - `bg-white/5` +Used for: Non-interactive info rows (RPC Host, Network, Status, etc.) + +```html +
+
+ + Label +
+ Value +
+``` + +**No gradient borders** - These are simple read-only display elements. + +--- + +## Standard Layout Pattern + +### 1. **Header Section** (`.glass-card`) + +```html +
+
+ +
+
+ {Service Name} +
+
+ + +
+

{Service Name}

+

{Service Description}

+
+ + +
+
+ +
+
+
+
+``` + +### 2. **Quick Status Bar** (`.glass-card` with `.info-card` grid) + +```html +
+
+
+ +
+ +
+
+``` + +### 3. **Main Content Sections** (`.glass-card` grid) + +```html +
+ +
+
+
+ +
+
+

Section Title

+

Section description

+
+
+ +
+ +
+ + +
+ + +
+``` + +### 4. **Modals** (`.glass-card` with backdrop) + +```html + +``` + +--- + +## Visual Hierarchy + +### **Container Importance (Most → Least)** + +1. **`.glass-card`** - Main containers, sections, modals + - Gradient border, strong blur (24px), inset highlights + +2. **`.info-card`** - Stat displays, status badges + - Gradient border, backdrop blur, **NO hover effects** + +3. **`.info-card-button`** - Primary action buttons + - Same as `.info-card` in default state + - **WITH hover effects** (lift, brighten, enhanced gradient) + +4. **`bg-white/5`** - Simple info rows + - Dark background, **NO borders**, **NO hover** + +5. **`.glass-button`** - Secondary buttons + - Simple glass effect, minimal hover + +--- + +## Port Assignments + +Reserve unique ports for each service UI: + +``` +8334 - Bitcoin Knots UI +8081 - LND UI +8082 - Core Lightning UI (future) +8083 - Mempool UI (future) +8084 - BTCPay Server UI (future) +... +``` + +Update backend's `docker_packages.rs` to map these ports: + +```rust +} else if app_id == "lnd" { + Some("http://localhost:8081".to_string()) +} else if app_id == "bitcoin-knots" { + Some("http://localhost:8334".to_string()) +} +``` + +--- + +## Dockerfile Template + +```dockerfile +FROM docker.io/library/nginx:alpine + +# Copy the HTML file +COPY index.html /usr/share/nginx/html/ + +# Create directories for assets +RUN mkdir -p /usr/share/nginx/html/assets/img/app-icons && \ + mkdir -p /usr/share/nginx/html/assets/img + +# Copy assets +COPY {service-icon} /usr/share/nginx/html/assets/img/app-icons/ +COPY bg-{theme}.jpg /usr/share/nginx/html/assets/img/ + +# Copy nginx config +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] +``` + +--- + +## Nginx Config Template + +### Simple Static Serving (LND, most services) + +```nginx +server { + listen 8XXX; # Unique port for this service + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} +``` + +### With RPC Proxy (Bitcoin Knots) + +```nginx +server { + listen 8334; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # RPC proxy to avoid CORS issues + location /bitcoin-rpc/ { + proxy_pass http://127.0.0.1:8332/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Authorization "Basic {BASE64_ENCODED_CREDS}"; + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Methods "POST, GET, OPTIONS"; + add_header Access-Control-Allow-Headers "Content-Type, Authorization"; + if ($request_method = OPTIONS) { + return 204; + } + } + + location / { + try_files $uri $uri/ /index.html; + } +} +``` + +--- + +## Deployment + +### Build and Run + +```bash +# Build the image +cd docker/{service}-ui +sudo podman build -t {service}-ui:latest . + +# Run the container +sudo podman run -d \ + --name {service}-ui \ + --restart unless-stopped \ + --network=host \ + --label 'com.archipelago.parent-app={service-id}' \ + {service}-ui:latest +``` + +### Backend Integration + +Update `core/archipelago/src/container/docker_packages.rs`: + +```rust +} else if app_id == "{service-id}" { + Some("http://localhost:8XXX".to_string()) +} +``` + +--- + +## Reference Implementations + +### ✅ Bitcoin Knots UI +- **Location:** `docker/bitcoin-ui/` +- **Port:** 8334 +- **Features:** + - Live sync status with animations + - RPC proxy for CORS handling + - Real-time block updates + - Connection info display + +### ✅ LND UI +- **Location:** `docker/lnd-ui/` +- **Port:** 8081 +- **Features:** + - Node status monitoring + - Channel count display + - REST API + gRPC info + - Settings and logs modals + +--- + +## Benefits of This Approach + +1. **Consistency** - All service UIs look and feel the same +2. **Lightweight** - Nginx Alpine base (~10MB) +3. **Fast Development** - Copy template, customize content +4. **Mobile Responsive** - Works on all screen sizes +5. **Low Resource Usage** - Static files, minimal CPU/RAM +6. **Easy Maintenance** - Single pattern to update globally + +--- + +## Creating a New Service UI + +### Step-by-Step Process + +1. **Create Directory Structure** + ```bash + mkdir -p docker/{service}-ui + cd docker/{service}-ui + ``` + +2. **Copy Template Files** + ```bash + cp ../bitcoin-ui/index.html ./ + cp ../bitcoin-ui/Dockerfile ./ + cp ../bitcoin-ui/nginx.conf ./ + ``` + +3. **Customize `index.html`** + - Update title, service name, description + - Modify status cards for your service's metrics + - Update connection info sections (RPC/REST/gRPC) + - Adjust modal content + +4. **Copy Assets** + ```bash + cp ../../neode-ui/public/assets/img/app-icons/{service-icon} ./ + cp ../../neode-ui/public/assets/img/bg-{theme}.jpg ./ + ``` + +5. **Update Nginx Config** + - Set unique port number + - Add RPC proxy if needed + +6. **Update Dockerfile** + - Update asset COPY commands + - Verify port EXPOSE + +7. **Build and Deploy** + ```bash + # Deploy to dev server + sshpass -p "archipelago" rsync -avz --delete ./ archipelago@192.168.1.228:/tmp/{service}-ui-build/ + + # Build on server + sshpass -p "archipelago" ssh archipelago@192.168.1.228 \ + "cd /tmp/{service}-ui-build && \ + sudo podman build -t {service}-ui:latest . && \ + sudo podman stop {service}-ui 2>/dev/null || true && \ + sudo podman rm {service}-ui 2>/dev/null || true && \ + sudo podman run -d --name {service}-ui --restart unless-stopped \ + --network=host --label 'com.archipelago.parent-app={service-id}' \ + {service}-ui:latest" + ``` + +8. **Update Backend** + - Edit `core/archipelago/src/container/docker_packages.rs` + - Add port mapping for your service + +--- + +## Testing Checklist + +- [ ] UI loads correctly at `http://{server}:8XXX/` +- [ ] Logo displays properly +- [ ] Background image loads +- [ ] All status cards show correct info +- [ ] Buttons have proper hover effects +- [ ] Modals open and close correctly +- [ ] Mobile responsive (test on phone) +- [ ] Glass effects render correctly +- [ ] Gradient borders visible +- [ ] Cache busting works (no stale content) + +--- + +## Future Enhancements + +- **Live Data Updates** - WebSocket connections for real-time status +- **Interactive Charts** - Add Chart.js for visualizing metrics +- **Theme Variations** - Allow users to select background themes +- **Dark/Light Mode** - Toggle between color schemes +- **Internationalization** - Support multiple languages +- **Accessibility** - Improve screen reader support + +--- + +## File Locations + +- **UI Standards Doc:** `/Users/dorian/Projects/archy/.cursor/rules/APP-UI-STANDARDS.md` +- **Global UI Standards:** `/Users/dorian/Projects/archy/.cursor/rules/UI-STANDARDS.md` +- **Reference Implementations:** + - Bitcoin UI: `/Users/dorian/Projects/archy/docker/bitcoin-ui/` + - LND UI: `/Users/dorian/Projects/archy/docker/lnd-ui/` + +--- + +**Version:** 1.0 +**Maintained by:** Archipelago Development Team +**Last Updated:** 2026-02-03 diff --git a/docker/lnd-ui/Dockerfile b/docker/lnd-ui/Dockerfile new file mode 100644 index 00000000..f91eaa5b --- /dev/null +++ b/docker/lnd-ui/Dockerfile @@ -0,0 +1,19 @@ +FROM docker.io/library/nginx:alpine + +# Copy the HTML file +COPY index.html /usr/share/nginx/html/ + +# Create directories for assets +RUN mkdir -p /usr/share/nginx/html/assets/img/app-icons && \ + mkdir -p /usr/share/nginx/html/assets/img + +# Copy assets +COPY lnd.svg /usr/share/nginx/html/assets/img/app-icons/ +COPY bg-web5.jpg /usr/share/nginx/html/assets/img/ + +# Copy nginx config +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/lnd-ui/bg-web5.jpg b/docker/lnd-ui/bg-web5.jpg new file mode 100644 index 00000000..cdd3f468 Binary files /dev/null and b/docker/lnd-ui/bg-web5.jpg differ diff --git a/docker/lnd-ui/index.html b/docker/lnd-ui/index.html index aa484aae..c45b127f 100644 --- a/docker/lnd-ui/index.html +++ b/docker/lnd-ui/index.html @@ -49,17 +49,43 @@ pointer-events: none; } + /* Glass card - Archipelago standard with gradient border */ .glass-card { - background-color: rgba(0, 0, 0, 0.65); - backdrop-filter: blur(18px); - -webkit-backdrop-filter: blur(18px); - border: 1px solid rgba(255, 255, 255, 0.18); - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45); + position: relative; + background: rgba(0, 0, 0, 0.60); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.45), + inset 0 1px 0 rgba(255, 255, 255, 0.22); border-radius: 1rem; overflow-x: hidden; overflow-y: visible; + border: none; } + .glass-card::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 2px; + background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), transparent); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + z-index: 1; + } + + .glass-card > * { + position: relative; + z-index: 2; + } + + /* Glass button - Archipelago standard (secondary actions) */ .glass-button { background-color: rgba(0, 0, 0, 0.6); backdrop-filter: blur(18px); @@ -71,6 +97,86 @@ .glass-button:hover { color: white; + background-color: rgba(0, 0, 0, 0.7); + } + + /* Info card - Archipelago standard (display only, no hover) */ + .info-card { + position: relative; + background: rgba(0, 0, 0, 0.60); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.45), + inset 0 1px 0 rgba(255, 255, 255, 0.22); + border-radius: 16px; + padding: 12px; + border: none; + } + + .info-card::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 2px; + background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), transparent); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + } + + /* Interactive button - Same as info-card but with hover effects */ + .info-card-button { + position: relative; + background: rgba(0, 0, 0, 0.60); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.45), + inset 0 1px 0 rgba(255, 255, 255, 0.22); + border-radius: 16px; + padding: 12px; + transition: all 0.3s ease; + border: none; + cursor: pointer; + color: rgba(255, 255, 255, 0.9); + } + + .info-card-button::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 2px; + background: linear-gradient(135deg, rgba(0, 0, 0, 0.8), transparent); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; + transition: all 0.3s ease; + } + + .info-card-button:hover { + transform: translateY(-2px); + background: rgba(0, 0, 0, 0.35); + box-shadow: + 0 12px 32px rgba(0, 0, 0, 0.6), + inset 0 1px 0 rgba(255, 255, 255, 0.25); + color: rgba(255, 255, 255, 1); + } + + .info-card-button:hover::before { + background: linear-gradient(135deg, rgba(255, 255, 255, 0.3), transparent); + } + + .info-card-button:active { + transform: translateY(1px); } .container { @@ -154,7 +260,7 @@
-
+
@@ -167,7 +273,7 @@
-
+
@@ -179,7 +285,7 @@
-
+
@@ -192,13 +298,13 @@
-
+
@@ -211,7 +317,7 @@
@@ -265,7 +371,7 @@
-
@@ -315,7 +421,7 @@
-
@@ -365,7 +471,7 @@
-
@@ -376,7 +482,7 @@

Node Settings

- +
@@ -403,7 +509,7 @@

Node Logs

- +
Loading logs... diff --git a/docker/lnd-ui/lnd.svg b/docker/lnd-ui/lnd.svg new file mode 100644 index 00000000..05df934e --- /dev/null +++ b/docker/lnd-ui/lnd.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/docker/lnd-ui/nginx.conf b/docker/lnd-ui/nginx.conf new file mode 100644 index 00000000..8498243c --- /dev/null +++ b/docker/lnd-ui/nginx.conf @@ -0,0 +1,11 @@ +server { + listen 8081; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +}