@ibgib/space-gib 0.0.32 → 0.0.34
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-25PXC4HP.mjs +9914 -0
- package/dist/client/chunk-RXWLL2AI.mjs +30 -0
- package/dist/client/css/activity-bar.css +417 -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 +896 -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 +202 -31
- package/dist/client/index.mjs +31 -31
- 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 +6879 -3109
- package/package.json +6 -6
- package/src/client/AUTO-GENERATED-version.mts +1 -1
- package/src/client/cosmos/seed-cosmos-workspace.mts +69 -0
- package/src/client/css/activity-bar.css +417 -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 +896 -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 +3 -3
- package/src/client/index.html +202 -31
- package/src/client/style.css +11 -1056
- 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 +104 -0
- package/src/client/ui/component/chat-view/chat-view.mts +277 -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 +620 -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 +275 -0
- package/src/client/ui/component/code-editor/code-editor.html +54 -0
- package/src/client/ui/component/code-editor/code-editor.mts +427 -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 +97 -0
- package/src/client/ui/component/cosmos-navigator/cosmos-navigator.mts +1084 -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 +256 -0
- package/src/client/ui/component/file-explorer/file-explorer.html +75 -0
- package/src/client/ui/component/file-explorer/file-explorer.mts +346 -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 +646 -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 +393 -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 +264 -0
- package/src/client/ui/component/timeline/timeline.html +23 -0
- package/src/client/ui/component/timeline/timeline.mts +317 -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 +272 -0
- package/src/client/ui/router/space-gib-router-helpers.respec.mts +353 -0
- package/src/client/ui/router/space-gib-router-service.mts +186 -0
- package/src/client/ui/router/space-gib-router-types.mts +93 -0
- package/src/client/ui/shell/cosmic-big-bang.mts +844 -0
- package/src/client/ui/shell/space-gib-shell-constants.mts +7 -0
- package/src/client/ui/shell/space-gib-shell-service.mts +1728 -31
- package/src/server/server.mts +20 -1
- package/tsconfig.test.json +5 -1
- package/dist/client/chunk-AX6BROMT.mjs +0 -1
- package/dist/client/chunk-CIQUGV7U.mjs +0 -25
- package/dist/client/chunk-YPCHYDKL.mjs +0 -3102
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import thisCss from "./code-editor.css";
|
|
2
|
+
import thisHtml from "./code-editor.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 {
|
|
9
|
+
IbGibDynamicComponentMetaBase, IbGibDynamicComponentInstanceBase,
|
|
10
|
+
} from "@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs";
|
|
11
|
+
import {
|
|
12
|
+
ElementsBase, IbGibDynamicComponentInstance,
|
|
13
|
+
} from "@ibgib/web-gib/dist/ui/component/component-types.mjs";
|
|
14
|
+
import { getComponentCtorArg } from "@ibgib/web-gib/dist/app-bootstrap/init-orchestration.mjs";
|
|
15
|
+
|
|
16
|
+
export const CODE_EDITOR_COMPONENT_NAME = 'space-gib-code-editor';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Metadata factory for space-gib-code-editor Web Component.
|
|
20
|
+
*/
|
|
21
|
+
export class CodeEditorComponentMeta extends IbGibDynamicComponentMetaBase {
|
|
22
|
+
protected lc: string = `[${CodeEditorComponentMeta.name}]`;
|
|
23
|
+
|
|
24
|
+
routeRegExp?: RegExp = new RegExp(`^${CODE_EDITOR_COMPONENT_NAME}$`);
|
|
25
|
+
componentName = CODE_EDITOR_COMPONENT_NAME;
|
|
26
|
+
|
|
27
|
+
constructor() {
|
|
28
|
+
super(getComponentCtorArg());
|
|
29
|
+
if (!customElements.get(this.componentName)) {
|
|
30
|
+
customElements.define(this.componentName, CodeEditorComponentInstance);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async createInstance({
|
|
35
|
+
path,
|
|
36
|
+
ibGibAddr
|
|
37
|
+
}: {
|
|
38
|
+
path: string;
|
|
39
|
+
ibGibAddr: IbGibAddr;
|
|
40
|
+
}): Promise<IbGibDynamicComponentInstance> {
|
|
41
|
+
const component = document.createElement(this.componentName) as CodeEditorComponentInstance;
|
|
42
|
+
await component.initialize({
|
|
43
|
+
ibGibAddr: ibGibAddr || ROOT_ADDR,
|
|
44
|
+
meta: this,
|
|
45
|
+
html: thisHtml,
|
|
46
|
+
css: [thisCss],
|
|
47
|
+
});
|
|
48
|
+
return component;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* References to DOM elements within the code editor Shadow Root.
|
|
54
|
+
*/
|
|
55
|
+
export interface CodeEditorElements extends ElementsBase {
|
|
56
|
+
rootEl: HTMLDivElement;
|
|
57
|
+
headerEl: HTMLElement;
|
|
58
|
+
crumbRepoEl: HTMLSpanElement;
|
|
59
|
+
crumbPathEl: HTMLSpanElement;
|
|
60
|
+
crumbBranchEl: HTMLSpanElement;
|
|
61
|
+
crumbTipEl: HTMLSpanElement;
|
|
62
|
+
crumbTipContainerEl: HTMLDivElement;
|
|
63
|
+
statLangEl: HTMLSpanElement;
|
|
64
|
+
statLinesEl: HTMLSpanElement;
|
|
65
|
+
statSizeEl: HTMLSpanElement;
|
|
66
|
+
btnToggleReadonlyEl: HTMLButtonElement;
|
|
67
|
+
modeIconEl: HTMLSpanElement;
|
|
68
|
+
modeTextEl: HTMLSpanElement;
|
|
69
|
+
btnCopyCodeEl: HTMLButtonElement;
|
|
70
|
+
editorGutterEl: HTMLDivElement;
|
|
71
|
+
editorTextareaEl: HTMLTextAreaElement;
|
|
72
|
+
contentEl: HTMLDivElement;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const DEFAULT_CODE = `/**
|
|
76
|
+
* @module my-app/index
|
|
77
|
+
* Application entry point running on ibgib VCS.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
import { getGlobalMetaspace } from '@ibgib/web-gib';
|
|
81
|
+
import { add } from './helper.mjs';
|
|
82
|
+
|
|
83
|
+
export const APP_NAME = 'space-gib in-memory vcs';
|
|
84
|
+
|
|
85
|
+
export async function boot(): Promise<void> {
|
|
86
|
+
console.log(\`Initializing \${APP_NAME}...\`);
|
|
87
|
+
const space = await getGlobalMetaspace();
|
|
88
|
+
const sum = add(40, 2);
|
|
89
|
+
console.log(\`Calculated sum: \${sum}\`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
boot();`;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Concrete implementation of the space-gib-code-editor component.
|
|
96
|
+
*/
|
|
97
|
+
export class CodeEditorComponentInstance
|
|
98
|
+
extends IbGibDynamicComponentInstanceBase<IbGib_V1, CodeEditorElements>
|
|
99
|
+
implements IbGibDynamicComponentInstance<IbGib_V1, CodeEditorElements> {
|
|
100
|
+
|
|
101
|
+
protected lc: string = `[${CodeEditorComponentInstance.name}]`;
|
|
102
|
+
|
|
103
|
+
private _readOnly: boolean = false;
|
|
104
|
+
private filepath: string = 'src/index.ts';
|
|
105
|
+
private repo: string = 'my-app';
|
|
106
|
+
private branch: string = 'main';
|
|
107
|
+
private tipAddr: string = '7f8a1b2';
|
|
108
|
+
private language: string = 'TypeScript';
|
|
109
|
+
|
|
110
|
+
get el(): CodeEditorElements {
|
|
111
|
+
if (!this.elements) {
|
|
112
|
+
throw new Error(`elements is not defined in ${this.lc}`);
|
|
113
|
+
}
|
|
114
|
+
return this.elements;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Read-only mode flag. When true, the editor acts as a protected code viewer.
|
|
119
|
+
*/
|
|
120
|
+
get readOnly(): boolean {
|
|
121
|
+
return this._readOnly;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
set readOnly(val: boolean) {
|
|
125
|
+
this._readOnly = Boolean(val);
|
|
126
|
+
this.updateReadOnlyState();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Alias for readOnly */
|
|
130
|
+
get readonly(): boolean {
|
|
131
|
+
return this.readOnly;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
set readonly(val: boolean) {
|
|
135
|
+
this.readOnly = val;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
static get observedAttributes(): string[] {
|
|
139
|
+
return ['readonly'];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
override async attributeChangedCallback(name: string, oldValue: any, newValue: any): Promise<void> {
|
|
143
|
+
await super.attributeChangedCallback(name, oldValue, newValue);
|
|
144
|
+
if (name === 'readonly') {
|
|
145
|
+
this.readOnly = newValue !== null && newValue !== 'false';
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
override async created(): Promise<void> {
|
|
150
|
+
const lc = `${this.lc}[${this.created.name}]`;
|
|
151
|
+
try {
|
|
152
|
+
const root = this.shadowRoot!;
|
|
153
|
+
this.elements = {
|
|
154
|
+
rootEl: root.getElementById('editor-root') as HTMLDivElement,
|
|
155
|
+
headerEl: root.getElementById('editor-header') as HTMLElement,
|
|
156
|
+
crumbRepoEl: root.getElementById('crumb-repo') as HTMLSpanElement,
|
|
157
|
+
crumbPathEl: root.getElementById('crumb-path') as HTMLSpanElement,
|
|
158
|
+
crumbBranchEl: root.getElementById('crumb-branch') as HTMLSpanElement,
|
|
159
|
+
crumbTipEl: root.getElementById('crumb-tip') as HTMLSpanElement,
|
|
160
|
+
crumbTipContainerEl: root.getElementById('crumb-tip-container') as HTMLDivElement,
|
|
161
|
+
statLangEl: root.getElementById('stat-lang') as HTMLSpanElement,
|
|
162
|
+
statLinesEl: root.getElementById('stat-lines') as HTMLSpanElement,
|
|
163
|
+
statSizeEl: root.getElementById('stat-size') as HTMLSpanElement,
|
|
164
|
+
btnToggleReadonlyEl: root.getElementById('btn-toggle-readonly') as HTMLButtonElement,
|
|
165
|
+
modeIconEl: root.getElementById('mode-icon') as HTMLSpanElement,
|
|
166
|
+
modeTextEl: root.getElementById('mode-text') as HTMLSpanElement,
|
|
167
|
+
btnCopyCodeEl: root.getElementById('btn-copy-code') as HTMLButtonElement,
|
|
168
|
+
editorGutterEl: root.getElementById('editor-gutter') as HTMLDivElement,
|
|
169
|
+
editorTextareaEl: root.getElementById('editor-textarea') as HTMLTextAreaElement,
|
|
170
|
+
contentEl: root.getElementById('editor-root') as HTMLDivElement,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Set initial content if empty
|
|
174
|
+
if (this.el.editorTextareaEl && !this.el.editorTextareaEl.value) {
|
|
175
|
+
this.el.editorTextareaEl.value = DEFAULT_CODE;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Sync readonly attribute if set on host
|
|
179
|
+
if (this.hasAttribute('readonly')) {
|
|
180
|
+
this._readOnly = true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
this.updateReadOnlyState();
|
|
184
|
+
this.updateGutter();
|
|
185
|
+
this.updateStats();
|
|
186
|
+
this.bindEvents();
|
|
187
|
+
|
|
188
|
+
} catch (error) {
|
|
189
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private bindEvents(): void {
|
|
194
|
+
const lc = `${this.lc}[${this.bindEvents.name}]`;
|
|
195
|
+
if (!this.el) return;
|
|
196
|
+
|
|
197
|
+
// Textarea input: update line numbers & stats
|
|
198
|
+
this.el.editorTextareaEl?.addEventListener('input', () => {
|
|
199
|
+
this.updateGutter();
|
|
200
|
+
this.updateStats();
|
|
201
|
+
|
|
202
|
+
this.dispatchEvent(new CustomEvent('space-gib-code-changed', {
|
|
203
|
+
bubbles: true,
|
|
204
|
+
composed: true,
|
|
205
|
+
detail: {
|
|
206
|
+
filepath: this.filepath,
|
|
207
|
+
content: this.el.editorTextareaEl.value,
|
|
208
|
+
lineCount: this.getLineCount(),
|
|
209
|
+
}
|
|
210
|
+
}));
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Synchronize scroll between textarea and line number gutter
|
|
214
|
+
this.el.editorTextareaEl?.addEventListener('scroll', () => {
|
|
215
|
+
if (this.el.editorGutterEl && this.el.editorTextareaEl) {
|
|
216
|
+
this.el.editorGutterEl.scrollTop = this.el.editorTextareaEl.scrollTop;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Handle Tab key in textarea for code editing
|
|
221
|
+
this.el.editorTextareaEl?.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
222
|
+
if (this._readOnly) return;
|
|
223
|
+
|
|
224
|
+
if (e.key === 'Tab') {
|
|
225
|
+
e.preventDefault();
|
|
226
|
+
const textarea = this.el.editorTextareaEl;
|
|
227
|
+
const start = textarea.selectionStart;
|
|
228
|
+
const end = textarea.selectionEnd;
|
|
229
|
+
|
|
230
|
+
// Insert 4 spaces
|
|
231
|
+
textarea.value = textarea.value.substring(0, start) + ' ' + textarea.value.substring(end);
|
|
232
|
+
textarea.selectionStart = textarea.selectionEnd = start + 4;
|
|
233
|
+
|
|
234
|
+
this.updateGutter();
|
|
235
|
+
this.updateStats();
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Toggle Read-Only / Editable Mode
|
|
240
|
+
this.el.btnToggleReadonlyEl?.addEventListener('click', () => {
|
|
241
|
+
this.readOnly = !this.readOnly;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Copy Code Button
|
|
245
|
+
this.el.btnCopyCodeEl?.addEventListener('click', async () => {
|
|
246
|
+
const code = this.getContent();
|
|
247
|
+
try {
|
|
248
|
+
if (navigator.clipboard) {
|
|
249
|
+
await navigator.clipboard.writeText(code);
|
|
250
|
+
} else {
|
|
251
|
+
// Fallback
|
|
252
|
+
const ta = document.createElement('textarea');
|
|
253
|
+
ta.value = code;
|
|
254
|
+
document.body.appendChild(ta);
|
|
255
|
+
ta.select();
|
|
256
|
+
document.execCommand('copy');
|
|
257
|
+
document.body.removeChild(ta);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (this.el.btnCopyCodeEl) {
|
|
261
|
+
const originalHtml = this.el.btnCopyCodeEl.innerHTML;
|
|
262
|
+
this.el.btnCopyCodeEl.innerHTML = '<span class="btn-icon">✓</span>';
|
|
263
|
+
setTimeout(() => {
|
|
264
|
+
if (this.el?.btnCopyCodeEl) {
|
|
265
|
+
this.el.btnCopyCodeEl.innerHTML = originalHtml;
|
|
266
|
+
}
|
|
267
|
+
}, 1500);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
this.dispatchEvent(new CustomEvent('space-gib-code-copied', {
|
|
271
|
+
bubbles: true,
|
|
272
|
+
composed: true,
|
|
273
|
+
detail: { filepath: this.filepath }
|
|
274
|
+
}));
|
|
275
|
+
} catch (err) {
|
|
276
|
+
console.warn(`${lc} Failed to copy code: ${extractErrorMsg(err)}`);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private updateReadOnlyState(): void {
|
|
282
|
+
if (!this.elements) return;
|
|
283
|
+
|
|
284
|
+
if (this.el.editorTextareaEl) {
|
|
285
|
+
this.el.editorTextareaEl.readOnly = this._readOnly;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (this.el.rootEl) {
|
|
289
|
+
this.el.rootEl.classList.toggle('readonly-mode', this._readOnly);
|
|
290
|
+
this.el.rootEl.classList.toggle('editable-mode', !this._readOnly);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (this.el.btnToggleReadonlyEl) {
|
|
294
|
+
this.el.btnToggleReadonlyEl.classList.toggle('readonly', this._readOnly);
|
|
295
|
+
this.el.btnToggleReadonlyEl.classList.toggle('editable', !this._readOnly);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (this.el.modeIconEl) {
|
|
299
|
+
this.el.modeIconEl.textContent = this._readOnly ? '🔒' : '✏️';
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (this.el.modeTextEl) {
|
|
303
|
+
this.el.modeTextEl.textContent = this._readOnly ? 'Read-Only' : 'Editable';
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private getLineCount(): number {
|
|
308
|
+
const text = this.el?.editorTextareaEl?.value || '';
|
|
309
|
+
return text.split('\n').length;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private updateGutter(): void {
|
|
313
|
+
if (!this.el?.editorGutterEl) return;
|
|
314
|
+
const lineCount = this.getLineCount();
|
|
315
|
+
const linesHtml = Array.from({ length: lineCount }, (_, i) => `<span class="line-num">${i + 1}</span>`).join('');
|
|
316
|
+
this.el.editorGutterEl.innerHTML = linesHtml;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private updateStats(): void {
|
|
320
|
+
if (!this.elements) return;
|
|
321
|
+
const text = this.el.editorTextareaEl?.value || '';
|
|
322
|
+
const lines = text.split('\n').length;
|
|
323
|
+
const bytes = new Blob([text]).size;
|
|
324
|
+
|
|
325
|
+
if (this.el.statLinesEl) {
|
|
326
|
+
this.el.statLinesEl.textContent = `${lines} line${lines === 1 ? '' : 's'}`;
|
|
327
|
+
}
|
|
328
|
+
if (this.el.statSizeEl) {
|
|
329
|
+
this.el.statSizeEl.textContent = bytes > 1024 ? `${(bytes / 1024).toFixed(1)} KB` : `${bytes} B`;
|
|
330
|
+
}
|
|
331
|
+
if (this.el.statLangEl) {
|
|
332
|
+
this.el.statLangEl.textContent = this.language;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Loads a file into the editor, updating breadcrumbs, content, branch tip, and stats.
|
|
338
|
+
*/
|
|
339
|
+
public loadFile(opts: {
|
|
340
|
+
filepath: string;
|
|
341
|
+
content?: string;
|
|
342
|
+
branch?: string;
|
|
343
|
+
tipAddr?: string;
|
|
344
|
+
language?: string;
|
|
345
|
+
repo?: string;
|
|
346
|
+
readOnly?: boolean;
|
|
347
|
+
}): void {
|
|
348
|
+
this.filepath = opts.filepath || this.filepath;
|
|
349
|
+
if (opts.repo) this.repo = opts.repo;
|
|
350
|
+
if (opts.branch) this.branch = opts.branch;
|
|
351
|
+
if (opts.tipAddr) this.tipAddr = opts.tipAddr;
|
|
352
|
+
|
|
353
|
+
// Auto-detect language from extension if not provided
|
|
354
|
+
if (opts.language) {
|
|
355
|
+
this.language = opts.language;
|
|
356
|
+
} else {
|
|
357
|
+
const ext = this.filepath.split('.').pop()?.toLowerCase();
|
|
358
|
+
switch (ext) {
|
|
359
|
+
case 'ts':
|
|
360
|
+
case 'mts':
|
|
361
|
+
this.language = 'TypeScript';
|
|
362
|
+
break;
|
|
363
|
+
case 'js':
|
|
364
|
+
case 'mjs':
|
|
365
|
+
this.language = 'JavaScript';
|
|
366
|
+
break;
|
|
367
|
+
case 'html':
|
|
368
|
+
this.language = 'HTML';
|
|
369
|
+
break;
|
|
370
|
+
case 'css':
|
|
371
|
+
this.language = 'CSS';
|
|
372
|
+
break;
|
|
373
|
+
case 'json':
|
|
374
|
+
this.language = 'JSON';
|
|
375
|
+
break;
|
|
376
|
+
case 'md':
|
|
377
|
+
this.language = 'Markdown';
|
|
378
|
+
break;
|
|
379
|
+
default:
|
|
380
|
+
this.language = 'Plain Text';
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (opts.readOnly !== undefined) {
|
|
385
|
+
this.readOnly = opts.readOnly;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (this.elements) {
|
|
389
|
+
if (this.el.crumbRepoEl) this.el.crumbRepoEl.textContent = this.repo;
|
|
390
|
+
if (this.el.crumbPathEl) this.el.crumbPathEl.textContent = this.filepath;
|
|
391
|
+
if (this.el.crumbBranchEl) this.el.crumbBranchEl.textContent = this.branch;
|
|
392
|
+
if (this.el.crumbTipEl) this.el.crumbTipEl.textContent = this.tipAddr;
|
|
393
|
+
|
|
394
|
+
if (opts.content !== undefined && this.el.editorTextareaEl) {
|
|
395
|
+
this.el.editorTextareaEl.value = opts.content;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
this.updateGutter();
|
|
399
|
+
this.updateStats();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
public getContent(): string {
|
|
404
|
+
return this.el?.editorTextareaEl?.value || '';
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
public setContent(content: string): void {
|
|
408
|
+
if (this.el?.editorTextareaEl) {
|
|
409
|
+
this.el.editorTextareaEl.value = content;
|
|
410
|
+
this.updateGutter();
|
|
411
|
+
this.updateStats();
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
public setReadOnly(readOnly: boolean): void {
|
|
416
|
+
this.readOnly = readOnly;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
override async disconnected(): Promise<void> {
|
|
420
|
+
const lc = `${this.lc}[${this.disconnected.name}]`;
|
|
421
|
+
try {
|
|
422
|
+
// cleanup if needed
|
|
423
|
+
} catch (error) {
|
|
424
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getComponentSvc } from "@ibgib/web-gib/dist/ui/component/ibgib-component-service.mjs";
|
|
2
|
+
import { CodeEditorComponentMeta } from "./code-editor.mjs";
|
|
3
|
+
|
|
4
|
+
export * from "./code-editor.mjs";
|
|
5
|
+
|
|
6
|
+
export async function registerCodeEditorComponent(): Promise<void> {
|
|
7
|
+
const componentSvc = await getComponentSvc();
|
|
8
|
+
componentSvc.registerComponentMeta(new CodeEditorComponentMeta());
|
|
9
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# space-gib-cosmos-navigator Implementation Specification
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
`space-gib-cosmos-navigator` is a custom Web Component built on top of the `@ibgib/web-gib` dynamic component microframework. It acts as the core cosmological navigation interface for `space-gib`, presenting both the overarching **Polycosmos** (multi-project) zoom state and the active **Cosmos** (project) zoom state within a single unified component.
|
|
5
|
+
|
|
6
|
+
## Architecture & Lifecycles
|
|
7
|
+
- **Tag Name**: `space-gib-cosmos-navigator`
|
|
8
|
+
- **Component Meta**: `CosmosNavigatorComponentMeta` (extends `IbGibDynamicComponentMetaBase`)
|
|
9
|
+
- **Component Instance**: `CosmosNavigatorComponentInstance` (extends `IbGibDynamicComponentInstanceBase`)
|
|
10
|
+
- **Backing ibGib**: Initialized with `ROOT_ADDR` (virtual root genesis).
|
|
11
|
+
- **Shadow DOM**: Uses open Shadow DOM. Encapsulates the starfield particle canvas, the central Big Bang singularity, orbiting cosmos nodes, and active cosmos cluster satellites.
|
|
12
|
+
- **Dynamic Injection**: Injected by `SpaceGibShellService` into host `<div id="cosmos-navigator-container"></div>` via `IbGibComponentService`.
|
|
13
|
+
|
|
14
|
+
## State & Zoom Continuum
|
|
15
|
+
1. **Empty Primordial State (0 Cosmoses)**:
|
|
16
|
+
- Central Big Bang singularity is visible.
|
|
17
|
+
- Clicking it triggers the Big Bang explosion and creates Cosmos 1.
|
|
18
|
+
2. **Polycosmos State (Zoomed Out)**:
|
|
19
|
+
- Central Big Bang singularity acts as the "+ New Cosmos" trigger.
|
|
20
|
+
- Existing cosmoses orbit around it as glowing galaxy nodes with space count badges.
|
|
21
|
+
- Clicking an orbiting cosmos calls `selectCosmos(id)` to zoom in.
|
|
22
|
+
- Clicking the central Big Bang creates a new Cosmos (orbiting nodes disperse, explosion fires, new Cosmos N is born).
|
|
23
|
+
3. **Cosmos State (Zoomed In)**:
|
|
24
|
+
- Focuses on the selected active cosmos.
|
|
25
|
+
- Central Superspaces galaxy node unfolds its satellites (Code Space `</>`, Text Space `📝`).
|
|
26
|
+
- Clicking a space triggers the app launch animation sequence and adds it to the Activity Bar rail.
|
|
27
|
+
|
|
28
|
+
## Custom Events Emitted (Composed & Bubbling)
|
|
29
|
+
- `space-gib-cosmos-selected`: Emitted when a cosmos is selected (detail: `{ cosmosId, name, spaces }`).
|
|
30
|
+
- `space-gib-space-added`: Emitted when a space (code/text) is added to the active cosmos (detail: `{ cosmosId, space }`).
|
|
31
|
+
- `space-gib-polycosmos-activated`: Emitted when navigating out to the Polycosmos level.
|