fix: BUG-33 CPU threshold, TASK-27 tab icons, TASK-36 iframe errors
- BUG-33: CPU load alert threshold increased from 2x to 4x core count (8→16 on 4-core machine) to reduce false alerts during container ops - TASK-27: Launch buttons for new-tab apps now show external link icon (BTCPay, Grafana, PhotoPrism, Portainer, OnlyOffice, etc.) - TASK-36: Iframe error screen now distinguishes between X-Frame-Options blocked vs container not reachable, with appropriate messaging Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
e1b2ade7c6
commit
302f22019d
@@ -1,133 +0,0 @@
|
||||
# ATOB Quick Start Guide
|
||||
|
||||
## 🚀 See ATOB in Action (30 seconds)
|
||||
|
||||
### Step 1: Start the Dev Server
|
||||
```bash
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Step 2: Login
|
||||
- Open: http://localhost:8100
|
||||
- Password: `password123`
|
||||
|
||||
### Step 3: View ATOB
|
||||
- Click "Apps" in the sidebar
|
||||
- See ATOB pre-installed and running
|
||||
- Click the **"Launch"** button (gradient blue)
|
||||
- ATOB web app opens in new tab!
|
||||
|
||||
## 📦 What's Pre-Installed
|
||||
|
||||
ATOB comes pre-loaded in the mock backend with:
|
||||
- ✅ Status: Running
|
||||
- ✅ Version: 0.1.0
|
||||
- ✅ Icon: Blue ATOB logo
|
||||
- ✅ Launch button: Opens https://app.atobitcoin.io
|
||||
- ✅ Start/Stop/Restart: Full controls
|
||||
|
||||
## 🎯 Features to Test
|
||||
|
||||
### In Apps List
|
||||
1. See ATOB card with icon
|
||||
2. Status badge shows "running" (green)
|
||||
3. Launch button appears (gradient)
|
||||
4. Click Launch → opens web app
|
||||
|
||||
### In App Details
|
||||
1. Click on ATOB card
|
||||
2. See full description
|
||||
3. Big Launch button at top
|
||||
4. Action buttons below
|
||||
5. Back to Apps link
|
||||
|
||||
### Actions Available
|
||||
- **Launch**: Opens ATOB web app
|
||||
- **Stop**: Simulates stopping (mock)
|
||||
- **Restart**: Simulates restart (mock)
|
||||
- Click card → View details
|
||||
|
||||
## 🎨 Visual Elements
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Apps │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ [ATOB Icon] A to B Bitcoin │ │
|
||||
│ │ Tools and services │ │
|
||||
│ │ [running] v0.1.0 │ │
|
||||
│ │ │ │
|
||||
│ │ [Launch] [Stop] │ │ ← Launch button!
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ [Bitcoin Icon] Bitcoin Core │ │
|
||||
│ │ ... │ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 📋 Mock Data Structure
|
||||
|
||||
ATOB is defined in `neode-ui/mock-backend.js`:
|
||||
|
||||
```javascript
|
||||
'atob': {
|
||||
title: 'A to B Bitcoin',
|
||||
version: '0.1.0',
|
||||
status: 'running', // ← Shows as running
|
||||
state: 'installed', // ← Already installed
|
||||
manifest: {
|
||||
id: 'atob',
|
||||
interfaces: {
|
||||
main: {
|
||||
ui: true, // ← Makes Launch button appear
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Launch Button Not Showing?
|
||||
- Check app state is 'running'
|
||||
- Verify manifest has `interfaces.main.ui: true`
|
||||
- Restart dev server
|
||||
|
||||
### ATOB Not in Apps List?
|
||||
- Check mock-backend.js has atob entry
|
||||
- Verify WebSocket connection
|
||||
- Check browser console for errors
|
||||
|
||||
### Launch Opens Wrong URL?
|
||||
- Check Apps.vue launchApp() function
|
||||
- Should open: https://app.atobitcoin.io
|
||||
|
||||
## 🎁 Bonus: Production Package
|
||||
|
||||
A complete s9pk package is also available at:
|
||||
`~/atob-package/atob.s9pk` (23MB)
|
||||
|
||||
Install on real Neode server:
|
||||
```bash
|
||||
start-cli package.install ~/atob-package/atob.s9pk
|
||||
```
|
||||
|
||||
## 📖 More Info
|
||||
|
||||
- **Full Integration Guide**: `/Users/tx1138/Code/Neode/ATOB_INTEGRATION.md`
|
||||
- **S9PK Installation**: `~/atob-package/INSTALLATION.md`
|
||||
- **Marketplace Guide**: `neode-ui/MARKETPLACE_SETUP.md`
|
||||
|
||||
---
|
||||
|
||||
## ✨ That's It!
|
||||
|
||||
ATOB is fully integrated and ready to use in both:
|
||||
1. **Development** (mock backend - works now!)
|
||||
2. **Production** (s9pk package - ready to deploy!)
|
||||
|
||||
**Enjoy launching ATOB from your Neode server!** 🚀
|
||||
|
||||
@@ -1,310 +0,0 @@
|
||||
# 🌐 Community Marketplace Integration
|
||||
|
||||
The Neode UI now includes a **Community Marketplace** tab that connects to the real Start9 app ecosystem!
|
||||
|
||||
## Features
|
||||
|
||||
### 📑 Two-Tab Interface
|
||||
|
||||
**Local Apps Tab:**
|
||||
- Your custom local apps (k484, atob, amin)
|
||||
- Sideload packages from URLs
|
||||
- Quick install for development apps
|
||||
|
||||
**Community Marketplace Tab:**
|
||||
- **Real Start9 packages** from the community registry
|
||||
- Search functionality
|
||||
- Bitcoin Core, Lightning Network, BTCPay, Nextcloud, and more!
|
||||
- GitHub repository links
|
||||
- Author information
|
||||
|
||||
### 🔍 Search & Filter
|
||||
|
||||
Search across:
|
||||
- App names
|
||||
- Descriptions
|
||||
- Package IDs
|
||||
- Author names
|
||||
|
||||
### 🎨 Beautiful UI
|
||||
|
||||
- Glass-morphism design (matching Neode aesthetic)
|
||||
- Responsive grid layout
|
||||
- Loading states
|
||||
- Error handling with retry
|
||||
- Install progress indicators
|
||||
|
||||
## How It Works
|
||||
|
||||
### 1. **Fetches Real Data**
|
||||
|
||||
Connects to Start9's community registry:
|
||||
```
|
||||
https://registry.start9.com/api/v1/packages
|
||||
```
|
||||
|
||||
### 2. **Smart Multi-Level Fallback System**
|
||||
|
||||
If the Start9 registry is unavailable, the system automatically tries multiple sources:
|
||||
|
||||
**Level 1:** Start9 Registry API (primary)
|
||||
```
|
||||
https://registry.start9.com/api/v1/packages
|
||||
```
|
||||
|
||||
**Level 2:** GitHub API (dynamic)
|
||||
```
|
||||
https://api.github.com/users/Start9Labs/repos
|
||||
```
|
||||
- Fetches all `-startos` repositories from Start9Labs
|
||||
- Dynamically builds app list from actual packages
|
||||
- Shows real-time ecosystem
|
||||
|
||||
**Level 3:** Curated App List (ultimate fallback)
|
||||
Shows 20+ popular Start9 ecosystem apps including:
|
||||
|
||||
**Bitcoin & Lightning:**
|
||||
- Bitcoin Core, Bitcoin Knots
|
||||
- Core Lightning (CLN), LND
|
||||
- BTCPay Server
|
||||
- Ride The Lightning, ThunderHub
|
||||
- Electrs, Mempool Explorer
|
||||
- Specter Desktop
|
||||
|
||||
**Communication & Social:**
|
||||
- Synapse (Matrix)
|
||||
- Nostr Relay
|
||||
- CUPS Messenger
|
||||
|
||||
**Productivity & Storage:**
|
||||
- Nextcloud
|
||||
- Vaultwarden (password manager)
|
||||
- File Browser
|
||||
|
||||
**Media:**
|
||||
- Jellyfin (media server)
|
||||
- PhotoPrism (AI photos)
|
||||
- Immich (photo backup)
|
||||
|
||||
**Smart Home:**
|
||||
- Home Assistant
|
||||
|
||||
**You'll see a blue info banner:** "📚 Community Apps: Showing X Start9 ecosystem applications."
|
||||
|
||||
### 3. **Installation Flow**
|
||||
|
||||
When you click "Install" on a community app:
|
||||
1. Calls `package.install` RPC method with the `.s9pk` URL
|
||||
2. Backend downloads and extracts the package
|
||||
3. Polls for installation completion
|
||||
4. Redirects to Apps page when done
|
||||
|
||||
## Usage
|
||||
|
||||
### Access the Marketplace
|
||||
|
||||
1. Navigate to **Dashboard > Marketplace**
|
||||
2. Click **"Community Marketplace"** tab
|
||||
3. Browse or search for apps
|
||||
4. Click **"Install"** on any app with a manifest URL
|
||||
|
||||
### Search for Apps
|
||||
|
||||
Type in the search bar:
|
||||
- "bitcoin" - finds Bitcoin Core
|
||||
- "lightning" - finds Lightning Network
|
||||
- "payment" - finds BTCPay Server
|
||||
- etc.
|
||||
|
||||
### Install Community Apps
|
||||
|
||||
Apps with green "Install" buttons:
|
||||
- ✅ Have downloadable `.s9pk` packages
|
||||
- ✅ Can be installed directly
|
||||
|
||||
Apps with "Not Available" buttons:
|
||||
- ⚠️ Don't have packages yet
|
||||
- 🔗 Can view GitHub repo for more info
|
||||
|
||||
## Technical Details
|
||||
|
||||
### API Integration
|
||||
|
||||
```typescript
|
||||
// Fetches community packages
|
||||
const response = await fetch('https://registry.start9.com/api/v1/packages')
|
||||
const data = await response.json()
|
||||
|
||||
// Transforms to our format
|
||||
communityApps.value = Object.entries(data).map(([id, pkg]) => ({
|
||||
id,
|
||||
title: pkg.title || id,
|
||||
version: latestVersion,
|
||||
description: pkg.description,
|
||||
icon: pkg.icon,
|
||||
author: pkg.author,
|
||||
manifestUrl: pkg.manifest,
|
||||
repoUrl: pkg.repository
|
||||
}))
|
||||
```
|
||||
|
||||
### Installation
|
||||
|
||||
```typescript
|
||||
// Installs community app
|
||||
await rpcClient.call({
|
||||
method: 'package.install',
|
||||
params: {
|
||||
id: app.id,
|
||||
url: app.manifestUrl, // .s9pk URL from registry
|
||||
version: app.version
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## App Card Features
|
||||
|
||||
Each community app card shows:
|
||||
- **Icon** - App logo (with fallback to Neode logo)
|
||||
- **Title** - App name
|
||||
- **Version** - Latest available version
|
||||
- **Author** - Package maintainer
|
||||
- **Description** - Truncated to 3 lines
|
||||
- **Install Button** - If manifest available
|
||||
- **GitHub Link** - Repository access
|
||||
|
||||
## States
|
||||
|
||||
### Loading
|
||||
```
|
||||
🔄 Loading Start9 Community Marketplace...
|
||||
```
|
||||
|
||||
### Error
|
||||
```
|
||||
❌ Failed to load marketplace
|
||||
[Error message]
|
||||
[Retry Button]
|
||||
```
|
||||
|
||||
### Empty Search
|
||||
```
|
||||
No apps found matching "[query]"
|
||||
```
|
||||
|
||||
## Backend Requirements
|
||||
|
||||
The mock backend needs to support:
|
||||
|
||||
1. **package.install** - Install from `.s9pk` URL
|
||||
2. **Docker** - Extract and run containers
|
||||
3. **Networking** - Download from registry URLs
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- [ ] **Categories** - Filter by Bitcoin, Storage, Communication, etc.
|
||||
- [ ] **Ratings** - Show community ratings
|
||||
- [ ] **Dependencies** - Show required apps
|
||||
- [ ] **Updates** - Notify when app updates available
|
||||
- [ ] **Reviews** - User reviews and comments
|
||||
- [ ] **Screenshots** - App previews
|
||||
- [ ] **Detailed Views** - Full app pages with more info
|
||||
|
||||
## Development
|
||||
|
||||
### Test the Feature
|
||||
|
||||
1. Start dev server:
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
2. Navigate to Marketplace
|
||||
|
||||
3. Switch to "Community Marketplace" tab
|
||||
|
||||
4. Should see apps load (or fallback list)
|
||||
|
||||
### Mock Data (Development Fallback)
|
||||
|
||||
If registry is unavailable, shows:
|
||||
- Bitcoin Core v27.0.0
|
||||
- Core Lightning v24.02.2
|
||||
- BTCPay Server v1.13.1
|
||||
- Nextcloud v29.0.0
|
||||
|
||||
### Adding More Mock Apps
|
||||
|
||||
Edit `loadCommunityMarketplace()` function in `Marketplace.vue`:
|
||||
|
||||
```typescript
|
||||
communityApps.value = [
|
||||
// ... existing apps ...
|
||||
{
|
||||
id: 'fedimint',
|
||||
title: 'Fedimint',
|
||||
version: '0.3.0',
|
||||
description: 'Federated Chaumian e-cash',
|
||||
icon: '/assets/img/fedimint.png',
|
||||
author: 'Fedimint Developers',
|
||||
manifestUrl: 'https://example.com/fedimint.s9pk',
|
||||
repoUrl: 'https://github.com/fedimint/fedimint'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Registry Not Loading
|
||||
|
||||
**Problem**: Community tab shows error
|
||||
|
||||
**Solutions**:
|
||||
1. Check internet connection
|
||||
2. Verify registry URL is accessible
|
||||
3. Check browser console for CORS errors
|
||||
4. Fallback mock data will still show
|
||||
|
||||
### Apps Won't Install
|
||||
|
||||
**Problem**: Install button doesn't work
|
||||
|
||||
**Check**:
|
||||
1. App has `manifestUrl` (not null)
|
||||
2. Backend is running
|
||||
3. Docker is available (for real installs)
|
||||
4. Check backend logs for errors
|
||||
|
||||
### Search Not Working
|
||||
|
||||
**Problem**: Search doesn't filter apps
|
||||
|
||||
**Check**:
|
||||
1. Type in search box
|
||||
2. Should filter in real-time
|
||||
3. Search is case-insensitive
|
||||
4. Searches title, description, ID, author
|
||||
|
||||
## Benefits
|
||||
|
||||
✅ **Real Apps** - Access actual Start9 packages
|
||||
✅ **Easy Discovery** - Browse full ecosystem
|
||||
✅ **One-Click Install** - Direct installation from registry
|
||||
✅ **Stay Updated** - See latest versions
|
||||
✅ **Community Driven** - Access community-maintained apps
|
||||
✅ **Transparent** - GitHub links for every app
|
||||
|
||||
## Security Note
|
||||
|
||||
Apps from the community marketplace are:
|
||||
- Open source (GitHub repos linked)
|
||||
- Community maintained
|
||||
- Same packages used by StartOS
|
||||
- Verified by signature (in production)
|
||||
|
||||
Always review an app's repository before installing!
|
||||
|
||||
---
|
||||
|
||||
🎉 **You can now browse and install real Start9 community apps directly from Neode!**
|
||||
|
||||
@@ -1,342 +0,0 @@
|
||||
# Angular vs Vue 3 - Side by Side Comparison
|
||||
|
||||
## Your Question: "Is there a better way?"
|
||||
|
||||
**YES! You were right to question it.** Here's why the Vue rewrite solves your problems:
|
||||
|
||||
## The Problems You Had
|
||||
|
||||
### ❌ Angular Issues
|
||||
|
||||
1. **"Disappearing interfaces"** - Components randomly vanishing on route changes
|
||||
2. **"Routing problems"** - Navigation breaking, routes not loading
|
||||
3. **"Untable and hard to work with"** - Complex module system, slow builds
|
||||
4. **"Seems a bit shit"** - Your words, but accurate! 😅
|
||||
|
||||
### ✅ Vue Solutions
|
||||
|
||||
1. **Stable routing** - Vue Router is simpler and more predictable
|
||||
2. **Components don't vanish** - Reactive system is more reliable
|
||||
3. **Fast & easy** - Vite HMR is instant, code is cleaner
|
||||
4. **Actually enjoyable** - Modern DX that doesn't fight you
|
||||
|
||||
## Technical Comparison
|
||||
|
||||
### Routing
|
||||
|
||||
**Angular (Complex & Brittle):**
|
||||
```typescript
|
||||
// app-routing.module.ts
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: '/login',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'login',
|
||||
loadChildren: () => import('./pages/login/login.module').then(m => m.LoginPageModule)
|
||||
},
|
||||
// Module imports, lazy loading, guards in separate files...
|
||||
]
|
||||
|
||||
// app.component.ts - Complex splash logic causing routing issues
|
||||
this.router.events
|
||||
.pipe(filter(e => e instanceof NavigationEnd))
|
||||
.subscribe((e: any) => {
|
||||
// Lots of state management that can break routes
|
||||
})
|
||||
```
|
||||
|
||||
**Vue (Clean & Simple):**
|
||||
```typescript
|
||||
// router/index.ts
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{ path: '/', redirect: '/login' },
|
||||
{ path: '/login', component: () => import('../views/Login.vue') },
|
||||
// Done. No modules, no complexity.
|
||||
]
|
||||
})
|
||||
|
||||
// Auth guard
|
||||
router.beforeEach((to, from, next) => {
|
||||
const isPublic = to.meta.public
|
||||
if (!isPublic && !store.isAuthenticated) {
|
||||
next('/login')
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
**Angular (RxJS Spaghetti):**
|
||||
```typescript
|
||||
// Observables everywhere
|
||||
this.authService.isVerified$
|
||||
.pipe(
|
||||
filter(verified => verified),
|
||||
take(1),
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.subscriptions.add((this.patchData as any).subscribe?.() ?? new Subscription())
|
||||
this.subscriptions.add((this.patchMonitor as any).subscribe?.() ?? new Subscription())
|
||||
// Easy to miss unsubscribe, causes memory leaks
|
||||
})
|
||||
```
|
||||
|
||||
**Vue (Simple & Reactive):**
|
||||
```typescript
|
||||
// Pinia store
|
||||
const isAuthenticated = ref(false)
|
||||
const serverInfo = computed(() => data.value?.['server-info'])
|
||||
|
||||
// No subscriptions to manage!
|
||||
async function login(password: string) {
|
||||
await rpcClient.login(password)
|
||||
isAuthenticated.value = true
|
||||
await connectWebSocket()
|
||||
}
|
||||
```
|
||||
|
||||
### Components
|
||||
|
||||
**Angular (Verbose):**
|
||||
```typescript
|
||||
import { Component, inject, OnDestroy } from '@angular/core'
|
||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'
|
||||
import { filter, take } from 'rxjs/operators'
|
||||
import { combineLatest, map, startWith, Subscription } from 'rxjs'
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: 'app.component.html',
|
||||
styleUrls: ['app.component.scss'],
|
||||
})
|
||||
export class AppComponent implements OnDestroy {
|
||||
private readonly subscriptions = new Subscription()
|
||||
|
||||
constructor(
|
||||
private readonly titleService: Title,
|
||||
private readonly patchData: PatchDataService,
|
||||
// ... 10 more injected services
|
||||
) {}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscriptions.unsubscribe()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Vue (Concise):**
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useAppStore()
|
||||
const serverName = computed(() => store.serverName)
|
||||
|
||||
async function logout() {
|
||||
await store.logout()
|
||||
router.push('/login')
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
### Styling (Glass Cards)
|
||||
|
||||
**Angular (Fighting Ionic):**
|
||||
```scss
|
||||
// Have to override Ionic parts
|
||||
ion-menu.left-menu::part(container) {
|
||||
background: rgba(0, 0, 0, 0.35) !important;
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
:host ::ng-deep ion-item.service-card {
|
||||
--background: rgba(0, 0, 0, 0.35) !important;
|
||||
// Fighting specificity wars
|
||||
}
|
||||
```
|
||||
|
||||
**Vue (Tailwind Utility Classes):**
|
||||
```vue
|
||||
<div class="glass-card p-6">
|
||||
<!-- That's it. No !important, no ::ng-deep -->
|
||||
</div>
|
||||
```
|
||||
|
||||
## Build Performance
|
||||
|
||||
### Angular CLI (Slow)
|
||||
|
||||
```bash
|
||||
$ npm run start:ui
|
||||
⠙ Building...
|
||||
[Build takes 45-60 seconds]
|
||||
⠙ Recompiling...
|
||||
[HMR takes 5-10 seconds per change]
|
||||
```
|
||||
|
||||
### Vite (Fast)
|
||||
|
||||
```bash
|
||||
$ npm run dev
|
||||
✓ ready in 344 ms
|
||||
|
||||
[HMR updates in < 50ms]
|
||||
```
|
||||
|
||||
**~100x faster development loop!**
|
||||
|
||||
## Bundle Size
|
||||
|
||||
| Framework | Size | Gzipped |
|
||||
|-----------|------|---------|
|
||||
| Angular UI | ~850 KB | ~250 KB |
|
||||
| Vue UI | ~150 KB | ~50 KB |
|
||||
|
||||
**5x smaller bundle!**
|
||||
|
||||
## Code Comparison - Same Feature
|
||||
|
||||
### Login Page (Angular)
|
||||
|
||||
```
|
||||
app/pages/login/
|
||||
├── login.page.ts (150 lines)
|
||||
├── login.page.html (80 lines)
|
||||
├── login.page.scss (72 lines)
|
||||
├── login.module.ts (40 lines)
|
||||
└── login-routing.module.ts (15 lines)
|
||||
```
|
||||
|
||||
**Total: 357 lines across 5 files**
|
||||
|
||||
### Login Page (Vue)
|
||||
|
||||
```
|
||||
views/Login.vue (120 lines)
|
||||
```
|
||||
|
||||
**Total: 120 lines in 1 file**
|
||||
|
||||
**3x less code!**
|
||||
|
||||
## The Backend Connection
|
||||
|
||||
### Both Work the Same!
|
||||
|
||||
**Angular:**
|
||||
```typescript
|
||||
this.http.httpRequest<T>({
|
||||
method: 'POST',
|
||||
url: '/rpc/v1',
|
||||
body: { method, params },
|
||||
})
|
||||
```
|
||||
|
||||
**Vue:**
|
||||
```typescript
|
||||
fetch('/rpc/v1', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ method, params }),
|
||||
})
|
||||
```
|
||||
|
||||
**Same API, same WebSocket, same everything.** The backend doesn't care!
|
||||
|
||||
## Migration Path
|
||||
|
||||
You have **two options**:
|
||||
|
||||
### Option 1: Use Vue UI (Recommended)
|
||||
|
||||
```bash
|
||||
cd neode-ui
|
||||
npm run dev # Develop in Vue
|
||||
npm run build # Build to ../web/dist/neode-ui/
|
||||
```
|
||||
|
||||
Update Rust to serve from `neode-ui` build directory.
|
||||
|
||||
**Pros:**
|
||||
- Clean slate, no baggage
|
||||
- Fast development
|
||||
- Modern patterns
|
||||
- Easier to maintain
|
||||
|
||||
**Cons:**
|
||||
- Need to recreate any Angular features you haven't ported yet
|
||||
|
||||
### Option 2: Keep Angular
|
||||
|
||||
```bash
|
||||
cd web
|
||||
npm run start:ui # Continue with Angular
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- No migration needed
|
||||
- All features intact
|
||||
|
||||
**Cons:**
|
||||
- Still have routing issues
|
||||
- Still slow
|
||||
- Still complex
|
||||
|
||||
## Recommendation
|
||||
|
||||
**Switch to Vue.** Here's why:
|
||||
|
||||
1. **You already questioned the Angular approach** - Trust your instincts!
|
||||
2. **Routing issues are gone** - Clean Vue Router
|
||||
3. **Development is faster** - Vite HMR is instant
|
||||
4. **Code is simpler** - Less boilerplate, easier to understand
|
||||
5. **All your UI is recreated** - Glassmorphism, splash, everything
|
||||
6. **Backend works the same** - No changes needed on Rust side
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Test the Vue UI:**
|
||||
```bash
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. **Compare the experience:**
|
||||
- Open http://localhost:8100
|
||||
- Navigate around
|
||||
- Notice how stable it is
|
||||
- Make a change and see instant HMR
|
||||
|
||||
3. **Decide:**
|
||||
- If you like it → migrate remaining features
|
||||
- If you don't → keep Angular (but you'll like it!)
|
||||
|
||||
## Final Thoughts
|
||||
|
||||
You asked: **"Is there a better way?"**
|
||||
|
||||
**Answer: Yes, and you're looking at it.** 🎉
|
||||
|
||||
The Vue + Tailwind approach is:
|
||||
- Simpler
|
||||
- Faster
|
||||
- More stable
|
||||
- Easier to maintain
|
||||
- More enjoyable to work with
|
||||
|
||||
Your "untable and hard to work with" Angular feeling was valid. This fixes it.
|
||||
|
||||
**Ready to try it?**
|
||||
```bash
|
||||
cd neode-ui && npm run dev
|
||||
```
|
||||
|
||||
+14
-12
@@ -107,20 +107,22 @@ tail -f /tmp/neode-dev.log
|
||||
## Features
|
||||
|
||||
### Mock Backend
|
||||
- **Docker Optional** - Apps run for real if Docker is available, otherwise simulated
|
||||
- **Auto-Detection** - Automatically detects Docker daemon and adapts
|
||||
- **Fixed Port Allocation** - atob:8102, k484:8103, amin:8104
|
||||
- **WebSocket Support** - Real-time updates
|
||||
- **Pre-loaded Apps** - Bitcoin and Lightning already "installed"
|
||||
- **Docker Optional** - Apps run for real if Docker/Podman is available, otherwise simulated
|
||||
- **Auto-Detection** - Automatically detects container runtime and adapts
|
||||
- **WebSocket Support** - Real-time state updates via JSON patches
|
||||
- **Pre-loaded Apps** - 8 apps always visible in My Apps
|
||||
|
||||
📖 **See [DOCKER-APPS.md](./DOCKER-APPS.md) for running real Docker containers**
|
||||
### Pre-installed Apps (always running in mock mode)
|
||||
- `bitcoin` - Bitcoin Core (port 8332)
|
||||
- `lnd` - Lightning Network Daemon (port 8080)
|
||||
- `electrs` - Electrum Server in Rust (port 50001)
|
||||
- `mempool` - Blockchain explorer (port 4080)
|
||||
- `filebrowser` - Web file manager (port 8083)
|
||||
- `lorabell` - LoRa doorbell (no UI port)
|
||||
- `thunderhub` - Lightning node management (port 3010)
|
||||
- `fedimint` - Federated Bitcoin mint (port 8175)
|
||||
|
||||
### Available Test Apps
|
||||
- `atob` - A to B Bitcoin (simulated)
|
||||
- `k484` - K484 POS/Admin (simulated)
|
||||
- `amin` - Admin interface (simulated)
|
||||
|
||||
All installations are simulated - they don't actually download or run Docker containers.
|
||||
Additional apps can be installed from the Marketplace (30+ available).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
# 🐳 Running Real Apps with Docker
|
||||
|
||||
The mock backend can run **actual** Docker containers for your apps, not just simulate them!
|
||||
|
||||
## Current Status
|
||||
|
||||
Check the banner when starting the mock backend:
|
||||
|
||||
```
|
||||
🐳 Available (apps will run for real!) ← Docker is running, apps will work!
|
||||
⚠️ Not available (simulated mode) ← Docker off, apps simulated only
|
||||
```
|
||||
|
||||
## Setup for Real Apps
|
||||
|
||||
### 1. Start Docker Desktop
|
||||
|
||||
Make sure Docker Desktop is running:
|
||||
|
||||
```bash
|
||||
# Check if Docker daemon is running
|
||||
docker ps
|
||||
|
||||
# If you see: "Cannot connect to the Docker daemon..."
|
||||
# → Open Docker Desktop application
|
||||
```
|
||||
|
||||
### 2. Build Your App Docker Images
|
||||
|
||||
For each app you want to run, you need a Docker image built with the exact name and version:
|
||||
|
||||
**For k484:**
|
||||
```bash
|
||||
# Assuming you have k484-package/ directory with a Dockerfile
|
||||
cd ~/k484-package
|
||||
docker build -t k484:0.1.0 .
|
||||
```
|
||||
|
||||
**For atob:**
|
||||
```bash
|
||||
# Assuming you have atob-package/ directory with a Dockerfile
|
||||
cd ~/atob-package
|
||||
docker build -t atob:0.1.0 .
|
||||
```
|
||||
|
||||
**For amin:**
|
||||
```bash
|
||||
cd ~/amin-package
|
||||
docker build -t amin:0.1.0 .
|
||||
```
|
||||
|
||||
### 3. Restart the Dev Server
|
||||
|
||||
```bash
|
||||
npm stop
|
||||
npm start
|
||||
```
|
||||
|
||||
You should now see: `🐳 Available (apps will run for real!)`
|
||||
|
||||
### 4. Install Apps in the UI
|
||||
|
||||
Visit http://localhost:8100, go to Marketplace, and install apps.
|
||||
|
||||
You'll see logs like:
|
||||
```
|
||||
[Package] 📦 Installing k484...
|
||||
[Package] 🐳 Docker available, attempting to run container...
|
||||
[Package] 🐳 Docker container running on port 8103
|
||||
[Package] ✅ k484 installed and RUNNING at http://localhost:8103
|
||||
```
|
||||
|
||||
### 5. Launch Apps
|
||||
|
||||
Click "Launch" on any installed app - it will open at:
|
||||
- **atob**: http://localhost:8102
|
||||
- **k484**: http://localhost:8103
|
||||
- **amin**: http://localhost:8104
|
||||
|
||||
---
|
||||
|
||||
## Port Mapping
|
||||
|
||||
The mock backend uses fixed ports for known apps:
|
||||
|
||||
| App | Port | Container Name |
|
||||
|-----|------|----------------|
|
||||
| atob | 8102 | atob-test |
|
||||
| k484 | 8103 | k484-test |
|
||||
| amin | 8104 | amin-test |
|
||||
| Other apps | 8105+ | {id}-test |
|
||||
|
||||
---
|
||||
|
||||
## Docker Image Requirements
|
||||
|
||||
Each app needs a Docker image with:
|
||||
- **Name**: `{app-id}:0.1.0`
|
||||
- **Port**: Expose port 80 inside container
|
||||
- **Format**: Standard web app serving HTTP
|
||||
|
||||
Example Dockerfile:
|
||||
```dockerfile
|
||||
FROM nginx:alpine
|
||||
COPY dist/ /usr/share/nginx/html/
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Docker image not found"
|
||||
|
||||
**Problem**: App installs but logs show:
|
||||
```
|
||||
[Package] ℹ️ Docker image k484:0.1.0 not found, using simulation mode
|
||||
```
|
||||
|
||||
**Solution**: Build the Docker image first:
|
||||
```bash
|
||||
cd ~/k484-package
|
||||
docker build -t k484:0.1.0 .
|
||||
```
|
||||
|
||||
### "Port is already allocated"
|
||||
|
||||
**Problem**:
|
||||
```
|
||||
Bind for 0.0.0.0:8103 failed: port is already allocated
|
||||
```
|
||||
|
||||
**Solution**: Stop the existing container:
|
||||
```bash
|
||||
docker stop k484-test
|
||||
docker rm k484-test
|
||||
```
|
||||
|
||||
Or use the UI to uninstall the app first, which will clean up the container.
|
||||
|
||||
### "Cannot connect to Docker daemon"
|
||||
|
||||
**Problem**:
|
||||
```
|
||||
⚠️ Not available (simulated mode)
|
||||
```
|
||||
|
||||
**Solution**:
|
||||
1. Open Docker Desktop
|
||||
2. Wait for it to fully start (whale icon in menu bar should be steady)
|
||||
3. Restart dev server: `npm stop && npm start`
|
||||
|
||||
### App appears installed but Launch doesn't work
|
||||
|
||||
**Simulated mode**: The app is only simulated in the database, no real container is running.
|
||||
|
||||
**Check the logs** when installing to see if Docker was used:
|
||||
```
|
||||
# Docker mode (good):
|
||||
[Package] 🐳 Docker container running on port 8103
|
||||
[Package] ✅ k484 installed and RUNNING at http://localhost:8103
|
||||
|
||||
# Simulated mode (no container):
|
||||
[Package] ℹ️ Docker not available, using simulation mode
|
||||
[Package] ✅ k484 installed (simulated - no Docker container)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checking Running Containers
|
||||
|
||||
```bash
|
||||
# List all running containers
|
||||
docker ps
|
||||
|
||||
# Check specific container
|
||||
docker ps --filter name=k484-test
|
||||
|
||||
# View container logs
|
||||
docker logs k484-test
|
||||
|
||||
# Stop a container
|
||||
docker stop k484-test
|
||||
|
||||
# Remove a container
|
||||
docker rm k484-test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Benefits of Docker Mode
|
||||
|
||||
✅ **Real apps** - Actually test your applications
|
||||
✅ **Full functionality** - All features work (not just UI)
|
||||
✅ **Integration testing** - Test API calls, WebSocket, etc.
|
||||
✅ **Realistic development** - Matches production environment
|
||||
|
||||
## Benefits of Simulated Mode
|
||||
|
||||
✅ **No Docker required** - Lightweight development
|
||||
✅ **Fast startup** - No containers to build/start
|
||||
✅ **UI testing** - Perfect for frontend-only work
|
||||
✅ **Lower resource usage** - No Docker overhead
|
||||
|
||||
---
|
||||
|
||||
## Auto-Detection
|
||||
|
||||
The mock backend automatically:
|
||||
1. Checks if Docker daemon is running
|
||||
2. Checks if image exists for the app
|
||||
3. Tries to start container if possible
|
||||
4. Falls back to simulation if Docker unavailable
|
||||
|
||||
This means you can develop with or without Docker, and the system adapts automatically!
|
||||
|
||||
---
|
||||
|
||||
## Uninstalling Apps
|
||||
|
||||
When you uninstall an app through the UI:
|
||||
- **Docker mode**: Stops and removes the container
|
||||
- **Simulated mode**: Just removes from database
|
||||
|
||||
Both clean up properly - no manual cleanup needed!
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Start Docker Desktop first
|
||||
open -a Docker
|
||||
|
||||
# Build images (one-time setup)
|
||||
cd ~/k484-package && docker build -t k484:0.1.0 .
|
||||
cd ~/atob-package && docker build -t atob:0.1.0 .
|
||||
|
||||
# Start dev servers
|
||||
cd neode-ui
|
||||
npm start
|
||||
|
||||
# Should see: 🐳 Available (apps will run for real!)
|
||||
|
||||
# Install apps via UI at http://localhost:8100
|
||||
# Apps will actually run at their ports!
|
||||
|
||||
# Stop everything when done
|
||||
npm stop
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Happy coding! 🚀🐳
|
||||
|
||||
@@ -1,316 +0,0 @@
|
||||
# Error Fixes - WebSocket & Marketplace Issues
|
||||
|
||||
## Issues Fixed
|
||||
|
||||
### ✅ 1. WebSocket Patch Error
|
||||
**Error**: `Cannot read properties of undefined (reading 'length')`
|
||||
|
||||
**Root Cause**: The `applyDataPatch` function was trying to read the `length` property of an undefined or null `patch` array.
|
||||
|
||||
**Fix Applied** (`src/api/websocket.ts`):
|
||||
```typescript
|
||||
export function applyDataPatch<T>(data: T, patch: PatchOperation[]): T {
|
||||
// ✅ NEW: Validate patch is an array before applying
|
||||
if (!Array.isArray(patch) || patch.length === 0) {
|
||||
console.warn('Invalid or empty patch received, returning original data')
|
||||
return data
|
||||
}
|
||||
|
||||
// ✅ NEW: Wrap in try/catch for safety
|
||||
try {
|
||||
const result = applyPatch(data, patch as any, false, false)
|
||||
return result.newDocument as T
|
||||
} catch (error) {
|
||||
console.error('Failed to apply patch:', error, 'Patch:', patch)
|
||||
return data // Return original data on error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Also Fixed** (`src/stores/app.ts`):
|
||||
```typescript
|
||||
// ✅ Added null checks and error handling
|
||||
wsClient.subscribe((update) => {
|
||||
if (data.value && update?.patch) { // Added update?.patch check
|
||||
try {
|
||||
data.value = applyDataPatch(data.value, update.patch)
|
||||
} catch (err) {
|
||||
console.error('Failed to apply WebSocket patch:', err)
|
||||
// Gracefully continue - app still works without real-time updates
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Result**:
|
||||
- No more crashes from malformed WebSocket messages
|
||||
- App continues working even if patches fail
|
||||
- Better logging for debugging
|
||||
|
||||
---
|
||||
|
||||
### ✅ 2. Marketplace API Error
|
||||
**Error**: `Method not found: marketplace.get`
|
||||
|
||||
**Root Causes**:
|
||||
1. Backend not running
|
||||
2. Not authenticated
|
||||
3. Method requires authentication
|
||||
|
||||
**Fixes Applied** (`src/views/Marketplace.vue`):
|
||||
|
||||
```typescript
|
||||
async function loadMarketplace() {
|
||||
// ✅ NEW: Check authentication first
|
||||
if (!store.isAuthenticated) {
|
||||
error.value = 'Please login first to access the marketplace'
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await store.getMarketplace(selectedMarketplace.value)
|
||||
// ... rest of logic
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Failed to load marketplace'
|
||||
|
||||
// ✅ NEW: Provide specific, actionable error messages
|
||||
if (errorMessage.includes('Method not found')) {
|
||||
error.value = 'Backend marketplace API not available. Ensure Neode backend is running and up to date.'
|
||||
} else if (errorMessage.includes('authenticated') || errorMessage.includes('401')) {
|
||||
error.value = 'Authentication required. Please login first.'
|
||||
} else if (errorMessage.includes('Network') || errorMessage.includes('fetch')) {
|
||||
error.value = 'Cannot connect to backend. Ensure Neode backend is running on port 5959.'
|
||||
} else {
|
||||
error.value = errorMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Result**:
|
||||
- Clear, actionable error messages
|
||||
- Authentication check before API calls
|
||||
- Better user experience
|
||||
|
||||
---
|
||||
|
||||
## What You Need to Do
|
||||
|
||||
### Immediate Next Steps
|
||||
|
||||
**1. Ensure Backend is Running**
|
||||
|
||||
The marketplace requires a running Neode backend. You have two options:
|
||||
|
||||
#### Option A: Start the Backend (Recommended)
|
||||
```bash
|
||||
# Build and run the backend
|
||||
cd /Users/tx1138/Code/Neode/core
|
||||
cargo build --release
|
||||
./target/release/startos
|
||||
|
||||
# Should see: "Neode backend listening on :5959"
|
||||
```
|
||||
|
||||
#### Option B: Use Mock Mode (Development Only)
|
||||
See `TROUBLESHOOTING.md` for how to enable mock mode for UI development without backend.
|
||||
|
||||
**2. Login First**
|
||||
|
||||
Before accessing the marketplace:
|
||||
```bash
|
||||
# Start UI
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
npm run dev
|
||||
|
||||
# Visit: http://localhost:8100
|
||||
# Navigate to Login
|
||||
# Enter credentials
|
||||
# Then access Marketplace
|
||||
```
|
||||
|
||||
**3. Test the Fixes**
|
||||
|
||||
```bash
|
||||
# With backend running and authenticated:
|
||||
# Navigate to Marketplace
|
||||
# Should see: Loading → Apps OR Clear error message (not a crash)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing the Fixes
|
||||
|
||||
### Test WebSocket Error Fix
|
||||
|
||||
1. **Start the UI**: `npm run dev`
|
||||
2. **Open DevTools Console**
|
||||
3. **Login** (to trigger WebSocket connection)
|
||||
4. **Look for**:
|
||||
- ✅ Should see: "WebSocket connected"
|
||||
- ✅ Should NOT crash on malformed patches
|
||||
- ✅ May see warnings: "Invalid or empty patch received" (this is OK)
|
||||
|
||||
### Test Marketplace Error Fix
|
||||
|
||||
1. **Without Backend**:
|
||||
- Navigate to Marketplace
|
||||
- Should see: "Backend marketplace API not available..." (clear message)
|
||||
- No crashes or undefined errors
|
||||
|
||||
2. **Without Login**:
|
||||
- Navigate to Marketplace
|
||||
- Should see: "Please login first..." (clear message)
|
||||
|
||||
3. **With Backend & Login**:
|
||||
- Navigate to Marketplace
|
||||
- Should see: Loading → Apps list OR specific error
|
||||
|
||||
---
|
||||
|
||||
## File Changes Summary
|
||||
|
||||
### Modified Files
|
||||
|
||||
1. **`src/api/websocket.ts`**
|
||||
- Added validation for patch array
|
||||
- Added try/catch for safety
|
||||
- Better error logging
|
||||
|
||||
2. **`src/stores/app.ts`**
|
||||
- Added null checks for WebSocket updates
|
||||
- Added try/catch in subscription handler
|
||||
- Removed premature `isConnected` reset on logout
|
||||
|
||||
3. **`src/views/Marketplace.vue`**
|
||||
- Added authentication check
|
||||
- Added specific error messages
|
||||
- Better error categorization
|
||||
|
||||
### New Documentation
|
||||
|
||||
4. **`TROUBLESHOOTING.md`**
|
||||
- Common issues and solutions
|
||||
- Mock mode setup
|
||||
- Debugging tips
|
||||
- Backend setup guide
|
||||
|
||||
5. **`ERROR_FIXES.md`** (this file)
|
||||
- Summary of fixes
|
||||
- Testing procedures
|
||||
- Next steps
|
||||
|
||||
---
|
||||
|
||||
## Architecture Notes
|
||||
|
||||
### Why These Errors Happened
|
||||
|
||||
1. **WebSocket Error**:
|
||||
- Backend sends JSON Patch operations over WebSocket
|
||||
- If patch format is unexpected or empty, code crashed
|
||||
- Now: Validates format and handles errors gracefully
|
||||
|
||||
2. **Marketplace Error**:
|
||||
- RPC method `marketplace.get` exists in backend
|
||||
- But requires running backend + authentication
|
||||
- Now: Checks auth first, provides clear error messages
|
||||
|
||||
### How It Works Now
|
||||
|
||||
```
|
||||
User navigates to Marketplace
|
||||
↓
|
||||
Check isAuthenticated ✅
|
||||
↓
|
||||
Call store.getMarketplace(url)
|
||||
↓
|
||||
RPC Client → POST /rpc/v1
|
||||
↓
|
||||
Backend: marketplace.get
|
||||
↓
|
||||
Return apps OR error with clear message ✅
|
||||
↓
|
||||
Display apps OR show actionable error ✅
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
Run through this checklist to verify fixes:
|
||||
|
||||
- [ ] UI starts without errors: `npm run dev`
|
||||
- [ ] Login works (with or without backend)
|
||||
- [ ] WebSocket connects (if backend available)
|
||||
- [ ] WebSocket errors don't crash app
|
||||
- [ ] Marketplace shows clear error when not logged in
|
||||
- [ ] Marketplace shows clear error when backend unavailable
|
||||
- [ ] Marketplace loads apps (when backend running + logged in)
|
||||
- [ ] No console errors about "Cannot read properties of undefined"
|
||||
- [ ] No crashes when navigating between pages
|
||||
|
||||
---
|
||||
|
||||
## Still Getting Errors?
|
||||
|
||||
### Check Backend Status
|
||||
|
||||
```bash
|
||||
# Is backend running?
|
||||
lsof -i :5959
|
||||
|
||||
# Test backend directly
|
||||
curl -X POST http://localhost:5959/rpc/v1 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"method":"echo","params":{"message":"hello"}}'
|
||||
|
||||
# Should return: {"result":"hello"}
|
||||
```
|
||||
|
||||
### Check UI Status
|
||||
|
||||
```bash
|
||||
# Is UI running?
|
||||
curl http://localhost:8100
|
||||
|
||||
# Check console for errors
|
||||
# Open browser DevTools → Console tab
|
||||
```
|
||||
|
||||
### Enable Debug Logging
|
||||
|
||||
Add to `src/api/rpc-client.ts`:
|
||||
```typescript
|
||||
async call<T>(options: RPCOptions): Promise<T> {
|
||||
console.log('🔵 RPC:', options.method, options.params)
|
||||
// ... rest of method
|
||||
console.log('🟢 Result:', data.result)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
### What Was Fixed
|
||||
✅ WebSocket no longer crashes on bad patches
|
||||
✅ Marketplace shows clear, actionable errors
|
||||
✅ Better authentication checks
|
||||
✅ Comprehensive error handling
|
||||
|
||||
### What You Should See Now
|
||||
✅ No crashes or undefined errors
|
||||
✅ Clear error messages with next steps
|
||||
✅ App continues working even if WebSocket fails
|
||||
✅ Marketplace works when backend is available
|
||||
|
||||
### Next Steps
|
||||
1. Start backend: `cargo run --release`
|
||||
2. Start UI: `npm run dev`
|
||||
3. Login at `http://localhost:8100`
|
||||
4. Test marketplace functionality
|
||||
|
||||
**See `TROUBLESHOOTING.md` for detailed debugging help!**
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
# Neode Logo Usage Guide
|
||||
|
||||
## Logo File
|
||||
**Primary Logo**: `/assets/img/logo-neode.png`
|
||||
- Format: PNG with transparency
|
||||
- Size: 454x454 pixels
|
||||
- Type: Square icon/badge style
|
||||
|
||||
## Usage Throughout App
|
||||
|
||||
### 1. **Splash Screen**
|
||||
- **Size**: Medium-large (300px max width)
|
||||
- **Position**: Centered on screen
|
||||
- **Timing**: Appears after alien intro and welcome message
|
||||
- **Style**: Drop shadow for depth
|
||||
|
||||
```vue
|
||||
<img
|
||||
src="/assets/img/logo-neode.png"
|
||||
class="w-[min(50vw,300px)] max-w-[80vw]"
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. **Login Page**
|
||||
- **Size**: 96px (24 in Tailwind = 96px)
|
||||
- **Position**: **Half in, half out of glass card** (original design)
|
||||
- **Style**: Absolute positioned at `-top-12` (48px up from card top)
|
||||
- **Effect**: Logo appears to "float" above the card
|
||||
|
||||
```vue
|
||||
<div class="glass-card p-8 pt-20 relative">
|
||||
<div class="absolute -top-12 left-1/2 -translate-x-1/2 z-10">
|
||||
<img
|
||||
src="/assets/img/logo-neode.png"
|
||||
class="w-24 h-24"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Visual Effect:**
|
||||
```
|
||||
┌─────┐
|
||||
│ │ ← Half of logo above card
|
||||
╔══│═════│══╗
|
||||
║ │ │ ║
|
||||
║ └─────┘ ║ ← Half of logo inside card
|
||||
║ ║
|
||||
║ Content ║
|
||||
╚═══════════╝
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. **Onboarding Intro**
|
||||
- **Size**: 128px (32 in Tailwind)
|
||||
- **Position**: **Half in, half out of glass card**
|
||||
- **Style**: Same floating effect as login
|
||||
|
||||
```vue
|
||||
<div class="glass-card p-12 pt-20 relative">
|
||||
<div class="absolute -top-16 left-1/2 -translate-x-1/2">
|
||||
<img
|
||||
src="/assets/img/logo-neode.png"
|
||||
class="w-32 h-32"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. **Onboarding Options**
|
||||
- **Size**: 128px (32 in Tailwind)
|
||||
- **Position**: Centered above heading (not in card)
|
||||
- **Style**: Standard centered logo
|
||||
|
||||
```vue
|
||||
<img
|
||||
src="/assets/img/logo-neode.png"
|
||||
class="w-32 h-32 mx-auto mb-8"
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. **Dashboard Sidebar**
|
||||
- **Size**: 64px (16 in Tailwind)
|
||||
- **Position**: Top of sidebar, inline with server name
|
||||
- **Style**: Compact for sidebar
|
||||
|
||||
```vue
|
||||
<div class="flex items-center gap-3">
|
||||
<img src="/assets/img/logo-neode.png" class="w-16 h-16" />
|
||||
<div>
|
||||
<h2>Server Name</h2>
|
||||
<p>v0.0.0</p>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. **Browser Tab (Favicon)**
|
||||
- **File**: Same logo used as favicon
|
||||
- **Platforms**: Standard favicon + Apple touch icon
|
||||
|
||||
```html
|
||||
<link rel="icon" type="image/png" href="/assets/img/logo-neode.png" />
|
||||
<link rel="apple-touch-icon" href="/assets/img/logo-neode.png" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Size Reference
|
||||
|
||||
| Location | Tailwind Class | Actual Size | Purpose |
|
||||
|----------|---------------|-------------|---------|
|
||||
| Splash | `w-[min(50vw,300px)]` | Up to 300px | Large reveal |
|
||||
| Onboarding Intro | `w-32 h-32` | 128px | Prominent |
|
||||
| Onboarding Options | `w-32 h-32` | 128px | Header |
|
||||
| Login | `w-24 h-24` | 96px | Floating |
|
||||
| Sidebar | `w-16 h-16` | 64px | Compact |
|
||||
|
||||
---
|
||||
|
||||
## The "Half In, Half Out" Effect
|
||||
|
||||
This is the signature Neode design pattern for modals/cards:
|
||||
|
||||
### CSS Pattern:
|
||||
```vue
|
||||
<div class="glass-card pt-20 relative"> <!-- Extra top padding -->
|
||||
<div class="absolute -top-12 left-1/2 -translate-x-1/2 z-10">
|
||||
<img src="/assets/img/logo-neode.png" class="w-24 h-24" />
|
||||
</div>
|
||||
<!-- Content -->
|
||||
</div>
|
||||
```
|
||||
|
||||
### Key Properties:
|
||||
- **Parent card**: `relative` positioning, `pt-20` (extra top padding for logo space)
|
||||
- **Logo container**: `absolute` positioning
|
||||
- **Vertical**: `-top-12` (moves logo up by 48px, half of 96px logo height)
|
||||
- **Horizontal**: `left-1/2 -translate-x-1/2` (perfect centering)
|
||||
- **Z-index**: `z-10` (appears above card)
|
||||
|
||||
### Math:
|
||||
- Logo height: 96px
|
||||
- Pushed up: `-48px` (half the height)
|
||||
- Result: **Top 48px outside card, bottom 48px inside card**
|
||||
|
||||
---
|
||||
|
||||
## Don't Use These (Deprecated)
|
||||
|
||||
❌ `/assets/img/logo-large.svg` - Old text-based logo
|
||||
❌ `/assets/img/icon.png` - Generic icon
|
||||
❌ `/assets/img/neode-logo.png` - Duplicate, use `logo-neode.png`
|
||||
|
||||
✅ **Always use**: `/assets/img/logo-neode.png`
|
||||
|
||||
---
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
The logo represents:
|
||||
- **Sovereignty**: Bold, centered presence
|
||||
- **Elegance**: Clean design that works with glassmorphism
|
||||
- **Trust**: Consistent across all touchpoints
|
||||
|
||||
The "floating" effect on cards creates visual hierarchy and draws attention to the brand while maintaining the clean, modern aesthetic.
|
||||
|
||||
---
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
### Mobile (< 768px):
|
||||
- Logo sizes scale down proportionally
|
||||
- Floating effect maintained
|
||||
- Touch-friendly sizing
|
||||
|
||||
### Tablet (768px - 1024px):
|
||||
- Standard sizes
|
||||
- Full effects
|
||||
|
||||
### Desktop (> 1024px):
|
||||
- Largest sizes for impact
|
||||
- Maximum visual effect
|
||||
|
||||
---
|
||||
|
||||
**Remember**: The logo is your brand identity. Use it consistently! 🎨
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
# Quick Video Optimization Guide
|
||||
|
||||
## Current Status
|
||||
|
||||
- **Video File**: `public/assets/video/video-intro.mp4`
|
||||
- **Current Size**: ~18MB
|
||||
- **Target**: Optimize to ~1MB for fast web loading
|
||||
|
||||
## Step 1: Install FFmpeg (if not already installed)
|
||||
|
||||
```bash
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
## Step 2: Run Optimization Script (1MB Target)
|
||||
|
||||
Once FFmpeg is installed, run:
|
||||
|
||||
```bash
|
||||
cd neode-ui
|
||||
./optimize-video-1mb.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
- ✅ Create a backup of your original video
|
||||
- ✅ Optimize using H.264 with CRF 30 (good quality, smaller file)
|
||||
- ✅ Scale to 1280x720 (HD resolution)
|
||||
- ✅ Reduce frame rate to 30fps
|
||||
- ✅ Compress audio to 64kbps
|
||||
- ✅ Add faststart flag for web streaming
|
||||
- ✅ Target ~1MB file size
|
||||
- ✅ Replace original with optimized version
|
||||
|
||||
## Expected Results
|
||||
|
||||
- **Original**: ~18MB
|
||||
- **Optimized**: ~1-2MB (90-95% reduction)
|
||||
- **Quality**: Good quality (suitable for background video)
|
||||
- **Loading**: 10-20x faster
|
||||
|
||||
## Manual Command (Alternative - 1MB Target)
|
||||
|
||||
If you prefer to run manually:
|
||||
|
||||
```bash
|
||||
cd neode-ui/public/assets/video
|
||||
|
||||
# Backup original
|
||||
cp video-intro.mp4 video-intro-backup.mp4
|
||||
|
||||
# Optimize for ~1MB (CRF 30, 1280x720, 30fps)
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 \
|
||||
-preset slow \
|
||||
-crf 30 \
|
||||
-profile:v high \
|
||||
-level 4.0 \
|
||||
-pix_fmt yuv420p \
|
||||
-vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" \
|
||||
-r 30 \
|
||||
-c:a aac \
|
||||
-b:a 64k \
|
||||
-ar 44100 \
|
||||
-movflags +faststart \
|
||||
-threads 0 \
|
||||
video-intro-optimized.mp4
|
||||
|
||||
# Replace original
|
||||
mv video-intro-optimized.mp4 video-intro.mp4
|
||||
```
|
||||
|
||||
### If Still Too Large, Use More Aggressive Settings:
|
||||
|
||||
```bash
|
||||
# Even smaller (~500KB-1MB) - CRF 32, 854x480
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 \
|
||||
-preset slow \
|
||||
-crf 32 \
|
||||
-profile:v high \
|
||||
-level 4.0 \
|
||||
-pix_fmt yuv420p \
|
||||
-vf "scale=854:480:force_original_aspect_ratio=decrease,pad=854:480:(ow-iw)/2:(oh-ih)/2" \
|
||||
-r 24 \
|
||||
-c:a aac \
|
||||
-b:a 48k \
|
||||
-ar 44100 \
|
||||
-movflags +faststart \
|
||||
video-intro-small.mp4
|
||||
```
|
||||
|
||||
## After Optimization
|
||||
|
||||
1. **Update cache version** in code:
|
||||
- `SplashScreen.vue`: Change `?v=3` to `?v=4`
|
||||
- `OnboardingWrapper.vue`: Change `?v=3` to `?v=4`
|
||||
|
||||
2. **Test the video**:
|
||||
- Verify smooth playback
|
||||
- Check looping works correctly
|
||||
- Test on mobile devices
|
||||
|
||||
## Quality Settings Explained (1MB Target)
|
||||
|
||||
- **CRF 30**: Good quality (recommended for 1MB target)
|
||||
- Good visual quality suitable for background video
|
||||
- ~1-2MB file size
|
||||
- Best balance for web
|
||||
|
||||
- **CRF 28**: Better quality
|
||||
- Higher quality, ~2-3MB file size
|
||||
- Use if 1MB is too aggressive
|
||||
|
||||
- **CRF 32**: Smaller file
|
||||
- Lower quality but still acceptable
|
||||
- ~500KB-1MB file size
|
||||
- Use if you need to hit 1MB exactly
|
||||
|
||||
**Recommendation**: Start with CRF 30, adjust based on results.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Script fails
|
||||
- Ensure FFmpeg is installed: `brew install ffmpeg`
|
||||
- Check file permissions: `chmod +x optimize-video.sh`
|
||||
- Verify video file exists: `ls -lh public/assets/video/video-intro.mp4`
|
||||
|
||||
### Quality not good enough
|
||||
- Use CRF 15 instead of 18 (larger file, better quality)
|
||||
- Use `preset veryslow` instead of `slow` (slower encoding, better compression)
|
||||
|
||||
### File still too large
|
||||
- Use CRF 20-23 (smaller file, slight quality trade-off)
|
||||
- Reduce resolution if video is 4K: add `-vf "scale=1920:1080"`
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
# 🚀 Quick Start Guide - Neode UI Development
|
||||
|
||||
## ✅ Problem Solved!
|
||||
|
||||
The **HTTP 500 error** you were seeing was because:
|
||||
1. The backend server wasn't running on port 5959
|
||||
2. Your Vue UI was trying to proxy requests to a non-existent backend
|
||||
|
||||
**Solution:** I've created a mock backend server that simulates the StartOS API.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 How to Run (Two Options)
|
||||
|
||||
### Option 1: Run Everything Together (Recommended)
|
||||
```bash
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
npm run dev:mock
|
||||
```
|
||||
|
||||
This starts:
|
||||
- ✅ Mock backend on http://localhost:5959
|
||||
- ✅ Vite dev server on http://localhost:8100
|
||||
|
||||
### Option 2: Run Separately
|
||||
|
||||
**Terminal 1 - Start Mock Backend:**
|
||||
```bash
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
npm run backend:mock
|
||||
```
|
||||
|
||||
**Terminal 2 - Start UI:**
|
||||
```bash
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Login Credentials
|
||||
|
||||
To login to the UI, use:
|
||||
- **Password:** `password123`
|
||||
|
||||
---
|
||||
|
||||
## ✅ What's Working Now
|
||||
|
||||
The mock backend provides responses for:
|
||||
|
||||
- ✅ `auth.login` - Login with password
|
||||
- ✅ `auth.logout` - Logout
|
||||
- ✅ `server.echo` - Echo test
|
||||
- ✅ `server.time` - Current time
|
||||
- ✅ `server.metrics` - System metrics
|
||||
- ✅ `package.*` - Package operations (install, start, stop, etc.)
|
||||
- ✅ WebSocket at `/ws/db` - Real-time updates
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Current Setup
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ Your Browser │
|
||||
│ localhost:8100 │
|
||||
│ │
|
||||
│ Vue 3 UI │
|
||||
└────────┬─────────┘
|
||||
│ proxy
|
||||
↓
|
||||
┌──────────────────┐
|
||||
│ Vite Server │
|
||||
│ localhost:8100 │
|
||||
└────────┬─────────┘
|
||||
│ forward
|
||||
↓
|
||||
┌──────────────────┐
|
||||
│ Mock Backend │
|
||||
│ localhost:5959 │
|
||||
│ │
|
||||
│ (Simulates │
|
||||
│ StartOS API) │
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Still seeing HTTP 500?
|
||||
|
||||
1. **Check if backend is running:**
|
||||
```bash
|
||||
lsof -ti:5959
|
||||
```
|
||||
If nothing returns, the backend isn't running.
|
||||
|
||||
2. **Start the backend manually:**
|
||||
```bash
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
node mock-backend.js
|
||||
```
|
||||
|
||||
3. **Check for errors:**
|
||||
Look for console output in the terminal where you started the backend.
|
||||
|
||||
### Port already in use?
|
||||
|
||||
```bash
|
||||
# Kill process on port 5959
|
||||
lsof -ti:5959 | xargs kill -9
|
||||
|
||||
# Kill process on port 8100
|
||||
lsof -ti:8100 | xargs kill -9
|
||||
```
|
||||
|
||||
### Login not working?
|
||||
|
||||
1. Make sure you're using password: `password123`
|
||||
2. Check browser console (F12) for specific errors
|
||||
3. Verify backend is responding:
|
||||
```bash
|
||||
curl -X POST http://localhost:5959/rpc/v1 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"method":"server.echo","params":{"message":"test"}}'
|
||||
```
|
||||
Should return: `{"result":"test"}`
|
||||
|
||||
---
|
||||
|
||||
## 📝 Next Steps
|
||||
|
||||
1. **Try logging in** with password `password123`
|
||||
2. **Explore the UI** - all API calls will return mock data
|
||||
3. **Add new features** - the mock backend will respond appropriately
|
||||
4. **Test real backend** - when ready, update `vite.config.ts` to point to your actual StartOS instance
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Connecting to Real Backend (Later)
|
||||
|
||||
When you have a real StartOS instance running, update `neode-ui/vite.config.ts`:
|
||||
|
||||
```typescript
|
||||
server: {
|
||||
proxy: {
|
||||
'/rpc': {
|
||||
target: 'http://YOUR_STARTOS_IP', // Change this
|
||||
changeOrigin: true,
|
||||
},
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 More Documentation
|
||||
|
||||
- See `README-MOCK-BACKEND.md` for detailed mock backend documentation
|
||||
- See `README.md` for general project information
|
||||
- See `QUICK_START.md` for Vue + Vite specifics
|
||||
|
||||
---
|
||||
|
||||
## ✨ Happy Coding!
|
||||
|
||||
Your development environment is now ready. The HTTP 500 error should be gone, and you can login with `password123`.
|
||||
|
||||
If you have any issues, check:
|
||||
1. Both servers are running (ports 5959 and 8100)
|
||||
2. No firewall blocking the ports
|
||||
3. Browser console for specific errors
|
||||
|
||||
**Current Status:**
|
||||
- ✅ Mock backend running on port 5959
|
||||
- ✅ Vite dev server should be running on port 8100
|
||||
- ✅ Login ready with password: `password123`
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
# 🚀 Quick Start Guide
|
||||
|
||||
## What Was Built
|
||||
|
||||
A **complete Vue 3 + Vite + Tailwind rewrite** of your Neode UI that:
|
||||
|
||||
✅ **Recreates ALL your UI work:**
|
||||
- Glassmorphism design system
|
||||
- Alien-style splash screen with typing animations
|
||||
- Login page with glass cards
|
||||
- Onboarding flow (intro + options)
|
||||
- Dashboard with glass sidebar
|
||||
- Apps list with service cards
|
||||
- Connection status handling
|
||||
|
||||
✅ **Fixes routing issues:**
|
||||
- Clean Vue Router (no more disappearing components!)
|
||||
- Predictable navigation
|
||||
- Proper auth guards
|
||||
|
||||
✅ **Connects to your backend:**
|
||||
- RPC client (same endpoints as Angular)
|
||||
- WebSocket for real-time updates
|
||||
- Pinia store (replaces PatchDB pattern)
|
||||
|
||||
## Start Developing
|
||||
|
||||
```bash
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Visit: **http://localhost:8100**
|
||||
|
||||
## Test the App
|
||||
|
||||
Since you're in mock mode, you can test:
|
||||
|
||||
1. **Splash Screen** - Should show the alien intro on first visit
|
||||
2. **Skip Button** - Click to skip the intro
|
||||
3. **Onboarding** - After splash, you'll see the onboarding flow
|
||||
4. **Login** - Glass card with password input
|
||||
5. **Dashboard** - Glass sidebar with navigation
|
||||
6. **Apps** - List view with glass cards
|
||||
|
||||
## Connect to Real Backend
|
||||
|
||||
When you're ready to connect to the actual backend:
|
||||
|
||||
1. **Update proxy in `vite.config.ts`** if backend isn't on port 5959
|
||||
2. **Remove mock logic** - The RPC client is ready to go!
|
||||
3. **Test login** - Use your actual password
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
neode-ui/
|
||||
├── src/
|
||||
│ ├── api/
|
||||
│ │ ├── rpc-client.ts # RPC methods (login, packages, etc.)
|
||||
│ │ └── websocket.ts # WebSocket connection & patches
|
||||
│ ├── stores/
|
||||
│ │ └── app.ts # Pinia store (global state)
|
||||
│ ├── views/
|
||||
│ │ ├── Login.vue # Login page
|
||||
│ │ ├── OnboardingIntro.vue # Onboarding welcome
|
||||
│ │ ├── OnboardingOptions.vue # Setup options
|
||||
│ │ ├── Dashboard.vue # Main layout with sidebar
|
||||
│ │ ├── Home.vue # Dashboard home
|
||||
│ │ ├── Apps.vue # Apps list
|
||||
│ │ └── ... # Other pages
|
||||
│ ├── components/
|
||||
│ │ └── SplashScreen.vue # Alien intro animation
|
||||
│ ├── router/
|
||||
│ │ └── index.ts # Routes & auth guard
|
||||
│ ├── types/
|
||||
│ │ └── api.ts # TypeScript types
|
||||
│ └── style.css # Tailwind + glassmorphism
|
||||
├── public/assets/img/ # Your images & logo
|
||||
└── vite.config.ts # Vite config with proxy
|
||||
```
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
# Start dev server (hot reload)
|
||||
npm run dev
|
||||
|
||||
# Build for production
|
||||
npm run build
|
||||
|
||||
# Preview production build
|
||||
npm run preview
|
||||
|
||||
# Type check
|
||||
npm run type-check
|
||||
```
|
||||
|
||||
## Key Differences from Angular
|
||||
|
||||
| Angular | Vue 3 |
|
||||
|---------|-------|
|
||||
| Modules + Services | Composables + Stores |
|
||||
| RxJS Observables | Reactive refs/computed |
|
||||
| Ionic Components | Native HTML + Tailwind |
|
||||
| Complex routing | Simple Vue Router |
|
||||
| PatchDB service | Pinia store |
|
||||
| Slow CLI | Fast Vite |
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Test the UI** - Run `npm run dev` and explore
|
||||
2. **Compare routing** - Notice how stable the navigation is
|
||||
3. **Check glassmorphism** - Your design is intact!
|
||||
4. **Connect backend** - Update vite.config.ts proxy if needed
|
||||
5. **Iterate** - Add features without Angular complexity
|
||||
|
||||
## Why This Is Better
|
||||
|
||||
### Routing Fixed ✅
|
||||
- No more disappearing components
|
||||
- Clean navigation with Vue Router
|
||||
- Predictable route guards
|
||||
|
||||
### Faster Development ⚡
|
||||
- Vite HMR is instant (vs Angular's slow recompile)
|
||||
- Simpler component structure
|
||||
- Less boilerplate
|
||||
|
||||
### Easier to Maintain 🛠️
|
||||
- Smaller bundle size
|
||||
- Modern patterns (Composition API)
|
||||
- Better TypeScript integration
|
||||
|
||||
### Same Features 🎨
|
||||
- All your glassmorphism styling
|
||||
- Splash screen with animations
|
||||
- Login, onboarding, apps list
|
||||
- WebSocket state sync
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Server won't start?**
|
||||
```bash
|
||||
# Kill any process on port 8100
|
||||
lsof -ti:8100 | xargs kill -9
|
||||
npm run dev
|
||||
```
|
||||
|
||||
**Assets missing?**
|
||||
```bash
|
||||
# Copy from Angular project
|
||||
cp -r ../web/projects/shared/assets/img/* public/assets/img/
|
||||
```
|
||||
|
||||
**Backend connection fails?**
|
||||
- Check backend is running
|
||||
- Update proxy in `vite.config.ts`
|
||||
- Check browser console for CORS errors
|
||||
|
||||
**TypeScript errors?**
|
||||
- Check `src/types/api.ts` matches your backend
|
||||
- Run `npm run type-check`
|
||||
|
||||
## Success! 🎉
|
||||
|
||||
You now have a **modern, stable, fast** UI that's easier to work with than Angular. The routing issues are gone, development is faster, and you can iterate quickly.
|
||||
|
||||
**Ready to test?**
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Then open http://localhost:8100 and see your beautiful glass UI in action!
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
# Mock Backend for Neode UI Development
|
||||
|
||||
This directory includes a mock backend server that simulates the StartOS backend API, allowing you to develop the UI without needing a full StartOS instance.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option 1: Run Both Mock Backend + UI Together
|
||||
```bash
|
||||
npm install
|
||||
npm run dev:mock
|
||||
```
|
||||
|
||||
This will start:
|
||||
- Mock backend on `http://localhost:5959`
|
||||
- Vite dev server on `http://localhost:8100`
|
||||
|
||||
### Option 2: Run Separately
|
||||
|
||||
**Terminal 1 - Mock Backend:**
|
||||
```bash
|
||||
npm run backend:mock
|
||||
```
|
||||
|
||||
**Terminal 2 - UI:**
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Mock Credentials
|
||||
|
||||
Use these credentials to login:
|
||||
- **Password:** `password123`
|
||||
|
||||
## What Works
|
||||
|
||||
The mock backend provides fake responses for:
|
||||
|
||||
### Authentication
|
||||
- ✅ `auth.login` - Login with password
|
||||
- ✅ `auth.logout` - Logout and clear session
|
||||
|
||||
### Server Operations
|
||||
- ✅ `server.echo` - Echo test
|
||||
- ✅ `server.time` - Current time and uptime
|
||||
- ✅ `server.metrics` - System metrics (CPU, memory, disk)
|
||||
- ✅ `server.update` - Trigger update (fake)
|
||||
- ✅ `server.restart` - Restart server (fake)
|
||||
- ✅ `server.shutdown` - Shutdown server (fake)
|
||||
|
||||
### Package Management
|
||||
- ✅ `package.install` - Install package (fake)
|
||||
- ✅ `package.uninstall` - Uninstall package (fake)
|
||||
- ✅ `package.start` - Start package (fake)
|
||||
- ✅ `package.stop` - Stop package (fake)
|
||||
- ✅ `package.restart` - Restart package (fake)
|
||||
|
||||
### Real-time Updates
|
||||
- ✅ WebSocket connection at `/ws/db`
|
||||
- ✅ Sends initial data on connection
|
||||
- ✅ Sends periodic JSON Patch updates
|
||||
|
||||
## Mock Data
|
||||
|
||||
The mock backend provides:
|
||||
- Server info (name, version, status)
|
||||
- 2 mock packages (Bitcoin Core, Lightning Network)
|
||||
- UI preferences and theme settings
|
||||
- Fake system metrics
|
||||
|
||||
## Connecting to Real Backend
|
||||
|
||||
If you have access to a real StartOS instance, update `vite.config.ts`:
|
||||
|
||||
```typescript
|
||||
server: {
|
||||
port: 8100,
|
||||
proxy: {
|
||||
'/rpc': {
|
||||
target: 'http://YOUR_STARTOS_IP', // Change this
|
||||
changeOrigin: true,
|
||||
},
|
||||
// ... other proxies
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
||||
│ │ │ │ │ │
|
||||
│ Vue 3 UI │────────▶│ Vite Proxy │────────▶│ Mock Backend │
|
||||
│ localhost:8100 │ │ (transparent) │ │ localhost:5959 │
|
||||
│ │◀────────│ │◀────────│ │
|
||||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
1. UI makes requests to `/rpc/v1` and `/ws/db`
|
||||
2. Vite dev server proxies them to `localhost:5959`
|
||||
3. Mock backend responds with fake data
|
||||
4. UI receives responses as if from a real backend
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Port 5959 Already in Use
|
||||
```bash
|
||||
# Find and kill the process
|
||||
lsof -ti:5959 | xargs kill -9
|
||||
```
|
||||
|
||||
### Mock Backend Won't Start
|
||||
Make sure dependencies are installed:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### WebSocket Connection Failed
|
||||
Check that:
|
||||
1. Mock backend is running (`npm run backend:mock`)
|
||||
2. Browser console shows WebSocket connection to `ws://localhost:5959/ws/db`
|
||||
3. No firewall blocking port 5959
|
||||
|
||||
## Development Tips
|
||||
|
||||
### Adding New RPC Methods
|
||||
|
||||
Edit `mock-backend.js` and add a new case in the switch statement:
|
||||
|
||||
```javascript
|
||||
case 'your.new.method': {
|
||||
return res.json({
|
||||
result: {
|
||||
// Your mock response
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Modifying Mock Data
|
||||
|
||||
Edit the `mockData` object in `mock-backend.js`:
|
||||
|
||||
```javascript
|
||||
const mockData = {
|
||||
'server-info': {
|
||||
name: 'Your Server Name', // Customize here
|
||||
version: '0.3.5',
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Testing WebSocket Updates
|
||||
|
||||
The mock backend sends random updates every 5 seconds. To customize:
|
||||
|
||||
```javascript
|
||||
const interval = setInterval(() => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'patch',
|
||||
patch: [
|
||||
// Your JSON Patch operations
|
||||
],
|
||||
}))
|
||||
}
|
||||
}, 5000) // Change interval here
|
||||
```
|
||||
|
||||
## Production Build
|
||||
|
||||
When building for production, make sure you're pointing to a real backend:
|
||||
|
||||
```bash
|
||||
# Build the UI
|
||||
npm run build
|
||||
|
||||
# Output goes to ../web/dist/neode-ui/
|
||||
```
|
||||
|
||||
The mock backend is only for development and should never be used in production!
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [ ] Connect to a real StartOS instance for full testing
|
||||
- [ ] Test all UI flows with mock data
|
||||
- [ ] Implement missing RPC methods as needed
|
||||
- [ ] Add more realistic mock data
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues:
|
||||
1. Check that Node.js v18+ is installed
|
||||
2. Verify all dependencies are installed (`npm install`)
|
||||
3. Check both terminal outputs for errors
|
||||
4. Review browser console for network errors
|
||||
|
||||
+112
-182
@@ -1,41 +1,10 @@
|
||||
# Neode UI - Vue 3 Edition
|
||||
# Archipelago Web UI
|
||||
|
||||
A modern, clean Vue 3 + Vite + Tailwind rewrite of the Neode OS interface.
|
||||
Vue 3 + TypeScript + Vite + Tailwind CSS + Pinia
|
||||
|
||||
## 🎯 Why This Rewrite?
|
||||
The web interface for Archipelago — a self-sovereign Bitcoin Node OS.
|
||||
|
||||
The original Angular interface had routing issues, disappearing components, and was difficult to maintain. This Vue 3 rewrite provides:
|
||||
|
||||
- ✅ **Clean routing** - Vue Router is simpler and more predictable than Angular router
|
||||
- ✅ **Modern tooling** - Vite is 10x faster than Angular CLI
|
||||
- ✅ **Better DX** - TypeScript + Vue 3 Composition API + Tailwind = rapid development
|
||||
- ✅ **Same glassmorphism design** - All your beautiful UI styling recreated
|
||||
- ✅ **Same features** - Splash screen, login, onboarding, apps list, etc.
|
||||
- ✅ **Backend agnostic** - Connects to the same Rust backend via RPC/WebSocket
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
```
|
||||
neode-ui/
|
||||
├── src/
|
||||
│ ├── api/ # RPC client & WebSocket handler
|
||||
│ ├── stores/ # Pinia state management (replaces PatchDB)
|
||||
│ ├── views/ # Page components
|
||||
│ ├── components/ # Reusable components (SplashScreen, etc.)
|
||||
│ ├── router/ # Vue Router configuration
|
||||
│ ├── types/ # TypeScript types (ported from Angular)
|
||||
│ └── style.css # Global styles + Tailwind
|
||||
├── public/assets/ # Static assets (images, fonts, icons)
|
||||
└── vite.config.ts # Vite config with proxy to backend
|
||||
```
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js 20.19+ or 22.12+ (20.18.2 works but shows warning)
|
||||
|
||||
### Quick Start (Recommended)
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
cd neode-ui
|
||||
@@ -43,183 +12,144 @@ npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
Visit **http://localhost:8100** and login with password: `password123`
|
||||
Visit **http://localhost:8100** — login with password: `password123`
|
||||
|
||||
This starts both:
|
||||
- ✅ Mock backend (port 5959) - no Docker required!
|
||||
- ✅ Vite dev server (port 8100) with instant HMR
|
||||
This starts:
|
||||
- Mock backend on port 5959 (no Docker required)
|
||||
- Vite dev server on port 8100 with HMR
|
||||
|
||||
**Stop servers:**
|
||||
```bash
|
||||
npm stop
|
||||
Stop with `npm stop`.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
neode-ui/
|
||||
├── src/
|
||||
│ ├── api/ # RPC client (rpc-client.ts), WebSocket, container client
|
||||
│ ├── stores/ # Pinia stores (app, container, appLauncher, monitoring)
|
||||
│ ├── views/ # Page components (Dashboard, Marketplace, Settings, etc.)
|
||||
│ ├── components/ # Reusable components (SplashScreen, AppSession, etc.)
|
||||
│ ├── router/ # Vue Router configuration
|
||||
│ ├── types/ # TypeScript type definitions
|
||||
│ └── style.css # Global styles + Tailwind utilities
|
||||
├── public/assets/ # Static assets (images, fonts, app icons, audio)
|
||||
├── mock-backend.js # Mock backend server (simulates Rust backend)
|
||||
├── docker/ # Docker configs (nginx, entrypoint)
|
||||
└── vite.config.ts # Vite config with backend proxy
|
||||
```
|
||||
|
||||
📖 **See [DEV-SCRIPTS.md](./DEV-SCRIPTS.md) for detailed documentation**
|
||||
## Dev Modes
|
||||
|
||||
### Development Options
|
||||
The mock backend supports multiple startup modes via `VITE_DEV_MODE`:
|
||||
|
||||
| Mode | Command | Behavior |
|
||||
|------|---------|----------|
|
||||
| **default** | `npm start` | Fully set up, login screen |
|
||||
| **existing** | `VITE_DEV_MODE=existing npm run dev:mock` | Same as default |
|
||||
| **setup** | `VITE_DEV_MODE=setup npm run dev:mock` | First-time password setup flow |
|
||||
| **onboarding** | `VITE_DEV_MODE=onboarding npm run dev:mock` | Post-setup onboarding flow |
|
||||
| **boot** | `npm run dev:boot` | 25s simulated boot sequence |
|
||||
|
||||
## Mock Backend
|
||||
|
||||
The mock backend (`mock-backend.js`) simulates the full Rust backend for local development:
|
||||
|
||||
**Pre-installed apps** (always visible in My Apps):
|
||||
- Bitcoin Core, LND, Electrs, Mempool, FileBrowser, LoraBell, ThunderHub, Fedimint
|
||||
|
||||
**Marketplace**: 30+ curated apps with Docker images, install/uninstall simulation
|
||||
|
||||
**Features simulated**:
|
||||
- Authentication (login, password change, TOTP 2FA)
|
||||
- System metrics (CPU, memory, disk — randomized for realism)
|
||||
- Node identity (DID, Nostr pubkey, Tor address)
|
||||
- Federation (3 mock nodes with apps, metrics, trust levels)
|
||||
- Mesh networking (4 LoRa peers, encrypted messaging, invoices)
|
||||
- Peer-to-peer messaging
|
||||
- FileBrowser API (mock file system with Music, Documents, Photos, Videos)
|
||||
- DWN sync status
|
||||
- Transport layer (mesh/LAN/Tor routing)
|
||||
- Notifications (5 realistic entries)
|
||||
- Claude AI chat proxy (requires `ANTHROPIC_API_KEY`)
|
||||
|
||||
**Container runtime**: If Docker/Podman is available, the mock backend will run real containers for installed apps. Otherwise, it simulates them.
|
||||
|
||||
## Demo Deployment (Portainer)
|
||||
|
||||
Deploy the demo via Docker Compose for showcasing:
|
||||
|
||||
```bash
|
||||
# Start everything (mock backend + Vite)
|
||||
npm start
|
||||
|
||||
# Stop everything
|
||||
npm stop
|
||||
|
||||
# Run mock backend only
|
||||
npm run backend:mock
|
||||
|
||||
# Run Vite only (requires backend running separately)
|
||||
npm run dev
|
||||
|
||||
# Run with real Rust backend (requires core/ to be running)
|
||||
npm run dev:real
|
||||
docker compose -f docker-compose.demo.yml build
|
||||
docker compose -f docker-compose.demo.yml up -d
|
||||
```
|
||||
|
||||
### Build for Production
|
||||
Or deploy through **Portainer Stacks**:
|
||||
1. Stacks > Add stack > name: `archy-demo`
|
||||
2. Web editor: paste `docker-compose.demo.yml` contents
|
||||
3. Add environment variable: `ANTHROPIC_API_KEY` (for Claude chat)
|
||||
4. Deploy
|
||||
|
||||
Access at **http://your-host:4848** — password: `password123`
|
||||
|
||||
## Development Commands
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npm start # Start mock backend + Vite (recommended)
|
||||
npm stop # Stop all servers
|
||||
npm run dev:mock # Same as start, without port cleanup
|
||||
npm run dev:boot # Boot mode (simulated startup delay)
|
||||
npm run backend:mock # Mock backend only
|
||||
npm run dev # Vite only (needs backend running separately)
|
||||
npm run dev:real # Vite with real Rust backend
|
||||
|
||||
npm run build # Production build (outputs to ../web/dist/neode-ui/)
|
||||
npm run build:docker # Build for Docker (no type checking)
|
||||
npm run type-check # TypeScript type checking
|
||||
npm test # Run tests
|
||||
```
|
||||
|
||||
Outputs to `../web/dist/neode-ui/`
|
||||
## Design System
|
||||
|
||||
## 🎨 Design System
|
||||
### Glass Classes
|
||||
|
||||
### Glassmorphism Classes
|
||||
| Class | Use |
|
||||
|-------|-----|
|
||||
| `.glass-card` | Content containers, modals, panels |
|
||||
| `.glass-button` | ALL buttons (primary and secondary) |
|
||||
| `.path-option-card` | Interactive cards with hover lift |
|
||||
| `.info-card` | Status badges, metric displays |
|
||||
|
||||
```html
|
||||
<!-- Glass card -->
|
||||
<div class="glass-card p-6">Content</div>
|
||||
### Tokens
|
||||
|
||||
<!-- Glass button -->
|
||||
<button class="glass-button px-4 py-3">Click me</button>
|
||||
- **Font**: Avenir Next (primary), Montserrat (`font-archipelago`)
|
||||
- **Glass**: `bg: rgba(0,0,0,0.60)`, `blur: 24px`, `border: rgba(255,255,255,0.22)`
|
||||
- **Accent**: `#fb923c` (Bitcoin orange), `#4ade80` (green), `#ef4444` (red)
|
||||
- **Text**: `rgba(255,255,255,0.9)` primary, `rgba(255,255,255,0.6)` muted
|
||||
|
||||
<!-- Manual glass styling -->
|
||||
<div class="bg-glass-dark backdrop-blur-glass border border-glass-border shadow-glass">
|
||||
Custom glass element
|
||||
</div>
|
||||
```
|
||||
### Rules
|
||||
|
||||
### Spacing (4px grid system)
|
||||
- Global CSS classes in `style.css` only — never inline Tailwind in components
|
||||
- `.gradient-button` is **banned** — use `.glass-button`
|
||||
- All components use `<script setup lang="ts">`
|
||||
|
||||
- `p-4` = 16px padding
|
||||
- `m-6` = 24px margin
|
||||
- `gap-4` = 16px gap
|
||||
|
||||
### Colors
|
||||
|
||||
- `text-white/80` = 80% white opacity
|
||||
- `bg-glass-dark` = rgba(0, 0, 0, 0.35)
|
||||
- `border-glass-border` = rgba(255, 255, 255, 0.18)
|
||||
|
||||
## 🔌 API Connection
|
||||
|
||||
### RPC Calls
|
||||
## API
|
||||
|
||||
```typescript
|
||||
import { rpcClient } from '@/api/rpc-client'
|
||||
|
||||
// Login
|
||||
await rpcClient.login('password')
|
||||
|
||||
// Start a package
|
||||
await rpcClient.startPackage('bitcoin')
|
||||
|
||||
// Get metrics
|
||||
const metrics = await rpcClient.getMetrics()
|
||||
```
|
||||
|
||||
### State Management (Pinia)
|
||||
State management via Pinia stores. WebSocket patches applied automatically.
|
||||
|
||||
```typescript
|
||||
import { useAppStore } from '@/stores/app'
|
||||
## Build Output
|
||||
|
||||
const store = useAppStore()
|
||||
- **Dev build**: `../web/dist/neode-ui/`
|
||||
- **Docker build**: `dist/` (deployed to nginx)
|
||||
- **Production deploy**: via `scripts/deploy-to-target.sh --live`
|
||||
|
||||
// Access reactive state
|
||||
const packages = computed(() => store.packages)
|
||||
const isAuthenticated = computed(() => store.isAuthenticated)
|
||||
## License
|
||||
|
||||
// Call actions
|
||||
await store.login(password)
|
||||
await store.installPackage('nextcloud', marketplaceUrl, '1.0.0')
|
||||
```
|
||||
|
||||
### WebSocket Updates
|
||||
|
||||
The store automatically subscribes to WebSocket updates and applies JSON patches to the state. No manual setup required!
|
||||
|
||||
## 📝 Routes
|
||||
|
||||
| Route | Component | Description |
|
||||
|-------|-----------|-------------|
|
||||
| `/` | Redirect | Redirects to /login |
|
||||
| `/login` | Login | Login page with glass styling |
|
||||
| `/onboarding/intro` | OnboardingIntro | Welcome screen |
|
||||
| `/onboarding/options` | OnboardingOptions | Setup options |
|
||||
| `/dashboard` | Dashboard | Main layout with sidebar |
|
||||
| `/dashboard/apps` | Apps | Apps list with glass cards |
|
||||
| `/dashboard/apps/:id` | AppDetails | App details page |
|
||||
| `/dashboard/marketplace` | Marketplace | Browse apps |
|
||||
| `/dashboard/server` | Server | Server settings |
|
||||
| `/dashboard/settings` | Settings | UI settings |
|
||||
|
||||
## ✨ Features Recreated
|
||||
|
||||
- [x] Alien-style terminal splash screen with typing animation
|
||||
- [x] Skip intro button
|
||||
- [x] Login page with glass card and fade-up animation
|
||||
- [x] Onboarding intro with feature highlights
|
||||
- [x] Onboarding options with selectable glass cards
|
||||
- [x] Dashboard layout with glass sidebar
|
||||
- [x] Apps list with status badges and quick actions
|
||||
- [x] Connection status banner
|
||||
- [x] Offline detection
|
||||
- [x] WebSocket state synchronization
|
||||
- [x] RPC authentication
|
||||
- [x] Responsive design
|
||||
|
||||
## 🐛 Debugging
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Assets not loading?**
|
||||
```bash
|
||||
# Ensure assets are copied
|
||||
cp -r ../web/projects/shared/assets/img/* public/assets/img/
|
||||
```
|
||||
|
||||
**Backend connection refused?**
|
||||
- Check backend is running on port 5959
|
||||
- Update proxy in `vite.config.ts` if using different port
|
||||
|
||||
**TypeScript errors?**
|
||||
```bash
|
||||
npm run type-check
|
||||
```
|
||||
|
||||
## 📦 Deployment
|
||||
|
||||
The Vue app can be served by the Rust backend (replace the Angular build):
|
||||
|
||||
1. Build: `npm run build` (outputs to `../web/dist/neode-ui/`)
|
||||
2. Update Rust to serve from this directory
|
||||
3. Restart backend
|
||||
|
||||
## 🔮 Future Enhancements
|
||||
|
||||
- [ ] Dark/light theme toggle
|
||||
- [ ] App configuration UI
|
||||
- [ ] Marketplace browsing & search
|
||||
- [ ] Server metrics charts
|
||||
- [ ] Backup/restore UI
|
||||
- [ ] Notification system
|
||||
- [ ] Multi-language support
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
This is a clean rewrite - feel free to add features without the baggage of the old Angular codebase!
|
||||
|
||||
## 📄 License
|
||||
|
||||
Same as Neode OS
|
||||
MIT
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
# How to See ATOB Changes
|
||||
|
||||
The mock backend has ATOB data, but your browser needs to refresh properly.
|
||||
|
||||
## Quick Fix (Try these in order)
|
||||
|
||||
### 1. Hard Refresh the Browser
|
||||
```
|
||||
Mac: Cmd + Shift + R
|
||||
Windows/Linux: Ctrl + Shift + R
|
||||
```
|
||||
|
||||
### 2. Clear Site Data and Refresh
|
||||
In Chrome/Brave:
|
||||
1. Open DevTools (F12)
|
||||
2. Right-click the refresh button
|
||||
3. Select "Empty Cache and Hard Reload"
|
||||
|
||||
### 3. Logout and Login Again
|
||||
1. Click logout in the UI
|
||||
2. Login again with: `password123`
|
||||
3. Navigate to Apps
|
||||
|
||||
### 4. Restart Everything
|
||||
```bash
|
||||
# Stop the current dev server (Ctrl+C)
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
npm run dev:mock
|
||||
```
|
||||
|
||||
Then visit: http://localhost:8100
|
||||
|
||||
## What to Expect
|
||||
|
||||
After refresh, you should see in the Apps page:
|
||||
- **Bitcoin Core** (running)
|
||||
- **Core Lightning** (stopped)
|
||||
- **A to B Bitcoin** (running) ← NEW!
|
||||
|
||||
The ATOB card will have:
|
||||
- Blue ATOB icon
|
||||
- "A to B Bitcoin" title
|
||||
- "running" green badge
|
||||
- **"Launch"** button (gradient blue)
|
||||
- Stop button
|
||||
|
||||
## Verification
|
||||
|
||||
Check browser console (F12):
|
||||
```javascript
|
||||
// Should show the WebSocket data includes atob
|
||||
```
|
||||
|
||||
Check Network tab:
|
||||
- WebSocket connection to `ws://localhost:5959/ws/db`
|
||||
- Should receive data with package-data containing atob
|
||||
|
||||
## Still Not Working?
|
||||
|
||||
Check if the WebSocket sent the data:
|
||||
1. Open DevTools (F12)
|
||||
2. Go to Network tab
|
||||
3. Filter by "WS" (WebSocket)
|
||||
4. Click on the WebSocket connection
|
||||
5. Look at Messages
|
||||
6. You should see initial data with atob in package-data
|
||||
|
||||
@@ -1,429 +0,0 @@
|
||||
# Neode UI Troubleshooting
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### 1. "Method not found: marketplace.get"
|
||||
|
||||
**Cause**: The backend isn't running or doesn't have the marketplace API enabled.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
#### Option A: Start the Backend
|
||||
```bash
|
||||
# Ensure the Neode backend is running
|
||||
cd /Users/tx1138/Code/Neode
|
||||
# Start your backend service (adjust command as needed)
|
||||
```
|
||||
|
||||
#### Option B: Check Backend Status
|
||||
```bash
|
||||
# Test if backend is accessible
|
||||
curl -X POST http://localhost:5959/rpc/v1 \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"method":"echo","params":{"message":"test"}}'
|
||||
|
||||
# Should return: {"result":"test"}
|
||||
```
|
||||
|
||||
#### Option C: Use Mock Mode (Development Only)
|
||||
Enable mock mode in `src/views/Marketplace.vue` by uncommenting the mock data section (see below).
|
||||
|
||||
---
|
||||
|
||||
### 2. WebSocket Error: "Cannot read properties of undefined (reading 'length')"
|
||||
|
||||
**Cause**: WebSocket patch data is malformed or empty.
|
||||
|
||||
**Fixed**: This is now handled gracefully. The app will log a warning but continue working.
|
||||
|
||||
**What Changed**:
|
||||
- Added validation in `src/api/websocket.ts` → `applyDataPatch()`
|
||||
- Added try/catch in `src/stores/app.ts` → `connectWebSocket()`
|
||||
|
||||
If you still see this error:
|
||||
1. Check browser console for details
|
||||
2. Verify backend is sending correct patch format
|
||||
3. The app will continue working without real-time updates
|
||||
|
||||
---
|
||||
|
||||
### 3. "Please login first to access the marketplace"
|
||||
|
||||
**Cause**: You're not authenticated yet.
|
||||
|
||||
**Solution**:
|
||||
1. Navigate to `/login`
|
||||
2. Enter your password
|
||||
3. Return to marketplace
|
||||
|
||||
**Check Auth Status**:
|
||||
```javascript
|
||||
// In browser console
|
||||
const store = useAppStore()
|
||||
console.log('Authenticated:', store.isAuthenticated)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. "Cannot connect to backend"
|
||||
|
||||
**Cause**: Backend isn't running or proxy isn't configured.
|
||||
|
||||
**Solutions**:
|
||||
|
||||
#### Check Vite Proxy
|
||||
Verify `vite.config.ts` has:
|
||||
```typescript
|
||||
proxy: {
|
||||
'/rpc/v1': 'http://localhost:5959',
|
||||
'/ws/db': {
|
||||
target: 'ws://localhost:5959',
|
||||
ws: true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Test Backend Connection
|
||||
```bash
|
||||
# Check if backend is listening
|
||||
lsof -i :5959
|
||||
|
||||
# Or use netstat
|
||||
netstat -an | grep 5959
|
||||
```
|
||||
|
||||
#### Start Backend Manually
|
||||
```bash
|
||||
cd /Users/tx1138/Code/Neode/core
|
||||
cargo run --release --bin startos
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Development Without Backend
|
||||
|
||||
### Enable Mock Mode
|
||||
|
||||
If you want to develop the UI without a running backend, you can enable mock mode:
|
||||
|
||||
**Edit `src/views/Marketplace.vue`**:
|
||||
|
||||
```typescript
|
||||
async function loadMarketplace() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
apps.value = []
|
||||
|
||||
// MOCK MODE - Comment out in production
|
||||
const MOCK_MODE = true // Set to false when backend is available
|
||||
|
||||
if (MOCK_MODE) {
|
||||
// Simulate loading delay
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
|
||||
apps.value = [
|
||||
{
|
||||
id: 'bitcoin',
|
||||
title: 'Bitcoin Core',
|
||||
description: 'A full Bitcoin node implementation. Store, validate, and relay blocks and transactions.',
|
||||
version: '25.0.0',
|
||||
icon: '/assets/img/bitcoin.png',
|
||||
},
|
||||
{
|
||||
id: 'lightning',
|
||||
title: 'Lightning Network',
|
||||
description: 'Lightning Network implementation for fast, low-cost Bitcoin payments.',
|
||||
version: '0.17.0',
|
||||
icon: '/assets/img/lightning.png',
|
||||
},
|
||||
{
|
||||
id: 'nextcloud',
|
||||
title: 'Nextcloud',
|
||||
description: 'Self-hosted file sync and sharing platform.',
|
||||
version: '27.1.0',
|
||||
icon: '/assets/img/nextcloud.png',
|
||||
},
|
||||
]
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
// END MOCK MODE
|
||||
|
||||
// Real implementation continues...
|
||||
try {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
### Enable Verbose Logging
|
||||
|
||||
**Add to `src/api/rpc-client.ts`**:
|
||||
```typescript
|
||||
async call<T>(options: RPCOptions): Promise<T> {
|
||||
console.log('🔵 RPC Request:', options) // Add this
|
||||
|
||||
const response = await fetch(this.baseUrl, {
|
||||
// ...
|
||||
})
|
||||
|
||||
const data = await response.json()
|
||||
console.log('🟢 RPC Response:', data) // Add this
|
||||
|
||||
return data.result as T
|
||||
}
|
||||
```
|
||||
|
||||
### Check WebSocket Status
|
||||
|
||||
In browser console:
|
||||
```javascript
|
||||
// Check WebSocket connection
|
||||
const store = useAppStore()
|
||||
console.log('Connected:', store.isConnected)
|
||||
console.log('Data:', store.data)
|
||||
```
|
||||
|
||||
### Monitor Network Traffic
|
||||
|
||||
1. Open Chrome DevTools (F12)
|
||||
2. Go to Network tab
|
||||
3. Filter by "WS" for WebSocket
|
||||
4. Filter by "XHR" for RPC calls
|
||||
5. Inspect request/response payloads
|
||||
|
||||
---
|
||||
|
||||
## Backend Build Issues
|
||||
|
||||
### Build Rust Backend
|
||||
|
||||
```bash
|
||||
cd /Users/tx1138/Code/Neode/core
|
||||
cargo build --release
|
||||
|
||||
# Binary will be at:
|
||||
# target/release/startos
|
||||
```
|
||||
|
||||
### Run Backend for Development
|
||||
|
||||
```bash
|
||||
# Run with debug logging
|
||||
RUST_LOG=debug ./target/release/startos
|
||||
|
||||
# Or use cargo run
|
||||
RUST_LOG=debug cargo run --release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Port Conflicts
|
||||
|
||||
### Check What's Using Port 5959
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
lsof -i :5959
|
||||
|
||||
# Kill process if needed
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
### Check What's Using Port 8100 (Vite)
|
||||
|
||||
```bash
|
||||
lsof -i :8100
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentication Issues
|
||||
|
||||
### Clear Session Cookies
|
||||
|
||||
In browser DevTools:
|
||||
1. Application tab → Cookies
|
||||
2. Delete all cookies for `localhost:8100`
|
||||
3. Refresh page and login again
|
||||
|
||||
### Check Auth Cookie
|
||||
|
||||
In browser console:
|
||||
```javascript
|
||||
// Check if auth cookie exists
|
||||
document.cookie
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WebSocket Connection Fails
|
||||
|
||||
### Common Causes
|
||||
|
||||
1. **Backend not running**: Start backend on port 5959
|
||||
2. **CORS issues**: Vite proxy should handle this
|
||||
3. **Port mismatch**: Check backend is listening on 5959
|
||||
|
||||
### Debug WebSocket
|
||||
|
||||
**Add to `src/api/websocket.ts`**:
|
||||
```typescript
|
||||
this.ws.onopen = () => {
|
||||
console.log('✅ WebSocket connected')
|
||||
this.reconnectAttempts = 0
|
||||
resolve()
|
||||
}
|
||||
|
||||
this.ws.onerror = (error) => {
|
||||
console.error('❌ WebSocket error:', error)
|
||||
reject(error)
|
||||
}
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
console.log('📨 WebSocket message:', event.data) // Add this
|
||||
try {
|
||||
const update = JSON.parse(event.data)
|
||||
this.callbacks.forEach((callback) => callback(update))
|
||||
} catch (error) {
|
||||
console.error('Failed to parse message:', error)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build Errors
|
||||
|
||||
### TypeScript Errors
|
||||
|
||||
```bash
|
||||
# Type check without building
|
||||
npm run type-check
|
||||
|
||||
# Fix common issues
|
||||
npm install
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install
|
||||
```
|
||||
|
||||
### Vite Build Fails
|
||||
|
||||
```bash
|
||||
# Clear cache and rebuild
|
||||
rm -rf dist node_modules/.vite
|
||||
npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Slow Hot Reload
|
||||
|
||||
1. **Check file watchers**:
|
||||
```bash
|
||||
# macOS - increase file watcher limit
|
||||
ulimit -n 4096
|
||||
```
|
||||
|
||||
2. **Clear Vite cache**:
|
||||
```bash
|
||||
rm -rf node_modules/.vite
|
||||
```
|
||||
|
||||
### High Memory Usage
|
||||
|
||||
- Close other tabs/applications
|
||||
- Restart Vite dev server
|
||||
- Check for memory leaks in DevTools → Memory
|
||||
|
||||
---
|
||||
|
||||
## Still Having Issues?
|
||||
|
||||
### Collect Debug Information
|
||||
|
||||
1. **Browser Console Logs**:
|
||||
- Open DevTools → Console
|
||||
- Copy all errors
|
||||
|
||||
2. **Network Tab**:
|
||||
- Check failed requests
|
||||
- Copy request/response details
|
||||
|
||||
3. **Backend Logs**:
|
||||
```bash
|
||||
# If using systemd
|
||||
journalctl -u startos -f
|
||||
|
||||
# If running manually
|
||||
# Check terminal output
|
||||
```
|
||||
|
||||
4. **System Info**:
|
||||
```bash
|
||||
node --version
|
||||
npm --version
|
||||
cargo --version
|
||||
```
|
||||
|
||||
### Clean Restart
|
||||
|
||||
```bash
|
||||
# Stop everything
|
||||
# Kill Vite dev server (Ctrl+C)
|
||||
# Kill backend process
|
||||
|
||||
# Clean rebuild
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
rm -rf dist node_modules/.vite
|
||||
npm install
|
||||
npm run dev
|
||||
|
||||
# In another terminal, start backend
|
||||
cd /Users/tx1138/Code/Neode/core
|
||||
cargo run --release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Start Development
|
||||
|
||||
```bash
|
||||
# Terminal 1: UI
|
||||
cd /Users/tx1138/Code/Neode/neode-ui
|
||||
npm run dev
|
||||
|
||||
# Terminal 2: Backend (if available)
|
||||
cd /Users/tx1138/Code/Neode/core
|
||||
cargo run --release
|
||||
```
|
||||
|
||||
### Check Service Status
|
||||
|
||||
```bash
|
||||
# Check if UI is running
|
||||
curl http://localhost:8100
|
||||
|
||||
# Check if backend is running
|
||||
curl http://localhost:5959/rpc/v1 -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"method":"echo","params":{"message":"test"}}'
|
||||
```
|
||||
|
||||
### Reset Everything
|
||||
|
||||
```bash
|
||||
# Clean all state
|
||||
rm -rf neode-ui/dist neode-ui/node_modules/.vite
|
||||
cd neode-ui && npm install && npm run dev
|
||||
```
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
# Video Compression Guide for Web
|
||||
|
||||
## Current Video Status
|
||||
|
||||
- **File**: `public/assets/video/video-intro.mp4`
|
||||
- **Size**: ~1MB (optimized - 95%+ reduction from original 36MB)
|
||||
- **Usage**: Background video for Welcome Noderunner screen and onboarding/login
|
||||
- **Format**: MP4 (H.264 compatible)
|
||||
- **Last Updated**: December 26, 2025 (16:35)
|
||||
- **Optimization**: CRF 32, 1280x720, 30fps (web-optimized for fast loading)
|
||||
|
||||
## Recommended Compression
|
||||
|
||||
### Target File Size
|
||||
- **Ideal**: 2-5MB for web
|
||||
- **Maximum**: 8MB (still acceptable but slower loading)
|
||||
- **Current**: 16MB (too large, needs compression)
|
||||
|
||||
### Recommended Settings
|
||||
|
||||
#### Using FFmpeg (Recommended)
|
||||
|
||||
```bash
|
||||
# Install FFmpeg (if not installed)
|
||||
# macOS: brew install ffmpeg
|
||||
# Linux: sudo apt install ffmpeg
|
||||
# Windows: Download from https://ffmpeg.org/download.html
|
||||
|
||||
# Compress video with H.264 codec (best browser compatibility)
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 \
|
||||
-preset slow \
|
||||
-crf 28 \
|
||||
-c:a aac \
|
||||
-b:a 128k \
|
||||
-movflags +faststart \
|
||||
-vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" \
|
||||
video-intro-compressed.mp4
|
||||
|
||||
# For even smaller file (more aggressive compression)
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 \
|
||||
-preset slow \
|
||||
-crf 32 \
|
||||
-c:a aac \
|
||||
-b:a 96k \
|
||||
-movflags +faststart \
|
||||
-vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" \
|
||||
video-intro-compressed-small.mp4
|
||||
```
|
||||
|
||||
#### Using HandBrake (GUI Tool)
|
||||
|
||||
1. Download HandBrake: https://handbrake.fr/
|
||||
2. Open your video file
|
||||
3. Preset: **Web/Google Gmail Large 3 Minutes 720p30**
|
||||
4. Adjust settings:
|
||||
- **Video Codec**: H.264 (x264)
|
||||
- **Framerate**: Same as source (or 30fps)
|
||||
- **Quality**: RF 28-32 (higher = smaller file, lower quality)
|
||||
- **Resolution**: 1920x1080 or 1280x720
|
||||
- **Audio**: AAC, 128kbps or 96kbps
|
||||
5. Check "Web Optimized" checkbox
|
||||
6. Start encoding
|
||||
|
||||
#### Using Online Tools
|
||||
|
||||
- **CloudConvert**: https://cloudconvert.com/mp4-compressor
|
||||
- **FreeConvert**: https://www.freeconvert.com/video-compressor
|
||||
- **Clideo**: https://clideo.com/compress-video
|
||||
|
||||
## FFmpeg Parameter Explanation
|
||||
|
||||
- `-c:v libx264`: Use H.264 video codec (best browser support)
|
||||
- `-preset slow`: Better compression (slower encoding, smaller file)
|
||||
- `-crf 28`: Quality setting (18-28 = high quality, 28-32 = medium, 32+ = lower)
|
||||
- `-c:a aac`: Audio codec
|
||||
- `-b:a 128k`: Audio bitrate (96k-128k is fine for background music)
|
||||
- `-movflags +faststart`: Enables progressive download (starts playing before fully downloaded)
|
||||
- `-vf scale=...`: Resize video (1920x1080 or 1280x720 recommended)
|
||||
- `pad=...`: Add black bars if aspect ratio doesn't match
|
||||
|
||||
## Recommended Settings by Use Case
|
||||
|
||||
### High Quality (5-8MB)
|
||||
```bash
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 -preset slow -crf 24 \
|
||||
-c:a aac -b:a 128k \
|
||||
-movflags +faststart \
|
||||
-vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" \
|
||||
video-intro-hq.mp4
|
||||
```
|
||||
|
||||
### Balanced (2-4MB) - **RECOMMENDED**
|
||||
```bash
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 -preset slow -crf 28 \
|
||||
-c:a aac -b:a 96k \
|
||||
-movflags +faststart \
|
||||
-vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" \
|
||||
video-intro-balanced.mp4
|
||||
```
|
||||
|
||||
### Small File (1-2MB)
|
||||
```bash
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 -preset slow -crf 32 \
|
||||
-c:a aac -b:a 64k \
|
||||
-movflags +faststart \
|
||||
-vf "scale=854:480:force_original_aspect_ratio=decrease,pad=854:480:(ow-iw)/2:(oh-ih)/2" \
|
||||
video-intro-small.mp4
|
||||
```
|
||||
|
||||
## Additional Optimizations
|
||||
|
||||
### 1. Trim Video Length
|
||||
If the video is longer than needed, trim it:
|
||||
```bash
|
||||
# Trim to first 30 seconds
|
||||
ffmpeg -i video-intro.mp4 -t 30 -c copy video-intro-trimmed.mp4
|
||||
```
|
||||
|
||||
### 2. Reduce Frame Rate
|
||||
If original is 60fps, reduce to 30fps:
|
||||
```bash
|
||||
ffmpeg -i video-intro.mp4 -r 30 -c:v libx264 -crf 28 video-intro-30fps.mp4
|
||||
```
|
||||
|
||||
### 3. Create Multiple Formats (Optional)
|
||||
For better browser support, create WebM version:
|
||||
```bash
|
||||
# WebM version (often smaller, but less browser support)
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libvpx-vp9 -crf 30 -b:v 0 \
|
||||
-c:a libopus -b:a 96k \
|
||||
video-intro.webm
|
||||
```
|
||||
|
||||
Then update HTML:
|
||||
```html
|
||||
<video>
|
||||
<source src="/assets/video/video-intro.webm" type="video/webm">
|
||||
<source src="/assets/video/video-intro.mp4" type="video/mp4">
|
||||
</video>
|
||||
```
|
||||
|
||||
## Testing Compression
|
||||
|
||||
After compression, test:
|
||||
1. **File size**: Should be 2-5MB
|
||||
2. **Visual quality**: Check on different screen sizes
|
||||
3. **Loading speed**: Test on slow connections
|
||||
4. **Playback**: Ensure smooth playback and looping
|
||||
|
||||
## Quick Command (Copy-Paste Ready)
|
||||
|
||||
```bash
|
||||
# Navigate to video directory
|
||||
cd neode-ui/public/assets/video
|
||||
|
||||
# Backup original
|
||||
cp video-intro.mp4 video-intro-original.mp4
|
||||
|
||||
# Compress (balanced quality, ~2-4MB)
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 -preset slow -crf 28 \
|
||||
-c:a aac -b:a 96k \
|
||||
-movflags +faststart \
|
||||
-vf "scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" \
|
||||
video-intro-compressed.mp4
|
||||
|
||||
# Replace original (after testing)
|
||||
mv video-intro-compressed.mp4 video-intro.mp4
|
||||
```
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
- **MP4 (H.264)**: Works in all modern browsers
|
||||
- **WebM (VP9)**: Works in Chrome, Firefox, Edge (not Safari)
|
||||
- **Recommendation**: Use MP4 for maximum compatibility
|
||||
|
||||
## Performance Tips
|
||||
|
||||
1. **Preload**: Add `preload="metadata"` to video tag (loads only metadata, not full video)
|
||||
2. **Poster Image**: Add `poster="/assets/img/bg-intro.jpg"` for instant display while loading
|
||||
3. **Lazy Load**: Consider loading video only when user reaches that screen
|
||||
4. **CDN**: Host video on CDN for faster delivery
|
||||
|
||||
## Current Implementation
|
||||
|
||||
The video is currently set to:
|
||||
- `autoplay`: Starts automatically
|
||||
- `loop`: Repeats continuously
|
||||
- `muted`: Required for autoplay in most browsers
|
||||
- `playsinline`: Prevents fullscreen on mobile
|
||||
|
||||
These settings are optimal for background video use.
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
# Video Optimization Guide - Quality Preservation
|
||||
|
||||
## Current Video
|
||||
|
||||
- **File**: `public/assets/video/video-intro.mp4`
|
||||
- **Size**: ~36MB
|
||||
- **Goal**: Optimize for web without losing quality
|
||||
|
||||
## Quick Optimization
|
||||
|
||||
Run the optimization script:
|
||||
|
||||
```bash
|
||||
cd neode-ui
|
||||
./optimize-video.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
1. Create a backup of the original video
|
||||
2. Optimize using H.264 with CRF 18 (near-lossless)
|
||||
3. Add faststart flag for web streaming
|
||||
4. Replace the original with the optimized version
|
||||
|
||||
## Manual Optimization
|
||||
|
||||
If you prefer to optimize manually:
|
||||
|
||||
### Option 1: Near-Lossless (Recommended)
|
||||
|
||||
```bash
|
||||
cd neode-ui/public/assets/video
|
||||
|
||||
# Backup original
|
||||
cp video-intro.mp4 video-intro-backup.mp4
|
||||
|
||||
# Optimize with CRF 18 (near-lossless, best quality)
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 \
|
||||
-preset slow \
|
||||
-crf 18 \
|
||||
-profile:v high \
|
||||
-level 4.0 \
|
||||
-pix_fmt yuv420p \
|
||||
-c:a aac \
|
||||
-b:a 128k \
|
||||
-ar 48000 \
|
||||
-movflags +faststart \
|
||||
-threads 0 \
|
||||
video-intro-optimized.mp4
|
||||
|
||||
# Replace original
|
||||
mv video-intro-optimized.mp4 video-intro.mp4
|
||||
```
|
||||
|
||||
**Expected result**: 15-25MB (40-60% reduction) with visually identical quality
|
||||
|
||||
### Option 2: Lossless (Maximum Quality)
|
||||
|
||||
```bash
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 \
|
||||
-preset veryslow \
|
||||
-crf 15 \
|
||||
-profile:v high \
|
||||
-level 4.0 \
|
||||
-pix_fmt yuv420p \
|
||||
-c:a aac \
|
||||
-b:a 192k \
|
||||
-ar 48000 \
|
||||
-movflags +faststart \
|
||||
video-intro-lossless.mp4
|
||||
```
|
||||
|
||||
**Expected result**: 20-30MB (20-40% reduction) with imperceptible quality loss
|
||||
|
||||
### Option 3: H.265/HEVC (Better Compression, Modern Browsers)
|
||||
|
||||
```bash
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx265 \
|
||||
-preset slow \
|
||||
-crf 20 \
|
||||
-pix_fmt yuv420p \
|
||||
-c:a aac \
|
||||
-b:a 128k \
|
||||
-movflags +faststart \
|
||||
video-intro-hevc.mp4
|
||||
```
|
||||
|
||||
**Note**: H.265 provides better compression but has limited browser support (Safari, Edge, Chrome on Android). Use H.264 for maximum compatibility.
|
||||
|
||||
## Parameter Explanation
|
||||
|
||||
### CRF (Constant Rate Factor)
|
||||
- **CRF 15**: Visually lossless (largest file)
|
||||
- **CRF 18**: Near-lossless (recommended, good balance)
|
||||
- **CRF 20**: High quality (smaller file, still excellent)
|
||||
- **CRF 23**: Good quality (noticeable but acceptable)
|
||||
- **Lower = Better Quality, Larger File**
|
||||
|
||||
### Preset
|
||||
- **veryslow**: Best compression, slowest encoding
|
||||
- **slow**: Best balance (recommended)
|
||||
- **medium**: Faster encoding, larger file
|
||||
- **fast**: Quick encoding, largest file
|
||||
|
||||
### Faststart Flag
|
||||
- `-movflags +faststart`: Moves metadata to beginning of file
|
||||
- Enables progressive download (video starts playing before fully downloaded)
|
||||
- **Essential for web video**
|
||||
|
||||
### Profile & Level
|
||||
- `-profile:v high`: Enables advanced H.264 features
|
||||
- `-level 4.0`: Maximum compatibility with modern devices
|
||||
- Ensures video plays on all browsers/devices
|
||||
|
||||
## Quality Comparison
|
||||
|
||||
| CRF | Quality | File Size | Use Case |
|
||||
|-----|---------|-----------|----------|
|
||||
| 15 | Lossless | ~30MB | Maximum quality needed |
|
||||
| 18 | Near-lossless | ~20MB | **Recommended** - Best balance |
|
||||
| 20 | High | ~15MB | Good quality, smaller file |
|
||||
| 23 | Good | ~10MB | Acceptable quality |
|
||||
|
||||
## Expected Results
|
||||
|
||||
With CRF 18 (recommended):
|
||||
- **Original**: 36MB
|
||||
- **Optimized**: ~18-22MB (40-50% reduction)
|
||||
- **Quality**: Visually identical to original
|
||||
- **Loading**: 2-3x faster on slower connections
|
||||
|
||||
## Testing
|
||||
|
||||
After optimization:
|
||||
|
||||
1. **Visual comparison**: Compare original and optimized side-by-side
|
||||
2. **File size**: Check reduction percentage
|
||||
3. **Loading speed**: Test on slow connection (Chrome DevTools → Network → Throttling)
|
||||
4. **Playback**: Verify smooth playback and looping
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
- **H.264 (MP4)**: Works in all modern browsers ✅
|
||||
- **H.265 (HEVC)**: Safari, Edge, Chrome Android ✅ (Chrome Desktop ❌)
|
||||
- **VP9 (WebM)**: Chrome, Firefox, Edge ✅ (Safari ❌)
|
||||
|
||||
**Recommendation**: Use H.264 for maximum compatibility.
|
||||
|
||||
## Advanced: Two-Pass Encoding
|
||||
|
||||
For even better quality at same file size:
|
||||
|
||||
```bash
|
||||
# First pass
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 \
|
||||
-preset slow \
|
||||
-crf 18 \
|
||||
-profile:v high \
|
||||
-level 4.0 \
|
||||
-pix_fmt yuv420p \
|
||||
-pass 1 \
|
||||
-an \
|
||||
-f null \
|
||||
/dev/null
|
||||
|
||||
# Second pass
|
||||
ffmpeg -i video-intro.mp4 \
|
||||
-c:v libx264 \
|
||||
-preset slow \
|
||||
-crf 18 \
|
||||
-profile:v high \
|
||||
-level 4.0 \
|
||||
-pix_fmt yuv420p \
|
||||
-c:a aac \
|
||||
-b:a 128k \
|
||||
-ar 48000 \
|
||||
-movflags +faststart \
|
||||
-pass 2 \
|
||||
video-intro-optimized.mp4
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "FFmpeg not found"
|
||||
```bash
|
||||
# macOS
|
||||
brew install ffmpeg
|
||||
|
||||
# Linux
|
||||
sudo apt install ffmpeg
|
||||
|
||||
# Windows
|
||||
# Download from https://ffmpeg.org/download.html
|
||||
```
|
||||
|
||||
### "Permission denied"
|
||||
```bash
|
||||
chmod +x optimize-video.sh
|
||||
```
|
||||
|
||||
### Video looks pixelated
|
||||
- Use lower CRF (15-18 instead of 20+)
|
||||
- Use slower preset (slow instead of medium)
|
||||
|
||||
### File size still too large
|
||||
- Use CRF 20-23 (slight quality trade-off)
|
||||
- Reduce resolution if video is 4K (scale to 1920x1080)
|
||||
- Reduce frame rate if 60fps (reduce to 30fps)
|
||||
|
||||
## After Optimization
|
||||
|
||||
1. **Update cache-busting version** in code:
|
||||
- `SplashScreen.vue`: Change `?v=3` to `?v=4`
|
||||
- `OnboardingWrapper.vue`: Change `?v=3` to `?v=4`
|
||||
|
||||
2. **Test the video**:
|
||||
- Check playback smoothness
|
||||
- Verify looping works
|
||||
- Test on mobile devices
|
||||
|
||||
3. **Update documentation**:
|
||||
- Update `VIDEO_COMPRESSION_GUIDE.md` with new file size
|
||||
|
||||
@@ -82,7 +82,7 @@ define(['./workbox-21a80088'], (function (workbox) { 'use strict';
|
||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||
}, {
|
||||
"url": "index.html",
|
||||
"revision": "0.2lte02eatlc"
|
||||
"revision": "0.qj72pfa74qs"
|
||||
}], {});
|
||||
workbox.cleanupOutdatedCaches();
|
||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||
|
||||
@@ -159,7 +159,8 @@ const portMappings = {
|
||||
'portainer': 9443,
|
||||
'uptime-kuma': 3001,
|
||||
'tailscale': 41641,
|
||||
'fedimint': 8174,
|
||||
'fedimint': 8175,
|
||||
'thunderhub': 3010,
|
||||
'nostr-rs-relay': 7000,
|
||||
'syncthing': 8384,
|
||||
'penpot': 9001,
|
||||
@@ -388,6 +389,7 @@ const marketplaceMetadata = {
|
||||
'dwn': { title: 'Decentralized Web Node', shortDesc: 'Store and sync personal data with DID-based access', icon: '/assets/img/app-icons/dwn.svg' },
|
||||
'nostr-rs-relay': { title: 'Nostr Relay', shortDesc: 'Run your own Nostr relay', icon: '/assets/img/app-icons/nostr-rs-relay.svg' },
|
||||
'syncthing': { title: 'Syncthing', shortDesc: 'Peer-to-peer file synchronization', icon: '/assets/img/app-icons/syncthing.png' },
|
||||
'thunderhub': { title: 'ThunderHub', shortDesc: 'Lightning node management UI with channel management and payments', icon: '/assets/img/app-icons/thunderhub.svg' },
|
||||
'tor': { title: 'Tor', shortDesc: 'Anonymous communication over the Tor network', icon: '/assets/img/app-icons/tor.png' },
|
||||
'amin': { title: 'Amin', shortDesc: 'Administrative interface for Archipelago', icon: '/assets/icon/pwa-192x192-v2.png' },
|
||||
}
|
||||
@@ -723,6 +725,24 @@ const staticDevApps = {
|
||||
lanPort: 8083,
|
||||
icon: '/assets/img/app-icons/file-browser.webp',
|
||||
}),
|
||||
thunderhub: staticApp({
|
||||
id: 'thunderhub',
|
||||
title: 'ThunderHub',
|
||||
version: '0.13.31',
|
||||
shortDesc: 'Lightning node management UI',
|
||||
longDesc: 'Full Lightning node management — channels, payments, routing fees, and node health. Connect to your LND and manage everything from one dashboard.',
|
||||
state: 'running',
|
||||
lanPort: 3010,
|
||||
}),
|
||||
fedimint: staticApp({
|
||||
id: 'fedimint',
|
||||
title: 'Fedimint',
|
||||
version: '0.10.0',
|
||||
shortDesc: 'Federated Bitcoin mint',
|
||||
longDesc: 'Federated Chaumian e-cash mint with Guardian UI. Community custody, private payments, and Lightning gateways.',
|
||||
state: 'running',
|
||||
lanPort: 8175,
|
||||
}),
|
||||
}
|
||||
|
||||
function mergePackageData(dockerApps) {
|
||||
@@ -997,12 +1017,22 @@ app.post('/rpc/v1', (req, res) => {
|
||||
id: 'fedimint',
|
||||
title: 'Fedimint',
|
||||
description: 'Federated Chaumian e-cash mint — community custody, private payments, and Lightning gateways for your tribe.',
|
||||
version: '0.4.3',
|
||||
version: '0.10.0',
|
||||
icon: '/assets/img/app-icons/fedimint.png',
|
||||
author: 'Fedimint',
|
||||
license: 'MIT',
|
||||
category: 'Bitcoin',
|
||||
},
|
||||
{
|
||||
id: 'thunderhub',
|
||||
title: 'ThunderHub',
|
||||
description: 'Lightning node management UI — manage channels, send and receive payments, view routing fees, and monitor your node health.',
|
||||
version: '0.13.31',
|
||||
icon: '/assets/img/app-icons/thunderhub.svg',
|
||||
author: 'Anthony Potdevin',
|
||||
license: 'MIT',
|
||||
category: 'Bitcoin',
|
||||
},
|
||||
{
|
||||
id: 'vaultwarden',
|
||||
title: 'Vaultwarden',
|
||||
@@ -1305,7 +1335,50 @@ app.post('/rpc/v1', (req, res) => {
|
||||
}
|
||||
|
||||
case 'node.notifications': {
|
||||
return res.json({ result: [] })
|
||||
return res.json({
|
||||
result: [
|
||||
{
|
||||
id: 'notif-1',
|
||||
type: 'info',
|
||||
title: 'Bitcoin Core synced',
|
||||
message: 'Blockchain fully synced at block 892,451. IBD complete.',
|
||||
timestamp: new Date(Date.now() - 1800000).toISOString(),
|
||||
read: false,
|
||||
},
|
||||
{
|
||||
id: 'notif-2',
|
||||
type: 'success',
|
||||
title: 'LND channel opened',
|
||||
message: 'Channel opened with ACINQ (500,000 sats capacity). 6 confirmations received.',
|
||||
timestamp: new Date(Date.now() - 7200000).toISOString(),
|
||||
read: false,
|
||||
},
|
||||
{
|
||||
id: 'notif-3',
|
||||
type: 'warning',
|
||||
title: 'Disk usage above 80%',
|
||||
message: 'Storage at 82% capacity. Consider pruning old blockchain data or expanding storage.',
|
||||
timestamp: new Date(Date.now() - 14400000).toISOString(),
|
||||
read: true,
|
||||
},
|
||||
{
|
||||
id: 'notif-4',
|
||||
type: 'info',
|
||||
title: 'System update available',
|
||||
message: 'Archipelago v0.1.1 is available. Review changelog before updating.',
|
||||
timestamp: new Date(Date.now() - 86400000).toISOString(),
|
||||
read: true,
|
||||
},
|
||||
{
|
||||
id: 'notif-5',
|
||||
type: 'success',
|
||||
title: 'Fedimint guardian connected',
|
||||
message: 'Guardian consensus achieved. Mint is operational with 3/4 guardians online.',
|
||||
timestamp: new Date(Date.now() - 43200000).toISOString(),
|
||||
read: false,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
"build:production": "NODE_ENV=production vue-tsc -b && vite build --mode production",
|
||||
"preview": "vite preview",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"prebuild": "cp ../../loop-start.mp3 public/assets/audio/ 2>/dev/null || true",
|
||||
"generate-pwa-icons": "pwa-assets-generator --preset minimal-2023 public/assets/icon/favico-black.svg && cp public/assets/icon/favicon.ico public/favicon.ico",
|
||||
"generate-welcome-speech": "node scripts/generate-welcome-speech.js"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
|
||||
<circle cx="32" cy="32" r="30" fill="#1a1a2e" stroke="#f7931a" stroke-width="2"/>
|
||||
<path d="M36 12L22 34h10l-4 18 18-24H34l2-16z" fill="#f7931a"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 230 B |
Binary file not shown.
Binary file not shown.
@@ -54,6 +54,7 @@ const PORT_TO_APP_ID: Record<string, string> = {
|
||||
'18081': 'nostr-rs-relay',
|
||||
'7777': 'indeedhub',
|
||||
'50002': 'electrumx',
|
||||
'3010': 'thunderhub',
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -139,8 +139,11 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-white mb-2">This site blocks embedded viewing</h3>
|
||||
<p class="text-white/50 text-sm mb-6">{{ appTitle }} sets security headers that prevent iframe embedding.<br>Open it in a new browser tab instead.</p>
|
||||
<h3 class="text-lg font-semibold text-white mb-2">{{ mustOpenNewTab ? 'This app opens in a new tab' : 'App not reachable' }}</h3>
|
||||
<p class="text-white/50 text-sm mb-6">
|
||||
<template v-if="mustOpenNewTab">{{ appTitle }} sets security headers that prevent iframe embedding.<br>Open it in a new browser tab instead.</template>
|
||||
<template v-else>{{ appTitle }} may still be starting up or the container is stopped.<br>Try opening in a new tab or check the app status.</template>
|
||||
</p>
|
||||
<button
|
||||
@click="openNewTabAndBack"
|
||||
class="glass-button px-6 py-3 rounded-lg text-sm font-semibold inline-flex items-center gap-2"
|
||||
|
||||
@@ -146,9 +146,10 @@
|
||||
v-if="canLaunch(pkg)"
|
||||
data-controller-launch-btn
|
||||
@click.stop="launchApp(id as string)"
|
||||
class="flex-1 px-4 py-2 glass-button glass-button-sm rounded-lg text-sm font-medium"
|
||||
class="flex-1 px-4 py-2 glass-button glass-button-sm rounded-lg text-sm font-medium flex items-center justify-center gap-1.5"
|
||||
>
|
||||
{{ t('common.launch') }}
|
||||
<svg v-if="opensInTab(id as string)" class="w-3.5 h-3.5 opacity-60" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /></svg>
|
||||
</button>
|
||||
<button
|
||||
v-if="!isWebOnlyApp(id as string) && (pkg.state === 'stopped' || pkg.state === 'exited')"
|
||||
@@ -490,6 +491,17 @@ function canLaunch(pkg: PackageDataEntry): boolean {
|
||||
return !!hasUI && canLaunchState
|
||||
}
|
||||
|
||||
/** Apps that open in a new browser tab (X-Frame-Options blocks iframe) */
|
||||
const TAB_LAUNCH_APPS = new Set([
|
||||
'btcpay-server', 'grafana', 'photoprism', 'homeassistant',
|
||||
'vaultwarden', 'nextcloud', 'uptime-kuma', 'portainer',
|
||||
'onlyoffice', 'nginx-proxy-manager',
|
||||
])
|
||||
|
||||
function opensInTab(id: string): boolean {
|
||||
return TAB_LAUNCH_APPS.has(id)
|
||||
}
|
||||
|
||||
function launchApp(id: string) {
|
||||
useAppLauncherStore().openSession(id)
|
||||
}
|
||||
|
||||
@@ -735,7 +735,7 @@ async function loadCommunityMarketplace() {
|
||||
// Get app tier classification (matches backend get_app_tier)
|
||||
function getAppTier(appId: string): string {
|
||||
const core = ['bitcoin-knots', 'bitcoin', 'lnd', 'mempool', 'btcpay-server', 'dwn', 'filebrowser']
|
||||
const recommended = ['fedimint', 'vaultwarden', 'uptime-kuma', 'grafana', 'searxng', 'tailscale', 'portainer']
|
||||
const recommended = ['fedimint', 'thunderhub', 'vaultwarden', 'uptime-kuma', 'grafana', 'searxng', 'tailscale', 'portainer']
|
||||
if (core.includes(appId)) return 'core'
|
||||
if (recommended.includes(appId)) return 'recommended'
|
||||
return 'optional'
|
||||
@@ -777,6 +777,17 @@ function getCuratedAppList() {
|
||||
manifestUrl: undefined,
|
||||
repoUrl: 'https://github.com/lightningnetwork/lnd'
|
||||
},
|
||||
{
|
||||
id: 'thunderhub',
|
||||
title: 'ThunderHub',
|
||||
version: '0.13.31',
|
||||
description: 'Lightning node management UI. Manage channels, send and receive payments, view routing fees, and monitor your Lightning node.',
|
||||
icon: '/assets/img/app-icons/thunderhub.svg',
|
||||
author: 'Anthony Potdevin',
|
||||
dockerImage: 'docker.io/apotdevin/thunderhub:v0.13.31',
|
||||
manifestUrl: undefined,
|
||||
repoUrl: 'https://github.com/apotdevin/thunderhub'
|
||||
},
|
||||
{
|
||||
id: 'mempool',
|
||||
title: 'Mempool Explorer',
|
||||
|
||||
+2
-38
@@ -69,46 +69,10 @@ kill_port 5173 # AIUI dev server
|
||||
echo -e "${GREEN}✅ Ports cleared${NC}"
|
||||
echo ""
|
||||
|
||||
# Check and start Docker Desktop if needed
|
||||
echo -e "${BLUE}🐳 Checking Docker...${NC}"
|
||||
if ! /usr/local/bin/docker ps > /dev/null 2>&1; then
|
||||
echo -e "${YELLOW} Docker Desktop not running, starting it...${NC}"
|
||||
open -a Docker
|
||||
|
||||
# Wait for Docker to be ready
|
||||
echo -e "${BLUE} Waiting for Docker to start...${NC}"
|
||||
max_wait=60
|
||||
waited=0
|
||||
while ! /usr/local/bin/docker ps > /dev/null 2>&1; do
|
||||
if [ $waited -ge $max_wait ]; then
|
||||
echo -e "${YELLOW} ⚠️ Docker took too long to start. Apps will be simulated.${NC}"
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
waited=$((waited + 2))
|
||||
echo -e "${BLUE} ...${NC}"
|
||||
done
|
||||
|
||||
if /usr/local/bin/docker ps > /dev/null 2>&1; then
|
||||
echo -e "${GREEN} ✅ Docker is ready!${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${GREEN} ✅ Docker is already running${NC}"
|
||||
fi
|
||||
# Mock backend handles app simulation — no Docker required for dev
|
||||
echo -e "${GREEN} Mock backend will simulate all apps${NC}"
|
||||
echo ""
|
||||
|
||||
# Start Docker apps
|
||||
if /usr/local/bin/docker ps > /dev/null 2>&1; then
|
||||
echo -e "${BLUE}🏝️ Starting Archipelago Docker apps...${NC}"
|
||||
cd ..
|
||||
./start-docker-apps.sh
|
||||
cd neode-ui
|
||||
echo ""
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Skipping Docker apps (Docker not available)${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Check if node_modules exists
|
||||
if [ ! -d "node_modules" ]; then
|
||||
echo -e "${YELLOW}⚠️ node_modules not found. Running npm install...${NC}"
|
||||
|
||||
@@ -63,14 +63,5 @@ echo ""
|
||||
echo -e "${GREEN}✅ All Neode development servers stopped!${NC}"
|
||||
echo ""
|
||||
|
||||
# Stop Docker containers
|
||||
echo -e "${BLUE}🐳 Stopping Docker containers...${NC}"
|
||||
if /usr/local/bin/docker ps > /dev/null 2>&1; then
|
||||
cd ..
|
||||
./stop-docker-apps.sh
|
||||
cd neode-ui
|
||||
else
|
||||
echo -e "${YELLOW} ℹ️ Docker not running${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user