@ibgib/space-gib 0.0.6 → 0.0.7

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 (37) hide show
  1. package/README.md +20 -0
  2. package/dist/client/bootstrap.mjs +31 -31
  3. package/dist/client/bootstrap.mjs.map +3 -3
  4. package/dist/client/chunk-RXFIBFRK.mjs +42 -0
  5. package/dist/client/chunk-RXFIBFRK.mjs.map +7 -0
  6. package/dist/client/chunk-ULFNE26Y.mjs +2216 -0
  7. package/dist/client/chunk-ULFNE26Y.mjs.map +7 -0
  8. package/dist/client/index.mjs +1 -1
  9. package/dist/client/script.mjs +1 -1
  10. package/dist/server/server.mjs +1346 -245
  11. package/dist/server/server.mjs.map +4 -4
  12. package/package.json +5 -5
  13. package/space-gib.localhost-1783713259760.log +45 -0
  14. package/src/client/AUTO-GENERATED-version.mts +1 -1
  15. package/src/client/bootstrap.mts +9 -0
  16. package/src/client/components/identity-header/identity-header.mts +43 -26
  17. package/src/client/components/identity-manager/identity-manager.css +57 -392
  18. package/src/client/components/identity-manager/identity-manager.html +0 -114
  19. package/src/client/components/identity-manager/identity-manager.mts +356 -543
  20. package/src/client/components/keystone-creator/keystone-creator.mts +4 -3
  21. package/src/client/components/keystone-details/keystone-details.css +472 -0
  22. package/src/client/components/keystone-details/keystone-details.html +99 -0
  23. package/src/client/components/keystone-details/keystone-details.mts +821 -0
  24. package/src/client/components/keystone-scrubber/SCRUBBER_IMPLEMENTATION.md +33 -0
  25. package/src/client/components/keystone-scrubber/keystone-scrubber.css +46 -0
  26. package/src/client/components/keystone-scrubber/keystone-scrubber.html +15 -0
  27. package/src/client/components/keystone-scrubber/keystone-scrubber.mts +356 -0
  28. package/src/client/ui/shell/space-gib-shell-service.mts +4 -0
  29. package/src/server/path-constants.mts +12 -0
  30. package/src/server/serve-gib/handlers/api/keystone/sso-config.handler.mts +66 -0
  31. package/src/server/serve-gib/handlers/api/keystone/sso-link.handler.mts +119 -0
  32. package/src/server/serve-gib/handlers/api/keystone/sso-login.handler.mts +189 -0
  33. package/src/server/server.mts +6 -0
  34. package/dist/client/chunk-734MMI4C.mjs +0 -42
  35. package/dist/client/chunk-734MMI4C.mjs.map +0 -7
  36. package/dist/client/chunk-SMOZ2D5E.mjs +0 -2049
  37. package/dist/client/chunk-SMOZ2D5E.mjs.map +0 -7
@@ -32,7 +32,7 @@ import { SettingsType } from "@ibgib/web-gib/dist/common/settings/settings-const
32
32
  import { SettingsIbGib_V1 } from "@ibgib/web-gib/dist/common/settings/settings-types.mjs";
33
33
  import { updateSpecialIndex, mut8Timeline } from "@ibgib/core-gib/dist/timeline/timeline-api.mjs";
34
34
  import { toDto } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
35
- import { KEYSTONE_USERNAME_REGEXP, KEYSTONE_DESCRIPTION_REGEXP, KEYSTONE_VERB_SYNC } from "@ibgib/core-gib/dist/keystone/keystone-constants.mjs";
35
+ import { KEYSTONE_USERNAME_REGEXP, KEYSTONE_DESCRIPTION_REGEXP, KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT } from "@ibgib/core-gib/dist/keystone/keystone-constants.mjs";
36
36
  import { deriveDelegateSecret } from "@ibgib/core-gib/dist/keystone/strategy/hash-reveal-v1/hash-reveal-v1.mjs";
37
37
  import { EVENT_IBGIB_IDENTITY_REQUEST_CHANGE } from "@ibgib/web-gib/dist/ui/ui-constants.mjs";
38
38
 
@@ -276,7 +276,8 @@ export class KeystoneCreatorComponentInstance
276
276
  configs,
277
277
  metaspace,
278
278
  space,
279
- frameDetails: builder.getFrameDetails()
279
+ frameDetails: builder.getFrameDetails(),
280
+ isPrimary: true
280
281
  });
281
282
 
282
283
  const addr = getIbGibAddr({ ibGib: keystoneIbGib });
@@ -329,7 +330,7 @@ export class KeystoneCreatorComponentInstance
329
330
  masterSecret,
330
331
  metaspace,
331
332
  space,
332
- allowedVerbs: [KEYSTONE_VERB_SYNC]
333
+ allowedVerbs: [KEYSTONE_VERB_SYNC, KEYSTONE_VERB_CONNECT]
333
334
  });
334
335
  const evolvedParentAddr = getIbGibAddr({ ibGib: evolvedParentKeystoneIbGib });
335
336
  console.log(`${lc} delegate registered. evolved parent address: ${evolvedParentAddr}`);
@@ -0,0 +1,472 @@
1
+ :host {
2
+ display: block;
3
+ width: 100%;
4
+ max-width: 100%;
5
+ font-family: var(--font-family-base, inherit);
6
+ font-size: var(--font-size-base, 1rem);
7
+ color: var(--text-color-base, var(--clr-text-primary, #ffffff));
8
+ box-sizing: border-box;
9
+ }
10
+
11
+ .details-view {
12
+ display: flex;
13
+ flex-direction: column;
14
+ gap: 1.5rem;
15
+ }
16
+
17
+ .card {
18
+ padding: 1.5rem;
19
+ background: rgba(255, 255, 255, 0.02);
20
+ border: 1px solid var(--border-color-base, rgba(255, 255, 255, 0.05));
21
+ border-radius: var(--radius-md, 8px);
22
+ display: flex;
23
+ flex-direction: column;
24
+ gap: 1rem;
25
+ }
26
+
27
+ .card h3 {
28
+ margin: 0;
29
+ font-size: var(--h3-font-size, 1.15rem);
30
+ font-weight: var(--h3-font-weight, 500);
31
+ color: var(--clr-accent, #50fa7b);
32
+ }
33
+
34
+ /* Key-Value Details Grid Layout */
35
+ .kv-grid {
36
+ display: grid;
37
+ grid-template-columns: minmax(140px, 180px) 1fr;
38
+ gap: 0.75rem 1.5rem;
39
+ align-items: baseline;
40
+ margin-top: 1rem;
41
+ margin-bottom: 1.5rem;
42
+ }
43
+
44
+ .kv-label {
45
+ font-family: var(--font-sans);
46
+ font-size: 0.75rem;
47
+ font-weight: 600;
48
+ text-transform: uppercase;
49
+ letter-spacing: 0.05em;
50
+ color: var(--clr-text-secondary, #8fa08f);
51
+ user-select: none;
52
+ }
53
+
54
+ .kv-value {
55
+ font-size: 0.9375rem;
56
+ color: var(--clr-text-primary, #ffffff);
57
+ word-break: break-all;
58
+ }
59
+
60
+ .kv-pre {
61
+ margin: 0;
62
+ padding: 0.6rem 0.8rem;
63
+ background: rgba(0, 0, 0, 0.3);
64
+ border: 1px solid var(--border-color-base, rgba(255, 255, 255, 0.05));
65
+ border-radius: var(--radius-sm, 6px);
66
+ font-family: var(--font-mono, monospace);
67
+ font-size: 0.8rem;
68
+ color: var(--clr-accent, #50fa7b);
69
+ max-height: 150px;
70
+ overflow: auto;
71
+ white-space: pre;
72
+ word-break: normal;
73
+ }
74
+
75
+ /* Accordion raw styles */
76
+ .accordion {
77
+ padding: 0;
78
+ overflow: hidden;
79
+ }
80
+
81
+ .accordion-header {
82
+ padding: 1.25rem 1.5rem;
83
+ display: flex;
84
+ justify-content: space-between;
85
+ align-items: center;
86
+ cursor: pointer;
87
+ background: rgba(255, 255, 255, 0.01);
88
+ transition: background 0.2s ease-in-out;
89
+ }
90
+
91
+ .accordion-header:hover {
92
+ background: rgba(255, 255, 255, 0.03);
93
+ }
94
+
95
+ .accordion-header .chevron {
96
+ transition: transform 0.2s ease-in-out;
97
+ font-size: 0.8rem;
98
+ color: var(--clr-text-secondary, #b3b3b3);
99
+ }
100
+
101
+ .accordion-header.expanded .chevron {
102
+ transform: rotate(180deg);
103
+ }
104
+
105
+ .accordion-content {
106
+ max-height: 0;
107
+ transition: max-height 0.3s cubic-bezier(0, 1, 0, 1);
108
+ background: rgba(0, 0, 0, 0.15);
109
+ border-top: 1px solid var(--border-color-base, rgba(255, 255, 255, 0.03));
110
+ }
111
+
112
+ .accordion-content.expanded {
113
+ max-height: 5000px;
114
+ overflow-y: visible;
115
+ transition: max-height 0.3s cubic-bezier(1, 0, 1, 0);
116
+ }
117
+
118
+ .json-code {
119
+ margin: 0;
120
+ padding: 1.25rem;
121
+ font-family: var(--font-mono, monospace);
122
+ font-size: 0.8rem;
123
+ color: var(--clr-accent, #50fa7b);
124
+ background: transparent;
125
+ overflow: auto;
126
+ max-height: 70dvh;
127
+ white-space: pre-wrap;
128
+ word-break: break-all;
129
+ }
130
+
131
+ .description {
132
+ font-size: 0.85rem;
133
+ color: var(--clr-text-secondary, #b3b3b3);
134
+ margin: 0;
135
+ }
136
+
137
+ .hidden {
138
+ display: none !important;
139
+ }
140
+
141
+ .pools-container {
142
+ display: flex;
143
+ flex-direction: column;
144
+ gap: 1rem;
145
+ }
146
+
147
+ .pool-card {
148
+ padding: 1rem;
149
+ background: rgba(0, 0, 0, 0.25);
150
+ border: 1px solid var(--border-color-base, rgba(255, 255, 255, 0.05));
151
+ border-radius: 6px;
152
+ display: flex;
153
+ flex-direction: column;
154
+ gap: 0.75rem;
155
+ }
156
+
157
+ .pool-header {
158
+ display: flex;
159
+ justify-content: space-between;
160
+ align-items: center;
161
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
162
+ padding-bottom: 0.5rem;
163
+ }
164
+
165
+ .pool-title {
166
+ font-size: 0.95rem;
167
+ font-weight: 600;
168
+ color: var(--clr-accent, #50fa7b);
169
+ }
170
+
171
+ .pool-tag {
172
+ font-size: 0.7rem;
173
+ padding: 0.15rem 0.4rem;
174
+ border-radius: 4px;
175
+ font-weight: bold;
176
+ text-transform: uppercase;
177
+ }
178
+
179
+ .pool-tag.foreign {
180
+ background: rgba(255, 184, 108, 0.15);
181
+ border: 1px solid rgba(255, 184, 108, 0.3);
182
+ color: #ffb86c;
183
+ }
184
+
185
+ .pool-tag.native {
186
+ background: rgba(80, 250, 123, 0.1);
187
+ border: 1px solid rgba(80, 250, 123, 0.2);
188
+ color: #50fa7b;
189
+ }
190
+
191
+ .pool-grid {
192
+ display: grid;
193
+ grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
194
+ gap: 0.75rem;
195
+ font-size: 0.85rem;
196
+ }
197
+
198
+ .pool-field {
199
+ display: flex;
200
+ flex-direction: column;
201
+ gap: 0.2rem;
202
+ }
203
+
204
+ .pool-field label {
205
+ font-size: 0.7rem;
206
+ color: var(--clr-text-secondary, #b3b3b3);
207
+ text-transform: uppercase;
208
+ letter-spacing: 0.03em;
209
+ }
210
+
211
+ .pool-field span {
212
+ color: #ffffff;
213
+ font-weight: 500;
214
+ word-break: break-all;
215
+ }
216
+
217
+ /* Pool Details & Challenges/Binding Map styling */
218
+ .pool-details {
219
+ margin-top: 0.75rem;
220
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
221
+ padding-top: 0.5rem;
222
+ }
223
+
224
+ .pool-details-summary {
225
+ font-size: 0.8rem;
226
+ font-weight: 600;
227
+ color: var(--clr-text-secondary, #8fa08f);
228
+ cursor: pointer;
229
+ user-select: none;
230
+ outline: none;
231
+ padding: 0.25rem 0;
232
+ display: flex;
233
+ align-items: center;
234
+ gap: 0.25rem;
235
+ transition: color var(--t-fast, 120ms ease);
236
+ }
237
+
238
+ .pool-details-summary:hover {
239
+ color: var(--clr-accent, #78f87e);
240
+ }
241
+
242
+ .pool-challenges-list {
243
+ margin-top: 0.5rem;
244
+ display: flex;
245
+ flex-direction: column;
246
+ gap: 0.4rem;
247
+ max-height: 180px;
248
+ overflow-y: auto;
249
+ padding-right: 0.25rem;
250
+ border: 1px solid rgba(255, 255, 255, 0.05);
251
+ background: rgba(0, 0, 0, 0.15);
252
+ border-radius: 4px;
253
+ padding: 0.5rem;
254
+ }
255
+
256
+ .pool-challenge-item {
257
+ display: flex;
258
+ justify-content: space-between;
259
+ align-items: center;
260
+ background: rgba(255, 255, 255, 0.02);
261
+ border: 1px solid rgba(255, 255, 255, 0.03);
262
+ border-radius: 4px;
263
+ padding: 0.35rem 0.5rem;
264
+ font-size: 0.75rem;
265
+ font-family: var(--font-mono, monospace);
266
+ }
267
+
268
+ .challenge-id {
269
+ color: var(--clr-text-secondary, #8fa08f);
270
+ font-weight: 500;
271
+ }
272
+
273
+ .challenge-hash {
274
+ color: var(--clr-accent, #78f87e);
275
+ word-break: break-all;
276
+ margin-left: 1rem;
277
+ text-align: right;
278
+ }
279
+
280
+ /* Delegates Table styling */
281
+ .delegates-table {
282
+ width: 100%;
283
+ border-collapse: collapse;
284
+ margin-top: 1rem;
285
+ font-size: 0.9rem;
286
+ background: rgba(0, 0, 0, 0.2);
287
+ border-radius: var(--radius-sm, 6px);
288
+ overflow: hidden;
289
+ }
290
+
291
+ .delegates-table th,
292
+ .delegates-table td {
293
+ padding: 0.75rem 1rem;
294
+ text-align: left;
295
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
296
+ }
297
+
298
+ .delegates-table th {
299
+ font-family: var(--font-sans);
300
+ font-size: 0.75rem;
301
+ font-weight: 600;
302
+ text-transform: uppercase;
303
+ letter-spacing: 0.05em;
304
+ color: var(--clr-text-secondary, #8fa08f);
305
+ background: rgba(255, 255, 255, 0.02);
306
+ }
307
+
308
+ .delegates-table td {
309
+ font-family: var(--font-mono, monospace);
310
+ font-size: 0.8rem;
311
+ color: var(--clr-text-primary, #ffffff);
312
+ word-break: break-all;
313
+ }
314
+
315
+ .delegates-table tbody tr:hover {
316
+ background: rgba(255, 255, 255, 0.02);
317
+ }
318
+
319
+ .action-row {
320
+ display: flex;
321
+ gap: 1rem;
322
+ margin-top: 1rem;
323
+ }
324
+
325
+ .action-btn {
326
+ padding: 0.6rem 1.2rem;
327
+ background: var(--clr-accent, #50fa7b);
328
+ border: none;
329
+ border-radius: var(--radius-sm, 4px);
330
+ color: #1a1a24;
331
+ font-weight: 600;
332
+ cursor: pointer;
333
+ transition: all 0.2s ease-in-out;
334
+ }
335
+
336
+ .action-btn:hover {
337
+ background: #40c767;
338
+ }
339
+
340
+ .action-btn.secondary {
341
+ background: transparent;
342
+ border: 1px solid var(--border-color-base, rgba(255, 255, 255, 0.1));
343
+ color: #ffffff;
344
+ }
345
+
346
+ .action-btn.secondary:hover {
347
+ background: rgba(255, 255, 255, 0.05);
348
+ }
349
+
350
+ .action-btn.small {
351
+ padding: 0.2rem 0.5rem;
352
+ font-size: var(--font-size-xs, 0.75rem);
353
+ border-radius: 3px;
354
+ }
355
+
356
+ .action-btn.active-primary {
357
+ background: rgba(80, 250, 123, 0.2);
358
+ border: 1px solid rgba(80, 250, 123, 0.4);
359
+ color: var(--clr-accent, #50fa7b);
360
+ cursor: default;
361
+ }
362
+
363
+ .address-code {
364
+ display: inline-block;
365
+ padding: 0.2rem 0.4rem;
366
+ background: rgba(0, 0, 0, 0.3);
367
+ border: 1px solid var(--border-color-base, rgba(255, 255, 255, 0.05));
368
+ border-radius: var(--radius-sm, 4px);
369
+ font-family: var(--font-mono, monospace);
370
+ font-size: 0.8rem;
371
+ color: var(--clr-accent, #50fa7b);
372
+ word-break: break-all;
373
+ }
374
+
375
+ .status-msg {
376
+ margin-top: 1rem;
377
+ padding: 0.75rem 1rem;
378
+ border-radius: var(--radius-sm, 4px);
379
+ font-size: 0.9rem;
380
+ }
381
+
382
+ .status-msg.info {
383
+ background: rgba(139, 233, 253, 0.1);
384
+ border: 1px solid rgba(139, 233, 253, 0.3);
385
+ color: #8be9fd;
386
+ }
387
+
388
+ .status-msg.success {
389
+ background: rgba(80, 250, 123, 0.1);
390
+ border: 1px solid rgba(80, 250, 123, 0.3);
391
+ color: #50fa7b;
392
+ }
393
+
394
+ .status-msg.error {
395
+ background: rgba(255, 85, 85, 0.1);
396
+ border: 1px solid rgba(255, 85, 85, 0.3);
397
+ color: #ff5555;
398
+ }
399
+
400
+ /* Warning styles for viewing historical frames */
401
+ #container.is-historical {
402
+ border: 3px dashed var(--clr-warning, #ffb86c);
403
+ border-radius: var(--radius-md, 8px);
404
+ padding: 1.5rem;
405
+ position: relative;
406
+ background: rgba(255, 184, 108, 0.01);
407
+ }
408
+
409
+ .warning-banner {
410
+ background: rgba(255, 184, 108, 0.1);
411
+ border: 1.5px solid rgba(255, 184, 108, 0.35);
412
+ color: var(--clr-warning, #ffb86c);
413
+ padding: 0.8rem 1.2rem;
414
+ border-radius: var(--radius-md, 8px);
415
+ font-weight: 600;
416
+ font-size: 1rem;
417
+ margin-bottom: 1.5rem;
418
+ display: flex;
419
+ align-items: center;
420
+ gap: 0.5rem;
421
+ box-shadow: 0 4px 12px rgba(255, 184, 108, 0.05);
422
+ animation: pulse-border 2s infinite alternate;
423
+ }
424
+
425
+ @keyframes pulse-border {
426
+ 0% {
427
+ border-color: rgba(255, 184, 108, 0.2);
428
+ box-shadow: 0 4px 12px rgba(255, 184, 108, 0.02);
429
+ }
430
+ 100% {
431
+ border-color: rgba(255, 184, 108, 0.5);
432
+ box-shadow: 0 4px 16px rgba(255, 184, 108, 0.08);
433
+ }
434
+ }
435
+
436
+ .sso-actions {
437
+ display: flex;
438
+ gap: 1rem;
439
+ margin-top: 1rem;
440
+ flex-wrap: wrap;
441
+ }
442
+
443
+ .sso-btn {
444
+ flex: 1;
445
+ min-width: 200px;
446
+ display: inline-flex;
447
+ justify-content: center;
448
+ align-items: center;
449
+ gap: 0.5rem;
450
+ padding: 0.75rem 1.25rem;
451
+ font-weight: 600;
452
+ }
453
+
454
+ .sso-btn.google {
455
+ background: #4285F4;
456
+ border-color: #4285F4;
457
+ color: #ffffff;
458
+ }
459
+
460
+ .sso-btn.google:hover {
461
+ background: #357ae8;
462
+ }
463
+
464
+ .sso-btn.github {
465
+ background: #333333;
466
+ border-color: #444444;
467
+ color: #ffffff;
468
+ }
469
+
470
+ .sso-btn.github:hover {
471
+ background: #444444;
472
+ }
@@ -0,0 +1,99 @@
1
+ <div id="container" class="component-container">
2
+ <div id="historical-warning-banner" class="warning-banner hidden">
3
+ <span>⚠️ Viewing Historical Frame (Past State)</span>
4
+ </div>
5
+ <div id="identity-details-view" class="details-view">
6
+ <!-- Details for the active Domain Identity -->
7
+ <div class="card">
8
+ <h3>Domain Identity Details</h3>
9
+
10
+ <!-- Key-Value Grid -->
11
+ <div class="kv-grid">
12
+ <div class="kv-label">Username</div>
13
+ <div id="identity-name" class="kv-value">-</div>
14
+
15
+ <div class="kv-label">Description</div>
16
+ <div id="identity-description" class="kv-value">-</div>
17
+
18
+ <div class="kv-label">Verifiable Address</div>
19
+ <code id="identity-addr" class="address-code"></code>
20
+
21
+ <div class="kv-label">Generation (n)</div>
22
+ <div id="identity-gen" class="kv-value">-</div>
23
+
24
+ <div class="kv-label">Timestamp</div>
25
+ <div id="identity-timestamp" class="kv-value">-</div>
26
+
27
+ <div class="kv-label">UUID</div>
28
+ <div id="identity-uuid" class="kv-value">-</div>
29
+
30
+ <div class="kv-label">Frame Details</div>
31
+ <div class="kv-value">
32
+ <pre id="identity-frame-details" class="kv-pre">-</pre>
33
+ </div>
34
+
35
+ <div class="kv-label">Aggr. Details</div>
36
+ <div class="kv-value">
37
+ <pre id="identity-aggr-details" class="kv-pre">-</pre>
38
+ </div>
39
+ </div>
40
+
41
+ <div class="action-row">
42
+ <button id="btn-set-active" class="action-btn">Set as Active Identity</button>
43
+ <button id="btn-verify-chain" class="action-btn secondary">Verify Identity Chain</button>
44
+ </div>
45
+ <div id="verification-status" class="status-msg hidden"></div>
46
+ </div>
47
+
48
+ <!-- Challenge Pools Card -->
49
+ <div class="card">
50
+ <h3>Challenge Pools</h3>
51
+ <div id="pools-container" class="pools-container">
52
+ <!-- Dynamic pools list gets injected here -->
53
+ </div>
54
+ </div>
55
+
56
+ <!-- Delegates Card -->
57
+ <div id="delegates-card" class="card">
58
+ <h3>Registered Delegates</h3>
59
+ <div id="delegates-container" class="delegates-container">
60
+ <p class="description">Active sub-keystones authorized to perform sync and operations on behalf of this Domain Identity.</p>
61
+ <table class="delegates-table">
62
+ <thead>
63
+ <tr>
64
+ <th>Name / Description</th>
65
+ <th>TJP Address</th>
66
+ <th>Allowed Verbs</th>
67
+ <th>Actions</th>
68
+ </tr>
69
+ </thead>
70
+ <tbody id="delegates-list">
71
+ <!-- Dynamic list of delegates gets injected here -->
72
+ </tbody>
73
+ </table>
74
+ </div>
75
+ </div>
76
+
77
+ <!-- SSO Identity Linking Card -->
78
+ <div id="sso-link-card" class="card hidden">
79
+ <h3>Link Identity Provider</h3>
80
+ <p class="description">Evolve this Domain Identity by linking a Web2 provider (Google, GitHub) to delegate sync access.</p>
81
+ <div class="sso-actions">
82
+ <button id="btn-link-google" class="action-btn sso-btn google">Link Google Account</button>
83
+ <button id="btn-link-github" class="action-btn sso-btn github">Link GitHub Account</button>
84
+ </div>
85
+ <div id="sso-status-msg" class="status-msg hidden"></div>
86
+ </div>
87
+
88
+ <!-- Raw Keystone Timeline Explorer -->
89
+ <div class="card accordion">
90
+ <div id="raw-accordion-header" class="accordion-header">
91
+ <span>Raw Keystone Timeline (JSON)</span>
92
+ <span class="chevron">▼</span>
93
+ </div>
94
+ <div id="raw-accordion-content" class="accordion-content">
95
+ <pre id="raw-keystone-json" class="json-code"></pre>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </div>