@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
@@ -5,16 +5,6 @@
5
5
  margin: 2rem auto;
6
6
  }
7
7
 
8
- .component-container {
9
- padding: 2rem;
10
- background: var(--clr-bg-glass);
11
- backdrop-filter: blur(16px);
12
- border-radius: var(--radius-lg);
13
- border: 1px solid var(--clr-border);
14
- color: var(--clr-text-primary);
15
- box-shadow: var(--shadow-card);
16
- }
17
-
18
8
  h2 {
19
9
  margin-top: 0;
20
10
  font-size: 1.5rem;
@@ -28,28 +18,6 @@ h2 {
28
18
  gap: 1.5rem;
29
19
  }
30
20
 
31
- .action-row {
32
- display: flex;
33
- justify-content: center;
34
- }
35
-
36
- .action-btn {
37
- padding: 0.75rem 1.5rem;
38
- background: var(--clr-accent);
39
- color: var(--clr-bg-deep);
40
- border: none;
41
- border-radius: var(--radius-md);
42
- font-weight: 600;
43
- cursor: pointer;
44
- transition: all var(--t-fast);
45
- }
46
-
47
- .action-btn:hover {
48
- background: #9fffa5;
49
- transform: translateY(-1px);
50
- box-shadow: 0 0 16px var(--clr-accent-glow);
51
- }
52
-
53
21
  .input-group {
54
22
  display: flex;
55
23
  flex-direction: column;
@@ -61,45 +29,14 @@ h2 {
61
29
  color: var(--clr-text-secondary);
62
30
  }
63
31
 
64
- .secret-input {
65
- padding: 0.75rem;
66
- background: rgba(0, 0, 0, 0.2);
67
- border: 1px solid var(--clr-border);
68
- border-radius: var(--radius-sm);
69
- color: var(--clr-text-primary);
70
- font-family: var(--font-mono);
71
- }
72
-
73
- .secret-input:focus {
74
- outline: none;
75
- border-color: var(--clr-accent);
76
- }
77
-
78
32
  .status-area {
79
- padding: 1rem;
80
- border-radius: var(--radius-md);
81
- background: rgba(255, 255, 255, 0.03);
33
+ margin-top: 0.5rem;
82
34
  }
83
35
 
84
36
  .status-area.hidden {
85
37
  display: none;
86
38
  }
87
39
 
88
- .status-msg {
89
- margin: 0;
90
- font-size: 0.9rem;
91
- }
92
-
93
- .status-msg.info { color: var(--clr-text-secondary); }
94
- .status-msg.success { color: var(--clr-accent); }
95
- .status-msg.error { color: var(--clr-warn); }
96
-
97
- .action-btn:disabled {
98
- opacity: 0.5;
99
- cursor: not-allowed;
100
- background: var(--clr-border);
101
- }
102
-
103
40
  .result-area {
104
41
  padding: 1.5rem;
105
42
  background: rgba(255, 255, 255, 0.03);
@@ -121,19 +58,8 @@ h2 {
121
58
  margin: 0;
122
59
  }
123
60
 
124
- .keystone-addr {
125
- display: block;
126
- padding: 0.75rem;
127
- background: rgba(0, 0, 0, 0.3);
128
- border-radius: var(--radius-sm);
129
- font-family: var(--font-mono);
130
- font-size: 0.85rem;
131
- word-break: break-all;
132
- color: var(--clr-accent);
133
- }
134
-
135
61
  .warning {
136
62
  font-size: 0.8rem;
137
63
  color: var(--clr-warn);
138
64
  margin: 0.5rem 0 0;
139
- }
65
+ }
@@ -1,26 +1,41 @@
1
- <div id="container" class="component-container">
2
- <h2 id="title">Keystone Creator</h2>
3
- <div class="content">
4
- <p>This will generate a new root keystone (identity) for your space.</p>
5
-
6
- <div class="input-group">
7
- <label for="input-secret">Master Secret (Keep this private!)</label>
8
- <input type="password" id="input-secret" placeholder="Enter your master secret..." class="secret-input">
9
- </div>
10
-
11
- <div class="action-row">
12
- <button id="btn-generate" class="action-btn">Generate Identity Keystone</button>
13
- </div>
14
-
15
- <div id="status-area" class="status-area hidden">
16
- <p id="status-text" class="status-msg"></p>
17
- </div>
18
-
19
- <div id="result-area" class="result-area collapsed">
20
- <h3>New Keystone Generated!</h3>
21
- <p class="address-label">Address:</p>
22
- <code id="keystone-addr" class="keystone-addr"></code>
23
- <p class="warning">⚠️ Store this address safely. It is your root identity for this space.</p>
24
- </div>
25
- </div>
26
- </div>
1
+ <div id="container" class="component-container">
2
+ <h2 id="title">Keystone Creator</h2>
3
+ <div class="content">
4
+ <p>This will generate a new root keystone (identity) for your space.</p>
5
+
6
+ <div class="input-group">
7
+ <label for="input-username">Username</label>
8
+ <input type="text" id="input-username" placeholder="Enter your username..." class="text-input">
9
+ </div>
10
+
11
+ <div class="input-group">
12
+ <label for="input-description">Description (Optional)</label>
13
+ <input type="text" id="input-description" placeholder="Enter an optional description..." class="text-input">
14
+ </div>
15
+
16
+ <div class="input-group">
17
+ <label for="input-secret">Master Secret (Keep this private!)</label>
18
+ <input type="password" id="input-secret" placeholder="Enter your master secret..." class="secret-input">
19
+ </div>
20
+
21
+ <div class="checkbox-group" style="margin-bottom: 1.5rem; display: flex; align-items: center; gap: 0.5rem;">
22
+ <input type="checkbox" id="checkbox-make-active" checked style="cursor: pointer; width: 1rem; height: 1rem;">
23
+ <label for="checkbox-make-active" style="cursor: pointer; font-size: 0.9rem; user-select: none;">Make this the active identity</label>
24
+ </div>
25
+
26
+ <div class="action-row">
27
+ <button id="btn-generate" class="action-btn">Generate Identity Keystone</button>
28
+ </div>
29
+
30
+ <div id="status-area" class="status-area hidden">
31
+ <p id="status-text" class="status-msg"></p>
32
+ </div>
33
+
34
+ <div id="result-area" class="result-area collapsed">
35
+ <h3>New Keystone Generated!</h3>
36
+ <p class="address-label">Address:</p>
37
+ <code id="keystone-addr" class="keystone-addr"></code>
38
+ <p class="warning">⚠️ Store this address safely. It is your root identity for this space.</p>
39
+ </div>
40
+ </div>
41
+ </div>
@@ -1,7 +1,8 @@
1
1
  import thisCss from "./keystone-creator.css";
2
2
  import thisHtml from "./keystone-creator.html";
3
+ import styleCss from "../../style.css";
3
4
 
4
- import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
5
+ import { delay, extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
5
6
  import { HashAlgorithm } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
6
7
  import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs";
7
8
  import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
@@ -10,9 +11,10 @@ import { KeystoneService_V1 } from "@ibgib/core-gib/dist/keystone/keystone-servi
10
11
  import {
11
12
  KeystonePoolConfig, KeystoneChallengeType, KeystoneReplenishStrategy
12
13
  } from "@ibgib/core-gib/dist/keystone/keystone-types.mjs";
13
- import { getGlobalMetaspace_waitIfNeeded } from "@ibgib/web-gib/dist/helpers.mjs";
14
+ import { KeystoneProfileBuilder } from "@ibgib/core-gib/dist/keystone/policy/keystone-profile-builder.mjs";
15
+ import { getGlobalMetaspace_waitIfNeeded, getLocalCoupledIbGibForDomainIbGib } from "@ibgib/web-gib/dist/helpers.mjs";
14
16
  import {
15
- IbGibDynamicComponentMetaBase, IbGibDynamicComponentInstanceBase,
17
+ IbGibDynamicComponentMetaBase, IbGibFormInstanceBase,
16
18
  } from "@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs";
17
19
  import {
18
20
  ElementsBase, IbGibDynamicComponentInstance,
@@ -23,7 +25,17 @@ import { getComponentCtorArg } from "../../helpers.web.mjs";
23
25
  import { spaceGibApiBridge } from "../../api/space-gib-api-bridge.mjs";
24
26
  import { devLog } from "../../dev-tools.mjs";
25
27
 
26
- export const KEYSTONE_CREATOR_COMPONENT_NAME = 'space-gib-keystone-creator';
28
+ import {
29
+ createSettings, getSectionName, getSettingsScope
30
+ } from "@ibgib/web-gib/dist/common/settings/settings-helpers.mjs";
31
+ import { SettingsType } from "@ibgib/web-gib/dist/common/settings/settings-constants.mjs";
32
+ import { SettingsIbGib_V1 } from "@ibgib/web-gib/dist/common/settings/settings-types.mjs";
33
+ import { updateSpecialIndex, mut8Timeline } from "@ibgib/core-gib/dist/timeline/timeline-api.mjs";
34
+ import { toDto } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
35
+ import { KEYSTONE_USERNAME_REGEXP, KEYSTONE_DESCRIPTION_REGEXP } from "@ibgib/core-gib/dist/keystone/keystone-constants.mjs";
36
+ import { EVENT_IBGIB_IDENTITY_REQUEST_CHANGE } from "@ibgib/web-gib/dist/ui/ui-constants.mjs";
37
+
38
+ export const KEYSTONE_CREATOR_COMPONENT_NAME = 'ibgib-keystone-creator';
27
39
 
28
40
  /**
29
41
  * Metadata for the Keystone Creator component.
@@ -54,7 +66,7 @@ export class KeystoneCreatorComponentMeta extends IbGibDynamicComponentMetaBase
54
66
  ibGibAddr,
55
67
  meta: this,
56
68
  html: thisHtml,
57
- css: [thisCss],
69
+ css: [styleCss, thisCss],
58
70
  });
59
71
  return component;
60
72
  }
@@ -69,7 +81,10 @@ interface KeystoneCreatorElements extends ElementsBase {
69
81
  btnGenerateEl: HTMLButtonElement;
70
82
  resultAreaEl: HTMLDivElement;
71
83
  keystoneAddrEl: HTMLElement;
84
+ inputUsername: HTMLInputElement;
85
+ inputDescription: HTMLInputElement;
72
86
  inputSecret: HTMLInputElement;
87
+ checkboxMakeActive: HTMLInputElement;
73
88
  statusArea: HTMLDivElement;
74
89
  statusText: HTMLParagraphElement;
75
90
  }
@@ -78,10 +93,11 @@ interface KeystoneCreatorElements extends ElementsBase {
78
93
  * Concrete implementation of the Keystone Creator component.
79
94
  */
80
95
  export class KeystoneCreatorComponentInstance
81
- extends IbGibDynamicComponentInstanceBase<IbGib_V1, KeystoneCreatorElements>
96
+ extends IbGibFormInstanceBase<IbGib_V1, KeystoneCreatorElements>
82
97
  implements IbGibDynamicComponentInstance<IbGib_V1, KeystoneCreatorElements> {
83
98
 
84
99
  protected lc: string = `[KeystoneCreatorComponentInstance]`;
100
+ private hasExistingIdentities = false;
85
101
 
86
102
  constructor() {
87
103
  super();
@@ -99,7 +115,10 @@ export class KeystoneCreatorComponentInstance
99
115
  this.elements.btnGenerateEl = this.shadowRoot!.getElementById('btn-generate') as HTMLButtonElement;
100
116
  this.elements.resultAreaEl = this.shadowRoot!.getElementById('result-area') as HTMLDivElement;
101
117
  this.elements.keystoneAddrEl = this.shadowRoot!.getElementById('keystone-addr') as HTMLElement;
118
+ this.elements.inputUsername = this.shadowRoot!.getElementById('input-username') as HTMLInputElement;
119
+ this.elements.inputDescription = this.shadowRoot!.getElementById('input-description') as HTMLInputElement;
102
120
  this.elements.inputSecret = this.shadowRoot!.getElementById('input-secret') as HTMLInputElement;
121
+ this.elements.checkboxMakeActive = this.shadowRoot!.getElementById('checkbox-make-active') as HTMLInputElement;
103
122
  this.elements.statusArea = this.shadowRoot!.getElementById('status-area') as HTMLDivElement;
104
123
  this.elements.statusText = this.shadowRoot!.getElementById('status-text') as HTMLParagraphElement;
105
124
 
@@ -111,15 +130,59 @@ export class KeystoneCreatorComponentInstance
111
130
  if (this.ibGibAddr && !this.ibGib) {
112
131
  await this.loadIbGib();
113
132
  }
133
+ await this.checkExistingIdentities();
114
134
  await this.renderUI();
115
135
  }
116
136
 
137
+ private async checkExistingIdentities() {
138
+ const lc = `${this.lc}[checkExistingIdentities]`;
139
+ try {
140
+ const metaspace = await getGlobalMetaspace_waitIfNeeded();
141
+ const space = await metaspace.getLocalUserSpace({});
142
+ if (space) {
143
+ const keystonesIndex = await metaspace.getSpecialIbGib({
144
+ type: "keystones",
145
+ space,
146
+ initialize: true,
147
+ });
148
+ const registeredKeystones = keystonesIndex?.rel8ns?.keystone ?? [];
149
+ this.hasExistingIdentities = registeredKeystones.length > 0;
150
+ }
151
+ } catch (error) {
152
+ console.warn(`${lc} Error checking existing identities: ${extractErrorMsg(error)}`);
153
+ this.hasExistingIdentities = false;
154
+ }
155
+
156
+ const checkbox = this.elements!.checkboxMakeActive;
157
+ if (checkbox) {
158
+ if (!this.hasExistingIdentities) {
159
+ checkbox.checked = true;
160
+ checkbox.disabled = true;
161
+ checkbox.title = "At least one active identity is required.";
162
+ } else {
163
+ checkbox.disabled = false;
164
+ checkbox.title = "";
165
+ }
166
+ }
167
+ }
168
+
117
169
  override async disconnected(): Promise<void> {
118
170
  // cleanup if needed
119
171
  }
120
172
 
121
173
  private initHandlers() {
122
174
  this.elements!.btnGenerateEl.addEventListener('click', () => this.handleGenerate());
175
+
176
+ // Clear invalid styling when user inputs text
177
+ this.elements!.inputUsername.addEventListener('input', () => {
178
+ this.elements!.inputUsername.classList.remove('invalid');
179
+ });
180
+ this.elements!.inputDescription.addEventListener('input', () => {
181
+ this.elements!.inputDescription.classList.remove('invalid');
182
+ });
183
+ this.elements!.inputSecret.addEventListener('input', () => {
184
+ this.elements!.inputSecret.classList.remove('invalid');
185
+ });
123
186
  }
124
187
 
125
188
  /**
@@ -130,35 +193,68 @@ export class KeystoneCreatorComponentInstance
130
193
  const lc = `${this.lc}[${this.handleGenerate.name}]`;
131
194
  try {
132
195
  console.log(`${lc} [NAG][ibgib-paradigm] starting genesis orchestration...`);
133
- this.setLoading(true);
196
+ /**
197
+ * Delay update messages so the human eye has a chance to comprehend
198
+ * progress.
199
+ */
200
+ const humanEyeDelay = 500;
134
201
  this.setStatus("Initializing genesis...", "info");
135
202
 
136
203
  devLog('Starting local genesis orchestration...');
204
+
205
+ // Validate Username using base class validation plumbing
206
+ if (!this.validateInputPattern({
207
+ input: this.elements!.inputUsername,
208
+ pattern: KEYSTONE_USERNAME_REGEXP,
209
+ errorMessage: "Username must be 1-63 characters (alphanumeric, underscores, hyphens, dots).",
210
+ required: true
211
+ })) {
212
+ this.setStatus("Invalid username. Must be 1-63 characters (alphanumeric, _, -, .).", "error");
213
+ devLog('✗ Invalid username.');
214
+ return; /* <<<< returns early */
215
+ }
216
+ const username = this.elements!.inputUsername.value.trim();
217
+
218
+ // Validate Description using base class validation plumbing
219
+ if (!this.validateInputPattern({
220
+ input: this.elements!.inputDescription,
221
+ pattern: KEYSTONE_DESCRIPTION_REGEXP,
222
+ errorMessage: "Description must be 0-128 characters and consist of English characters and common punctuation.",
223
+ required: false
224
+ })) {
225
+ this.setStatus("Invalid description. Alphanumeric + common punctuation, max 128 chars.", "error");
226
+ devLog('✗ Invalid description.');
227
+ return; /* <<<< returns early */
228
+ }
229
+ const description = this.elements!.inputDescription.value.trim();
230
+
137
231
  const masterSecret = this.elements!.inputSecret.value;
138
232
  if (!masterSecret) {
233
+ this.elements!.inputSecret.classList.add('invalid');
139
234
  this.setStatus("Please enter a master secret.", "error");
140
235
  devLog('✗ Please enter a master secret.');
141
- return;
236
+ return; /* <<<< returns early */
237
+ } else {
238
+ this.elements!.inputSecret.classList.remove('invalid');
142
239
  }
143
240
 
144
- // 1. Prepare Keystone Configs (Standard Phase 1 config)
145
- const configs: KeystonePoolConfig[] = [
146
- {
147
- id: 'primary',
148
- type: KeystoneChallengeType.hash_reveal_v1,
149
- salt: 'primary_pool_salt',
150
- algo: HashAlgorithm.sha_256,
151
- rounds: 1,
152
- behavior: {
153
- size: 3,
154
- replenish: KeystoneReplenishStrategy.topUp,
155
- selectSequentially: 1,
156
- selectRandomly: 0,
157
- targetBindingChars: 0,
158
- },
159
- allowedVerbs: [], // General purpose
160
- }
161
- ];
241
+ this.showBusy({
242
+ isBusy: true,
243
+ title: 'Generating Identity...',
244
+ msg: 'Preparing configuration and policy options...',
245
+ animationEmoji: '⚙️',
246
+ submitBtnSelector: '#btn-generate',
247
+ loadingText: 'Generating...'
248
+ });
249
+
250
+ // 1. Prepare Keystone Configs using the test behavior profile (with username and description)
251
+ const builder = KeystoneProfileBuilder.buildKeystone('test')
252
+ .withUsername(username)
253
+ .withDescription(description)
254
+ .withDetails({
255
+ client: 'space-gib-web',
256
+ });
257
+ const configs = await builder.compileConfigs();
162
258
 
163
259
  // 2. Get local services
164
260
  const metaspace = await getGlobalMetaspace_waitIfNeeded();
@@ -166,37 +262,85 @@ export class KeystoneCreatorComponentInstance
166
262
  if (!space) { throw new Error("No default space found in metaspace."); }
167
263
 
168
264
  // 3. Perform Genesis (Local)
265
+ this.showBusy({
266
+ isBusy: true,
267
+ title: 'Generating Identity...',
268
+ msg: 'Performing cryptographic local genesis...\n(This runs compute-heavy Key Derivation Functions)',
269
+ animationEmoji: '🗝️'
270
+ });
169
271
  const keystoneService = new KeystoneService_V1();
170
272
  const keystoneIbGib = await keystoneService.genesis({
171
273
  masterSecret,
172
274
  configs,
173
275
  metaspace,
174
276
  space,
175
- frameDetails: { client: 'space-gib-web', timestamp: Date.now() }
277
+ frameDetails: builder.getFrameDetails()
176
278
  });
177
279
 
178
280
  const addr = getIbGibAddr({ ibGib: keystoneIbGib });
179
281
  console.log(`${lc} [NAG][ibgib-paradigm] local genesis complete: ${addr}`);
282
+ await delay(humanEyeDelay); // for human eyes, not needed for functionality
180
283
  this.setStatus("Local genesis complete. Syncing to server...", "info");
181
284
  devLog(`✓ Local genesis complete: ${addr}`);
182
-
285
+
183
286
  // Expose for dev tools
184
287
  (window as any).dev_domainI = keystoneIbGib;
185
288
  (window as any).dev_domainIMasterSecret = masterSecret;
186
289
 
187
290
  // 4. Post to Server
188
291
  devLog(`Posting genesis keystone to server...`);
292
+ this.showBusy({
293
+ isBusy: true,
294
+ title: 'Syncing to Server...',
295
+ msg: [
296
+ 'Local genesis complete!',
297
+ 'Posting genesis keystone to server sync node...',
298
+ '(may take awhile, depending on strength of keystone...)',
299
+ ].join('\n'),
300
+ animationEmoji: '🔄'
301
+ });
189
302
  const resSync = await spaceGibApiBridge.postGenesisKeystone(keystoneIbGib);
190
303
  if (!resSync.success) {
191
304
  this.setStatus(`Sync failed: ${resSync.message}`, "error");
192
305
  devLog(`✗ Server rejected genesis keystone: ${resSync.message}`);
193
- return;
306
+ return; /* <<<< returns early */
307
+ }
308
+
309
+ // 5. Register with the keystones special index
310
+ this.showBusy({
311
+ isBusy: true,
312
+ title: 'Registering Identity...',
313
+ msg: 'Server sync succeeded!\nUpdating local identity indexes and scope settings...',
314
+ animationEmoji: '💾'
315
+ });
316
+ await delay(humanEyeDelay); // for human eyes, not needed for functionality
317
+ await updateSpecialIndex({
318
+ type: "keystones",
319
+ rel8nInfos: [
320
+ {
321
+ rel8nName: "keystone",
322
+ ibGibs: [toDto({ ibGib: keystoneIbGib })],
323
+ },
324
+ ],
325
+ metaspace,
326
+ space,
327
+ });
328
+
329
+ // 6. Request to make the new identity active if the checkbox is checked
330
+ const makeActive = this.elements!.checkboxMakeActive?.checked;
331
+ if (makeActive) {
332
+ window.dispatchEvent(new CustomEvent(EVENT_IBGIB_IDENTITY_REQUEST_CHANGE, {
333
+ detail: { activeIdentityAddr: addr },
334
+ bubbles: true,
335
+ composed: true
336
+ }));
194
337
  }
195
338
 
196
- // 5. Success
339
+ // 7. Success
197
340
  this.elements!.keystoneAddrEl.textContent = addr;
198
341
  this.elements!.statusArea.classList.remove('hidden');
199
342
  this.setStatus("Keystone successfully created and synced!", "success");
343
+ await delay(humanEyeDelay); // for human eyes, not needed for functionality
200
344
  devLog(`✓ Keystone successfully created and synced!`);
201
345
 
202
346
  } catch (error) {
@@ -205,20 +349,13 @@ export class KeystoneCreatorComponentInstance
205
349
  this.setStatus(`Error: ${emsg}`, "error");
206
350
  devLog(`✗ Error: ${emsg}`);
207
351
  } finally {
208
- this.setLoading(false);
352
+ this.showBusy({
353
+ isBusy: false,
354
+ submitBtnSelector: '#btn-generate'
355
+ });
209
356
  }
210
357
  }
211
358
 
212
- private setLoading(loading: boolean) {
213
- this.elements!.btnGenerateEl.disabled = loading;
214
- this.elements!.btnGenerateEl.textContent = loading ? "Generating..." : "Generate Identity Keystone";
215
- }
216
-
217
- private setStatus(msg: string, type: 'info' | 'success' | 'error') {
218
- this.elements!.statusText.textContent = msg;
219
- this.elements!.statusText.className = `status-msg ${type}`;
220
- }
221
-
222
359
  protected override async renderUI(): Promise<void> {
223
360
  // No complex rendering needed for this simple creator component yet
224
361
  }