@ibgib/space-gib 0.0.34 → 0.0.35

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 (45) hide show
  1. package/dist/client/bootstrap.mjs +1 -1
  2. package/dist/client/{chunk-25PXC4HP.mjs → chunk-FIAGRVBY.mjs} +1487 -468
  3. package/dist/client/chunk-XLQ3UCQY.mjs +53 -0
  4. package/dist/client/css/activity-bar.css +21 -3
  5. package/dist/client/css/layout.css +126 -5
  6. package/dist/client/index.html +6 -0
  7. package/dist/client/index.mjs +22 -45
  8. package/dist/client/privacy.html +22 -22
  9. package/dist/client/script.mjs +1 -1
  10. package/dist/server/server.mjs +3 -3
  11. package/package.json +6 -6
  12. package/src/client/AUTO-GENERATED-version.mts +1 -1
  13. package/src/client/cosmos/seed-cosmos-workspace.mts +20 -6
  14. package/src/client/css/activity-bar.css +21 -3
  15. package/src/client/css/layout.css +126 -5
  16. package/src/client/dev-tools/vcs-workspace.mts +4 -4
  17. package/src/client/index.html +6 -0
  18. package/src/client/privacy.html +22 -22
  19. package/src/client/ui/component/changes/changes.css +413 -0
  20. package/src/client/ui/component/changes/changes.html +37 -0
  21. package/src/client/ui/component/changes/changes.mts +766 -0
  22. package/src/client/ui/component/changes/index.mts +9 -0
  23. package/src/client/ui/component/chat-view/chat-view.html +6 -58
  24. package/src/client/ui/component/chat-view/chat-view.mts +13 -26
  25. package/src/client/ui/component/clone/clone.mts +67 -39
  26. package/src/client/ui/component/code-editor/code-editor.css +91 -9
  27. package/src/client/ui/component/code-editor/code-editor.html +58 -45
  28. package/src/client/ui/component/code-editor/code-editor.mts +265 -49
  29. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.css +1 -1
  30. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.html +5 -10
  31. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.mts +297 -64
  32. package/src/client/ui/component/file-explorer/file-explorer.css +475 -11
  33. package/src/client/ui/component/file-explorer/file-explorer.html +45 -65
  34. package/src/client/ui/component/file-explorer/file-explorer.mts +1164 -98
  35. package/src/client/ui/component/nested-chat/chat/chat.mts +5 -90
  36. package/src/client/ui/component/nested-chat/nested-chat.mts +10 -8
  37. package/src/client/ui/component/timeline/timeline.css +16 -1
  38. package/src/client/ui/component/timeline/timeline.mts +37 -93
  39. package/src/client/ui/router/space-gib-router-helpers.mts +33 -233
  40. package/src/client/ui/router/space-gib-router-helpers.respec.mts +298 -122
  41. package/src/client/ui/router/space-gib-router-service.mts +28 -151
  42. package/src/client/ui/router/space-gib-router-types.mts +32 -76
  43. package/src/client/ui/shell/cosmic-big-bang.mts +12 -3
  44. package/src/client/ui/shell/space-gib-shell-service.mts +687 -119
  45. package/dist/client/chunk-RXWLL2AI.mjs +0 -30
@@ -8,10 +8,15 @@ import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs
8
8
  import {
9
9
  IbGibDynamicComponentMetaBase, IbGibDynamicComponentInstanceBase,
10
10
  } from "@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs";
11
- import {
12
- ElementsBase, IbGibDynamicComponentInstance,
13
- } from "@ibgib/web-gib/dist/ui/component/component-types.mjs";
11
+ import { ElementsBase, IbGibDynamicComponentInstance } from "@ibgib/web-gib/dist/ui/component/component-types.mjs";
14
12
  import { getComponentCtorArg } from "@ibgib/web-gib/dist/app-bootstrap/init-orchestration.mjs";
13
+ import { getDeterministicColorInfo } from "@ibgib/web-gib/dist/helpers.mjs";
14
+ import { GibColorInfo } from "@ibgib/web-gib/dist/types.mjs";
15
+ import { getIbGibFromSpace, getTjpAddr } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
16
+ import { getBranchAndRootItemInfosAndIbGibsAndSpaceFromGalaxyIbGib } from "@ibgib/core-gib/dist/cosmology/branching/item/item-helpers.mjs";
17
+ import { B2tFSGalaxyIbGib_V1 } from "@ibgib/core-gib/dist/cosmology/branching/repo/b2tfs-repo-types.mjs";
18
+ import { getPlaygroundMetaspaceAndSpace } from "../../../cosmos/seed-cosmos-workspace.mjs";
19
+ import { getSpaceGibRouterSvc } from "../../router/index.mjs";
15
20
 
16
21
  export const CODE_EDITOR_COMPONENT_NAME = 'space-gib-code-editor';
17
22
 
@@ -54,7 +59,10 @@ export class CodeEditorComponentMeta extends IbGibDynamicComponentMetaBase {
54
59
  */
55
60
  export interface CodeEditorElements extends ElementsBase {
56
61
  rootEl: HTMLDivElement;
62
+ placeholderEl?: HTMLDivElement;
63
+ mainContainerEl?: HTMLDivElement;
57
64
  headerEl: HTMLElement;
65
+ btnSaveSpinnerEl?: HTMLSpanElement;
58
66
  crumbRepoEl: HTMLSpanElement;
59
67
  crumbPathEl: HTMLSpanElement;
60
68
  crumbBranchEl: HTMLSpanElement;
@@ -72,25 +80,6 @@ export interface CodeEditorElements extends ElementsBase {
72
80
  contentEl: HTMLDivElement;
73
81
  }
74
82
 
75
- const DEFAULT_CODE = `/**
76
- * @module my-app/index
77
- * Application entry point running on ibgib VCS.
78
- */
79
-
80
- import { getGlobalMetaspace } from '@ibgib/web-gib';
81
- import { add } from './helper.mjs';
82
-
83
- export const APP_NAME = 'space-gib in-memory vcs';
84
-
85
- export async function boot(): Promise<void> {
86
- console.log(\`Initializing \${APP_NAME}...\`);
87
- const space = await getGlobalMetaspace();
88
- const sum = add(40, 2);
89
- console.log(\`Calculated sum: \${sum}\`);
90
- }
91
-
92
- boot();`;
93
-
94
83
  /**
95
84
  * Concrete implementation of the space-gib-code-editor component.
96
85
  */
@@ -101,11 +90,17 @@ export class CodeEditorComponentInstance
101
90
  protected lc: string = `[${CodeEditorComponentInstance.name}]`;
102
91
 
103
92
  private _readOnly: boolean = false;
104
- private filepath: string = 'src/index.ts';
105
- private repo: string = 'my-app';
93
+ private filepath: string = '';
94
+ private repo: string = '';
106
95
  private branch: string = 'main';
107
- private tipAddr: string = '7f8a1b2';
108
- private language: string = 'TypeScript';
96
+ private tipAddr: string = '';
97
+ private language: string = '';
98
+ private originalContent?: string;
99
+ private isUntracked: boolean = false;
100
+ private debounceTimer?: any;
101
+ private saveStartTime: number = 0;
102
+ private isSaving: boolean = false;
103
+ private branchColorCache = new Map<string, GibColorInfo>();
109
104
 
110
105
  get el(): CodeEditorElements {
111
106
  if (!this.elements) {
@@ -152,6 +147,8 @@ export class CodeEditorComponentInstance
152
147
  const root = this.shadowRoot!;
153
148
  this.elements = {
154
149
  rootEl: root.getElementById('editor-root') as HTMLDivElement,
150
+ placeholderEl: root.getElementById('editor-empty-placeholder') as HTMLDivElement,
151
+ mainContainerEl: root.getElementById('editor-main-container') as HTMLDivElement,
155
152
  headerEl: root.getElementById('editor-header') as HTMLElement,
156
153
  crumbRepoEl: root.getElementById('crumb-repo') as HTMLSpanElement,
157
154
  crumbPathEl: root.getElementById('crumb-path') as HTMLSpanElement,
@@ -162,6 +159,7 @@ export class CodeEditorComponentInstance
162
159
  statLinesEl: root.getElementById('stat-lines') as HTMLSpanElement,
163
160
  statSizeEl: root.getElementById('stat-size') as HTMLSpanElement,
164
161
  btnToggleReadonlyEl: root.getElementById('btn-toggle-readonly') as HTMLButtonElement,
162
+ btnSaveSpinnerEl: root.getElementById('btn-save-spinner') as HTMLSpanElement,
165
163
  modeIconEl: root.getElementById('mode-icon') as HTMLSpanElement,
166
164
  modeTextEl: root.getElementById('mode-text') as HTMLSpanElement,
167
165
  btnCopyCodeEl: root.getElementById('btn-copy-code') as HTMLButtonElement,
@@ -170,19 +168,20 @@ export class CodeEditorComponentInstance
170
168
  contentEl: root.getElementById('editor-root') as HTMLDivElement,
171
169
  };
172
170
 
173
- // Set initial content if empty
174
- if (this.el.editorTextareaEl && !this.el.editorTextareaEl.value) {
175
- this.el.editorTextareaEl.value = DEFAULT_CODE;
176
- }
177
-
178
171
  // Sync readonly attribute if set on host
179
172
  if (this.hasAttribute('readonly')) {
180
173
  this._readOnly = true;
181
174
  }
182
175
 
183
- this.updateReadOnlyState();
184
- this.updateGutter();
185
- this.updateStats();
176
+ if (!this.filepath) {
177
+ this.clearFile({ repo: this.repo, branch: this.branch });
178
+ } else {
179
+ this.updateReadOnlyState();
180
+ this.updateGutter();
181
+ this.updateStats();
182
+ this.updateBranchColorStyling();
183
+ }
184
+
186
185
  this.bindEvents();
187
186
 
188
187
  } catch (error) {
@@ -194,20 +193,19 @@ export class CodeEditorComponentInstance
194
193
  const lc = `${this.lc}[${this.bindEvents.name}]`;
195
194
  if (!this.el) return;
196
195
 
197
- // Textarea input: update line numbers & stats
196
+ // Textarea input: update line numbers & stats, reset 500ms debounce
198
197
  this.el.editorTextareaEl?.addEventListener('input', () => {
199
198
  this.updateGutter();
200
199
  this.updateStats();
201
200
 
202
- this.dispatchEvent(new CustomEvent('space-gib-code-changed', {
203
- bubbles: true,
204
- composed: true,
205
- detail: {
206
- filepath: this.filepath,
207
- content: this.el.editorTextareaEl.value,
208
- lineCount: this.getLineCount(),
209
- }
210
- }));
201
+ if (this.debounceTimer) {
202
+ clearTimeout(this.debounceTimer);
203
+ }
204
+ this.debounceTimer = setTimeout(() => {
205
+ this.saveStartTime = Date.now();
206
+ this.showSavingIndicator();
207
+ this.handleDebouncedSave();
208
+ }, 500);
211
209
  });
212
210
 
213
211
  // Synchronize scroll between textarea and line number gutter
@@ -217,7 +215,7 @@ export class CodeEditorComponentInstance
217
215
  }
218
216
  });
219
217
 
220
- // Handle Tab key in textarea for code editing
218
+ // Handle Tab key in textarea for indentation
221
219
  this.el.editorTextareaEl?.addEventListener('keydown', (e: KeyboardEvent) => {
222
220
  if (this._readOnly) return;
223
221
 
@@ -293,17 +291,89 @@ export class CodeEditorComponentInstance
293
291
  if (this.el.btnToggleReadonlyEl) {
294
292
  this.el.btnToggleReadonlyEl.classList.toggle('readonly', this._readOnly);
295
293
  this.el.btnToggleReadonlyEl.classList.toggle('editable', !this._readOnly);
294
+ if (!this.isSaving) {
295
+ this.el.btnToggleReadonlyEl.classList.remove('saving');
296
+ }
296
297
  }
297
298
 
298
- if (this.el.modeIconEl) {
299
- this.el.modeIconEl.textContent = this._readOnly ? '🔒' : '✏️';
299
+ if (!this.isSaving) {
300
+ if (this.el.btnSaveSpinnerEl) {
301
+ this.el.btnSaveSpinnerEl.style.display = 'none';
302
+ }
303
+ if (this.el.modeIconEl) {
304
+ this.el.modeIconEl.style.display = 'inline';
305
+ this.el.modeIconEl.textContent = this._readOnly ? '🔒' : '✏️';
306
+ }
307
+ if (this.el.modeTextEl) {
308
+ this.el.modeTextEl.textContent = this._readOnly ? 'Read-Only' : 'Editable';
309
+ }
300
310
  }
311
+ }
301
312
 
302
- if (this.el.modeTextEl) {
303
- this.el.modeTextEl.textContent = this._readOnly ? 'Read-Only' : 'Editable';
313
+ private showSavingIndicator(): void {
314
+ this.isSaving = true;
315
+ if (this.elements) {
316
+ if (this.el.btnSaveSpinnerEl) {
317
+ this.el.btnSaveSpinnerEl.style.display = 'inline-block';
318
+ }
319
+ if (this.el.modeIconEl) {
320
+ this.el.modeIconEl.style.display = 'none';
321
+ }
322
+ if (this.el.modeTextEl) {
323
+ this.el.modeTextEl.textContent = 'Saving...';
324
+ }
325
+ if (this.el.btnToggleReadonlyEl) {
326
+ this.el.btnToggleReadonlyEl.classList.add('saving');
327
+ }
304
328
  }
305
329
  }
306
330
 
331
+ private hideSavingIndicator(): void {
332
+ this.isSaving = false;
333
+ if (this.elements) {
334
+ if (this.el.btnSaveSpinnerEl) {
335
+ this.el.btnSaveSpinnerEl.style.display = 'none';
336
+ }
337
+ if (this.el.modeIconEl) {
338
+ this.el.modeIconEl.style.display = 'inline';
339
+ }
340
+ if (this.el.btnToggleReadonlyEl) {
341
+ this.el.btnToggleReadonlyEl.classList.remove('saving');
342
+ }
343
+ this.updateReadOnlyState();
344
+ }
345
+ }
346
+
347
+ private async handleDebouncedSave(): Promise<void> {
348
+ if (!this.filepath) return;
349
+ const currentContent = this.getContent();
350
+
351
+ // Diff against originalContent
352
+ const isReverted = !this.isUntracked && this.originalContent !== undefined && currentContent === this.originalContent;
353
+
354
+ this.dispatchEvent(new CustomEvent('space-gib-code-changed', {
355
+ bubbles: true,
356
+ composed: true,
357
+ detail: {
358
+ filepath: this.filepath,
359
+ content: currentContent,
360
+ lineCount: this.getLineCount(),
361
+ isReverted,
362
+ isUntracked: this.isUntracked,
363
+ repo: this.repo,
364
+ branch: this.branch,
365
+ }
366
+ }));
367
+
368
+ // Minimum 0.5s (500ms) animation duration for saving indicator
369
+ const elapsed = Date.now() - this.saveStartTime;
370
+ const remaining = Math.max(0, 500 - elapsed);
371
+ setTimeout(() => {
372
+ this.hideSavingIndicator();
373
+ this.saveStartTime = 0;
374
+ }, remaining);
375
+ }
376
+
307
377
  private getLineCount(): number {
308
378
  const text = this.el?.editorTextareaEl?.value || '';
309
379
  return text.split('\n').length;
@@ -339,17 +409,35 @@ export class CodeEditorComponentInstance
339
409
  public loadFile(opts: {
340
410
  filepath: string;
341
411
  content?: string;
412
+ originalContent?: string;
413
+ isUntracked?: boolean;
342
414
  branch?: string;
343
415
  tipAddr?: string;
344
416
  language?: string;
345
417
  repo?: string;
346
418
  readOnly?: boolean;
347
419
  }): void {
420
+ if (this.debounceTimer) {
421
+ clearTimeout(this.debounceTimer);
422
+ this.debounceTimer = undefined;
423
+ }
424
+ this.saveStartTime = 0;
425
+ this.hideSavingIndicator();
426
+
348
427
  this.filepath = opts.filepath || this.filepath;
349
428
  if (opts.repo) this.repo = opts.repo;
350
429
  if (opts.branch) this.branch = opts.branch;
351
430
  if (opts.tipAddr) this.tipAddr = opts.tipAddr;
352
431
 
432
+ if (opts.originalContent !== undefined) {
433
+ this.originalContent = opts.originalContent;
434
+ } else if (opts.content !== undefined && !opts.isUntracked) {
435
+ this.originalContent = opts.content;
436
+ } else {
437
+ this.originalContent = undefined;
438
+ }
439
+ this.isUntracked = Boolean(opts.isUntracked);
440
+
353
441
  // Auto-detect language from extension if not provided
354
442
  if (opts.language) {
355
443
  this.language = opts.language;
@@ -386,6 +474,9 @@ export class CodeEditorComponentInstance
386
474
  }
387
475
 
388
476
  if (this.elements) {
477
+ if (this.el.placeholderEl) this.el.placeholderEl.style.display = 'none';
478
+ if (this.el.mainContainerEl) this.el.mainContainerEl.style.display = 'flex';
479
+
389
480
  if (this.el.crumbRepoEl) this.el.crumbRepoEl.textContent = this.repo;
390
481
  if (this.el.crumbPathEl) this.el.crumbPathEl.textContent = this.filepath;
391
482
  if (this.el.crumbBranchEl) this.el.crumbBranchEl.textContent = this.branch;
@@ -395,9 +486,69 @@ export class CodeEditorComponentInstance
395
486
  this.el.editorTextareaEl.value = opts.content;
396
487
  }
397
488
 
489
+ this.updateReadOnlyState();
490
+ this.updateGutter();
491
+ this.updateStats();
492
+ }
493
+ this.updateBranchColorStyling();
494
+ }
495
+
496
+ public setTipAddr(tipAddr: string): void {
497
+ this.tipAddr = tipAddr;
498
+ if (this.elements && this.el.crumbTipEl) {
499
+ this.el.crumbTipEl.textContent = tipAddr;
500
+ }
501
+ }
502
+
503
+ public markAsCommitted(tipAddr?: string): void {
504
+ this.originalContent = this.getContent();
505
+ this.isUntracked = false;
506
+ if (tipAddr) {
507
+ this.setTipAddr(tipAddr);
508
+ }
509
+ }
510
+
511
+ /**
512
+ * Clears the active file and displays the empty state placeholder.
513
+ */
514
+ public clearFile(opts?: { repo?: string; branch?: string }): void {
515
+ if (this.debounceTimer) {
516
+ clearTimeout(this.debounceTimer);
517
+ this.debounceTimer = undefined;
518
+ }
519
+ this.saveStartTime = 0;
520
+ this.hideSavingIndicator();
521
+ this.originalContent = undefined;
522
+ this.isUntracked = false;
523
+
524
+ this.filepath = '';
525
+ if (opts?.repo) this.repo = opts.repo;
526
+ if (opts?.branch) this.branch = opts.branch;
527
+ this.tipAddr = '';
528
+ this.language = '';
529
+
530
+ if (this.elements) {
531
+ if (this.el.placeholderEl) this.el.placeholderEl.style.display = 'flex';
532
+ if (this.el.mainContainerEl) this.el.mainContainerEl.style.display = 'none';
533
+
534
+ if (this.el.crumbRepoEl) this.el.crumbRepoEl.textContent = this.repo;
535
+ if (this.el.crumbPathEl) this.el.crumbPathEl.textContent = '';
536
+ if (this.el.crumbBranchEl) this.el.crumbBranchEl.textContent = this.branch;
537
+ if (this.el.crumbTipEl) this.el.crumbTipEl.textContent = '';
538
+
539
+ if (this.el.editorTextareaEl) {
540
+ this.el.editorTextareaEl.value = '';
541
+ }
542
+
543
+ this.updateReadOnlyState();
398
544
  this.updateGutter();
399
545
  this.updateStats();
400
546
  }
547
+ this.updateBranchColorStyling();
548
+ }
549
+
550
+ public getFilepath(): string {
551
+ return this.filepath;
401
552
  }
402
553
 
403
554
  public getContent(): string {
@@ -416,6 +567,71 @@ export class CodeEditorComponentInstance
416
567
  this.readOnly = readOnly;
417
568
  }
418
569
 
570
+ public setGalaxyAndBranch(repo: string, branch: string = 'main'): void {
571
+ this.repo = repo;
572
+ this.branch = branch;
573
+ if (this.elements) {
574
+ if (this.el.crumbRepoEl) this.el.crumbRepoEl.textContent = this.repo;
575
+ if (this.el.crumbBranchEl) this.el.crumbBranchEl.textContent = this.branch;
576
+ }
577
+ this.updateBranchColorStyling();
578
+ }
579
+
580
+ public clearBranchColorCache(): void {
581
+ this.branchColorCache.clear();
582
+ }
583
+
584
+ private async resolveGalaxyIbGib(galaxyName: string, space: any): Promise<B2tFSGalaxyIbGib_V1 | undefined> {
585
+ const router = getSpaceGibRouterSvc();
586
+ for (const manifest of router.cosmosManifestCache?.values() || []) {
587
+ const entry = manifest.data?.galaxies?.[galaxyName];
588
+ if (entry?.galaxyAddr) {
589
+ const gib = await getIbGibFromSpace<B2tFSGalaxyIbGib_V1>({ addr: entry.galaxyAddr, space });
590
+ if (gib) return gib;
591
+ }
592
+ }
593
+ return undefined;
594
+ }
595
+
596
+ public async updateBranchColorStyling(): Promise<void> {
597
+ try {
598
+ if (!this.repo) return;
599
+ const cacheKey = `${this.repo || 'unknown'}~${this.branch}`;
600
+ let colorInfo = this.branchColorCache.get(cacheKey);
601
+ if (!colorInfo) {
602
+ const { metaspace, space } = await getPlaygroundMetaspaceAndSpace();
603
+ const galaxyIbGib = await this.resolveGalaxyIbGib(this.repo, space);
604
+ let branchAddrOrTjp: string | undefined;
605
+ if (galaxyIbGib) {
606
+ const branchAndRoot = await getBranchAndRootItemInfosAndIbGibsAndSpaceFromGalaxyIbGib({
607
+ galaxyIbGib,
608
+ branchName: this.branch,
609
+ metaspace,
610
+ }).catch(() => undefined);
611
+ if (branchAndRoot?.branchIbGib) {
612
+ branchAddrOrTjp = getTjpAddr({ ibGib: branchAndRoot.branchIbGib }) || branchAndRoot.branchInfo?.branchAddr;
613
+ }
614
+ }
615
+ colorInfo = getDeterministicColorInfo({
616
+ ibGibAddr: branchAddrOrTjp,
617
+ gib: branchAddrOrTjp ? undefined : this.branch,
618
+ translucentAlpha: 18,
619
+ });
620
+ if (this.repo && branchAddrOrTjp) {
621
+ this.branchColorCache.set(cacheKey, colorInfo);
622
+ }
623
+ }
624
+
625
+ if (colorInfo) {
626
+ this.style.setProperty('--branch-accent-color', colorInfo.punctiliarColor);
627
+ this.style.setProperty('--branch-accent-translucent', colorInfo.punctiliarColorTranslucent);
628
+ this.style.setProperty('--branch-accent-contrast', colorInfo.punctiliarColorContrast);
629
+ }
630
+ } catch (err) {
631
+ console.warn(`${this.lc} Error updating branch color styling: ${extractErrorMsg(err)}`);
632
+ }
633
+ }
634
+
419
635
  override async disconnected(): Promise<void> {
420
636
  const lc = `${this.lc}[${this.disconnected.name}]`;
421
637
  try {
@@ -861,4 +861,4 @@
861
861
  100% {
862
862
  transform: translate(-50%, calc(-50% - 215px)) scale(1);
863
863
  }
864
- }
864
+ }
@@ -58,28 +58,23 @@
58
58
  <span id="active-cosmos-title-caption" class="node-caption">Cosmos 1</span>
59
59
  <span id="active-cosmos-temp-badge" class="cosmos-temp-badge hidden">✨ In-Memory (Temporary)</span>
60
60
 
61
- <!-- Second-Order Satellites (Unfolds & Revolves around Galaxy) -->
61
+ <!-- Second-Order Satellites (Revolving around Cosmos) -->
62
62
  <div id="superspace-satellites" class="superspace-satellites">
63
63
  <!-- Visual Orbit Track Guide Ring -->
64
64
  <div class="satellite-orbit-guide-ring"></div>
65
-
66
- <!-- Code Space -->
67
65
  <div class="satellite-wrapper sat-code-pos">
68
- <button id="btn-sat-code" class="satellite-node-btn" type="button" title="Code Space (Repositories / VCS)">
66
+ <button id="btn-sat-code" class="satellite-node-btn" type="button" title="Code Galaxy (Repositories / VCS)">
69
67
  <span class="satellite-emoji">&lt;/&gt;</span>
70
68
  <span class="satellite-pulse"></span>
71
69
  </button>
72
- <span class="satellite-caption">Code Space</span>
70
+ <span class="satellite-caption">Code Galaxy</span>
73
71
  </div>
74
-
75
- <!-- Text Space -->
76
72
  <div class="satellite-wrapper sat-notes-pos">
77
- <button id="btn-sat-text" class="satellite-node-btn" type="button"
78
- title="Text Space (Metatext Discussions & Notes)">
73
+ <button id="btn-sat-text" class="satellite-node-btn" type="button" title="Notes Galaxy (Discussions &amp; Notes)">
79
74
  <span class="satellite-emoji">📝</span>
80
75
  <span class="satellite-pulse"></span>
81
76
  </button>
82
- <span class="satellite-caption">Text Space</span>
77
+ <span class="satellite-caption">Notes Galaxy</span>
83
78
  </div>
84
79
  </div>
85
80
  </div>