@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,648 @@
|
|
|
1
|
+
import thisCss from "./clone.css";
|
|
2
|
+
import thisHtml from "./clone.html";
|
|
3
|
+
|
|
4
|
+
import { ROOT_ADDR } from "@ibgib/ts-gib/dist/V1/constants.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 { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
|
|
8
|
+
import { promptForText } from "@ibgib/web-gib/dist/helpers.web.mjs";
|
|
9
|
+
import { validateBranchName } from "@ibgib/core-gib/dist/cosmology/branching/branch/branch-helpers.mjs";
|
|
10
|
+
import {
|
|
11
|
+
IbGibDynamicComponentMetaBase, IbGibDynamicComponentInstanceBase,
|
|
12
|
+
} from "@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs";
|
|
13
|
+
import {
|
|
14
|
+
ElementsBase, IbGibDynamicComponentInstance,
|
|
15
|
+
} from "@ibgib/web-gib/dist/ui/component/component-types.mjs";
|
|
16
|
+
import { getComponentCtorArg } from "@ibgib/web-gib/dist/app-bootstrap/init-orchestration.mjs";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
CLONE_COMPONENT_NAME, EVENT_SPACE_GIB_CLONE_CLOSED,
|
|
20
|
+
EVENT_SPACE_GIB_CLONE_EXECUTED, CLONE_SCOPE_COSMOS, CLONE_SCOPE_GALAXY,
|
|
21
|
+
CLONE_SCOPE_ITEM, CLONE_MODALITY_CLI, CLONE_MODALITY_ZIP,
|
|
22
|
+
CLONE_MODALITY_DAG,
|
|
23
|
+
} from "./clone-constants.mjs";
|
|
24
|
+
import {
|
|
25
|
+
CloneScope, CloneModality, CloneItemContext, CloneTreeState,
|
|
26
|
+
CloneModalOptions,
|
|
27
|
+
} from "./clone-types.mjs";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Metadata factory for space-gib-clone Web Component.
|
|
31
|
+
*/
|
|
32
|
+
export class CloneComponentMeta extends IbGibDynamicComponentMetaBase {
|
|
33
|
+
protected lc: string = `[${CloneComponentMeta.name}]`;
|
|
34
|
+
|
|
35
|
+
routeRegExp?: RegExp = new RegExp(`^${CLONE_COMPONENT_NAME}$`);
|
|
36
|
+
componentName = CLONE_COMPONENT_NAME;
|
|
37
|
+
|
|
38
|
+
constructor() {
|
|
39
|
+
super(getComponentCtorArg());
|
|
40
|
+
if (!customElements.get(this.componentName)) {
|
|
41
|
+
customElements.define(this.componentName, CloneComponentInstance);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async createInstance({
|
|
46
|
+
path,
|
|
47
|
+
ibGibAddr
|
|
48
|
+
}: {
|
|
49
|
+
path: string;
|
|
50
|
+
ibGibAddr: IbGibAddr;
|
|
51
|
+
}): Promise<IbGibDynamicComponentInstance> {
|
|
52
|
+
const component = document.createElement(this.componentName) as CloneComponentInstance;
|
|
53
|
+
await component.initialize({
|
|
54
|
+
ibGibAddr: ibGibAddr || ROOT_ADDR,
|
|
55
|
+
meta: this,
|
|
56
|
+
html: thisHtml,
|
|
57
|
+
css: [thisCss],
|
|
58
|
+
});
|
|
59
|
+
return component;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* DOM Elements interface for CloneComponentInstance
|
|
65
|
+
*/
|
|
66
|
+
export interface CloneElements extends ElementsBase {
|
|
67
|
+
contentEl: HTMLDivElement;
|
|
68
|
+
backdropEl: HTMLDivElement;
|
|
69
|
+
dialogEl: HTMLDivElement;
|
|
70
|
+
headerEl: HTMLDivElement;
|
|
71
|
+
btnCloseEl: HTMLButtonElement;
|
|
72
|
+
targetTitleEl: HTMLSpanElement;
|
|
73
|
+
|
|
74
|
+
// Scopes
|
|
75
|
+
scopeCosmosLabelEl: HTMLLabelElement;
|
|
76
|
+
scopeGalaxyLabelEl: HTMLLabelElement;
|
|
77
|
+
scopeItemLabelEl: HTMLLabelElement;
|
|
78
|
+
scopeCosmosRadioEl: HTMLInputElement;
|
|
79
|
+
scopeGalaxyRadioEl: HTMLInputElement;
|
|
80
|
+
scopeItemRadioEl: HTMLInputElement;
|
|
81
|
+
|
|
82
|
+
// Tree / Item
|
|
83
|
+
treeContainerEl: HTMLDivElement;
|
|
84
|
+
singleItemContainerEl: HTMLDivElement;
|
|
85
|
+
selectionSummaryEl: HTMLSpanElement;
|
|
86
|
+
|
|
87
|
+
// Modality Tabs
|
|
88
|
+
tabCliEl: HTMLButtonElement;
|
|
89
|
+
tabZipEl: HTMLButtonElement;
|
|
90
|
+
tabDagEl: HTMLButtonElement;
|
|
91
|
+
|
|
92
|
+
// Panels
|
|
93
|
+
panelCliEl: HTMLDivElement;
|
|
94
|
+
panelZipEl: HTMLDivElement;
|
|
95
|
+
panelDagEl: HTMLDivElement;
|
|
96
|
+
|
|
97
|
+
// CLI actions
|
|
98
|
+
cmdPreviewEl: HTMLElement;
|
|
99
|
+
btnCopyEl: HTMLButtonElement;
|
|
100
|
+
|
|
101
|
+
// Download actions
|
|
102
|
+
btnDownloadZipEl: HTMLButtonElement;
|
|
103
|
+
btnDownloadDagEl: HTMLButtonElement;
|
|
104
|
+
|
|
105
|
+
// Status
|
|
106
|
+
statusMessageEl: HTMLDivElement;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* space-gib-clone Web Component Instance.
|
|
111
|
+
* Implements Cosmos, Galaxy, and Single Item cloning via CLI or direct downloads.
|
|
112
|
+
*/
|
|
113
|
+
export class CloneComponentInstance
|
|
114
|
+
extends IbGibDynamicComponentInstanceBase<IbGib_V1, CloneElements>
|
|
115
|
+
implements IbGibDynamicComponentInstance<IbGib_V1, CloneElements> {
|
|
116
|
+
protected override lc: string = `[${CloneComponentInstance.name}]`;
|
|
117
|
+
|
|
118
|
+
get el(): CloneElements {
|
|
119
|
+
if (!this.elements) {
|
|
120
|
+
throw new Error(`elements is not defined in ${this.lc}`);
|
|
121
|
+
}
|
|
122
|
+
return this.elements;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
protected activeScope: CloneScope = CLONE_SCOPE_COSMOS;
|
|
126
|
+
protected activeModality: CloneModality = CLONE_MODALITY_CLI;
|
|
127
|
+
|
|
128
|
+
// Tree state populated either from context or sample cosmos hierarchy
|
|
129
|
+
protected treeState: CloneTreeState = {
|
|
130
|
+
cosmosId: 'cosmos-1',
|
|
131
|
+
cosmosName: 'Cosmos 1',
|
|
132
|
+
selected: true,
|
|
133
|
+
galaxies: [
|
|
134
|
+
{
|
|
135
|
+
id: 'galaxy-code',
|
|
136
|
+
name: 'Code Galaxy',
|
|
137
|
+
icon: '</>',
|
|
138
|
+
type: 'code',
|
|
139
|
+
selected: true,
|
|
140
|
+
branches: [
|
|
141
|
+
{ id: 'branch-main', name: 'main', isCanonical: true, selected: true },
|
|
142
|
+
{ id: 'branch-feat', name: 'feature/cosmos-drift', isCanonical: false, selected: false },
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: 'galaxy-notes',
|
|
147
|
+
name: 'Notes Galaxy',
|
|
148
|
+
icon: '📝',
|
|
149
|
+
type: 'notes',
|
|
150
|
+
selected: true,
|
|
151
|
+
branches: [
|
|
152
|
+
{ id: 'branch-notes-main', name: 'main', isCanonical: true, selected: true },
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
override async created(): Promise<void> {
|
|
159
|
+
const lc = `${this.lc}[${this.created.name}]`;
|
|
160
|
+
try {
|
|
161
|
+
console.log(`${lc} created starting...`);
|
|
162
|
+
const root = this.shadowRoot!;
|
|
163
|
+
|
|
164
|
+
this.elements = {
|
|
165
|
+
contentEl: root.getElementById('clone-dialog') as HTMLDivElement,
|
|
166
|
+
backdropEl: root.getElementById('clone-backdrop') as HTMLDivElement,
|
|
167
|
+
dialogEl: root.getElementById('clone-dialog') as HTMLDivElement,
|
|
168
|
+
headerEl: root.getElementById('clone-header') as HTMLDivElement,
|
|
169
|
+
btnCloseEl: root.getElementById('btn-clone-close') as HTMLButtonElement,
|
|
170
|
+
targetTitleEl: root.getElementById('clone-target-title') as HTMLSpanElement,
|
|
171
|
+
|
|
172
|
+
scopeCosmosLabelEl: root.getElementById('scope-cosmos-label') as HTMLLabelElement,
|
|
173
|
+
scopeGalaxyLabelEl: root.getElementById('scope-galaxy-label') as HTMLLabelElement,
|
|
174
|
+
scopeItemLabelEl: root.getElementById('scope-item-label') as HTMLLabelElement,
|
|
175
|
+
scopeCosmosRadioEl: root.getElementById('scope-cosmos') as HTMLInputElement,
|
|
176
|
+
scopeGalaxyRadioEl: root.getElementById('scope-galaxy') as HTMLInputElement,
|
|
177
|
+
scopeItemRadioEl: root.getElementById('scope-item') as HTMLInputElement,
|
|
178
|
+
|
|
179
|
+
treeContainerEl: root.getElementById('clone-tree-container') as HTMLDivElement,
|
|
180
|
+
singleItemContainerEl: root.getElementById('clone-single-item-container') as HTMLDivElement,
|
|
181
|
+
selectionSummaryEl: root.getElementById('clone-selection-summary') as HTMLSpanElement,
|
|
182
|
+
|
|
183
|
+
tabCliEl: root.getElementById('tab-cli') as HTMLButtonElement,
|
|
184
|
+
tabZipEl: root.getElementById('tab-zip') as HTMLButtonElement,
|
|
185
|
+
tabDagEl: root.getElementById('tab-dag') as HTMLButtonElement,
|
|
186
|
+
|
|
187
|
+
panelCliEl: root.getElementById('panel-cli') as HTMLDivElement,
|
|
188
|
+
panelZipEl: root.getElementById('panel-zip') as HTMLDivElement,
|
|
189
|
+
panelDagEl: root.getElementById('panel-dag') as HTMLDivElement,
|
|
190
|
+
|
|
191
|
+
cmdPreviewEl: root.getElementById('clone-cmd-preview') as HTMLElement,
|
|
192
|
+
btnCopyEl: root.getElementById('btn-clone-copy') as HTMLButtonElement,
|
|
193
|
+
|
|
194
|
+
btnDownloadZipEl: root.getElementById('btn-clone-download-zip') as HTMLButtonElement,
|
|
195
|
+
btnDownloadDagEl: root.getElementById('btn-clone-download-dag') as HTMLButtonElement,
|
|
196
|
+
|
|
197
|
+
statusMessageEl: root.getElementById('clone-status-message') as HTMLDivElement,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
this.bindEvents();
|
|
201
|
+
this.applyScopeRules();
|
|
202
|
+
this.renderTree();
|
|
203
|
+
this.updateCommandPreview();
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Configure modal with options when invoked by shell service.
|
|
211
|
+
*/
|
|
212
|
+
public setOptions(opts: CloneModalOptions): void {
|
|
213
|
+
const lc = `${this.lc}[${this.setOptions.name}]`;
|
|
214
|
+
console.log(`${lc} configuring options:`, opts);
|
|
215
|
+
|
|
216
|
+
if (opts.cosmosId) {
|
|
217
|
+
this.treeState.cosmosId = opts.cosmosId;
|
|
218
|
+
this.treeState.cosmosName = opts.cosmosName || opts.cosmosId;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (opts.singleItem) {
|
|
222
|
+
this.treeState.singleItem = opts.singleItem;
|
|
223
|
+
this.activeScope = CLONE_SCOPE_ITEM;
|
|
224
|
+
} else if (opts.galaxyName) {
|
|
225
|
+
this.treeState.singleItem = undefined;
|
|
226
|
+
this.activeScope = CLONE_SCOPE_GALAXY;
|
|
227
|
+
} else {
|
|
228
|
+
this.treeState.singleItem = undefined;
|
|
229
|
+
this.activeScope = CLONE_SCOPE_COSMOS;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (this.elements) {
|
|
233
|
+
this.applyScopeRules();
|
|
234
|
+
this.renderTree();
|
|
235
|
+
this.updateCommandPreview();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private bindEvents(): void {
|
|
240
|
+
// Close modal on close button or backdrop click
|
|
241
|
+
this.el.btnCloseEl?.addEventListener('click', () => this.close());
|
|
242
|
+
this.el.backdropEl?.addEventListener('click', (e) => {
|
|
243
|
+
if (e.target === this.el.backdropEl) {
|
|
244
|
+
this.close();
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Scope change listeners
|
|
249
|
+
this.el.scopeCosmosRadioEl?.addEventListener('change', () => this.setScope(CLONE_SCOPE_COSMOS));
|
|
250
|
+
this.el.scopeGalaxyRadioEl?.addEventListener('change', () => this.setScope(CLONE_SCOPE_GALAXY));
|
|
251
|
+
this.el.scopeItemRadioEl?.addEventListener('change', () => this.setScope(CLONE_SCOPE_ITEM));
|
|
252
|
+
|
|
253
|
+
// Modality tabs
|
|
254
|
+
this.el.tabCliEl?.addEventListener('click', () => this.setModality(CLONE_MODALITY_CLI));
|
|
255
|
+
this.el.tabZipEl?.addEventListener('click', () => this.setModality(CLONE_MODALITY_ZIP));
|
|
256
|
+
this.el.tabDagEl?.addEventListener('click', () => this.setModality(CLONE_MODALITY_DAG));
|
|
257
|
+
|
|
258
|
+
// CLI Copy button
|
|
259
|
+
this.el.btnCopyEl?.addEventListener('click', () => this.copyCommandToClipboard());
|
|
260
|
+
|
|
261
|
+
// Downloads
|
|
262
|
+
this.el.btnDownloadZipEl?.addEventListener('click', () => this.executeDownload('zip'));
|
|
263
|
+
this.el.btnDownloadDagEl?.addEventListener('click', () => this.executeDownload('dag'));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Enforces the mutual exclusion / greying-out rule:
|
|
268
|
+
* - If Single Item context is present: Single Item is selected; Cosmos and Galaxy are greyed out.
|
|
269
|
+
* - If Single Item context is NOT present: Single Item is greyed out; Cosmos and Galaxy are selectable.
|
|
270
|
+
*/
|
|
271
|
+
protected applyScopeRules(): void {
|
|
272
|
+
const hasSingleItem = !!this.treeState.singleItem;
|
|
273
|
+
|
|
274
|
+
if (hasSingleItem) {
|
|
275
|
+
// Force Single Item active
|
|
276
|
+
this.activeScope = CLONE_SCOPE_ITEM;
|
|
277
|
+
this.el.scopeItemRadioEl.checked = true;
|
|
278
|
+
|
|
279
|
+
// Grey out Cosmos and Galaxy
|
|
280
|
+
this.el.scopeCosmosRadioEl.disabled = true;
|
|
281
|
+
this.el.scopeGalaxyRadioEl.disabled = true;
|
|
282
|
+
this.el.scopeItemRadioEl.disabled = false;
|
|
283
|
+
|
|
284
|
+
this.el.scopeCosmosLabelEl.classList.add('disabled');
|
|
285
|
+
this.el.scopeGalaxyLabelEl.classList.add('disabled');
|
|
286
|
+
this.el.scopeItemLabelEl.classList.remove('disabled');
|
|
287
|
+
|
|
288
|
+
this.el.scopeCosmosLabelEl.classList.remove('selected');
|
|
289
|
+
this.el.scopeGalaxyLabelEl.classList.remove('selected');
|
|
290
|
+
this.el.scopeItemLabelEl.classList.add('selected');
|
|
291
|
+
|
|
292
|
+
this.el.targetTitleEl.textContent = this.treeState.singleItem!.path;
|
|
293
|
+
} else {
|
|
294
|
+
// No single item: Grey out Single Item
|
|
295
|
+
this.el.scopeItemRadioEl.disabled = true;
|
|
296
|
+
this.el.scopeItemLabelEl.classList.add('disabled');
|
|
297
|
+
this.el.scopeItemLabelEl.classList.remove('selected');
|
|
298
|
+
|
|
299
|
+
this.el.scopeCosmosRadioEl.disabled = false;
|
|
300
|
+
this.el.scopeGalaxyRadioEl.disabled = false;
|
|
301
|
+
this.el.scopeCosmosLabelEl.classList.remove('disabled');
|
|
302
|
+
this.el.scopeGalaxyLabelEl.classList.remove('disabled');
|
|
303
|
+
|
|
304
|
+
if (this.activeScope === CLONE_SCOPE_ITEM) {
|
|
305
|
+
this.activeScope = CLONE_SCOPE_COSMOS;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (this.activeScope === CLONE_SCOPE_COSMOS) {
|
|
309
|
+
this.el.scopeCosmosRadioEl.checked = true;
|
|
310
|
+
this.el.scopeCosmosLabelEl.classList.add('selected');
|
|
311
|
+
this.el.scopeGalaxyLabelEl.classList.remove('selected');
|
|
312
|
+
this.el.targetTitleEl.textContent = this.treeState.cosmosName;
|
|
313
|
+
} else {
|
|
314
|
+
this.el.scopeGalaxyRadioEl.checked = true;
|
|
315
|
+
this.el.scopeGalaxyLabelEl.classList.add('selected');
|
|
316
|
+
this.el.scopeCosmosLabelEl.classList.remove('selected');
|
|
317
|
+
this.el.targetTitleEl.textContent = `${this.treeState.cosmosName} (Galaxy Scope)`;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private setScope(scope: CloneScope): void {
|
|
323
|
+
this.activeScope = scope;
|
|
324
|
+
this.applyScopeRules();
|
|
325
|
+
this.renderTree();
|
|
326
|
+
this.updateCommandPreview();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private setModality(modality: CloneModality): void {
|
|
330
|
+
this.activeModality = modality;
|
|
331
|
+
|
|
332
|
+
this.el.tabCliEl.classList.toggle('active', modality === CLONE_MODALITY_CLI);
|
|
333
|
+
this.el.tabZipEl.classList.toggle('active', modality === CLONE_MODALITY_ZIP);
|
|
334
|
+
this.el.tabDagEl.classList.toggle('active', modality === CLONE_MODALITY_DAG);
|
|
335
|
+
|
|
336
|
+
this.el.panelCliEl.classList.toggle('active', modality === CLONE_MODALITY_CLI);
|
|
337
|
+
this.el.panelZipEl.classList.toggle('active', modality === CLONE_MODALITY_ZIP);
|
|
338
|
+
this.el.panelDagEl.classList.toggle('active', modality === CLONE_MODALITY_DAG);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Renders the tree hierarchy or single item details.
|
|
343
|
+
*/
|
|
344
|
+
protected renderTree(): void {
|
|
345
|
+
if (this.activeScope === CLONE_SCOPE_ITEM && this.treeState.singleItem) {
|
|
346
|
+
this.el.treeContainerEl.style.display = 'none';
|
|
347
|
+
this.el.singleItemContainerEl.style.display = 'block';
|
|
348
|
+
|
|
349
|
+
const item = this.treeState.singleItem;
|
|
350
|
+
const icon = item.isDir ? '📁' : '📄';
|
|
351
|
+
this.el.singleItemContainerEl.innerHTML = `
|
|
352
|
+
<div class="single-item-row" style="display: flex; align-items: center; justify-content: space-between; gap: 8px;">
|
|
353
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
354
|
+
<span style="font-size: 1.2rem;">${icon}</span>
|
|
355
|
+
<div>
|
|
356
|
+
<strong style="color: #f8fafc;">${item.path}</strong>
|
|
357
|
+
<div style="font-size: 0.75rem; color: #94a3b8; margin-top: 2px;">
|
|
358
|
+
${item.isDir ? 'Directory Subtree Workspace' : 'Isolated File Timeline Projection'}
|
|
359
|
+
${item.galaxyName ? `• Galaxy: ${item.galaxyName}` : ''}
|
|
360
|
+
${item.branchName ? `• Branch: ${item.branchName}` : ''}
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
<button type="button" id="btn-clone-modal-branch-item" class="btn-action" style="background: rgba(16, 185, 129, 0.15); border: 1px solid #10b981; color: #6ee7b7; padding: 4px 10px; border-radius: 5px; font-size: 0.78rem; cursor: pointer; white-space: nowrap;" title="Branch from this item (🌿+)">🌿+ Branch</button>
|
|
365
|
+
</div>
|
|
366
|
+
`;
|
|
367
|
+
this.bindSingleItemEvents(item);
|
|
368
|
+
this.el.selectionSummaryEl.textContent = 'Single item isolated projection';
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
this.el.singleItemContainerEl.style.display = 'none';
|
|
373
|
+
this.el.treeContainerEl.style.display = 'block';
|
|
374
|
+
|
|
375
|
+
let html = '';
|
|
376
|
+
|
|
377
|
+
// Cosmos Root Node
|
|
378
|
+
html += `
|
|
379
|
+
<div class="tree-node depth-0">
|
|
380
|
+
<input type="checkbox" id="chk-tree-cosmos" ${this.treeState.selected ? 'checked' : ''}>
|
|
381
|
+
<span class="node-icon">🌌</span>
|
|
382
|
+
<span>${this.treeState.cosmosName}</span>
|
|
383
|
+
</div>
|
|
384
|
+
`;
|
|
385
|
+
|
|
386
|
+
// Galaxies & Branches
|
|
387
|
+
this.treeState.galaxies.forEach((galaxy, gIdx) => {
|
|
388
|
+
html += `
|
|
389
|
+
<div class="tree-node depth-1">
|
|
390
|
+
<input type="checkbox" id="chk-tree-g-${gIdx}" ${galaxy.selected ? 'checked' : ''}>
|
|
391
|
+
<span class="node-icon">${galaxy.icon}</span>
|
|
392
|
+
<span>${galaxy.name}</span>
|
|
393
|
+
<span class="node-badge">${galaxy.branches.length} branches</span>
|
|
394
|
+
</div>
|
|
395
|
+
`;
|
|
396
|
+
|
|
397
|
+
galaxy.branches.forEach((branch, bIdx) => {
|
|
398
|
+
html += `
|
|
399
|
+
<div class="tree-node depth-2" style="justify-content: space-between;">
|
|
400
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
401
|
+
<input type="checkbox" id="chk-tree-g-${gIdx}-b-${bIdx}" ${branch.selected ? 'checked' : ''}>
|
|
402
|
+
<span class="node-icon">🌿</span>
|
|
403
|
+
<span>${branch.name}</span>
|
|
404
|
+
${branch.isCanonical ? '<span class="node-badge" style="background:#1e3a2f; color:#6ee7b7;">canonical</span>' : ''}
|
|
405
|
+
</div>
|
|
406
|
+
<button type="button" class="btn-tree-branch-action" data-branch="${branch.name}" data-galaxy="${galaxy.name}" title="Branch from ${branch.name} (🌿+)" style="background: transparent; border: none; color: #6ee7b7; cursor: pointer; font-size: 0.75rem; padding: 1px 6px; border-radius: 3px;">🌿+</button>
|
|
407
|
+
</div>
|
|
408
|
+
`;
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
this.el.treeContainerEl.innerHTML = html;
|
|
413
|
+
this.bindTreeEvents();
|
|
414
|
+
this.updateSelectionSummary();
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private bindSingleItemEvents(item: CloneItemContext): void {
|
|
418
|
+
const btnBranchItem = this.el.singleItemContainerEl.querySelector('#btn-clone-modal-branch-item');
|
|
419
|
+
btnBranchItem?.addEventListener('click', async () => {
|
|
420
|
+
const filename = item.path.split('/').pop() || item.path;
|
|
421
|
+
let candidateName = `feature-branch-${filename.replace(/[^a-zA-Z0-9_.-]/g, '-')}`;
|
|
422
|
+
let promptMsg = `Enter branch name isolating and diverging from item [${item.path}]:`;
|
|
423
|
+
let cleanBranchName = '';
|
|
424
|
+
|
|
425
|
+
while (true) {
|
|
426
|
+
const newBranchName = await promptForText({
|
|
427
|
+
title: `Create Branch from Item`,
|
|
428
|
+
msg: promptMsg,
|
|
429
|
+
defaultValue: candidateName,
|
|
430
|
+
cancelable: true,
|
|
431
|
+
});
|
|
432
|
+
if (!newBranchName || !newBranchName.trim()) return;
|
|
433
|
+
cleanBranchName = newBranchName.trim();
|
|
434
|
+
const validation = validateBranchName(cleanBranchName);
|
|
435
|
+
if (!validation.isValid) {
|
|
436
|
+
candidateName = cleanBranchName;
|
|
437
|
+
promptMsg = `Invalid branch name: ${validation.error}\n\nPlease enter a valid branch name:`;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
this.showStatus(`Branch created: ${cleanBranchName}`);
|
|
444
|
+
window.dispatchEvent(new CustomEvent('space-gib-branch-requested', {
|
|
445
|
+
detail: {
|
|
446
|
+
galaxyName: item.galaxyName || 'Code Galaxy',
|
|
447
|
+
sourceBranch: item.branchName || 'main',
|
|
448
|
+
itemPath: item.path,
|
|
449
|
+
isDir: item.isDir,
|
|
450
|
+
newBranchName: cleanBranchName,
|
|
451
|
+
scope: 'item',
|
|
452
|
+
},
|
|
453
|
+
bubbles: true,
|
|
454
|
+
composed: true,
|
|
455
|
+
}));
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
private bindTreeEvents(): void {
|
|
460
|
+
const rootChk = this.el.treeContainerEl.querySelector('#chk-tree-cosmos') as HTMLInputElement | null;
|
|
461
|
+
rootChk?.addEventListener('change', () => {
|
|
462
|
+
const checked = rootChk.checked;
|
|
463
|
+
this.treeState.selected = checked;
|
|
464
|
+
this.treeState.galaxies.forEach(g => {
|
|
465
|
+
g.selected = checked;
|
|
466
|
+
g.branches.forEach(b => b.selected = checked);
|
|
467
|
+
});
|
|
468
|
+
this.renderTree();
|
|
469
|
+
this.updateCommandPreview();
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
this.treeState.galaxies.forEach((galaxy, gIdx) => {
|
|
473
|
+
const gChk = this.el.treeContainerEl.querySelector(`#chk-tree-g-${gIdx}`) as HTMLInputElement | null;
|
|
474
|
+
gChk?.addEventListener('change', () => {
|
|
475
|
+
const checked = gChk.checked;
|
|
476
|
+
galaxy.selected = checked;
|
|
477
|
+
galaxy.branches.forEach(b => b.selected = checked);
|
|
478
|
+
this.syncParentState();
|
|
479
|
+
this.renderTree();
|
|
480
|
+
this.updateCommandPreview();
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
galaxy.branches.forEach((branch, bIdx) => {
|
|
484
|
+
const bChk = this.el.treeContainerEl.querySelector(`#chk-tree-g-${gIdx}-b-${bIdx}`) as HTMLInputElement | null;
|
|
485
|
+
bChk?.addEventListener('change', () => {
|
|
486
|
+
branch.selected = bChk.checked;
|
|
487
|
+
galaxy.selected = galaxy.branches.some(b => b.selected);
|
|
488
|
+
this.syncParentState();
|
|
489
|
+
this.renderTree();
|
|
490
|
+
this.updateCommandPreview();
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// Branch from branch buttons in tree
|
|
496
|
+
this.el.treeContainerEl.querySelectorAll('.btn-tree-branch-action').forEach(btn => {
|
|
497
|
+
btn.addEventListener('click', async (e) => {
|
|
498
|
+
e.stopPropagation();
|
|
499
|
+
const branchName = btn.getAttribute('data-branch') || 'main';
|
|
500
|
+
const galaxyName = btn.getAttribute('data-galaxy') || 'Code Galaxy';
|
|
501
|
+
let candidateName = `feature-branch-from-${branchName}`;
|
|
502
|
+
let promptMsg = `Enter branch name diverging from [${branchName}]:`;
|
|
503
|
+
let cleanBranchName = '';
|
|
504
|
+
|
|
505
|
+
while (true) {
|
|
506
|
+
const newBranchName = await promptForText({
|
|
507
|
+
title: 'Create Branch from Branch',
|
|
508
|
+
msg: promptMsg,
|
|
509
|
+
defaultValue: candidateName,
|
|
510
|
+
cancelable: true,
|
|
511
|
+
});
|
|
512
|
+
if (!newBranchName || !newBranchName.trim()) return;
|
|
513
|
+
cleanBranchName = newBranchName.trim();
|
|
514
|
+
const validation = validateBranchName(cleanBranchName);
|
|
515
|
+
if (!validation.isValid) {
|
|
516
|
+
candidateName = cleanBranchName;
|
|
517
|
+
promptMsg = `Invalid branch name: ${validation.error}\n\nPlease enter a valid branch name:`;
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
this.showStatus(`Branch created: ${cleanBranchName}`);
|
|
524
|
+
window.dispatchEvent(new CustomEvent('space-gib-branch-requested', {
|
|
525
|
+
detail: {
|
|
526
|
+
galaxyName,
|
|
527
|
+
sourceBranch: branchName,
|
|
528
|
+
newBranchName: cleanBranchName,
|
|
529
|
+
scope: 'branch',
|
|
530
|
+
},
|
|
531
|
+
bubbles: true,
|
|
532
|
+
composed: true,
|
|
533
|
+
}));
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
private syncParentState(): void {
|
|
539
|
+
this.treeState.selected = this.treeState.galaxies.some(g => g.selected);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
private updateSelectionSummary(): void {
|
|
543
|
+
let totalBranches = 0;
|
|
544
|
+
let selectedBranches = 0;
|
|
545
|
+
this.treeState.galaxies.forEach(g => {
|
|
546
|
+
totalBranches += g.branches.length;
|
|
547
|
+
selectedBranches += g.branches.filter(b => b.selected).length;
|
|
548
|
+
});
|
|
549
|
+
this.el.selectionSummaryEl.textContent = `${selectedBranches} of ${totalBranches} branches selected`;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Generates the dynamic CLI command string.
|
|
554
|
+
*/
|
|
555
|
+
protected updateCommandPreview(): void {
|
|
556
|
+
const host = window.location.host || 'space-gib.localhost';
|
|
557
|
+
const protocol = window.location.protocol === 'http:' ? 'http' : 'https';
|
|
558
|
+
const cosmosPath = this.treeState.cosmosId;
|
|
559
|
+
const baseUrl = `${protocol}://${host}/${cosmosPath}`;
|
|
560
|
+
|
|
561
|
+
let cmd = `$ vcs clone ${baseUrl}`;
|
|
562
|
+
|
|
563
|
+
if (this.activeScope === CLONE_SCOPE_ITEM && this.treeState.singleItem) {
|
|
564
|
+
cmd += ` \\\n --item "${this.treeState.singleItem.path}"`;
|
|
565
|
+
} else {
|
|
566
|
+
const selectedFlags: string[] = [];
|
|
567
|
+
this.treeState.galaxies.forEach(g => {
|
|
568
|
+
const selectedBranches = g.branches.filter(b => b.selected);
|
|
569
|
+
selectedBranches.forEach(b => {
|
|
570
|
+
selectedFlags.push(`--galaxy ${g.type}:${b.name}`);
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
if (selectedFlags.length > 0) {
|
|
575
|
+
cmd += ` \\\n ${selectedFlags.join(' ')}`;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (this.el.cmdPreviewEl) {
|
|
580
|
+
this.el.cmdPreviewEl.querySelector('code')!.textContent = cmd;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
private async copyCommandToClipboard(): Promise<void> {
|
|
585
|
+
const code = this.el.cmdPreviewEl.querySelector('code')?.textContent?.replace(/^\$\s*/, '') || '';
|
|
586
|
+
try {
|
|
587
|
+
await navigator.clipboard.writeText(code);
|
|
588
|
+
this.el.btnCopyEl.textContent = '✓ Copied!';
|
|
589
|
+
this.el.btnCopyEl.classList.add('copied');
|
|
590
|
+
this.showStatus('Command copied to clipboard!');
|
|
591
|
+
setTimeout(() => {
|
|
592
|
+
if (this.el.btnCopyEl) {
|
|
593
|
+
this.el.btnCopyEl.textContent = '📋 Copy';
|
|
594
|
+
this.el.btnCopyEl.classList.remove('copied');
|
|
595
|
+
}
|
|
596
|
+
}, 2000);
|
|
597
|
+
} catch (err) {
|
|
598
|
+
console.error('Failed to copy to clipboard:', err);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
private executeDownload(type: 'zip' | 'dag'): void {
|
|
603
|
+
const desc = type === 'zip' ? 'Workspace ZIP archive' : 'ibGib DAG bundle';
|
|
604
|
+
this.showStatus(`Preparing ${desc} download...`);
|
|
605
|
+
|
|
606
|
+
this.dispatchEvent(new CustomEvent(EVENT_SPACE_GIB_CLONE_EXECUTED, {
|
|
607
|
+
bubbles: true,
|
|
608
|
+
composed: true,
|
|
609
|
+
detail: {
|
|
610
|
+
scope: this.activeScope,
|
|
611
|
+
type,
|
|
612
|
+
treeState: this.treeState,
|
|
613
|
+
}
|
|
614
|
+
}));
|
|
615
|
+
|
|
616
|
+
setTimeout(() => {
|
|
617
|
+
this.showStatus(`Download started: ${this.treeState.cosmosName}-${type}.zip`);
|
|
618
|
+
}, 800);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
private showStatus(msg: string): void {
|
|
622
|
+
if (!this.el.statusMessageEl) return;
|
|
623
|
+
this.el.statusMessageEl.textContent = msg;
|
|
624
|
+
this.el.statusMessageEl.classList.add('visible');
|
|
625
|
+
setTimeout(() => {
|
|
626
|
+
if (this.el.statusMessageEl) {
|
|
627
|
+
this.el.statusMessageEl.classList.remove('visible');
|
|
628
|
+
}
|
|
629
|
+
}, 3000);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
public close(): void {
|
|
633
|
+
this.dispatchEvent(new CustomEvent(EVENT_SPACE_GIB_CLONE_CLOSED, {
|
|
634
|
+
bubbles: true,
|
|
635
|
+
composed: true,
|
|
636
|
+
}));
|
|
637
|
+
this.remove();
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
override async disconnected(): Promise<void> {
|
|
641
|
+
const lc = `${this.lc}[${this.disconnected.name}]`;
|
|
642
|
+
try {
|
|
643
|
+
// cleanup if needed
|
|
644
|
+
} catch (error) {
|
|
645
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getComponentSvc } from "@ibgib/web-gib/dist/ui/component/ibgib-component-service.mjs";
|
|
2
|
+
import { CloneComponentMeta } from "./clone.mjs";
|
|
3
|
+
|
|
4
|
+
export * from "./clone.mjs";
|
|
5
|
+
export * from "./clone-constants.mjs";
|
|
6
|
+
export * from "./clone-types.mjs";
|
|
7
|
+
|
|
8
|
+
export async function registerCloneComponent(): Promise<void> {
|
|
9
|
+
const componentSvc = await getComponentSvc();
|
|
10
|
+
componentSvc.registerComponentMeta(new CloneComponentMeta());
|
|
11
|
+
}
|