# Architecture

C0ckp1t is a zero-build Vue 3 framework. A site can be a plain static website, can call HTTP or WebSocket **backends**, and can mount remote applications (**islands**) served by a backend. All `.vue` Single File Components are compiled at runtime by [vue3-sfc-loader](https://github.com/FranckFreiburger/vue3-sfc-loader) -- no Webpack, Vite, or any build step is required. ES module [import maps](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) provide bare specifier resolution directly in the browser.

This document covers the system architecture, module responsibilities, communication protocols, and key design patterns.

---

## Table of Contents

- [High-Level Architecture](#high-level-architecture)
- [Application Bootstrap Flow](#application-bootstrap-flow)
- [Site, Backends and Islands](#site-backends-and-islands)
- [Core Modules](#core-modules)
- [Runtime SFC Compilation](#runtime-sfc-compilation)
- [WebSocket Protocol](#websocket-protocol)
- [Connection Lifecycle](#connection-lifecycle)
- [State Management](#state-management)
- [Routing](#routing)
- [Component Library](#component-library)
- [Application Shell](#application-shell)
- [Module Dependency Graph](#module-dependency-graph)
- [Directory Structure](#directory-structure)

---

## High-Level Architecture

The system is organized in three layers: the **Browser** runtime, the **Framework Core**, and an optional **Server** backend.

```mermaid
block-beta
  columns 1

  block:browser["Browser"]
    columns 3
    A["index.html\nImport Map\nconfig.mjs"]
    B["Global.mjs\n(Orchestrator)"]
    C["Vue 3 App\nVue Router\nPageMain.vue"]

    block:site["Site (always)"]
      columns 2
      D["api.http\nHttpClient"]
      E["api.site\nfetch resolver"]
    end

    block:backends["Backends (opt-in, lazy)"]
      columns 2
      F["HttpBackend\nexec / exec2 over HTTP"]
      G["WsBackend\nexec / exec2 over WebSocket"]
    end

    block:islands["Islands (opt-in, lazy)"]
      columns 2
      H["Island\nstatic folder"]
      I["ServerIsland\nC0ckp1t server"]
    end

    block:services["Services"]
      columns 5
      J["VueUtils\nSFC Loader"]
      K["Theme\nStyling"]
      L["NotifyUtils\nToasts"]
      M["Stash\nIndexedDB"]
      N["Documentation\n(opt-in)"]
    end

    block:wsstack["WebSocket Stack (lazy)"]
      columns 3
      O["Connection\nXState FSM"]
      P["WsClient\nRxJS Subject"]
      Q["AuthNState\nState Machine"]
    end
  end

  browser -- "HTTP (JSON, NDJSON) / WebSocket (msgpack)" --> server

  block:server["Server"]
    columns 2
    R["Static files\n+ HTTP API"]
    S["RPC endpoints\n/exec /exec2 /socket"]
  end
```

**Key characteristics:**

- **Zero-build** -- No compilation step. The browser loads ES modules directly and compiles `.vue` files at runtime.
- **Import maps** -- Bare specifiers like `"vue"`, `"GlobalStore"`, `"rxjs"` are resolved to local files or CDN URLs via the HTML `<script type="importmap">`.
- **Three opt-in layers** -- *Site* (static website), *Backend* (a named HTTP or WebSocket server with `exec`/`exec2`), *Island* (a backend mounted at `/<id>` with its own routes, files and sidebar tree). A static site downloads none of the WebSocket code.
- **Binary protocol** -- WebSocket communication uses [MessagePack](https://msgpack.org/) for efficient binary serialization. HTTP backends use JSON and NDJSON.

---

## Application Bootstrap Flow

The application starts from `index.html`, which loads CSS, defines the import map, and runs the bootstrap script:

```mermaid
sequenceDiagram
    participant HTML as index.html
    participant Config as config.mjs
    participant Log as Logging.mjs
    participant GS as Global.mjs
    participant CU as ConfigUtils
    participant VU as VueUtils
    participant BE as Backend (HttpBackend / WsBackend)
    participant IS as Island / ServerIsland

    HTML->>Config: import config
    Config->>CU: (imports defaultVueComponents / devRoutes)
    Config-->>HTML: config object (routes, backends, islands)

    HTML->>Log: initLogger(config)
    HTML->>GS: api.init("app-default", config)

    activate GS
    GS->>CU: validateAppConfig(config)
    GS->>GS: createApp() + createRouter()
    GS->>GS: Register 35+ async components
    GS->>VU: transformRoutes(routes)
    GS->>GS: app.mount("#app-default")
    Note over GS: a static site is complete here
    GS->>GS: for each config.backends → api.registerBackend()
    GS->>BE: import() → new Backend(id, config) → init()
    GS->>GS: for each config.islands → api.registerIsland()
    GS->>IS: import() → new Island(api, config, backend)
    GS->>IS: island.init()
    IS->>BE: connect / exec('/user'), exec('/', ['infoNode'])
    BE-->>IS: user context + node tree
    IS->>GS: insertRoutes('/<id>/...')
    deactivate GS
```

**Step-by-step:**

1. **`index.html`** loads CSS (Bootstrap, FontAwesome, highlight.js, KaTeX) and defines the import map mapping bare specifiers to local files. It imports the single app config from `config.mjs`.
2. **`config.mjs`** is a single, explicit plain config object (no factory, no merge). A static site needs only `routes`, `components` and `appMainComponent`. Optional: `nav`, `backends`, `islands`.
3. **`Logging.mjs`** initializes `loglevel` with per-module log levels from config.
4. **`GlobalStore.api.init()`** (`core/Global.mjs`) bootstraps the Vue application:
   - Validates config via `validateAppConfig()`
   - Creates the Vue 3 app instance and Vue Router (hash mode by default)
   - Registers 35+ reusable components as global async components
   - Transforms route configs into lazy-loaded Vue Router routes
   - Mounts the app to the `#app-default` DOM element
   - Registers each entry of `config.backends` with `api.registerBackend()`
   - Registers each entry of `config.islands` with `api.registerIsland()`
5. **`api.registerBackend(id, config)`** loads the transport class on demand (`net/HttpBackend.mjs` or `net/WsBackend.mjs`). This is the first time rxjs / xstate / msgpack are downloaded.
6. **`api.registerIsland(config)`** creates an `Island` or `ServerIsland` around a backend, stores it in `store.r[id]` and calls `island.init()`. A `ServerIsland` connects, loads the user context and the server's node tree, and adds routes under `/<id>/`. A failing island is logged and does not stop the others.

---

---

## Site, Backends and Islands

Three concepts, each opt-in on top of the previous one:

```mermaid
flowchart TB
    subgraph Site["Site (always)"]
        HTTP["api.http<br/>HttpClient for the page origin"]
        SITE["api.site<br/>fetch based file resolver"]
    end
    subgraph Backends["Backends (config.backends)"]
        HB["HttpBackend<br/>exec / exec2 over HTTP"]
        WB["WsBackend<br/>exec / exec2 over WebSocket"]
    end
    subgraph Islands["Islands (config.islands)"]
        IS["Island (static)<br/>routes + nav from config"]
        SI["ServerIsland<br/>userContext, infoNode, node routes"]
    end
    HB -. extends .-> WB
    IS --> HB
    SI --> WB
    SI -. or .-> HB
```

| Concept | Config | What it is | Loaded |
|---------|--------|------------|--------|
| **Site** | `routes`, `components` | The page's own origin. Files are fetched with `fetch`. `api.http.getJson('/api/x')` for plain HTTP calls. | always (a few KB) |
| **Backend** | `backends: {id: {...}}` | A named server with the same API over HTTP or WebSocket: `exec`, `exec2`, `getJson`, ... | on demand |
| **Island** | `islands: [{id, kind, backend}]` | A backend **mounted at `/<id>`**: its routes, its `.vue`/`.md` files and its sidebar tree. | on demand |

Rule of thumb: a static website needs only the Site. If your code calls a server, declare a backend. If the server also serves *pages* that should appear in your app, mount it as an island.

### One result shape

Every transport call returns the same envelope and never throws:

```javascript
{ isOk: true,  result: <data>,          status?: 200 }
{ isOk: false, result: "<error text>",  status?: 404, stack?: [...] }
```

`api.http.getJson()`, `backend.exec()`, `backend.getJson()` and `island.getText()` all use it. The only function that throws is `readFile()`, because vue3-sfc-loader expects its resolver to throw.

### Backend API (`HttpBackend`, `WsBackend`)

| Member | Description |
|--------|-------------|
| `exec(endpoint, args, bytes)` | `Promise<{isOk, result}>` |
| `exec2(endpoint, args, bytes)` | `Observable<Uint8Array>` raw chunks |
| `exec2Result(...)` / `exec2ResultBinary(...)` | `Observable<{type: START\|STDOUT\|STDERR\|STDIELD\|END, ...}>`, stdout as text / bytes |
| `getText/getJson/getBinary/postJson(path)` | plain HTTP against `backend.url` (relative paths) |
| `readFile(path)` / `readBinary(path)` | file access used by islands and the SFC loader |
| `state.isReady`, `await ready()` | usable for requests; HTTP is always ready, WebSocket after login |
| `connect()` / `disconnect()` | WebSocket only (no-ops for HTTP) |
| `capabilities` | `{stream, push}` |

`WsBackend extends HttpBackend`: `exec`/`exec2` use the socket, while the cookie login, `getJson`, `/c0ckp1t/` files go over HTTP to the same server. See [Island-Interface.md](./Island-Interface.md) for the HTTP wire format.

### Island API (stable contract used by node applications)

`storeMain.r[id]` is the island registry. Applications in `content/actions/<node>/www` rely on:

`exec`, `exec2`, `exec2Result`, `exec2ResultBinary`, `state.isReady`, `await ready()`, `store.context`, `store.root`, `store.selectedNode`, `routeByEndpoint`, `selectNode`, `selectDefaultDashboard`, `getText`, `getBinary`, `getJson`, `postJson`, and (`ServerIsland`) `state.workflowTable`, `findNodeByWfId`, `findNodeByEndpoint`. The island delegates RPC to its backend with explicit methods.

### Registration

```javascript
await api.registerBackend('api', {transport: 'http', url: '/api'})
const be = api.backend('api')                   // throws [BACKEND_NOT_FOUND] with the registered ids
await api.registerIsland({id: 'admin', kind: 'server',
                          backend: {transport: 'ws', url: 'https://host:1995'}})
api.getIsland('admin')                          // throws [ISLAND_NOT_FOUND] with the registered ids
api.source('admin')                             // island, or the Site when called with no id
```

An island id becomes the first URL segment, so ids that collide with a top level folder (`core`, `components`, `css`, `js_ext`, `docs`, `_c0ckp1t`, ...) or an existing route are rejected.

---

## Core Modules

### Orchestration

| Module | File | Purpose |
|--------|------|---------|
| **Global** | `core/Global.mjs` (import name `GlobalStore`) | Creates the Vue app and router, holds the backend (`store.backends`) and island (`store.r`) registries, and provides the public `api`. |
| **Site** | `core/Site.mjs` | The page's own origin with the island read API (`api.site`). |
| **ConfigUtils** | `core/ConfigUtils.mjs` | Config helpers only: `defaultVueComponents()`, `devRoutes()`/`devNav(docsPath?)` (opt-in framework pages), `validateAppConfig()`, `validateBackendConfig()`, `validateIslandConfig()`, `findHostnamePortProtocol()`. |
| **Layout** | `core/Layout.mjs` | Sidebar / alerts state of the default page layout. |

### SFC Engine

| Module | File | Purpose |
|--------|------|---------|
| **VueUtils** | `core/VueUtils.mjs` | Configures `vue3-sfc-loader` with module cache, file resolver, and lazy-loading. Exports `loadModule()`, `loadModuleFromText()`, and `transformRoutes()`. |

### Transport (`core/net/`)

| Module | File | Purpose |
|--------|------|---------|
| **Http** | `core/net/Http.mjs` | `HttpClient`: fetch with timeout and credentials, the single `{isOk, result, status}` contract. No dependencies. |
| **ExecResult** | `core/net/ExecResult.mjs` | Shared helpers: argument validation, base64, `Exec2Error`, mapping of exec2 events. |
| **HttpBackend** | `core/net/HttpBackend.mjs` | RPC over HTTP (`POST /exec`, `POST /exec2` NDJSON). |
| **WsBackend** | `core/net/WsBackend.mjs` | Extends `HttpBackend`; `exec`/`exec2` over the WebSocket. |
| **WsProtocol** | `core/net/WsProtocol.mjs` | Packet codes (`Code`, `Code2`) and msgpack `toBinary`/`fromBinary`. |
| **WsClient** | `core/net/ws/WsClient.mjs` | RxJS `WebSocketSubject` wrapper. Binary msgpack, ID-keyed message routing, status observables. |
| **Connection** | `core/net/ws/Connection.mjs` | XState-driven connection manager: cookie fetch -> WS connect -> ready. `execute()` has a timeout and always resolves. |
| **AuthNState** | `core/net/ws/AuthNState.mjs` | XState v5 state machine definition for the connection lifecycle. |
| **EventBus** | `core/EventBus.mjs` | App-wide `mitt` bus for server push events. |
| **WsUtils** | `core/WsUtils.mjs` | Compatibility re-export of `EventBus` + `WsProtocol` for node applications. The core never imports it. |
| **WsLogUtils** | `core/WsLogUtils.mjs` | WebSocket traffic logger. Circular buffer (50 entries) of exec/exec2 packets. |

### Islands (`core/islands/`)

| Module | File | Purpose |
|--------|------|---------|
| **Island** | `core/islands/Island.mjs` | A backend mounted at `/<id>`: routes, files, nav tree, `ready()`. Works for static folders over HTTP. |
| **ServerIsland** | `core/islands/ServerIsland.mjs` | C0ckp1t server protocol: `userContext`, `infoNode` tree, per-node `config.json` routes, admin fallbacks, workflow table. |

### Utilities

| Module | File | Purpose |
|--------|------|---------|
| **JsUtils** | `core/JsUtils.mjs` | General utilities: SHA256/SHA1 hashing, string operations (`kebabCase`, `capitalize`, `ellipsis`), sorting, date formatting, `sleep()`, `uid()`, path manipulation. |
| **Logging** | `core/Logging.mjs` | Logging facade wrapping `loglevel` with `loglevel-plugin-prefix`. Per-module log level configuration. |
| **Theme** | `core/Theme.mjs` | Dynamic theming engine. CSS variable overrides, 27+ Bootswatch themes, light/dark mode toggle, custom color editor. |
| **Stash** | `core/Stash.mjs` | IndexedDB key-value store via `idb-keyval`. Isolated databases per store name (`c0ckp1t-${name}`). |
| **NotifyUtils** | `core/pages/notify/NotifyUtils.mjs` | Notification queue. Reactive notification list with GOOD/BAD/INFO types, auto-dismiss timers. |

### Documentation feature (`core/pages/documentation/`, opt-in)

Everything the markdown documentation needs lives in one folder, so sites that do not use it never download it. `Documentation.vue` stays at `core/pages/Documentation.vue` because node applications import that path.

| Module | File | Purpose |
|--------|------|---------|
| **Documentation** | `core/pages/Documentation.vue` | The page: loads `.md` from a source, renders with `x-markdown`, editor, TOC, navigation. Every option is a prop. |
| **DocUtils** | `core/pages/documentation/DocUtils.mjs` | No imports. Path helpers (`normalizePath`, `resolvePath`, `toRemotePath`), `canWrite`/`writeText`, `waitFor`, and the `config.docs` block: `DOCS_DEFAULTS`, `validateDocsConfig`, `docsRoutes`. |
| **md-toc / toc-list** | `core/pages/documentation/md-toc.vue`, `toc-list.vue` | Table of contents built from the markdown headings. |
| **Content** | `core/pages/documentation/Content.mjs` | Article index with an IndexedDB cache (sha1 invalidation). Not wired up yet. |

Enable it with `config.docs` (see [Configuration.md](./Configuration.md#documentation-opt-in)). `Global.init` then does `import('./pages/documentation/DocUtils.mjs')`, validates the block and puts `docsRoutes()` in front of `config.routes`.

How documents are read (`apiMain.source(instanceId)`, no caching by the feature):

| Source | Text (`getText`) | Images | Save |
|--------|------------------|--------|------|
| Site (static web server / CDN) | `fetch` (SPA `index.html` fallback = 404) | URL handed to `<img>` (browser HTTP cache) | no |
| Static island (HTTP backend) | `backend.readFile` (`fetch`) | `getBinary` -> blob URL (revoked on navigation) | no |
| Server island (WebSocket) | `exec('/sys/resolver', ['read', path])` | `exec2` stream -> blob URL | `accessLevel <= 500` |

---

## Runtime SFC Compilation

The zero-build architecture relies on `vue3-sfc-loader` to compile `.vue` files in the browser. The `VueUtils.mjs` module configures this with a custom resolver, module cache, and lazy-loading system.

### File Resolution Strategy

When the SFC loader needs a file, it calls `options.getFile(path)`:

```mermaid
flowchart TD
    A["getFile(path)"] --> B{"cached source?"}
    B -- yes --> Z["use cached text"]
    B -- no --> C{"first path segment is a<br/>registered island id?"}
    C -- yes --> D["island.resolver(rest of path)<br/>HTTP fetch or /sys/resolver"]
    C -- no --> E["api.site.resolver(path)<br/>plain fetch"]
```

Paths of the form `/<islandId>/...` belong to that island; everything else is a plain file of the site (or a CDN URL).

### Module Cache and Lazy Loading

The `moduleCache` is pre-seeded with all framework modules so any `.vue` SFC can import them by bare specifier:

```javascript
// These are available in any SFC without configuration:
import { store, api } from "GlobalStore";
import { ok, nok } from "JsUtils";
import { api as notify } from "NotifyUtils";
import { reactive } from "vue";
```

Heavy libraries are lazy-loaded using `Object.defineProperty` getters on the module cache. They are only fetched when an SFC actually imports them:

| Library | Loaded When |
|---------|-------------|
| `rxjs`, `rxjs/operators` | An SFC imports RxJS |
| `xstate` | An SFC imports XState |
| `mitt` | An SFC imports mitt |
| `idb-keyval` | An SFC imports idb-keyval |
| `wavesurfer` | The `<x-sound>` component is used |
| `msgpack`, `WsProtocol`, `WsUtils`, `WsLogUtils` | An SFC imports them (node applications import `{fromBinary, fromByteArray, eventBus}` from `WsUtils`) |
| `Stash` | An SFC imports it (Connections page) |

### How SFC Loading Works

1. A route is navigated to, triggering `() => loadModule("/path/to/Component.vue")`
2. `vue3-sfc-loader` calls `getFile(path)` to fetch the `.vue` source
3. The resolver sends the path to an island or to the site (see above)
4. The `<template>`, `<script>`, and `<style>` blocks are compiled in-browser
5. The resulting component is cached in `moduleCache` to avoid recompilation
6. `<style>` blocks are injected into `document.head`

### Loading SFCs from Strings

`loadModuleFromText(sfcText, name)` compiles raw SFC strings without network fetches. This enables server-pushed or user-authored components to be compiled on the fly using virtual paths (`virtual://${name}.vue`).

---

## WebSocket Protocol

WebSocket communication uses a binary MessagePack protocol with typed packet codes.

### Packet Structure

```javascript
{
  id:       number,    // Random 32-bit integer, correlates request/response
  code:     string,    // Packet type (Code2 enum)
  endpoint: string,    // Server endpoint path (e.g., "/sys/resolver")
  args:     Array,     // Endpoint arguments
  bytes:    Uint8Array // MessagePack-encoded payload (nullable)
}
```

### Packet Codes (Code2)

| Code | Direction | Purpose |
|------|-----------|---------|
| `EXEC_REQ` | Client -> Server | Request/response RPC call |
| `EXEC_RESP` | Server -> Client | Response to `EXEC_REQ` |
| `EXEC2_REQ` | Client -> Server | Start a streaming RPC |
| `EXEC2_RESP` | Server -> Client | Stream data packet |
| `EXEC3_REQ` | Client -> Server | Start bidirectional stream |
| `EXEC3_PUSH` | Client -> Server | Push data to open stream |
| `EXEC3_RESP` | Server -> Client | Stream response data |
| `EXEC3_CLOSE` | Client -> Server | Close the stream |
| `ACCEPT` | Server -> Client | Stream accepted |
| `COMPLETE` | Server -> Client | Stream completed |
| `EVENT` | Server -> Client | Server-pushed event |
| `ERROR` | Server -> Client | Error response |

### RPC Patterns

![rpc-patterns width=600x800](./assets/rpc-patterns.png)

### RPCResult Envelope

All `exec()` responses use a standard envelope:

```javascript
// Success
{ isOk: true,  result: <data> }

// Failure
{ isOk: false, result: <error message>, stack: [...] }
```

Created via the `ok()` and `nok()` helper functions from `JsUtils.mjs`. HTTP calls add a `status` field.

### exec2 Stream Deserialization

`exec2Result()` pipes `exec2()` through a mapper that deserializes binary packets into structured objects:

| Type | Meaning |
|------|---------|
| `START` | Stream started |
| `END` | Stream ended |
| `STDOUT` | Standard output data |
| `STDERR` | Standard error data |
| `STDIELD` | Structured data (yield) |

---

## Connection Lifecycle

Each WebSocket backend manages its connection through an XState state machine defined in `AuthNState.mjs`. The `Connection` class (`Connection.mjs`) drives this machine and syncs state to Vue reactivity.

![connection-lifecycle width=600x800](./assets/connection-lifecycle.png)

### Connection States

| State | Description |
|-------|-------------|
| **IDLE** | Initial state. Waiting for `connect` event. |
| **FETCHING_COOKIE** | POSTs session metadata (uniqueId, password, user agent, screen size) to `http(s)://<host>:<port>/cookie` to establish a session cookie. 10-second timeout. |
| **CONNECTING_WS** | Opens a WebSocket to `ws(s)://<host>:<port>/<endpoint>?connectionId=<islandId>` via `WsClient`. 10-second timeout. |
| **CONNECTED** | Transient state, immediately transitions to READY. |
| **READY** | Fully connected and authenticated. RPC calls are available. A `ServerIsland` then loads its user context and node tree. |
| **DISCONNECTED** | Connection was closed (clean or not). Can reconnect via `connect`. |
| **FAILED** | Error occurred during cookie fetch or WS connect. Error details stored in `context.error`. Can retry via `connect` (increments retry counter). |

### Connection Components

| Component | File | Role |
|-----------|------|------|
| **Connection** | `core/net/ws/Connection.mjs` | Manages the XState actor, provides `connect()`/`disconnect()`, and implements `execute()`/`execute2()`/`execute3()` RPC methods. |
| **WsClient** | `core/net/ws/WsClient.mjs` | Low-level RxJS `WebSocketSubject` wrapper. Routes incoming packets by `id` to the correct subscriber. Handles binary msgpack deserialization. |
| **AuthNState** | `core/net/ws/AuthNState.mjs` | Pure XState state machine definition. Defines states, transitions, and error assignment actions. |

---

## State Management

C0ckp1t uses a **distributed reactive store** pattern instead of Vuex or Pinia. Each module exports a `{ store, api }` pair:

- **`store`** -- A `Vue.reactive({})` object holding the module's state
- **`api`** -- A plain object with methods that read/mutate the store

![state-management width=1000x800](./assets/state-management.png)

### Why Not Vuex/Pinia?

This is a deliberate design decision aligned with the zero-build philosophy. Since there is no build step, the framework avoids any tool that requires compile-time plugins or transformations. `Vue.reactive()` provides the same reactivity guarantees without additional dependencies.

### Store Pattern

```javascript
// In a module (e.g., NotifyUtils.mjs):
export const store = reactive({ notifications: [] });
export const api = {
  good(msg) { store.notifications.push({ type: "GOOD", msg }); },
  bad(msg)  { store.notifications.push({ type: "BAD", msg }); },
};

// In a component:
import { store, api } from "NotifyUtils";
// store.notifications is reactive — templates auto-update
// api.good("Success!") adds a notification
```

---

## Routing

Vue Router is configured in hash mode by default (`createWebHashHistory()`), configurable via `config.vueRouterModeIsHash`.

### Route Sources

Routes come from two sources:

1. **Config routes** (`config.routes`) -- the only place a page of the site is declared. `config.docs` adds the documentation routes (`/docs` by default) in front of them. `...devRoutes(frameworkBase)` adds the framework pages (Connections, Cache, Traffic, Notifies, component showcase) under `/_c0ckp1t/`; leave it out for a production site.
2. **Island routes** (`api.insertRoutes()`) -- added when an island is registered: from `island.config.routes` (static island) or from the server's node tree (`ServerIsland`).

### Lazy-Loaded Route Components

All route components are lazy-loaded and compiled on demand via `transformRoutes()`:

```javascript
// Config format:
{ path: "/about", location: "/pages/About.vue" }

// Transformed to Vue Router format:
{ path: "/about", component: () => loadModule("/pages/About.vue") }
```

The `.vue` file is only fetched and compiled when the user first navigates to that route.

### Dynamic Route Insertion

When a `ServerIsland` connects, it loads each node's `config.json` from the server (via `/sys/resolver`) and adds routes with `router.addRoute()`. Each island's routes are namespaced under `/<id>/`:

```
/docs/Introduction.md             → Documentation.vue (config.docs, site, plain fetch)
/_c0ckp1t/connections             → Connections.vue (framework page, opt-in)
/admin/osgi/bundles-installed     → node page served by the "admin" island
```

`api.unregisterIsland(id)` removes the island's routes again.

---

## Component Library

C0ckp1t includes 35+ reusable Vue components globally registered as async components. All use Bootstrap 5 styling and support `v-model` where applicable.

### Component Categories

**Form Inputs:** `<x-input>`, `<x-textarea>`, `<x-checkbox>`, `<x-dropdown>`, `<x-color>`, `<x-upload>`, `<x-hidden>`

**Buttons & Toggles:** `<x-button>`, `<ExecButton>`, `<x-check>`, `<x-toggle>`, `<x-toggle3>`

**Data Display:** `<x-label>`, `<x-kv>`, `<x-json>`, `<x-table-open>`, `<x-code>`, `<x-code-slot>`

**Layout & Structure:** `<x-card>`, `<x-card-h>`, `<x-section>`, `<x-collapse>`, `<x-tabs>`, `<x-nav>`

**Advanced:** `<x-markdown>` (markdown-it), `<x-terminal>` (Ace-based terminal), `<x-sound>` (WaveSurfer audio player), `<x-tree>` (recursive tree view), `<FsTree>` (filesystem browser), `<vue3-ace-editor>` (code editor), `<code-mirror>` (CodeMirror 6 editor)

### Naming Convention

- Reusable components use the `x-` prefix in templates (e.g., `<x-input>`)
- Component files use the `x` prefix without hyphen (e.g., `xinput.vue`)
- Non-prefixed components are standalone utilities (e.g., `ExecButton.vue`, `FsTree.vue`)

### Registration

Components are registered in `GlobalStore.api.init()` via `defineAsyncComponent()`:

```javascript
app.component("x-input", defineAsyncComponent(() => loadModule("/components/xinput.vue")));
```

Components are only fetched and compiled when first rendered in a template.

---

## Application Shell

`PageMain.vue` is the root component mounted as `<app-main>`. It provides the application layout:

![app-shell width=300x800](./assets/app-shell.png)




**Key behaviors:**

- **Theme switching** -- Watches `Theme.store.theme` and applies `data-bs-theme` attribute to `<html>` for Bootstrap dark/light mode.
- **Error capture** -- `onErrorCaptured` catches all child component errors and pushes them to `store.componentErrors`, displayed by `PageFallback`.
- **Responsive sidebar** -- When the sidebar is open, `mainContentStyle` shifts the main content area (margin and width adjustments).

### Sidebar (MainOffCanvas)

The sidebar (`main-offcanvas.vue`) uses Bootstrap's offcanvas component. It only exists when `config.showSidebar` is true, `config.nav` is set, or islands are registered. It displays:
- The site's own tree (`config.nav`), if any
- For each island: the connection status header (WebSocket islands only) and its node tree

### Navigation (PageNavigation)

The top navbar (`PageNavigation.vue`) includes:
- Brand/logo with animated logo component
- Navigation items from config
- Theme toggle (light/dark mode)
- Search functionality

---

## Module Dependency Graph

```mermaid
graph TD
    HTML["index.html"]
    CONFIG["config.mjs"]

    HTML --> CONFIG
    HTML --> LOG["Logging.mjs"]
    HTML --> GS["Global.mjs"]

    CONFIG --> CU["ConfigUtils.mjs"]

    GS --> VU["VueUtils.mjs"]
    GS --> CU
    GS --> HTTP["net/Http.mjs"]
    GS --> SITE["Site.mjs"]
    GS -. import on demand .-> HB["net/HttpBackend.mjs"]
    GS -. import on demand .-> WB["net/WsBackend.mjs"]
    GS -. import on demand .-> IS["islands/Island.mjs"]
    GS -. import on demand .-> SI["islands/ServerIsland.mjs"]

    SITE --> HTTP
    VU --> SFC["vue3-sfc-loader"]
    VU --> GS
    VU --> EB["EventBus.mjs"]

    HB --> HTTP
    HB --> RXJS["RxJS"]
    HB --> ER["net/ExecResult.mjs"]
    WB --> HB
    WB --> CONN["net/ws/Connection.mjs"]
    WB --> WP["net/WsProtocol.mjs"]

    IS --> HTTP
    SI --> IS

    CONN --> WC["net/ws/WsClient.mjs"]
    CONN --> AUTH["net/ws/AuthNState.mjs"]
    CONN --> WP
    CONN --> XST["XState"]
    WC --> RXJS
    WC --> EB
    WC --> WLOG["WsLogUtils.mjs"]
    AUTH --> XST

    WP --> MSGPACK["msgpack"]
    EB --> MITT["mitt"]
    NU["NotifyUtils.mjs"] --> EB

    PM["PageMain.vue"] --> GS
    PM --> TH["Theme.mjs"]
    PM --> NU
```

---

## Directory Structure

```
c0ckp1t-webroot/
├── index.html                  # Main entry point (local modules)
├── index-cdn.html              # CDN entry point (jsdelivr)
├── config.mjs                  # Single app config (edit this; routes, backends, islands)
├── config.default.mjs          # Pristine config template (copy to config.mjs)
├── package.json                # npm package
├── style.css                   # Global styles
│
├── core/                       # Framework core
│   ├── Global.mjs              # Central orchestrator (import name: GlobalStore)
│   ├── GlobalStore.mjs         # Deprecated re-export of Global.mjs
│   ├── Layout.mjs              # Default page layout state (sidebar, alerts)
│   ├── Site.mjs                # The page's own origin (api.site)
│   ├── VueUtils.mjs            # Runtime SFC compiler config
│   ├── ConfigUtils.mjs         # Config helpers, validation, devRoutes
│   ├── EventBus.mjs            # App-wide event bus (mitt)
│   ├── WsUtils.mjs             # Compatibility re-export for node applications
│   ├── JsUtils.mjs             # General utilities (ok/nok)
│   ├── WsLogUtils.mjs          # WebSocket traffic logger
│   ├── Logging.mjs             # Logging facade (loglevel)
│   ├── Theme.mjs               # Dynamic theming engine
│   ├── Stash.mjs               # IndexedDB key-value store
│   ├── PageMain.vue            # Root app shell
│   ├── PageNavigation.vue      # Top navbar
│   ├── PageFooter.vue          # Footer
│   ├── PageFallback.vue        # Error display
│   ├── Page404.vue             # 404 page
│   ├── LogoAnimated.vue        # Animated logo SVG
│   ├── main-offcanvas.vue      # Sidebar navigation
│   │
│   ├── net/                    # Transport: HTTP and WebSocket
│   │   ├── Http.mjs            # HttpClient + result contract (no dependencies)
│   │   ├── ExecResult.mjs      # Shared exec helpers
│   │   ├── HttpBackend.mjs     # RPC over HTTP
│   │   ├── WsBackend.mjs       # RPC over WebSocket (extends HttpBackend)
│   │   ├── WsProtocol.mjs      # Packet codes + msgpack
│   │   ├── whenReady.mjs       # await ready() helper
│   │   └── ws/                 # WebSocket client stack
│   │       ├── WsClient.mjs    # RxJS WebSocketSubject wrapper
│   │       ├── Connection.mjs  # XState-driven connection manager
│   │       └── AuthNState.mjs  # State machine definition
│   │
│   ├── islands/                # Remote applications mounted at /<id>
│   │   ├── Island.mjs          # Generic island (static folder over HTTP)
│   │   └── ServerIsland.mjs    # C0ckp1t server protocol
│   │
│   ├── nodes/                  # Sidebar node tree components
│   │   ├── node.vue            # Recursive tree node
│   │   ├── root.vue            # Root node display
│   │   ├── place-holder.vue    # Default placeholder
│   │   └── _api.vue            # API node inspector
│   │
│   ├── sfc/                    # SFC helper components
│   │   ├── code-item.vue       # Code viewer (reads from the site)
│   │   ├── code-item2.vue      # Code viewer (provide/inject)
│   │   ├── scroll-to-top.vue   # Scroll-to-top button
│   │   ├── task.vue            # Task/event display
│   │   └── info-api.vue        # API endpoint inspector
│   │
│   └── pages/                  # Built-in framework pages
│       ├── Documentation.vue   # Markdown documentation viewer (opt-in feature)
│       ├── documentation/      # Everything else of the Documentation feature
│       │   ├── DocUtils.mjs    # Paths, sources, config.docs validation + routes
│       │   ├── md-toc.vue      # Markdown table of contents
│       │   ├── toc-list.vue    # Recursive TOC renderer
│       │   └── Content.mjs     # Article index + IndexedDB cache (not wired up)
│       ├── Connections.vue     # Connection list/management
│       ├── Connection.vue      # Single connection detail
│       ├── Cache.vue           # Cache management
│       ├── Traffic.vue         # WebSocket traffic viewer
│       ├── Notifies.vue        # Notification history
│       ├── About.vue           # About/system info
│       ├── connections/        # Connection sub-pages
│       ├── frontend/           # Component showcase pages
│       ├── traffic/            # Traffic log sub-components
│       └── notify/             # Notification system
│           ├── NotifyUtils.mjs # Notification queue + API
│           └── toast.vue       # Toast display component
│
├── components/                 # 35+ reusable Vue components
│   ├── xinput.vue              # Text input
│   ├── xbutton.vue             # Button with spinner
│   ├── xcard.vue               # Card layout
│   ├── xmarkdown.vue           # Markdown renderer
│   ├── xterminal.vue           # Terminal emulator
│   ├── vue3-ace-editor.vue     # Ace editor wrapper
│   ├── code-mirror.vue         # CodeMirror 6 wrapper
│   ├── ...                     # (see Component Library section)
│   ├── MarkdownUtils.mjs       # Markdown-it configuration
│   ├── Tree.mjs                # Tree data utilities
│   └── AceLoader.mjs           # Ace editor lazy loader
│
├── css/                        # Stylesheets
│   ├── bootstrap.min.css       # Bootstrap 5
│   ├── bootstrap-c0ckp1t.css   # Custom overrides
│   ├── fontawesome/            # FontAwesome CSS
│   └── webfonts/               # Font files
│
├── js_ext/                     # Vendored JS libraries (no npm install)
│   ├── vue.esm-browser.prod.min.js
│   ├── vue-router.esm-browser.prod.js
│   ├── vue3-sfc-loader.esm.js
│   ├── rxjs.esm.mjs            # RxJS
│   ├── xstate.web.mjs          # XState
│   ├── msgpack.mjs             # MessagePack
│   ├── mitt.mjs                # Event emitter
│   ├── idb-keyval-6.2.2.mjs    # IndexedDB wrapper
│   ├── highlight/              # Highlight.js + languages
│   ├── ace-editor/             # Ace editor + modes/themes
│   └── markdown/               # markdown-it + plugins
│
├── examples/                   # Example projects
│   ├── minimum/                # Minimal starter
│   ├── blog/                   # Blog example
│   ├── galore/                 # Feature showcase
│   ├── landing-page/           # Landing page examples
│   └── c0ckp1t-demo/           # Full demo app
│
├── tests/                      # Unit tests (Vitest)
│   └── src/
│       ├── JsUtils.test.mjs    # Utility function tests
│       └── DocUtils.test.mjs   # Documentation feature tests
│
├── c0ckp1t-server/             # Development server
│   └── src/
│       ├── c0ckp1t-server.mjs  # Express + CLI (commander)
│       └── WsServer.mjs        # WebSocket server
│
└── docs/                       # Documentation
    ├── Introduction.md
    ├── Architecture.md         # (this document)
    ├── Getting-Started.md
    ├── Configuration.md
    ├── Creating-SFC.md
    ├── Components.md
    ├── Theming.md
    └── Articles/               # Deep-dive articles
```

---

## External Dependencies

All external libraries are vendored in `js_ext/` -- no `npm install` is needed for the frontend.

| Library | Purpose |
|---------|---------|
| **Vue 3** | Reactive UI framework |
| **Vue Router** | Client-side routing |
| **vue3-sfc-loader** | Runtime `.vue` file compiler |
| **RxJS** | Reactive WebSocket streams |
| **XState** | Connection state machine |
| **MessagePack** | Binary WebSocket serialization |
| **mitt** | Event bus |
| **idb-keyval** | IndexedDB key-value store |
| **Bootstrap 5** | CSS framework + JS components |
| **FontAwesome** | Icon library |
| **highlight.js** | Code syntax highlighting |
| **Ace Editor** | Code editor |
| **CodeMirror 6** | Code editor (alternative) |
| **markdown-it** | Markdown rendering |
| **KaTeX** | LaTeX math rendering |
| **WaveSurfer.js** | Audio waveform visualization |
| **loglevel** | Logging with level control |
| **Moment.js** | Date formatting |
