@ibgib/space-gib 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/IMPLEMENTATION.md +9 -13
  3. package/README.md +7 -0
  4. package/dist/client/bootstrap.mjs +1 -1
  5. package/dist/client/bootstrap.mjs.map +1 -1
  6. package/dist/client/chunk-ANGVYAEK.mjs +42 -0
  7. package/dist/client/chunk-ANGVYAEK.mjs.map +7 -0
  8. package/dist/client/chunk-IRGFDQRD.mjs +1920 -0
  9. package/dist/client/chunk-IRGFDQRD.mjs.map +7 -0
  10. package/dist/client/index.html +103 -5
  11. package/dist/client/index.mjs +1 -1
  12. package/dist/client/script.mjs +1 -1
  13. package/dist/client/style.css +466 -61
  14. package/dist/respec-gib.node.mjs +5 -0
  15. package/dist/server/server.mjs +533 -233
  16. package/dist/server/server.mjs.map +2 -2
  17. package/package.json +6 -6
  18. package/src/client/AUTO-GENERATED-version.mts +1 -1
  19. package/src/client/components/identity-header/IMPLEMENTATION.md +45 -0
  20. package/src/client/components/identity-header/identity-header.css +74 -0
  21. package/src/client/components/identity-header/identity-header.html +10 -0
  22. package/src/client/components/identity-header/identity-header.mts +361 -0
  23. package/src/client/components/identity-manager/IMPLEMENTATION.md +100 -0
  24. package/src/client/components/identity-manager/identity-manager.css +467 -0
  25. package/src/client/components/identity-manager/identity-manager.html +113 -0
  26. package/src/client/components/identity-manager/identity-manager.mts +767 -0
  27. package/src/client/components/keystone-creator/keystone-creator.css +2 -76
  28. package/src/client/components/keystone-creator/keystone-creator.html +41 -26
  29. package/src/client/components/keystone-creator/keystone-creator.mts +178 -41
  30. package/src/client/dev-tools/base-tools.mts +252 -0
  31. package/src/client/dev-tools/common.mts +217 -0
  32. package/src/client/dev-tools/phase-1.mts +156 -0
  33. package/src/client/dev-tools/phase-2.mts +143 -0
  34. package/src/client/dev-tools/phase-3.mts +189 -0
  35. package/src/client/dev-tools/phase-4-1.mts +197 -0
  36. package/src/client/dev-tools/phase-4-10.mts +884 -0
  37. package/src/client/dev-tools/phase-4-2.mts +388 -0
  38. package/src/client/dev-tools/phase-4-3.mts +391 -0
  39. package/src/client/dev-tools/phase-4-4.mts +374 -0
  40. package/src/client/dev-tools/phase-4-5.mts +376 -0
  41. package/src/client/dev-tools/phase-4-6.mts +273 -0
  42. package/src/client/dev-tools/phase-4-7.mts +399 -0
  43. package/src/client/dev-tools/phase-4-8.mts +430 -0
  44. package/src/client/dev-tools/phase-4-9.mts +398 -0
  45. package/src/client/dev-tools/phase-4.mts +1302 -0
  46. package/src/client/dev-tools.mts +52 -1194
  47. package/src/client/index.html +103 -5
  48. package/src/client/style.css +466 -61
  49. package/src/client/ui/shell/space-gib-shell-constants.mts +0 -2
  50. package/src/client/ui/shell/space-gib-shell-service.mts +82 -10
  51. package/src/common/common-constants.mts +0 -0
  52. package/src/common/keystone-policies.json +40 -43
  53. package/src/common/keystone-policies.mts +3 -5
  54. package/src/server/path-helper.respec.mts +99 -94
  55. package/src/server/serve-gib/README.md +9 -0
  56. package/src/server/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +1 -1
  57. package/src/server/serve-gib/handlers/api/keystone/keystone-get.respec.mts +1 -1
  58. package/src/server/serve-gib/handlers/ws/sync-upgrade-handler-base.mts +31 -3
  59. package/src/server/serve-gib/handlers/ws/ws-helper.mts +73 -45
  60. package/dist/client/chunk-2KJC5XKE.mjs +0 -31
  61. package/dist/client/chunk-2KJC5XKE.mjs.map +0 -7
  62. package/dist/client/chunk-QNIXTRFO.mjs +0 -235
  63. package/dist/client/chunk-QNIXTRFO.mjs.map +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ibgib/space-gib",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "description": "ibgib storage and provider service — SaaS at ibgib.space",
6
6
  "scripts": {
@@ -12,11 +12,11 @@
12
12
  "build:test": "tsc -p tsconfig.test.json"
13
13
  },
14
14
  "dependencies": {
15
- "@ibgib/core-gib": "*",
16
- "@ibgib/helper-gib": "*",
17
- "@ibgib/ts-gib": "*",
18
- "@ibgib/web-gib": "*",
19
- "@ibgib/node-gib": "*"
15
+ "@ibgib/core-gib": "^0.1.62",
16
+ "@ibgib/helper-gib": "^0.0.36",
17
+ "@ibgib/ts-gib": "^0.5.32",
18
+ "@ibgib/web-gib": "^0.0.48",
19
+ "@ibgib/node-gib": "^0.0.6"
20
20
  },
21
21
  "engines": {
22
22
  "node": ">=20.0.0"
@@ -8,4 +8,4 @@
8
8
  /**
9
9
  * Version of space-gib, auto-updated by the build process.
10
10
  */
11
- export const AUTO_GENERATED_VERSION = '0.0.3';
11
+ export const AUTO_GENERATED_VERSION = '0.0.5';
@@ -0,0 +1,45 @@
1
+ # Identity Header Component - Implementation Spec
2
+
3
+ This document details the layout, styling, and behavior for the `identity-header` component (`ibgib-identity-header`). It is rendered inside the app shell's header and acts as a status indicator and shortcut route for the user's active identity.
4
+
5
+ ## Requirements
6
+
7
+ 1. **State-Driven Display**:
8
+ - **Active Identity Mode**: If an active Domain Identity (Keystone) address exists in settings and is resolved in the local space, the component displays an identity pill showing a truncated version of the address.
9
+ - **Inactive/Default Mode**: If no identity is resolved, the component renders a prominent "Create Identity" button.
10
+ 2. **Interactive Routing**:
11
+ - Clicking either the identity pill or the fallback "Create Identity" button calls `SpaceGibShellService.showIdentityManager()` to load the full `<ibgib-identity-manager>` component in the center view panel.
12
+ 3. **Agent-Themeable Styles**:
13
+ - The layout must be highly compact to fit inside a standard horizontal header panel.
14
+ - All styling must prioritize valid theme CSS variables (e.g., `--button-background-color-base`, `--tab-text-color-active`) to remain dynamic and styleable by runtime agents.
15
+ 4. **Shared Settings Scope**:
16
+ - It is initialized with the virtual `ROOT_ADDR` and manually binds to the `"keystones"` index `ibgib`.
17
+ - It initializes its coupled settings via `this.initSettings()`. Since both this component and the manager share the `"keystones"` index backing model, they automatically resolve the exact same coupled settings `ibgib` and settings timeline, reactively syncing active identity selection changes.
18
+ - For detailed data schema, see [identity-manager IMPLEMENTATION.md](../identity-manager/IMPLEMENTATION.md).
19
+
20
+ ---
21
+
22
+ ## Component Structure
23
+
24
+ * **[identity-header.mts](file:///c:/Users/billm/antigravity/ibgib/apps/space-gib/src/client/components/identity-header/identity-header.mts)**: Element definition, lifecycle events, active identity resolution, shell service navigation call.
25
+ * **[identity-header.html](file:///c:/Users/billm/antigravity/ibgib/apps/space-gib/src/client/components/identity-header/identity-header.html)**: Compact HTML structures for the pill and fallback button.
26
+ * **[identity-header.css](file:///c:/Users/billm/antigravity/ibgib/apps/space-gib/src/client/components/identity-header/identity-header.css)**: Glassmorphic pill styling matching current themes.
27
+
28
+ ---
29
+
30
+ ## Implementation Checklist
31
+
32
+ ### Phase 1: Basic Scaffolding & Setup
33
+ - [x] Scaffold `identity-header.mts`, `.html`, and `.css` files.
34
+ - [x] Register `IdentityHeaderComponentMeta` with `IbGibComponentService`.
35
+ - [x] Mount `<ibgib-identity-header>` inside the header panel in `SpaceGibShellService`.
36
+
37
+ ### Phase 2: Navigation & Identity State
38
+ - [x] Wire up click handlers to trigger center-panel navigation to `ibgib-identity-manager`.
39
+ - [x] Resolve settings and check the `"keystones"` index to render the appropriate active/inactive view mode.
40
+
41
+ ### Phase 3: Visual Polish & Bugs
42
+ - [ ] Ensure the header component has an initial size/button and is visible while the page is initializing.
43
+
44
+ ### Testing Note
45
+ - [ ] **Manual Testing Only**: Visual integration and navigation behavior will be verified manually by the developer to limit token consumption.
@@ -0,0 +1,74 @@
1
+ :host {
2
+ display: inline-block;
3
+ vertical-align: middle;
4
+ }
5
+
6
+ .header-widget-container {
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: flex-end;
10
+ }
11
+
12
+ /* Glassmorphic identity status pill */
13
+ .identity-pill {
14
+ display: flex;
15
+ align-items: center;
16
+ gap: 0.5rem;
17
+ padding: 0.4rem 0.8rem;
18
+ background: var(--tab-background-color-active, rgba(255, 255, 255, 0.1));
19
+ border: 1px solid var(--tab-border-color-active, rgba(80, 250, 123, 0.3));
20
+ border-radius: var(--radius-lg, 20px);
21
+ cursor: pointer;
22
+ transition: all 0.2s ease-in-out;
23
+ }
24
+
25
+ .identity-pill:hover {
26
+ background: rgba(80, 250, 123, 0.15);
27
+ border-color: var(--clr-accent, #50fa7b);
28
+ transform: translateY(-1px);
29
+ box-shadow: 0 2px 8px rgba(80, 250, 123, 0.1);
30
+ }
31
+
32
+ .avatar-circle {
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ width: 24px;
37
+ height: 24px;
38
+ border-radius: 50%;
39
+ font-size: 0.85rem;
40
+ font-weight: 700;
41
+ text-transform: uppercase;
42
+ background: var(--clr-accent, #50fa7b);
43
+ color: #11111b;
44
+ flex-shrink: 0;
45
+ }
46
+
47
+ .address-text {
48
+ font-family: var(--font-sans, sans-serif);
49
+ font-size: 0.85rem;
50
+ color: var(--clr-text-primary, #ffffff);
51
+ font-weight: 500;
52
+ }
53
+
54
+ /* Fallback button when no identity is loaded */
55
+ .action-btn {
56
+ padding: 0.4rem 0.9rem;
57
+ border-radius: 20px;
58
+ font-size: 0.8rem;
59
+ background: transparent;
60
+ border: 1px dashed var(--clr-warn, #ffcc44);
61
+ color: var(--clr-warn, #ffcc44);
62
+ cursor: pointer;
63
+ transition: all 0.2s ease-in-out;
64
+ }
65
+
66
+ .action-btn:hover {
67
+ background: rgba(255, 204, 68, 0.1);
68
+ border-color: var(--clr-warn, #ffcc44);
69
+ box-shadow: 0 0 8px rgba(255, 204, 68, 0.2);
70
+ }
71
+
72
+ .hidden {
73
+ display: none !important;
74
+ }
@@ -0,0 +1,10 @@
1
+ <div id="container" class="header-widget-container">
2
+ <!-- Active Identity Pill -->
3
+ <div id="active-identity-pill" class="identity-pill hidden" title="Active Domain Identity. Click to manage.">
4
+ <div id="identity-avatar" class="avatar-circle">-</div>
5
+ <span id="identity-address-truncated" class="address-text">No Identity</span>
6
+ </div>
7
+
8
+ <!-- Fallback Action Button -->
9
+ <button id="btn-create-identity" class="action-btn hidden" title="No active identity. Click to manage.">No Active Identity</button>
10
+ </div>
@@ -0,0 +1,361 @@
1
+ import thisCss from "./identity-header.css";
2
+ import thisHtml from "./identity-header.html";
3
+ import styleCss from "../../style.css";
4
+
5
+ import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs";
6
+ import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
7
+ import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
8
+ import { ROOT_ADDR } from "@ibgib/ts-gib/dist/V1/constants.mjs";
9
+ import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
10
+ import {
11
+ IbGibDynamicComponentMetaBase, IbGibDynamicComponentInstanceBase,
12
+ } from "@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs";
13
+ import {
14
+ ElementsBase, IbGibDynamicComponentInstance,
15
+ IbGibDynamicComponentInstanceInitOpts,
16
+ } from "@ibgib/web-gib/dist/ui/component/component-types.mjs";
17
+ import { SettingsType } from "@ibgib/web-gib/dist/common/settings/settings-constants.mjs";
18
+ import { Settings_General } from "@ibgib/web-gib/dist/common/settings/settings-types.mjs";
19
+
20
+ import { getComponentCtorArg, getIbGibGlobalThis_SpaceGib } from "../../helpers.web.mjs";
21
+ import { APP_CONFIG } from "../../constants.mjs";
22
+ import { getSpaceGibShellSvc } from "../../ui/shell/space-gib-shell-service.mjs";
23
+ import { devLog } from "../../dev-tools.mjs";
24
+ import { KeystoneService_V1 } from "@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs";
25
+ import { KeystoneIbGib_V1 } from "@ibgib/core-gib/dist/keystone/keystone-types.mjs";
26
+ import { getDeterministicColorInfo } from "@ibgib/web-gib/dist/helpers.mjs";
27
+ import { EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, EVENT_IBGIB_IDENTITY_CHANGED } from "@ibgib/web-gib/dist/ui/ui-constants.mjs";
28
+
29
+ export const IDENTITY_HEADER_COMPONENT_NAME = 'ibgib-identity-header';
30
+
31
+ /**
32
+ * Metadata for the Identity Header component.
33
+ */
34
+ export class IdentityHeaderComponentMeta extends IbGibDynamicComponentMetaBase {
35
+ protected lc: string = `[IdentityHeaderComponentMeta]`;
36
+
37
+ routeRegExp?: RegExp = new RegExp(`^${IDENTITY_HEADER_COMPONENT_NAME}$`);
38
+ componentName = IDENTITY_HEADER_COMPONENT_NAME;
39
+
40
+ constructor() {
41
+ super(getComponentCtorArg());
42
+ if (!customElements.get(this.componentName)) {
43
+ customElements.define(this.componentName, IdentityHeaderComponentInstance);
44
+ }
45
+ }
46
+
47
+ async createInstance({
48
+ path,
49
+ ibGibAddr
50
+ }: {
51
+ path: string;
52
+ ibGibAddr: IbGibAddr;
53
+ }): Promise<IbGibDynamicComponentInstance> {
54
+ const component = document.createElement(this.componentName) as IdentityHeaderComponentInstance;
55
+ await component.initialize({
56
+ ibGibAddr,
57
+ meta: this,
58
+ html: thisHtml,
59
+ css: [styleCss, thisCss],
60
+ });
61
+ return component;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * References to DOM elements within the component's shadow root.
67
+ */
68
+ interface IdentityHeaderElements extends ElementsBase {
69
+ containerEl: HTMLDivElement;
70
+ activeIdentityPillEl: HTMLDivElement;
71
+ identityAddressTruncatedEl: HTMLSpanElement;
72
+ btnCreateIdentityEl: HTMLButtonElement;
73
+ identityAvatarEl: HTMLDivElement;
74
+ }
75
+
76
+ /**
77
+ * Concrete implementation of the Identity Header component.
78
+ */
79
+ export class IdentityHeaderComponentInstance
80
+ extends IbGibDynamicComponentInstanceBase<IbGib_V1, IdentityHeaderElements>
81
+ implements IbGibDynamicComponentInstance<IbGib_V1, IdentityHeaderElements> {
82
+
83
+ protected lc: string = `[IdentityHeaderComponentInstance]`;
84
+ private activeAddr: string | null = null;
85
+ private _onRequestIdentityChange: ((e: any) => void) | null = null;
86
+ private keystoneCache: Map<string, { username: string, description: string }> = new Map();
87
+
88
+ constructor() {
89
+ super();
90
+ }
91
+
92
+ private async getKeystoneDetails(addr: string): Promise<{ username: string, description: string }> {
93
+ const cached = this.keystoneCache.get(addr);
94
+ if (cached) return cached;
95
+
96
+ const metaspace = getIbGibGlobalThis_SpaceGib(APP_CONFIG).metaspace;
97
+ if (!metaspace) return { username: '', description: '' };
98
+ const space = await metaspace.getLocalUserSpace({ lock: false });
99
+ if (!space) return { username: '', description: '' };
100
+
101
+ try {
102
+ const res = await metaspace.get({ addr, space });
103
+ if (res.success && res.ibGibs && res.ibGibs.length > 0) {
104
+ const keystone = res.ibGibs[0] as KeystoneIbGib_V1;
105
+ const keystoneSvc = new KeystoneService_V1();
106
+ const aggregated = await keystoneSvc.getAggregateDetails({
107
+ latestKeystone: keystone,
108
+ metaspace,
109
+ space
110
+ });
111
+ const username = aggregated?.username || aggregated?.profile?.name || aggregated?.name || '';
112
+ const description = aggregated?.description || aggregated?.profile?.description || '';
113
+ const details = { username, description };
114
+ this.keystoneCache.set(addr, details);
115
+ return details;
116
+ }
117
+ } catch (err) {
118
+ console.warn(`Error getting details for keystone ${addr}: ${extractErrorMsg(err)}`);
119
+ }
120
+
121
+ return { username: '', description: '' };
122
+ }
123
+
124
+
125
+ override async initialize(opts: IbGibDynamicComponentInstanceInitOpts): Promise<void> {
126
+ await super.initialize(opts);
127
+ }
128
+
129
+ override async created(): Promise<void> {
130
+ this.elements = {} as IdentityHeaderElements;
131
+
132
+ const shadow = this.shadowRoot!;
133
+ this.elements.containerEl = shadow.getElementById('container') as HTMLDivElement;
134
+ this.elements.activeIdentityPillEl = shadow.getElementById('active-identity-pill') as HTMLDivElement;
135
+ this.elements.identityAddressTruncatedEl = shadow.getElementById('identity-address-truncated') as HTMLSpanElement;
136
+ this.elements.btnCreateIdentityEl = shadow.getElementById('btn-create-identity') as HTMLButtonElement;
137
+ this.elements.identityAvatarEl = shadow.getElementById('identity-avatar') as HTMLDivElement;
138
+
139
+ // Required contentEl mapping
140
+ this.elements.contentEl = this.elements.containerEl;
141
+
142
+ this.initHandlers();
143
+
144
+ // Subscribe to global active identity request changes
145
+ this._onRequestIdentityChange = (e: any) => this.handleRequestIdentityChange(e);
146
+ window.addEventListener(EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, this._onRequestIdentityChange);
147
+
148
+ if (this.ibGibAddr === ROOT_ADDR || !this.ibGib) {
149
+ await this.loadKeystonesIndex();
150
+ } else {
151
+ await this.initSettings();
152
+ }
153
+ await this.renderUI();
154
+ }
155
+
156
+ private async loadKeystonesIndex(): Promise<void> {
157
+ const lc = `${this.lc}[loadKeystonesIndex]`;
158
+ try {
159
+ const metaspace = getIbGibGlobalThis_SpaceGib(APP_CONFIG).metaspace;
160
+ if (!metaspace) { return; }
161
+ const space = await metaspace.getLocalUserSpace({ lock: false });
162
+ if (!space) { return; }
163
+ const keystonesIndex = await metaspace.getSpecialIbGib({
164
+ type: "keystones",
165
+ space,
166
+ initialize: true,
167
+ });
168
+ if (keystonesIndex) {
169
+ this.ibGibAddr = getIbGibAddr({ ibGib: keystonesIndex });
170
+ await this.loadIbGib();
171
+ await this.initSettings();
172
+ }
173
+ } catch (error) {
174
+ console.error(`${lc} ${extractErrorMsg(error)}`);
175
+ }
176
+ }
177
+
178
+ override async disconnected(): Promise<void> {
179
+ if (this._onRequestIdentityChange) {
180
+ window.removeEventListener(EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, this._onRequestIdentityChange);
181
+ }
182
+ }
183
+
184
+ private initHandlers() {
185
+ // Handle clicks: route to the full identity manager
186
+ this.elements!.activeIdentityPillEl.addEventListener('click', () => this.navigateToManager());
187
+ this.elements!.btnCreateIdentityEl.addEventListener('click', () => this.navigateToManager());
188
+ }
189
+
190
+ private navigateToManager() {
191
+ const shellSvc = getSpaceGibShellSvc();
192
+ // Route to center view identity manager
193
+ shellSvc.showIdentityManager();
194
+ }
195
+
196
+ /**
197
+ * Resolves the active identity from settings and verifies it exists in the keystones index.
198
+ */
199
+ private async resolveActiveIdentity() {
200
+ const lc = `${this.lc}[resolveActiveIdentity]`;
201
+ try {
202
+ if (!this.settings) {
203
+ this.activeAddr = null;
204
+ const globalState = getIbGibGlobalThis_SpaceGib(APP_CONFIG);
205
+ globalState.identity = undefined;
206
+ return;
207
+ }
208
+ const generalSettings = await this.getSettings<Settings_General>({
209
+ settingsType: SettingsType.general,
210
+ useCase: 'current',
211
+ });
212
+ const activeAddrInSettings = (generalSettings as any)?.activeIdentityAddr;
213
+
214
+ // Verify it is actually one of the registered keystones in the index relations
215
+ const registeredKeystones = this.ibGib?.rel8ns?.keystone ?? [];
216
+ if (activeAddrInSettings && registeredKeystones.includes(activeAddrInSettings)) {
217
+ this.activeAddr = activeAddrInSettings;
218
+ } else if (registeredKeystones.length > 0) {
219
+ this.activeAddr = registeredKeystones[0];
220
+ } else {
221
+ this.activeAddr = null;
222
+ }
223
+
224
+ // Sync with globalState.identity
225
+ const globalState = getIbGibGlobalThis_SpaceGib(APP_CONFIG) as any;
226
+ if (this.activeAddr) {
227
+ const metaspace = globalState.metaspace;
228
+ if (metaspace) {
229
+ const space = await metaspace.getLocalUserSpace({ lock: false });
230
+ if (space) {
231
+ const res = await metaspace.get({ addr: this.activeAddr, space });
232
+ if (res.success && res.ibGibs && res.ibGibs.length > 0) {
233
+ globalState.identity = res.ibGibs[0];
234
+ }
235
+ }
236
+ }
237
+ } else {
238
+ globalState.identity = undefined;
239
+ }
240
+ } catch (error) {
241
+ console.error(`${lc} ${extractErrorMsg(error)}`);
242
+ this.activeAddr = null;
243
+ const globalState = getIbGibGlobalThis_SpaceGib(APP_CONFIG) as any;
244
+ globalState.identity = undefined;
245
+ }
246
+ }
247
+
248
+ protected override async renderUI(): Promise<void> {
249
+ if (!this.elements) return;
250
+
251
+ // Double check state on render
252
+ await this.resolveActiveIdentity();
253
+
254
+ if (this.activeAddr) {
255
+ this.elements.activeIdentityPillEl.classList.remove('hidden');
256
+ this.elements.btnCreateIdentityEl.classList.add('hidden');
257
+
258
+ const addr = this.activeAddr;
259
+
260
+ // Initial fallback render
261
+ let displayAddr = addr;
262
+ if (displayAddr.length > 14) {
263
+ displayAddr = displayAddr.substring(0, 7) + '...' + displayAddr.substring(displayAddr.length - 5);
264
+ }
265
+ this.elements.identityAddressTruncatedEl.textContent = displayAddr;
266
+ this.elements.identityAvatarEl.textContent = '-';
267
+ this.elements.identityAvatarEl.style.backgroundColor = 'var(--clr-accent)';
268
+ this.elements.identityAvatarEl.style.color = '#11111b';
269
+ this.elements.activeIdentityPillEl.title = `Active Identity: ${addr}\nClick to manage.`;
270
+
271
+ // Load details dynamically
272
+ const details = await this.getKeystoneDetails(addr);
273
+
274
+ // First letter of username, or fallback to first char of address (or 'A')
275
+ let letter = '-';
276
+ if (details.username) {
277
+ letter = details.username.charAt(0).toUpperCase();
278
+ } else {
279
+ const match = addr.match(/[a-zA-Z]/);
280
+ letter = match ? match[0].toUpperCase() : 'I';
281
+ }
282
+ if (!this.elements) { throw new Error(`(UNEXPECTED) this.elements falsy? (E: fd6bfa87c968a38c4860588818c3cd26)`); }
283
+
284
+ this.elements.identityAvatarEl.textContent = letter;
285
+
286
+ // Colorize avatar deterministically based on cryptographic gib
287
+ const colorInfo = getDeterministicColorInfo({ ibGibAddr: addr });
288
+ const bg = colorInfo.tjpColor || colorInfo.punctiliarColor;
289
+ const fg = colorInfo.tjpColorContrast || colorInfo.punctiliarColorContrast;
290
+ this.elements.identityAvatarEl.style.backgroundColor = bg;
291
+ this.elements.identityAvatarEl.style.color = fg;
292
+
293
+ // Set user display name or address in pill
294
+ let displayName = details.username;
295
+ const maxLen = 14;
296
+ if (displayName) {
297
+ if (displayName.length > maxLen) {
298
+ displayName = displayName.substring(0, maxLen - 3) + '...';
299
+ }
300
+ this.elements.identityAddressTruncatedEl.textContent = displayName;
301
+ }
302
+
303
+ // Title with full details
304
+ const titleParts: string[] = [];
305
+ if (details.username) {
306
+ titleParts.push(`User: ${details.username}`);
307
+ }
308
+ if (details.description) {
309
+ const desc = details.description.length > 100
310
+ ? details.description.substring(0, 97) + '...'
311
+ : details.description;
312
+ titleParts.push(`Desc: ${desc}`);
313
+ }
314
+ titleParts.push(`Addr: ${addr}`);
315
+ titleParts.push(`Click to manage.`);
316
+ this.elements.activeIdentityPillEl.title = titleParts.join('\n');
317
+ } else {
318
+ this.elements.activeIdentityPillEl.classList.add('hidden');
319
+ this.elements.btnCreateIdentityEl.classList.remove('hidden');
320
+ }
321
+ }
322
+
323
+ private async handleRequestIdentityChange(e: any) {
324
+ const lc = `${this.lc}[handleRequestIdentityChange]`;
325
+ const addr = e.detail?.activeIdentityAddr;
326
+ if (!addr) return;
327
+ devLog(`${lc} Request to change active identity to: ${addr}`);
328
+ try {
329
+ if (!this.settings) return;
330
+ const generalSettings = await this.getSettings<Settings_General>({
331
+ settingsType: SettingsType.general,
332
+ useCase: 'current',
333
+ }) ?? { type: 'general' };
334
+
335
+ (generalSettings as any).activeIdentityAddr = addr;
336
+
337
+ await this.updateSettings({
338
+ settingsType: SettingsType.general,
339
+ useCase: 'current',
340
+ newSectionInfo: generalSettings,
341
+ });
342
+
343
+ await this.resolveActiveIdentity();
344
+ await this.renderUI();
345
+
346
+ // Dispatch EVENT_IBGIB_IDENTITY_CHANGED
347
+ window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_CHANGED, {
348
+ detail: { activeIdentityAddr: addr },
349
+ bubbles: true,
350
+ composed: true
351
+ }));
352
+ devLog(`${lc} Successfully changed active identity settings to: ${addr}`);
353
+ } catch (error) {
354
+ console.error(`${lc} Failed to switch active identity: ${extractErrorMsg(error)}`);
355
+ }
356
+ }
357
+
358
+ override async handleContextUpdated(): Promise<void> {
359
+ await this.renderUI();
360
+ }
361
+ }
@@ -0,0 +1,100 @@
1
+ # Identity Manager Component - Implementation Spec
2
+
3
+ This document maps the implementation details for the reusable `identity-manager` component (`ibgib-identity-manager`). While developed initially inside `space-gib` as a reference implementation, it is designed to be highly decoupled and may eventually migrate down to `web-gib` for use in other downstream applications.
4
+
5
+ ## Core Architecture (Separation of App and Data)
6
+
7
+ We advocate for a strict separation of **applications** and **data providers**:
8
+ - **Local-First & Sync-Based**: Applications run local-first, storing user data in a local space and syncing peer-to-peer with remote data spaces/hosts via the `sync-gib` protocol.
9
+ - **Auditable Keystone Identity**: Identity is represented by a **Domain Keystone ($I$)**—a verifiable, historical timeline of challenge-reveals. Authentication is performed by walking and verifying the evolution history of the keystone timeline.
10
+ - *Note on Pricing/Gas:* While the core identity protocol does not require transaction fees, downstream apps/hosts are free to implement pricing plans or gas-like charging mechanisms per node.
11
+ - **Verification Flexibility**: In V1, the verification strategy is strictly `hash-reveal-v1`. However, the keystone architecture is designed as a generic audit log. Future challenge types could wrap cryptographic certificates, witness testimonies, or simple/trivial challenges.
12
+ - **SSO & Custodian Delegation**: Integration with external OAuth2 providers (e.g., Google Sign-In) is a key requirement. This will likely involve delegating a "custodian status" to the hosting server/app node, allowing it to perform authorized managerial mutations on the identity timeline on behalf of the user. The cryptographic implementation details of this delegation are TBD and will be designed after the front-end is established.
13
+ - **Delegated Authority**: To sync data, the Domain Keystone is evolved once per session to delegate signing authority to a short-lived **Session Keystone ($S$)**. The Session Keystone targets the remote storage space, solving challenges to authorize specific read/write operations without exposing the root master secrets.
14
+ - **Backing Index & Coupling**: When initialized with a virtual address (`ROOT_ADDR`), the component manually retrieves and binds to the `"keystones"` special index `ibgib` from the local space. It also initializes and couples a local settings `ibgib` using `this.initSettings()`.
15
+ - **Settings-driven Active Selection:** The user's active identity address is stored inside the coupled settings `ibgib` under the `general` section (`SettingsType.general`). Tab switching updates this setting reactively. Since other components (like `<ibgib-identity-header>`) bind to the same `"keystones"` index and share its settings scope, they reactively adapt to updates on the settings timeline.
16
+
17
+ ---
18
+
19
+ ## Component Role & Hierarchy
20
+
21
+ The `identity-manager` functions as a tabbed/list parent component (extending `IbGibDynamicComponentInstanceBase_ParentOfTabs` or `IbGibDynamicComponentInstanceBase_Parent`) that manages one or more Domain Identities:
22
+
23
+ 1. **Identity List & Selection**:
24
+ - Renders a list of loaded/configured Domain Identities from the `"keystones"` index relation `"keystone"`.
25
+ - Leverages tab/list activation plumbing to switch between active identities.
26
+ - Saves and restores the active primary identity in the `general` settings section via `this.updateSettings()`.
27
+ 2. **Identity Creator (Child Component)**:
28
+ - If no identity is loaded or created, it dynamically mounts the generic `ibgib-keystone-creator` component to generate a new Genesis Keystone or import one via a master secret.
29
+ 3. **Identity Explorer / Raw Viewer**:
30
+ - For any selected identity, provides a "Raw View" tab/accordion.
31
+ - This embeds a raw explorer similar to the `ibgib-raw` component (`apps/blank-gib/src/components/common/raw/`) to display the raw JSON of the keystone frames and trace their evolutionary lineage.
32
+ 4. **Active Sync & Replicas Control**:
33
+ - Displays the active primary sync channel and session delegation status.
34
+ - Provides options to register additional remote space URLs as replicas (structured similarly to how a package manager configures repositories). V1 focuses on a single active sync channel, with structural UI support for multiple future backup peers.
35
+ - *Investigation:* We will analyze whether and how to securely propagate/sync the identity itself to other providers using keystone evolution.
36
+
37
+ ---
38
+
39
+ ## Component Structure
40
+
41
+ Following the custom `web-gib` Web Component microframework, the component consists of:
42
+
43
+ * **[identity-manager.mts](file:///c:/Users/billm/antigravity/ibgib/apps/space-gib/src/client/components/identity-manager/identity-manager.mts)**: Element definition, lifecycle hooks (`initialize`, `created`, `renderUI`), event bindings, settings management, and integration with `KeystoneService_V1`.
44
+ * **[identity-manager.html](file:///c:/Users/billm/antigravity/ibgib/apps/space-gib/src/client/components/identity-manager/identity-manager.html)**: Markup template for Shadow DOM injection.
45
+ * **[identity-manager.css](file:///c:/Users/billm/antigravity/ibgib/apps/space-gib/src/client/components/identity-manager/identity-manager.css)**: Glassmorphic dark styling.
46
+
47
+ ---
48
+
49
+ ## Implementation Checklist
50
+
51
+ ### Phase 1: Basic Scaffolding & Setup
52
+ - [x] Scaffold `identity-manager.mts`, `.html`, and `.css` files (Reference: [@.agents/skills/ibgib-create-component/SKILL.md](file:///c:/Users/billm/antigravity/ibgib/.agents/skills/ibgib-create-component/SKILL.md)).
53
+ - [x] Register `IdentityManagerComponentMeta` with `IbGibComponentService`.
54
+ - [x] Expose in `SpaceGibShellService` to load `<ibgib-identity-manager>`.
55
+
56
+ ### Phase 2: Identity List & Settings Integration
57
+ - [x] Initialize the component settings to track loaded Domain Identity addresses.
58
+ - [x] Implement UI tabs for switching between identities.
59
+ - [x] Mount `ibgib-keystone-creator` as a child view if no identity exists.
60
+
61
+ ### Phase 3: Raw Explorer Integration
62
+ - [x] Integrate a child details panel showing raw keystone JSON using patterns from the `ibgib-raw` component.
63
+ - [x] Add a "Verify Chain" button executing `validateKeystoneTimeline` and reporting status.
64
+
65
+ ### Phase 4: Sync & Replication Setup
66
+ - [x] Implement inputs to configure remote sync repositories (URLs) (currently disabled for V1).
67
+ - [x] Add sync button triggering a delegation evolution and starting the sync sequence (currently disabled for V1).
68
+ - [x] Document the custom `web-gib` Web Component microframework (covering element lifecycles, routing, parent-child delegation, and settings/tabs state persistence).
69
+
70
+ ### Phase 5: UI/UX Enhancements & Bugs
71
+ - [x] Center/align the `<ibgib-identity-manager>` component instead of having it shoved to the left (maxing out at ~66% width of the center-panel).
72
+ - [x] Adjust scrolling behavior so the entire manager or manager-content div scrolls, instead of limiting the scroll region to the accordion detail pre-tag.
73
+ - [x] Automatically select/activate the newly created identity keystone in the tab list upon creation.
74
+
75
+ ### Phase 6: Detail Views (Pools & Keystone Metadata)
76
+ - [x] Add a details view for challenge pools on the keystone (`KeystoneData_V1.challengePools`).
77
+ - [x] Add display details for each of the main areas of the keystone included in `KeystoneData_V1` and core `IbGibData_V1` properties (e.g. `n`, `timestamp`, `uuid`, etc.).
78
+ - [x] Support human-readable profile details (such as name, email, contact info) stored in `frameDetails` and aggregated via `checkpointDetails`/`getAggregateDetails()`.
79
+
80
+ ### Phase 7: Key-Value Details & History Scrubber
81
+ - [x] Expand component max-width to fill the center panel (remove/adjust `max-width: 800px` on `:host`).
82
+ - [x] Re-layout Domain Identity Details into an inverted key-value grid (Labels in Column 1, Values in Column 2).
83
+ - [x] Add dedicated key-value rows for "Name" and "Description" in the details list.
84
+ - [x] Add "Aggr. Details" displaying the aggregated details JSON in a formatted, scrollable pre-tag.
85
+ - [x] Add "Frame Details" displaying the current frame's `frameDetails` JSON in a formatted, scrollable pre-tag.
86
+ - [x] Implement history scrubber navigation (First, Back 10, Back 1, Next 1, Next 10, Latest) to traverse the keystone timeline.
87
+
88
+ ### Phase 8: Active Identity Management
89
+ - [x] Define reusable event constants (`EVENT_IBGIB_IDENTITY_REQUEST_CHANGE` and `EVENT_IBGIB_IDENTITY_CHANGED`) in `libs/web-gib/src/ui/ui-constants.mts`.
90
+ - [x] Add checkbox in `<ibgib-keystone-creator>` to make the newly created identity the active one. Enforce/lock to checked if no identities exist yet.
91
+ - [x] If checked on creation, `<ibgib-keystone-creator>` dispatches `EVENT_IBGIB_IDENTITY_REQUEST_CHANGE` containing the new keystone address.
92
+ - [x] Add "Set as Active Identity" button to `<ibgib-identity-manager>` details view. When clicked, it dispatches `EVENT_IBGIB_IDENTITY_REQUEST_CHANGE` with the selected keystone address.
93
+ - [x] Decouple tab clicks in `<ibgib-identity-manager>` so they only browse details via `viewIdentityDetails(addr)` without setting active identity.
94
+ - [x] Update `<ibgib-identity-header>` to listen for `EVENT_IBGIB_IDENTITY_REQUEST_CHANGE`. On receipt, write the address to its settings timeline, update `ibGibGlobalThis.identity`, re-render, and dispatch `EVENT_IBGIB_IDENTITY_CHANGED`.
95
+ - [x] Set `ibGibGlobalThis.identity` on `<ibgib-identity-header>` initialization using settings.
96
+ - [x] Update `<ibgib-identity-manager>` to subscribe to `EVENT_IBGIB_IDENTITY_CHANGED` and re-render/update its "Set as Active" button state accordingly.
97
+ - [ ] TODO: Enhance the `<ibgib-identity-header>` component to support selecting/switching between loaded identities directly from a dropdown/dropdown-like UI inside the header.
98
+
99
+ ### Testing Protocol
100
+ - [ ] **Manual Testing Only**: All interactive and visual validation will be performed manually by the user to avoid token overhead from screenshot/video processing.