# el-ui Framework Specification

Version 0.1.0 — April 2026

---

## Overview

el-ui is a frontend framework where state is an Engram graph and reactivity is spreading activation.

Every framework answers the same question differently: **what re-renders when state changes?**

| Framework | Reactivity model |
|-----------|-----------------|
| React | Virtual DOM diffing |
| Vue | Dependency tracking (Proxy-based) |
| Svelte | Compile-time analysis |
| **el-ui** | **Spreading activation over a state graph** |

The core insight: treating component state as a graph of related nodes, and using the same spreading activation algorithm as the Engram knowledge engine to determine what to update. Reactivity is not declared, tracked, or diffed — it is activated and propagated, exactly as associative memory works in biological neural networks.

---

## 1. Activation-Based Reactivity

### 1.1 The Model

Every piece of state in an el-ui application is a **node** in an in-browser Engram graph. Nodes have:

- `id` — UUID, the node's identity
- `type` — `'state'`, `'route'`, or user-defined
- `name` — the state variable name
- `content` — the current value
- `importance` — a salience weight `[0, 1]`, boosted when the node is activated

Nodes are connected by **edges** with a `weight` `[0, 1]` and a `relation` label.

When state changes (via `setState()`), spreading activation runs from the changed node:

```
strength = parent_strength × edge.weight × target.importance
```

This is multiplicative, matching the Engram core engine (`engram-core/src/activation.rs`). Every factor must be non-trivial for a path to propagate — weak edges, dormant nodes, and irrelevant connections die immediately. Components subscribed to nodes in the resulting **activation surface** update. Everything else stays still.

### 1.2 Why Multiplication, Not Addition

Addition allows many weak signals to accumulate into false relevance — an observation from the Engram architecture. The brain's associative memory is conjunctive: a path requires ALL its links to be strong. Multiplication enforces this. If any factor is near zero, the path dies.

### 1.3 Pruning

Paths with activation strength below `PRUNE_THRESHOLD` (default: `0.01`) are cut. This prevents exponential blowup in large state graphs and models the brain's attention filter.

### 1.4 Importance Boost

When a node is updated, its importance increases by 0.1 (capped at 1.0). Recently-changed state is more salient — it activates more easily in future propagation. This mirrors long-term potentiation: frequently-used pathways become lower-resistance.

---

## 2. Component Definition Syntax

Components are defined in `.el` files (the same extension as el source). A component is a specialized el module.

### 2.1 Structure

```
component ComponentName {
    props {
        // optional prop declarations
    }

    state {
        // optional state declarations
    }

    fn methodName(param: Type) -> ReturnType {
        // method body
    }

    template {
        // HTML template
    }
}
```

All four sections (`props`, `state`, methods, `template`) are optional. Components with no template render an empty string.

### 2.2 Props

Props are inputs from the parent component. They are read-only inside the component.

```
props {
    label: String               // required
    variant: String = "primary" // optional, with default
    disabled: Bool = false      // boolean with default
    onClick: Fn() -> Void       // function prop
}
```

Prop types: `String`, `Int`, `Float`, `Bool`, `Fn(...) -> T`, `[T]`, `T?`.

### 2.3 State

State declarations create nodes in the component's Engram graph.

```
state {
    count: Int = 0
    label: String = "hello"
    active: Bool = false
}
```

Each state variable becomes:
1. A node in `this._graph` (seeded at construction time)
2. A reactive binding: changing the value via `setState()` triggers activation

### 2.4 Methods

Methods are `fn` definitions inside the component body. They have access to the component's current state and can call `setState()`.

```
fn increment() -> Void {
    count = count + 1
}
```

State assignments in method bodies (`count = count + 1`) are compiled to `setState('count', count + 1)` calls.

---

## 3. Template Syntax

### 3.1 Interpolation

Embed any expression with `{expr}`:

```
<h1>{count}</h1>
<p>{"Hello, " + name + "!"}</p>
<span>{active ? "on" : "off"}</span>
```

### 3.2 Event Binding

Bind DOM events with `on:event={handler}`:

```
<button on:click={() => count = count + 1}>+</button>
<input on:input={(e) => value = e.target.value} />
<div on:mouseenter={() => hovered = true} on:mouseleave={() => hovered = false} />
```

Supported events: `click`, `input`, `change`, `mouseenter`, `mouseleave`, `keydown`, `keyup`, `keypress`, `focus`, `blur`, `submit`, `mousedown`, `mouseup`, `dblclick`, `contextmenu`.

The compiler emits `data-el-{event}` attributes. The renderer binds handlers at mount time and rebinds after each patch.

### 3.3 Dynamic Attributes

```
<div class={active ? "btn-active" : "btn"}></div>
<a href={currentUrl}></a>
```

### 3.4 Static Attributes

```
<div class="container"></div>
<input type="text" />
```

### 3.5 Boolean Attributes

```
<button disabled={isDisabled}>Submit</button>
<input checked={isChecked} />
```

Boolean attributes emit the attribute name if the expression is truthy, nothing if falsy.

### 3.6 Component Usage

Components are invoked by their name (uppercase first letter). Props are passed as attributes:

```
<Button label="Click me" variant="primary" onClick={() => handleClick()} />
<Counter />
<UserCard name={currentUser.name} />
```

Lowercase tags are HTML elements. Uppercase tags are el-ui components.

### 3.7 Conditional Rendering — `{#if}`

```
{#if condition}
    <div>shown when true</div>
{/if}

{#if loggedIn}
    <Dashboard />
{:else}
    <Login />
{/if}
```

The condition is any JavaScript-compatible boolean expression.

### 3.8 List Rendering — `{#each}`

```
{#each items as item}
    <li>{item}</li>
{/each}

{#each users as user}
    <UserCard name={user.name} />
{/each}
```

The items expression must evaluate to an array.

### 3.9 Semantic Activation — `{#activate}` (The Novel Construct)

```
{#activate "recent premium subscribers" as results}
    <UserCard name={results.name} />
{/activate}
```

This is el-ui's distinguishing feature. `{#activate}` runs a semantic query against the application's state graph at render time. The query string is a natural language description. The runtime:

1. Searches the graph for nodes whose content or name matches the query.
2. Sorts results by their activation score (importance × text match score).
3. Renders the template body once for each result, binding the result as `results`.

In v0.1, the search is string-based. In future versions, it will use embedding vectors and cosine similarity — the same semantic search engine as the Engram knowledge database. This means state queries become conceptual rather than structural: instead of `items.filter(i => i.type === 'premium')`, you write `{#activate "premium subscribers"}` and the activation surface finds semantically matching nodes.

---

## 4. The State Graph

### 4.1 Graph Structure

Each component instance owns an Engram graph (`this._graph`). State nodes are seeded at construction time. The graph is shared between a component and its children (they operate on the same activation surface).

### 4.2 Node Types

| Type | Description |
|------|-------------|
| `'state'` | Component state variable |
| `'route'` | Router path node |
| User-defined | Any custom node type via `this._graph.seed()` |

### 4.3 Seeding Nodes

```javascript
const nodeId = graph.seed({ type: 'state', name: 'count', content: 0, importance: 0.5 });
```

Returns a UUID string that permanently identifies the node.

### 4.4 Connecting Nodes

```javascript
graph.connect(fromId, toId, { weight: 0.8, relation: 'derived' });
```

Connects two nodes with a directed edge. Higher weight = stronger activation path. Use this to encode semantic relationships between state (e.g., a derived value has an edge from its source).

### 4.5 Subscribing to Activation

```javascript
const unsubscribe = graph.subscribe(nodeId, (node) => {
    console.log('node activated:', node.content);
});
// Later:
unsubscribe();
```

### 4.6 Semantic Search

```javascript
const results = graph.search("recent items", "state");
// Returns array of { id, name, content, importance, score } sorted by score
```

---

## 5. The `{#activate}` Construct — Semantic State Queries

### 5.1 Philosophy

Traditional reactivity systems force you to structure your state queries around your storage schema. If you need "all premium users with recent activity", you write:

```javascript
users.filter(u => u.isPremium && u.lastActive > threshold)
```

This is structural — it knows the shape of your data.

el-ui's `{#activate}` is semantic — it knows the *meaning* of your query:

```
{#activate "premium users with recent activity" as users}
```

The activation engine finds nodes that semantically match the query, regardless of their exact structure.

### 5.2 Compilation

`{#activate "query" as results}` compiles to:

```javascript
${(this._graph.search("query") || []).map((results) => `...template...`).join('')}
```

### 5.3 Future: Full Semantic Activation

In a future version connected to an Engram database, `{#activate}` will:
1. Embed the query string into the same vector space as node embeddings.
2. Seed activation at the closest semantic nodes.
3. Spread outward via the graph's weighted edges.
4. Return all nodes above the activation threshold.

This produces a truly associative query: finding nodes that are *semantically related* to the query, not just textually matching.

---

## 6. Router — Graph-Based Routing

### 6.1 Overview

Routes are nodes in the application graph. Navigation activates the target route node. Components subscribed to routing update automatically via spreading activation.

### 6.2 Usage

```javascript
import { Router, mount } from './el-ui.js';
import { Home, About, NotFound } from './app.js';

const graph = component._graph;
const router = new Router(graph, {
    '/': Home,
    '/about': About,
    '*': NotFound,
});

// Navigate programmatically
router.navigate('/about');

// Get current component
const CurrentPage = router.currentComponent();

// Subscribe to route changes
const unsub = router.subscribe((path) => console.log('navigated to', path));
```

### 6.3 Route Node Connections

The router connects parent routes to child routes via edges (weight: 0.8, relation: `'subroute'`). Navigating to `/about/team` activates the `/about` route node as well (activation spreads upward through the subroute chain).

### 6.4 Path Matching

1. Exact match: `/about` matches `/about`
2. Prefix match (longest wins): `/about` matches `/about/team`
3. Wildcard: `'*'` catches all unmatched paths

---

## 7. Plugin API

### 7.1 Custom Node Types

Extend the graph with application-specific node types:

```javascript
import { mount } from './el-ui.js';
import { App } from './app.js';

const component = mount(App, '#app');

// Add a custom "user session" node
const sessionNodeId = component._graph.seed({
    type: 'session',
    name: 'currentUser',
    content: { id: '123', name: 'Alice' },
    importance: 0.9,
});

// Connect it to a state node for reactive updates
component._graph.connect(sessionNodeId, component._stateNodes['userId'], {
    weight: 1.0,
    relation: 'owns',
});
```

### 7.2 Custom Activation Sources

Trigger activation from any node, not just state updates:

```javascript
import { spreadActivation } from './el-ui.js';

// Run a custom activation query
const results = spreadActivation(graph, [myNodeId], {
    maxDepth: 4,
    limit: 10,
    pruneThreshold: 0.05,
});

results.forEach(({ nodeId, strength, node }) => {
    console.log(`${node.name}: ${strength.toFixed(3)}`);
});
```

### 7.3 Component Lifecycle Extension

```javascript
class MyComponent extends Component {
    constructor(props) {
        super(props);
        // Seed custom nodes
        this._externalData = this._graph.seed({
            type: 'external',
            name: 'fetchedData',
            content: null,
            importance: 0.7,
        });
    }

    onMount() {
        // Side effects after first render
        fetch('/api/data')
            .then(r => r.json())
            .then(data => {
                this._graph.update(this._externalData, data);
            });
    }

    render() {
        const data = this._graph.get(this._externalData)?.content;
        return data ? `<div>${data.title}</div>` : `<div>Loading...</div>`;
    }
}
```

---

## 8. Compilation Pipeline

### 8.1 Overview

```
source.el  →  [lexer]  →  [parser]  →  [codegen]  →  output.js
```

### 8.2 Lexer (`crates/el-ui-compiler/src/lexer.rs`)

The lexer is a single-pass, O(n) tokenizer. It is context-sensitive: when it encounters the `template` keyword followed by `{`, it switches to template mode, producing template-specific tokens:

| Token | Description |
|-------|-------------|
| `HashIdent(kw)` | `{#if}`, `{#each}`, `{#activate}` |
| `SlashIdent(kw)` | `{/if}`, `{/each}`, `{/activate}` |
| `ColonIdent(kw)` | `{:else}` |
| `OnColon(event)` | `on:click`, `on:input`, etc. |
| `SelfClose` | `/>` |
| `CloseTag(name)` | `</div>` |
| `RawText(s)` | Inline expression text, text content |

### 8.3 Parser (`crates/el-ui-compiler/src/parser.rs`)

Hand-written recursive descent. Produces `Vec<Component>`. Each component is parsed as:

1. `component Name {` — component declaration
2. `props { ... }` — prop definitions (optional)
3. `state { ... }` — state definitions (optional)
4. `fn method(...) -> Type { ... }` — methods (optional, multiple)
5. `template { ... }` — template tree (optional)

The template is parsed as a tree of `TemplateNode` values. HTML elements, component references, interpolations, and block directives are all represented as nodes in this tree.

### 8.4 Code Generator (`crates/el-ui-compiler/src/codegen.rs`)

The code generator transforms the AST into a JavaScript ES2022 module. For each component:

1. Emits a class extending `Component` from the runtime.
2. In the constructor: seeds state nodes into `this._graph`, subscribes to activation events.
3. Emits `setState()`, which calls `this._graph.update()` to trigger activation.
4. Emits a `render()` method returning a template literal string.
5. Translates state assignments in methods: `count = count + 1` → `this.setState('count', count + 1)`.
6. Translates template interpolations: `{count}` → `${count}`.
7. Emits event handlers as `data-el-{event}` attributes for the renderer to bind.

### 8.5 CLI

```bash
# Compile a single .el file
el-ui-compiler App.el -o app.js

# Default output: same name, .js extension
el-ui-compiler App.el   # produces App.js
```

---

## 9. Production Build — Quantum-Sealed via engram-crypto

The production build pipeline follows the el sealed artifact format:

```bash
# 1. Compile .el to .js
el-ui-compiler App.el -o app.js --target prod

# 2. Seal the JavaScript bundle
ENGRAM_SEAL_KEY=my-deploy-key el seal app.js -o app.sealed
```

The sealed artifact is an `ENGRAM01` sealed bundle (same format as the el production target):

```
Offset  Size    Field
──────  ──────  ─────────────────────────────────────────
0       8       Magic: b"ENGRAM01"
8       2       Format version: u16 big-endian (currently 1)
10      *       JSON: { algorithm_id, signature, encapsulated_key, nonce, ciphertext }
```

AES-256-GCM encryption. The key is derived from the deployment binding (environment variable, machine fingerprint, or none). Without the key, the bundle is indistinguishable from random bytes. No static analysis tool can extract the application logic, queries, or API keys from a sealed bundle.

**Why "quantum-sealed":** AES-256 is quantum-resistant at 128-bit quantum security (Grover's algorithm provides only a quadratic speedup). The `algorithm_id` field is forward-compatible with ML-KEM when it stabilizes.

### 9.1 The `{#activate}` Query is Protected

A key benefit of production sealing: `{#activate}` query strings are embedded in the compiled bundle and encrypted with the application logic. Proprietary semantic queries (which encode business logic about how your application understands its data) are invisible to competitors who decompile your application.

---

## 10. Runtime Size Target

The `dist/el-ui.js` runtime targets **under 15KB minified and gzipped**. As of v0.1:

| Module | Purpose | ~Size |
|--------|---------|-------|
| `graph.js` | Engram graph (nodes, edges, activation, search, subscribe) | ~3KB |
| `activation.js` | Standalone activation utilities | ~1.5KB |
| `renderer.js` | DOM patching, event binding | ~2KB |
| `router.js` | Graph-based routing | ~1.5KB |
| `index.js` | Component base class, `mount()`, re-exports | ~1KB |

Total: ~9KB source, ~4KB minified+gzipped (estimated).

---

## 11. Versioning and Compatibility

el-ui follows semantic versioning.

- **v0.1.x** — Initial release. Full re-render on state change. String-based `{#activate}` search.
- **v0.2.x** — Targeted DOM patching (patch only nodes in the activation surface). `{#activate}` with embedding-based semantic search.
- **v0.3.x** — ML-KEM sealed artifacts. Engram database integration for compile-time semantic type checking of `{#activate}` queries.
- **v1.0.0** — Stable API. Full production sealing. LSP integration with spreading activation autocomplete.

---

## 12. Relationship to el

el-ui `.el` files share the `.el` extension with el source files. Components are specialized el modules — in a future version, an `.el` file can mix component definitions with el type definitions, constants, and utility functions in a single compilation unit.

The spreading activation algorithm in `graph.js` and `activation.js` faithfully mirrors `engram-core/src/activation.rs`:
- Same BFS-based traversal
- Same multiplicative strength formula
- Same pruning threshold semantics
- Same winner-take-most rule (strongest path to each node wins)

The in-browser graph is a lightweight implementation without Engram's full embedding vector machinery. In production, a WASM-compiled Engram core can replace the JavaScript graph entirely, enabling true semantic activation with cosine similarity over embedding vectors.
