@ibgib/space-gib 0.0.33 → 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.
Files changed (112) hide show
  1. package/dist/client/bootstrap.mjs +24 -51
  2. package/dist/client/chunk-25PXC4HP.mjs +9914 -0
  3. package/dist/client/chunk-RXWLL2AI.mjs +30 -0
  4. package/dist/client/css/activity-bar.css +417 -0
  5. package/dist/client/css/base.css +167 -0
  6. package/dist/client/css/components.css +218 -0
  7. package/dist/client/css/cosmic.css +557 -0
  8. package/dist/client/css/dev-tools.css +163 -0
  9. package/dist/client/css/landing.css +184 -0
  10. package/dist/client/css/layout.css +896 -0
  11. package/dist/client/css/onboarding.css +181 -0
  12. package/dist/client/css/repos.css +740 -0
  13. package/dist/client/css/variables.css +64 -0
  14. package/dist/client/index.html +202 -31
  15. package/dist/client/index.mjs +32 -32
  16. package/dist/client/script.mjs +1 -1
  17. package/dist/client/style.css +11 -1056
  18. package/dist/respec-gib.node.mjs +5 -0
  19. package/dist/server/server.mjs +6879 -3109
  20. package/package.json +6 -6
  21. package/src/client/AUTO-GENERATED-version.mts +1 -1
  22. package/src/client/cosmos/seed-cosmos-workspace.mts +69 -0
  23. package/src/client/css/activity-bar.css +417 -0
  24. package/src/client/css/base.css +167 -0
  25. package/src/client/css/components.css +218 -0
  26. package/src/client/css/cosmic.css +557 -0
  27. package/src/client/css/dev-tools.css +163 -0
  28. package/src/client/css/landing.css +184 -0
  29. package/src/client/css/layout.css +896 -0
  30. package/src/client/css/onboarding.css +181 -0
  31. package/src/client/css/repos.css +740 -0
  32. package/src/client/css/variables.css +64 -0
  33. package/src/client/dev-tools/vcs-workspace.mts +3 -3
  34. package/src/client/index.html +202 -31
  35. package/src/client/style.css +11 -1056
  36. package/src/client/ui/component/chat-view/IMPLEMENTATION.md +48 -0
  37. package/src/client/ui/component/chat-view/chat-view.css +381 -0
  38. package/src/client/ui/component/chat-view/chat-view.html +104 -0
  39. package/src/client/ui/component/chat-view/chat-view.mts +277 -0
  40. package/src/client/ui/component/chat-view/index.mts +9 -0
  41. package/src/client/ui/component/clone/IMPLEMENTATION.md +68 -0
  42. package/src/client/ui/component/clone/clone-constants.mts +16 -0
  43. package/src/client/ui/component/clone/clone-types.mts +60 -0
  44. package/src/client/ui/component/clone/clone.css +383 -0
  45. package/src/client/ui/component/clone/clone.html +89 -0
  46. package/src/client/ui/component/clone/clone.mts +620 -0
  47. package/src/client/ui/component/clone/index.mts +11 -0
  48. package/src/client/ui/component/code-editor/IMPLEMENTATION.md +89 -0
  49. package/src/client/ui/component/code-editor/code-editor.css +275 -0
  50. package/src/client/ui/component/code-editor/code-editor.html +54 -0
  51. package/src/client/ui/component/code-editor/code-editor.mts +427 -0
  52. package/src/client/ui/component/code-editor/index.mts +9 -0
  53. package/src/client/ui/component/cosmos-navigator/IMPLEMENTATION.md +31 -0
  54. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.css +864 -0
  55. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.html +97 -0
  56. package/src/client/ui/component/cosmos-navigator/cosmos-navigator.mts +1084 -0
  57. package/src/client/ui/component/cosmos-navigator/index.mts +18 -0
  58. package/src/client/ui/component/file-explorer/IMPLEMENTATION.md +42 -0
  59. package/src/client/ui/component/file-explorer/file-explorer.css +256 -0
  60. package/src/client/ui/component/file-explorer/file-explorer.html +75 -0
  61. package/src/client/ui/component/file-explorer/file-explorer.mts +346 -0
  62. package/src/client/ui/component/file-explorer/index.mts +9 -0
  63. package/src/client/ui/component/nested-chat/IMPLEMENTATION.md +18 -0
  64. package/src/client/ui/component/nested-chat/chat/IMPLEMENTATION.md +14 -0
  65. package/src/client/ui/component/nested-chat/chat/chat-constants.mts +14 -0
  66. package/src/client/ui/component/nested-chat/chat/chat-helpers.mts +66 -0
  67. package/src/client/ui/component/nested-chat/chat/chat.css +654 -0
  68. package/src/client/ui/component/nested-chat/chat/chat.html +90 -0
  69. package/src/client/ui/component/nested-chat/chat/chat.mts +646 -0
  70. package/src/client/ui/component/nested-chat/chat/index.mts +7 -0
  71. package/src/client/ui/component/nested-chat/index.mts +14 -0
  72. package/src/client/ui/component/nested-chat/nested-chat-constants.mts +12 -0
  73. package/src/client/ui/component/nested-chat/nested-chat-types.mts +53 -0
  74. package/src/client/ui/component/nested-chat/nested-chat.css +142 -0
  75. package/src/client/ui/component/nested-chat/nested-chat.html +11 -0
  76. package/src/client/ui/component/nested-chat/nested-chat.mts +393 -0
  77. package/src/client/ui/component/notes-explorer/index.mts +9 -0
  78. package/src/client/ui/component/notes-explorer/notes-explorer.css +313 -0
  79. package/src/client/ui/component/notes-explorer/notes-explorer.html +136 -0
  80. package/src/client/ui/component/notes-explorer/notes-explorer.mts +240 -0
  81. package/src/client/ui/component/notes-inspector/index.mts +9 -0
  82. package/src/client/ui/component/notes-inspector/notes-inspector.css +244 -0
  83. package/src/client/ui/component/notes-inspector/notes-inspector.html +97 -0
  84. package/src/client/ui/component/notes-inspector/notes-inspector.mts +152 -0
  85. package/src/client/ui/component/pitch/IMPLEMENTATION.md +48 -0
  86. package/src/client/ui/component/pitch/index.mts +9 -0
  87. package/src/client/ui/component/pitch/pitch.css +295 -0
  88. package/src/client/ui/component/pitch/pitch.html +97 -0
  89. package/src/client/ui/component/pitch/pitch.mts +135 -0
  90. package/src/client/ui/component/timeline/IMPLEMENTATION.md +33 -0
  91. package/src/client/ui/component/timeline/index.mts +9 -0
  92. package/src/client/ui/component/timeline/timeline.css +264 -0
  93. package/src/client/ui/component/timeline/timeline.html +23 -0
  94. package/src/client/ui/component/timeline/timeline.mts +317 -0
  95. package/src/client/ui/component/timeline-item-details/IMPLEMENTATION.md +28 -0
  96. package/src/client/ui/component/timeline-item-details/index.mts +9 -0
  97. package/src/client/ui/component/timeline-item-details/timeline-item-details.css +286 -0
  98. package/src/client/ui/component/timeline-item-details/timeline-item-details.html +64 -0
  99. package/src/client/ui/component/timeline-item-details/timeline-item-details.mts +194 -0
  100. package/src/client/ui/router/index.mts +3 -0
  101. package/src/client/ui/router/space-gib-router-helpers.mts +272 -0
  102. package/src/client/ui/router/space-gib-router-helpers.respec.mts +353 -0
  103. package/src/client/ui/router/space-gib-router-service.mts +186 -0
  104. package/src/client/ui/router/space-gib-router-types.mts +93 -0
  105. package/src/client/ui/shell/cosmic-big-bang.mts +844 -0
  106. package/src/client/ui/shell/space-gib-shell-constants.mts +7 -0
  107. package/src/client/ui/shell/space-gib-shell-service.mts +1728 -31
  108. package/src/server/server.mts +20 -1
  109. package/tsconfig.test.json +5 -1
  110. package/dist/client/chunk-CRJV762I.mjs +0 -3102
  111. package/dist/client/chunk-LXRCF5OS.mjs +0 -25
  112. package/dist/client/chunk-UBDQUZZV.mjs +0 -1
@@ -0,0 +1,346 @@
1
+ import thisCss from "./file-explorer.css";
2
+ import thisHtml from "./file-explorer.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 {
10
+ IbGibDynamicComponentMetaBase, IbGibDynamicComponentInstanceBase,
11
+ } from "@ibgib/web-gib/dist/ui/component/ibgib-dynamic-component-bases.mjs";
12
+ import {
13
+ ElementsBase, IbGibDynamicComponentInstance,
14
+ } from "@ibgib/web-gib/dist/ui/component/component-types.mjs";
15
+ import { getComponentCtorArg } from "@ibgib/web-gib/dist/app-bootstrap/init-orchestration.mjs";
16
+
17
+ export const FILE_EXPLORER_COMPONENT_NAME = 'space-gib-file-explorer';
18
+
19
+ /**
20
+ * Metadata factory for space-gib-file-explorer Web Component.
21
+ */
22
+ export class FileExplorerComponentMeta extends IbGibDynamicComponentMetaBase {
23
+ protected lc: string = `[${FileExplorerComponentMeta.name}]`;
24
+
25
+ routeRegExp?: RegExp = new RegExp(`^${FILE_EXPLORER_COMPONENT_NAME}$`);
26
+ componentName = FILE_EXPLORER_COMPONENT_NAME;
27
+
28
+ constructor() {
29
+ super(getComponentCtorArg());
30
+ if (!customElements.get(this.componentName)) {
31
+ customElements.define(this.componentName, FileExplorerComponentInstance);
32
+ }
33
+ }
34
+
35
+ async createInstance({
36
+ path,
37
+ ibGibAddr
38
+ }: {
39
+ path: string;
40
+ ibGibAddr: IbGibAddr;
41
+ }): Promise<IbGibDynamicComponentInstance> {
42
+ const component = document.createElement(this.componentName) as FileExplorerComponentInstance;
43
+ await component.initialize({
44
+ ibGibAddr: ibGibAddr || ROOT_ADDR,
45
+ meta: this,
46
+ html: thisHtml,
47
+ css: [thisCss],
48
+ });
49
+ return component;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * References to DOM elements within the component's Shadow Root.
55
+ */
56
+ export interface FileExplorerElements extends ElementsBase {
57
+ rootEl: HTMLDivElement;
58
+ branchHeaderEl: HTMLDivElement;
59
+ btnBranchToggleEl: HTMLButtonElement;
60
+ branchTitleEl: HTMLSpanElement;
61
+ fileTreeContainerEl: HTMLDivElement;
62
+ btnNewFileEl: HTMLButtonElement;
63
+ btnNewFolderEl: HTMLButtonElement;
64
+ btnCollapseAllEl: HTMLButtonElement;
65
+ btnBranchBranchEl?: HTMLButtonElement;
66
+ btnCloneBranchEl?: HTMLButtonElement;
67
+ }
68
+
69
+ /**
70
+ * Concrete implementation of the Branch File Explorer component.
71
+ */
72
+ export class FileExplorerComponentInstance
73
+ extends IbGibDynamicComponentInstanceBase<IbGib_V1, FileExplorerElements>
74
+ implements IbGibDynamicComponentInstance<IbGib_V1, FileExplorerElements> {
75
+
76
+ protected lc: string = `[${FileExplorerComponentInstance.name}]`;
77
+
78
+ private branchName: string = 'main';
79
+ private isBranchExpanded: boolean = true;
80
+ private selectedFilePath: string = 'src/index.ts';
81
+
82
+ get el(): FileExplorerElements {
83
+ if (!this.elements) {
84
+ throw new Error(`elements is not defined in ${this.lc}`);
85
+ }
86
+ return this.elements;
87
+ }
88
+
89
+ override async created(): Promise<void> {
90
+ const lc = `${this.lc}[${this.created.name}]`;
91
+ try {
92
+ const shadow = this.shadowRoot!;
93
+ this.elements = {
94
+ rootEl: shadow.getElementById('file-explorer-root') as HTMLDivElement,
95
+ branchHeaderEl: shadow.getElementById('branch-header') as HTMLDivElement,
96
+ btnBranchToggleEl: shadow.getElementById('btn-branch-toggle') as HTMLButtonElement,
97
+ branchTitleEl: shadow.getElementById('branch-title') as HTMLSpanElement,
98
+ fileTreeContainerEl: shadow.getElementById('file-tree-container') as HTMLDivElement,
99
+ btnNewFileEl: shadow.getElementById('btn-new-file') as HTMLButtonElement,
100
+ btnNewFolderEl: shadow.getElementById('btn-new-folder') as HTMLButtonElement,
101
+ btnCollapseAllEl: shadow.getElementById('btn-collapse-all') as HTMLButtonElement,
102
+ btnBranchBranchEl: shadow.getElementById('btn-branch-branch') as HTMLButtonElement,
103
+ btnCloneBranchEl: shadow.getElementById('btn-clone-branch') as HTMLButtonElement,
104
+ contentEl: shadow.getElementById('file-explorer-root') as HTMLDivElement,
105
+ };
106
+
107
+ this.bindHandlers();
108
+ } catch (error) {
109
+ console.error(`${lc} ${extractErrorMsg(error)}`);
110
+ }
111
+ }
112
+
113
+ private bindHandlers(): void {
114
+ const shadow = this.shadowRoot!;
115
+
116
+ // Branch header toggle
117
+ this.el.branchHeaderEl.addEventListener('click', (e) => {
118
+ const target = e.target as HTMLElement;
119
+ if (target.closest('.action-btn')) return; // ignore action buttons
120
+ this.toggleBranchAccordion();
121
+ });
122
+
123
+ // Action buttons
124
+ this.el.btnNewFileEl.addEventListener('click', (e) => {
125
+ e.stopPropagation();
126
+ this.dispatchEvent(new CustomEvent('space-gib-new-file-requested', {
127
+ detail: { branch: this.branchName },
128
+ bubbles: true,
129
+ composed: true,
130
+ }));
131
+ });
132
+
133
+ this.el.btnNewFolderEl.addEventListener('click', (e) => {
134
+ e.stopPropagation();
135
+ this.dispatchEvent(new CustomEvent('space-gib-new-folder-requested', {
136
+ detail: { branch: this.branchName },
137
+ bubbles: true,
138
+ composed: true,
139
+ }));
140
+ });
141
+
142
+ this.el.btnCollapseAllEl.addEventListener('click', (e) => {
143
+ e.stopPropagation();
144
+ this.collapseAllFolders();
145
+ });
146
+
147
+ // Worktree Branch action
148
+ this.el.btnBranchBranchEl?.addEventListener('click', async (e) => {
149
+ e.stopPropagation();
150
+ const newBranchName = await promptForText({
151
+ title: 'Branch from Worktree',
152
+ msg: `Create new branch diverging from [${this.branchName}]:`,
153
+ defaultValue: `feature/branch-from-${this.branchName}`,
154
+ cancelable: true,
155
+ });
156
+ if (newBranchName) {
157
+ console.log(`${this.lc} New branch created from worktree [${this.branchName}]: ${newBranchName}`);
158
+ this.setBranch(newBranchName);
159
+ this.dispatchEvent(new CustomEvent('space-gib-branch-requested', {
160
+ detail: {
161
+ galaxyName: 'Code Galaxy',
162
+ sourceBranch: this.branchName,
163
+ newBranchName,
164
+ scope: 'branch',
165
+ },
166
+ bubbles: true,
167
+ composed: true,
168
+ }));
169
+ }
170
+ });
171
+
172
+ // Branch Clone action
173
+ this.el.btnCloneBranchEl?.addEventListener('click', (e) => {
174
+ e.stopPropagation();
175
+ this.dispatchEvent(new CustomEvent('space-gib-clone-requested', {
176
+ detail: {
177
+ galaxyName: 'Code Galaxy',
178
+ branchName: this.branchName,
179
+ },
180
+ bubbles: true,
181
+ composed: true,
182
+ }));
183
+ });
184
+
185
+ // Single Item Branch actions
186
+ shadow.querySelectorAll('.file-branch-btn').forEach(btn => {
187
+ btn.addEventListener('click', async (e) => {
188
+ e.stopPropagation();
189
+ const treeItem = btn.closest('.tree-item');
190
+ const path = treeItem?.getAttribute('data-path') || treeItem?.getAttribute('data-folder') || '';
191
+ const isDir = treeItem?.classList.contains('folder') || false;
192
+ const filename = path.split('/').pop() || path;
193
+ const defaultBranch = `feature/branch-${filename.replace(/[^a-zA-Z0-9]/g, '-')}`;
194
+
195
+ const newBranchName = await promptForText({
196
+ title: `Branch from ${isDir ? 'Directory' : 'File'} Item`,
197
+ msg: `Create new branch isolating and diverging from item [${path}]:`,
198
+ defaultValue: defaultBranch,
199
+ cancelable: true,
200
+ });
201
+
202
+ if (newBranchName) {
203
+ console.log(`${this.lc} New branch created from item [${path}]: ${newBranchName}`);
204
+ this.setBranch(newBranchName);
205
+ this.dispatchEvent(new CustomEvent('space-gib-branch-requested', {
206
+ detail: {
207
+ galaxyName: 'Code Galaxy',
208
+ sourceBranch: this.branchName,
209
+ itemPath: path,
210
+ isDir,
211
+ newBranchName,
212
+ scope: 'item',
213
+ },
214
+ bubbles: true,
215
+ composed: true,
216
+ }));
217
+ }
218
+ });
219
+ });
220
+
221
+ // Single Item Clone actions
222
+ shadow.querySelectorAll('.file-clone-btn').forEach(btn => {
223
+ btn.addEventListener('click', (e) => {
224
+ e.stopPropagation();
225
+ const treeItem = btn.closest('.tree-item');
226
+ const path = treeItem?.getAttribute('data-path') || treeItem?.getAttribute('data-folder') || '';
227
+ const isDir = treeItem?.classList.contains('folder') || false;
228
+ this.dispatchEvent(new CustomEvent('space-gib-clone-requested', {
229
+ detail: {
230
+ singleItem: {
231
+ path,
232
+ isDir,
233
+ galaxyName: 'Code Galaxy',
234
+ branchName: this.branchName,
235
+ },
236
+ },
237
+ bubbles: true,
238
+ composed: true,
239
+ }));
240
+ });
241
+ });
242
+
243
+ // Folder toggles
244
+ shadow.querySelectorAll('.tree-item.folder').forEach(folderEl => {
245
+ folderEl.addEventListener('click', () => {
246
+ const folderName = folderEl.getAttribute('data-folder');
247
+ if (!folderName) { return; }
248
+
249
+ const isExpanded = folderEl.classList.contains('expanded');
250
+ const chevron = folderEl.querySelector('.tree-chevron');
251
+
252
+ if (isExpanded) {
253
+ folderEl.classList.remove('expanded');
254
+ if (chevron) { chevron.textContent = '▼'; }
255
+ shadow.querySelectorAll(`.child-of-${folderName}`).forEach(child => child.classList.add('hidden'));
256
+ } else {
257
+ folderEl.classList.add('expanded');
258
+ if (chevron) { chevron.textContent = '▼'; }
259
+ shadow.querySelectorAll(`.child-of-${folderName}`).forEach(child => child.classList.remove('hidden'));
260
+ }
261
+ });
262
+ });
263
+
264
+ // File selections
265
+ shadow.querySelectorAll('.tree-item.file').forEach(fileEl => {
266
+ fileEl.addEventListener('click', () => {
267
+ const path = fileEl.getAttribute('data-path') || '';
268
+ const name = fileEl.getAttribute('data-name') || '';
269
+ this.selectFile(path);
270
+
271
+ this.dispatchEvent(new CustomEvent('space-gib-file-selected', {
272
+ detail: {
273
+ branch: this.branchName,
274
+ path,
275
+ name,
276
+ },
277
+ bubbles: true,
278
+ composed: true,
279
+ }));
280
+ });
281
+ });
282
+ }
283
+
284
+ public toggleBranchAccordion(): void {
285
+ this.isBranchExpanded = !this.isBranchExpanded;
286
+ if (this.isBranchExpanded) {
287
+ this.el.branchHeaderEl.classList.remove('collapsed');
288
+ this.el.branchHeaderEl.classList.add('expanded');
289
+ this.el.fileTreeContainerEl.classList.remove('collapsed');
290
+ } else {
291
+ this.el.branchHeaderEl.classList.add('collapsed');
292
+ this.el.branchHeaderEl.classList.remove('expanded');
293
+ this.el.fileTreeContainerEl.classList.add('collapsed');
294
+ }
295
+ }
296
+
297
+ public collapseAllFolders(): void {
298
+ const shadow = this.shadowRoot!;
299
+ shadow.querySelectorAll('.tree-item.folder').forEach(folderEl => {
300
+ const folderName = folderEl.getAttribute('data-folder');
301
+ folderEl.classList.remove('expanded');
302
+ const chevron = folderEl.querySelector('.tree-chevron');
303
+ // if (chevron) chevron.textContent = '▶';
304
+ if (chevron) { chevron.textContent = '▼'; }
305
+ if (folderName) {
306
+ shadow.querySelectorAll(`.child-of-${folderName}`).forEach(child => child.classList.add('hidden'));
307
+ }
308
+ });
309
+ }
310
+
311
+ public selectFile(path: string): void {
312
+ this.selectedFilePath = path;
313
+ const shadow = this.shadowRoot!;
314
+ shadow.querySelectorAll('.tree-item.file').forEach(el => {
315
+ if (el.getAttribute('data-path') === path) {
316
+ el.classList.add('selected');
317
+ } else {
318
+ el.classList.remove('selected');
319
+ }
320
+ });
321
+ }
322
+
323
+ public setBranch(branchName: string): void {
324
+ this.branchName = branchName;
325
+ if (this.el?.branchTitleEl) {
326
+ this.el.branchTitleEl.textContent = branchName;
327
+ }
328
+ }
329
+
330
+ public getBranch(): string {
331
+ return this.branchName;
332
+ }
333
+
334
+ public getSelectedFilePath(): string {
335
+ return this.selectedFilePath;
336
+ }
337
+
338
+ override async disconnected(): Promise<void> {
339
+ const lc = `${this.lc}[${this.disconnected.name}]`;
340
+ try {
341
+ // cleanup if needed
342
+ } catch (error) {
343
+ console.error(`${lc} ${extractErrorMsg(error)}`);
344
+ }
345
+ }
346
+ }
@@ -0,0 +1,9 @@
1
+ import { getComponentSvc } from "@ibgib/web-gib/dist/ui/component/ibgib-component-service.mjs";
2
+ import { FileExplorerComponentMeta } from "./file-explorer.mjs";
3
+
4
+ export * from "./file-explorer.mjs";
5
+
6
+ export async function registerFileExplorerComponent(): Promise<void> {
7
+ const componentSvc = await getComponentSvc();
8
+ componentSvc.registerComponentMeta(new FileExplorerComponentMeta());
9
+ }
@@ -0,0 +1,18 @@
1
+ # space-gib-nested-chat Component Implementation
2
+
3
+ ## 1. Overview
4
+ `space-gib-nested-chat` is a dynamic container Web Component that hosts recursive `space-gib-chat` instances in the `space-gib` right panel. It extends `IbGibDynamicComponentInstanceBase_ParentOfTabs` to manage child tabs formatted as an interactive breadcrumb navigation rail.
5
+
6
+ ## 2. Core Concepts & Navigation Paradigm
7
+ - **Breadcrumb-Styled Tabs**:
8
+ - Rather than traditional square tabs, tabs are styled as a horizontal breadcrumb path (`[🌿 src/index.ts] › [💬 Architectural Rationale...] › [💬 Boundary Try/Catch...]`).
9
+ - The currently active breadcrumb has high-contrast accent styling.
10
+ - **Non-Destructive Ancestor Navigation**:
11
+ - Clicking any ancestor breadcrumb activates that ancestor's `space-gib-chat` component in the main content viewport (`this.activateIbGib({ addr })`).
12
+ - Descendant breadcrumb tabs remain in the rail so the user can easily hop back and forth along the conversation trail.
13
+ - **Branch Divergence**:
14
+ - If the user is currently focused on an ancestor tab and selects a *different* comment to drill down into, any descendant tabs beyond the active tab are pruned and the new child comment is appended as the new active tab.
15
+
16
+ ## 3. Events Handled & Dispatched
17
+ - Listens for `space-gib-chat-drill-down`: Triggers drill-down into child comment context.
18
+ - Dispatches `space-gib-chat-closed` / `space-gib-chat-close-requested`: Requests shell collapse/close of right chat panel.
@@ -0,0 +1,14 @@
1
+ # space-gib-chat Component Implementation
2
+
3
+ ## 1. Overview
4
+ `space-gib-chat` is a dynamic Web Component representing a single chatroom context within the `space-gib-nested-chat` container. In the ibGib model, any ibGib (a source file, commit, diff, or individual comment) can act as a chatroom context.
5
+
6
+ ## 2. Responsibilities
7
+ - Renders the list of living comments attached to the context ibGib (`this.ibGib.rel8ns.chat`).
8
+ - Renders author identity, avatar, role (Keystone/Peer/Agent), timestamps, and optional code anchors.
9
+ - Provides a drill-down trigger (`↳ N replies / Drill In 🔍`) that fires `space-gib-chat-drill-down` custom event upward to `space-gib-nested-chat`.
10
+ - Provides an inline message composer to append new living comments to the current context.
11
+
12
+ ## 3. Events Dispatched
13
+ - `space-gib-chat-drill-down`: Bubbling, composed event carrying `{ addr: IbGibAddr, text: string, author?: string }`.
14
+ - `space-gib-chat-message-sent`: Notification event carrying `{ message: ChatCommentItem, contextAddr: IbGibAddr }`.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * in the chat component, there are two different types of behavior for ENTER
3
+ * key: "send" | "newline".
4
+ *
5
+ * This is the key used in storage for storing this small settings locally.
6
+ *
7
+ * ## driving use case
8
+ *
9
+ * It drives me bonkers when I have to remember to hit SHIFT+ENTER in order to
10
+ * avoid sending a complex message/text.
11
+ */
12
+ export const SPACE_GIB_CHAT_ENTER_BEHAVIOR_KEY = 'space_gib_enter_behavior';
13
+
14
+ export const SPACE_GIB_CHAT_ENTER_BEHAVIOR_KEY_DEFAULT = 'newline';
@@ -0,0 +1,66 @@
1
+ import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
2
+ import { storageGet, storagePut } from "@ibgib/web-gib/dist/storage/storage-helpers.web.mjs";
3
+
4
+
5
+ import { APP_CONFIG, GLOBAL_LOG_A_LOT } from "../../../../constants.mjs";
6
+ import { SPACE_GIB_CHAT_ENTER_BEHAVIOR_KEY, SPACE_GIB_CHAT_ENTER_BEHAVIOR_KEY_DEFAULT } from "./chat-constants.mjs";
7
+
8
+ const logalot = GLOBAL_LOG_A_LOT;
9
+
10
+ /**
11
+ * gets value of how the ENTER key should behave when pressed in the chat.
12
+ * First looks in storage, and if not found (or errored), returns default.
13
+ *
14
+ * ## driving use case
15
+ *
16
+ * It drives me bonkers when I have to remember to hit SHIFT+ENTER in order to
17
+ * avoid sending a complex message/text.
18
+ *
19
+ * @returns enter behavior from storage or default
20
+ *
21
+ * @see {@link SPACE_GIB_CHAT_ENTER_BEHAVIOR_KEY}
22
+ * @see {@link SPACE_GIB_CHAT_ENTER_BEHAVIOR_KEY_DEFAULT}
23
+ */
24
+ export async function getSavedEnterBehavior(): Promise<'send' | 'newline'> {
25
+ const lc = `[${getSavedEnterBehavior.name}]`;
26
+ try {
27
+ const saved = await storageGet({
28
+ dbName: APP_CONFIG.dbName,
29
+ storeName: APP_CONFIG.storeName,
30
+ key: SPACE_GIB_CHAT_ENTER_BEHAVIOR_KEY,
31
+ });
32
+ if (saved === 'newline' || saved === 'send') {
33
+ return saved;
34
+ } else {
35
+ throw new Error(`invalid saved value: ${saved}. expected "newline" or "send" (E: 2ff05888161f88781ec3a778b8dbdc26)`);
36
+ }
37
+ } catch (error) {
38
+ console.log(`${lc} (NOT THROWN) error: ${extractErrorMsg(error)}. returning default value: ${SPACE_GIB_CHAT_ENTER_BEHAVIOR_KEY_DEFAULT} (E: 71a0df6714c22a322e65702a2f743126)`);
39
+ return SPACE_GIB_CHAT_ENTER_BEHAVIOR_KEY_DEFAULT;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * @see {@link getSavedEnterBehavior}
45
+ */
46
+ export async function setSavedEnterBehavior(value: 'send' | 'newline'): Promise<void> {
47
+ const lc = `[${setSavedEnterBehavior.name}]`;
48
+ try {
49
+ if (logalot) { console.log(`${lc} starting... (I: 39e3f846651cb92cfec3da4ee305ea26)`); }
50
+
51
+ if (value !== "send" && value !== "newline") {
52
+ throw new Error(`(UNEXPECTED) invalid val (val)? should be "send" or "newline" (E: b5323681d3d66ddb3868cc9aa7fae826)`);
53
+ }
54
+
55
+ await storagePut({
56
+ dbName: APP_CONFIG.dbName,
57
+ storeName: APP_CONFIG.storeName,
58
+ key: SPACE_GIB_CHAT_ENTER_BEHAVIOR_KEY,
59
+ value,
60
+ });
61
+ } catch (error) {
62
+ console.error(`${lc} (NOT THROWN) ${extractErrorMsg(error)}`);
63
+ } finally {
64
+ if (logalot) { console.log(`${lc} complete.`); }
65
+ }
66
+ }