@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
|
@@ -0,0 +1,1317 @@
|
|
|
1
|
+
import thisCss from "./cosmos-navigator.css";
|
|
2
|
+
import thisHtml from "./cosmos-navigator.html";
|
|
3
|
+
|
|
4
|
+
import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
5
|
+
import { IbGibAddr } from "@ibgib/ts-gib/dist/types.mjs";
|
|
6
|
+
import { IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
|
|
7
|
+
import { getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
8
|
+
import { getSpaceIdFromIb } from "@ibgib/core-gib/dist/witness/space/metaspace/metaspace-innerspace/metaspace-innerspace-helper.mjs";
|
|
9
|
+
import { createCosmosIbGib, addGalaxyToCosmos } from "@ibgib/core-gib/dist/cosmology/cosmos/cosmos-helpers.mjs";
|
|
10
|
+
import { getSpaceQualifiedIbGibAddr, getIbGibFromSpace } from "@ibgib/core-gib/dist/common/other/ibgib-helper.mjs";
|
|
11
|
+
import { CosmosIbGib_V1 } from "@ibgib/core-gib/dist/cosmology/cosmos/cosmos-types.mjs";
|
|
12
|
+
import { GalaxyPresetType } from "@ibgib/core-gib/dist/cosmology/galaxy/galaxy-configs.mjs";
|
|
13
|
+
import { createGalaxyIbGib } from "@ibgib/core-gib/dist/cosmology/galaxy/galaxy-helpers.mjs";
|
|
14
|
+
import { IbGibDynamicComponentMetaBase, IbGibDynamicComponentInstanceBase, } from "@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs";
|
|
15
|
+
import {
|
|
16
|
+
ElementsBase, IbGibDynamicComponentInstance,
|
|
17
|
+
IbGibDynamicComponentInstanceInitOpts,
|
|
18
|
+
} from "@ibgib/web-gib/dist/ui/component/component-types.mjs";
|
|
19
|
+
import { getComponentCtorArg } from "@ibgib/web-gib/dist/app-bootstrap/init-orchestration.mjs";
|
|
20
|
+
import { promptForText } from "@ibgib/web-gib/dist/helpers.web.mjs";
|
|
21
|
+
|
|
22
|
+
import { getPlaygroundMetaspaceAndSpace, seedInMemoryCosmosWorkspace } from "../../../cosmos/seed-cosmos-workspace.mjs";
|
|
23
|
+
import { executeVcsInit } from "@ibgib/core-gib/dist/cosmology/branching/commands/core-init-cmd.mjs";
|
|
24
|
+
import { GalaxyIbGib_V1 } from "@ibgib/core-gib/dist/cosmology/galaxy/galaxy-types.mjs";
|
|
25
|
+
import { getSpaceGibRouterSvc } from "../../router/index.mjs";
|
|
26
|
+
|
|
27
|
+
export const COSMOS_NAVIGATOR_COMPONENT_NAME = 'space-gib-cosmos-navigator';
|
|
28
|
+
|
|
29
|
+
export interface AttachedSpace {
|
|
30
|
+
id: string;
|
|
31
|
+
type: GalaxyPresetType | 'code' | 'text';
|
|
32
|
+
name: string;
|
|
33
|
+
shortLabel: string;
|
|
34
|
+
icon: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface CosmosInfo {
|
|
38
|
+
id: string;
|
|
39
|
+
name: string;
|
|
40
|
+
ibGibAddr?: IbGibAddr;
|
|
41
|
+
spaces: AttachedSpace[];
|
|
42
|
+
createdAt: number;
|
|
43
|
+
isTemporary?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Metadata factory for space-gib-cosmos-navigator Web Component.
|
|
48
|
+
*/
|
|
49
|
+
export class CosmosNavigatorComponentMeta extends IbGibDynamicComponentMetaBase {
|
|
50
|
+
protected lc: string = `[${CosmosNavigatorComponentMeta.name}]`;
|
|
51
|
+
|
|
52
|
+
routeRegExp?: RegExp = new RegExp(`^${COSMOS_NAVIGATOR_COMPONENT_NAME}$`);
|
|
53
|
+
componentName = COSMOS_NAVIGATOR_COMPONENT_NAME;
|
|
54
|
+
|
|
55
|
+
constructor() {
|
|
56
|
+
super(getComponentCtorArg());
|
|
57
|
+
if (!customElements.get(this.componentName)) {
|
|
58
|
+
customElements.define(this.componentName, CosmosNavigatorComponentInstance);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async createInstance({
|
|
63
|
+
path,
|
|
64
|
+
ibGibAddr
|
|
65
|
+
}: {
|
|
66
|
+
path: string;
|
|
67
|
+
ibGibAddr: IbGibAddr;
|
|
68
|
+
}): Promise<IbGibDynamicComponentInstance<IbGib_V1>> {
|
|
69
|
+
const component = new CosmosNavigatorComponentInstance();
|
|
70
|
+
await component.initialize({
|
|
71
|
+
ibGibAddr,
|
|
72
|
+
meta: this,
|
|
73
|
+
html: thisHtml,
|
|
74
|
+
css: [thisCss],
|
|
75
|
+
});
|
|
76
|
+
return component;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* References to DOM elements within the component's Shadow Root.
|
|
82
|
+
*/
|
|
83
|
+
export interface CosmosNavigatorElements extends ElementsBase {
|
|
84
|
+
rootEl: HTMLDivElement;
|
|
85
|
+
canvasEl: HTMLCanvasElement;
|
|
86
|
+
polycosmosStageEl: HTMLDivElement;
|
|
87
|
+
singularityContainerEl: HTMLDivElement;
|
|
88
|
+
btnSingularityEl: HTMLButtonElement;
|
|
89
|
+
singularityLabelEl: HTMLSpanElement;
|
|
90
|
+
orbitTrackEl: HTMLDivElement;
|
|
91
|
+
orbitingCosmosesContainerEl: HTMLDivElement;
|
|
92
|
+
activeCosmosStageEl: HTMLDivElement;
|
|
93
|
+
nodePitchWrapperEl: HTMLDivElement;
|
|
94
|
+
btnNodePitchEl: HTMLButtonElement;
|
|
95
|
+
nodeIdentityWrapperEl: HTMLDivElement;
|
|
96
|
+
btnNodeIdentityEl: HTMLButtonElement;
|
|
97
|
+
nodeSuperspaceWrapperEl: HTMLDivElement;
|
|
98
|
+
btnNodeSuperspaceEl: HTMLButtonElement;
|
|
99
|
+
activeCosmosTitleCaptionEl: HTMLSpanElement;
|
|
100
|
+
activeCosmosTempBadgeEl?: HTMLSpanElement;
|
|
101
|
+
superspaceSatellitesEl: HTMLDivElement;
|
|
102
|
+
btnSatCodeEl?: HTMLButtonElement;
|
|
103
|
+
btnSatTextEl?: HTMLButtonElement;
|
|
104
|
+
nodeAgentWrapperEl: HTMLDivElement;
|
|
105
|
+
btnNodeAgentEl: HTMLButtonElement;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Concrete implementation of the unified Cosmos & Polycosmos Navigator component.
|
|
110
|
+
*/
|
|
111
|
+
export class CosmosNavigatorComponentInstance
|
|
112
|
+
extends IbGibDynamicComponentInstanceBase<IbGib_V1, CosmosNavigatorElements>
|
|
113
|
+
implements IbGibDynamicComponentInstance<IbGib_V1, CosmosNavigatorElements> {
|
|
114
|
+
|
|
115
|
+
protected lc: string = `[${CosmosNavigatorComponentInstance.name}]`;
|
|
116
|
+
|
|
117
|
+
private get el(): CosmosNavigatorElements {
|
|
118
|
+
if (!this.elements) {
|
|
119
|
+
throw new Error(`[${CosmosNavigatorComponentInstance.name}] elements not initialized.`);
|
|
120
|
+
}
|
|
121
|
+
return this.elements;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// State
|
|
125
|
+
private cosmoses: CosmosInfo[] = [];
|
|
126
|
+
private activeCosmosId: string | null = null;
|
|
127
|
+
private zoomState: 'polycosmos' | 'cosmos' = 'polycosmos';
|
|
128
|
+
private initialOnboardingDone: boolean = false;
|
|
129
|
+
private isSpaceLaunching: boolean = false;
|
|
130
|
+
|
|
131
|
+
// Canvas & Starfield State
|
|
132
|
+
public hasBigBangExploded: boolean = false;
|
|
133
|
+
private starfieldFadeAlpha: number = 0;
|
|
134
|
+
private ctx: CanvasRenderingContext2D | null = null;
|
|
135
|
+
private animFrameId: number | null = null;
|
|
136
|
+
private resizeObserver: ResizeObserver | null = null;
|
|
137
|
+
private stars: Array<{ x: number; y: number; r: number; alpha: number; speed: number; phase: number }> = [];
|
|
138
|
+
private particles: Array<{
|
|
139
|
+
x: number;
|
|
140
|
+
y: number;
|
|
141
|
+
vx: number;
|
|
142
|
+
vy: number;
|
|
143
|
+
r: number;
|
|
144
|
+
color: string;
|
|
145
|
+
alpha: number;
|
|
146
|
+
decay: number;
|
|
147
|
+
}> = [];
|
|
148
|
+
private shockwaveRadius = 0;
|
|
149
|
+
private shockwaveAlpha = 0;
|
|
150
|
+
|
|
151
|
+
constructor() {
|
|
152
|
+
super();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
override async initialize(opts: IbGibDynamicComponentInstanceInitOpts): Promise<void> {
|
|
156
|
+
await super.initialize(opts);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
override async disconnected(): Promise<void> {
|
|
160
|
+
const lc = `${this.lc}[${this.disconnected.name}]`;
|
|
161
|
+
try {
|
|
162
|
+
this.clearAutoDockTimer();
|
|
163
|
+
if (this.animFrameId) {
|
|
164
|
+
cancelAnimationFrame(this.animFrameId);
|
|
165
|
+
this.animFrameId = null;
|
|
166
|
+
}
|
|
167
|
+
if (this.resizeObserver) {
|
|
168
|
+
this.resizeObserver.disconnect();
|
|
169
|
+
this.resizeObserver = null;
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
protected override async connectedCallback_reconnectComponent(): Promise<void> {
|
|
177
|
+
await super.connectedCallback_reconnectComponent();
|
|
178
|
+
this.ensureStars();
|
|
179
|
+
this.renderCurrentState();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
override async created(): Promise<void> {
|
|
183
|
+
const lc = `${this.lc}[${this.created.name}]`;
|
|
184
|
+
try {
|
|
185
|
+
const shadow = this.shadowRoot!;
|
|
186
|
+
this.elements = {
|
|
187
|
+
rootEl: shadow.getElementById('cosmos-navigator-root') as HTMLDivElement,
|
|
188
|
+
canvasEl: shadow.getElementById('starfield-canvas') as HTMLCanvasElement,
|
|
189
|
+
polycosmosStageEl: shadow.getElementById('polycosmos-stage') as HTMLDivElement,
|
|
190
|
+
singularityContainerEl: shadow.getElementById('singularity-container') as HTMLDivElement,
|
|
191
|
+
btnSingularityEl: shadow.getElementById('btn-singularity') as HTMLButtonElement,
|
|
192
|
+
singularityLabelEl: shadow.getElementById('singularity-label') as HTMLSpanElement,
|
|
193
|
+
orbitTrackEl: shadow.getElementById('orbit-track') as HTMLDivElement,
|
|
194
|
+
orbitingCosmosesContainerEl: shadow.getElementById('orbiting-cosmoses-container') as HTMLDivElement,
|
|
195
|
+
activeCosmosStageEl: shadow.getElementById('active-cosmos-stage') as HTMLDivElement,
|
|
196
|
+
nodePitchWrapperEl: shadow.getElementById('node-pitch-wrapper') as HTMLDivElement,
|
|
197
|
+
btnNodePitchEl: shadow.getElementById('btn-node-pitch') as HTMLButtonElement,
|
|
198
|
+
nodeIdentityWrapperEl: shadow.getElementById('node-identity-wrapper') as HTMLDivElement,
|
|
199
|
+
btnNodeIdentityEl: shadow.getElementById('btn-node-identity') as HTMLButtonElement,
|
|
200
|
+
nodeSuperspaceWrapperEl: shadow.getElementById('node-superspace-wrapper') as HTMLDivElement,
|
|
201
|
+
btnNodeSuperspaceEl: shadow.getElementById('btn-node-superspace') as HTMLButtonElement,
|
|
202
|
+
activeCosmosTitleCaptionEl: shadow.getElementById('active-cosmos-title-caption') as HTMLSpanElement,
|
|
203
|
+
activeCosmosTempBadgeEl: shadow.getElementById('active-cosmos-temp-badge') as HTMLSpanElement,
|
|
204
|
+
superspaceSatellitesEl: shadow.getElementById('superspace-satellites') as HTMLDivElement,
|
|
205
|
+
btnSatCodeEl: shadow.getElementById('btn-sat-code') as HTMLButtonElement,
|
|
206
|
+
btnSatTextEl: shadow.getElementById('btn-sat-text') as HTMLButtonElement,
|
|
207
|
+
nodeAgentWrapperEl: shadow.getElementById('node-agent-wrapper') as HTMLDivElement,
|
|
208
|
+
btnNodeAgentEl: shadow.getElementById('btn-node-agent') as HTMLButtonElement,
|
|
209
|
+
contentEl: shadow.getElementById('cosmos-navigator-root') as HTMLDivElement,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
this.loadState();
|
|
213
|
+
this.initCanvas();
|
|
214
|
+
this.bindHandlers();
|
|
215
|
+
this.renderCurrentState();
|
|
216
|
+
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private loadState(): void {
|
|
223
|
+
// In-memory only for Phase 1 testing; backing ibgibs will handle persistence in Phase 2
|
|
224
|
+
this.cosmoses = [];
|
|
225
|
+
this.activeCosmosId = null;
|
|
226
|
+
this.zoomState = 'polycosmos';
|
|
227
|
+
this.hasBigBangExploded = false;
|
|
228
|
+
this.starfieldFadeAlpha = 0;
|
|
229
|
+
|
|
230
|
+
// Clear any stale keys from earlier runs so reload presents a clean slate
|
|
231
|
+
try {
|
|
232
|
+
localStorage.removeItem('space-gib-cosmoses');
|
|
233
|
+
localStorage.removeItem('space-gib-active-cosmos-id');
|
|
234
|
+
localStorage.removeItem('space-gib-mode');
|
|
235
|
+
} catch {
|
|
236
|
+
// ignore
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private saveState(): void {
|
|
241
|
+
// In-memory only for Phase 1 testing; backing ibgibs will handle persistence in Phase 2
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private initCanvas(): void {
|
|
245
|
+
if (!this.el.canvasEl) return;
|
|
246
|
+
this.ctx = this.el.canvasEl.getContext('2d');
|
|
247
|
+
if (!this.ctx) return;
|
|
248
|
+
|
|
249
|
+
this.resizeCanvas();
|
|
250
|
+
|
|
251
|
+
if (typeof window !== 'undefined' && 'ResizeObserver' in window && this.el.rootEl) {
|
|
252
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
253
|
+
this.resizeCanvas();
|
|
254
|
+
});
|
|
255
|
+
this.resizeObserver.observe(this.el.rootEl);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
window.addEventListener('resize', () => this.resizeCanvas());
|
|
259
|
+
|
|
260
|
+
this.initStars();
|
|
261
|
+
this.startCanvasLoop();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
public ensureStars(): void {
|
|
265
|
+
if (!this.animFrameId) {
|
|
266
|
+
this.startCanvasLoop();
|
|
267
|
+
}
|
|
268
|
+
this.resizeCanvas();
|
|
269
|
+
if (this.stars.length === 0) {
|
|
270
|
+
this.initStars();
|
|
271
|
+
}
|
|
272
|
+
setTimeout(() => {
|
|
273
|
+
this.resizeCanvas();
|
|
274
|
+
if (this.stars.length === 0) {
|
|
275
|
+
this.initStars();
|
|
276
|
+
}
|
|
277
|
+
}, 50);
|
|
278
|
+
setTimeout(() => {
|
|
279
|
+
this.resizeCanvas();
|
|
280
|
+
}, 200);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private resizeCanvas(): void {
|
|
284
|
+
if (!this.el.canvasEl) return;
|
|
285
|
+
const parent = this.el.rootEl || this.el.canvasEl.parentElement;
|
|
286
|
+
const parentW = parent && parent.clientWidth > 0 ? parent.clientWidth : window.innerWidth;
|
|
287
|
+
const parentH = parent && parent.clientHeight > 0 ? parent.clientHeight : 500;
|
|
288
|
+
const targetW = Math.max(300, Math.floor(parentW));
|
|
289
|
+
const targetH = Math.max(300, Math.floor(parentH));
|
|
290
|
+
|
|
291
|
+
if (this.el.canvasEl.width !== targetW || this.el.canvasEl.height !== targetH) {
|
|
292
|
+
this.el.canvasEl.width = targetW;
|
|
293
|
+
this.el.canvasEl.height = targetH;
|
|
294
|
+
if (this.stars.length === 0) {
|
|
295
|
+
this.initStars();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private initStars(): void {
|
|
301
|
+
if (!this.el.canvasEl) return;
|
|
302
|
+
const w = Math.max(300, this.el.canvasEl.width);
|
|
303
|
+
const h = Math.max(300, this.el.canvasEl.height);
|
|
304
|
+
this.stars = [];
|
|
305
|
+
const count = Math.max(100, Math.min(260, Math.floor((w * h) / 3600)));
|
|
306
|
+
for (let i = 0; i < count; i++) {
|
|
307
|
+
this.stars.push({
|
|
308
|
+
x: Math.random() * w,
|
|
309
|
+
y: Math.random() * h,
|
|
310
|
+
r: Math.random() * 1.6 + 0.4,
|
|
311
|
+
alpha: Math.random() * 0.75 + 0.25,
|
|
312
|
+
speed: Math.random() * 0.008 + 0.003,
|
|
313
|
+
phase: Math.random() * Math.PI * 2,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private startCanvasLoop(): void {
|
|
319
|
+
if (this.animFrameId) return;
|
|
320
|
+
|
|
321
|
+
const loop = () => {
|
|
322
|
+
this.renderCanvasFrame();
|
|
323
|
+
this.animFrameId = requestAnimationFrame(loop);
|
|
324
|
+
};
|
|
325
|
+
this.animFrameId = requestAnimationFrame(loop);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private renderCanvasFrame(): void {
|
|
329
|
+
if (!this.ctx || !this.el.canvasEl) return;
|
|
330
|
+
const ctx = this.ctx;
|
|
331
|
+
const w = this.el.canvasEl.width;
|
|
332
|
+
const h = this.el.canvasEl.height;
|
|
333
|
+
|
|
334
|
+
ctx.clearRect(0, 0, w, h);
|
|
335
|
+
|
|
336
|
+
// 1. Draw starfield (only after Big Bang explosion)
|
|
337
|
+
if (this.hasBigBangExploded) {
|
|
338
|
+
if (this.starfieldFadeAlpha < 1.0) {
|
|
339
|
+
this.starfieldFadeAlpha = Math.min(1.0, this.starfieldFadeAlpha + 0.02);
|
|
340
|
+
}
|
|
341
|
+
for (let i = 0; i < this.stars.length; i++) {
|
|
342
|
+
const s = this.stars[i];
|
|
343
|
+
s.phase += s.speed;
|
|
344
|
+
const currentAlpha = Math.max(0.1, Math.min(1.0, s.alpha + Math.sin(s.phase) * 0.35)) * this.starfieldFadeAlpha;
|
|
345
|
+
|
|
346
|
+
ctx.fillStyle = `rgba(255, 255, 255, ${currentAlpha})`;
|
|
347
|
+
ctx.beginPath();
|
|
348
|
+
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
|
|
349
|
+
ctx.fill();
|
|
350
|
+
|
|
351
|
+
// Subtle drift
|
|
352
|
+
s.y -= 0.08;
|
|
353
|
+
if (s.y < 0) {
|
|
354
|
+
s.y = h;
|
|
355
|
+
s.x = Math.random() * w;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// 2. Shockwave ring (Big Bang explosion)
|
|
361
|
+
if (this.shockwaveAlpha > 0.01) {
|
|
362
|
+
ctx.strokeStyle = `rgba(167, 139, 250, ${this.shockwaveAlpha})`;
|
|
363
|
+
ctx.lineWidth = 4 * this.shockwaveAlpha;
|
|
364
|
+
ctx.beginPath();
|
|
365
|
+
ctx.arc(w / 2, h / 2, this.shockwaveRadius, 0, Math.PI * 2);
|
|
366
|
+
ctx.stroke();
|
|
367
|
+
|
|
368
|
+
// Secondary golden/amber inner shockwave ring
|
|
369
|
+
ctx.strokeStyle = `rgba(251, 191, 36, ${this.shockwaveAlpha * 0.75})`;
|
|
370
|
+
ctx.lineWidth = 2.5 * this.shockwaveAlpha;
|
|
371
|
+
ctx.beginPath();
|
|
372
|
+
ctx.arc(w / 2, h / 2, Math.max(0, this.shockwaveRadius - 16), 0, Math.PI * 2);
|
|
373
|
+
ctx.stroke();
|
|
374
|
+
|
|
375
|
+
this.shockwaveRadius += 10;
|
|
376
|
+
// Slower decay: lasts ~1 second longer than before
|
|
377
|
+
this.shockwaveAlpha *= 0.975;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// 3. Explosion Particles
|
|
381
|
+
for (let i = this.particles.length - 1; i >= 0; i--) {
|
|
382
|
+
const p = this.particles[i];
|
|
383
|
+
p.x += p.vx;
|
|
384
|
+
p.y += p.vy;
|
|
385
|
+
p.vx *= 0.978;
|
|
386
|
+
p.vy *= 0.978;
|
|
387
|
+
p.alpha -= p.decay;
|
|
388
|
+
|
|
389
|
+
if (p.alpha <= 0.01) {
|
|
390
|
+
this.particles.splice(i, 1);
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
ctx.fillStyle = p.color.replace('rgb', 'rgba').replace(')', `, ${p.alpha})`);
|
|
395
|
+
ctx.beginPath();
|
|
396
|
+
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
|
|
397
|
+
ctx.fill();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
public triggerBigBangExplosion(): void {
|
|
402
|
+
this.hasBigBangExploded = true;
|
|
403
|
+
if (!this.el.canvasEl) return;
|
|
404
|
+
const w = this.el.canvasEl.width;
|
|
405
|
+
const h = this.el.canvasEl.height;
|
|
406
|
+
const cx = w / 2;
|
|
407
|
+
const cy = h / 2;
|
|
408
|
+
|
|
409
|
+
this.shockwaveRadius = 10;
|
|
410
|
+
this.shockwaveAlpha = 1.0;
|
|
411
|
+
|
|
412
|
+
this.particles = [];
|
|
413
|
+
const colors = [
|
|
414
|
+
'rgb(255, 255, 255)',
|
|
415
|
+
'rgb(167, 139, 250)',
|
|
416
|
+
'rgb(124, 92, 252)',
|
|
417
|
+
'rgb(56, 189, 248)',
|
|
418
|
+
'rgb(236, 72, 153)',
|
|
419
|
+
'rgb(251, 191, 36)',
|
|
420
|
+
];
|
|
421
|
+
|
|
422
|
+
const particleCount = 140;
|
|
423
|
+
for (let i = 0; i < particleCount; i++) {
|
|
424
|
+
const angle = Math.random() * Math.PI * 2;
|
|
425
|
+
const speed = Math.random() * 11 + 2.5;
|
|
426
|
+
this.particles.push({
|
|
427
|
+
x: cx,
|
|
428
|
+
y: cy,
|
|
429
|
+
vx: Math.cos(angle) * speed,
|
|
430
|
+
vy: Math.sin(angle) * speed,
|
|
431
|
+
r: Math.random() * 3.5 + 1.2,
|
|
432
|
+
color: colors[Math.floor(Math.random() * colors.length)],
|
|
433
|
+
alpha: 1.0,
|
|
434
|
+
// Slower decay: lasts ~1 second longer
|
|
435
|
+
decay: Math.random() * 0.007 + 0.005,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Staged sequence for the Big Bang initiation:
|
|
442
|
+
* 1. Singularity compresses with high velocity spinning firecracker rotation (1000ms).
|
|
443
|
+
* 2. Explosion triggers: shockwave & particles burst outward across the canvas.
|
|
444
|
+
* 3. Dispatches 'space-gib-big-bang-exploded' so the Shell can fade in header & footer.
|
|
445
|
+
* 4. Cosmos & escaping buttons blossom out with the explosion shockwave (120ms).
|
|
446
|
+
*/
|
|
447
|
+
public triggerBigBangSequence(onComplete?: () => void): void {
|
|
448
|
+
const btn = this.el.btnSingularityEl;
|
|
449
|
+
if (!btn) {
|
|
450
|
+
this.triggerBigBangExplosion();
|
|
451
|
+
onComplete?.();
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Stage 1: Gravitational compression with high-velocity firecracker rotation (1000ms)
|
|
456
|
+
btn.classList.add('compressing');
|
|
457
|
+
|
|
458
|
+
setTimeout(() => {
|
|
459
|
+
// Stage 2: Explosion burst (BOOM!)
|
|
460
|
+
btn.classList.remove('compressing');
|
|
461
|
+
btn.classList.add('exploded');
|
|
462
|
+
|
|
463
|
+
this.triggerBigBangExplosion();
|
|
464
|
+
|
|
465
|
+
// Dispatch event to Shell: the Big Bang explosion has hit!
|
|
466
|
+
this.dispatchEvent(new CustomEvent('space-gib-big-bang-exploded', {
|
|
467
|
+
bubbles: true,
|
|
468
|
+
composed: true,
|
|
469
|
+
}));
|
|
470
|
+
|
|
471
|
+
// Stage 3: Cosmos and buttons emerge directly with the explosion shockwave
|
|
472
|
+
setTimeout(() => {
|
|
473
|
+
onComplete?.();
|
|
474
|
+
}, 120);
|
|
475
|
+
|
|
476
|
+
setTimeout(() => {
|
|
477
|
+
btn.classList.remove('exploded');
|
|
478
|
+
}, 600);
|
|
479
|
+
}, 1000);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private bindHandlers(): void {
|
|
483
|
+
// Singularity Button click (Empty state Big Bang OR Polycosmos New Cosmos)
|
|
484
|
+
this.el.btnSingularityEl.addEventListener('click', () => {
|
|
485
|
+
this.triggerBigBangSequence(async () => {
|
|
486
|
+
if (this.cosmoses.length === 0) {
|
|
487
|
+
try {
|
|
488
|
+
const seeded = await seedInMemoryCosmosWorkspace();
|
|
489
|
+
if (!this.cosmoses.some(c => c.id === seeded.id)) {
|
|
490
|
+
this.cosmoses.push(seeded);
|
|
491
|
+
}
|
|
492
|
+
this.selectCosmos(seeded.id);
|
|
493
|
+
} catch (seedErr) {
|
|
494
|
+
console.error(`${this.lc} Failed to seed in-memory cosmos: ${extractErrorMsg(seedErr)}`);
|
|
495
|
+
throw seedErr;
|
|
496
|
+
}
|
|
497
|
+
} else {
|
|
498
|
+
const nextIndex = this.cosmoses.length + 1;
|
|
499
|
+
const newCosmos = await this.createCosmos(`cosmos-${nextIndex}`);
|
|
500
|
+
this.selectCosmos(newCosmos.id);
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
// Pitch Node Click (💰)
|
|
508
|
+
this.el.btnNodePitchEl?.addEventListener('click', (e) => {
|
|
509
|
+
e.stopPropagation();
|
|
510
|
+
this.handlePitchClick();
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// Identity Node Click
|
|
514
|
+
this.el.btnNodeIdentityEl.addEventListener('click', (e) => {
|
|
515
|
+
e.stopPropagation();
|
|
516
|
+
this.handleIdentityClick();
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// Agent Chat Click
|
|
520
|
+
this.el.btnNodeAgentEl.addEventListener('click', (e) => {
|
|
521
|
+
e.stopPropagation();
|
|
522
|
+
this.handleAgentClick();
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Code Galaxy Revolving Satellite Button Click (</>)
|
|
526
|
+
this.el.btnSatCodeEl?.addEventListener('click', async (e) => {
|
|
527
|
+
e.stopPropagation();
|
|
528
|
+
await this.handleSatCodeClick();
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
// Notes Galaxy Revolving Satellite Button Click (📝)
|
|
532
|
+
this.el.btnSatTextEl?.addEventListener('click', async (e) => {
|
|
533
|
+
e.stopPropagation();
|
|
534
|
+
await this.handleSatTextClick();
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// Hover pause on satellites
|
|
538
|
+
[this.el.btnSatCodeEl, this.el.btnSatTextEl].forEach(btn => {
|
|
539
|
+
btn?.addEventListener('mouseenter', () => {
|
|
540
|
+
this.el.activeCosmosStageEl?.classList.add('satellites-hovered');
|
|
541
|
+
});
|
|
542
|
+
btn?.addEventListener('mouseleave', () => {
|
|
543
|
+
this.el.activeCosmosStageEl?.classList.remove('satellites-hovered');
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
public async handleSatCodeClick(): Promise<void> {
|
|
549
|
+
const activeCosmos = this.getActiveCosmos();
|
|
550
|
+
if (!activeCosmos) return;
|
|
551
|
+
|
|
552
|
+
const count = activeCosmos.spaces.filter(s => s.type === 'code').length;
|
|
553
|
+
const defaultName = count === 0 ? 'code-1' : `code-${count + 1}`;
|
|
554
|
+
const galaxyName = await promptForText({
|
|
555
|
+
title: 'Create Code Galaxy',
|
|
556
|
+
msg: `Name of the new Code Galaxy for [${activeCosmos.name}]:`,
|
|
557
|
+
defaultValue: defaultName,
|
|
558
|
+
cancelable: true,
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
if (!galaxyName) return;
|
|
562
|
+
|
|
563
|
+
const sourceBtn = this.el.btnSatCodeEl || this.el.btnNodeSuperspaceEl;
|
|
564
|
+
const sourceWrapper = (sourceBtn?.closest('.satellite-wrapper') as HTMLElement) || sourceBtn;
|
|
565
|
+
|
|
566
|
+
let space = activeCosmos.spaces.find(s => s.id === galaxyName || s.name === galaxyName);
|
|
567
|
+
if (!space) {
|
|
568
|
+
space = await this.createAndAttachGalaxy({
|
|
569
|
+
cosmosId: activeCosmos.id,
|
|
570
|
+
galaxyName,
|
|
571
|
+
discriminator: GalaxyPresetType.code,
|
|
572
|
+
skipEvent: true,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (space) {
|
|
577
|
+
this.handleSpaceClick(space, sourceBtn, sourceWrapper);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
public async handleSatTextClick(): Promise<void> {
|
|
582
|
+
const activeCosmos = this.getActiveCosmos();
|
|
583
|
+
if (!activeCosmos) return;
|
|
584
|
+
|
|
585
|
+
const count = activeCosmos.spaces.filter(s => s.type === 'text' || s.type === GalaxyPresetType.notes).length;
|
|
586
|
+
const defaultName = count === 0 ? 'notes-1' : `notes-${count + 1}`;
|
|
587
|
+
const galaxyName = await promptForText({
|
|
588
|
+
title: 'Create Notes Galaxy',
|
|
589
|
+
msg: `Name of the new Notes Galaxy for [${activeCosmos.name}]:`,
|
|
590
|
+
defaultValue: defaultName,
|
|
591
|
+
cancelable: true,
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
if (!galaxyName) return;
|
|
595
|
+
|
|
596
|
+
const sourceBtn = this.el.btnSatTextEl || this.el.btnNodeSuperspaceEl;
|
|
597
|
+
const sourceWrapper = (sourceBtn?.closest('.satellite-wrapper') as HTMLElement) || sourceBtn;
|
|
598
|
+
|
|
599
|
+
let space = activeCosmos.spaces.find(s => s.id === galaxyName || s.name === galaxyName);
|
|
600
|
+
if (!space) {
|
|
601
|
+
space = await this.createAndAttachGalaxy({
|
|
602
|
+
cosmosId: activeCosmos.id,
|
|
603
|
+
galaxyName,
|
|
604
|
+
discriminator: GalaxyPresetType.notes,
|
|
605
|
+
skipEvent: true,
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (space) {
|
|
610
|
+
this.handleSpaceClick(space, sourceBtn, sourceWrapper);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
private launchProjectile({
|
|
615
|
+
sourceWrapperEl,
|
|
616
|
+
sourceBtnEl,
|
|
617
|
+
targetEl,
|
|
618
|
+
fallbackTarget,
|
|
619
|
+
emoji,
|
|
620
|
+
isPitch = false,
|
|
621
|
+
projectileType,
|
|
622
|
+
duration = 1600,
|
|
623
|
+
onApproach,
|
|
624
|
+
onDocked,
|
|
625
|
+
}: {
|
|
626
|
+
sourceWrapperEl: HTMLElement;
|
|
627
|
+
sourceBtnEl: HTMLElement;
|
|
628
|
+
targetEl?: HTMLElement | null;
|
|
629
|
+
fallbackTarget?: { x: number; y: number; width?: number; height?: number };
|
|
630
|
+
emoji: string;
|
|
631
|
+
isPitch?: boolean;
|
|
632
|
+
projectileType?: 'pitch' | 'identity' | 'agent' | 'code' | 'text';
|
|
633
|
+
duration?: number;
|
|
634
|
+
onApproach?: () => void;
|
|
635
|
+
onDocked: () => void;
|
|
636
|
+
}): void {
|
|
637
|
+
const fromRect = sourceBtnEl.getBoundingClientRect();
|
|
638
|
+
let toRect: { left: number; top: number; width: number; height: number };
|
|
639
|
+
|
|
640
|
+
if (targetEl) {
|
|
641
|
+
const r = targetEl.getBoundingClientRect();
|
|
642
|
+
if (r.width > 0 && r.height > 0) {
|
|
643
|
+
toRect = { left: r.left, top: r.top, width: r.width, height: r.height };
|
|
644
|
+
} else if (fallbackTarget) {
|
|
645
|
+
toRect = { left: fallbackTarget.x, top: fallbackTarget.y, width: fallbackTarget.width || 44, height: fallbackTarget.height || 44 };
|
|
646
|
+
} else {
|
|
647
|
+
toRect = { left: r.left, top: r.top, width: 44, height: 44 };
|
|
648
|
+
}
|
|
649
|
+
} else if (fallbackTarget) {
|
|
650
|
+
toRect = { left: fallbackTarget.x, top: fallbackTarget.y, width: fallbackTarget.width || 44, height: fallbackTarget.height || 44 };
|
|
651
|
+
} else {
|
|
652
|
+
onDocked();
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Hide source wrapper in the canvas stage so only the flying projectile is seen
|
|
657
|
+
sourceWrapperEl.classList.add('node-is-escaping');
|
|
658
|
+
|
|
659
|
+
// Create the flying projectile on document.body (above all panels, headers, overflow:hidden)
|
|
660
|
+
const projectile = document.createElement('div');
|
|
661
|
+
const isPitchProj = isPitch || projectileType === 'pitch';
|
|
662
|
+
projectile.className = `cosmos-flying-projectile ${isPitchProj ? 'pitch-projectile' : ''}`;
|
|
663
|
+
projectile.style.position = 'fixed';
|
|
664
|
+
projectile.style.left = `${fromRect.left}px`;
|
|
665
|
+
projectile.style.top = `${fromRect.top}px`;
|
|
666
|
+
projectile.style.width = `${fromRect.width}px`;
|
|
667
|
+
projectile.style.height = `${fromRect.height}px`;
|
|
668
|
+
projectile.style.zIndex = '999999';
|
|
669
|
+
projectile.style.pointerEvents = 'none';
|
|
670
|
+
projectile.style.margin = '0';
|
|
671
|
+
projectile.style.borderRadius = '50%';
|
|
672
|
+
projectile.style.display = 'flex';
|
|
673
|
+
projectile.style.alignItems = 'center';
|
|
674
|
+
projectile.style.justifyContent = 'center';
|
|
675
|
+
projectile.style.willChange = 'transform, opacity';
|
|
676
|
+
|
|
677
|
+
if (projectileType === 'code') {
|
|
678
|
+
projectile.style.background = 'radial-gradient(circle, rgba(14, 165, 233, 0.45) 0%, rgba(15, 23, 42, 0.98) 100%)';
|
|
679
|
+
projectile.style.border = '2px solid rgba(56, 189, 248, 0.95)';
|
|
680
|
+
projectile.style.boxShadow = '0 0 28px rgba(56, 189, 248, 0.85), inset 0 0 14px rgba(56, 189, 248, 0.45)';
|
|
681
|
+
projectile.style.color = '#38bdf8';
|
|
682
|
+
projectile.style.fontSize = '1.25rem';
|
|
683
|
+
projectile.style.fontWeight = '700';
|
|
684
|
+
projectile.style.fontFamily = 'monospace';
|
|
685
|
+
} else if (projectileType === 'text') {
|
|
686
|
+
projectile.style.background = 'radial-gradient(circle, rgba(167, 139, 250, 0.45) 0%, rgba(15, 23, 42, 0.98) 100%)';
|
|
687
|
+
projectile.style.border = '2px solid rgba(167, 139, 250, 0.95)';
|
|
688
|
+
projectile.style.boxShadow = '0 0 28px rgba(167, 139, 250, 0.85), inset 0 0 14px rgba(167, 139, 250, 0.45)';
|
|
689
|
+
projectile.style.fontSize = '1.45rem';
|
|
690
|
+
} else if (isPitchProj) {
|
|
691
|
+
projectile.style.background = 'radial-gradient(circle, rgba(251, 191, 36, 0.3) 0%, rgba(217, 119, 6, 0.95) 100%)';
|
|
692
|
+
projectile.style.border = '2px solid rgba(251, 191, 36, 0.9)';
|
|
693
|
+
projectile.style.boxShadow = '0 0 28px rgba(251, 191, 36, 0.75), inset 0 0 14px rgba(251, 191, 36, 0.4)';
|
|
694
|
+
projectile.style.fontSize = '1.35rem';
|
|
695
|
+
} else {
|
|
696
|
+
projectile.style.background = 'radial-gradient(circle, rgba(30, 41, 59, 0.95) 0%, rgba(15, 23, 42, 0.98) 100%)';
|
|
697
|
+
projectile.style.border = '2px solid rgba(124, 92, 252, 0.85)';
|
|
698
|
+
projectile.style.boxShadow = '0 0 28px rgba(124, 92, 252, 0.7), inset 0 0 15px rgba(124, 92, 252, 0.3)';
|
|
699
|
+
projectile.style.fontSize = '1.5rem';
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
const emojiSpan = document.createElement('span');
|
|
703
|
+
emojiSpan.textContent = emoji;
|
|
704
|
+
projectile.appendChild(emojiSpan);
|
|
705
|
+
|
|
706
|
+
document.body.appendChild(projectile);
|
|
707
|
+
|
|
708
|
+
if (onApproach && duration > 350) {
|
|
709
|
+
window.setTimeout(() => {
|
|
710
|
+
onApproach?.();
|
|
711
|
+
}, duration - 350);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const deltaX = (toRect.left + toRect.width / 2) - (fromRect.left + fromRect.width / 2);
|
|
715
|
+
const deltaY = (toRect.top + toRect.height / 2) - (fromRect.top + fromRect.height / 2);
|
|
716
|
+
const targetScale = Math.min(toRect.width / fromRect.width, toRect.height / fromRect.height) || 0.85;
|
|
717
|
+
|
|
718
|
+
// Smooth trajectory: fully visible throughout the flight
|
|
719
|
+
const anim = projectile.animate([
|
|
720
|
+
{
|
|
721
|
+
transform: 'translate(0px, 0px) scale(1)',
|
|
722
|
+
opacity: 1,
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
transform: `translate(${deltaX * 0.45}px, ${deltaY * 0.35}px) scale(${1 + (targetScale - 1) * 0.2})`,
|
|
726
|
+
opacity: 1,
|
|
727
|
+
offset: 0.45,
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
transform: `translate(${deltaX}px, ${deltaY}px) scale(${targetScale})`,
|
|
731
|
+
opacity: 1,
|
|
732
|
+
offset: 0.9,
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
transform: `translate(${deltaX}px, ${deltaY}px) scale(${targetScale})`,
|
|
736
|
+
opacity: 0,
|
|
737
|
+
offset: 1.0,
|
|
738
|
+
}
|
|
739
|
+
], {
|
|
740
|
+
duration,
|
|
741
|
+
easing: 'cubic-bezier(0.22, 1, 0.36, 1)',
|
|
742
|
+
fill: 'forwards',
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
anim.onfinish = () => {
|
|
746
|
+
projectile.remove();
|
|
747
|
+
if (projectileType === 'code' || projectileType === 'text') {
|
|
748
|
+
sourceWrapperEl.classList.remove('node-is-escaping');
|
|
749
|
+
}
|
|
750
|
+
onDocked();
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
public handlePitchClick(isAuto: boolean = false): void {
|
|
755
|
+
this.initialOnboardingDone = true;
|
|
756
|
+
if (!this.el.nodePitchWrapperEl || !this.el.btnNodePitchEl) return;
|
|
757
|
+
if (this.el.nodePitchWrapperEl.classList.contains('node-is-escaping')) return;
|
|
758
|
+
|
|
759
|
+
const targetEl = document.getElementById('btn-header-pitch');
|
|
760
|
+
const fallbackTarget = {
|
|
761
|
+
x: window.innerWidth / 2 - 26,
|
|
762
|
+
y: 8,
|
|
763
|
+
width: 52,
|
|
764
|
+
height: 52,
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
this.launchProjectile({
|
|
768
|
+
sourceWrapperEl: this.el.nodePitchWrapperEl,
|
|
769
|
+
sourceBtnEl: this.el.btnNodePitchEl,
|
|
770
|
+
targetEl,
|
|
771
|
+
fallbackTarget,
|
|
772
|
+
emoji: '💰',
|
|
773
|
+
isPitch: true,
|
|
774
|
+
duration: 1600,
|
|
775
|
+
onDocked: () => {
|
|
776
|
+
this.dispatchEvent(new CustomEvent('space-gib-pitch-docked', {
|
|
777
|
+
bubbles: true,
|
|
778
|
+
composed: true,
|
|
779
|
+
detail: { isAuto },
|
|
780
|
+
}));
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
public handleIdentityClick(isAuto: boolean = false): void {
|
|
786
|
+
this.initialOnboardingDone = true;
|
|
787
|
+
if (!this.el.nodeIdentityWrapperEl || !this.el.btnNodeIdentityEl) return;
|
|
788
|
+
if (this.el.nodeIdentityWrapperEl.classList.contains('node-is-escaping')) return;
|
|
789
|
+
|
|
790
|
+
const targetEl = document.getElementById('identity-header-container');
|
|
791
|
+
const fallbackTarget = {
|
|
792
|
+
x: window.innerWidth - 180,
|
|
793
|
+
y: 8,
|
|
794
|
+
width: 140,
|
|
795
|
+
height: 40,
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
this.launchProjectile({
|
|
799
|
+
sourceWrapperEl: this.el.nodeIdentityWrapperEl,
|
|
800
|
+
sourceBtnEl: this.el.btnNodeIdentityEl,
|
|
801
|
+
targetEl,
|
|
802
|
+
fallbackTarget,
|
|
803
|
+
emoji: '👤',
|
|
804
|
+
isPitch: false,
|
|
805
|
+
duration: 1600,
|
|
806
|
+
onDocked: () => {
|
|
807
|
+
this.dispatchEvent(new CustomEvent('space-gib-identity-docked', {
|
|
808
|
+
bubbles: true,
|
|
809
|
+
composed: true,
|
|
810
|
+
detail: { isAuto },
|
|
811
|
+
}));
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
public handleAgentClick(isAuto: boolean = false): void {
|
|
817
|
+
this.initialOnboardingDone = true;
|
|
818
|
+
if (!this.el.nodeAgentWrapperEl || !this.el.btnNodeAgentEl) return;
|
|
819
|
+
if (this.el.nodeAgentWrapperEl.classList.contains('node-is-escaping')) return;
|
|
820
|
+
|
|
821
|
+
const targetEl = document.getElementById('btn-footer-agent');
|
|
822
|
+
const fallbackTarget = {
|
|
823
|
+
x: window.innerWidth - 60,
|
|
824
|
+
y: window.innerHeight - 44,
|
|
825
|
+
width: 44,
|
|
826
|
+
height: 44,
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
this.launchProjectile({
|
|
830
|
+
sourceWrapperEl: this.el.nodeAgentWrapperEl,
|
|
831
|
+
sourceBtnEl: this.el.btnNodeAgentEl,
|
|
832
|
+
targetEl,
|
|
833
|
+
fallbackTarget,
|
|
834
|
+
emoji: '🤖',
|
|
835
|
+
isPitch: false,
|
|
836
|
+
projectileType: 'agent',
|
|
837
|
+
duration: 1600,
|
|
838
|
+
onDocked: () => {
|
|
839
|
+
this.dispatchEvent(new CustomEvent('space-gib-agent-docked', {
|
|
840
|
+
bubbles: true,
|
|
841
|
+
composed: true,
|
|
842
|
+
detail: { isAuto },
|
|
843
|
+
}));
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
public resetDriftAnimations(): void {
|
|
849
|
+
if (this.el.nodePitchWrapperEl) {
|
|
850
|
+
this.el.nodePitchWrapperEl.classList.remove('node-is-escaping');
|
|
851
|
+
this.el.nodePitchWrapperEl.style.opacity = '';
|
|
852
|
+
this.el.nodePitchWrapperEl.style.pointerEvents = '';
|
|
853
|
+
this.el.nodePitchWrapperEl.style.animation = 'none';
|
|
854
|
+
void this.el.nodePitchWrapperEl.offsetHeight; // trigger reflow
|
|
855
|
+
this.el.nodePitchWrapperEl.style.animation = '';
|
|
856
|
+
}
|
|
857
|
+
if (this.el.nodeIdentityWrapperEl) {
|
|
858
|
+
this.el.nodeIdentityWrapperEl.classList.remove('node-is-escaping');
|
|
859
|
+
this.el.nodeIdentityWrapperEl.style.opacity = '';
|
|
860
|
+
this.el.nodeIdentityWrapperEl.style.pointerEvents = '';
|
|
861
|
+
this.el.nodeIdentityWrapperEl.style.animation = 'none';
|
|
862
|
+
void this.el.nodeIdentityWrapperEl.offsetHeight; // trigger reflow
|
|
863
|
+
this.el.nodeIdentityWrapperEl.style.animation = '';
|
|
864
|
+
}
|
|
865
|
+
if (this.el.nodeAgentWrapperEl) {
|
|
866
|
+
this.el.nodeAgentWrapperEl.classList.remove('node-is-escaping');
|
|
867
|
+
this.el.nodeAgentWrapperEl.style.opacity = '';
|
|
868
|
+
this.el.nodeAgentWrapperEl.style.pointerEvents = '';
|
|
869
|
+
this.el.nodeAgentWrapperEl.style.animation = 'none';
|
|
870
|
+
void this.el.nodeAgentWrapperEl.offsetHeight; // trigger reflow
|
|
871
|
+
this.el.nodeAgentWrapperEl.style.animation = '';
|
|
872
|
+
}
|
|
873
|
+
if (this.el.nodeSuperspaceWrapperEl) {
|
|
874
|
+
this.el.nodeSuperspaceWrapperEl.style.animation = 'none';
|
|
875
|
+
void this.el.nodeSuperspaceWrapperEl.offsetHeight; // trigger reflow
|
|
876
|
+
this.el.nodeSuperspaceWrapperEl.style.animation = '';
|
|
877
|
+
}
|
|
878
|
+
if (this.el.superspaceSatellitesEl) {
|
|
879
|
+
const wrappers = this.el.superspaceSatellitesEl.querySelectorAll<HTMLElement>('.satellite-wrapper');
|
|
880
|
+
this.el.superspaceSatellitesEl.style.animation = 'none';
|
|
881
|
+
wrappers.forEach(w => w.style.animation = 'none');
|
|
882
|
+
void this.el.superspaceSatellitesEl.offsetHeight; // trigger reflow
|
|
883
|
+
this.el.superspaceSatellitesEl.style.animation = '';
|
|
884
|
+
wrappers.forEach(w => w.style.animation = '');
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
private autoDockTimer?: number;
|
|
889
|
+
|
|
890
|
+
public startAutoDockTimer(): void {
|
|
891
|
+
this.clearAutoDockTimer();
|
|
892
|
+
this.autoDockTimer = window.setTimeout(() => {
|
|
893
|
+
console.log(`${this.lc} 8-second auto-dock timer triggered.`);
|
|
894
|
+
this.initialOnboardingDone = true;
|
|
895
|
+
this.handlePitchClick(true);
|
|
896
|
+
setTimeout(() => this.handleIdentityClick(true), 250);
|
|
897
|
+
setTimeout(() => this.handleAgentClick(true), 500);
|
|
898
|
+
}, 8000);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
public clearAutoDockTimer(): void {
|
|
902
|
+
if (this.autoDockTimer) {
|
|
903
|
+
clearTimeout(this.autoDockTimer);
|
|
904
|
+
this.autoDockTimer = undefined;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
public handleSpaceClick(
|
|
909
|
+
typeOrSpace: AttachedSpace | 'code' | 'text',
|
|
910
|
+
sourceBtn?: HTMLElement,
|
|
911
|
+
sourceWrapper?: HTMLElement
|
|
912
|
+
): AttachedSpace | null {
|
|
913
|
+
if (!this.activeCosmosId) return null;
|
|
914
|
+
const activeCosmos = this.getActiveCosmos();
|
|
915
|
+
if (!activeCosmos) { return null; }
|
|
916
|
+
if (this.isSpaceLaunching) return null;
|
|
917
|
+
|
|
918
|
+
this.clearAutoDockTimer();
|
|
919
|
+
this.initialOnboardingDone = true;
|
|
920
|
+
this.isSpaceLaunching = true;
|
|
921
|
+
|
|
922
|
+
const type = typeof typeOrSpace === 'string' ? typeOrSpace : typeOrSpace.type;
|
|
923
|
+
const space = typeof typeOrSpace === 'object' ? typeOrSpace : undefined;
|
|
924
|
+
|
|
925
|
+
const count = activeCosmos.spaces.filter(s => s.type === type).length;
|
|
926
|
+
const existingSpacesCount = activeCosmos.spaces.length;
|
|
927
|
+
const newSpace: AttachedSpace = space || {
|
|
928
|
+
id: `${type}-space-${count + 1}-${Date.now()}`,
|
|
929
|
+
type,
|
|
930
|
+
name: type === 'code' ? `Code Space ${count + 1}` : `Text Space ${count + 1}`,
|
|
931
|
+
shortLabel: type === 'code' ? `Code ${count + 1}` : `Text ${count + 1}`,
|
|
932
|
+
icon: type === 'code' ? '</>' : '📝',
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
if (!activeCosmos.spaces.some(s => s.id === newSpace.id)) {
|
|
936
|
+
activeCosmos.spaces.push(newSpace);
|
|
937
|
+
this.saveState();
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
const sourceBtnEl = sourceBtn || (this.el.superspaceSatellitesEl?.querySelector('.satellite-node-btn') as HTMLElement) || this.el.btnNodeSuperspaceEl;
|
|
941
|
+
const sourceWrapperEl = sourceWrapper || (sourceBtnEl?.closest('.satellite-wrapper') as HTMLElement) || sourceBtnEl;
|
|
942
|
+
|
|
943
|
+
const headerH = document.getElementById('main-header')?.getBoundingClientRect().height || 60;
|
|
944
|
+
const targetTop = headerH + 128 + (existingSpacesCount * 50);
|
|
945
|
+
const fallbackTarget = {
|
|
946
|
+
x: 5,
|
|
947
|
+
y: targetTop,
|
|
948
|
+
width: 42,
|
|
949
|
+
height: 42,
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
if (!sourceBtnEl || !sourceWrapperEl) {
|
|
953
|
+
this.isSpaceLaunching = false;
|
|
954
|
+
this.dispatchEvent(new CustomEvent('space-gib-space-added', {
|
|
955
|
+
detail: {
|
|
956
|
+
cosmosId: activeCosmos.id,
|
|
957
|
+
space: newSpace,
|
|
958
|
+
cosmos: activeCosmos,
|
|
959
|
+
},
|
|
960
|
+
bubbles: true,
|
|
961
|
+
composed: true,
|
|
962
|
+
}));
|
|
963
|
+
return newSpace;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// Make activity bar visible BEFORE the zoom flight starts
|
|
967
|
+
window.dispatchEvent(new CustomEvent('space-gib-activity-bar-expand-requested', {
|
|
968
|
+
bubbles: true,
|
|
969
|
+
composed: true,
|
|
970
|
+
}));
|
|
971
|
+
|
|
972
|
+
setTimeout(() => {
|
|
973
|
+
this.launchProjectile({
|
|
974
|
+
sourceWrapperEl,
|
|
975
|
+
sourceBtnEl,
|
|
976
|
+
fallbackTarget,
|
|
977
|
+
emoji: newSpace.icon || (type === 'code' ? '</>' : '📝'),
|
|
978
|
+
projectileType: type === 'code' ? 'code' : 'text',
|
|
979
|
+
duration: 1400,
|
|
980
|
+
onDocked: () => {
|
|
981
|
+
this.isSpaceLaunching = false;
|
|
982
|
+
sourceWrapperEl.classList.remove('node-is-escaping');
|
|
983
|
+
this.dispatchEvent(new CustomEvent('space-gib-space-added', {
|
|
984
|
+
detail: {
|
|
985
|
+
cosmosId: activeCosmos.id,
|
|
986
|
+
space: newSpace,
|
|
987
|
+
cosmos: activeCosmos,
|
|
988
|
+
},
|
|
989
|
+
bubbles: true,
|
|
990
|
+
composed: true,
|
|
991
|
+
}));
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
}, 180);
|
|
995
|
+
|
|
996
|
+
return newSpace;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
public async createAndAttachGalaxy({
|
|
1000
|
+
cosmosId,
|
|
1001
|
+
galaxyName,
|
|
1002
|
+
discriminator,
|
|
1003
|
+
skipEvent = false,
|
|
1004
|
+
}: {
|
|
1005
|
+
cosmosId?: string;
|
|
1006
|
+
galaxyName: string;
|
|
1007
|
+
discriminator?: GalaxyPresetType | 'code' | 'notes';
|
|
1008
|
+
skipEvent?: boolean;
|
|
1009
|
+
}): Promise<AttachedSpace | undefined> {
|
|
1010
|
+
const cosmos = cosmosId ? this.cosmoses.find(c => c.id === cosmosId) : this.getActiveCosmos();
|
|
1011
|
+
if (!cosmos) return undefined;
|
|
1012
|
+
|
|
1013
|
+
try {
|
|
1014
|
+
const { metaspace, space } = await getPlaygroundMetaspaceAndSpace();
|
|
1015
|
+
const spaceId = space?.ib ? getSpaceIdFromIb(space.ib) || 'temp_space' : 'temp_space';
|
|
1016
|
+
|
|
1017
|
+
// 1. Fetch Cosmos IbGib
|
|
1018
|
+
let cosmosIbGib: CosmosIbGib_V1 | undefined;
|
|
1019
|
+
if (cosmos.ibGibAddr) {
|
|
1020
|
+
cosmosIbGib = await getIbGibFromSpace<CosmosIbGib_V1>({ addr: cosmos.ibGibAddr, space });
|
|
1021
|
+
}
|
|
1022
|
+
if (!cosmosIbGib) {
|
|
1023
|
+
// Try by name in router cache
|
|
1024
|
+
const router = getSpaceGibRouterSvc();
|
|
1025
|
+
cosmosIbGib = router.cosmosManifestCache?.get(cosmos.name);
|
|
1026
|
+
}
|
|
1027
|
+
if (!cosmosIbGib) {
|
|
1028
|
+
throw new Error(`cosmosIbGib not found for cosmos "${cosmos.name}" (addr: ${cosmos.ibGibAddr || 'none'}). (E: 661ab8c2b7b8800be247f588b17ee726)`);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// 2. Create Galaxy IbGib
|
|
1032
|
+
let galaxyIbGib: GalaxyIbGib_V1;
|
|
1033
|
+
const isCode = discriminator === GalaxyPresetType.code;
|
|
1034
|
+
if (isCode) {
|
|
1035
|
+
const initRes = await executeVcsInit({
|
|
1036
|
+
metaspace,
|
|
1037
|
+
localSpace: space,
|
|
1038
|
+
galaxyName,
|
|
1039
|
+
branchName: 'main',
|
|
1040
|
+
});
|
|
1041
|
+
galaxyIbGib = initRes.galaxyIbGib;
|
|
1042
|
+
} else {
|
|
1043
|
+
galaxyIbGib = await createGalaxyIbGib({
|
|
1044
|
+
galaxyName,
|
|
1045
|
+
discriminator: GalaxyPresetType.notes,
|
|
1046
|
+
space,
|
|
1047
|
+
metaspace,
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// 3. Add galaxy to cosmos manifest
|
|
1052
|
+
const sqGalaxyAddr = getSpaceQualifiedIbGibAddr({
|
|
1053
|
+
spaceId,
|
|
1054
|
+
ibGibAddr: getIbGibAddr({ ibGib: galaxyIbGib }),
|
|
1055
|
+
});
|
|
1056
|
+
cosmosIbGib = await addGalaxyToCosmos({
|
|
1057
|
+
cosmosIbGib,
|
|
1058
|
+
sqGalaxyAddr,
|
|
1059
|
+
galaxyIbGib,
|
|
1060
|
+
space,
|
|
1061
|
+
metaspace,
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
// 4. Update cosmos ibGibAddr and router manifest cache
|
|
1065
|
+
cosmos.ibGibAddr = getIbGibAddr({ ibGib: cosmosIbGib });
|
|
1066
|
+
getSpaceGibRouterSvc().setCosmosManifest(cosmos.name, cosmosIbGib);
|
|
1067
|
+
|
|
1068
|
+
// 5. Update local cosmos state
|
|
1069
|
+
const newSpace: AttachedSpace = {
|
|
1070
|
+
id: galaxyName,
|
|
1071
|
+
type: isCode ? GalaxyPresetType.code : GalaxyPresetType.notes,
|
|
1072
|
+
name: galaxyName,
|
|
1073
|
+
shortLabel: galaxyName,
|
|
1074
|
+
icon: isCode ? '</>' : '📝',
|
|
1075
|
+
};
|
|
1076
|
+
if (!cosmos.spaces.some(s => s.id === galaxyName)) {
|
|
1077
|
+
cosmos.spaces.push(newSpace);
|
|
1078
|
+
}
|
|
1079
|
+
this.saveState();
|
|
1080
|
+
|
|
1081
|
+
// 7. Dispatch space added event if not skipped (e.g. handleSpaceClick dispatches on projectile dock)
|
|
1082
|
+
if (!skipEvent) {
|
|
1083
|
+
this.dispatchEvent(new CustomEvent('space-gib-space-added', {
|
|
1084
|
+
detail: {
|
|
1085
|
+
cosmosId: cosmos.id,
|
|
1086
|
+
space: newSpace,
|
|
1087
|
+
cosmos,
|
|
1088
|
+
},
|
|
1089
|
+
bubbles: true,
|
|
1090
|
+
composed: true,
|
|
1091
|
+
}));
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
return newSpace;
|
|
1095
|
+
} catch (err) {
|
|
1096
|
+
console.error(`${this.lc} Failed to create and attach galaxy: ${extractErrorMsg(err)}`);
|
|
1097
|
+
throw err;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
public async createCosmos(name?: string, isTemporary?: boolean): Promise<CosmosInfo> {
|
|
1102
|
+
const cosmosName = name || `cosmos-${this.cosmoses.length + 1}`;
|
|
1103
|
+
const hasIdentity = Boolean((globalThis as any).ibgib?.identity?.domainIdentity);
|
|
1104
|
+
const temp = isTemporary !== undefined ? isTemporary : !hasIdentity;
|
|
1105
|
+
|
|
1106
|
+
const { metaspace, space } = await getPlaygroundMetaspaceAndSpace();
|
|
1107
|
+
const cosmosIbGib = await createCosmosIbGib({
|
|
1108
|
+
cosmosName,
|
|
1109
|
+
description: `${cosmosName} Cosmos`,
|
|
1110
|
+
space,
|
|
1111
|
+
metaspace,
|
|
1112
|
+
});
|
|
1113
|
+
const ibGibAddr = getIbGibAddr({ ibGib: cosmosIbGib });
|
|
1114
|
+
getSpaceGibRouterSvc().setCosmosManifest(cosmosName, cosmosIbGib);
|
|
1115
|
+
|
|
1116
|
+
const newCosmos: CosmosInfo = {
|
|
1117
|
+
id: cosmosName,
|
|
1118
|
+
name: cosmosName,
|
|
1119
|
+
ibGibAddr,
|
|
1120
|
+
isTemporary: temp,
|
|
1121
|
+
spaces: [],
|
|
1122
|
+
createdAt: Date.now(),
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
this.cosmoses.push(newCosmos);
|
|
1126
|
+
this.activeCosmosId = newCosmos.id;
|
|
1127
|
+
this.saveState();
|
|
1128
|
+
return newCosmos;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
public selectCosmos(cosmosId: string): void {
|
|
1132
|
+
const cosmos = this.cosmoses.find(c => c.id === cosmosId || c.name === cosmosId);
|
|
1133
|
+
if (!cosmos) { return; }
|
|
1134
|
+
|
|
1135
|
+
this.activeCosmosId = cosmos.id;
|
|
1136
|
+
this.saveState();
|
|
1137
|
+
this.setZoomState('cosmos', cosmos.id);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
public async findCosmos(cosmosNameOrId: string): Promise<CosmosInfo | undefined> {
|
|
1141
|
+
let existing = this.cosmoses.find(c => c.id === cosmosNameOrId || c.name === cosmosNameOrId);
|
|
1142
|
+
if (existing) { return existing; }
|
|
1143
|
+
|
|
1144
|
+
if (cosmosNameOrId === 'welcome-cosmos') {
|
|
1145
|
+
const seeded = await seedInMemoryCosmosWorkspace();
|
|
1146
|
+
if (!this.cosmoses.some(c => c.id === seeded.id)) {
|
|
1147
|
+
this.cosmoses.push(seeded);
|
|
1148
|
+
}
|
|
1149
|
+
return seeded;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const router = getSpaceGibRouterSvc();
|
|
1153
|
+
const cachedManifest = router.getCachedCosmosManifest(cosmosNameOrId);
|
|
1154
|
+
if (cachedManifest) {
|
|
1155
|
+
const ibGibAddr = getIbGibAddr({ ibGib: cachedManifest });
|
|
1156
|
+
const restoredCosmos: CosmosInfo = {
|
|
1157
|
+
id: cosmosNameOrId,
|
|
1158
|
+
name: cosmosNameOrId,
|
|
1159
|
+
ibGibAddr,
|
|
1160
|
+
spaces: [],
|
|
1161
|
+
createdAt: Date.now(),
|
|
1162
|
+
};
|
|
1163
|
+
this.cosmoses.push(restoredCosmos);
|
|
1164
|
+
return restoredCosmos;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
return undefined;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
public async ensureCosmos(cosmosId: string): Promise<CosmosInfo | undefined> {
|
|
1171
|
+
return this.findCosmos(cosmosId);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
public setZoomState(state: 'polycosmos' | 'cosmos', cosmosId?: string): void {
|
|
1175
|
+
if (cosmosId) {
|
|
1176
|
+
this.activeCosmosId = cosmosId;
|
|
1177
|
+
this.saveState();
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
this.zoomState = state;
|
|
1181
|
+
this.renderCurrentState();
|
|
1182
|
+
|
|
1183
|
+
if (state === 'polycosmos') {
|
|
1184
|
+
this.dispatchEvent(new CustomEvent('space-gib-polycosmos-activated', {
|
|
1185
|
+
detail: { cosmoses: this.cosmoses, activeCosmosId: this.activeCosmosId },
|
|
1186
|
+
bubbles: true,
|
|
1187
|
+
composed: true,
|
|
1188
|
+
}));
|
|
1189
|
+
} else {
|
|
1190
|
+
const activeCosmos = this.getActiveCosmos();
|
|
1191
|
+
if (activeCosmos) {
|
|
1192
|
+
this.dispatchEvent(new CustomEvent('space-gib-cosmos-selected', {
|
|
1193
|
+
detail: { cosmos: activeCosmos },
|
|
1194
|
+
bubbles: true,
|
|
1195
|
+
composed: true,
|
|
1196
|
+
}));
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
private renderCurrentState(): void {
|
|
1202
|
+
if (this.zoomState === 'cosmos' || this.cosmoses.length > 0) {
|
|
1203
|
+
this.hasBigBangExploded = true;
|
|
1204
|
+
this.starfieldFadeAlpha = 1.0;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
if (this.zoomState === 'polycosmos') {
|
|
1208
|
+
this.clearAutoDockTimer();
|
|
1209
|
+
this.el.rootEl.classList.remove('zoom-cosmos');
|
|
1210
|
+
this.el.rootEl.classList.add('zoom-polycosmos');
|
|
1211
|
+
|
|
1212
|
+
this.el.polycosmosStageEl.classList.remove('hidden');
|
|
1213
|
+
this.el.activeCosmosStageEl.classList.add('hidden');
|
|
1214
|
+
|
|
1215
|
+
if (this.cosmoses.length === 0) {
|
|
1216
|
+
// Primordial 0-state
|
|
1217
|
+
this.el.orbitTrackEl.classList.add('hidden');
|
|
1218
|
+
this.el.singularityLabelEl.textContent = '';
|
|
1219
|
+
} else {
|
|
1220
|
+
// Multi-cosmos Polycosmos
|
|
1221
|
+
this.el.orbitTrackEl.classList.remove('hidden');
|
|
1222
|
+
this.el.singularityLabelEl.textContent = '+ New Cosmos';
|
|
1223
|
+
this.renderOrbitNodes();
|
|
1224
|
+
}
|
|
1225
|
+
} else {
|
|
1226
|
+
// Zoomed-in Cosmos
|
|
1227
|
+
this.el.rootEl.classList.remove('zoom-polycosmos');
|
|
1228
|
+
this.el.rootEl.classList.add('zoom-cosmos');
|
|
1229
|
+
|
|
1230
|
+
this.el.polycosmosStageEl.classList.add('hidden');
|
|
1231
|
+
this.el.activeCosmosStageEl.classList.remove('hidden');
|
|
1232
|
+
|
|
1233
|
+
const showInitialNodes = !this.initialOnboardingDone && this.cosmoses.length <= 1;
|
|
1234
|
+
|
|
1235
|
+
if (this.el.nodePitchWrapperEl) {
|
|
1236
|
+
this.el.nodePitchWrapperEl.style.display = showInitialNodes ? '' : 'none';
|
|
1237
|
+
}
|
|
1238
|
+
if (this.el.nodeIdentityWrapperEl) {
|
|
1239
|
+
this.el.nodeIdentityWrapperEl.style.display = showInitialNodes ? '' : 'none';
|
|
1240
|
+
}
|
|
1241
|
+
if (this.el.nodeAgentWrapperEl) {
|
|
1242
|
+
this.el.nodeAgentWrapperEl.style.display = showInitialNodes ? '' : 'none';
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
if (showInitialNodes) {
|
|
1246
|
+
this.resetDriftAnimations();
|
|
1247
|
+
this.startAutoDockTimer();
|
|
1248
|
+
} else {
|
|
1249
|
+
this.clearAutoDockTimer();
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
const activeCosmos = this.getActiveCosmos();
|
|
1253
|
+
if (activeCosmos) {
|
|
1254
|
+
this.el.activeCosmosTitleCaptionEl.textContent = activeCosmos.name;
|
|
1255
|
+
if (activeCosmos.isTemporary) {
|
|
1256
|
+
this.el.activeCosmosTempBadgeEl?.classList.remove('hidden');
|
|
1257
|
+
} else {
|
|
1258
|
+
this.el.activeCosmosTempBadgeEl?.classList.add('hidden');
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
private renderOrbitNodes(): void {
|
|
1265
|
+
const container = this.el.orbitingCosmosesContainerEl;
|
|
1266
|
+
if (!container) { return; }
|
|
1267
|
+
|
|
1268
|
+
container.innerHTML = '';
|
|
1269
|
+
const N = this.cosmoses.length;
|
|
1270
|
+
if (N === 0) { return; }
|
|
1271
|
+
|
|
1272
|
+
const radius = 210; // orbit radius
|
|
1273
|
+
const cx = 240;
|
|
1274
|
+
const cy = 240;
|
|
1275
|
+
|
|
1276
|
+
for (let i = 0; i < N; i++) {
|
|
1277
|
+
const c = this.cosmoses[i];
|
|
1278
|
+
const angle = (i / N) * 2 * Math.PI - Math.PI / 2;
|
|
1279
|
+
const x = Math.round(cx + Math.cos(angle) * radius);
|
|
1280
|
+
const y = Math.round(cy + Math.sin(angle) * radius);
|
|
1281
|
+
|
|
1282
|
+
const btn = document.createElement('button');
|
|
1283
|
+
btn.type = 'button';
|
|
1284
|
+
btn.className = `orbit-cosmos-node ${c.id === this.activeCosmosId ? 'is-active' : ''}`;
|
|
1285
|
+
btn.style.left = `${x}px`;
|
|
1286
|
+
btn.style.top = `${y}px`;
|
|
1287
|
+
btn.title = `Enter ${c.name} (${c.spaces.length} spaces)`;
|
|
1288
|
+
|
|
1289
|
+
btn.innerHTML = `
|
|
1290
|
+
<span class="orbit-cosmos-icon">🌌</span>
|
|
1291
|
+
<span class="orbit-cosmos-name">${c.name}</span>
|
|
1292
|
+
<span class="orbit-cosmos-badge">${c.spaces.length} space${c.spaces.length === 1 ? '' : 's'}</span>
|
|
1293
|
+
`;
|
|
1294
|
+
|
|
1295
|
+
btn.addEventListener('click', (e) => {
|
|
1296
|
+
e.stopPropagation();
|
|
1297
|
+
this.selectCosmos(c.id);
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
container.appendChild(btn);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
public getCosmoses(): CosmosInfo[] {
|
|
1305
|
+
return [...this.cosmoses];
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
public getActiveCosmos(): CosmosInfo | null {
|
|
1309
|
+
if (!this.activeCosmosId) return this.cosmoses[0] || null;
|
|
1310
|
+
return this.cosmoses.find(c => c.id === this.activeCosmosId) || this.cosmoses[0] || null;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
public getZoomState(): 'polycosmos' | 'cosmos' {
|
|
1314
|
+
return this.zoomState;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
|