# Custom @ibgib UI Component Framework

This directory houses the custom `@ibgib` UI microframework. Rather than using conventional state-binding engines, this system leverages the native ibgib protocol and its Merkle-DAG content-addressing structure to orchestrate a unique, timeline-driven reactivity. Component instances are implemented as native HTML5 Web Components (Shadow DOM) and bind directly to evolutionary `ibGib` timelines, resolving, mutating, and updating state in sync with the DLT storage.

For the internal architectural details and class reference, see [ARCHITECTURE.md](file:///c:/Users/billm/antigravity/ibgib/libs/web-gib/src/ui/component/ARCHITECTURE.md).

For a helper agentic skill, see the [ibgib-create-component folder](file:///c:\Users\billm\antigravity\ibgib\.agents\skills\ibgib-create-component)

---

## 💡 Overview

Unlike standard frameworks (React, Vue, etc.) that bind to a local central store, `@ibgib` components are linked to a backing `ibgib` timeline.
- The component uses the timeline's genesis address (its immutable `tjpGib`) as its identifier.
- The component subscribes to the metaspace's local pubsub/event bus.
- When mutations or new frames occur on the timeline, the component automatically evolves, synchronizes its state, and redraws itself.

The protocol itself is a universe-sized address spacetime that allows truly distributed data to drive the reactivity.

### The Component Framework Parts

1. **`IbGibComponentService` (Registry & Router)**: A central registry and router singleton. When navigating, it matches route pathing, selects the appropriate component factory meta, and injects the instantiated element into the target DOM.
2. **`IbGibDynamicComponentMetaBase` (Factory Meta)**: A metadata blueprint specifying the component's HTML tag name, routing regex, and containing the `createInstance` factory method.
3. **`IbGibDynamicComponentInstanceBase` (UI Component)**: The concrete custom element inheriting from `HTMLElement` (i.e. web component). It wraps the backing data in a `LiveProxyIbGib` and automatically triggers lifecycle/render hooks on timeline updates.
4. **`IbGibFormInstanceBase` (Form Handler)**: An intermediate base class extending the instance base, adding status reporting, button load toggling, and validation for behavior specific to forms.

---

## 📦 Built-In Component Suites

`@ibgib/web-gib` exports standard, pre-built web component suites that downstream applications can register and consume out-of-the-box.

### 🔑 Keystone Identity Component Suite

The Keystone Identity suite provides a complete, decentralized identity management interface powered by ibgib's unique keystone architecture (domain keys, sync delegate keys, KDF genesis, and timeline verification).

| Component Tag Name | Class | Description |
| :--- | :--- | :--- |
| **`ibgib-identity-header`** | `IdentityHeaderComponentMeta` / `Instance` | Compact header bar widget displaying active identity avatar, username, dropdown selection, and quick-access management buttons. |
| **`ibgib-identity-manager`** | `IdentityManagerComponentMeta` / `Instance` | Full tab-based identity management panel with settings persistence (`Settings_IdentityManager`), identity tabs, and creation/import triggers. |
| **`ibgib-keystone-creator`** | `KeystoneCreatorComponentMeta` / `Instance` | Interactive form for local KDF key derivation, domain keystone genesis, and sync delegate generation. |
| **`ibgib-keystone-details`** | `KeystoneDetailsComponentMeta` / `Instance` | Deep-dive inspector for individual keystones, showing pool configs, SSO provider linkage, secret reveal, and timeline scrubber. |
| **`ibgib-keystone-scrubber`** | `KeystoneScrubberComponentMeta` / `Instance` | Timeline scrubber control permitting step-by-step navigation across historical keystone evolution frames. |

### Downstream Suite Registration

Downstream applications can register all 5 Keystone Identity components with a single convenience call during app shell bootstrap:

```typescript
import { registerStandardIdentityComponents } from '@ibgib/web-gib/dist/ui/component/identity/index.mjs';

// Inside app initialization (e.g. ShellService.registerComponents):
await registerStandardIdentityComponents();
```

---

## 🛠️ Downstream Integration Patterns & API Bridge

### 1. REST API Bridge Abstraction (`IbGibApiBridge`)

Components in `@ibgib/web-gib` that make REST API requests (e.g., identity creation, SSO login, keystone evolution sync) interact through an abstracted `IbGibApiBridge` interface rather than hardcoding endpoint URLs.

Downstream apps can extend `IbGibApiBridge` to customize API routing or authentication, registering their bridge instance on startup:

```typescript
import { IbGibApiBridge, setApiBridge } from '@ibgib/web-gib/dist/api/ibgib-api-bridge.mjs';

export class MyAppApiBridge extends IbGibApiBridge {
    // Override base REST methods or base URLs as needed
}

// Register on app startup:
setApiBridge(new MyAppApiBridge());
```

### 2. Common Component Design System (`common-component-styles.css`)

Because Web Components use Shadow DOM encapsulation, shared button, tab, form, and tag styles are housed in `libs/web-gib/src/ui/component/common-component-styles.css`.

When implementing custom components, import `commonComponentStylesCss` and include it in your component's `css` initialization array so your controls automatically inherit standard `@ibgib` aesthetics (`.action-btn`, `.secondary`, `.tab-btn`, `.text-input`, `.status-msg`, `.address-code`):

```typescript
import thisCss from "./my-component.css";
import thisHtml from "./my-component.html";
import commonComponentStylesCss from "../common-component-styles.css";

// Inside Meta.createInstance():
await component.initialize({
    ibGibAddr,
    meta: this,
    html: thisHtml,
    css: [commonComponentStylesCss, thisCss],
});
```

---

## 🛠️ How to Create an IbGib UI Component

### 1. Registering your Component Metadata
To register your component with the shell router, extend `IbGibDynamicComponentMetaBase` and call `componentSvc.registerComponentMeta(new MyComponentMeta())` during your application bootstrap:

```typescript
import { IbGibDynamicComponentMetaBase } from '@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs';
import { getComponentSvc } from '@ibgib/web-gib/dist/ui/component/ibgib-component-service.mjs';

export const MY_COMPONENT_NAME = 'my-custom-component';

export class MyComponentMeta extends IbGibDynamicComponentMetaBase {
    routeRegExp = new RegExp(`^${MY_COMPONENT_NAME}$`);
    componentName = MY_COMPONENT_NAME;

    async createInstance(opts) {
        const component = document.createElement(this.componentName);
        await component.initialize({
            ibGibAddr: opts.ibGibAddr,
            meta: this,
            html: thisHtml,
            css: [commonComponentStylesCss, thisCss],
        });
        return component;
    }
}

// Register with central service
const componentSvc = await getComponentSvc();
await componentSvc.registerComponentMeta(new MyComponentMeta());
```

### 2. Implementing the Component Instance
Your component class extends `IbGibDynamicComponentInstanceBase`. You override `created()` for DOM initialization and `renderUI()` for drawing data:

```typescript
import { IbGibDynamicComponentInstanceBase } from '@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs';
import thisHtml from './my-component.html';
import thisCss from './my-component.css';

export class MyComponentInstance extends IbGibDynamicComponentInstanceBase {
    protected lc = '[MyComponentInstance]';

    // Specify the HTML template and stylesheets
    protected get html() { return thisHtml; }
    protected get css() { return [thisCss]; }

    /** Called once when custom element is connected to the DOM */
    override async created(): Promise<void> {
        // Query elements within this.shadowRoot
        this.elements.myValueEl = this.shadowRoot.getElementById('value-display');

        // Load backing model if ibGibAddr was passed
        if (this.ibGibAddr && !this.ibGib) {
            await this.loadIbGib();
        }
        await this.renderUI();
    }

    /** Called initially, and automatically on backing ibgib updates */
    override async renderUI(): Promise<void> {
        if (!this.ibGib) return;

        // Dynamically update elements
        this.elements.myValueEl.textContent = this.ibGib.data?.value ?? '';
    }
}

customElements.define(MY_COMPONENT_NAME, MyComponentInstance);
```

---

## 📝 Form Handling & Validation UI

When building forms, your component instance should extend **`IbGibFormInstanceBase`** instead of `IbGibDynamicComponentInstanceBase`. This adds powerful built-in validation and status tracking.

### Form Validation API Reference

*   **`validateForm(formSelector?: string): boolean`**
    Checks standard HTML5 constraint validation on all inputs in the form (or shadow root). Reports validity and returns `true` if valid, `false` otherwise.
*   **`validateInputPattern(opts: ValidateInputPatternOpts): boolean`**
    Validates a single input's value against a JS `RegExp`. If invalid, it adds the `.invalid` CSS class to the input element, sets custom error validity messages, and calls `reportValidity()` to trigger standard browser tooltip notifications.
*   **`setLoading(loading: boolean, submitBtnSelector?: string): void`**
    Toggles the `disabled` state on the submit/action button, adding a `data-loading` attribute.
*   **`setStatus(msg: string, type: 'info' | 'success' | 'error', statusAreaId?: string, statusTextId?: string): void`**
    Displays success/error banners on the form page, removing the `.hidden` class from the status area wrapper.

### Validation Example Flow

Here is how to structure form validation and clear state flags as the user types:

#### HTML Template (`my-form.html`)
```html
<div id="container">
    <div id="status-area" class="hidden">
        <p id="status-text"></p>
    </div>

    <form id="my-form">
        <label for="input-username">Username</label>
        <input type="text" id="input-username" required />

        <button type="submit" id="btn-submit">Submit</button>
    </form>
</div>
```

#### CSS Stylesheet (`my-form.css`)
```css
/* Styling input fields marked as invalid */
input.invalid {
    border: 2px solid var(--clr-danger, #ff4444);
    box-shadow: 0 0 5px rgba(255, 68, 68, 0.5);
    outline: none;
}

.status-msg.error {
    color: var(--clr-danger, #ff4444);
    font-weight: 500;
}
.status-msg.success {
    color: var(--clr-success, #00c851);
}
```

#### TypeScript Implementation (`my-form.mts`)
```typescript
import { IbGibFormInstanceBase } from '@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs';
import { KEYSTONE_USERNAME_REGEXP } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';

export class MyFormInstance extends IbGibFormInstanceBase {

    override async created(): Promise<void> {
        this.elements.inputUsername = this.shadowRoot.getElementById('input-username');
        this.elements.btnSubmit = this.shadowRoot.getElementById('btn-submit');

        this.initHandlers();
    }

    private initHandlers() {
        this.shadowRoot.getElementById('my-form').addEventListener('submit', (e) => {
            e.preventDefault();
            this.handleSubmit();
        });

        // CRITICAL: Clear the invalid CSS class as the user starts typing
        this.elements.inputUsername.addEventListener('input', () => {
            this.elements.inputUsername.classList.remove('invalid');
        });
    }

    private async handleSubmit() {
        // 1. Perform custom Regexp validation
        const isValidUsername = this.validateInputPattern({
            input: this.elements.inputUsername,
            pattern: KEYSTONE_USERNAME_REGEXP,
            errorMessage: "Username must be 1-63 characters (alphanumeric, dots, hyphens, underscores).",
            required: true
        });

        if (!isValidUsername) {
            this.setStatus("Please correct the invalid fields.", "error");
            return;
        }

        // 2. Toggle Loading State
        this.setLoading(true, '#btn-submit');
        this.setStatus("Submitting username...", "info");

        try {
            // ... perform business logic / space evolution ...
            this.setStatus("Form successfully submitted!", "success");
        } catch (err) {
            // ...
        } finally {
            this.setLoading(false, '#btn-submit');
        }
    }
}
```

---

## 🗂️ Parent / Child Tab Components

For complex views that act as shells containing other interactive component tabs (e.g. `ibgib-projects` or `ibgib-identity-manager`), the framework provides specialized parent classes:

*   **`IbGibDynamicComponentInstanceBase_Parent<TIbGib, TElements, TChildInfo>`**:
    Base class coordinating child injection, child components mapping, and activation of a specific backing child `ibgib` address.
*   **`IbGibDynamicComponentInstanceBase_ParentOfTabs<TSettings, TIbGib, TElements, TChildInfo>`**:
    Extends the Parent base class to integrate tab state persistence using `SettingsWithTabs` (e.g. saving which child tabs are open and which one is active).

### Core Tab Integration Steps

1.  **Define Tab Info**: Define a tab interface that extends `ChildInfoBase<TComponent>`:
    ```typescript
    interface MyTabInfo extends ChildInfoBase<IbGibDynamicComponentInstance<any, any>> {
        // Add custom metadata if needed (e.g. component cache, lens modes)
    }
    ```
2.  **Parent Class Configuration**: Extend `IbGibDynamicComponentInstanceBase_ParentOfTabs` and define `settingsType` and `getLoadedChildInfo()`:
    ```typescript
    export class MyParentComponent extends IbGibDynamicComponentInstanceBase_ParentOfTabs<
        Settings_MyParent, IbGib_V1, MyElements, MyTabInfo
    > {
        protected get settingsType() { return SettingsType.myParent; }

        protected async getLoadedChildInfo({ addr, ibGib }) {
            // Check if tab info exists in this.childInfos, else instantiate
            // the child component using componentSvc.getComponentInstance
        }

        protected async addChild({ addr, ibGib }) {
            // Create tab button element, attach listener, append to DOM, and return it
        }

        protected async removeTabBtn({ tabInfo }) {
            // Remove tab button element from DOM
        }
    }
    ```
3.  **Dynamic Delegate Tab Navigation**: 
    To allow deep links or delegate views (e.g. clicking a delegate key to open its details in a temporary sub-tab), have the child component dispatch a bubbling Custom Event:
    ```typescript
    this.dispatchEvent(new CustomEvent('ibgib-view-details-request', {
        detail: { addr: delegateAddr },
        bubbles: true,
        composed: true
    }));
    ```
    The parent component listens to this event in `created()` and triggers `activateIbGib({ addr })` to dynamically open and activate the sub-tab.
