# IbGib Dynamic UI Component Architecture

This document describes the design of the `@ibgib` UI microframework in `web-gib` and details the base classes, routing service, proxy-backed model synchronization, and form handling base class.

---

## 📖 Component System Architecture Summary

*   **Overall Approach**: A timeline-driven UI microframework built on standard HTML5 Web Components (Shadow DOM). Rather than relying on standard local state stores, UI rendering, routing, and lifecycle transitions are powered by the native ibgib protocol and Merkle-DAG content-addressing timelines.
*   **Registry & Routing**: `IbGibComponentService` acts as a central registry. When navigating, the service filters registered `IbGibDynamicComponentMeta` factories by path and regex, executing `fnHandleRoute()` to select and inject the matching component instance.
*   **Backing Model Sync via Proxy**:
    *   Components maintain a live binding to their backing data via a **`LiveProxyIbGib`**.
    *   When the local space or metaspace registers a new mutation/frame on the timeline, the proxy fires `contextUpdated$` and `newContextChild$` (if it's a parent ibgib with children that were rel8d/unrel8d).
    *   The base class subscribes to these streams, automatically updating `this.ibGibAddr` and invoking `renderUI()` to redraw the component.

---

## 🏛️ Core Classes Reference

### 1. `IbGibComponentService`
A singleton service (`componentSvc`) that acts as a central registry and router for all shell components.
*   **Registration**: Components register their factory metadata blueprints using `registerComponentMeta(meta)`.
*   **Resolution**: When routing, `getComponentInstance()` iterates through registered components, first testing `routeRegExp` and then calling `fnHandleRoute()` for fine-grained content validation.
*   **Injection**: `inject(parentEl, instance)` handles clearing old views, appending the new custom element, and handling injection errors.

### 2. `IbGibDynamicComponentMetaBase`
The abstract factory blueprint class representing a component type's metadata and routing constraints.
*   **Properties**:
    *   `componentName`: The custom HTML tag name (e.g. `keystone-creator`).
    *   `routeRegExp`: Prefilter regex matching current route path.
    *   `fnHandleRoute`: Detailed route matching lambda.
    *   `bootstrapPromise`: Deferment promise coordinating with the global ibgib bootstrap state.
*   **Methods**:
    *   `createInstance({ path, ibGibAddr })`: Creates the concrete `HTMLElement` instance.

### 3. `IbGibDynamicComponentInstanceBase`
The base `HTMLElement` custom element class that coordinates DOM lifecycles and `ibgib` context bindings.
*   **Backing Proxy & Subscription**:
    *   `loadIbGib()`: Pulls the initial backing data record from the space, wrapping it in a `LiveProxyIbGib`.
    *   `handleContextUpdated()`: Reacts to `contextUpdated$` events, synchronizing `this.ibGibAddr`.
    *   `handleNewContextChild()`: Reacts to child timeline changes (e.g., additions or removals of related nodes).
*   **Local Coupling**:
    *   `initSettings()`: Manages local-only configurations (like active identities or layout preferences) coupled to a shared timeline using mapping indices without mutating the domain data.
*   **Lifecycle Hooks**:
    *   `connectedCallback()`: Attaches the Shadow DOM template, style arrays, and triggers `created()`.
    *   `created()`: Abstract hook called after the DOM is ready for component-specific setup and render.
    *   `renderUI()`: Triggers UI redrawing, resolving unique color palettes (`getDeterministicColorInfo`) based on the backing `ibgib`'s hash.
    *   `disconnectedCallback()` / `disconnected()`: Handles event listener unbinding and proxy resource cleanup when removed from the DOM.

### 4. `IbGibFormInstanceBase`
The intermediate abstract base class specifically designed to handle interactive form operations, loading states, and validation rules.
*   **Subclass Responsibility**: Inherited by components containing inputs and action buttons (e.g., `KeystoneCreatorComponentInstance`).
*   **Standard Methods**:
    *   `setLoading(loading, selector)`: Automatically toggles submit/generate button disabled state and updates text.
    *   `setStatus(msg, type, areaId, textId)`: Manages error/success status elements and toggles the `hidden` class.
    *   `validateForm(formSelector)`: Inspects all form controls within the shadow root and runs HTML5 Constraint Validation checks.
    *   `validateInputPattern(opts)`: Validates a single input/textarea element's value against a custom RegExp pattern, adding/removing the `invalid` CSS class and updating validity messages.
    *   `dispatchShellMessage(message, level)`: Dispatches bubbling, composed Custom Events (`ibgib-ui-message`) to notify the parent Shell.

---

## 📋 Implementation Plan: `IbGibFormInstanceBase`

```mermaid
graph TD
    HTMLElement --> IbGibDynamicComponentInstanceBase
    IbGibDynamicComponentInstanceBase --> IbGibFormInstanceBase
    IbGibFormInstanceBase --> KeystoneCreatorComponentInstance
```

### 1. Class Interface Implementation
Introduce `IbGibFormInstanceBase` inside `libs/web-gib/src/ui/component/ibgib-dynamic-component-bases.mts`:

```typescript
export abstract class IbGibFormInstanceBase<TIbGib extends IbGib_V1 = IbGib_V1, TElements = any>
    extends IbGibDynamicComponentInstanceBase<TIbGib, TElements> {

    protected setLoading(loading: boolean, submitBtnSelector = '#btn-submit, #btn-generate'): void {
        if (!this.shadowRoot) return;
        const btn = this.shadowRoot.querySelector(submitBtnSelector) as HTMLButtonElement | null;
        if (btn) {
            btn.disabled = loading;
            // Optionally toggle an attribute or standard class
            if (loading) {
                btn.setAttribute('data-loading', 'true');
            } else {
                btn.removeAttribute('data-loading');
            }
        }
    }

    protected setStatus(
        msg: string,
        type: 'info' | 'success' | 'error',
        statusAreaId = 'status-area',
        statusTextId = 'status-text'
    ): void {
        if (!this.shadowRoot) return;
        const area = this.shadowRoot.getElementById(statusAreaId);
        const text = this.shadowRoot.getElementById(statusTextId);

        if (text) {
            text.textContent = msg;
            text.className = `status-msg ${type}`;
        }
        if (area) {
            if (msg) {
                area.classList.remove('hidden');
            } else {
                area.classList.add('hidden');
            }
        }
    }

    protected dispatchShellMessage(message: string, level: 'info' | 'success' | 'warning' | 'error'): void {
        this.dispatchEvent(new CustomEvent(EVENT_IBGIB_UI_MESSAGE, {
            detail: { message, level, source: this.tagName.toLowerCase() },
            bubbles: true,
            composed: true
        }));
    }

    protected validateForm(formSelector = 'form'): boolean {
        if (!this.shadowRoot) return true;
        const form = this.shadowRoot.querySelector(formSelector) as HTMLFormElement | null;
        if (form) {
            return form.checkValidity();
        }

        // Fallback: check all individual inputs in shadowRoot
        const inputs = this.shadowRoot.querySelectorAll('input, select, textarea');
        let isValid = true;
        for (const input of Array.from(inputs)) {
            const el = input as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
            if (!el.checkValidity()) {
                isValid = false;
                el.reportValidity();
                break; // Stop on first invalid field
            }
        }
        return isValid;
    }

    protected validateInputPattern({
        input,
        pattern,
        errorMessage,
        required = true,
    }: {
        input: HTMLInputElement | HTMLTextAreaElement | null;
        pattern: RegExp;
        errorMessage: string;
        required?: boolean;
    }): boolean {
        if (!input) return true;
        const val = input.value.trim();

        if (!val && !required) {
            input.classList.remove('invalid');
            input.setCustomValidity('');
            return true;
        }

        const isValid = pattern.test(val);
        if (!isValid) {
            input.classList.add('invalid');
            input.setCustomValidity(errorMessage);
            input.reportValidity();
        } else {
            input.classList.remove('invalid');
            input.setCustomValidity('');
        }
        return isValid;
    }
}
```

### 2. Integration
*   Refactor `KeystoneCreatorComponentInstance` in `space-gib` to extend `IbGibFormInstanceBase` instead of `IbGibDynamicComponentInstanceBase`.
*   Replace its local duplicate validation and loading methods with inherited calls.
