Enhance UI styling and components for Archipelago
- Updated the glass-card and glass-button styles for improved aesthetics and functionality, including gradient borders and hover effects. - Introduced new info-card and info-card-button components with enhanced styling and interactive features. - Refactored existing HTML structure to utilize new card components, improving consistency across the UI. - Enhanced button interactions for better user experience during settings and logs access.
This commit is contained in:
parent
337ebee510
commit
47a56e2212
98
.cursor/rules/APP-UI-QUICK-REF.md
Normal file
98
.cursor/rules/APP-UI-QUICK-REF.md
Normal file
@ -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
|
||||
<div class="info-card flex items-center gap-3">
|
||||
<svg class="w-5 h-5 text-white/60"><!-- icon --></svg>
|
||||
<div>
|
||||
<p class="text-xs text-white/60">Label</p>
|
||||
<p class="text-sm font-medium text-white">Value</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Action Button
|
||||
```html
|
||||
<button class="mt-4 w-full info-card-button text-sm font-medium" onclick="doAction()">
|
||||
Button Text
|
||||
</button>
|
||||
```
|
||||
|
||||
### Info Row
|
||||
```html
|
||||
<div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="w-5 h-5 text-white/60"><!-- icon --></svg>
|
||||
<span class="text-white/80 text-sm">Label</span>
|
||||
</div>
|
||||
<span class="text-white/60 text-sm">Value</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
│ └─────────────────────────────────┘ │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
588
.cursor/rules/APP-UI-STANDARDS.md
Normal file
588
.cursor/rules/APP-UI-STANDARDS.md
Normal file
@ -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
|
||||
<div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="w-5 h-5 text-white/60"><!-- icon --></svg>
|
||||
<span class="text-white/80 text-sm">Label</span>
|
||||
</div>
|
||||
<span class="text-white/60 text-sm">Value</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
**No gradient borders** - These are simple read-only display elements.
|
||||
|
||||
---
|
||||
|
||||
## Standard Layout Pattern
|
||||
|
||||
### 1. **Header Section** (`.glass-card`)
|
||||
|
||||
```html
|
||||
<div class="glass-card p-6 mb-6">
|
||||
<div class="flex flex-col md:flex-row items-center md:items-center gap-4 md:gap-6">
|
||||
<!-- Logo (left) -->
|
||||
<div class="flex-shrink-0">
|
||||
<div class="logo-gradient-border">
|
||||
<img src="/assets/img/app-icons/{service-icon}" alt="{Service Name}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Title and Description (center) -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<h1 class="text-3xl font-bold text-white mb-2">{Service Name}</h1>
|
||||
<p class="text-white/70">{Service Description}</p>
|
||||
</div>
|
||||
|
||||
<!-- Status Info (right) - OPTIONAL for headers with status -->
|
||||
<div class="w-full md:w-auto flex flex-col md:flex-row gap-3 md:gap-4">
|
||||
<div class="info-card flex items-center gap-3">
|
||||
<!-- Status info -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 2. **Quick Status Bar** (`.glass-card` with `.info-card` grid)
|
||||
|
||||
```html
|
||||
<div class="glass-card p-6 mb-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div class="info-card flex items-center justify-between">
|
||||
<!-- Status indicator -->
|
||||
</div>
|
||||
<!-- ... more status cards -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 3. **Main Content Sections** (`.glass-card` grid)
|
||||
|
||||
```html
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
|
||||
<!-- Service 1: Node Status -->
|
||||
<div class="glass-card p-6">
|
||||
<div class="flex items-start gap-4 mb-4">
|
||||
<div class="flex-shrink-0 w-12 h-12 rounded-lg bg-white/10 flex items-center justify-center">
|
||||
<svg><!-- icon --></svg>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h2 class="text-xl font-semibold text-white mb-2">Section Title</h2>
|
||||
<p class="text-white/70 text-sm mb-4">Section description</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
<!-- Info rows (bg-white/5) -->
|
||||
</div>
|
||||
|
||||
<button class="mt-4 w-full info-card-button text-sm font-medium" onclick="action()">
|
||||
Action Button
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ... more sections -->
|
||||
</div>
|
||||
```
|
||||
|
||||
### 4. **Modals** (`.glass-card` with backdrop)
|
||||
|
||||
```html
|
||||
<div class="modal hidden fixed inset-0 bg-black/80 backdrop-blur-sm z-50 items-center justify-center p-4" id="modalId">
|
||||
<div class="glass-card p-6 max-w-2xl w-full max-h-[80vh] overflow-y-auto">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-2xl font-bold text-white">Modal Title</h2>
|
||||
<button onclick="closeModal()" class="glass-button px-3 py-2 rounded-lg text-xl font-medium">×</button>
|
||||
</div>
|
||||
<!-- Modal content -->
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
19
docker/lnd-ui/Dockerfile
Normal file
19
docker/lnd-ui/Dockerfile
Normal file
@ -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;"]
|
||||
BIN
docker/lnd-ui/bg-web5.jpg
Normal file
BIN
docker/lnd-ui/bg-web5.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 570 KiB |
@ -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 @@
|
||||
|
||||
<div class="glass-card p-6 mb-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div class="flex items-center justify-between p-4 bg-white/5 rounded-lg">
|
||||
<div class="info-card flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="relative">
|
||||
<div class="w-3 h-3 rounded-full bg-green-400"></div>
|
||||
@ -167,7 +273,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between p-4 bg-white/5 rounded-lg">
|
||||
<div class="info-card flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="relative">
|
||||
<span class="text-2xl text-orange-500 font-bold">⚡</span>
|
||||
@ -179,7 +285,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between p-4 bg-white/5 rounded-lg">
|
||||
<div class="info-card flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="relative">
|
||||
<div class="w-3 h-3 rounded-full bg-green-400"></div>
|
||||
@ -192,13 +298,13 @@
|
||||
</div>
|
||||
<button
|
||||
onclick="openSettings()"
|
||||
class="px-3 py-1.5 glass-button rounded text-xs font-medium text-white/90 hover:text-white transition-colors"
|
||||
class="px-3 py-1.5 glass-button rounded text-xs font-medium"
|
||||
>
|
||||
Settings
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between p-4 bg-white/5 rounded-lg">
|
||||
<div class="info-card flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="relative">
|
||||
<div class="w-3 h-3 rounded-full bg-green-400"></div>
|
||||
@ -211,7 +317,7 @@
|
||||
</div>
|
||||
<button
|
||||
onclick="openLogs()"
|
||||
class="px-3 py-1.5 glass-button rounded text-xs font-medium text-white/90 hover:text-white transition-colors"
|
||||
class="px-3 py-1.5 glass-button rounded text-xs font-medium"
|
||||
>
|
||||
Logs
|
||||
</button>
|
||||
@ -265,7 +371,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="mt-4 w-full px-4 py-2 glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors" onclick="openSettings()">
|
||||
<button class="mt-4 w-full info-card-button text-sm font-medium" onclick="openSettings()">
|
||||
Node Settings
|
||||
</button>
|
||||
</div>
|
||||
@ -315,7 +421,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="mt-4 w-full px-4 py-2 glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors" onclick="copyRESTInfo()">
|
||||
<button class="mt-4 w-full info-card-button text-sm font-medium" onclick="copyRESTInfo()">
|
||||
Copy REST Info
|
||||
</button>
|
||||
</div>
|
||||
@ -365,7 +471,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="mt-4 w-full px-4 py-2 glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors" onclick="openLogs()">
|
||||
<button class="mt-4 w-full info-card-button text-sm font-medium" onclick="openLogs()">
|
||||
View Logs
|
||||
</button>
|
||||
</div>
|
||||
@ -376,7 +482,7 @@
|
||||
<div class="glass-card p-6 max-w-2xl w-full max-h-[80vh] overflow-y-auto">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-2xl font-bold text-white">Node Settings</h2>
|
||||
<button onclick="closeSettings()" class="w-10 h-10 rounded-lg bg-white/10 border border-white/18 text-white text-xl flex items-center justify-center hover:bg-white/20 transition-colors">×</button>
|
||||
<button onclick="closeSettings()" class="glass-button px-3 py-2 rounded-lg text-xl font-medium">×</button>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<div class="p-3 bg-white/5 rounded-lg">
|
||||
@ -403,7 +509,7 @@
|
||||
<div class="glass-card p-6 max-w-4xl w-full max-h-[80vh] overflow-y-auto">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-2xl font-bold text-white">Node Logs</h2>
|
||||
<button onclick="closeLogs()" class="w-10 h-10 rounded-lg bg-white/10 border border-white/18 text-white text-xl flex items-center justify-center hover:bg-white/20 transition-colors">×</button>
|
||||
<button onclick="closeLogs()" class="glass-button px-3 py-2 rounded-lg text-xl font-medium">×</button>
|
||||
</div>
|
||||
<div class="bg-black/40 rounded-lg p-4 font-mono text-xs text-white/80 whitespace-pre-wrap break-all" id="logsContent">
|
||||
Loading logs...
|
||||
|
||||
21
docker/lnd-ui/lnd.svg
Normal file
21
docker/lnd-ui/lnd.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 179 KiB |
11
docker/lnd-ui/nginx.conf
Normal file
11
docker/lnd-ui/nginx.conf
Normal file
@ -0,0 +1,11 @@
|
||||
server {
|
||||
listen 8081;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user