@ibgib/space-gib 0.0.33 → 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.
- package/dist/client/bootstrap.mjs +24 -51
- package/dist/client/chunk-FIAGRVBY.mjs +10933 -0
- package/dist/client/chunk-XLQ3UCQY.mjs +53 -0
- package/dist/client/css/activity-bar.css +435 -0
- package/dist/client/css/base.css +167 -0
- package/dist/client/css/components.css +218 -0
- package/dist/client/css/cosmic.css +557 -0
- package/dist/client/css/dev-tools.css +163 -0
- package/dist/client/css/landing.css +184 -0
- package/dist/client/css/layout.css +1017 -0
- package/dist/client/css/onboarding.css +181 -0
- package/dist/client/css/repos.css +740 -0
- package/dist/client/css/variables.css +64 -0
- package/dist/client/index.html +208 -31
- package/dist/client/index.mjs +22 -45
- package/dist/client/privacy.html +22 -22
- package/dist/client/script.mjs +1 -1
- package/dist/client/style.css +11 -1056
- package/dist/respec-gib.node.mjs +5 -0
- package/dist/server/server.mjs +6880 -3110
- package/package.json +6 -6
- package/src/client/AUTO-GENERATED-version.mts +1 -1
- package/src/client/cosmos/seed-cosmos-workspace.mts +83 -0
- package/src/client/css/activity-bar.css +435 -0
- package/src/client/css/base.css +167 -0
- package/src/client/css/components.css +218 -0
- package/src/client/css/cosmic.css +557 -0
- package/src/client/css/dev-tools.css +163 -0
- package/src/client/css/landing.css +184 -0
- package/src/client/css/layout.css +1017 -0
- package/src/client/css/onboarding.css +181 -0
- package/src/client/css/repos.css +740 -0
- package/src/client/css/variables.css +64 -0
- package/src/client/dev-tools/vcs-workspace.mts +7 -7
- package/src/client/index.html +208 -31
- package/src/client/privacy.html +22 -22
- package/src/client/style.css +11 -1056
- package/src/client/ui/component/changes/changes.css +413 -0
- package/src/client/ui/component/changes/changes.html +37 -0
- package/src/client/ui/component/changes/changes.mts +766 -0
- package/src/client/ui/component/changes/index.mts +9 -0
- package/src/client/ui/component/chat-view/IMPLEMENTATION.md +48 -0
- package/src/client/ui/component/chat-view/chat-view.css +381 -0
- package/src/client/ui/component/chat-view/chat-view.html +52 -0
- package/src/client/ui/component/chat-view/chat-view.mts +264 -0
- package/src/client/ui/component/chat-view/index.mts +9 -0
- package/src/client/ui/component/clone/IMPLEMENTATION.md +68 -0
- package/src/client/ui/component/clone/clone-constants.mts +16 -0
- package/src/client/ui/component/clone/clone-types.mts +60 -0
- package/src/client/ui/component/clone/clone.css +383 -0
- package/src/client/ui/component/clone/clone.html +89 -0
- package/src/client/ui/component/clone/clone.mts +648 -0
- package/src/client/ui/component/clone/index.mts +11 -0
- package/src/client/ui/component/code-editor/IMPLEMENTATION.md +89 -0
- package/src/client/ui/component/code-editor/code-editor.css +357 -0
- package/src/client/ui/component/code-editor/code-editor.html +67 -0
- package/src/client/ui/component/code-editor/code-editor.mts +643 -0
- package/src/client/ui/component/code-editor/index.mts +9 -0
- package/src/client/ui/component/cosmos-navigator/IMPLEMENTATION.md +31 -0
- package/src/client/ui/component/cosmos-navigator/cosmos-navigator.css +864 -0
- package/src/client/ui/component/cosmos-navigator/cosmos-navigator.html +92 -0
- package/src/client/ui/component/cosmos-navigator/cosmos-navigator.mts +1317 -0
- package/src/client/ui/component/cosmos-navigator/index.mts +18 -0
- package/src/client/ui/component/file-explorer/IMPLEMENTATION.md +42 -0
- package/src/client/ui/component/file-explorer/file-explorer.css +720 -0
- package/src/client/ui/component/file-explorer/file-explorer.html +55 -0
- package/src/client/ui/component/file-explorer/file-explorer.mts +1412 -0
- package/src/client/ui/component/file-explorer/index.mts +9 -0
- package/src/client/ui/component/nested-chat/IMPLEMENTATION.md +18 -0
- package/src/client/ui/component/nested-chat/chat/IMPLEMENTATION.md +14 -0
- package/src/client/ui/component/nested-chat/chat/chat-constants.mts +14 -0
- package/src/client/ui/component/nested-chat/chat/chat-helpers.mts +66 -0
- package/src/client/ui/component/nested-chat/chat/chat.css +654 -0
- package/src/client/ui/component/nested-chat/chat/chat.html +90 -0
- package/src/client/ui/component/nested-chat/chat/chat.mts +561 -0
- package/src/client/ui/component/nested-chat/chat/index.mts +7 -0
- package/src/client/ui/component/nested-chat/index.mts +14 -0
- package/src/client/ui/component/nested-chat/nested-chat-constants.mts +12 -0
- package/src/client/ui/component/nested-chat/nested-chat-types.mts +53 -0
- package/src/client/ui/component/nested-chat/nested-chat.css +142 -0
- package/src/client/ui/component/nested-chat/nested-chat.html +11 -0
- package/src/client/ui/component/nested-chat/nested-chat.mts +395 -0
- package/src/client/ui/component/notes-explorer/index.mts +9 -0
- package/src/client/ui/component/notes-explorer/notes-explorer.css +313 -0
- package/src/client/ui/component/notes-explorer/notes-explorer.html +136 -0
- package/src/client/ui/component/notes-explorer/notes-explorer.mts +240 -0
- package/src/client/ui/component/notes-inspector/index.mts +9 -0
- package/src/client/ui/component/notes-inspector/notes-inspector.css +244 -0
- package/src/client/ui/component/notes-inspector/notes-inspector.html +97 -0
- package/src/client/ui/component/notes-inspector/notes-inspector.mts +152 -0
- package/src/client/ui/component/pitch/IMPLEMENTATION.md +48 -0
- package/src/client/ui/component/pitch/index.mts +9 -0
- package/src/client/ui/component/pitch/pitch.css +295 -0
- package/src/client/ui/component/pitch/pitch.html +97 -0
- package/src/client/ui/component/pitch/pitch.mts +135 -0
- package/src/client/ui/component/timeline/IMPLEMENTATION.md +33 -0
- package/src/client/ui/component/timeline/index.mts +9 -0
- package/src/client/ui/component/timeline/timeline.css +279 -0
- package/src/client/ui/component/timeline/timeline.html +23 -0
- package/src/client/ui/component/timeline/timeline.mts +261 -0
- package/src/client/ui/component/timeline-item-details/IMPLEMENTATION.md +28 -0
- package/src/client/ui/component/timeline-item-details/index.mts +9 -0
- package/src/client/ui/component/timeline-item-details/timeline-item-details.css +286 -0
- package/src/client/ui/component/timeline-item-details/timeline-item-details.html +64 -0
- package/src/client/ui/component/timeline-item-details/timeline-item-details.mts +194 -0
- package/src/client/ui/router/index.mts +3 -0
- package/src/client/ui/router/space-gib-router-helpers.mts +72 -0
- package/src/client/ui/router/space-gib-router-helpers.respec.mts +529 -0
- package/src/client/ui/router/space-gib-router-service.mts +63 -0
- package/src/client/ui/router/space-gib-router-types.mts +49 -0
- package/src/client/ui/shell/cosmic-big-bang.mts +853 -0
- package/src/client/ui/shell/space-gib-shell-constants.mts +7 -0
- package/src/client/ui/shell/space-gib-shell-service.mts +2297 -32
- package/src/server/server.mts +20 -1
- package/tsconfig.test.json +5 -1
- package/dist/client/chunk-CRJV762I.mjs +0 -3102
- package/dist/client/chunk-LXRCF5OS.mjs +0 -25
- package/dist/client/chunk-UBDQUZZV.mjs +0 -1
|
@@ -1,32 +1,217 @@
|
|
|
1
1
|
import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
2
|
+
import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
2
3
|
import { ROOT_ADDR } from "@ibgib/ts-gib/dist/V1/constants.mjs";
|
|
3
4
|
import { getComponentSvc } from "@ibgib/web-gib/dist/ui/component/ibgib-component-service.mjs";
|
|
4
|
-
import { IbGibDynamicComponentMeta } from "@ibgib/web-gib/dist/ui/component/component-types.mjs";
|
|
5
|
-
|
|
6
|
-
import { APP_CONFIG } from "../../constants.mjs";
|
|
7
|
-
import { getIbGibGlobalThis_SpaceGib } from "../../helpers.web.mjs";
|
|
8
|
-
import {
|
|
9
|
-
ID_APP_ROOT, ID_HEADER_PANEL, ID_LEFT_PANEL, ID_CENTER_PANEL,
|
|
10
|
-
ID_RIGHT_PANEL, ID_FOOTER_PANEL, ID_LEFT_RESIZER, ID_RIGHT_RESIZER,
|
|
11
|
-
ID_FOOTER_RESIZER, ID_BTN_LEFT_PANEL_TOGGLE, ID_BTN_RIGHT_PANEL_TOGGLE,
|
|
12
|
-
ID_BTN_CREATE_KEYSTONE, ID_CENTER_PANEL_CONTENT
|
|
13
|
-
} from "./space-gib-shell-constants.mjs";
|
|
14
5
|
import {
|
|
15
6
|
EVENT_IBGIB_SHELL_READY, EVENT_IBGIB_UI_BUSY, EVENT_IBGIB_UI_MESSAGE,
|
|
16
|
-
|
|
17
|
-
UI_ACTION_SHOW_ADD_DEVICE,
|
|
7
|
+
EVENT_IBGIB_IDENTITY_CHANGED, UI_ACTION_SHOW_IDENTITY_MANAGER,
|
|
8
|
+
UI_ACTION_SHOW_KEYSTONE_CREATOR, UI_ACTION_SHOW_ADD_DEVICE,
|
|
9
|
+
UI_ACTION_SHOW_PRIVACY_POLICY
|
|
18
10
|
} from "@ibgib/web-gib/dist/ui/ui-constants.mjs";
|
|
19
11
|
import { showFullscreenDialog, FullscreenDialogController } from "@ibgib/web-gib/dist/ui/ui-helpers.mjs";
|
|
20
|
-
import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
21
12
|
import { getKeystoneSyncService } from "@ibgib/web-gib/dist/identity/keystone-sync-service.mjs";
|
|
22
|
-
import { PanelState } from "./space-gib-shell-types.mjs";
|
|
23
13
|
import { registerStandardIdentityComponents } from "@ibgib/web-gib/dist/ui/component/identity/index.mjs";
|
|
24
14
|
import { registerPrivacyPolicyComponent } from "@ibgib/web-gib/dist/ui/component/privacy-policy/index.mjs";
|
|
15
|
+
import { registerAdminReviewComponent } from "@ibgib/web-gib/dist/ui/component/admin/admin-review/index.mjs";
|
|
16
|
+
|
|
17
|
+
import { APP_CONFIG } from "../../constants.mjs";
|
|
18
|
+
import { getIbGibGlobalThis_SpaceGib } from "../../helpers.web.mjs";
|
|
19
|
+
import {
|
|
20
|
+
ID_HEADER_PANEL, ID_LEFT_PANEL, ID_CENTER_PANEL, ID_RIGHT_PANEL,
|
|
21
|
+
ID_FOOTER_PANEL, ID_LEFT_RESIZER, ID_RIGHT_RESIZER, ID_FOOTER_RESIZER,
|
|
22
|
+
ID_BTN_LEFT_PANEL_TOGGLE, ID_BTN_RIGHT_PANEL_TOGGLE, ID_BTN_CREATE_KEYSTONE,
|
|
23
|
+
ID_CENTER_PANEL_CONTENT,
|
|
24
|
+
} from "./space-gib-shell-constants.mjs";
|
|
25
|
+
import { PanelState } from "./space-gib-shell-types.mjs";
|
|
26
|
+
import {
|
|
27
|
+
registerCosmosNavigatorComponent, COSMOS_NAVIGATOR_COMPONENT_NAME,
|
|
28
|
+
CosmosNavigatorComponentInstance, CosmosInfo,
|
|
29
|
+
} from "../component/cosmos-navigator/index.mjs";
|
|
30
|
+
import {
|
|
31
|
+
registerFileExplorerComponent, FILE_EXPLORER_COMPONENT_NAME,
|
|
32
|
+
FileExplorerComponentInstance,
|
|
33
|
+
} from "../component/file-explorer/index.mjs";
|
|
34
|
+
import {
|
|
35
|
+
registerChangesComponent, CHANGES_COMPONENT_NAME,
|
|
36
|
+
ChangesComponentInstance,
|
|
37
|
+
} from "../component/changes/index.mjs";
|
|
38
|
+
import {
|
|
39
|
+
registerTimelineComponent, TIMELINE_COMPONENT_NAME,
|
|
40
|
+
TimelineComponentInstance, TimelineItem,
|
|
41
|
+
} from "../component/timeline/index.mjs";
|
|
42
|
+
import {
|
|
43
|
+
registerTimelineItemDetailsComponent, TIMELINE_ITEM_DETAILS_COMPONENT_NAME,
|
|
44
|
+
TimelineItemDetailsComponentInstance,
|
|
45
|
+
} from "../component/timeline-item-details/index.mjs";
|
|
46
|
+
import {
|
|
47
|
+
registerChatViewComponent, ChatViewComponentInstance,
|
|
48
|
+
} from "../component/chat-view/index.mjs";
|
|
49
|
+
import {
|
|
50
|
+
registerNestedChatComponent, NESTED_CHAT_COMPONENT_NAME,
|
|
51
|
+
NestedChatComponentInstance,
|
|
52
|
+
} from "../component/nested-chat/index.mjs";
|
|
53
|
+
import {
|
|
54
|
+
registerPitchComponent, PITCH_COMPONENT_NAME, PitchComponentInstance,
|
|
55
|
+
} from "../component/pitch/index.mjs";
|
|
56
|
+
import {
|
|
57
|
+
registerCodeEditorComponent, CODE_EDITOR_COMPONENT_NAME,
|
|
58
|
+
CodeEditorComponentInstance,
|
|
59
|
+
} from "../component/code-editor/index.mjs";
|
|
60
|
+
import {
|
|
61
|
+
registerCloneComponent, CLONE_COMPONENT_NAME, CloneComponentInstance,
|
|
62
|
+
CloneModalOptions, EVENT_SPACE_GIB_CLONE_CLOSED,
|
|
63
|
+
} from "../component/clone/index.mjs";
|
|
64
|
+
import {
|
|
65
|
+
registerNotesExplorerComponent, NOTES_EXPLORER_COMPONENT_NAME,
|
|
66
|
+
NotesExplorerComponentInstance,
|
|
67
|
+
} from "../component/notes-explorer/index.mjs";
|
|
68
|
+
import {
|
|
69
|
+
registerNotesInspectorComponent, NOTES_INSPECTOR_COMPONENT_NAME,
|
|
70
|
+
NotesInspectorComponentInstance,
|
|
71
|
+
} from "../component/notes-inspector/index.mjs";
|
|
72
|
+
import {
|
|
73
|
+
getSpaceGibRouterSvc,
|
|
74
|
+
SpaceGibRouteInfo,
|
|
75
|
+
SpaceGibTenant,
|
|
76
|
+
GalaxyRouteInfo,
|
|
77
|
+
RawAddrRouteInfo,
|
|
78
|
+
RawTjpRouteInfo,
|
|
79
|
+
} from "../router/index.mjs";
|
|
80
|
+
import { GalaxyPolicy } from "@ibgib/core-gib/dist/cosmology/galaxy/galaxy-types.mjs";
|
|
81
|
+
import { getGlobalMetaspace_waitIfNeeded } from "@ibgib/web-gib/dist/helpers.mjs";
|
|
82
|
+
import { getPlaygroundMetaspaceAndSpace, prewarmPlaygroundMetaspace } from "../../cosmos/seed-cosmos-workspace.mjs";
|
|
83
|
+
import {
|
|
84
|
+
getBranchAndRootItemInfosAndIbGibsAndSpaceFromGalaxyIbGib,
|
|
85
|
+
getFileItemFromPath,
|
|
86
|
+
getFileContent,
|
|
87
|
+
} from "@ibgib/core-gib/dist/cosmology/branching/item/item-helpers.mjs";
|
|
88
|
+
import { GalaxyIbGib_V1 } from "@ibgib/core-gib/dist/cosmology/galaxy/galaxy-types.mjs";
|
|
89
|
+
import { B2tFSGalaxyIbGib_V1 } from "@ibgib/core-gib/dist/cosmology/branching/repo/b2tfs-repo-types.mjs";
|
|
90
|
+
import { getIbGibFromSpace } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
|
|
91
|
+
import {
|
|
92
|
+
executeVcsBranch,
|
|
93
|
+
executeVcsCheckout,
|
|
94
|
+
} from "@ibgib/core-gib/dist/cosmology/branching/commands/index.mjs";
|
|
95
|
+
import { validateBranchName } from "@ibgib/core-gib/dist/cosmology/branching/branch/branch-helpers.mjs";
|
|
96
|
+
|
|
97
|
+
export interface PanelLoadContext {
|
|
98
|
+
route: GalaxyRouteInfo;
|
|
99
|
+
cosmosName: string;
|
|
100
|
+
galaxyName: string;
|
|
101
|
+
discriminator?: string;
|
|
102
|
+
policy: GalaxyPolicy;
|
|
103
|
+
cosmosInfo?: CosmosInfo;
|
|
104
|
+
shell: SpaceGibShellService;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type PanelName = 'LeftPanel' | 'CenterPanel' | 'RightPanel';
|
|
108
|
+
|
|
109
|
+
export type PanelLoaderFn = (route: GalaxyRouteInfo, context: PanelLoadContext) => Promise<void>;
|
|
110
|
+
|
|
111
|
+
export type PanelLoaderMap = {
|
|
112
|
+
[K in PanelName as `loadRouteFor${K}`]: PanelLoaderFn;
|
|
113
|
+
};
|
|
25
114
|
|
|
26
115
|
export class SpaceGibShellService {
|
|
27
116
|
private lc: string = `[SpaceGibShellService]`;
|
|
28
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Default convention panel loaders for Branching Galaxies (Class I Substrate / Code).
|
|
120
|
+
*/
|
|
121
|
+
protected branchingPanelLoaders: PanelLoaderMap = {
|
|
122
|
+
loadRouteForLeftPanel: async (route, context) => {
|
|
123
|
+
this.expandActivityBar();
|
|
124
|
+
this.expandLeftPanel();
|
|
125
|
+
this.setActiveActivityBarButton('space', route.galaxy);
|
|
126
|
+
this.showLeftPanelView('code', route.galaxy);
|
|
127
|
+
if (this.fileExplorerInstance) {
|
|
128
|
+
this.fileExplorerInstance.setGalaxyAndBranch(route.galaxy, route.branch || 'main');
|
|
129
|
+
if (route.filePath) {
|
|
130
|
+
this.fileExplorerInstance.selectFile(route.filePath);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (this.changesInstance) {
|
|
134
|
+
this.changesInstance.setGalaxyAndBranch(route.galaxy, route.branch || 'main', { preserveChanges: true });
|
|
135
|
+
}
|
|
136
|
+
if (this.codeEditorInstance) {
|
|
137
|
+
this.codeEditorInstance.setGalaxyAndBranch(route.galaxy, route.branch || 'main');
|
|
138
|
+
}
|
|
139
|
+
if (this.timelineInstance) {
|
|
140
|
+
this.timelineInstance.setBranch(route.branch || 'main');
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
loadRouteForCenterPanel: async (route, context) => {
|
|
144
|
+
this.switchToReposMode({ skipNavigate: true });
|
|
145
|
+
await this.showCode(route.filePath, route.branch, route.galaxy);
|
|
146
|
+
},
|
|
147
|
+
loadRouteForRightPanel: async (route, context) => {
|
|
148
|
+
if (route.query?.inspect) {
|
|
149
|
+
this.expandRightPanel();
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Default convention panel loaders for Linear Galaxies (Class IV Discourse / Notes).
|
|
156
|
+
*/
|
|
157
|
+
protected linearPanelLoaders: PanelLoaderMap = {
|
|
158
|
+
loadRouteForLeftPanel: async (route, context) => {
|
|
159
|
+
this.expandActivityBar();
|
|
160
|
+
this.expandLeftPanel();
|
|
161
|
+
this.setActiveActivityBarButton('space', route.galaxy);
|
|
162
|
+
this.showLeftPanelView('text', route.galaxy);
|
|
163
|
+
},
|
|
164
|
+
loadRouteForCenterPanel: async (route, context) => {
|
|
165
|
+
this.switchToNotesMode(route.topicTjp || route.galaxy, { skipNavigate: true, galaxyId: route.galaxy });
|
|
166
|
+
await this.showNotes(route.topicTjp || route.galaxy, {
|
|
167
|
+
trail: route.trail,
|
|
168
|
+
trailMode: route.trailMode,
|
|
169
|
+
query: route.query,
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
loadRouteForRightPanel: async (route, context) => {
|
|
173
|
+
if (route.query?.inspect) {
|
|
174
|
+
this.expandRightPanel();
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Registry of convention panel loaders keyed by topology or discriminator.
|
|
181
|
+
*/
|
|
182
|
+
protected panelLoaderRegistry: Record<string, PanelLoaderMap> = {
|
|
183
|
+
branching: this.branchingPanelLoaders,
|
|
184
|
+
linear: this.linearPanelLoaders,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
public registerPanelLoaders(key: string, loaders: PanelLoaderMap): void {
|
|
188
|
+
this.panelLoaderRegistry[key] = loaders;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
public async loadRouteForPanels(route: GalaxyRouteInfo, context: PanelLoadContext): Promise<void> {
|
|
192
|
+
const loaders =
|
|
193
|
+
(context.discriminator ? this.panelLoaderRegistry[context.discriminator] : undefined) ||
|
|
194
|
+
this.panelLoaderRegistry[context.policy.topology] ||
|
|
195
|
+
this.branchingPanelLoaders;
|
|
196
|
+
|
|
197
|
+
await loaders.loadRouteForLeftPanel(route, context);
|
|
198
|
+
await loaders.loadRouteForCenterPanel(route, context);
|
|
199
|
+
await loaders.loadRouteForRightPanel(route, context);
|
|
200
|
+
}
|
|
201
|
+
|
|
29
202
|
public initialized: Promise<void>;
|
|
203
|
+
private isApplyingRoute: boolean = false;
|
|
204
|
+
private cosmosNavigatorInstance?: CosmosNavigatorComponentInstance;
|
|
205
|
+
private fileExplorerInstance?: FileExplorerComponentInstance;
|
|
206
|
+
private changesInstance?: ChangesComponentInstance;
|
|
207
|
+
private timelineInstance?: TimelineComponentInstance;
|
|
208
|
+
private timelineItemDetailsInstance?: TimelineItemDetailsComponentInstance;
|
|
209
|
+
private chatViewInstance?: ChatViewComponentInstance;
|
|
210
|
+
private nestedChatInstance?: NestedChatComponentInstance;
|
|
211
|
+
private pitchInstance?: PitchComponentInstance;
|
|
212
|
+
private codeEditorInstance?: CodeEditorComponentInstance;
|
|
213
|
+
private notesExplorerInstance?: NotesExplorerComponentInstance;
|
|
214
|
+
private notesInspectorInstance?: NotesInspectorComponentInstance;
|
|
30
215
|
|
|
31
216
|
// Panel States
|
|
32
217
|
private leftPanelState: PanelState = 'collapsed';
|
|
@@ -36,6 +221,11 @@ export class SpaceGibShellService {
|
|
|
36
221
|
private isResizingLeft = false;
|
|
37
222
|
private isResizingRight = false;
|
|
38
223
|
private isResizingFooter = false;
|
|
224
|
+
private isResizingExplorerChanges = false;
|
|
225
|
+
private isResizingChangesTimeline = false;
|
|
226
|
+
|
|
227
|
+
// Playground Banner State
|
|
228
|
+
private hasBigBangExploded = false;
|
|
39
229
|
|
|
40
230
|
constructor() {
|
|
41
231
|
this.initialized = this.initialize();
|
|
@@ -45,6 +235,13 @@ export class SpaceGibShellService {
|
|
|
45
235
|
this.initElements();
|
|
46
236
|
this.initEventHandlers();
|
|
47
237
|
await this.registerComponents();
|
|
238
|
+
await this.initCosmosNavigator();
|
|
239
|
+
await this.initFileExplorer();
|
|
240
|
+
await this.initChanges();
|
|
241
|
+
await this.initTimeline();
|
|
242
|
+
await this.initChatView();
|
|
243
|
+
await this.initCodeEditor();
|
|
244
|
+
await this.initPitch();
|
|
48
245
|
}
|
|
49
246
|
|
|
50
247
|
private initElements(): void {
|
|
@@ -60,6 +257,14 @@ export class SpaceGibShellService {
|
|
|
60
257
|
private initEventHandlers(): void {
|
|
61
258
|
const lc = `${this.lc}[${this.initEventHandlers.name}]`;
|
|
62
259
|
try {
|
|
260
|
+
// Playground Mode Banner actions
|
|
261
|
+
const btnBannerCreate = document.getElementById('btn-banner-create-identity');
|
|
262
|
+
if (btnBannerCreate) {
|
|
263
|
+
btnBannerCreate.addEventListener('click', () => {
|
|
264
|
+
this.showKeystoneCreator();
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
63
268
|
// Left Panel Toggle
|
|
64
269
|
const btnLeft = document.getElementById(ID_BTN_LEFT_PANEL_TOGGLE);
|
|
65
270
|
if (btnLeft) {
|
|
@@ -94,6 +299,44 @@ export class SpaceGibShellService {
|
|
|
94
299
|
footerResizer.addEventListener('mousedown', () => this.isResizingFooter = true);
|
|
95
300
|
}
|
|
96
301
|
|
|
302
|
+
// Accordion Horizontal Resizers (Left Panel)
|
|
303
|
+
const resizerExplorerChanges = document.getElementById('resizer-explorer-changes');
|
|
304
|
+
if (resizerExplorerChanges) {
|
|
305
|
+
resizerExplorerChanges.addEventListener('mousedown', (e) => {
|
|
306
|
+
e.preventDefault();
|
|
307
|
+
this.isResizingExplorerChanges = true;
|
|
308
|
+
resizerExplorerChanges.classList.add('active');
|
|
309
|
+
});
|
|
310
|
+
resizerExplorerChanges.addEventListener('dblclick', () => {
|
|
311
|
+
const explorerEl = document.getElementById('code-file-explorer-container');
|
|
312
|
+
if (explorerEl) {
|
|
313
|
+
explorerEl.style.flex = '';
|
|
314
|
+
explorerEl.style.height = '';
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const resizerChangesTimeline = document.getElementById('resizer-changes-timeline');
|
|
320
|
+
if (resizerChangesTimeline) {
|
|
321
|
+
resizerChangesTimeline.addEventListener('mousedown', (e) => {
|
|
322
|
+
e.preventDefault();
|
|
323
|
+
this.isResizingChangesTimeline = true;
|
|
324
|
+
resizerChangesTimeline.classList.add('active');
|
|
325
|
+
});
|
|
326
|
+
resizerChangesTimeline.addEventListener('dblclick', () => {
|
|
327
|
+
const changesEl = document.getElementById('code-changes-container');
|
|
328
|
+
const timelineEl = document.getElementById('code-timeline-container');
|
|
329
|
+
if (changesEl) {
|
|
330
|
+
changesEl.style.flex = '';
|
|
331
|
+
changesEl.style.height = '';
|
|
332
|
+
}
|
|
333
|
+
if (timelineEl) {
|
|
334
|
+
timelineEl.style.flex = '';
|
|
335
|
+
timelineEl.style.height = '';
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
97
340
|
// Window mouse events for dragging
|
|
98
341
|
window.addEventListener('mousemove', (e) => this.handleMouseMove(e));
|
|
99
342
|
window.addEventListener('mouseup', () => this.handleMouseUp());
|
|
@@ -123,6 +366,8 @@ export class SpaceGibShellService {
|
|
|
123
366
|
this.showAddDevice();
|
|
124
367
|
} else if (action === UI_ACTION_SHOW_PRIVACY_POLICY) {
|
|
125
368
|
this.showPrivacyPolicy();
|
|
369
|
+
} else if (action === 'show-admin-review' || action === 'admin-review') {
|
|
370
|
+
this.showAdminReview();
|
|
126
371
|
}
|
|
127
372
|
});
|
|
128
373
|
|
|
@@ -162,11 +407,47 @@ export class SpaceGibShellService {
|
|
|
162
407
|
}
|
|
163
408
|
});
|
|
164
409
|
|
|
410
|
+
// Mode Switcher (Spaces vs Repositories)
|
|
411
|
+
this.initModeSwitcher();
|
|
412
|
+
|
|
165
413
|
} catch (error) {
|
|
166
414
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
167
415
|
}
|
|
168
416
|
}
|
|
169
417
|
|
|
418
|
+
private currentMode: 'spaces' | 'repos' | 'notes' = 'spaces';
|
|
419
|
+
|
|
420
|
+
private setMode(mode: 'spaces' | 'repos' | 'notes', notesTitle?: string): void {
|
|
421
|
+
this.currentMode = mode;
|
|
422
|
+
if (mode === 'repos') {
|
|
423
|
+
this.showCode();
|
|
424
|
+
} else if (mode === 'notes') {
|
|
425
|
+
this.showNotes(notesTitle);
|
|
426
|
+
} else {
|
|
427
|
+
this.showCosmos();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private initModeSwitcher(): void {
|
|
432
|
+
const lc = `${this.lc}[${this.initModeSwitcher.name}]`;
|
|
433
|
+
try {
|
|
434
|
+
// URL param mode check (spaces by default, in-memory)
|
|
435
|
+
let initialMode: 'spaces' | 'repos' = 'spaces';
|
|
436
|
+
try {
|
|
437
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
438
|
+
const urlMode = urlParams.get('mode');
|
|
439
|
+
if (urlMode === 'repos') {
|
|
440
|
+
initialMode = 'repos';
|
|
441
|
+
}
|
|
442
|
+
} catch {
|
|
443
|
+
// ignore
|
|
444
|
+
}
|
|
445
|
+
this.setMode(initialMode);
|
|
446
|
+
} catch (error) {
|
|
447
|
+
console.error(`${lc} ${extractErrorMsg(error)} (E: c8e054e0639221c7b673949d9b2a2d26)`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
170
451
|
public async showIdentityManager() {
|
|
171
452
|
const lc = `${this.lc}[${this.showIdentityManager.name}]`;
|
|
172
453
|
try {
|
|
@@ -262,6 +543,37 @@ export class SpaceGibShellService {
|
|
|
262
543
|
}
|
|
263
544
|
}
|
|
264
545
|
|
|
546
|
+
public async showAdminReview(tjpGib?: string) {
|
|
547
|
+
const lc = `${this.lc}[${this.showAdminReview.name}]`;
|
|
548
|
+
try {
|
|
549
|
+
const componentSvc = await getComponentSvc();
|
|
550
|
+
|
|
551
|
+
// create the component
|
|
552
|
+
const adminReviewComponent = await componentSvc.getComponentInstance({
|
|
553
|
+
path: 'ibgib-admin-review',
|
|
554
|
+
ibGibAddr: ROOT_ADDR, // Virtual address for initial render
|
|
555
|
+
useRegExpPrefilter: true,
|
|
556
|
+
});
|
|
557
|
+
if (!adminReviewComponent) { throw new Error(`(UNEXPECTED) adminReviewComponent falsy? (E: 8392a019bb2819827364810293847199)`); }
|
|
558
|
+
|
|
559
|
+
if (tjpGib && typeof (adminReviewComponent as any).setTjpGib === 'function') {
|
|
560
|
+
(adminReviewComponent as any).setTjpGib(tjpGib);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// get a ref to the parent container
|
|
564
|
+
const centerPanel = document.getElementById(ID_CENTER_PANEL_CONTENT);
|
|
565
|
+
if (!centerPanel) { throw new Error(`(UNEXPECTED) centerPanel falsy? (E: 9392a019bb2819827364810293847199)`); }
|
|
566
|
+
|
|
567
|
+
// inject via componentSvc
|
|
568
|
+
await componentSvc.inject({
|
|
569
|
+
parentEl: centerPanel,
|
|
570
|
+
componentToInject: adminReviewComponent
|
|
571
|
+
});
|
|
572
|
+
} catch (error) {
|
|
573
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
265
577
|
private toggleLeftPanel() {
|
|
266
578
|
this.leftPanelState = this.leftPanelState === 'expanded' ? 'collapsed' : 'expanded';
|
|
267
579
|
const panel = document.getElementById(ID_LEFT_PANEL);
|
|
@@ -287,7 +599,7 @@ export class SpaceGibShellService {
|
|
|
287
599
|
}
|
|
288
600
|
|
|
289
601
|
private handleMouseMove(e: MouseEvent) {
|
|
290
|
-
if (!this.isResizingLeft && !this.isResizingRight && !this.isResizingFooter) return;
|
|
602
|
+
if (!this.isResizingLeft && !this.isResizingRight && !this.isResizingFooter && !this.isResizingExplorerChanges && !this.isResizingChangesTimeline) return;
|
|
291
603
|
|
|
292
604
|
// Prevent text selection while dragging
|
|
293
605
|
e.preventDefault();
|
|
@@ -309,12 +621,40 @@ export class SpaceGibShellService {
|
|
|
309
621
|
let newHeight = Math.max(24, Math.min(window.innerHeight - e.clientY, 400));
|
|
310
622
|
document.documentElement.style.setProperty('--footer-panel-height', `${newHeight}px`);
|
|
311
623
|
}
|
|
624
|
+
|
|
625
|
+
if (this.isResizingExplorerChanges) {
|
|
626
|
+
const explorerEl = document.getElementById('code-file-explorer-container');
|
|
627
|
+
if (explorerEl) {
|
|
628
|
+
const rect = explorerEl.getBoundingClientRect();
|
|
629
|
+
const parentHeight = explorerEl.parentElement?.clientHeight || 600;
|
|
630
|
+
const maxExplorerHeight = parentHeight - 75;
|
|
631
|
+
const newHeight = Math.max(76, Math.min(e.clientY - rect.top, maxExplorerHeight));
|
|
632
|
+
explorerEl.style.flex = 'none';
|
|
633
|
+
explorerEl.style.height = `${newHeight}px`;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (this.isResizingChangesTimeline) {
|
|
638
|
+
const changesEl = document.getElementById('code-changes-container');
|
|
639
|
+
if (changesEl) {
|
|
640
|
+
const rect = changesEl.getBoundingClientRect();
|
|
641
|
+
const parentHeight = changesEl.parentElement?.clientHeight || 600;
|
|
642
|
+
const maxChangesHeight = parentHeight - (changesEl.offsetTop || 0) - 35;
|
|
643
|
+
const newHeight = Math.max(35, Math.min(e.clientY - rect.top, maxChangesHeight));
|
|
644
|
+
changesEl.style.flex = 'none';
|
|
645
|
+
changesEl.style.height = `${newHeight}px`;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
312
648
|
}
|
|
313
649
|
|
|
314
650
|
private handleMouseUp() {
|
|
315
651
|
this.isResizingLeft = false;
|
|
316
652
|
this.isResizingRight = false;
|
|
317
653
|
this.isResizingFooter = false;
|
|
654
|
+
this.isResizingExplorerChanges = false;
|
|
655
|
+
this.isResizingChangesTimeline = false;
|
|
656
|
+
document.getElementById('resizer-explorer-changes')?.classList.remove('active');
|
|
657
|
+
document.getElementById('resizer-changes-timeline')?.classList.remove('active');
|
|
318
658
|
}
|
|
319
659
|
|
|
320
660
|
private updateCssVariables() {
|
|
@@ -326,40 +666,1965 @@ export class SpaceGibShellService {
|
|
|
326
666
|
try {
|
|
327
667
|
await registerStandardIdentityComponents();
|
|
328
668
|
await registerPrivacyPolicyComponent();
|
|
669
|
+
await registerAdminReviewComponent();
|
|
670
|
+
await registerCosmosNavigatorComponent();
|
|
671
|
+
await registerFileExplorerComponent();
|
|
672
|
+
await registerChangesComponent();
|
|
673
|
+
await registerTimelineComponent();
|
|
674
|
+
await registerTimelineItemDetailsComponent();
|
|
675
|
+
await registerChatViewComponent();
|
|
676
|
+
await registerNestedChatComponent();
|
|
677
|
+
await registerPitchComponent();
|
|
678
|
+
await registerCodeEditorComponent();
|
|
679
|
+
await registerCloneComponent();
|
|
680
|
+
await registerNotesExplorerComponent();
|
|
681
|
+
await registerNotesInspectorComponent();
|
|
329
682
|
} catch (error) {
|
|
330
683
|
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
331
684
|
}
|
|
332
685
|
}
|
|
333
686
|
|
|
334
|
-
|
|
335
|
-
const lc = `${this.lc}[${this.
|
|
687
|
+
private async initCosmosNavigator(): Promise<void> {
|
|
688
|
+
const lc = `${this.lc}[${this.initCosmosNavigator.name}]`;
|
|
336
689
|
try {
|
|
337
|
-
await
|
|
338
|
-
|
|
690
|
+
const componentSvc = await getComponentSvc();
|
|
691
|
+
this.cosmosNavigatorInstance = await componentSvc.getComponentInstance({
|
|
692
|
+
path: COSMOS_NAVIGATOR_COMPONENT_NAME,
|
|
693
|
+
ibGibAddr: ROOT_ADDR,
|
|
694
|
+
useRegExpPrefilter: true,
|
|
695
|
+
}) as CosmosNavigatorComponentInstance;
|
|
339
696
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
697
|
+
if (!this.cosmosNavigatorInstance) {
|
|
698
|
+
throw new Error(`Failed to instantiate ${COSMOS_NAVIGATOR_COMPONENT_NAME}`);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
this.bindCosmosNavigatorEvents();
|
|
702
|
+
this.bindCosmosChromeEvents();
|
|
703
|
+
|
|
704
|
+
// Initial state sync
|
|
705
|
+
const activeCosmos = this.cosmosNavigatorInstance.getActiveCosmos();
|
|
706
|
+
if (activeCosmos) {
|
|
707
|
+
this.updateActiveCosmosDisplay(activeCosmos);
|
|
708
|
+
}
|
|
709
|
+
const allCosmoses = this.cosmosNavigatorInstance.getCosmoses();
|
|
710
|
+
if (allCosmoses.length > 0) {
|
|
711
|
+
this.revealChrome();
|
|
712
|
+
}
|
|
713
|
+
this.updatePolycosmosList(allCosmoses, activeCosmos?.id || null);
|
|
714
|
+
|
|
715
|
+
// Inject directly into center panel on startup
|
|
716
|
+
await this.showCosmos();
|
|
717
|
+
|
|
718
|
+
} catch (error) {
|
|
719
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
public async showCosmos(): Promise<void> {
|
|
724
|
+
const lc = `${this.lc}[${this.showCosmos.name}]`;
|
|
725
|
+
try {
|
|
726
|
+
const centerPanel = document.getElementById(ID_CENTER_PANEL_CONTENT);
|
|
727
|
+
if (!centerPanel) {
|
|
728
|
+
console.warn(`${lc} ${ID_CENTER_PANEL_CONTENT} element not found.`);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if (!this.cosmosNavigatorInstance) {
|
|
733
|
+
const componentSvc = await getComponentSvc();
|
|
734
|
+
this.cosmosNavigatorInstance = await componentSvc.getComponentInstance({
|
|
735
|
+
path: COSMOS_NAVIGATOR_COMPONENT_NAME,
|
|
736
|
+
ibGibAddr: ROOT_ADDR,
|
|
737
|
+
useRegExpPrefilter: true,
|
|
738
|
+
}) as CosmosNavigatorComponentInstance;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (this.cosmosNavigatorInstance) {
|
|
742
|
+
const componentSvc = await getComponentSvc();
|
|
743
|
+
await componentSvc.inject({
|
|
744
|
+
parentEl: centerPanel,
|
|
745
|
+
componentToInject: this.cosmosNavigatorInstance,
|
|
746
|
+
});
|
|
747
|
+
this.cosmosNavigatorInstance.ensureStars();
|
|
748
|
+
}
|
|
749
|
+
} catch (error) {
|
|
750
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
public show404(message: string): void {
|
|
755
|
+
const centerPanel = document.getElementById(ID_CENTER_PANEL_CONTENT);
|
|
756
|
+
if (centerPanel) {
|
|
757
|
+
centerPanel.innerHTML = `
|
|
758
|
+
<div class="cosmos-not-found-view" style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #94a3b8; text-align: center; padding: 2rem; font-family: sans-serif;">
|
|
759
|
+
<div style="font-size: 3.5rem; margin-bottom: 1rem;">🌌 404</div>
|
|
760
|
+
<h2 style="font-size: 1.5rem; color: #fff; margin-bottom: 0.5rem; font-weight: 600;">Cosmos Not Found</h2>
|
|
761
|
+
<p style="margin-bottom: 1.5rem; max-width: 420px; font-size: 0.95rem; line-height: 1.5;">${message}</p>
|
|
762
|
+
<button id="btn-404-return" type="button" style="padding: 10px 22px; border-radius: 8px; background: rgba(124, 92, 252, 0.2); border: 1px solid rgba(124, 92, 252, 0.4); color: #c4b5fd; cursor: pointer; font-weight: 500; font-size: 0.95rem; transition: background 0.2s;">
|
|
763
|
+
Return to Polycosmos
|
|
764
|
+
</button>
|
|
765
|
+
</div>
|
|
766
|
+
`;
|
|
767
|
+
const returnBtn = document.getElementById('btn-404-return');
|
|
768
|
+
returnBtn?.addEventListener('click', () => {
|
|
769
|
+
getSpaceGibRouterSvc().navigate({ kind: 'polycosmos' });
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
this.collapsePanels();
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
private async initFileExplorer(): Promise<void> {
|
|
776
|
+
const lc = `${this.lc}[${this.initFileExplorer.name}]`;
|
|
777
|
+
try {
|
|
778
|
+
const container = document.getElementById('code-file-explorer-container');
|
|
779
|
+
if (!container) {
|
|
780
|
+
console.warn(`${lc} code-file-explorer-container element not found.`);
|
|
781
|
+
return;
|
|
344
782
|
}
|
|
345
783
|
|
|
346
|
-
// Mount identity-header component in the header bar
|
|
347
784
|
const componentSvc = await getComponentSvc();
|
|
348
|
-
|
|
349
|
-
path:
|
|
785
|
+
this.fileExplorerInstance = await componentSvc.getComponentInstance({
|
|
786
|
+
path: FILE_EXPLORER_COMPONENT_NAME,
|
|
350
787
|
ibGibAddr: ROOT_ADDR,
|
|
351
788
|
useRegExpPrefilter: true,
|
|
789
|
+
}) as FileExplorerComponentInstance;
|
|
790
|
+
|
|
791
|
+
if (!this.fileExplorerInstance) {
|
|
792
|
+
throw new Error(`Failed to instantiate ${FILE_EXPLORER_COMPONENT_NAME}`);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
await componentSvc.inject({
|
|
796
|
+
parentEl: container,
|
|
797
|
+
componentToInject: this.fileExplorerInstance,
|
|
352
798
|
});
|
|
353
799
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
800
|
+
this.bindFileExplorerEvents();
|
|
801
|
+
|
|
802
|
+
} catch (error) {
|
|
803
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
private bindFileExplorerEvents(): void {
|
|
808
|
+
window.addEventListener('space-gib-file-selected', (e: any) => {
|
|
809
|
+
const { branch, path, filename, galaxy } = e.detail || {};
|
|
810
|
+
console.log(`${this.lc} File selected: ${path || filename} on worktree branch ${branch} in galaxy ${galaxy}`);
|
|
811
|
+
const fullFile = path || filename;
|
|
812
|
+
if (!fullFile) {
|
|
813
|
+
return;
|
|
358
814
|
}
|
|
815
|
+
const branchName = branch || 'main';
|
|
816
|
+
const galaxyName = galaxy || 'code-1';
|
|
817
|
+
const tipAddr = 'tip';
|
|
359
818
|
|
|
360
|
-
|
|
819
|
+
this.chatViewInstance?.setTargetFile(fullFile, `${branchName} @ ${tipAddr}`);
|
|
820
|
+
this.nestedChatInstance?.activateIbGib({ addr: `file ${fullFile}^${tipAddr}` });
|
|
821
|
+
|
|
822
|
+
this.showCode(fullFile, branchName, galaxyName);
|
|
823
|
+
|
|
824
|
+
const activeCosmos = this.cosmosNavigatorInstance?.getActiveCosmos();
|
|
825
|
+
if (activeCosmos) {
|
|
826
|
+
const tenant = this.getActiveTenant();
|
|
827
|
+
getSpaceGibRouterSvc().navigate({
|
|
828
|
+
kind: 'galaxy',
|
|
829
|
+
cosmos: activeCosmos.id || activeCosmos.name,
|
|
830
|
+
galaxy: galaxyName,
|
|
831
|
+
topology: 'branching',
|
|
832
|
+
discriminator: 'code',
|
|
833
|
+
branch: branchName,
|
|
834
|
+
filePath: fullFile,
|
|
835
|
+
...(tenant ? { tenant } : {}),
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
window.addEventListener('space-gib-branch-switched', async (e: any) => {
|
|
841
|
+
const { galaxy, branch } = e.detail || {};
|
|
842
|
+
const targetGalaxy = galaxy || this.fileExplorerInstance?.getGalaxyName() || 'code-1';
|
|
843
|
+
const targetBranch = branch || 'main';
|
|
844
|
+
|
|
845
|
+
console.log(`${this.lc} Branch switched: [${targetGalaxy}] -> [${targetBranch}]`);
|
|
846
|
+
|
|
847
|
+
// 1. Update File Explorer, Changes, Timeline, Code Editor
|
|
848
|
+
this.fileExplorerInstance?.setGalaxyAndBranch(targetGalaxy, targetBranch);
|
|
849
|
+
this.changesInstance?.setGalaxyAndBranch(targetGalaxy, targetBranch, { preserveChanges: true });
|
|
850
|
+
this.codeEditorInstance?.setGalaxyAndBranch(targetGalaxy, targetBranch);
|
|
851
|
+
this.timelineInstance?.setBranch(targetBranch);
|
|
852
|
+
|
|
853
|
+
// 2. Refresh active file under new branch context
|
|
854
|
+
const activePath = this.fileExplorerInstance?.getSelectedFilePath() || this.codeEditorInstance?.getFilepath();
|
|
855
|
+
if (activePath) {
|
|
856
|
+
await this.showCode(activePath, targetBranch, targetGalaxy);
|
|
857
|
+
} else {
|
|
858
|
+
await this.showCode(undefined, targetBranch, targetGalaxy);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// 3. Update router navigation
|
|
862
|
+
const activeCosmos = this.cosmosNavigatorInstance?.getActiveCosmos();
|
|
863
|
+
if (activeCosmos) {
|
|
864
|
+
const tenant = this.getActiveTenant(activeCosmos);
|
|
865
|
+
getSpaceGibRouterSvc().navigate({
|
|
866
|
+
kind: 'galaxy',
|
|
867
|
+
cosmos: activeCosmos.id || activeCosmos.name,
|
|
868
|
+
galaxy: targetGalaxy,
|
|
869
|
+
topology: 'branching',
|
|
870
|
+
discriminator: 'code',
|
|
871
|
+
branch: targetBranch,
|
|
872
|
+
...(activePath ? { filePath: activePath } : {}),
|
|
873
|
+
...(tenant ? { tenant } : {}),
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
window.addEventListener('space-gib-view-mode-changed', (e: any) => {
|
|
879
|
+
const { mode } = e.detail || {};
|
|
880
|
+
if (mode) {
|
|
881
|
+
this.changesInstance?.setViewMode(mode);
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
window.addEventListener('space-gib-accordion-toggled', () => {
|
|
886
|
+
const explorerEl = document.getElementById('code-file-explorer-container');
|
|
887
|
+
const changesEl = document.getElementById('code-changes-container');
|
|
888
|
+
const timelineEl = document.getElementById('code-timeline-container');
|
|
889
|
+
if (explorerEl) { explorerEl.style.flex = ''; explorerEl.style.height = ''; }
|
|
890
|
+
if (changesEl) { changesEl.style.flex = ''; changesEl.style.height = ''; }
|
|
891
|
+
if (timelineEl) { timelineEl.style.flex = ''; timelineEl.style.height = ''; }
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
window.addEventListener('space-gib-new-file-requested', (e: any) => {
|
|
895
|
+
const { branch } = e.detail || {};
|
|
896
|
+
const filename = prompt(`Create new file on branch [${branch || 'main'}]:`, 'untitled.ts');
|
|
897
|
+
if (filename) {
|
|
898
|
+
console.log(`${this.lc} New file requested: ${filename} on branch ${branch}`);
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
window.addEventListener('space-gib-new-folder-requested', (e: any) => {
|
|
903
|
+
const { branch } = e.detail || {};
|
|
904
|
+
const foldername = prompt(`Create new folder on branch [${branch || 'main'}]:`, 'components');
|
|
905
|
+
if (foldername) {
|
|
906
|
+
console.log(`${this.lc} New folder requested: ${foldername} on branch ${branch}`);
|
|
907
|
+
}
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
private async initChanges(): Promise<void> {
|
|
912
|
+
const lc = `${this.lc}[${this.initChanges.name}]`;
|
|
913
|
+
try {
|
|
914
|
+
const container = document.getElementById('code-changes-container');
|
|
915
|
+
if (!container) {
|
|
916
|
+
console.warn(`${lc} code-changes-container element not found.`);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
const componentSvc = await getComponentSvc();
|
|
921
|
+
this.changesInstance = await componentSvc.getComponentInstance({
|
|
922
|
+
path: CHANGES_COMPONENT_NAME,
|
|
923
|
+
ibGibAddr: ROOT_ADDR,
|
|
924
|
+
useRegExpPrefilter: true,
|
|
925
|
+
}) as ChangesComponentInstance;
|
|
926
|
+
|
|
927
|
+
if (!this.changesInstance) {
|
|
928
|
+
throw new Error(`Failed to instantiate ${CHANGES_COMPONENT_NAME}`);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
await componentSvc.inject({
|
|
932
|
+
parentEl: container,
|
|
933
|
+
componentToInject: this.changesInstance,
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
if (this.fileExplorerInstance) {
|
|
937
|
+
this.changesInstance.setViewMode(this.fileExplorerInstance.getViewMode());
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
this.bindChangesEvents();
|
|
941
|
+
|
|
942
|
+
} catch (error) {
|
|
943
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
private bindChangesEvents(): void {
|
|
948
|
+
window.addEventListener('space-gib-commit-created', (e: any) => {
|
|
949
|
+
const { branch, message, tipAddr, shortHash } = e.detail || {};
|
|
950
|
+
console.log(`${this.lc} Commit created: [${shortHash}] "${message}" on branch [${branch}]`);
|
|
951
|
+
|
|
952
|
+
// 1. Clear status badges in file explorer for this branch
|
|
953
|
+
this.fileExplorerInstance?.clearAllStatusBadges(branch);
|
|
954
|
+
this.fileExplorerInstance?.updateDirtyBadges();
|
|
955
|
+
|
|
956
|
+
// 2. Add snapshot to timeline
|
|
957
|
+
this.timelineInstance?.addSnapshot({
|
|
958
|
+
hash: tipAddr,
|
|
959
|
+
shortHash: shortHash || (tipAddr ? tipAddr.slice(-8) : 'unknown'),
|
|
960
|
+
message: message || 'Update',
|
|
961
|
+
author: 'You',
|
|
962
|
+
timestamp: 'Just now',
|
|
963
|
+
branch: branch || 'main',
|
|
964
|
+
isTip: true,
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
// 3. Update breadcrumb tip and mark committed in code editor
|
|
968
|
+
if (this.codeEditorInstance) {
|
|
969
|
+
if (shortHash) {
|
|
970
|
+
this.codeEditorInstance.setTipAddr(shortHash);
|
|
971
|
+
}
|
|
972
|
+
const activePath = this.codeEditorInstance.getFilepath();
|
|
973
|
+
if (activePath) {
|
|
974
|
+
this.codeEditorInstance.markAsCommitted(shortHash || tipAddr);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
window.addEventListener('space-gib-file-created', (e: any) => {
|
|
980
|
+
const { path, content, galaxy, branch } = e.detail || {};
|
|
981
|
+
if (galaxy || branch) {
|
|
982
|
+
const g = galaxy || this.changesInstance?.getGalaxyName() || 'code-1';
|
|
983
|
+
const b = branch || this.changesInstance?.getBranchName() || 'main';
|
|
984
|
+
this.changesInstance?.setGalaxyAndBranch(g, b, { preserveChanges: true });
|
|
985
|
+
this.codeEditorInstance?.setGalaxyAndBranch(g, b);
|
|
986
|
+
}
|
|
987
|
+
if (path) {
|
|
988
|
+
this.changesInstance?.recordChange({
|
|
989
|
+
path,
|
|
990
|
+
status: 'untracked',
|
|
991
|
+
content: content ?? '',
|
|
992
|
+
});
|
|
993
|
+
this.fileExplorerInstance?.updateDirtyBadges();
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
|
|
997
|
+
window.addEventListener('space-gib-change-discarded', (e: any) => {
|
|
998
|
+
const { path, originalContent, branch, galaxy } = e.detail || {};
|
|
999
|
+
if (path) {
|
|
1000
|
+
const b = branch || this.fileExplorerInstance?.getBranchName() || 'main';
|
|
1001
|
+
const g = galaxy || this.fileExplorerInstance?.getGalaxyName() || 'code-1';
|
|
1002
|
+
if (this.fileExplorerInstance?.isUntracked(path, b, g)) {
|
|
1003
|
+
this.fileExplorerInstance.removeFile(path, b, g);
|
|
1004
|
+
if (this.codeEditorInstance && this.codeEditorInstance.getFilepath() === path) {
|
|
1005
|
+
this.codeEditorInstance.clearFile({ repo: g, branch: b });
|
|
1006
|
+
}
|
|
1007
|
+
} else {
|
|
1008
|
+
this.fileExplorerInstance?.setFileStatus(path, undefined, b, g);
|
|
1009
|
+
if (this.codeEditorInstance && this.codeEditorInstance.getFilepath() === path) {
|
|
1010
|
+
if (originalContent !== undefined) {
|
|
1011
|
+
this.codeEditorInstance.setContent(originalContent);
|
|
1012
|
+
} else {
|
|
1013
|
+
this.showCode(path, b, g);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
this.fileExplorerInstance?.updateDirtyBadges();
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
private async initTimeline(): Promise<void> {
|
|
1023
|
+
const lc = `${this.lc}[${this.initTimeline.name}]`;
|
|
1024
|
+
try {
|
|
1025
|
+
const container = document.getElementById('code-timeline-container');
|
|
1026
|
+
if (!container) {
|
|
1027
|
+
console.warn(`${lc} code-timeline-container element not found.`);
|
|
1028
|
+
return;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
const componentSvc = await getComponentSvc();
|
|
1032
|
+
this.timelineInstance = await componentSvc.getComponentInstance({
|
|
1033
|
+
path: TIMELINE_COMPONENT_NAME,
|
|
1034
|
+
ibGibAddr: ROOT_ADDR,
|
|
1035
|
+
useRegExpPrefilter: true,
|
|
1036
|
+
}) as TimelineComponentInstance;
|
|
1037
|
+
|
|
1038
|
+
if (!this.timelineInstance) {
|
|
1039
|
+
throw new Error(`Failed to instantiate ${TIMELINE_COMPONENT_NAME}`);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
await componentSvc.inject({
|
|
1043
|
+
parentEl: container,
|
|
1044
|
+
componentToInject: this.timelineInstance,
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
this.bindTimelineEvents();
|
|
1048
|
+
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
private bindTimelineEvents(): void {
|
|
1055
|
+
window.addEventListener('space-gib-timeline-item-selected', (e: any) => {
|
|
1056
|
+
const item: TimelineItem = e.detail?.item;
|
|
1057
|
+
if (item) {
|
|
1058
|
+
console.log(`${this.lc} Timeline snapshot selected: ${item.shortHash} - ${item.message}`);
|
|
1059
|
+
this.showTimelineItemDetails(item);
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
window.addEventListener('space-gib-timeline-details-closed', () => {
|
|
1064
|
+
console.log(`${this.lc} Timeline details closed. Returning to previous view.`);
|
|
1065
|
+
const currentRoute = getSpaceGibRouterSvc().getCurrentRoute();
|
|
1066
|
+
if (currentRoute && (currentRoute.kind === 'raw-addr' || currentRoute.kind === 'raw-tjp')) {
|
|
1067
|
+
const tenant = this.getActiveTenant() || currentRoute.tenant;
|
|
1068
|
+
getSpaceGibRouterSvc().navigate({
|
|
1069
|
+
kind: 'cosmos',
|
|
1070
|
+
cosmos: currentRoute.cosmos,
|
|
1071
|
+
...(tenant ? { tenant } : {}),
|
|
1072
|
+
});
|
|
1073
|
+
} else {
|
|
1074
|
+
this.showCode();
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
private async initChatView(): Promise<void> {
|
|
1080
|
+
const lc = `${this.lc}[${this.initChatView.name}]`;
|
|
1081
|
+
try {
|
|
1082
|
+
const container = document.getElementById('code-chat-view-container');
|
|
1083
|
+
if (!container) {
|
|
1084
|
+
console.warn(`${lc} code-chat-view-container element not found.`);
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
const componentSvc = await getComponentSvc();
|
|
1089
|
+
this.nestedChatInstance = await componentSvc.getComponentInstance({
|
|
1090
|
+
path: NESTED_CHAT_COMPONENT_NAME,
|
|
1091
|
+
ibGibAddr: ROOT_ADDR,
|
|
1092
|
+
useRegExpPrefilter: true,
|
|
1093
|
+
}) as NestedChatComponentInstance;
|
|
1094
|
+
|
|
1095
|
+
if (!this.nestedChatInstance) {
|
|
1096
|
+
throw new Error(`Failed to instantiate ${NESTED_CHAT_COMPONENT_NAME}`);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
await componentSvc.inject({
|
|
1100
|
+
parentEl: container,
|
|
1101
|
+
componentToInject: this.nestedChatInstance,
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
this.bindChatViewEvents();
|
|
1105
|
+
|
|
1106
|
+
} catch (error) {
|
|
1107
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
private bindChatViewEvents(): void {
|
|
1112
|
+
window.addEventListener('space-gib-chat-close-requested', () => {
|
|
1113
|
+
this.toggleRightPanel();
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
window.addEventListener('space-gib-chat-closed', () => {
|
|
1117
|
+
if (this.rightPanelState === 'expanded') {
|
|
1118
|
+
this.toggleRightPanel();
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
window.addEventListener('space-gib-chat-message-sent', (e: any) => {
|
|
1123
|
+
const { message, targetFile, contextAddr } = e.detail || {};
|
|
1124
|
+
console.log(`${this.lc} Chat message sent on context ${contextAddr || targetFile}:`, message);
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
private async initCodeEditor(): Promise<void> {
|
|
1129
|
+
const lc = `${this.lc}[${this.initCodeEditor.name}]`;
|
|
1130
|
+
try {
|
|
1131
|
+
const componentSvc = await getComponentSvc();
|
|
1132
|
+
this.codeEditorInstance = await componentSvc.getComponentInstance({
|
|
1133
|
+
path: CODE_EDITOR_COMPONENT_NAME,
|
|
1134
|
+
ibGibAddr: ROOT_ADDR,
|
|
1135
|
+
useRegExpPrefilter: true,
|
|
1136
|
+
}) as CodeEditorComponentInstance;
|
|
1137
|
+
|
|
1138
|
+
if (!this.codeEditorInstance) {
|
|
1139
|
+
throw new Error(`Failed to instantiate ${CODE_EDITOR_COMPONENT_NAME}`);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
this.bindCodeEditorEvents();
|
|
1143
|
+
|
|
1144
|
+
} catch (error) {
|
|
1145
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
public async showCode(filePath?: string, branch?: string, galaxy?: string): Promise<void> {
|
|
1150
|
+
const lc = `${this.lc}[${this.showCode.name}]`;
|
|
1151
|
+
try {
|
|
1152
|
+
const centerPanel = document.getElementById(ID_CENTER_PANEL_CONTENT);
|
|
1153
|
+
if (!centerPanel) {
|
|
1154
|
+
console.warn(`${lc} ${ID_CENTER_PANEL_CONTENT} element not found.`);
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
const currentRoute = getSpaceGibRouterSvc().getCurrentRoute();
|
|
1159
|
+
const targetGalaxy = galaxy || (currentRoute && 'galaxy' in currentRoute ? (currentRoute as any).galaxy : undefined) || this.fileExplorerInstance?.getGalaxyName() || this.changesInstance?.getGalaxyName() || 'code-1';
|
|
1160
|
+
const targetBranch = branch || (currentRoute && 'branch' in currentRoute ? (currentRoute as any).branch : undefined) || this.fileExplorerInstance?.getBranchName() || this.changesInstance?.getBranchName() || 'main';
|
|
1161
|
+
|
|
1162
|
+
if (!this.codeEditorInstance) {
|
|
1163
|
+
const componentSvc = await getComponentSvc();
|
|
1164
|
+
this.codeEditorInstance = await componentSvc.getComponentInstance({
|
|
1165
|
+
path: CODE_EDITOR_COMPONENT_NAME,
|
|
1166
|
+
ibGibAddr: ROOT_ADDR,
|
|
1167
|
+
useRegExpPrefilter: true,
|
|
1168
|
+
}) as CodeEditorComponentInstance;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
if (this.codeEditorInstance) {
|
|
1172
|
+
const componentSvc = await getComponentSvc();
|
|
1173
|
+
await componentSvc.inject({
|
|
1174
|
+
parentEl: centerPanel,
|
|
1175
|
+
componentToInject: this.codeEditorInstance,
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
this.codeEditorInstance.setGalaxyAndBranch(targetGalaxy, targetBranch);
|
|
1179
|
+
|
|
1180
|
+
if (filePath) {
|
|
1181
|
+
let casContent: string | undefined = undefined;
|
|
1182
|
+
let tipAddr: string = 'tip';
|
|
1183
|
+
|
|
1184
|
+
try {
|
|
1185
|
+
const { metaspace, space } = await getPlaygroundMetaspaceAndSpace();
|
|
1186
|
+
const router = getSpaceGibRouterSvc();
|
|
1187
|
+
let galaxyIbGib: B2tFSGalaxyIbGib_V1 | undefined;
|
|
1188
|
+
for (const manifest of router.cosmosManifestCache?.values() || []) {
|
|
1189
|
+
const entry = manifest.data?.galaxies?.[targetGalaxy];
|
|
1190
|
+
if (entry?.galaxyAddr) {
|
|
1191
|
+
galaxyIbGib = await getIbGibFromSpace<B2tFSGalaxyIbGib_V1>({ addr: entry.galaxyAddr, space });
|
|
1192
|
+
if (galaxyIbGib) break;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
if (galaxyIbGib) {
|
|
1196
|
+
const branchAndRoot = await getBranchAndRootItemInfosAndIbGibsAndSpaceFromGalaxyIbGib({
|
|
1197
|
+
galaxyIbGib,
|
|
1198
|
+
branchName: targetBranch,
|
|
1199
|
+
metaspace,
|
|
1200
|
+
});
|
|
1201
|
+
tipAddr = branchAndRoot.branchIbGib.gib || 'tip';
|
|
1202
|
+
const rootItem = branchAndRoot.rootItemIbGib_latest || branchAndRoot.rootItemIbGib;
|
|
1203
|
+
const fileItem = await getFileItemFromPath({
|
|
1204
|
+
branchSpace: branchAndRoot.branchSpace,
|
|
1205
|
+
rootItem,
|
|
1206
|
+
filePath,
|
|
1207
|
+
});
|
|
1208
|
+
if (fileItem) {
|
|
1209
|
+
casContent = await getFileContent({ branchSpace: branchAndRoot.branchSpace, fileItem });
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
} catch (readErr) {
|
|
1213
|
+
console.warn(`${lc} Error reading file [${filePath}] from branch space: ${extractErrorMsg(readErr)}`);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
const workingChange = this.changesInstance?.getChange(filePath, targetBranch);
|
|
1217
|
+
const isUntrackedInExplorer = this.fileExplorerInstance?.isUntracked(filePath, targetBranch, targetGalaxy);
|
|
1218
|
+
const fileExistsInCas = casContent !== undefined;
|
|
1219
|
+
const hasWorkingTreeChange = workingChange !== undefined;
|
|
1220
|
+
|
|
1221
|
+
// If file doesn't exist in CAS, and is neither in working tree nor marked untracked in explorer, clear editor
|
|
1222
|
+
if (!fileExistsInCas && !hasWorkingTreeChange && !isUntrackedInExplorer) {
|
|
1223
|
+
this.codeEditorInstance.clearFile({
|
|
1224
|
+
repo: targetGalaxy,
|
|
1225
|
+
branch: targetBranch,
|
|
1226
|
+
});
|
|
1227
|
+
this.fileExplorerInstance?.selectFile('');
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
const isUntracked = !fileExistsInCas;
|
|
1232
|
+
const content = workingChange?.content ?? casContent ?? '';
|
|
1233
|
+
|
|
1234
|
+
this.codeEditorInstance.loadFile({
|
|
1235
|
+
filepath: filePath,
|
|
1236
|
+
repo: targetGalaxy,
|
|
1237
|
+
branch: targetBranch,
|
|
1238
|
+
tipAddr,
|
|
1239
|
+
content,
|
|
1240
|
+
originalContent: casContent,
|
|
1241
|
+
isUntracked,
|
|
1242
|
+
});
|
|
1243
|
+
} else {
|
|
1244
|
+
this.codeEditorInstance.clearFile({
|
|
1245
|
+
repo: targetGalaxy,
|
|
1246
|
+
branch: targetBranch,
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
this.changesInstance?.setGalaxyAndBranch(targetGalaxy, targetBranch, { preserveChanges: true });
|
|
1252
|
+
|
|
1253
|
+
const codeChatContainer = document.getElementById('code-chat-view-container');
|
|
1254
|
+
const notesInspectorContainer = document.getElementById('notes-inspector-container');
|
|
1255
|
+
if (notesInspectorContainer) { notesInspectorContainer.style.display = 'none'; }
|
|
1256
|
+
if (codeChatContainer) {
|
|
1257
|
+
codeChatContainer.style.display = 'block';
|
|
1258
|
+
if (this.nestedChatInstance && this.nestedChatInstance.parentElement !== codeChatContainer) {
|
|
1259
|
+
codeChatContainer.appendChild(this.nestedChatInstance as any);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
this.expandRightPanel();
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
public async showNotes(
|
|
1270
|
+
title?: string,
|
|
1271
|
+
opts?: {
|
|
1272
|
+
trail?: string[];
|
|
1273
|
+
trailMode?: 'tjp' | 'addr';
|
|
1274
|
+
query?: Record<string, string | undefined>;
|
|
1275
|
+
}
|
|
1276
|
+
): Promise<void> {
|
|
1277
|
+
const lc = `${this.lc}[${this.showNotes.name}]`;
|
|
1278
|
+
try {
|
|
1279
|
+
const centerPanel = document.getElementById(ID_CENTER_PANEL_CONTENT);
|
|
1280
|
+
if (!centerPanel) {
|
|
1281
|
+
console.warn(`${lc} ${ID_CENTER_PANEL_CONTENT} element not found.`);
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
// In Notes mode, Center Panel is the primary nested chat topic stream!
|
|
1286
|
+
if (!this.nestedChatInstance) {
|
|
1287
|
+
const componentSvc = await getComponentSvc();
|
|
1288
|
+
this.nestedChatInstance = await componentSvc.getComponentInstance({
|
|
1289
|
+
path: NESTED_CHAT_COMPONENT_NAME,
|
|
1290
|
+
ibGibAddr: ROOT_ADDR,
|
|
1291
|
+
useRegExpPrefilter: true,
|
|
1292
|
+
}) as NestedChatComponentInstance;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
if (this.nestedChatInstance) {
|
|
1296
|
+
const componentSvc = await getComponentSvc();
|
|
1297
|
+
await componentSvc.inject({
|
|
1298
|
+
parentEl: centerPanel,
|
|
1299
|
+
componentToInject: this.nestedChatInstance,
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
if (opts?.trail && opts.trail.length > 0) {
|
|
1303
|
+
for (const step of opts.trail) {
|
|
1304
|
+
const stepAddr = opts.trailMode === 'addr' ? step : `topic ${step}^gib_notes`;
|
|
1305
|
+
await this.nestedChatInstance.activateIbGib({ addr: stepAddr });
|
|
1306
|
+
}
|
|
1307
|
+
} else {
|
|
1308
|
+
await this.nestedChatInstance.activateIbGib({ addr: `topic ${title || 'Notes Space 1'}^gib_notes` });
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// In Notes mode, Right Panel is the contextual inspector!
|
|
1313
|
+
await this.initNotesInspector();
|
|
1314
|
+
const codeChatContainer = document.getElementById('code-chat-view-container');
|
|
1315
|
+
const notesInspectorContainer = document.getElementById('notes-inspector-container');
|
|
1316
|
+
if (codeChatContainer) { codeChatContainer.style.display = 'none'; }
|
|
1317
|
+
if (notesInspectorContainer) { notesInspectorContainer.style.display = 'block'; }
|
|
1318
|
+
|
|
1319
|
+
if (opts?.query?.inspect && this.notesInspectorInstance) {
|
|
1320
|
+
const inspectVal = opts.query.inspect;
|
|
1321
|
+
if (inspectVal.includes('#L')) {
|
|
1322
|
+
const [file, lineStr] = inspectVal.split('#L');
|
|
1323
|
+
this.notesInspectorInstance.inspectFileAnchor({
|
|
1324
|
+
file,
|
|
1325
|
+
line: Number(lineStr) || 1,
|
|
1326
|
+
snippet: `// Anchor in ${file} at line ${lineStr}`,
|
|
1327
|
+
});
|
|
1328
|
+
} else {
|
|
1329
|
+
this.notesInspectorInstance.inspectNode({ addr: inspectVal });
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
this.expandRightPanel();
|
|
1334
|
+
} catch (error) {
|
|
1335
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
private async initNotesExplorer(title?: string): Promise<void> {
|
|
1340
|
+
const lc = `${this.lc}[${this.initNotesExplorer.name}]`;
|
|
1341
|
+
try {
|
|
1342
|
+
const container = document.getElementById('notes-explorer-container');
|
|
1343
|
+
if (!container) {
|
|
1344
|
+
console.warn(`${lc} notes-explorer-container element not found.`);
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
if (!this.notesExplorerInstance) {
|
|
1349
|
+
const componentSvc = await getComponentSvc();
|
|
1350
|
+
this.notesExplorerInstance = await componentSvc.getComponentInstance({
|
|
1351
|
+
path: NOTES_EXPLORER_COMPONENT_NAME,
|
|
1352
|
+
ibGibAddr: ROOT_ADDR,
|
|
1353
|
+
useRegExpPrefilter: true,
|
|
1354
|
+
}) as NotesExplorerComponentInstance;
|
|
1355
|
+
|
|
1356
|
+
if (this.notesExplorerInstance) {
|
|
1357
|
+
await componentSvc.inject({
|
|
1358
|
+
parentEl: container,
|
|
1359
|
+
componentToInject: this.notesExplorerInstance,
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
if (title && this.notesExplorerInstance) {
|
|
1365
|
+
this.notesExplorerInstance.setTitle(title);
|
|
1366
|
+
}
|
|
1367
|
+
} catch (error) {
|
|
1368
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
private async initNotesInspector(): Promise<void> {
|
|
1373
|
+
const lc = `${this.lc}[${this.initNotesInspector.name}]`;
|
|
1374
|
+
try {
|
|
1375
|
+
const container = document.getElementById('notes-inspector-container');
|
|
1376
|
+
if (!container) { return; }
|
|
1377
|
+
|
|
1378
|
+
if (!this.notesInspectorInstance) {
|
|
1379
|
+
const componentSvc = await getComponentSvc();
|
|
1380
|
+
this.notesInspectorInstance = await componentSvc.getComponentInstance({
|
|
1381
|
+
path: NOTES_INSPECTOR_COMPONENT_NAME,
|
|
1382
|
+
ibGibAddr: ROOT_ADDR,
|
|
1383
|
+
useRegExpPrefilter: true,
|
|
1384
|
+
}) as NotesInspectorComponentInstance;
|
|
1385
|
+
|
|
1386
|
+
if (this.notesInspectorInstance) {
|
|
1387
|
+
await componentSvc.inject({
|
|
1388
|
+
parentEl: container,
|
|
1389
|
+
componentToInject: this.notesInspectorInstance,
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
} catch (error) {
|
|
1394
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
public async showTimelineItemDetails(item: TimelineItem): Promise<void> {
|
|
1399
|
+
const lc = `${this.lc}[${this.showTimelineItemDetails.name}]`;
|
|
1400
|
+
try {
|
|
1401
|
+
const centerPanel = document.getElementById(ID_CENTER_PANEL_CONTENT);
|
|
1402
|
+
if (!centerPanel) {
|
|
1403
|
+
console.warn(`${lc} ${ID_CENTER_PANEL_CONTENT} element not found.`);
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
if (!this.timelineItemDetailsInstance) {
|
|
1408
|
+
const componentSvc = await getComponentSvc();
|
|
1409
|
+
this.timelineItemDetailsInstance = await componentSvc.getComponentInstance({
|
|
1410
|
+
path: TIMELINE_ITEM_DETAILS_COMPONENT_NAME,
|
|
1411
|
+
ibGibAddr: ROOT_ADDR,
|
|
1412
|
+
useRegExpPrefilter: true,
|
|
1413
|
+
}) as TimelineItemDetailsComponentInstance;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
if (this.timelineItemDetailsInstance) {
|
|
1417
|
+
const componentSvc = await getComponentSvc();
|
|
1418
|
+
await componentSvc.inject({
|
|
1419
|
+
parentEl: centerPanel,
|
|
1420
|
+
componentToInject: this.timelineItemDetailsInstance,
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1423
|
+
this.timelineItemDetailsInstance.setTimelineItem(item);
|
|
1424
|
+
}
|
|
1425
|
+
} catch (error) {
|
|
1426
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
private bindCodeEditorEvents(): void {
|
|
1431
|
+
window.addEventListener('space-gib-code-changed', (e: any) => {
|
|
1432
|
+
const { filepath, content, lineCount, isReverted, isUntracked: isUntrackedProp, branch, repo } = e.detail || {};
|
|
1433
|
+
const targetBranch = branch || this.changesInstance?.getBranchName() || 'main';
|
|
1434
|
+
const targetGalaxy = repo || this.changesInstance?.getGalaxyName() || 'code-1';
|
|
1435
|
+
console.log(`${this.lc} Code changed in ${filepath} on branch [${targetBranch}] (${lineCount} lines, isReverted=${isReverted})`);
|
|
1436
|
+
if (filepath) {
|
|
1437
|
+
if (isReverted) {
|
|
1438
|
+
this.changesInstance?.removeChange(filepath, targetBranch);
|
|
1439
|
+
this.fileExplorerInstance?.setFileStatus(filepath, undefined, targetBranch, targetGalaxy);
|
|
1440
|
+
} else {
|
|
1441
|
+
const isUntracked = Boolean(isUntrackedProp || this.changesInstance?.isUntracked(filepath, targetBranch) || this.fileExplorerInstance?.isUntracked(filepath, targetBranch, targetGalaxy));
|
|
1442
|
+
const status = isUntracked ? 'untracked' : 'modified';
|
|
1443
|
+
this.changesInstance?.recordChange({
|
|
1444
|
+
path: filepath,
|
|
1445
|
+
status,
|
|
1446
|
+
content: content ?? '',
|
|
1447
|
+
}, targetBranch);
|
|
1448
|
+
this.fileExplorerInstance?.setFileStatus(filepath, status, targetBranch, targetGalaxy);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
window.addEventListener('space-gib-code-copied', (e: any) => {
|
|
1454
|
+
const { filepath } = e.detail || {};
|
|
1455
|
+
console.log(`${this.lc} Code copied from ${filepath}`);
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
private async initPitch(): Promise<void> {
|
|
1460
|
+
const lc = `${this.lc}[${this.initPitch.name}]`;
|
|
1461
|
+
try {
|
|
1462
|
+
const componentSvc = await getComponentSvc();
|
|
1463
|
+
this.pitchInstance = await componentSvc.getComponentInstance({
|
|
1464
|
+
path: PITCH_COMPONENT_NAME,
|
|
1465
|
+
ibGibAddr: ROOT_ADDR,
|
|
1466
|
+
useRegExpPrefilter: true,
|
|
1467
|
+
}) as PitchComponentInstance;
|
|
1468
|
+
|
|
1469
|
+
if (!this.pitchInstance) {
|
|
1470
|
+
throw new Error(`Failed to instantiate ${PITCH_COMPONENT_NAME}`);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
this.bindPitchEvents();
|
|
1474
|
+
|
|
1475
|
+
} catch (error) {
|
|
1476
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
private bindPitchEvents(): void {
|
|
1481
|
+
window.addEventListener('space-gib-pitch-closed', () => {
|
|
1482
|
+
this.closePitch();
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
public collapsePanels(): void {
|
|
1487
|
+
this.leftPanelState = 'collapsed';
|
|
1488
|
+
const leftPanel = document.getElementById(ID_LEFT_PANEL);
|
|
1489
|
+
if (leftPanel) {
|
|
1490
|
+
leftPanel.classList.add('collapsed');
|
|
1491
|
+
}
|
|
1492
|
+
this.rightPanelState = 'collapsed';
|
|
1493
|
+
const rightPanel = document.getElementById(ID_RIGHT_PANEL);
|
|
1494
|
+
if (rightPanel) {
|
|
1495
|
+
rightPanel.classList.add('collapsed');
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
public async showPitch(): Promise<void> {
|
|
1500
|
+
const lc = `${this.lc}[${this.showPitch.name}]`;
|
|
1501
|
+
try {
|
|
1502
|
+
const centerPanel = document.getElementById(ID_CENTER_PANEL_CONTENT);
|
|
1503
|
+
if (!centerPanel) {
|
|
1504
|
+
console.warn(`${lc} ${ID_CENTER_PANEL_CONTENT} element not found.`);
|
|
1505
|
+
return;
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
if (!this.pitchInstance) {
|
|
1509
|
+
const componentSvc = await getComponentSvc();
|
|
1510
|
+
this.pitchInstance = await componentSvc.getComponentInstance({
|
|
1511
|
+
path: PITCH_COMPONENT_NAME,
|
|
1512
|
+
ibGibAddr: ROOT_ADDR,
|
|
1513
|
+
useRegExpPrefilter: true,
|
|
1514
|
+
}) as PitchComponentInstance;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
if (this.pitchInstance) {
|
|
1518
|
+
const componentSvc = await getComponentSvc();
|
|
1519
|
+
await componentSvc.inject({
|
|
1520
|
+
parentEl: centerPanel,
|
|
1521
|
+
componentToInject: this.pitchInstance,
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
this.collapsePanels();
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
public closePitch(): void {
|
|
1532
|
+
this.showCosmos();
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
public async openCloneModal(options?: CloneModalOptions): Promise<void> {
|
|
1536
|
+
const lc = `${this.lc}[${this.openCloneModal.name}]`;
|
|
1537
|
+
try {
|
|
1538
|
+
const componentSvc = await getComponentSvc();
|
|
1539
|
+
const cloneInstance = await componentSvc.getComponentInstance({
|
|
1540
|
+
path: CLONE_COMPONENT_NAME,
|
|
1541
|
+
ibGibAddr: ROOT_ADDR,
|
|
1542
|
+
useRegExpPrefilter: true,
|
|
1543
|
+
}) as CloneComponentInstance;
|
|
1544
|
+
|
|
1545
|
+
if (!cloneInstance) {
|
|
1546
|
+
throw new Error(`Failed to instantiate ${CLONE_COMPONENT_NAME}`);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
const activeCosmos = this.cosmosNavigatorInstance?.getActiveCosmos();
|
|
1550
|
+
const effectiveOpts: CloneModalOptions = {
|
|
1551
|
+
cosmosId: activeCosmos?.id || 'cosmos-1',
|
|
1552
|
+
cosmosName: activeCosmos?.name || 'Cosmos 1',
|
|
1553
|
+
...options,
|
|
1554
|
+
};
|
|
1555
|
+
cloneInstance.setOptions(effectiveOpts);
|
|
1556
|
+
|
|
1557
|
+
document.body.appendChild(cloneInstance);
|
|
1558
|
+
|
|
1559
|
+
cloneInstance.addEventListener(EVENT_SPACE_GIB_CLONE_CLOSED, () => {
|
|
1560
|
+
cloneInstance.remove();
|
|
1561
|
+
});
|
|
1562
|
+
} catch (error) {
|
|
1563
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
public revealChrome(): void {
|
|
1568
|
+
const header = document.getElementById(ID_HEADER_PANEL);
|
|
1569
|
+
const footer = document.getElementById(ID_FOOTER_PANEL);
|
|
1570
|
+
const footerResizer = document.getElementById(ID_FOOTER_RESIZER);
|
|
1571
|
+
|
|
1572
|
+
header?.classList.remove('pre-bang-hidden');
|
|
1573
|
+
header?.classList.add('chrome-fade-in');
|
|
1574
|
+
|
|
1575
|
+
footer?.classList.remove('pre-bang-hidden');
|
|
1576
|
+
footer?.classList.add('chrome-fade-in');
|
|
1577
|
+
|
|
1578
|
+
footerResizer?.classList.remove('pre-bang-hidden');
|
|
1579
|
+
footerResizer?.classList.add('chrome-fade-in');
|
|
1580
|
+
|
|
1581
|
+
setTimeout(() => {
|
|
1582
|
+
header?.classList.remove('chrome-fade-in');
|
|
1583
|
+
header?.classList.add('chrome-revealed');
|
|
1584
|
+
footer?.classList.remove('chrome-fade-in');
|
|
1585
|
+
footer?.classList.add('chrome-revealed');
|
|
1586
|
+
footerResizer?.classList.remove('chrome-fade-in');
|
|
1587
|
+
footerResizer?.classList.add('chrome-revealed');
|
|
1588
|
+
}, 2500);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
public setActiveActivityBarButton(type: 'polycosmos' | 'cosmos' | 'space', spaceId?: string): void {
|
|
1592
|
+
const btnPoly = document.getElementById('activity-btn-polycosmos');
|
|
1593
|
+
const btnCosmos = document.getElementById('activity-btn-cosmos');
|
|
1594
|
+
const activitySpaces = document.getElementById('activity-spaces-container');
|
|
1595
|
+
|
|
1596
|
+
if (type === 'polycosmos') {
|
|
1597
|
+
btnPoly?.classList.add('active');
|
|
1598
|
+
btnCosmos?.classList.remove('active');
|
|
1599
|
+
activitySpaces?.querySelectorAll('.activity-bar-btn').forEach(b => b.classList.remove('active'));
|
|
1600
|
+
} else if (type === 'cosmos') {
|
|
1601
|
+
btnCosmos?.classList.add('active');
|
|
1602
|
+
btnPoly?.classList.remove('active');
|
|
1603
|
+
activitySpaces?.querySelectorAll('.activity-bar-btn').forEach(b => b.classList.remove('active'));
|
|
1604
|
+
} else if (type === 'space') {
|
|
1605
|
+
btnPoly?.classList.remove('active');
|
|
1606
|
+
btnCosmos?.classList.remove('active');
|
|
1607
|
+
if (activitySpaces) {
|
|
1608
|
+
let matched = false;
|
|
1609
|
+
activitySpaces.querySelectorAll('.activity-bar-btn').forEach(b => {
|
|
1610
|
+
const isMatch = b.getAttribute('data-space-id') === spaceId;
|
|
1611
|
+
b.classList.toggle('active', isMatch);
|
|
1612
|
+
if (isMatch) matched = true;
|
|
1613
|
+
});
|
|
1614
|
+
if (!matched && (spaceId === 'code' || spaceId === 'text')) {
|
|
1615
|
+
const firstOfType = activitySpaces.querySelector(`.activity-bar-btn[data-space-type="${spaceId}"]`);
|
|
1616
|
+
firstOfType?.classList.add('active');
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
private bindCosmosNavigatorEvents(): void {
|
|
1623
|
+
window.addEventListener('space-gib-big-bang-exploded', () => {
|
|
1624
|
+
this.hasBigBangExploded = true;
|
|
1625
|
+
this.revealChrome();
|
|
1626
|
+
this.updatePlaygroundBannerVisibility();
|
|
1627
|
+
});
|
|
1628
|
+
|
|
1629
|
+
window.addEventListener('space-gib-identity-docked', () => {
|
|
1630
|
+
const identityContainer = document.getElementById('identity-header-container');
|
|
1631
|
+
if (identityContainer) {
|
|
1632
|
+
if (!this.hasActiveIdentity()) {
|
|
1633
|
+
identityContainer.classList.add('identity-active-highlight');
|
|
1634
|
+
}
|
|
1635
|
+
const btn = identityContainer.querySelector('button') || identityContainer;
|
|
1636
|
+
(btn as HTMLElement).focus?.();
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
window.addEventListener(EVENT_IBGIB_IDENTITY_CHANGED, () => {
|
|
1641
|
+
const identityContainer = document.getElementById('identity-header-container');
|
|
1642
|
+
if (this.hasActiveIdentity()) {
|
|
1643
|
+
identityContainer?.classList.remove('identity-active-highlight');
|
|
1644
|
+
}
|
|
1645
|
+
this.updatePlaygroundBannerVisibility();
|
|
1646
|
+
});
|
|
1647
|
+
|
|
1648
|
+
window.addEventListener('space-gib-agent-docked', () => {
|
|
1649
|
+
const btnAgent = document.getElementById('btn-footer-agent');
|
|
1650
|
+
if (btnAgent) {
|
|
1651
|
+
btnAgent.classList.add('is-docked');
|
|
1652
|
+
btnAgent.classList.add('agent-btn-docked-pulse');
|
|
1653
|
+
setTimeout(() => {
|
|
1654
|
+
btnAgent.classList.remove('agent-btn-docked-pulse');
|
|
1655
|
+
}, 1800);
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
|
|
1659
|
+
window.addEventListener('space-gib-pitch-docked', () => {
|
|
1660
|
+
const btnPitch = document.getElementById('btn-header-pitch');
|
|
1661
|
+
if (btnPitch) {
|
|
1662
|
+
btnPitch.classList.add('is-docked');
|
|
1663
|
+
btnPitch.classList.add('pitch-btn-active-pulse');
|
|
1664
|
+
setTimeout(() => {
|
|
1665
|
+
btnPitch.classList.remove('pitch-btn-active-pulse');
|
|
1666
|
+
}, 2000);
|
|
1667
|
+
}
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1670
|
+
window.addEventListener('space-gib-pitch-requested', () => {
|
|
1671
|
+
this.showPitch();
|
|
1672
|
+
});
|
|
1673
|
+
|
|
1674
|
+
window.addEventListener('space-gib-cosmos-selected', (e: any) => {
|
|
1675
|
+
const cosmos: CosmosInfo = e.detail?.cosmos;
|
|
1676
|
+
if (cosmos) {
|
|
1677
|
+
this.updateActiveCosmosDisplay(cosmos);
|
|
1678
|
+
this.showLeftPanelView('cosmos');
|
|
1679
|
+
if (!this.isApplyingRoute) {
|
|
1680
|
+
const tenant = this.getActiveTenant() || (cosmos.isTemporary ? { type: 'username', siteUsername: 'playground' } : undefined);
|
|
1681
|
+
getSpaceGibRouterSvc().navigate({
|
|
1682
|
+
kind: 'cosmos',
|
|
1683
|
+
cosmos: cosmos.name || cosmos.id,
|
|
1684
|
+
...(tenant ? { tenant } : {}),
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1689
|
+
|
|
1690
|
+
window.addEventListener('space-gib-activity-bar-expand-requested', () => {
|
|
1691
|
+
this.expandActivityBar();
|
|
1692
|
+
});
|
|
1693
|
+
|
|
1694
|
+
window.addEventListener('space-gib-space-added', (e: any) => {
|
|
1695
|
+
const { space, cosmos } = e.detail;
|
|
1696
|
+
if (cosmos) {
|
|
1697
|
+
this.updateActiveCosmosDisplay(cosmos, space?.id || space?.type);
|
|
1698
|
+
}
|
|
1699
|
+
this.expandActivityBar();
|
|
1700
|
+
this.expandLeftPanel();
|
|
1701
|
+
|
|
1702
|
+
if (space) {
|
|
1703
|
+
this.setActiveActivityBarButton('space', space.id);
|
|
1704
|
+
const dockedBtn = document.querySelector(`.activity-space-btn[data-space-id="${space.id}"]`);
|
|
1705
|
+
if (dockedBtn) {
|
|
1706
|
+
dockedBtn.classList.add('space-btn-docked-pulse');
|
|
1707
|
+
setTimeout(() => {
|
|
1708
|
+
dockedBtn.classList.remove('space-btn-docked-pulse');
|
|
1709
|
+
}, 1800);
|
|
1710
|
+
}
|
|
1711
|
+
if (space.type === 'code') {
|
|
1712
|
+
this.showLeftPanelView('code', space.name);
|
|
1713
|
+
this.switchToReposMode({ skipNavigate: true, galaxyId: space.name });
|
|
1714
|
+
if (!this.isApplyingRoute && cosmos?.name) {
|
|
1715
|
+
const tenant = this.getActiveTenant(cosmos.name);
|
|
1716
|
+
getSpaceGibRouterSvc().navigate({
|
|
1717
|
+
kind: 'galaxy',
|
|
1718
|
+
...(tenant ? { tenant } : {}),
|
|
1719
|
+
cosmos: cosmos.name,
|
|
1720
|
+
galaxy: space.name,
|
|
1721
|
+
topology: 'branching',
|
|
1722
|
+
discriminator: 'code',
|
|
1723
|
+
branch: 'main',
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
} else if (space.type === 'text') {
|
|
1727
|
+
this.showLeftPanelView('text', space.name);
|
|
1728
|
+
this.switchToNotesMode(space.name, { skipNavigate: true, galaxyId: space.name });
|
|
1729
|
+
if (!this.isApplyingRoute && cosmos?.name) {
|
|
1730
|
+
const tenant = this.getActiveTenant(cosmos.name);
|
|
1731
|
+
getSpaceGibRouterSvc().navigate({
|
|
1732
|
+
kind: 'galaxy',
|
|
1733
|
+
...(tenant ? { tenant } : {}),
|
|
1734
|
+
cosmos: cosmos.name,
|
|
1735
|
+
galaxy: space.name,
|
|
1736
|
+
topology: 'linear',
|
|
1737
|
+
discriminator: 'notes',
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
});
|
|
1743
|
+
|
|
1744
|
+
window.addEventListener('space-gib-polycosmos-activated', (e: any) => {
|
|
1745
|
+
const { cosmoses, activeCosmosId } = e.detail;
|
|
1746
|
+
this.updatePolycosmosDisplay(cosmoses || [], activeCosmosId);
|
|
1747
|
+
this.showLeftPanelView('polycosmos');
|
|
1748
|
+
if (!this.isApplyingRoute) {
|
|
1749
|
+
const tenant = this.getActiveTenant();
|
|
1750
|
+
getSpaceGibRouterSvc().navigate({
|
|
1751
|
+
kind: 'polycosmos',
|
|
1752
|
+
...(tenant ? { tenant } : {}),
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
private bindCosmosChromeEvents(): void {
|
|
1759
|
+
const btnHeaderPolycosmos = document.getElementById('btn-header-polycosmos');
|
|
1760
|
+
const btnHeaderCosmosSelect = document.getElementById('btn-header-cosmos-select');
|
|
1761
|
+
const headerCosmosMenu = document.getElementById('header-cosmos-menu');
|
|
1762
|
+
const activityBtnPolycosmos = document.getElementById('activity-btn-polycosmos');
|
|
1763
|
+
const activityBtnCosmos = document.getElementById('activity-btn-cosmos');
|
|
1764
|
+
const btnSideNewCosmos = document.getElementById('btn-side-new-cosmos');
|
|
1765
|
+
const brandGalaxy = document.getElementById('header-brand-galaxy');
|
|
1766
|
+
const btnFooterAgent = document.getElementById('btn-footer-agent');
|
|
1767
|
+
const btnHeaderPitch = document.getElementById('btn-header-pitch');
|
|
1768
|
+
const btnHeaderClone = document.getElementById('btn-header-clone');
|
|
1769
|
+
|
|
1770
|
+
btnHeaderPitch?.addEventListener('click', () => {
|
|
1771
|
+
this.showPitch();
|
|
1772
|
+
});
|
|
1773
|
+
|
|
1774
|
+
btnHeaderClone?.addEventListener('click', () => {
|
|
1775
|
+
this.openCloneModal();
|
|
1776
|
+
});
|
|
1777
|
+
|
|
1778
|
+
window.addEventListener('space-gib-clone-requested', (e: any) => {
|
|
1779
|
+
this.openCloneModal(e.detail);
|
|
1780
|
+
});
|
|
1781
|
+
|
|
1782
|
+
window.addEventListener('space-gib-branch-requested', async (e: any) => {
|
|
1783
|
+
const { galaxyName, sourceBranch, newBranchName, scope, itemPath } = e.detail || {};
|
|
1784
|
+
const cleanGalaxy = galaxyName || 'code-1';
|
|
1785
|
+
const cleanSourceBranch = sourceBranch || 'main';
|
|
1786
|
+
const cleanNewBranch = newBranchName?.trim();
|
|
1787
|
+
if (!cleanNewBranch) return;
|
|
1788
|
+
|
|
1789
|
+
const validation = validateBranchName(cleanNewBranch);
|
|
1790
|
+
if (!validation.isValid) {
|
|
1791
|
+
alert(`Invalid branch name: ${validation.error}`);
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
const lc = `${this.lc}[space-gib-branch-requested]`;
|
|
1796
|
+
try {
|
|
1797
|
+
const { metaspace, space } = await getPlaygroundMetaspaceAndSpace();
|
|
1798
|
+
const router = getSpaceGibRouterSvc();
|
|
1799
|
+
|
|
1800
|
+
// 1. Resolve GalaxyIbGib
|
|
1801
|
+
let galaxyIbGib: B2tFSGalaxyIbGib_V1 | undefined;
|
|
1802
|
+
for (const manifest of router.cosmosManifestCache?.values() || []) {
|
|
1803
|
+
const entry = manifest.data?.galaxies?.[cleanGalaxy];
|
|
1804
|
+
if (entry?.galaxyAddr) {
|
|
1805
|
+
galaxyIbGib = await getIbGibFromSpace<B2tFSGalaxyIbGib_V1>({ addr: entry.galaxyAddr, space });
|
|
1806
|
+
if (galaxyIbGib) break;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
if (!galaxyIbGib) {
|
|
1811
|
+
throw new Error(`Galaxy [${cleanGalaxy}] not found in cosmos manifest.`);
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// 2. Resolve source branch space
|
|
1815
|
+
const branchAndRoot = await getBranchAndRootItemInfosAndIbGibsAndSpaceFromGalaxyIbGib({
|
|
1816
|
+
galaxyIbGib,
|
|
1817
|
+
branchName: cleanSourceBranch,
|
|
1818
|
+
metaspace,
|
|
1819
|
+
});
|
|
1820
|
+
|
|
1821
|
+
const activeBranchSpace = branchAndRoot.branchSpace;
|
|
1822
|
+
|
|
1823
|
+
// 3. Execute isomorphic VCS branch creation
|
|
1824
|
+
const branchRes = await executeVcsBranch({
|
|
1825
|
+
metaspace,
|
|
1826
|
+
localSpace: space,
|
|
1827
|
+
sourceBranchSpace: activeBranchSpace,
|
|
1828
|
+
galaxyIbGib,
|
|
1829
|
+
branchName: cleanNewBranch,
|
|
1830
|
+
sourceBranchName: cleanSourceBranch,
|
|
1831
|
+
itemPath: scope === 'item' ? itemPath : undefined,
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1834
|
+
const finalGalaxy = branchRes.updatedGalaxy || galaxyIbGib;
|
|
1835
|
+
const finalGalaxyAddr = getIbGibAddr({ ibGib: finalGalaxy });
|
|
1836
|
+
|
|
1837
|
+
// 4. Update router cosmos manifest cache with updated galaxy address
|
|
1838
|
+
for (const manifest of router.cosmosManifestCache?.values() || []) {
|
|
1839
|
+
if (manifest.data?.galaxies?.[cleanGalaxy]) {
|
|
1840
|
+
manifest.data.galaxies[cleanGalaxy].galaxyAddr = finalGalaxyAddr;
|
|
1841
|
+
break;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
// 5. Broadcast new galaxy IbGib to metaspace subscribers
|
|
1846
|
+
await (metaspace as any).registerNewIbGib?.({ ibGib: finalGalaxy, space });
|
|
1847
|
+
|
|
1848
|
+
// 6. Update Changes, File Explorer & Code Editor
|
|
1849
|
+
this.changesInstance?.setGalaxyAndBranch(cleanGalaxy, cleanNewBranch);
|
|
1850
|
+
this.fileExplorerInstance?.setGalaxyAndBranch(cleanGalaxy, cleanNewBranch);
|
|
1851
|
+
this.codeEditorInstance?.setGalaxyAndBranch(cleanGalaxy, cleanNewBranch);
|
|
1852
|
+
this.timelineInstance?.setBranch(cleanNewBranch);
|
|
1853
|
+
|
|
1854
|
+
// 8. Navigate router to new branch
|
|
1855
|
+
const activeCosmos = this.cosmosNavigatorInstance?.getActiveCosmos();
|
|
1856
|
+
const activePath = this.codeEditorInstance?.getFilepath();
|
|
1857
|
+
if (activeCosmos) {
|
|
1858
|
+
const tenant = this.getActiveTenant(activeCosmos);
|
|
1859
|
+
router.navigate({
|
|
1860
|
+
kind: 'galaxy',
|
|
1861
|
+
cosmos: activeCosmos.id || activeCosmos.name,
|
|
1862
|
+
galaxy: cleanGalaxy,
|
|
1863
|
+
topology: 'branching',
|
|
1864
|
+
discriminator: 'code',
|
|
1865
|
+
branch: cleanNewBranch,
|
|
1866
|
+
...(activePath ? { filePath: activePath } : {}),
|
|
1867
|
+
...(tenant ? { tenant } : {}),
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
console.log(`${lc} Successfully diverged to new branch [${cleanNewBranch}] from ${scope === 'item' ? `item [${itemPath}]` : `worktree [${cleanSourceBranch}]`} in galaxy [${cleanGalaxy}]`);
|
|
1872
|
+
} catch (err) {
|
|
1873
|
+
console.error(`${lc} Failed to branch: ${extractErrorMsg(err)}`);
|
|
1874
|
+
alert(`Failed to create branch: ${extractErrorMsg(err)}`);
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
|
|
1878
|
+
btnFooterAgent?.addEventListener('click', () => {
|
|
1879
|
+
alert('🤖 ibgib Agent: "Hello! I am your AI assistant across superspaces. How can I help you today?"');
|
|
1880
|
+
});
|
|
1881
|
+
|
|
1882
|
+
const goToPolycosmos = () => {
|
|
1883
|
+
this.switchToSpacesMode();
|
|
1884
|
+
this.cosmosNavigatorInstance?.setZoomState('polycosmos');
|
|
1885
|
+
this.setActiveActivityBarButton('polycosmos');
|
|
1886
|
+
this.showLeftPanelView('polycosmos');
|
|
1887
|
+
const allCosmoses = this.cosmosNavigatorInstance?.getCosmoses() || [];
|
|
1888
|
+
const activeCosmos = this.cosmosNavigatorInstance?.getActiveCosmos();
|
|
1889
|
+
this.updatePolycosmosDisplay(allCosmoses, activeCosmos?.id || null);
|
|
1890
|
+
|
|
1891
|
+
const tenant = this.getActiveTenant();
|
|
1892
|
+
getSpaceGibRouterSvc().navigate({
|
|
1893
|
+
kind: 'polycosmos',
|
|
1894
|
+
...(tenant ? { tenant } : {}),
|
|
1895
|
+
});
|
|
1896
|
+
};
|
|
1897
|
+
|
|
1898
|
+
const goToCosmos = () => {
|
|
1899
|
+
this.switchToSpacesMode();
|
|
1900
|
+
this.cosmosNavigatorInstance?.setZoomState('cosmos');
|
|
1901
|
+
this.setActiveActivityBarButton('cosmos');
|
|
1902
|
+
this.showLeftPanelView('cosmos');
|
|
1903
|
+
const activeCosmos = this.cosmosNavigatorInstance?.getActiveCosmos();
|
|
1904
|
+
if (activeCosmos) {
|
|
1905
|
+
this.updateActiveCosmosDisplay(activeCosmos);
|
|
1906
|
+
const tenant = this.getActiveTenant() || (activeCosmos.isTemporary ? { type: 'username', siteUsername: 'playground' } : undefined);
|
|
1907
|
+
getSpaceGibRouterSvc().navigate({
|
|
1908
|
+
kind: 'cosmos',
|
|
1909
|
+
cosmos: activeCosmos.name || activeCosmos.id,
|
|
1910
|
+
...(tenant ? { tenant } : {}),
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
|
|
1915
|
+
window.addEventListener('space-gib-new-note-requested', async (e: any) => {
|
|
1916
|
+
const { title } = e.detail || {};
|
|
1917
|
+
await this.showNotes();
|
|
1918
|
+
this.nestedChatInstance?.enterNewTopicMode(title);
|
|
1919
|
+
});
|
|
1920
|
+
|
|
1921
|
+
window.addEventListener('space-gib-note-selected', (e: any) => {
|
|
1922
|
+
const { file, line } = e.detail || {};
|
|
1923
|
+
if (file) {
|
|
1924
|
+
this.notesInspectorInstance?.inspectFileAnchor({ file, line });
|
|
1925
|
+
}
|
|
1926
|
+
});
|
|
1927
|
+
|
|
1928
|
+
window.addEventListener('space-gib-jump-to-code-requested', (e: any) => {
|
|
1929
|
+
const { file, line } = e.detail || {};
|
|
1930
|
+
this.showLeftPanelView('code', 'Code Galaxy 1');
|
|
1931
|
+
this.showCode(file);
|
|
1932
|
+
this.setActiveActivityBarButton('space', 'space-code-1');
|
|
1933
|
+
});
|
|
1934
|
+
|
|
1935
|
+
window.addEventListener('space-gib-inspector-close-requested', () => {
|
|
1936
|
+
this.toggleRightPanel();
|
|
1937
|
+
});
|
|
1938
|
+
|
|
1939
|
+
btnHeaderPolycosmos?.addEventListener('click', () => goToPolycosmos());
|
|
1940
|
+
activityBtnPolycosmos?.addEventListener('click', () => goToPolycosmos());
|
|
1941
|
+
brandGalaxy?.addEventListener('click', (e) => {
|
|
1942
|
+
e.preventDefault();
|
|
1943
|
+
goToPolycosmos();
|
|
1944
|
+
});
|
|
1945
|
+
const brandTitle = document.querySelector('.brand-title');
|
|
1946
|
+
brandTitle?.addEventListener('click', (e) => {
|
|
1947
|
+
e.preventDefault();
|
|
1948
|
+
goToPolycosmos();
|
|
1949
|
+
});
|
|
1950
|
+
|
|
1951
|
+
activityBtnCosmos?.addEventListener('click', () => goToCosmos());
|
|
1952
|
+
|
|
1953
|
+
btnHeaderCosmosSelect?.addEventListener('click', (e) => {
|
|
1954
|
+
e.stopPropagation();
|
|
1955
|
+
if (headerCosmosMenu) {
|
|
1956
|
+
headerCosmosMenu.classList.toggle('hidden');
|
|
1957
|
+
}
|
|
1958
|
+
});
|
|
1959
|
+
|
|
1960
|
+
document.addEventListener('click', (e) => {
|
|
1961
|
+
if (headerCosmosMenu && !headerCosmosMenu.contains(e.target as Node)) {
|
|
1962
|
+
headerCosmosMenu.classList.add('hidden');
|
|
1963
|
+
}
|
|
1964
|
+
});
|
|
1965
|
+
|
|
1966
|
+
btnSideNewCosmos?.addEventListener('click', () => {
|
|
1967
|
+
this.switchToSpacesMode();
|
|
1968
|
+
this.cosmosNavigatorInstance?.triggerBigBangSequence(async () => {
|
|
1969
|
+
const nextIdx = (this.cosmosNavigatorInstance?.getCosmoses().length || 0) + 1;
|
|
1970
|
+
const newCosmos = await this.cosmosNavigatorInstance?.createCosmos(`cosmos-${nextIdx}`);
|
|
1971
|
+
if (newCosmos) {
|
|
1972
|
+
this.cosmosNavigatorInstance?.selectCosmos(newCosmos.id);
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
});
|
|
1976
|
+
|
|
1977
|
+
const btnSideAddSpace = document.getElementById('btn-side-add-space');
|
|
1978
|
+
btnSideAddSpace?.addEventListener('click', () => {
|
|
1979
|
+
this.cosmosNavigatorInstance?.handleSpaceClick('code');
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
private updateActiveCosmosDisplay(cosmos: CosmosInfo, activeSpaceIdOrType?: string): void {
|
|
1984
|
+
const headerName = document.getElementById('header-active-cosmos-name');
|
|
1985
|
+
if (headerName) { headerName.textContent = cosmos.name; }
|
|
1986
|
+
|
|
1987
|
+
const headerBadge = document.getElementById('header-temp-badge');
|
|
1988
|
+
if (headerBadge) {
|
|
1989
|
+
headerBadge.style.display = cosmos.isTemporary ? 'inline-flex' : 'none';
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
const projectName = document.getElementById('cosmos-project-name');
|
|
1993
|
+
if (projectName) { projectName.textContent = cosmos.name; }
|
|
1994
|
+
|
|
1995
|
+
const spacesCount = document.getElementById('cosmos-spaces-count');
|
|
1996
|
+
if (spacesCount) { spacesCount.textContent = String(cosmos.spaces.length); }
|
|
1997
|
+
|
|
1998
|
+
const spacesList = document.getElementById('cosmos-spaces-list');
|
|
1999
|
+
if (spacesList) {
|
|
2000
|
+
if (cosmos.spaces.length === 0) {
|
|
2001
|
+
spacesList.innerHTML = `<p class="empty-list-note">No spaces added yet.<br>Click Code or Text in Cosmos to attach a space.</p>`;
|
|
2002
|
+
} else {
|
|
2003
|
+
spacesList.innerHTML = cosmos.spaces.map(s => `
|
|
2004
|
+
<div class="cosmos-space-item" data-space-id="${s.id}" data-space-type="${s.type}">
|
|
2005
|
+
<span class="space-item-icon">${s.type === 'code' ? '</>' : (s.icon || '📦')}</span>
|
|
2006
|
+
<div class="space-item-info">
|
|
2007
|
+
<span class="space-item-name">${s.name}</span>
|
|
2008
|
+
<span class="space-item-sub">${s.type === 'code' ? 'Code Repository' : 'Metatext Discussion'}</span>
|
|
2009
|
+
</div>
|
|
2010
|
+
</div>
|
|
2011
|
+
`).join('');
|
|
2012
|
+
|
|
2013
|
+
spacesList.querySelectorAll('.cosmos-space-item').forEach(el => {
|
|
2014
|
+
el.addEventListener('click', () => {
|
|
2015
|
+
const type = el.getAttribute('data-space-type');
|
|
2016
|
+
const spaceId = el.getAttribute('data-space-id') || '';
|
|
2017
|
+
const name = el.querySelector('.space-item-name')?.textContent || '';
|
|
2018
|
+
if (type === 'code') {
|
|
2019
|
+
this.showLeftPanelView('code', name);
|
|
2020
|
+
this.switchToReposMode({ galaxyId: spaceId });
|
|
2021
|
+
} else {
|
|
2022
|
+
this.showLeftPanelView('text', name);
|
|
2023
|
+
this.switchToNotesMode(name, { galaxyId: spaceId });
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
const activitySpaces = document.getElementById('activity-spaces-container');
|
|
2031
|
+
if (activitySpaces) {
|
|
2032
|
+
activitySpaces.innerHTML = cosmos.spaces.map(s => `
|
|
2033
|
+
<button class="activity-bar-btn activity-space-btn" type="button" title="${s.name}" data-space-id="${s.id}" data-space-type="${s.type}">
|
|
2034
|
+
<span class="activity-icon">${s.type === 'code' ? '</>' : (s.icon || '📦')}</span>
|
|
2035
|
+
<span class="activity-label">${s.shortLabel}</span>
|
|
2036
|
+
</button>
|
|
2037
|
+
`).join('');
|
|
2038
|
+
|
|
2039
|
+
activitySpaces.querySelectorAll('.activity-space-btn').forEach(btn => {
|
|
2040
|
+
btn.addEventListener('click', () => {
|
|
2041
|
+
const type = btn.getAttribute('data-space-type');
|
|
2042
|
+
const title = btn.getAttribute('title') || '';
|
|
2043
|
+
const spaceId = btn.getAttribute('data-space-id') || '';
|
|
2044
|
+
this.setActiveActivityBarButton('space', spaceId);
|
|
2045
|
+
|
|
2046
|
+
if (type === 'code') {
|
|
2047
|
+
this.showLeftPanelView('code', title);
|
|
2048
|
+
this.switchToReposMode({ galaxyId: spaceId });
|
|
2049
|
+
} else {
|
|
2050
|
+
this.showLeftPanelView('text', title);
|
|
2051
|
+
this.switchToNotesMode(title, { galaxyId: spaceId });
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
if (activeSpaceIdOrType) {
|
|
2058
|
+
this.setActiveActivityBarButton('space', activeSpaceIdOrType);
|
|
2059
|
+
} else {
|
|
2060
|
+
this.setActiveActivityBarButton('cosmos');
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
if (cosmos.spaces.length > 0) {
|
|
2064
|
+
this.expandActivityBar();
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
const allCosmoses = this.cosmosNavigatorInstance?.getCosmoses() || [];
|
|
2068
|
+
this.updateHeaderCosmosMenu(allCosmoses, cosmos.id);
|
|
2069
|
+
this.updatePlaygroundBannerVisibility();
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
public updatePlaygroundBannerVisibility(): void {
|
|
2073
|
+
const banner = document.getElementById('playground-banner');
|
|
2074
|
+
if (!banner) { return; }
|
|
2075
|
+
|
|
2076
|
+
if (this.hasActiveIdentity()) {
|
|
2077
|
+
banner.classList.add('hidden');
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
// On bare landing before the big bang explosion, do not show the banner
|
|
2082
|
+
const currentRoute = getSpaceGibRouterSvc().getCurrentRoute();
|
|
2083
|
+
const isBareLanding = !currentRoute || currentRoute.kind === 'polycosmos';
|
|
2084
|
+
if (isBareLanding && !this.hasBigBangExploded) {
|
|
2085
|
+
banner.classList.add('hidden');
|
|
2086
|
+
return;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
// In playground mode (no authenticated identity), the banner is ALWAYS visible
|
|
2090
|
+
banner.classList.remove('hidden');
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
private updatePolycosmosDisplay(cosmoses: CosmosInfo[], activeId: string | null): void {
|
|
2094
|
+
const polyCount = document.getElementById('polycosmos-total-count');
|
|
2095
|
+
if (polyCount) { polyCount.textContent = String(cosmoses.length); }
|
|
2096
|
+
|
|
2097
|
+
this.updatePolycosmosList(cosmoses, activeId);
|
|
2098
|
+
this.setActiveActivityBarButton('polycosmos');
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
private updatePolycosmosList(cosmoses: CosmosInfo[], activeId: string | null): void {
|
|
2102
|
+
const listEl = document.getElementById('polycosmos-projects-list');
|
|
2103
|
+
if (!listEl) { return; }
|
|
2104
|
+
|
|
2105
|
+
if (cosmoses.length === 0) {
|
|
2106
|
+
listEl.innerHTML = `<p class="empty-list-note">No cosmos projects yet.<br>Click "Initiate Big Bang" in the center to create your first cosmos!</p>`;
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
listEl.innerHTML = cosmoses.map(c => `
|
|
2111
|
+
<div class="cosmos-space-item ${c.id === activeId ? 'active' : ''}" data-cosmos-id="${c.id}">
|
|
2112
|
+
<span class="space-item-icon">🌌</span>
|
|
2113
|
+
<div class="space-item-info">
|
|
2114
|
+
<span class="space-item-name">${c.name}</span>
|
|
2115
|
+
<span class="space-item-sub">${c.spaces.length} space${c.spaces.length === 1 ? '' : 's'}</span>
|
|
2116
|
+
</div>
|
|
2117
|
+
</div>
|
|
2118
|
+
`).join('');
|
|
2119
|
+
|
|
2120
|
+
listEl.querySelectorAll('.cosmos-space-item').forEach(el => {
|
|
2121
|
+
el.addEventListener('click', () => {
|
|
2122
|
+
const id = el.getAttribute('data-cosmos-id');
|
|
2123
|
+
if (id) {
|
|
2124
|
+
this.cosmosNavigatorInstance?.selectCosmos(id);
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
private updateHeaderCosmosMenu(cosmoses: CosmosInfo[], activeId: string | null): void {
|
|
2131
|
+
const menu = document.getElementById('header-cosmos-menu');
|
|
2132
|
+
if (!menu) { return; }
|
|
2133
|
+
|
|
2134
|
+
menu.innerHTML = `
|
|
2135
|
+
${cosmoses.map(c => `
|
|
2136
|
+
<button type="button" class="header-menu-item ${c.id === activeId ? 'active' : ''}" data-cosmos-id="${c.id}">
|
|
2137
|
+
<span>🌌 ${c.name}</span>
|
|
2138
|
+
<span class="orbit-cosmos-badge" style="font-size:0.7rem; padding:1px 6px;">${c.spaces.length}</span>
|
|
2139
|
+
</button>
|
|
2140
|
+
`).join('')}
|
|
2141
|
+
<div class="header-menu-divider"></div>
|
|
2142
|
+
<button type="button" class="header-menu-item" id="btn-header-menu-new-cosmos">
|
|
2143
|
+
<span>+ New Cosmos</span>
|
|
2144
|
+
</button>
|
|
2145
|
+
`;
|
|
2146
|
+
|
|
2147
|
+
menu.querySelectorAll('.header-menu-item[data-cosmos-id]').forEach(btn => {
|
|
2148
|
+
btn.addEventListener('click', () => {
|
|
2149
|
+
const id = btn.getAttribute('data-cosmos-id');
|
|
2150
|
+
if (id) {
|
|
2151
|
+
menu.classList.add('hidden');
|
|
2152
|
+
this.switchToSpacesMode();
|
|
2153
|
+
this.cosmosNavigatorInstance?.selectCosmos(id);
|
|
2154
|
+
}
|
|
2155
|
+
});
|
|
2156
|
+
});
|
|
2157
|
+
|
|
2158
|
+
const btnNew = menu.querySelector('#btn-header-menu-new-cosmos');
|
|
2159
|
+
btnNew?.addEventListener('click', async () => {
|
|
2160
|
+
menu.classList.add('hidden');
|
|
2161
|
+
this.switchToSpacesMode();
|
|
2162
|
+
this.cosmosNavigatorInstance?.triggerBigBangExplosion();
|
|
2163
|
+
const nextIdx = cosmoses.length + 1;
|
|
2164
|
+
const newC = await this.cosmosNavigatorInstance?.createCosmos(`cosmos-${nextIdx}`);
|
|
2165
|
+
if (newC) {
|
|
2166
|
+
setTimeout(() => {
|
|
2167
|
+
this.cosmosNavigatorInstance?.selectCosmos(newC.id);
|
|
2168
|
+
}, 300);
|
|
2169
|
+
}
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
private showLeftPanelView(viewName: 'polycosmos' | 'cosmos' | 'code' | 'text', title?: string): void {
|
|
2174
|
+
const viewPoly = document.getElementById('left-panel-polycosmos-view');
|
|
2175
|
+
const viewCosmos = document.getElementById('left-panel-cosmos-view');
|
|
2176
|
+
const viewCode = document.getElementById('left-panel-code-view');
|
|
2177
|
+
const viewText = document.getElementById('left-panel-text-view');
|
|
2178
|
+
|
|
2179
|
+
if (viewPoly) {
|
|
2180
|
+
viewPoly.style.display = viewName === 'polycosmos' ? 'flex' : 'none';
|
|
2181
|
+
viewPoly.style.flexDirection = 'column';
|
|
2182
|
+
}
|
|
2183
|
+
if (viewCosmos) {
|
|
2184
|
+
viewCosmos.style.display = viewName === 'cosmos' ? 'flex' : 'none';
|
|
2185
|
+
viewCosmos.style.flexDirection = 'column';
|
|
2186
|
+
}
|
|
2187
|
+
if (viewCode) {
|
|
2188
|
+
viewCode.style.display = viewName === 'code' ? 'flex' : 'none';
|
|
2189
|
+
viewCode.style.flexDirection = 'column';
|
|
2190
|
+
if (title) {
|
|
2191
|
+
const titleEl = document.getElementById('left-code-space-title');
|
|
2192
|
+
if (titleEl) { titleEl.textContent = title; }
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
if (viewText) {
|
|
2196
|
+
viewText.style.display = viewName === 'text' ? 'flex' : 'none';
|
|
2197
|
+
viewText.style.flexDirection = 'column';
|
|
2198
|
+
if (viewName === 'text') {
|
|
2199
|
+
this.initNotesExplorer(title);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
private expandActivityBar(): void {
|
|
2205
|
+
const bar = document.getElementById('activity-bar');
|
|
2206
|
+
if (bar) {
|
|
2207
|
+
bar.classList.remove('collapsed');
|
|
2208
|
+
bar.classList.add('expanded');
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
private expandLeftPanel(): void {
|
|
2213
|
+
const panel = document.getElementById(ID_LEFT_PANEL);
|
|
2214
|
+
if (panel) {
|
|
2215
|
+
panel.classList.remove('collapsed');
|
|
2216
|
+
this.leftPanelState = 'expanded';
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
public expandRightPanel(): void {
|
|
2221
|
+
const panel = document.getElementById(ID_RIGHT_PANEL);
|
|
2222
|
+
if (panel) {
|
|
2223
|
+
panel.classList.remove('collapsed');
|
|
2224
|
+
this.rightPanelState = 'expanded';
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
private switchToSpacesMode(): void {
|
|
2229
|
+
this.setMode('spaces');
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
private switchToReposMode(opts?: { skipNavigate?: boolean; galaxyId?: string }): void {
|
|
2233
|
+
this.setMode('repos');
|
|
2234
|
+
this.expandRightPanel();
|
|
2235
|
+
if (!opts?.skipNavigate && !this.isApplyingRoute) {
|
|
2236
|
+
const activeCosmos = this.cosmosNavigatorInstance?.getActiveCosmos();
|
|
2237
|
+
if (activeCosmos) {
|
|
2238
|
+
const tenant = this.getActiveTenant(activeCosmos);
|
|
2239
|
+
getSpaceGibRouterSvc().navigate({
|
|
2240
|
+
kind: 'galaxy',
|
|
2241
|
+
cosmos: activeCosmos.id || activeCosmos.name,
|
|
2242
|
+
galaxy: opts?.galaxyId || 'code-1',
|
|
2243
|
+
topology: 'branching',
|
|
2244
|
+
discriminator: 'code',
|
|
2245
|
+
branch: 'main',
|
|
2246
|
+
...(tenant ? { tenant } : {}),
|
|
2247
|
+
});
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
private switchToNotesMode(title?: string, opts?: { skipNavigate?: boolean; galaxyId?: string }): void {
|
|
2253
|
+
this.setMode('notes', title);
|
|
2254
|
+
this.expandRightPanel();
|
|
2255
|
+
if (!opts?.skipNavigate && !this.isApplyingRoute) {
|
|
2256
|
+
const activeCosmos = this.cosmosNavigatorInstance?.getActiveCosmos();
|
|
2257
|
+
if (activeCosmos) {
|
|
2258
|
+
const tenant = this.getActiveTenant(activeCosmos);
|
|
2259
|
+
getSpaceGibRouterSvc().navigate({
|
|
2260
|
+
kind: 'galaxy',
|
|
2261
|
+
cosmos: activeCosmos.id || activeCosmos.name,
|
|
2262
|
+
galaxy: opts?.galaxyId || 'notes-1',
|
|
2263
|
+
topology: 'linear',
|
|
2264
|
+
discriminator: 'notes',
|
|
2265
|
+
...(title ? { topicTjp: title, trail: [title], trailMode: 'tjp' } : {}),
|
|
2266
|
+
...(tenant ? { tenant } : {}),
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
public getActiveTenant(cosmos?: CosmosInfo | string): SpaceGibTenant | undefined {
|
|
2273
|
+
const domainIdentity = (globalThis as any).ibgib?.identity?.domainIdentity;
|
|
2274
|
+
if (domainIdentity) {
|
|
2275
|
+
const siteUsername = domainIdentity.data?.siteUsername || domainIdentity.data?.name || domainIdentity.data?.username;
|
|
2276
|
+
if (siteUsername) {
|
|
2277
|
+
return { type: 'username', siteUsername };
|
|
2278
|
+
}
|
|
2279
|
+
const tjpAddr = domainIdentity.data?.tjp?.tjpAddr || domainIdentity.data?.tjpGib;
|
|
2280
|
+
if (tjpAddr) {
|
|
2281
|
+
return { type: 'keystone', keystoneTjp: tjpAddr };
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
// Without an active authenticated identity, all workspaces and routes are playground-scoped
|
|
2285
|
+
return { type: 'username', siteUsername: 'playground' };
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
private async initRouterIntegration(): Promise<void> {
|
|
2289
|
+
const lc = `${this.lc}[${this.initRouterIntegration.name}]`;
|
|
2290
|
+
try {
|
|
2291
|
+
const router = getSpaceGibRouterSvc();
|
|
2292
|
+
|
|
2293
|
+
// 1. Subscribe to route changes (popstate, direct navigation)
|
|
2294
|
+
router.subscribe(async (route) => {
|
|
2295
|
+
await this.handleRouteActivation(route);
|
|
2296
|
+
});
|
|
2297
|
+
|
|
2298
|
+
// 2. Load current URL from address bar
|
|
2299
|
+
const initialRoute = await router.loadCurrentURL();
|
|
2300
|
+
console.log(`${lc} Initial route activated:`, initialRoute);
|
|
2301
|
+
} catch (err) {
|
|
2302
|
+
console.error(`${lc} Failed to initialize router integration: ${extractErrorMsg(err)}`);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
public async handleRouteActivation(route: SpaceGibRouteInfo): Promise<void> {
|
|
2307
|
+
const lc = `${this.lc}[${this.handleRouteActivation.name}]`;
|
|
2308
|
+
if (this.isApplyingRoute) { return; }
|
|
2309
|
+
this.isApplyingRoute = true;
|
|
2310
|
+
try {
|
|
2311
|
+
if (route.kind !== 'polycosmos') {
|
|
2312
|
+
this.hasBigBangExploded = true;
|
|
2313
|
+
if (this.cosmosNavigatorInstance) {
|
|
2314
|
+
this.cosmosNavigatorInstance.hasBigBangExploded = true;
|
|
2315
|
+
}
|
|
2316
|
+
this.revealChrome();
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
// If the route omitted a tenant specifier but the user is authenticated (or in playground),
|
|
2320
|
+
// canonicalize the URL in the address bar so copying and sharing is always fully qualified.
|
|
2321
|
+
// Exception: For bare landing ('polycosmos'), do not attach tenant or redirect to /@playground/polycosmos
|
|
2322
|
+
// so the URL remains bare (e.g. https://local.ibgib.space) to keep full focus on Big Bang.
|
|
2323
|
+
if (route.kind !== 'admin-review' && route.kind !== 'polycosmos' && !route.tenant) {
|
|
2324
|
+
const cosmosName = 'cosmos' in route ? route.cosmos : undefined;
|
|
2325
|
+
const activeTenant = this.getActiveTenant(cosmosName);
|
|
2326
|
+
if (activeTenant) {
|
|
2327
|
+
route.tenant = activeTenant;
|
|
2328
|
+
getSpaceGibRouterSvc().navigate(route, { replace: true, silent: true });
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
switch (route.kind) {
|
|
2333
|
+
case 'polycosmos': {
|
|
2334
|
+
await this.showCosmos();
|
|
2335
|
+
this.cosmosNavigatorInstance?.setZoomState('polycosmos');
|
|
2336
|
+
break;
|
|
2337
|
+
}
|
|
2338
|
+
case 'cosmos': {
|
|
2339
|
+
if (this.cosmosNavigatorInstance) {
|
|
2340
|
+
const cosmos = await this.cosmosNavigatorInstance.findCosmos(route.cosmos);
|
|
2341
|
+
if (!cosmos) {
|
|
2342
|
+
this.show404(`Cosmos "${route.cosmos}" not found.`);
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
this.cosmosNavigatorInstance.selectCosmos(cosmos.id);
|
|
2346
|
+
this.updateActiveCosmosDisplay(cosmos);
|
|
2347
|
+
}
|
|
2348
|
+
this.showLeftPanelView('cosmos');
|
|
2349
|
+
this.expandLeftPanel();
|
|
2350
|
+
await this.showCosmos();
|
|
2351
|
+
break;
|
|
2352
|
+
}
|
|
2353
|
+
case 'galaxy': {
|
|
2354
|
+
let cosmosInfo: CosmosInfo | undefined;
|
|
2355
|
+
if (this.cosmosNavigatorInstance) {
|
|
2356
|
+
cosmosInfo = await this.cosmosNavigatorInstance.findCosmos(route.cosmos);
|
|
2357
|
+
if (!cosmosInfo) {
|
|
2358
|
+
this.show404(`Cosmos "${route.cosmos}" not found.`);
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
const spaceType = route.topology === 'branching' ? 'code' : 'text';
|
|
2362
|
+
if (!cosmosInfo.spaces.some(s => s.id === route.galaxy)) {
|
|
2363
|
+
cosmosInfo.spaces.push({
|
|
2364
|
+
id: route.galaxy,
|
|
2365
|
+
type: spaceType,
|
|
2366
|
+
name: route.galaxy,
|
|
2367
|
+
shortLabel: route.galaxy,
|
|
2368
|
+
icon: route.topology === 'branching' ? '</>' : '📝',
|
|
2369
|
+
});
|
|
2370
|
+
}
|
|
2371
|
+
this.cosmosNavigatorInstance.selectCosmos(cosmosInfo.id);
|
|
2372
|
+
this.updateActiveCosmosDisplay(cosmosInfo, route.galaxy || spaceType);
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
const policy: GalaxyPolicy = {
|
|
2376
|
+
topology: route.topology,
|
|
2377
|
+
role: route.topology === 'branching' ? 'intrinsic' : 'extrinsic',
|
|
2378
|
+
storageMode: 'text',
|
|
2379
|
+
defaultBranch: 'main',
|
|
2380
|
+
};
|
|
2381
|
+
|
|
2382
|
+
const context: PanelLoadContext = {
|
|
2383
|
+
route,
|
|
2384
|
+
cosmosName: route.cosmos,
|
|
2385
|
+
galaxyName: route.galaxy,
|
|
2386
|
+
discriminator: route.discriminator,
|
|
2387
|
+
policy,
|
|
2388
|
+
cosmosInfo,
|
|
2389
|
+
shell: this,
|
|
2390
|
+
};
|
|
2391
|
+
|
|
2392
|
+
await this.loadRouteForPanels(route, context);
|
|
2393
|
+
break;
|
|
2394
|
+
}
|
|
2395
|
+
case 'raw-addr': {
|
|
2396
|
+
await this.handleRawAddrSnapshotRoute(route);
|
|
2397
|
+
break;
|
|
2398
|
+
}
|
|
2399
|
+
case 'raw-tjp': {
|
|
2400
|
+
await this.handleRawTjpRoute(route);
|
|
2401
|
+
break;
|
|
2402
|
+
}
|
|
2403
|
+
case 'admin-review': {
|
|
2404
|
+
await this.showAdminReview(route.tjpGib);
|
|
2405
|
+
break;
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
} catch (err) {
|
|
2409
|
+
console.error(`${lc} Failed to apply route: ${extractErrorMsg(err)}`);
|
|
2410
|
+
} finally {
|
|
2411
|
+
this.isApplyingRoute = false;
|
|
2412
|
+
this.updatePlaygroundBannerVisibility();
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
private async handleRawAddrSnapshotRoute(route: RawAddrRouteInfo): Promise<void> {
|
|
2417
|
+
const lc = `${this.lc}[${this.handleRawAddrSnapshotRoute.name}]`;
|
|
2418
|
+
try {
|
|
2419
|
+
console.log(`${lc} Resolving raw snapshot route: cosmos=${route.cosmos}, spaceId=${route.spaceId}, addr=${route.ibGibAddr}`);
|
|
2420
|
+
if (this.cosmosNavigatorInstance) {
|
|
2421
|
+
const cosmos = await this.cosmosNavigatorInstance.findCosmos(route.cosmos);
|
|
2422
|
+
if (!cosmos) {
|
|
2423
|
+
this.show404(`Cosmos "${route.cosmos}" not found.`);
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
this.cosmosNavigatorInstance.selectCosmos(cosmos.id);
|
|
2427
|
+
this.updateActiveCosmosDisplay(cosmos);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
2431
|
+
let space = (metaspace as any).localUserSpaces?.find((s: any) => s.data?.uuid === route.spaceId || s.data?.name === route.spaceId);
|
|
2432
|
+
if (!space) {
|
|
2433
|
+
space = (metaspace as any).spaces?.find((s: any) => s.data?.uuid === route.spaceId || s.data?.name === route.spaceId);
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
let targetIbGib: any;
|
|
2437
|
+
if (space) {
|
|
2438
|
+
const getRes = await space.get({ addr: route.ibGibAddr });
|
|
2439
|
+
targetIbGib = getRes?.ibGibs?.[0];
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
if (!targetIbGib) {
|
|
2443
|
+
try {
|
|
2444
|
+
const domainAddr = route.tenant?.type === 'username' ? route.tenant.siteUsername : route.cosmos;
|
|
2445
|
+
const res = await fetch(`/api/ibgib/${encodeURIComponent(domainAddr)}/${encodeURIComponent(route.ibGibAddr)}`);
|
|
2446
|
+
if (res.ok) {
|
|
2447
|
+
const payload = await res.json();
|
|
2448
|
+
targetIbGib = payload?.ibGib;
|
|
2449
|
+
}
|
|
2450
|
+
} catch (netErr) {
|
|
2451
|
+
console.warn(`${lc} Server fetch for raw addr failed: ${extractErrorMsg(netErr)}`);
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
await this.showRawSnapshot(route.ibGibAddr, targetIbGib);
|
|
2456
|
+
} catch (err) {
|
|
2457
|
+
console.error(`${lc} ${extractErrorMsg(err)}`);
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2461
|
+
private async handleRawTjpRoute(route: RawTjpRouteInfo): Promise<void> {
|
|
2462
|
+
const lc = `${this.lc}[${this.handleRawTjpRoute.name}]`;
|
|
2463
|
+
try {
|
|
2464
|
+
console.log(`${lc} Resolving living tip route: cosmos=${route.cosmos}, spaceId=${route.spaceId}, tjp=${route.tjpAddr}`);
|
|
2465
|
+
if (this.cosmosNavigatorInstance) {
|
|
2466
|
+
const cosmos = await this.cosmosNavigatorInstance.findCosmos(route.cosmos);
|
|
2467
|
+
if (!cosmos) {
|
|
2468
|
+
this.show404(`Cosmos "${route.cosmos}" not found.`);
|
|
2469
|
+
return;
|
|
2470
|
+
}
|
|
2471
|
+
this.cosmosNavigatorInstance.selectCosmos(cosmos.id);
|
|
2472
|
+
this.updateActiveCosmosDisplay(cosmos);
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
const metaspace = await getGlobalMetaspace_waitIfNeeded();
|
|
2476
|
+
let space = (metaspace as any).localUserSpaces?.find((s: any) => s.data?.uuid === route.spaceId || s.data?.name === route.spaceId);
|
|
2477
|
+
if (!space) {
|
|
2478
|
+
space = (metaspace as any).spaces?.find((s: any) => s.data?.uuid === route.spaceId || s.data?.name === route.spaceId);
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
let tipAddr = route.tjpAddr;
|
|
2482
|
+
let targetIbGib: any;
|
|
2483
|
+
if (space && typeof (space as any).getLatestIbGib === 'function') {
|
|
2484
|
+
const latest = await (space as any).getLatestIbGib({ tjpAddr: route.tjpAddr });
|
|
2485
|
+
if (latest) {
|
|
2486
|
+
targetIbGib = latest;
|
|
2487
|
+
tipAddr = (latest as any).ibGibAddr || getIbGibAddr({ ibGib: latest }) || tipAddr;
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
if (!targetIbGib) {
|
|
2492
|
+
try {
|
|
2493
|
+
const domainAddr = route.tenant?.type === 'username' ? route.tenant.siteUsername : route.cosmos;
|
|
2494
|
+
const res = await fetch(`/api/ibgib/${encodeURIComponent(domainAddr)}/${encodeURIComponent(route.tjpAddr)}`);
|
|
2495
|
+
if (res.ok) {
|
|
2496
|
+
const payload = await res.json();
|
|
2497
|
+
targetIbGib = payload?.ibGib;
|
|
2498
|
+
if (targetIbGib) {
|
|
2499
|
+
tipAddr = (targetIbGib as any).ibGibAddr || getIbGibAddr({ ibGib: targetIbGib }) || tipAddr;
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
} catch (netErr) {
|
|
2503
|
+
console.warn(`${lc} Server fetch for raw tjp failed: ${extractErrorMsg(netErr)}`);
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
await this.showRawSnapshot(tipAddr, targetIbGib, { isLivingTip: true, tjpAddr: route.tjpAddr });
|
|
2508
|
+
} catch (err) {
|
|
2509
|
+
console.error(`${lc} ${extractErrorMsg(err)}`);
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
public async showRawSnapshot(
|
|
2514
|
+
addr: string,
|
|
2515
|
+
ibGib?: any,
|
|
2516
|
+
opts?: { isLivingTip?: boolean; tjpAddr?: string }
|
|
2517
|
+
): Promise<void> {
|
|
2518
|
+
const lc = `${this.lc}[${this.showRawSnapshot.name}]`;
|
|
2519
|
+
try {
|
|
2520
|
+
const centerPanel = document.getElementById(ID_CENTER_PANEL_CONTENT);
|
|
2521
|
+
if (!centerPanel) { return; }
|
|
2522
|
+
|
|
2523
|
+
if (!this.timelineItemDetailsInstance) {
|
|
2524
|
+
const componentSvc = await getComponentSvc();
|
|
2525
|
+
this.timelineItemDetailsInstance = await componentSvc.getComponentInstance({
|
|
2526
|
+
path: TIMELINE_ITEM_DETAILS_COMPONENT_NAME,
|
|
2527
|
+
ibGibAddr: ROOT_ADDR,
|
|
2528
|
+
useRegExpPrefilter: true,
|
|
2529
|
+
}) as TimelineItemDetailsComponentInstance;
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
if (this.timelineItemDetailsInstance) {
|
|
2533
|
+
const componentSvc = await getComponentSvc();
|
|
2534
|
+
await componentSvc.inject({
|
|
2535
|
+
parentEl: centerPanel,
|
|
2536
|
+
componentToInject: this.timelineItemDetailsInstance,
|
|
2537
|
+
});
|
|
2538
|
+
|
|
2539
|
+
const shortHash = addr.slice(0, 10);
|
|
2540
|
+
const isLiving = opts?.isLivingTip;
|
|
2541
|
+
const message = isLiving
|
|
2542
|
+
? `🌱 Living Tip: ${addr.split('^')[0] || addr}`
|
|
2543
|
+
: `📌 Snapshot (Pinned): ${addr.split('^')[0] || addr}`;
|
|
2544
|
+
const timestamp = isLiving ? 'Latest tip' : 'Pinned point-in-time';
|
|
2545
|
+
const branch = isLiving ? 'living-tip' : 'snapshot';
|
|
2546
|
+
|
|
2547
|
+
let rawData: string;
|
|
2548
|
+
if (ibGib?.data) {
|
|
2549
|
+
if (typeof ibGib.data.text === 'string') {
|
|
2550
|
+
rawData = ibGib.data.text;
|
|
2551
|
+
} else if (typeof ibGib.data.content === 'string') {
|
|
2552
|
+
rawData = ibGib.data.content;
|
|
2553
|
+
} else {
|
|
2554
|
+
rawData = JSON.stringify(ibGib.data, null, 2);
|
|
2555
|
+
}
|
|
2556
|
+
} else if (ibGib) {
|
|
2557
|
+
rawData = JSON.stringify(ibGib, null, 2);
|
|
2558
|
+
} else {
|
|
2559
|
+
rawData = `Address: ${addr}\n(Content not loaded)`;
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
this.timelineItemDetailsInstance.setTimelineItem({
|
|
2563
|
+
hash: addr,
|
|
2564
|
+
shortHash: shortHash,
|
|
2565
|
+
message,
|
|
2566
|
+
author: ibGib?.rel8ns?.ancestor?.[0] || (isLiving ? 'ibgib living tip' : 'ibgib snapshot'),
|
|
2567
|
+
timestamp,
|
|
2568
|
+
branch,
|
|
2569
|
+
diffPreview: rawData,
|
|
2570
|
+
changedFiles: [
|
|
2571
|
+
{ path: addr, status: 'A', additions: 1, deletions: 0 }
|
|
2572
|
+
]
|
|
2573
|
+
});
|
|
2574
|
+
}
|
|
2575
|
+
} catch (error) {
|
|
2576
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
public hasActiveIdentity(): boolean {
|
|
2581
|
+
return Boolean((globalThis as any).ibgib?.identity?.domainIdentity);
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
public async onEngineReady(): Promise<void> {
|
|
2585
|
+
const lc = `${this.lc}[${this.onEngineReady.name}]`;
|
|
2586
|
+
try {
|
|
2587
|
+
await this.initialized;
|
|
2588
|
+
console.log(`${lc} SpaceGib Shell Service is ready.`);
|
|
2589
|
+
|
|
2590
|
+
// Ensure the UI removes the loading text
|
|
2591
|
+
const statusSection = document.getElementById('status-section');
|
|
2592
|
+
if (statusSection) {
|
|
2593
|
+
statusSection.style.display = 'none';
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
// Mount identity-header component in the header bar
|
|
2597
|
+
const componentSvc = await getComponentSvc();
|
|
2598
|
+
const identityHeader = await componentSvc.getComponentInstance({
|
|
2599
|
+
path: 'ibgib-identity-header',
|
|
2600
|
+
ibGibAddr: ROOT_ADDR,
|
|
2601
|
+
useRegExpPrefilter: true,
|
|
2602
|
+
});
|
|
2603
|
+
|
|
2604
|
+
const headerContainer = document.getElementById('identity-header-container');
|
|
2605
|
+
if (headerContainer && identityHeader) {
|
|
2606
|
+
headerContainer.innerHTML = '';
|
|
2607
|
+
headerContainer.appendChild(identityHeader as any);
|
|
2608
|
+
if (this.hasActiveIdentity()) {
|
|
2609
|
+
headerContainer.classList.remove('identity-active-highlight');
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
window.dispatchEvent(new CustomEvent(EVENT_IBGIB_SHELL_READY, { detail: { timestamp: Date.now() } }));
|
|
2614
|
+
|
|
2615
|
+
// Connect HTML5 History API router and activate current URL
|
|
2616
|
+
await this.initRouterIntegration();
|
|
2617
|
+
|
|
2618
|
+
// Update playground mode banner visibility
|
|
2619
|
+
this.updatePlaygroundBannerVisibility();
|
|
2620
|
+
|
|
2621
|
+
// Pre-warm in-memory temporary metaspace in background for snappy tutorial/playground mode
|
|
2622
|
+
prewarmPlaygroundMetaspace().catch(err => {
|
|
2623
|
+
console.warn(`${lc} Background prewarm of temporary metaspace failed: ${extractErrorMsg(err)}`);
|
|
2624
|
+
});
|
|
2625
|
+
|
|
2626
|
+
// Trigger 1 (App Boot): On onEngineReady, if active identity exists, check for updates.
|
|
361
2627
|
|
|
362
|
-
// Trigger 1 (App Boot): On onEngineReady, if active identity exists, check for updates.
|
|
363
2628
|
const domainIdentity = (globalThis as any).ibgib?.identity?.domainIdentity;
|
|
364
2629
|
if (domainIdentity) {
|
|
365
2630
|
const activePrimaryAddr = getIbGibAddr({ ibGib: domainIdentity });
|