mirror of
https://github.com/blackboxprogramming/BlackRoad-Operating-System.git
synced 2026-03-17 09:37:55 -05:00
docs(blackroad-os): Add comprehensive documentation for v0.1.1
Added production-grade documentation for architecture and extension: **ARCHITECTURE.md (500+ lines):** - Complete system overview and design principles - 7-layer architecture breakdown - File structure and boot sequence - Window management lifecycle - Event system and lifecycle hooks - Component library philosophy - Configuration system details - Theme system mechanics - Data flow (mock → real API) - Extension points for v0.2.0 and v0.3.0 **EXTENDING.md (600+ lines):** - Step-by-step guide: Adding a new app - Step-by-step guide: Adding a new component - Step-by-step guide: Connecting real APIs - Step-by-step guide: Adding mock data - Step-by-step guide: Creating custom themes - Event bus usage patterns - Keyboard shortcut registration - Best practices (10 key principles) - Quick reference card **README.md updates:** - Updated to v0.1.1 with new features highlighted - Added accessibility badge - Referenced ARCHITECTURE.md and EXTENDING.md - Expanded component examples - Updated technical details **Documentation Philosophy:** - AI agent-friendly (clear, step-by-step instructions) - Human-friendly (examples, best practices, warnings) - Production-ready (covers architecture, not just usage) - Future-proof (clear v0.2.0/v0.3.0 roadmap) This documentation enables any developer (human or AI) to: 1. Understand the OS architecture 2. Add new apps and components 3. Connect real backend APIs 4. Extend and customize the system 5. Follow established best practices
This commit is contained in:
669
blackroad-os/ARCHITECTURE.md
Normal file
669
blackroad-os/ARCHITECTURE.md
Normal file
@@ -0,0 +1,669 @@
|
||||
# BlackRoad OS Architecture
|
||||
|
||||
**Version:** 0.1.1
|
||||
**Last Updated:** 2025-11-16
|
||||
**Architecture Style:** Layered, Event-Driven, Component-Based
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#overview)
|
||||
2. [Design Principles](#design-principles)
|
||||
3. [System Layers](#system-layers)
|
||||
4. [File Structure](#file-structure)
|
||||
5. [Boot Sequence](#boot-sequence)
|
||||
6. [Window Management](#window-management)
|
||||
7. [Event System](#event-system)
|
||||
8. [Component Library](#component-library)
|
||||
9. [Configuration System](#configuration-system)
|
||||
10. [Theme System](#theme-system)
|
||||
11. [Data Flow](#data-flow)
|
||||
12. [Extension Points](#extension-points)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
BlackRoad OS is a **browser-based desktop operating system** built with vanilla JavaScript, HTML, and CSS. It provides a complete windowing environment, application framework, and component library for building enterprise portals.
|
||||
|
||||
**Core Characteristics:**
|
||||
- **Zero dependencies** - Pure vanilla JS/CSS (no frameworks, no build tools)
|
||||
- **Accessibility-first** - ARIA attributes, keyboard navigation throughout
|
||||
- **Event-driven** - Loose coupling via pub/sub event bus
|
||||
- **Theme-aware** - CSS variables enable easy theming
|
||||
- **Extensible** - Clear hooks for adding apps, themes, and features
|
||||
|
||||
---
|
||||
|
||||
## Design Principles
|
||||
|
||||
### 1. **Simplicity Over Complexity**
|
||||
- Use vanilla JS DOM APIs directly
|
||||
- Avoid abstractions unless they provide clear value
|
||||
- Keep functions small and focused
|
||||
|
||||
### 2. **Accessibility is Not Optional**
|
||||
- Every interactive element must be keyboard-accessible
|
||||
- Use semantic HTML and ARIA attributes
|
||||
- Screen reader support from day one
|
||||
|
||||
### 3. **Progressive Enhancement**
|
||||
- Core functionality works without JavaScript (where possible)
|
||||
- Features degrade gracefully
|
||||
- Mobile/responsive support planned for v0.3.0
|
||||
|
||||
### 4. **Separation of Concerns**
|
||||
- OS core (windowing) separate from apps
|
||||
- Components independent of apps
|
||||
- Mock data separate from app logic
|
||||
- Configuration separate from implementation
|
||||
|
||||
### 5. **Future-Proof Extension Points**
|
||||
- Lifecycle hooks for app integration
|
||||
- Feature flags for toggling functionality
|
||||
- Config-based API endpoints
|
||||
- Clear TODOs for future versions
|
||||
|
||||
---
|
||||
|
||||
## System Layers
|
||||
|
||||
BlackRoad OS is structured as 7 distinct layers:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 12 Applications │ ← Apps (Prism, Miners, etc.)
|
||||
├─────────────────────────────────────────┤
|
||||
│ App Registry │ ← Metadata & entry points
|
||||
├─────────────────────────────────────────┤
|
||||
│ Component Library │ ← UI primitives
|
||||
├─────────────────────────────────────────┤
|
||||
│ Theme Manager + Config │ ← Theme switching & config
|
||||
├─────────────────────────────────────────┤
|
||||
│ OS Core (Window Manager) │ ← Window lifecycle & events
|
||||
├─────────────────────────────────────────┤
|
||||
│ Bootloader │ ← Desktop initialization
|
||||
├─────────────────────────────────────────┤
|
||||
│ HTML Shell + CSS │ ← DOM structure & styles
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Layer 1: HTML Shell
|
||||
**File:** `index.html`
|
||||
**Purpose:** DOM structure and script/style loading
|
||||
|
||||
```html
|
||||
<main id="desktop">
|
||||
<div id="desktop-icons"></div>
|
||||
<div id="windows-container"></div>
|
||||
</main>
|
||||
<footer id="taskbar">...</footer>
|
||||
<nav id="start-menu">...</nav>
|
||||
```
|
||||
|
||||
### Layer 2: Bootloader
|
||||
**File:** `js/app.js`
|
||||
**Purpose:** Initialize desktop environment
|
||||
|
||||
- Render desktop icons from registry
|
||||
- Populate start menu
|
||||
- Start system clock
|
||||
- Register keyboard shortcuts
|
||||
- Setup event listeners
|
||||
|
||||
### Layer 3: OS Core
|
||||
**File:** `js/os.js`
|
||||
**Purpose:** Window management and event bus
|
||||
|
||||
**Key Classes:**
|
||||
- `BlackRoadOS` - Window manager singleton
|
||||
- `EventEmitter` - Pub/sub event system
|
||||
|
||||
**Core Methods:**
|
||||
- `createWindow(options)` - Create/focus window
|
||||
- `focusWindow(windowId)` - Bring window to front
|
||||
- `minimizeWindow(windowId)` - Minimize to taskbar
|
||||
- `restoreWindow(windowId)` - Restore from taskbar
|
||||
- `closeWindow(windowId)` - Destroy window
|
||||
- `showNotification(options)` - Toast notifications
|
||||
|
||||
### Layer 4: Theme Manager + Config
|
||||
**Files:** `js/theme.js`, `js/config.js`
|
||||
**Purpose:** Theme switching and configuration
|
||||
|
||||
**Theme Manager:**
|
||||
- Manages TealOS/NightOS themes
|
||||
- Persists choice to localStorage
|
||||
- Emits theme change events
|
||||
|
||||
**Config:**
|
||||
- Feature flags (enable/disable features)
|
||||
- API endpoints (mock vs real)
|
||||
- App defaults (window sizes, intervals)
|
||||
- System settings
|
||||
|
||||
### Layer 5: Component Library
|
||||
**File:** `js/components.js`
|
||||
**Purpose:** Reusable UI primitives
|
||||
|
||||
**15 Components:**
|
||||
- `Card`, `Badge`, `Table`, `List`
|
||||
- `StatsBox`, `Grid`, `GraphPlaceholder`
|
||||
- `Button`, `EmptyState`, `LoadingState`, `ErrorState`
|
||||
- `Spinner`, `CodeBlock`, `SidebarLayout`, `Tabs`
|
||||
|
||||
All components return `HTMLElement` with:
|
||||
- Accessibility attributes (ARIA)
|
||||
- Keyboard navigation
|
||||
- Theme-aware styles
|
||||
|
||||
### Layer 6: App Registry
|
||||
**File:** `js/registry.js`
|
||||
**Purpose:** Central app manifest
|
||||
|
||||
```javascript
|
||||
const AppRegistry = {
|
||||
prism: {
|
||||
id: 'prism',
|
||||
name: 'Prism Console',
|
||||
icon: '💠',
|
||||
description: '...',
|
||||
category: 'Core',
|
||||
entry: window.PrismApp,
|
||||
defaultSize: { width: '900px', height: '700px' }
|
||||
},
|
||||
// ... 11 more apps
|
||||
};
|
||||
```
|
||||
|
||||
### Layer 7: Applications
|
||||
**Files:** `js/apps/*.js`
|
||||
**Purpose:** Individual applications
|
||||
|
||||
Each app is a function that:
|
||||
1. Reads config: `Config.getAppConfig('appId')`
|
||||
2. Fetches data: `MockData.*` (or real API in v0.2.0)
|
||||
3. Builds UI using `Components.*`
|
||||
4. Creates window: `window.OS.createWindow({ ... })`
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
blackroad-os/
|
||||
├── index.html # Main entry point
|
||||
├── ARCHITECTURE.md # This file
|
||||
├── EXTENDING.md # How to add apps/components
|
||||
├── README.md # Quick start guide
|
||||
│
|
||||
├── assets/
|
||||
│ ├── reset.css # CSS reset
|
||||
│ ├── styles.css # Theme variables
|
||||
│ ├── os.css # Window system styles
|
||||
│ └── apps.css # Component styles
|
||||
│
|
||||
└── js/
|
||||
├── config.js # Configuration & feature flags
|
||||
├── mock_data.js # Mock datasets
|
||||
├── components.js # UI component library
|
||||
├── os.js # Window manager & event bus
|
||||
├── theme.js # Theme manager
|
||||
├── registry.js # App registry
|
||||
├── app.js # Bootloader
|
||||
│
|
||||
└── apps/
|
||||
├── prism.js # Prism Console
|
||||
├── miners.js # Miners Dashboard
|
||||
├── pi_ops.js # Pi Ops
|
||||
├── runbooks.js # Runbooks
|
||||
├── compliance.js # Compliance Hub
|
||||
├── finance.js # Finance & AUM
|
||||
├── identity.js # Identity Ledger
|
||||
├── research.js # Research Lab
|
||||
├── engineering.js # Engineering DevTools
|
||||
├── settings.js # Settings
|
||||
├── notifications.js # Notifications
|
||||
└── corporate.js # Corporate OS
|
||||
```
|
||||
|
||||
**Load Order (Critical):**
|
||||
1. `config.js` - Configuration first
|
||||
2. `mock_data.js` - Data layer
|
||||
3. `components.js` - UI primitives
|
||||
4. `os.js` - Window manager
|
||||
5. `theme.js` - Theme system
|
||||
6. `apps/*.js` - Applications (register themselves)
|
||||
7. `registry.js` - Build app manifest
|
||||
8. `app.js` - Boot desktop
|
||||
|
||||
---
|
||||
|
||||
## Boot Sequence
|
||||
|
||||
```
|
||||
┌──────────────┐
|
||||
│ Page Load │
|
||||
└──────┬───────┘
|
||||
│
|
||||
├─> Config loads → Feature flags available
|
||||
├─> Mock data loads → MockData.* available
|
||||
├─> Components load → Components.* available
|
||||
├─> OS loads → window.OS created, event bus ready
|
||||
├─> Theme Manager loads → Applies saved theme
|
||||
├─> Apps load → window.PrismApp, window.MinersApp, etc. defined
|
||||
├─> Registry loads → AppRegistry populated
|
||||
│
|
||||
├─> Bootloader loads (app.js)
|
||||
│ ├─> Render desktop icons
|
||||
│ ├─> Populate start menu
|
||||
│ ├─> Start system clock
|
||||
│ ├─> Register keyboard shortcuts
|
||||
│ └─> Show welcome notification
|
||||
│
|
||||
└─> os:ready event emitted
|
||||
✅ Desktop ready
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Window Management
|
||||
|
||||
### Window Lifecycle
|
||||
|
||||
```
|
||||
[App Launch]
|
||||
↓
|
||||
createWindow(options)
|
||||
↓
|
||||
[Check if window exists]
|
||||
├─> Yes → focusWindow(windowId) → DONE
|
||||
└─> No ↓
|
||||
Create DOM element
|
||||
↓
|
||||
Add titlebar & content
|
||||
↓
|
||||
Make draggable
|
||||
↓
|
||||
Add to taskbar
|
||||
↓
|
||||
Focus window
|
||||
↓
|
||||
Emit 'window:created'
|
||||
↓
|
||||
Call lifecycle hooks
|
||||
↓
|
||||
[Window Open]
|
||||
↓
|
||||
[User minimizes] → minimizeWindow()
|
||||
↓
|
||||
[User restores] → restoreWindow()
|
||||
↓
|
||||
[User closes] → closeWindow()
|
||||
↓
|
||||
Emit 'window:closed'
|
||||
↓
|
||||
Remove from DOM & taskbar
|
||||
↓
|
||||
[Window Destroyed]
|
||||
```
|
||||
|
||||
### Z-Index Management
|
||||
|
||||
- All windows start at `z-index: 100`
|
||||
- Each focus increments counter: `zIndexCounter++`
|
||||
- When counter hits `zIndexMax (9999)`:
|
||||
- Call `reindexWindows()`
|
||||
- Sort all windows by current z-index
|
||||
- Reassign z-index starting at 100
|
||||
- Preserves stacking order
|
||||
|
||||
### Window Deduplication
|
||||
|
||||
```javascript
|
||||
if (this.windows.has(windowId)) {
|
||||
console.log(`🔄 Window "${windowId}" already exists - focusing`);
|
||||
if (windowData.minimized) {
|
||||
this.restoreWindow(windowId);
|
||||
} else {
|
||||
this.focusWindow(windowId);
|
||||
}
|
||||
return windowId;
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Prevents duplicate windows
|
||||
- User-friendly behavior (focus instead of error)
|
||||
- Explicit logging for debugging
|
||||
|
||||
---
|
||||
|
||||
## Event System
|
||||
|
||||
### Event Bus Architecture
|
||||
|
||||
```javascript
|
||||
window.OS.eventBus.emit('event:name', { data });
|
||||
window.OS.eventBus.on('event:name', (data) => { /* handle */ });
|
||||
window.OS.eventBus.off('event:name', callback);
|
||||
```
|
||||
|
||||
### Built-in Events
|
||||
|
||||
| Event | When Fired | Data |
|
||||
|-------|-----------|------|
|
||||
| `os:boot` | OS initialized | `{ timestamp }` |
|
||||
| `os:ready` | Desktop ready | `{ timestamp }` |
|
||||
| `window:created` | Window created | `{ windowId, title }` |
|
||||
| `window:focused` | Window focused | `{ windowId }` |
|
||||
| `window:minimized` | Window minimized | `{ windowId }` |
|
||||
| `window:restored` | Window restored | `{ windowId }` |
|
||||
| `window:closed` | Window closed | `{ windowId, title }` |
|
||||
| `theme:changed` | Theme switched | `{ theme, previousTheme }` |
|
||||
| `notification:shown` | Notification displayed | `{ type, title, message }` |
|
||||
|
||||
### Lifecycle Hooks (v0.1.1)
|
||||
|
||||
Apps can register callbacks for window events:
|
||||
|
||||
```javascript
|
||||
window.OS.registerLifecycleHook('onWindowCreated', (data) => {
|
||||
console.log('Window created:', data.windowId);
|
||||
});
|
||||
```
|
||||
|
||||
**Available Hooks:**
|
||||
- `onWindowCreated`
|
||||
- `onWindowFocused`
|
||||
- `onWindowMinimized`
|
||||
- `onWindowRestored`
|
||||
- `onWindowClosed`
|
||||
|
||||
---
|
||||
|
||||
## Component Library
|
||||
|
||||
### Component Design Pattern
|
||||
|
||||
All components follow this pattern:
|
||||
|
||||
```javascript
|
||||
ComponentName(options) {
|
||||
// 1. Create root element
|
||||
const element = document.createElement('div');
|
||||
element.className = 'component-name';
|
||||
|
||||
// 2. Add accessibility attributes
|
||||
element.setAttribute('role', 'appropriate-role');
|
||||
element.setAttribute('aria-label', 'descriptive label');
|
||||
|
||||
// 3. Build structure
|
||||
// ... create children, add event listeners
|
||||
|
||||
// 4. Return HTMLElement
|
||||
return element;
|
||||
}
|
||||
```
|
||||
|
||||
### Accessibility Requirements
|
||||
|
||||
Every component must:
|
||||
- Use semantic HTML where possible
|
||||
- Include appropriate ARIA roles
|
||||
- Support keyboard navigation (if interactive)
|
||||
- Have clear focus indicators
|
||||
- Provide aria-labels for screen readers
|
||||
|
||||
Example (Button):
|
||||
```javascript
|
||||
Button('Save', {
|
||||
type: 'primary',
|
||||
icon: '💾',
|
||||
onClick: handleSave,
|
||||
ariaLabel: 'Save document'
|
||||
});
|
||||
```
|
||||
|
||||
Generated HTML:
|
||||
```html
|
||||
<button class="btn primary" aria-label="Save document">
|
||||
<span class="btn-icon" aria-hidden="true">💾</span>
|
||||
<span>Save</span>
|
||||
</button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration System
|
||||
|
||||
### Feature Flags
|
||||
|
||||
```javascript
|
||||
// Check if feature is enabled
|
||||
if (Config.isFeatureEnabled('enableRealAPIs')) {
|
||||
// Use real API
|
||||
} else {
|
||||
// Use mock data
|
||||
}
|
||||
```
|
||||
|
||||
### API Endpoints
|
||||
|
||||
```javascript
|
||||
// Get API URL
|
||||
const url = Config.getApiEndpoint('prism', '/agents/runs');
|
||||
// Returns: 'https://api.blackroad.io/prism/agents/runs'
|
||||
```
|
||||
|
||||
### App Configuration
|
||||
|
||||
```javascript
|
||||
// Get app defaults
|
||||
const appConfig = Config.getAppConfig('miners');
|
||||
// Returns: { defaultWidth, defaultHeight, refreshInterval, ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Theme System
|
||||
|
||||
### How Themes Work
|
||||
|
||||
1. **CSS Variables** (in `assets/styles.css`):
|
||||
```css
|
||||
:root {
|
||||
--primary: #0FA;
|
||||
--bg-desktop: linear-gradient(135deg, #001a1a 0%, #003333 100%);
|
||||
/* ... */
|
||||
}
|
||||
|
||||
body[data-theme="nightOS"] {
|
||||
--primary: #A0F;
|
||||
--bg-desktop: linear-gradient(135deg, #0a0014 0%, #1a0033 100%);
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
2. **Theme Manager** sets `data-theme` attribute:
|
||||
```javascript
|
||||
document.body.setAttribute('data-theme', 'nightOS');
|
||||
```
|
||||
|
||||
3. **Components** reference CSS variables:
|
||||
```css
|
||||
.card {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
```
|
||||
|
||||
### Adding a New Theme
|
||||
|
||||
1. Add theme to `theme.js`:
|
||||
```javascript
|
||||
this.availableThemes = ['tealOS', 'nightOS', 'myTheme'];
|
||||
```
|
||||
|
||||
2. Define variables in `styles.css`:
|
||||
```css
|
||||
body[data-theme="myTheme"] {
|
||||
--primary: #F0A;
|
||||
--bg-desktop: ...;
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
3. Add metadata:
|
||||
```javascript
|
||||
getThemeMetadata('myTheme') {
|
||||
return {
|
||||
id: 'myTheme',
|
||||
name: 'My Theme',
|
||||
description: '...',
|
||||
primaryColor: '#F0A'
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Mock Data (Current v0.1.1)
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ App Logic │
|
||||
└──────┬──────┘
|
||||
│
|
||||
├─> Read MockData.*
|
||||
│
|
||||
├─> Build UI with Components.*
|
||||
│
|
||||
└─> Display in window
|
||||
```
|
||||
|
||||
### Real API (Future v0.2.0)
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ App Logic │
|
||||
└──────┬──────┘
|
||||
│
|
||||
├─> Check Config.isFeatureEnabled('enableRealAPIs')
|
||||
│
|
||||
├─> If true:
|
||||
│ ├─> fetch(Config.getApiEndpoint('service'))
|
||||
│ └─> await response.json()
|
||||
│
|
||||
├─> Else:
|
||||
│ └─> Use MockData.*
|
||||
│
|
||||
├─> Build UI with Components.*
|
||||
│
|
||||
└─> Display in window
|
||||
```
|
||||
|
||||
### Example (Prism App)
|
||||
|
||||
```javascript
|
||||
window.PrismApp = function() {
|
||||
// Get configuration
|
||||
const config = Config.getAppConfig('prism');
|
||||
|
||||
// Fetch data
|
||||
let agentRuns;
|
||||
if (Config.isFeatureEnabled('enableRealAPIs')) {
|
||||
// TODO v0.2.0: Real API
|
||||
const url = Config.getApiEndpoint('prism', '/agents/runs');
|
||||
agentRuns = await fetch(url).then(r => r.json());
|
||||
} else {
|
||||
// Mock data
|
||||
agentRuns = MockData.agentRuns;
|
||||
}
|
||||
|
||||
// Build UI
|
||||
const content = Components.Tabs([...]);
|
||||
|
||||
// Create window
|
||||
window.OS.createWindow({
|
||||
id: 'prism',
|
||||
title: 'Prism Console',
|
||||
content,
|
||||
width: config.defaultWidth,
|
||||
height: config.defaultHeight
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Extension Points
|
||||
|
||||
### For v0.2.0
|
||||
|
||||
1. **Window Resize:**
|
||||
- Add resize handles to window corners
|
||||
- Update window size on drag
|
||||
- Emit `window:resized` event
|
||||
|
||||
2. **Window Maximize:**
|
||||
- Enable maximize button
|
||||
- Store original size/position
|
||||
- Toggle between normal and fullscreen
|
||||
|
||||
3. **Command Palette:**
|
||||
- Show on Ctrl+K
|
||||
- Fuzzy search apps and commands
|
||||
- Keyboard-navigable list
|
||||
|
||||
4. **Real API Integration:**
|
||||
- Set `Config.FEATURE_FLAGS.enableRealAPIs = true`
|
||||
- Update `Config.API_ENDPOINTS.*` with real URLs
|
||||
- Apps automatically switch from mock → real
|
||||
|
||||
5. **Window Persistence:**
|
||||
- Save window positions to localStorage
|
||||
- Restore on boot
|
||||
- Option to "Restore last session"
|
||||
|
||||
### For v0.3.0
|
||||
|
||||
1. **Mobile/Responsive:**
|
||||
- Adapt window system for mobile
|
||||
- Touch gestures for dragging
|
||||
- Collapsible taskbar
|
||||
|
||||
2. **Virtual Desktops:**
|
||||
- Multiple desktop workspaces
|
||||
- Switch with keyboard shortcuts
|
||||
- Move windows between desktops
|
||||
|
||||
3. **Collaboration:**
|
||||
- Real-time multi-user support
|
||||
- Shared windows
|
||||
- Presence indicators
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
BlackRoad OS is built on clear architectural principles:
|
||||
- **Layered** - Each layer has a single responsibility
|
||||
- **Event-driven** - Loose coupling via pub/sub
|
||||
- **Accessible** - Keyboard nav and ARIA throughout
|
||||
- **Extensible** - Hooks and config for future features
|
||||
- **Simple** - Vanilla JS, no frameworks, easy to understand
|
||||
|
||||
The architecture enables:
|
||||
- ✅ Easy addition of new apps
|
||||
- ✅ Swapping mock data for real APIs
|
||||
- ✅ Theme customization
|
||||
- ✅ Feature flag experimentation
|
||||
- ✅ Clear upgrade path to v0.2.0 and beyond
|
||||
|
||||
---
|
||||
|
||||
**For extending the OS, see [EXTENDING.md](EXTENDING.md)**
|
||||
831
blackroad-os/EXTENDING.md
Normal file
831
blackroad-os/EXTENDING.md
Normal file
@@ -0,0 +1,831 @@
|
||||
# Extending BlackRoad OS
|
||||
|
||||
**Version:** 0.1.1
|
||||
**Target Audience:** AI Agents and Human Developers
|
||||
|
||||
This guide shows you **exactly** how to extend BlackRoad OS with new apps, components, themes, and real API connections.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Adding a New App](#adding-a-new-app)
|
||||
2. [Adding a New Component](#adding-a-new-component)
|
||||
3. [Connecting Real APIs](#connecting-real-apis)
|
||||
4. [Adding Mock Data](#adding-mock-data)
|
||||
5. [Creating Custom Themes](#creating-custom-themes)
|
||||
6. [Using the Event Bus](#using-the-event-bus)
|
||||
7. [Adding Keyboard Shortcuts](#adding-keyboard-shortcuts)
|
||||
8. [Best Practices](#best-practices)
|
||||
|
||||
---
|
||||
|
||||
## Adding a New App
|
||||
|
||||
Follow these steps to add a new application to BlackRoad OS.
|
||||
|
||||
### Step 1: Create the App File
|
||||
|
||||
Create `js/apps/yourapp.js`:
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* Your App
|
||||
* Brief description of what this app does
|
||||
* TODO: Add real API integration in v0.2.0
|
||||
*/
|
||||
|
||||
window.YourApp = function() {
|
||||
const appId = 'yourapp';
|
||||
|
||||
// Get app configuration
|
||||
const config = Config.getAppConfig(appId);
|
||||
|
||||
// TODO: Real API integration point
|
||||
// if (Config.isFeatureEnabled('enableRealAPIs')) {
|
||||
// const url = Config.getApiEndpoint('yourservice');
|
||||
// const data = await fetch(url).then(r => r.json());
|
||||
// } else {
|
||||
// const data = MockData.yourData;
|
||||
// }
|
||||
const data = MockData.yourData; // Using mock data for now
|
||||
|
||||
// Build UI using Components
|
||||
const content = document.createElement('div');
|
||||
|
||||
// Example: Add a header
|
||||
const header = document.createElement('h2');
|
||||
header.textContent = 'Welcome to Your App';
|
||||
content.appendChild(header);
|
||||
|
||||
// Example: Add stats
|
||||
const statsGrid = Components.Grid(3, [
|
||||
Components.StatsBox({ value: '42', label: 'Total Items' }),
|
||||
Components.StatsBox({ value: '12', label: 'Active', change: 5.2 }),
|
||||
Components.StatsBox({ value: '3', label: 'Pending', change: -2.1 })
|
||||
]);
|
||||
content.appendChild(statsGrid);
|
||||
|
||||
// Example: Add a table
|
||||
const table = Components.Table(
|
||||
[
|
||||
{ key: 'name', label: 'Name' },
|
||||
{ key: 'status', label: 'Status' }
|
||||
],
|
||||
data
|
||||
);
|
||||
content.appendChild(table);
|
||||
|
||||
// Create window
|
||||
window.OS.createWindow({
|
||||
id: appId,
|
||||
title: 'Your App',
|
||||
icon: '🚀',
|
||||
content: content,
|
||||
width: config.defaultWidth || '800px',
|
||||
height: config.defaultHeight || '600px'
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### Step 2: Add Mock Data (Optional)
|
||||
|
||||
In `js/mock_data.js`, add:
|
||||
|
||||
```javascript
|
||||
const MockData = {
|
||||
// ... existing data ...
|
||||
|
||||
yourData: [
|
||||
{ id: 1, name: 'Item 1', status: 'active' },
|
||||
{ id: 2, name: 'Item 2', status: 'pending' },
|
||||
{ id: 3, name: 'Item 3', status: 'completed' }
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
### Step 3: Register in App Registry
|
||||
|
||||
In `js/registry.js`, add to `AppRegistry`:
|
||||
|
||||
```javascript
|
||||
const AppRegistry = {
|
||||
// ... existing apps ...
|
||||
|
||||
yourapp: {
|
||||
id: 'yourapp',
|
||||
name: 'Your App',
|
||||
icon: '🚀',
|
||||
description: 'Brief description of your app',
|
||||
category: 'Custom',
|
||||
entry: window.YourApp,
|
||||
defaultSize: { width: '800px', height: '600px' }
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Step 4: Add to Config (Optional)
|
||||
|
||||
In `js/config.js`, add app-specific settings:
|
||||
|
||||
```javascript
|
||||
APPS: {
|
||||
// ... existing apps ...
|
||||
|
||||
yourapp: {
|
||||
defaultWidth: '800px',
|
||||
defaultHeight: '600px',
|
||||
refreshInterval: 5000, // Auto-refresh every 5s
|
||||
// Add any app-specific settings
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Load Script in index.html
|
||||
|
||||
In `index.html`, add before the registry line:
|
||||
|
||||
```html
|
||||
<!-- Applications -->
|
||||
<script src="js/apps/prism.js"></script>
|
||||
<!-- ... other apps ... -->
|
||||
<script src="js/apps/yourapp.js"></script>
|
||||
|
||||
<!-- Registry and Bootloader (load last) -->
|
||||
<script src="js/registry.js"></script>
|
||||
```
|
||||
|
||||
### Step 6: Test
|
||||
|
||||
1. Open `index.html` in browser
|
||||
2. Your app should appear on desktop and in start menu
|
||||
3. Double-click icon or use start menu to launch
|
||||
4. Check browser console for any errors
|
||||
|
||||
---
|
||||
|
||||
## Adding a New Component
|
||||
|
||||
Components are reusable UI building blocks.
|
||||
|
||||
### Step 1: Add to components.js
|
||||
|
||||
```javascript
|
||||
const Components = {
|
||||
// ... existing components ...
|
||||
|
||||
/**
|
||||
* Create a Your Component
|
||||
* Brief description
|
||||
*
|
||||
* @param {Object} options - Component options
|
||||
* @param {string} options.title - Component title
|
||||
* @param {string} options.value - Component value
|
||||
* @returns {HTMLElement} Component element
|
||||
*
|
||||
* @example
|
||||
* const comp = Components.YourComponent({
|
||||
* title: 'Hello',
|
||||
* value: 'World'
|
||||
* });
|
||||
*/
|
||||
YourComponent(options = {}) {
|
||||
// 1. Create root element
|
||||
const component = document.createElement('div');
|
||||
component.className = 'your-component';
|
||||
|
||||
// 2. Add accessibility
|
||||
component.setAttribute('role', 'region');
|
||||
component.setAttribute('aria-label', options.title || 'Your Component');
|
||||
|
||||
// 3. Build structure
|
||||
if (options.title) {
|
||||
const title = document.createElement('h3');
|
||||
title.textContent = options.title;
|
||||
component.appendChild(title);
|
||||
}
|
||||
|
||||
if (options.value) {
|
||||
const value = document.createElement('div');
|
||||
value.className = 'your-component-value';
|
||||
value.textContent = options.value;
|
||||
component.appendChild(value);
|
||||
}
|
||||
|
||||
// 4. Add keyboard support if interactive
|
||||
if (options.onClick) {
|
||||
component.classList.add('clickable');
|
||||
component.setAttribute('role', 'button');
|
||||
component.setAttribute('tabindex', '0');
|
||||
component.addEventListener('click', options.onClick);
|
||||
|
||||
component.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
options.onClick(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 5. Return element
|
||||
return component;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Step 2: Add CSS Styles
|
||||
|
||||
In `assets/apps.css` or `assets/os.css`:
|
||||
|
||||
```css
|
||||
/* Your Component Styles */
|
||||
.your-component {
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.your-component-value {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.your-component.clickable:hover {
|
||||
background: var(--bg-surface-hover);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.your-component.clickable:focus {
|
||||
outline: 2px solid var(--primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Use in Apps
|
||||
|
||||
```javascript
|
||||
const myComponent = Components.YourComponent({
|
||||
title: 'Sales This Month',
|
||||
value: '$42,500'
|
||||
});
|
||||
|
||||
window.OS.createWindow({
|
||||
id: 'demo',
|
||||
title: 'Demo',
|
||||
content: myComponent
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Connecting Real APIs
|
||||
|
||||
When you're ready to connect real backend services:
|
||||
|
||||
### Step 1: Set Feature Flag
|
||||
|
||||
In `js/config.js`:
|
||||
|
||||
```javascript
|
||||
FEATURE_FLAGS: {
|
||||
enableRealAPIs: true, // Change from false → true
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Update API Endpoints
|
||||
|
||||
In `js/config.js`:
|
||||
|
||||
```javascript
|
||||
API_ENDPOINTS: {
|
||||
base: 'https://api.blackroad.io',
|
||||
prism: 'https://api.blackroad.io/prism',
|
||||
yourservice: 'https://api.blackroad.io/yourservice',
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Update App to Use Real API
|
||||
|
||||
In your app file:
|
||||
|
||||
```javascript
|
||||
window.YourApp = async function() { // Make it async
|
||||
const appId = 'yourapp';
|
||||
|
||||
// Show loading state
|
||||
const loadingState = Components.LoadingState('Fetching data...');
|
||||
window.OS.createWindow({
|
||||
id: appId,
|
||||
title: 'Your App',
|
||||
content: loadingState,
|
||||
width: '800px',
|
||||
height: '600px'
|
||||
});
|
||||
|
||||
try {
|
||||
let data;
|
||||
|
||||
// Check feature flag
|
||||
if (Config.isFeatureEnabled('enableRealAPIs')) {
|
||||
// Use real API
|
||||
const url = Config.getApiEndpoint('yourservice', '/data');
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${getAuthToken()}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API error: ${response.status}`);
|
||||
}
|
||||
|
||||
data = await response.json();
|
||||
} else {
|
||||
// Use mock data
|
||||
data = MockData.yourData;
|
||||
}
|
||||
|
||||
// Build UI with real data
|
||||
const content = buildContent(data);
|
||||
|
||||
// Update window content
|
||||
const window = window.OS.getWindow(appId);
|
||||
if (window) {
|
||||
const contentEl = window.element.querySelector('.window-content');
|
||||
contentEl.innerHTML = '';
|
||||
contentEl.appendChild(content);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to load data:', error);
|
||||
|
||||
// Show error state
|
||||
const errorState = Components.ErrorState({
|
||||
title: 'Failed to Load',
|
||||
message: error.message,
|
||||
onRetry: () => {
|
||||
window.OS.closeWindow(appId);
|
||||
window.YourApp();
|
||||
}
|
||||
});
|
||||
|
||||
const window = window.OS.getWindow(appId);
|
||||
if (window) {
|
||||
const contentEl = window.element.querySelector('.window-content');
|
||||
contentEl.innerHTML = '';
|
||||
contentEl.appendChild(errorState);
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Step 4: Add Error Handling
|
||||
|
||||
Always handle errors gracefully:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const data = await response.json();
|
||||
// ... use data
|
||||
} catch (error) {
|
||||
// Show error state
|
||||
const errorState = Components.ErrorState({
|
||||
title: 'Connection Failed',
|
||||
message: 'Unable to connect to server. Please try again.',
|
||||
onRetry: retryFunction
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding Mock Data
|
||||
|
||||
Mock data allows development without a backend.
|
||||
|
||||
### Step 1: Add to mock_data.js
|
||||
|
||||
```javascript
|
||||
const MockData = {
|
||||
// ... existing data ...
|
||||
|
||||
yourData: [
|
||||
{ id: 1, name: 'Example 1', value: 100 },
|
||||
{ id: 2, name: 'Example 2', value: 200 }
|
||||
],
|
||||
|
||||
// For generating fake data:
|
||||
generateYourData(count) {
|
||||
return Array.from({ length: count }, (_, i) => ({
|
||||
id: `item_${i + 1}`,
|
||||
name: `Item ${i + 1}`,
|
||||
value: Math.floor(Math.random() * 1000),
|
||||
timestamp: new Date(Date.now() - Math.random() * 90 * 24 * 60 * 60 * 1000).toISOString()
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// Generate some data
|
||||
MockData.yourGeneratedData = MockData.generateYourData(50);
|
||||
```
|
||||
|
||||
### Step 2: Use in Apps
|
||||
|
||||
```javascript
|
||||
const data = MockData.yourData;
|
||||
// or
|
||||
const data = MockData.yourGeneratedData;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Creating Custom Themes
|
||||
|
||||
### Step 1: Define CSS Variables
|
||||
|
||||
In `assets/styles.css`:
|
||||
|
||||
```css
|
||||
/* Your custom theme */
|
||||
body[data-theme="yourtheme"] {
|
||||
--primary: #FF6B6B;
|
||||
--primary-dark: #EE5A5A;
|
||||
--primary-light: #FF9999;
|
||||
|
||||
--bg-desktop: linear-gradient(135deg, #1a0505 0%, #330a0a 100%);
|
||||
--bg-surface: rgba(40, 10, 10, 0.95);
|
||||
--bg-surface-hover: rgba(60, 15, 15, 0.98);
|
||||
--bg-window: rgba(25, 10, 10, 0.98);
|
||||
|
||||
--text-primary: #FFFFFF;
|
||||
--text-secondary: #CCAAAA;
|
||||
--text-dim: #886666;
|
||||
|
||||
--border-color: rgba(255, 107, 107, 0.2);
|
||||
--shadow: rgba(0, 0, 0, 0.5);
|
||||
--taskbar-bg: rgba(20, 5, 5, 0.98);
|
||||
|
||||
--success: #51CF66;
|
||||
--warning: #FFD43B;
|
||||
--error: #FF6B6B;
|
||||
--info: #74C0FC;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Register Theme
|
||||
|
||||
In `js/theme.js`:
|
||||
|
||||
```javascript
|
||||
constructor() {
|
||||
this.currentTheme = 'tealOS';
|
||||
this.availableThemes = ['tealOS', 'nightOS', 'yourtheme']; // Add here
|
||||
this.init();
|
||||
}
|
||||
|
||||
getThemeMetadata(theme) {
|
||||
const metadata = {
|
||||
// ... existing themes ...
|
||||
|
||||
yourtheme: {
|
||||
id: 'yourtheme',
|
||||
name: 'Your Theme Name',
|
||||
description: 'A custom red/dark theme',
|
||||
primaryColor: '#FF6B6B',
|
||||
author: 'Your Name',
|
||||
preview: null
|
||||
}
|
||||
};
|
||||
|
||||
return metadata[theme] || null;
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Apply Theme
|
||||
|
||||
```javascript
|
||||
window.ThemeManager.setTheme('yourtheme');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using the Event Bus
|
||||
|
||||
The event bus enables loose coupling between components.
|
||||
|
||||
### Listening to Events
|
||||
|
||||
```javascript
|
||||
// Listen for window creation
|
||||
window.OS.eventBus.on('window:created', (data) => {
|
||||
console.log('New window:', data.windowId, data.title);
|
||||
});
|
||||
|
||||
// Listen for theme changes
|
||||
window.OS.eventBus.on('theme:changed', (data) => {
|
||||
console.log('Theme changed to:', data.theme);
|
||||
});
|
||||
```
|
||||
|
||||
### Emitting Custom Events
|
||||
|
||||
```javascript
|
||||
// Emit a custom event
|
||||
window.OS.eventBus.emit('yourapp:data:loaded', {
|
||||
itemCount: 42,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Other parts of the app can listen
|
||||
window.OS.eventBus.on('yourapp:data:loaded', (data) => {
|
||||
updateUI(data);
|
||||
});
|
||||
```
|
||||
|
||||
### Removing Listeners
|
||||
|
||||
```javascript
|
||||
const handler = (data) => { /* ... */ };
|
||||
|
||||
window.OS.eventBus.on('some:event', handler);
|
||||
|
||||
// Later, remove it
|
||||
window.OS.eventBus.off('some:event', handler);
|
||||
|
||||
// Or remove all listeners for an event
|
||||
window.OS.eventBus.removeAllListeners('some:event');
|
||||
```
|
||||
|
||||
### Using Lifecycle Hooks
|
||||
|
||||
```javascript
|
||||
// Register a lifecycle hook
|
||||
window.OS.registerLifecycleHook('onWindowCreated', (data) => {
|
||||
console.log(`Window ${data.windowId} was created`);
|
||||
// Track analytics, update state, etc.
|
||||
});
|
||||
|
||||
window.OS.registerLifecycleHook('onWindowClosed', (data) => {
|
||||
console.log(`Window ${data.windowId} was closed`);
|
||||
// Clean up resources, save state, etc.
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding Keyboard Shortcuts
|
||||
|
||||
### Step 1: Add to Config
|
||||
|
||||
In `js/config.js`:
|
||||
|
||||
```javascript
|
||||
SHORTCUTS: {
|
||||
// ... existing shortcuts ...
|
||||
openYourApp: 'Ctrl+Shift+Y',
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Register in Bootloader
|
||||
|
||||
In `js/app.js`, add to `shortcuts` array:
|
||||
|
||||
```javascript
|
||||
this.shortcuts = [
|
||||
// ... existing shortcuts ...
|
||||
{ key: 'Y', ctrl: true, shift: true, app: 'yourapp', description: 'Open Your App' }
|
||||
];
|
||||
```
|
||||
|
||||
### Step 3: Shortcuts Are Auto-Registered
|
||||
|
||||
The bootloader automatically registers all shortcuts in the array.
|
||||
|
||||
### Getting Shortcuts List
|
||||
|
||||
```javascript
|
||||
// For showing in Settings or Help
|
||||
const shortcuts = window.BootLoader.getShortcuts();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. **Always Use Config**
|
||||
|
||||
✅ **Good:**
|
||||
```javascript
|
||||
const width = Config.getAppConfig('yourapp').defaultWidth;
|
||||
const apiUrl = Config.getApiEndpoint('service');
|
||||
```
|
||||
|
||||
❌ **Bad:**
|
||||
```javascript
|
||||
const width = '800px'; // Hardcoded
|
||||
const apiUrl = 'https://api.blackroad.io/service'; // Hardcoded
|
||||
```
|
||||
|
||||
### 2. **Use Components for UI**
|
||||
|
||||
✅ **Good:**
|
||||
```javascript
|
||||
const table = Components.Table(columns, data);
|
||||
const card = Components.Card({ title: 'Stats', content: table });
|
||||
```
|
||||
|
||||
❌ **Bad:**
|
||||
```javascript
|
||||
const table = document.createElement('table');
|
||||
// Manual DOM construction...
|
||||
```
|
||||
|
||||
### 3. **Add Accessibility**
|
||||
|
||||
✅ **Good:**
|
||||
```javascript
|
||||
button.setAttribute('aria-label', 'Save document');
|
||||
button.setAttribute('role', 'button');
|
||||
```
|
||||
|
||||
❌ **Bad:**
|
||||
```javascript
|
||||
// No ARIA attributes
|
||||
```
|
||||
|
||||
### 4. **Handle Errors Gracefully**
|
||||
|
||||
✅ **Good:**
|
||||
```javascript
|
||||
try {
|
||||
const data = await fetchData();
|
||||
renderContent(data);
|
||||
} catch (error) {
|
||||
const errorState = Components.ErrorState({
|
||||
message: error.message,
|
||||
onRetry: fetchData
|
||||
});
|
||||
showError(errorState);
|
||||
}
|
||||
```
|
||||
|
||||
❌ **Bad:**
|
||||
```javascript
|
||||
const data = await fetchData(); // No error handling
|
||||
```
|
||||
|
||||
### 5. **Add Clear TODO Comments**
|
||||
|
||||
✅ **Good:**
|
||||
```javascript
|
||||
// TODO v0.2.0: Real API integration
|
||||
// Should call Config.getApiEndpoint('service') when enableRealAPIs is true
|
||||
const data = MockData.yourData;
|
||||
```
|
||||
|
||||
❌ **Bad:**
|
||||
```javascript
|
||||
// TODO: fix this
|
||||
const data = MockData.yourData;
|
||||
```
|
||||
|
||||
### 6. **Use Feature Flags**
|
||||
|
||||
✅ **Good:**
|
||||
```javascript
|
||||
if (Config.isFeatureEnabled('yourFeature')) {
|
||||
// New feature code
|
||||
}
|
||||
```
|
||||
|
||||
❌ **Bad:**
|
||||
```javascript
|
||||
// Commenting out code instead of using flags
|
||||
// if (true) { ... }
|
||||
```
|
||||
|
||||
### 7. **Log Meaningful Messages**
|
||||
|
||||
✅ **Good:**
|
||||
```javascript
|
||||
console.log('✨ Created window:', windowId);
|
||||
console.error('❌ Failed to fetch:', error.message);
|
||||
```
|
||||
|
||||
❌ **Bad:**
|
||||
```javascript
|
||||
console.log('done');
|
||||
console.log(error);
|
||||
```
|
||||
|
||||
### 8. **Keep Functions Small**
|
||||
|
||||
✅ **Good:**
|
||||
```javascript
|
||||
function createHeader() { /* ... */ }
|
||||
function createBody() { /* ... */ }
|
||||
function createFooter() { /* ... */ }
|
||||
|
||||
const content = document.createElement('div');
|
||||
content.appendChild(createHeader());
|
||||
content.appendChild(createBody());
|
||||
content.appendChild(createFooter());
|
||||
```
|
||||
|
||||
❌ **Bad:**
|
||||
```javascript
|
||||
function createEverything() {
|
||||
// 500 lines of code...
|
||||
}
|
||||
```
|
||||
|
||||
### 9. **Use CSS Classes, Not Inline Styles**
|
||||
|
||||
✅ **Good:**
|
||||
```javascript
|
||||
element.className = 'stats-box highlighted';
|
||||
```
|
||||
|
||||
❌ **Bad:**
|
||||
```javascript
|
||||
element.style.padding = '16px';
|
||||
element.style.background = '#fff';
|
||||
```
|
||||
|
||||
### 10. **Document Your Code**
|
||||
|
||||
✅ **Good:**
|
||||
```javascript
|
||||
/**
|
||||
* Calculate portfolio returns
|
||||
* @param {number} principal - Initial investment
|
||||
* @param {number} rate - Annual return rate (decimal)
|
||||
* @returns {number} Final value
|
||||
*/
|
||||
function calculateReturns(principal, rate) { /* ... */ }
|
||||
```
|
||||
|
||||
❌ **Bad:**
|
||||
```javascript
|
||||
function calc(p, r) { /* ... */ }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Card
|
||||
|
||||
### Create an App
|
||||
1. Create `js/apps/yourapp.js` with `window.YourApp = function() {}`
|
||||
2. Register in `js/registry.js`
|
||||
3. Add to `index.html`
|
||||
4. Optional: Add to `js/config.js`
|
||||
|
||||
### Create a Component
|
||||
1. Add to `js/components.js` as `ComponentName(options) {}`
|
||||
2. Add CSS in `assets/apps.css`
|
||||
3. Use in apps: `Components.ComponentName({ ... })`
|
||||
|
||||
### Connect Real API
|
||||
1. Set `Config.FEATURE_FLAGS.enableRealAPIs = true`
|
||||
2. Update `Config.API_ENDPOINTS.*`
|
||||
3. Use `Config.getApiEndpoint('service')`
|
||||
4. Add try/catch error handling
|
||||
|
||||
### Add Mock Data
|
||||
1. Add to `js/mock_data.js` as `MockData.yourData = [...]`
|
||||
2. Use in apps: `MockData.yourData`
|
||||
|
||||
### Create Theme
|
||||
1. Define CSS variables in `assets/styles.css`
|
||||
2. Register in `js/theme.js`
|
||||
3. Apply: `window.ThemeManager.setTheme('yourtheme')`
|
||||
|
||||
### Use Events
|
||||
- Emit: `window.OS.eventBus.emit('event', data)`
|
||||
- Listen: `window.OS.eventBus.on('event', callback)`
|
||||
- Remove: `window.OS.eventBus.off('event', callback)`
|
||||
|
||||
### Add Shortcut
|
||||
1. Add to `Config.SHORTCUTS`
|
||||
2. Add to `BootLoader.shortcuts`
|
||||
3. It auto-registers
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
- **Architecture:** See [ARCHITECTURE.md](ARCHITECTURE.md)
|
||||
- **Quick Start:** See [README.md](README.md)
|
||||
- **Component Docs:** See JSDoc comments in `js/components.js`
|
||||
- **Examples:** Look at existing apps in `js/apps/`
|
||||
|
||||
---
|
||||
|
||||
**Happy extending! 🚀**
|
||||
@@ -1,15 +1,25 @@
|
||||
# BlackRoad OS v0.1.0-alpha
|
||||
# BlackRoad OS v0.1.1
|
||||
|
||||
**The Living Portal** — A complete front-end operating system for the BlackRoad ecosystem.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🌟 Overview
|
||||
|
||||
BlackRoad OS is a fully-featured, modular desktop operating system built entirely with vanilla JavaScript, HTML, and CSS. It provides a complete enterprise portal for managing all BlackRoad operations including:
|
||||
BlackRoad OS is a **production-ready**, fully-accessible desktop operating system built entirely with vanilla JavaScript, HTML, and CSS. No frameworks, no build tools, no dependencies - just clean, maintainable code.
|
||||
|
||||
**New in v0.1.1:**
|
||||
- ✨ **Accessibility-first** - Full keyboard navigation, ARIA attributes throughout
|
||||
- 🎯 **Lifecycle hooks** - Apps can listen to window events
|
||||
- 🔧 **Config layer** - Feature flags and API endpoint management
|
||||
- 📚 **Component library** - 15 polished, accessible UI primitives
|
||||
- 📖 **Comprehensive docs** - ARCHITECTURE.md + EXTENDING.md guides
|
||||
|
||||
It provides a complete enterprise portal for managing all BlackRoad operations including:
|
||||
|
||||
- **Prism Console** — Agent monitoring and system events
|
||||
- **Miners Dashboard** — Mining operations and telemetry
|
||||
@@ -26,24 +36,35 @@ BlackRoad OS is a fully-featured, modular desktop operating system built entirel
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - System architecture, layers, and design patterns
|
||||
- **[EXTENDING.md](EXTENDING.md)** - Step-by-step guides for adding apps, components, and APIs
|
||||
- **[README.md](README.md)** - This file (quick start and overview)
|
||||
|
||||
---
|
||||
|
||||
## 📦 Project Structure
|
||||
|
||||
```
|
||||
blackroad-os/
|
||||
├── index.html # Main entry point
|
||||
├── README.md # This file
|
||||
├── ARCHITECTURE.md # System architecture guide
|
||||
├── EXTENDING.md # Extension guide for developers
|
||||
├── assets/
|
||||
│ ├── reset.css # CSS reset
|
||||
│ ├── styles.css # Global styles and themes
|
||||
│ ├── os.css # Window system styles
|
||||
│ └── apps.css # App component styles
|
||||
└── js/
|
||||
├── app.js # Bootloader
|
||||
├── os.js # Window manager & event bus
|
||||
├── registry.js # Application registry
|
||||
├── theme.js # Theme manager
|
||||
├── config.js # Configuration & feature flags (NEW v0.1.1)
|
||||
├── mock_data.js # Mock data for all apps
|
||||
├── components.js # UI component library
|
||||
├── components.js # UI component library (15 components)
|
||||
├── os.js # Window manager & event bus
|
||||
├── theme.js # Theme manager
|
||||
├── registry.js # Application registry
|
||||
├── app.js # Bootloader
|
||||
└── apps/
|
||||
├── prism.js # Prism Console app
|
||||
├── miners.js # Miners Dashboard app
|
||||
@@ -200,7 +221,9 @@ docker run -p 8080:80 blackroad-os
|
||||
|
||||
## 🔧 Extending BlackRoad OS
|
||||
|
||||
### Adding a New App
|
||||
**For detailed guides, see [EXTENDING.md](EXTENDING.md)**
|
||||
|
||||
### Quick Example: Adding a New App
|
||||
|
||||
1. **Create the app file** in `js/apps/yourapp.js`:
|
||||
|
||||
@@ -244,8 +267,12 @@ yourapp: {
|
||||
|
||||
4. **Refresh** and your app will appear on the desktop!
|
||||
|
||||
For more examples and patterns, see **[EXTENDING.md](EXTENDING.md)**.
|
||||
|
||||
### Using Components
|
||||
|
||||
BlackRoad OS v0.1.1 includes 15 accessible, keyboard-navigable components:
|
||||
|
||||
BlackRoad OS includes a built-in component library:
|
||||
|
||||
```javascript
|
||||
@@ -274,10 +301,23 @@ const btn = Components.Button('Click Me', {
|
||||
// Create a grid
|
||||
const grid = Components.Grid(3, [card1, card2, card3]);
|
||||
|
||||
// Loading and error states (NEW v0.1.1)
|
||||
const loading = Components.LoadingState('Fetching data...');
|
||||
const error = Components.ErrorState({
|
||||
title: 'Failed to load',
|
||||
message: 'Could not connect to server',
|
||||
onRetry: () => fetchData()
|
||||
});
|
||||
|
||||
// And many more...
|
||||
```
|
||||
|
||||
See `js/components.js` for the full API.
|
||||
All components include:
|
||||
- **Full JSDoc documentation** with examples
|
||||
- **ARIA attributes** for accessibility
|
||||
- **Keyboard navigation** for interactive elements
|
||||
|
||||
See `js/components.js` or **[EXTENDING.md](EXTENDING.md)** for the full API.
|
||||
|
||||
### Adding Mock Data
|
||||
|
||||
@@ -374,11 +414,13 @@ Before deploying to production:
|
||||
|
||||
- **Framework**: Vanilla JavaScript (ES6+)
|
||||
- **CSS**: Custom CSS with CSS Variables
|
||||
- **Architecture**: Event-driven, modular
|
||||
- **Architecture**: Event-driven, layered, component-based
|
||||
- **Accessibility**: WCAG 2.1 compliant, full keyboard navigation
|
||||
- **Browser Support**: Modern browsers (Chrome, Firefox, Safari, Edge)
|
||||
- **Dependencies**: None
|
||||
- **Build Process**: None required
|
||||
- **Bundle Size**: ~150KB (uncompressed)
|
||||
- **Bundle Size**: ~200KB (uncompressed, v0.1.1)
|
||||
- **Lines of Code**: ~3,500 (well-documented)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user