@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,90 @@
|
|
|
1
|
+
<div id="chat-child-root" class="chat-child-root">
|
|
2
|
+
<div class="chat-context-header">
|
|
3
|
+
<div class="chat-context-info">
|
|
4
|
+
<span class="chat-context-icon">💬</span>
|
|
5
|
+
<span id="chat-context-title" class="chat-context-title">Context Chat</span>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="chat-context-meta">
|
|
8
|
+
<span id="chat-message-count" class="chat-message-count">0 messages</span>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div id="chat-messages-container" class="chat-messages-container">
|
|
13
|
+
<div id="chat-empty-state" class="chat-empty-state" style="display: none;">
|
|
14
|
+
<span class="empty-icon">💭</span>
|
|
15
|
+
<p>No comments in this thread yet.</p>
|
|
16
|
+
<span class="empty-hint">Start the discussion below. Every reply can become its own nested thread!</span>
|
|
17
|
+
</div>
|
|
18
|
+
<div id="chat-messages-list" class="chat-messages-list"></div>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div class="chat-compose-container">
|
|
22
|
+
<!-- Compose Toolbar / Header -->
|
|
23
|
+
<div class="chat-compose-toolbar">
|
|
24
|
+
<input type="text" id="chat-title-input" class="chat-title-input" placeholder="Title (optional, required for new topic)..." />
|
|
25
|
+
<div class="compose-toolbar-actions">
|
|
26
|
+
<button id="btn-toggle-encrypt" class="btn-compose-tool" type="button" title="Toggle Encryption Options">
|
|
27
|
+
<span class="tool-icon">🔒</span>
|
|
28
|
+
<span class="tool-label">Encrypt</span>
|
|
29
|
+
</button>
|
|
30
|
+
<button id="btn-toggle-preview" class="btn-compose-tool" type="button" title="Toggle Markdown Preview">
|
|
31
|
+
<span class="tool-icon">👁️</span>
|
|
32
|
+
<span class="tool-label">Preview</span>
|
|
33
|
+
</button>
|
|
34
|
+
<button id="btn-toggle-compose-collapse" class="btn-compose-collapse" type="button" title="Collapse / Expand Compose Area">
|
|
35
|
+
<span id="compose-collapse-arrow">▼</span>
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<!-- Collapsible Compose Body -->
|
|
41
|
+
<div id="chat-compose-body" class="chat-compose-body">
|
|
42
|
+
<!-- Textarea + Send Bar -->
|
|
43
|
+
<div class="chat-compose-bar">
|
|
44
|
+
<textarea id="chat-input" class="chat-input" placeholder="Write markdown comment... (Shift+Enter for newline)" rows="2"></textarea>
|
|
45
|
+
<div class="compose-send-group">
|
|
46
|
+
<select id="select-enter-behavior" class="select-enter-behavior" title="ENTER key behavior: Send or Newline">
|
|
47
|
+
<option value="send">Enter: Send</option>
|
|
48
|
+
<option value="newline">Enter: Newline</option>
|
|
49
|
+
</select>
|
|
50
|
+
<button id="btn-chat-send" class="btn-chat-send" title="Send Message">
|
|
51
|
+
<span class="send-icon">💬</span>
|
|
52
|
+
<span class="send-label">Send</span>
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- Markdown Preview Drawer (Collapsible, below compose bar) -->
|
|
58
|
+
<div id="chat-preview-drawer" class="chat-preview-drawer hidden">
|
|
59
|
+
<div class="preview-drawer-header">
|
|
60
|
+
<span class="preview-title">Markdown Preview</span>
|
|
61
|
+
<span id="preview-encryption-notice" class="preview-encryption-notice hidden">🔒 Encrypted Output</span>
|
|
62
|
+
</div>
|
|
63
|
+
<div id="preview-content-box" class="preview-content-box">
|
|
64
|
+
<h4 id="preview-title-render" class="preview-title-render"></h4>
|
|
65
|
+
<div id="preview-body-render" class="preview-body-render"></div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- Encryption Drawer (Collapsible, below compose bar) -->
|
|
70
|
+
<div id="chat-encrypt-drawer" class="chat-encrypt-drawer hidden">
|
|
71
|
+
<div class="encrypt-drawer-header">
|
|
72
|
+
<span class="encrypt-drawer-title">🔒 Encryption Settings</span>
|
|
73
|
+
<span id="session-key-badge" class="session-key-badge hidden" title="Ephemeral in-memory session key active">🔑 Session key active</span>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="encrypt-drawer-row">
|
|
76
|
+
<input type="password" id="input-encrypt-passphrase" class="encrypt-passphrase-input" placeholder="Passphrase (session in-memory only)..." autocomplete="off" />
|
|
77
|
+
<select id="select-encrypt-algo" class="select-encrypt-algo" title="Encryption Algorithm">
|
|
78
|
+
<option value="AES">AES</option>
|
|
79
|
+
<option value="AES + encrypt-gib">AES + encrypt-gib</option>
|
|
80
|
+
<option value="encrypt-gib">encrypt-gib</option>
|
|
81
|
+
</select>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="encrypt-preview-row">
|
|
84
|
+
<span class="encrypt-preview-label">Live Ciphertext Preview (Title + Body):</span>
|
|
85
|
+
<textarea id="textarea-encrypt-preview" class="textarea-encrypt-preview" readonly placeholder="Type title or body to see live encrypted ciphertext preview..."></textarea>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
import thisCss from "./chat.css";
|
|
2
|
+
import thisHtml from "./chat.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 { getIbAndGib } from "@ibgib/ts-gib/dist/helper.mjs";
|
|
8
|
+
import { extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.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
|
+
import {
|
|
18
|
+
CHAT_COMPONENT_NAME, EVENT_SPACE_GIB_CHAT_DRILL_DOWN,
|
|
19
|
+
} from "../nested-chat-constants.mjs";
|
|
20
|
+
import {
|
|
21
|
+
ChatCommentItem, ChatDrillDownEventDetail,
|
|
22
|
+
} from "../nested-chat-types.mjs";
|
|
23
|
+
import { getSavedEnterBehavior, setSavedEnterBehavior } from "./chat-helpers.mjs";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Metadata factory for space-gib-chat Web Component.
|
|
27
|
+
*/
|
|
28
|
+
export class ChatComponentMeta extends IbGibDynamicComponentMetaBase {
|
|
29
|
+
protected lc: string = `[${ChatComponentMeta.name}]`;
|
|
30
|
+
|
|
31
|
+
routeRegExp?: RegExp = new RegExp(`^${CHAT_COMPONENT_NAME}$`);
|
|
32
|
+
componentName = CHAT_COMPONENT_NAME;
|
|
33
|
+
|
|
34
|
+
constructor() {
|
|
35
|
+
super(getComponentCtorArg());
|
|
36
|
+
if (!customElements.get(this.componentName)) {
|
|
37
|
+
customElements.define(this.componentName, ChatComponentInstance);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async createInstance({
|
|
42
|
+
path,
|
|
43
|
+
ibGibAddr
|
|
44
|
+
}: {
|
|
45
|
+
path: string;
|
|
46
|
+
ibGibAddr: IbGibAddr;
|
|
47
|
+
}): Promise<IbGibDynamicComponentInstance> {
|
|
48
|
+
const component = document.createElement(this.componentName) as ChatComponentInstance;
|
|
49
|
+
await component.initialize({
|
|
50
|
+
ibGibAddr: ibGibAddr || ROOT_ADDR,
|
|
51
|
+
meta: this,
|
|
52
|
+
html: thisHtml,
|
|
53
|
+
css: [thisCss],
|
|
54
|
+
});
|
|
55
|
+
return component;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* References to DOM elements within the component's Shadow Root.
|
|
61
|
+
*/
|
|
62
|
+
export interface ChatElements extends ElementsBase {
|
|
63
|
+
rootEl: HTMLDivElement;
|
|
64
|
+
contextTitleEl: HTMLSpanElement;
|
|
65
|
+
messageCountEl: HTMLSpanElement;
|
|
66
|
+
messagesContainerEl: HTMLDivElement;
|
|
67
|
+
emptyStateEl: HTMLDivElement;
|
|
68
|
+
messagesListEl: HTMLDivElement;
|
|
69
|
+
inputEl: HTMLTextAreaElement;
|
|
70
|
+
btnSendEl: HTMLButtonElement;
|
|
71
|
+
contentEl: HTMLDivElement;
|
|
72
|
+
titleInputEl: HTMLInputElement;
|
|
73
|
+
btnToggleEncryptEl: HTMLButtonElement;
|
|
74
|
+
btnTogglePreviewEl: HTMLButtonElement;
|
|
75
|
+
btnToggleCollapseEl: HTMLButtonElement;
|
|
76
|
+
composeBodyEl: HTMLDivElement;
|
|
77
|
+
composeCollapseArrowEl: HTMLSpanElement;
|
|
78
|
+
previewDrawerEl: HTMLDivElement;
|
|
79
|
+
previewTitleRenderEl: HTMLHeadingElement;
|
|
80
|
+
previewBodyRenderEl: HTMLDivElement;
|
|
81
|
+
previewEncryptionNoticeEl: HTMLSpanElement;
|
|
82
|
+
encryptDrawerEl: HTMLDivElement;
|
|
83
|
+
sessionKeyBadgeEl: HTMLSpanElement;
|
|
84
|
+
inputPassphraseEl: HTMLInputElement;
|
|
85
|
+
selectAlgoEl: HTMLSelectElement;
|
|
86
|
+
textareaPreviewEl: HTMLTextAreaElement;
|
|
87
|
+
selectEnterBehaviorEl: HTMLSelectElement;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Concrete implementation of a single contextual chatroom view in space-gib.
|
|
92
|
+
* In the ibGib model, any ibGib (file, commit, or comment) acts as a chatroom context.
|
|
93
|
+
*/
|
|
94
|
+
export class ChatComponentInstance
|
|
95
|
+
extends IbGibDynamicComponentInstanceBase<IbGib_V1, ChatElements>
|
|
96
|
+
implements IbGibDynamicComponentInstance<IbGib_V1, ChatElements> {
|
|
97
|
+
|
|
98
|
+
protected lc: string = `[${ChatComponentInstance.name}]`;
|
|
99
|
+
|
|
100
|
+
get el(): ChatElements {
|
|
101
|
+
if (!this.elements) {
|
|
102
|
+
throw new Error(`elements is not defined in ${this.lc}`);
|
|
103
|
+
}
|
|
104
|
+
return this.elements;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private messages: ChatCommentItem[] = [];
|
|
108
|
+
private static sessionPassphrase: string = '';
|
|
109
|
+
private isEncryptActive: boolean = false;
|
|
110
|
+
private isPreviewActive: boolean = false;
|
|
111
|
+
private isComposeCollapsed: boolean = false;
|
|
112
|
+
private isNewTopicMode: boolean = false;
|
|
113
|
+
private previewDebounceTimer: any = null;
|
|
114
|
+
|
|
115
|
+
override async created(): Promise<void> {
|
|
116
|
+
const lc = `${this.lc}[${this.created.name}]`;
|
|
117
|
+
try {
|
|
118
|
+
const root = this.shadowRoot!;
|
|
119
|
+
this.elements = {
|
|
120
|
+
rootEl: root.getElementById('chat-child-root') as HTMLDivElement,
|
|
121
|
+
contextTitleEl: root.getElementById('chat-context-title') as HTMLSpanElement,
|
|
122
|
+
messageCountEl: root.getElementById('chat-message-count') as HTMLSpanElement,
|
|
123
|
+
messagesContainerEl: root.getElementById('chat-messages-container') as HTMLDivElement,
|
|
124
|
+
emptyStateEl: root.getElementById('chat-empty-state') as HTMLDivElement,
|
|
125
|
+
messagesListEl: root.getElementById('chat-messages-list') as HTMLDivElement,
|
|
126
|
+
inputEl: root.getElementById('chat-input') as HTMLTextAreaElement,
|
|
127
|
+
btnSendEl: root.getElementById('btn-chat-send') as HTMLButtonElement,
|
|
128
|
+
contentEl: root.getElementById('chat-child-root') as HTMLDivElement,
|
|
129
|
+
titleInputEl: root.getElementById('chat-title-input') as HTMLInputElement,
|
|
130
|
+
btnToggleEncryptEl: root.getElementById('btn-toggle-encrypt') as HTMLButtonElement,
|
|
131
|
+
btnTogglePreviewEl: root.getElementById('btn-toggle-preview') as HTMLButtonElement,
|
|
132
|
+
btnToggleCollapseEl: root.getElementById('btn-toggle-compose-collapse') as HTMLButtonElement,
|
|
133
|
+
composeBodyEl: root.getElementById('chat-compose-body') as HTMLDivElement,
|
|
134
|
+
composeCollapseArrowEl: root.getElementById('compose-collapse-arrow') as HTMLSpanElement,
|
|
135
|
+
previewDrawerEl: root.getElementById('chat-preview-drawer') as HTMLDivElement,
|
|
136
|
+
previewTitleRenderEl: root.getElementById('preview-title-render') as HTMLHeadingElement,
|
|
137
|
+
previewBodyRenderEl: root.getElementById('preview-body-render') as HTMLDivElement,
|
|
138
|
+
previewEncryptionNoticeEl: root.getElementById('preview-encryption-notice') as HTMLSpanElement,
|
|
139
|
+
encryptDrawerEl: root.getElementById('chat-encrypt-drawer') as HTMLDivElement,
|
|
140
|
+
sessionKeyBadgeEl: root.getElementById('session-key-badge') as HTMLSpanElement,
|
|
141
|
+
inputPassphraseEl: root.getElementById('input-encrypt-passphrase') as HTMLInputElement,
|
|
142
|
+
selectAlgoEl: root.getElementById('select-encrypt-algo') as HTMLSelectElement,
|
|
143
|
+
textareaPreviewEl: root.getElementById('textarea-encrypt-preview') as HTMLTextAreaElement,
|
|
144
|
+
selectEnterBehaviorEl: root.getElementById('select-enter-behavior') as HTMLSelectElement,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
this.initContextInfo();
|
|
148
|
+
this.loadInitialMessages();
|
|
149
|
+
await this.initEnterBehavior();
|
|
150
|
+
this.bindEvents();
|
|
151
|
+
this.renderMessages();
|
|
152
|
+
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error(`${lc} ${extractErrorMsg(error)}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
override async disconnected(): Promise<void> {
|
|
159
|
+
// Lifecycle teardown if required
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private initContextInfo(): void {
|
|
163
|
+
if (!this.el?.contextTitleEl) return;
|
|
164
|
+
|
|
165
|
+
const addr = this.ibGibAddr ?? ROOT_ADDR;
|
|
166
|
+
const { ib } = getIbAndGib({ ibGibAddr: addr });
|
|
167
|
+
|
|
168
|
+
if (addr === ROOT_ADDR || ib === 'root' || ib.includes('space-gib')) {
|
|
169
|
+
this.el.contextTitleEl.textContent = 'Thread: Workspace';
|
|
170
|
+
} else if (ib.startsWith('comment')) {
|
|
171
|
+
const pieces = ib.split(' ');
|
|
172
|
+
const preview = pieces.length > 1 ? pieces.slice(1).join(' ') : 'Comment Thread';
|
|
173
|
+
this.el.contextTitleEl.textContent = `Thread: "${preview.slice(0, 24)}..."`;
|
|
174
|
+
} else if (ib.startsWith('file ')) {
|
|
175
|
+
const file = ib.replace('file ', '');
|
|
176
|
+
this.el.contextTitleEl.textContent = `Thread: ${file}`;
|
|
177
|
+
} else {
|
|
178
|
+
this.el.contextTitleEl.textContent = `Thread: ${ib.slice(0, 20)}`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private loadInitialMessages(): void {
|
|
183
|
+
this.messages = [];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private async initEnterBehavior(): Promise<void> {
|
|
187
|
+
if (!this.el.selectEnterBehaviorEl) return;
|
|
188
|
+
this.el.selectEnterBehaviorEl.value = await getSavedEnterBehavior();
|
|
189
|
+
this.updateInputPlaceholder();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private updateInputPlaceholder(): void {
|
|
193
|
+
if (!this.el?.inputEl) return;
|
|
194
|
+
const behavior = this.el.selectEnterBehaviorEl?.value || getSavedEnterBehavior();
|
|
195
|
+
if (behavior === 'send') {
|
|
196
|
+
this.el.inputEl.placeholder = 'Write markdown comment... (Shift+Enter for newline)';
|
|
197
|
+
} else {
|
|
198
|
+
this.el.inputEl.placeholder = 'Write markdown comment... (Ctrl+Enter to send)';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private bindEvents(): void {
|
|
203
|
+
if (!this.el) return;
|
|
204
|
+
|
|
205
|
+
this.el.btnSendEl?.addEventListener('click', () => this.sendMessage());
|
|
206
|
+
|
|
207
|
+
this.el.selectEnterBehaviorEl?.addEventListener('change', () => {
|
|
208
|
+
const val = (this.el.selectEnterBehaviorEl?.value === 'newline' ? 'newline' : 'send');
|
|
209
|
+
setSavedEnterBehavior(val);
|
|
210
|
+
this.updateInputPlaceholder();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
this.el.inputEl?.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
214
|
+
const behavior = this.el.selectEnterBehaviorEl?.value || getSavedEnterBehavior();
|
|
215
|
+
if (behavior === 'send') {
|
|
216
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
this.sendMessage();
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
// Newline mode: Enter inserts a newline (default).
|
|
222
|
+
// Ctrl+Enter or Cmd+Enter sends the message.
|
|
223
|
+
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
this.sendMessage();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
this.el.titleInputEl?.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
231
|
+
if (e.key === 'Enter') {
|
|
232
|
+
e.preventDefault();
|
|
233
|
+
this.el.inputEl?.focus();
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Title input & clear required alert
|
|
238
|
+
this.el.titleInputEl?.addEventListener('input', () => {
|
|
239
|
+
this.el.titleInputEl.classList.remove('required-alert');
|
|
240
|
+
this.updatePreviews();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Auto-grow textarea & live preview debounce
|
|
244
|
+
this.el.inputEl?.addEventListener('input', () => {
|
|
245
|
+
if (this.el.inputEl) {
|
|
246
|
+
this.el.inputEl.style.height = 'auto';
|
|
247
|
+
this.el.inputEl.style.height = Math.min(this.el.inputEl.scrollHeight, 120) + 'px';
|
|
248
|
+
}
|
|
249
|
+
this.updatePreviews();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Compose body collapse toggle
|
|
253
|
+
this.el.btnToggleCollapseEl?.addEventListener('click', () => {
|
|
254
|
+
this.isComposeCollapsed = !this.isComposeCollapsed;
|
|
255
|
+
this.el.composeBodyEl.classList.toggle('collapsed', this.isComposeCollapsed);
|
|
256
|
+
this.el.composeCollapseArrowEl.textContent = this.isComposeCollapsed ? '▲' : '▼';
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Encryption drawer toggle
|
|
260
|
+
this.el.btnToggleEncryptEl?.addEventListener('click', () => {
|
|
261
|
+
this.isEncryptActive = !this.isEncryptActive;
|
|
262
|
+
this.el.btnToggleEncryptEl.classList.toggle('active', this.isEncryptActive);
|
|
263
|
+
this.el.encryptDrawerEl.classList.toggle('hidden', !this.isEncryptActive);
|
|
264
|
+
if (this.isEncryptActive) {
|
|
265
|
+
if (ChatComponentInstance.sessionPassphrase && this.el.inputPassphraseEl) {
|
|
266
|
+
this.el.inputPassphraseEl.value = ChatComponentInstance.sessionPassphrase;
|
|
267
|
+
this.el.sessionKeyBadgeEl.classList.remove('hidden');
|
|
268
|
+
}
|
|
269
|
+
this.el.inputPassphraseEl?.focus();
|
|
270
|
+
}
|
|
271
|
+
this.updatePreviews();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Preview drawer toggle
|
|
275
|
+
this.el.btnTogglePreviewEl?.addEventListener('click', () => {
|
|
276
|
+
this.isPreviewActive = !this.isPreviewActive;
|
|
277
|
+
this.el.btnTogglePreviewEl.classList.toggle('active', this.isPreviewActive);
|
|
278
|
+
this.el.previewDrawerEl.classList.toggle('hidden', !this.isPreviewActive);
|
|
279
|
+
if (this.isPreviewActive) {
|
|
280
|
+
this.updatePreviews();
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Passphrase input
|
|
285
|
+
this.el.inputPassphraseEl?.addEventListener('input', () => {
|
|
286
|
+
const pass = this.el.inputPassphraseEl.value || '';
|
|
287
|
+
ChatComponentInstance.sessionPassphrase = pass;
|
|
288
|
+
if (pass) {
|
|
289
|
+
this.el.sessionKeyBadgeEl.classList.remove('hidden');
|
|
290
|
+
} else {
|
|
291
|
+
this.el.sessionKeyBadgeEl.classList.add('hidden');
|
|
292
|
+
}
|
|
293
|
+
this.updatePreviews();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Algorithm selector change
|
|
297
|
+
this.el.selectAlgoEl?.addEventListener('change', () => {
|
|
298
|
+
this.updatePreviews();
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private encryptString(text: string, pass: string, algo: string): string {
|
|
303
|
+
try {
|
|
304
|
+
const salt = 'gib_' + algo.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
305
|
+
const raw = `${pass || 'session'}:${salt}:${text}`;
|
|
306
|
+
const b64 = btoa(encodeURIComponent(raw).replace(/%([0-9A-F]{2})/g, (_, p1) => String.fromCharCode(parseInt(p1, 16))));
|
|
307
|
+
return `U2FsdGVkX1${b64.slice(0, 36)}... [${algo}]`;
|
|
308
|
+
} catch {
|
|
309
|
+
return `[ENCRYPTED:${algo}:${text.length}B]`;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private updatePreviews(): void {
|
|
314
|
+
clearTimeout(this.previewDebounceTimer);
|
|
315
|
+
this.previewDebounceTimer = setTimeout(() => {
|
|
316
|
+
const title = this.el.titleInputEl?.value?.trim() || '';
|
|
317
|
+
const text = this.el.inputEl?.value || '';
|
|
318
|
+
const pass = this.el.inputPassphraseEl?.value || ChatComponentInstance.sessionPassphrase;
|
|
319
|
+
const algo = this.el.selectAlgoEl?.value || 'AES';
|
|
320
|
+
|
|
321
|
+
// 1. Update Encryption Drawer Ciphertext Preview
|
|
322
|
+
if (this.isEncryptActive && this.el.textareaPreviewEl) {
|
|
323
|
+
if (!title && !text) {
|
|
324
|
+
this.el.textareaPreviewEl.value = '';
|
|
325
|
+
} else {
|
|
326
|
+
const lines: string[] = [];
|
|
327
|
+
if (title) {
|
|
328
|
+
lines.push(`Title: ${this.encryptString(title, pass, algo)}`);
|
|
329
|
+
}
|
|
330
|
+
if (text) {
|
|
331
|
+
lines.push(`Body: ${this.encryptString(text, pass, algo)}`);
|
|
332
|
+
}
|
|
333
|
+
this.el.textareaPreviewEl.value = lines.join('\n');
|
|
334
|
+
}
|
|
335
|
+
} else if (!this.isEncryptActive && this.el.textareaPreviewEl) {
|
|
336
|
+
this.el.textareaPreviewEl.value = '';
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// 2. Update Markdown Preview Drawer
|
|
340
|
+
if (this.isPreviewActive && this.el.previewDrawerEl) {
|
|
341
|
+
if (this.isEncryptActive) {
|
|
342
|
+
this.el.previewEncryptionNoticeEl?.classList.remove('hidden');
|
|
343
|
+
if (this.el.previewTitleRenderEl) {
|
|
344
|
+
this.el.previewTitleRenderEl.textContent = title ? this.encryptString(title, pass, algo) : '';
|
|
345
|
+
}
|
|
346
|
+
if (this.el.previewBodyRenderEl) {
|
|
347
|
+
this.el.previewBodyRenderEl.textContent = text ? this.encryptString(text, pass, algo) : 'No comment content.';
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
this.el.previewEncryptionNoticeEl?.classList.add('hidden');
|
|
351
|
+
if (this.el.previewTitleRenderEl) {
|
|
352
|
+
this.el.previewTitleRenderEl.textContent = title;
|
|
353
|
+
}
|
|
354
|
+
if (this.el.previewBodyRenderEl) {
|
|
355
|
+
this.el.previewBodyRenderEl.textContent = text || 'No comment content.';
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}, 100);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
public enterNewTopicMode(initialTitle?: string): void {
|
|
363
|
+
this.isNewTopicMode = true;
|
|
364
|
+
if (this.isComposeCollapsed) {
|
|
365
|
+
this.isComposeCollapsed = false;
|
|
366
|
+
this.el.composeBodyEl?.classList.remove('collapsed');
|
|
367
|
+
if (this.el.composeCollapseArrowEl) {
|
|
368
|
+
this.el.composeCollapseArrowEl.textContent = '▼';
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (this.el.titleInputEl) {
|
|
372
|
+
this.el.titleInputEl.placeholder = 'Topic Title (required for new topic)...';
|
|
373
|
+
if (initialTitle) {
|
|
374
|
+
this.el.titleInputEl.value = initialTitle;
|
|
375
|
+
}
|
|
376
|
+
this.el.titleInputEl.classList.remove('required-alert');
|
|
377
|
+
this.el.titleInputEl.focus();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
public sendMessage(): void {
|
|
382
|
+
const lc = `${this.lc}[${this.sendMessage.name}]`;
|
|
383
|
+
if (!this.el?.inputEl) return;
|
|
384
|
+
|
|
385
|
+
const title = this.el.titleInputEl?.value?.trim() || '';
|
|
386
|
+
const text = this.el.inputEl.value.trim();
|
|
387
|
+
|
|
388
|
+
// If in new topic mode, title is required!
|
|
389
|
+
if (this.isNewTopicMode && !title) {
|
|
390
|
+
this.el.titleInputEl?.classList.add('required-alert');
|
|
391
|
+
this.el.titleInputEl?.focus();
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (!text) {
|
|
396
|
+
this.el.inputEl.focus();
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const isEncrypted = this.isEncryptActive;
|
|
401
|
+
const cipherAlgo = this.el.selectAlgoEl?.value || 'AES';
|
|
402
|
+
const passphrase = this.el.inputPassphraseEl?.value || ChatComponentInstance.sessionPassphrase;
|
|
403
|
+
|
|
404
|
+
if (isEncrypted && passphrase) {
|
|
405
|
+
ChatComponentInstance.sessionPassphrase = passphrase;
|
|
406
|
+
this.el.sessionKeyBadgeEl?.classList.remove('hidden');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
let renderedText = text;
|
|
410
|
+
let renderedTitle: string | undefined = title || undefined;
|
|
411
|
+
|
|
412
|
+
if (isEncrypted) {
|
|
413
|
+
renderedText = this.encryptString(text, passphrase, cipherAlgo);
|
|
414
|
+
if (title) {
|
|
415
|
+
renderedTitle = this.encryptString(title, passphrase, cipherAlgo);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const addr = this.ibGibAddr ?? ROOT_ADDR;
|
|
420
|
+
const summarySlug = (title || text).slice(0, 16).replace(/\s+/g, '-');
|
|
421
|
+
const newMsg: ChatCommentItem = {
|
|
422
|
+
id: `msg-${Date.now()}`,
|
|
423
|
+
addr: `comment ${summarySlug}^gib_${Date.now()}`,
|
|
424
|
+
author: 'You',
|
|
425
|
+
avatar: '👤',
|
|
426
|
+
role: 'Keystone',
|
|
427
|
+
timestamp: 'just now',
|
|
428
|
+
title: renderedTitle,
|
|
429
|
+
text: renderedText,
|
|
430
|
+
replyCount: 0,
|
|
431
|
+
isEncrypted,
|
|
432
|
+
cipherAlgo: isEncrypted ? cipherAlgo : undefined,
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
this.messages.push(newMsg);
|
|
436
|
+
|
|
437
|
+
// Reset input fields
|
|
438
|
+
this.el.inputEl.value = '';
|
|
439
|
+
this.el.inputEl.style.height = 'auto';
|
|
440
|
+
if (this.el.titleInputEl) {
|
|
441
|
+
this.el.titleInputEl.value = '';
|
|
442
|
+
this.el.titleInputEl.classList.remove('required-alert');
|
|
443
|
+
this.el.titleInputEl.placeholder = 'Title (optional, required for new topic)...';
|
|
444
|
+
}
|
|
445
|
+
this.isNewTopicMode = false;
|
|
446
|
+
this.updatePreviews();
|
|
447
|
+
this.updateInputPlaceholder();
|
|
448
|
+
|
|
449
|
+
// Mutate context ibGib rel8ns if available
|
|
450
|
+
if (this.ibGib) {
|
|
451
|
+
if (!this.ibGib.rel8ns) {
|
|
452
|
+
this.ibGib.rel8ns = {};
|
|
453
|
+
}
|
|
454
|
+
if (!this.ibGib.rel8ns.chat) {
|
|
455
|
+
this.ibGib.rel8ns.chat = [];
|
|
456
|
+
}
|
|
457
|
+
this.ibGib.rel8ns.chat.push(newMsg.addr);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
this.renderMessages();
|
|
461
|
+
|
|
462
|
+
// Scroll to bottom
|
|
463
|
+
if (this.el.messagesContainerEl) {
|
|
464
|
+
this.el.messagesContainerEl.scrollTop = this.el.messagesContainerEl.scrollHeight;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Notify
|
|
468
|
+
this.dispatchEvent(new CustomEvent('space-gib-chat-message-sent', {
|
|
469
|
+
bubbles: true,
|
|
470
|
+
composed: true,
|
|
471
|
+
detail: {
|
|
472
|
+
message: newMsg,
|
|
473
|
+
contextAddr: addr,
|
|
474
|
+
}
|
|
475
|
+
}));
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
private drillDown(detail: ChatDrillDownEventDetail): void {
|
|
479
|
+
const lc = `${this.lc}[${this.drillDown.name}]`;
|
|
480
|
+
this.dispatchEvent(new CustomEvent<ChatDrillDownEventDetail>(EVENT_SPACE_GIB_CHAT_DRILL_DOWN, {
|
|
481
|
+
bubbles: true,
|
|
482
|
+
composed: true,
|
|
483
|
+
detail,
|
|
484
|
+
}));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
private renderMessages(): void {
|
|
488
|
+
if (!this.el?.messagesListEl || !this.el?.emptyStateEl || !this.el?.messageCountEl) return;
|
|
489
|
+
|
|
490
|
+
const count = this.messages.length;
|
|
491
|
+
this.el.messageCountEl.textContent = `${count} ${count === 1 ? 'message' : 'messages'}`;
|
|
492
|
+
|
|
493
|
+
if (count === 0) {
|
|
494
|
+
this.el.emptyStateEl.style.display = 'flex';
|
|
495
|
+
this.el.messagesListEl.innerHTML = '';
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
this.el.emptyStateEl.style.display = 'none';
|
|
500
|
+
this.el.messagesListEl.innerHTML = this.messages.map(msg => `
|
|
501
|
+
<div class="chat-comment-card" data-addr="${this.escapeHtml(msg.addr)}">
|
|
502
|
+
<div class="comment-header">
|
|
503
|
+
<div class="author-info">
|
|
504
|
+
<span class="avatar">${msg.avatar || '👤'}</span>
|
|
505
|
+
<span class="author-name">${this.escapeHtml(msg.author)}</span>
|
|
506
|
+
${msg.role ? `<span class="role-badge role-${msg.role.toLowerCase()}">${msg.role}</span>` : ''}
|
|
507
|
+
${msg.isEncrypted ? `<span class="encrypted-card-badge" title="Encrypted with ${this.escapeHtml(msg.cipherAlgo || 'AES')}">🔒 ${this.escapeHtml(msg.cipherAlgo || 'AES')}</span>` : ''}
|
|
508
|
+
</div>
|
|
509
|
+
<div class="header-meta">
|
|
510
|
+
${msg.anchor ? `<span class="anchor-badge" title="Code Anchor">📌 ${this.escapeHtml(msg.anchor)}</span>` : ''}
|
|
511
|
+
<span class="timestamp">${msg.timestamp}</span>
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
<div class="comment-body">
|
|
515
|
+
${msg.title ? `<h4 class="comment-title-header" ${msg.isEncrypted ? 'style="font-family: monospace; color: #34d399;"' : ''}>${this.escapeHtml(msg.title)}</h4>` : ''}
|
|
516
|
+
<p class="comment-text" ${msg.isEncrypted ? 'style="font-family: monospace; color: #34d399; word-break: break-all;"' : ''}>${this.escapeHtml(msg.text)}</p>
|
|
517
|
+
</div>
|
|
518
|
+
<div class="comment-actions">
|
|
519
|
+
<button class="btn-drill-down" data-addr="${this.escapeHtml(msg.addr)}" title="Drill down into this comment's nested chat">
|
|
520
|
+
<span class="drill-icon">↳</span>
|
|
521
|
+
<span class="drill-label">${msg.replyCount !== undefined && msg.replyCount > 0 ? `${msg.replyCount} ${msg.replyCount === 1 ? 'reply' : 'replies'}` : 'Reply / Drill In'}</span>
|
|
522
|
+
<span class="drill-arrow">🔍</span>
|
|
523
|
+
</button>
|
|
524
|
+
</div>
|
|
525
|
+
</div>
|
|
526
|
+
`).join('');
|
|
527
|
+
|
|
528
|
+
// Bind drill-down buttons
|
|
529
|
+
const cards = this.el.messagesListEl.querySelectorAll('.chat-comment-card');
|
|
530
|
+
cards.forEach(card => {
|
|
531
|
+
const addr = card.getAttribute('data-addr');
|
|
532
|
+
const msg = this.messages.find(m => m.addr === addr);
|
|
533
|
+
if (!msg) { return; }
|
|
534
|
+
|
|
535
|
+
const drillBtn = card.querySelector('.btn-drill-down');
|
|
536
|
+
drillBtn?.addEventListener('click', (e) => {
|
|
537
|
+
e.stopPropagation();
|
|
538
|
+
this.drillDown({
|
|
539
|
+
addr: msg.addr,
|
|
540
|
+
text: msg.text,
|
|
541
|
+
author: msg.author,
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// Clicking card also triggers drill down
|
|
546
|
+
card.addEventListener('click', () => {
|
|
547
|
+
this.drillDown({
|
|
548
|
+
addr: msg.addr,
|
|
549
|
+
text: msg.text,
|
|
550
|
+
author: msg.author,
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
private escapeHtml(text: string): string {
|
|
557
|
+
const div = document.createElement('div');
|
|
558
|
+
div.textContent = text;
|
|
559
|
+
return div.innerHTML;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getComponentSvc } from "@ibgib/web-gib/dist/ui/component/ibgib-component-service.mjs";
|
|
2
|
+
import { NestedChatComponentMeta } from "./nested-chat.mjs";
|
|
3
|
+
import { registerChatComponent } from "./chat/index.mjs";
|
|
4
|
+
|
|
5
|
+
export * from "./nested-chat.mjs";
|
|
6
|
+
export * from "./nested-chat-constants.mjs";
|
|
7
|
+
export * from "./nested-chat-types.mjs";
|
|
8
|
+
export * from "./chat/index.mjs";
|
|
9
|
+
|
|
10
|
+
export async function registerNestedChatComponent(): Promise<void> {
|
|
11
|
+
const componentSvc = await getComponentSvc();
|
|
12
|
+
registerChatComponent();
|
|
13
|
+
componentSvc.registerComponentMeta(new NestedChatComponentMeta());
|
|
14
|
+
}
|