@carbon/ai-chat-components 0.8.0 → 0.9.0
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/README.md +1 -0
- package/custom-elements.json +917 -258
- package/es/components/card/src/card-footer.scss.js +1 -1
- package/es/components/card/src/card-steps.scss.js +1 -1
- package/es/components/card/src/card.scss.js +1 -1
- package/es/components/chat-button/src/chat-button.scss.js +1 -1
- package/es/components/chat-shell/index.d.ts +2 -2
- package/es/components/chat-shell/index.js +2 -2
- package/es/components/chat-shell/src/{cds-aichat-panel.d.ts → panel.d.ts} +11 -2
- package/es/components/chat-shell/src/{cds-aichat-panel.js → panel.js} +79 -26
- package/es/components/chat-shell/src/panel.js.map +1 -0
- package/es/components/chat-shell/src/panel.scss.js +13 -0
- package/es/components/chat-shell/src/panel.scss.js.map +1 -0
- package/es/components/chat-shell/src/{cds-aichat-shell.d.ts → shell.d.ts} +20 -3
- package/es/components/chat-shell/src/{cds-aichat-shell.js → shell.js} +298 -97
- package/es/components/chat-shell/src/shell.js.map +1 -0
- package/es/components/chat-shell/src/shell.scss.js +13 -0
- package/es/components/chat-shell/src/shell.scss.js.map +1 -0
- package/es/components/chat-shell/src/workspace-manager-utils.d.ts +90 -0
- package/es/components/chat-shell/src/workspace-manager-utils.js +120 -0
- package/es/components/chat-shell/src/workspace-manager-utils.js.map +1 -0
- package/es/components/chat-shell/src/workspace-manager.d.ts +52 -8
- package/es/components/chat-shell/src/workspace-manager.js +330 -117
- package/es/components/chat-shell/src/workspace-manager.js.map +1 -1
- package/es/components/code-snippet/src/code-snippet.d.ts +27 -11
- package/es/components/code-snippet/src/code-snippet.scss.js +1 -1
- package/es/components/markdown/src/markdown.js +1 -1
- package/es/components/markdown/src/markdown.js.map +1 -1
- package/es/components/toolbar/src/toolbar.d.ts +4 -0
- package/es/components/toolbar/src/toolbar.js +62 -28
- package/es/components/toolbar/src/toolbar.js.map +1 -1
- package/es/components/toolbar/src/toolbar.scss.js +1 -1
- package/es/components/workspace-shell/src/workspace-shell-footer.js +6 -1
- package/es/components/workspace-shell/src/workspace-shell-footer.js.map +1 -1
- package/es/components/workspace-shell/src/workspace-shell-footer.scss.js +1 -1
- package/es/components/workspace-shell/src/workspace-shell.d.ts +0 -1
- package/es/components/workspace-shell/src/workspace-shell.js +0 -6
- package/es/components/workspace-shell/src/workspace-shell.js.map +1 -1
- package/es/components/workspace-shell/src/workspace-shell.scss.js +1 -1
- package/es/react/chat-shell.d.ts +3 -3
- package/es/react/chat-shell.js +4 -4
- package/es/react/chat-shell.js.map +1 -1
- package/es/react/panel.d.ts +3 -3
- package/es/react/panel.js +5 -4
- package/es/react/panel.js.map +1 -1
- package/es/react/toolbar.js +1 -1
- package/es/react/toolbar.js.map +1 -1
- package/es-custom/components/card/src/card-footer.scss.js +1 -1
- package/es-custom/components/card/src/card-steps.scss.js +1 -1
- package/es-custom/components/card/src/card.scss.js +1 -1
- package/es-custom/components/chat-button/src/chat-button.scss.js +1 -1
- package/es-custom/components/chat-shell/index.d.ts +2 -2
- package/es-custom/components/chat-shell/index.js +2 -2
- package/es-custom/components/chat-shell/src/{cds-aichat-panel.d.ts → panel.d.ts} +11 -2
- package/es-custom/components/chat-shell/src/{cds-aichat-panel.js → panel.js} +79 -26
- package/es-custom/components/chat-shell/src/panel.js.map +1 -0
- package/es-custom/components/chat-shell/src/panel.scss.js +13 -0
- package/es-custom/components/chat-shell/src/panel.scss.js.map +1 -0
- package/es-custom/components/chat-shell/src/{cds-aichat-shell.d.ts → shell.d.ts} +20 -3
- package/es-custom/components/chat-shell/src/{cds-aichat-shell.js → shell.js} +298 -97
- package/es-custom/components/chat-shell/src/shell.js.map +1 -0
- package/es-custom/components/chat-shell/src/shell.scss.js +13 -0
- package/es-custom/components/chat-shell/src/shell.scss.js.map +1 -0
- package/es-custom/components/chat-shell/src/workspace-manager-utils.d.ts +90 -0
- package/es-custom/components/chat-shell/src/workspace-manager-utils.js +120 -0
- package/es-custom/components/chat-shell/src/workspace-manager-utils.js.map +1 -0
- package/es-custom/components/chat-shell/src/workspace-manager.d.ts +52 -8
- package/es-custom/components/chat-shell/src/workspace-manager.js +330 -117
- package/es-custom/components/chat-shell/src/workspace-manager.js.map +1 -1
- package/es-custom/components/code-snippet/src/code-snippet.d.ts +27 -11
- package/es-custom/components/code-snippet/src/code-snippet.scss.js +1 -1
- package/es-custom/components/markdown/src/markdown.js +1 -1
- package/es-custom/components/markdown/src/markdown.js.map +1 -1
- package/es-custom/components/toolbar/src/toolbar.d.ts +4 -0
- package/es-custom/components/toolbar/src/toolbar.js +62 -28
- package/es-custom/components/toolbar/src/toolbar.js.map +1 -1
- package/es-custom/components/toolbar/src/toolbar.scss.js +1 -1
- package/es-custom/components/workspace-shell/src/workspace-shell-footer.js +6 -1
- package/es-custom/components/workspace-shell/src/workspace-shell-footer.js.map +1 -1
- package/es-custom/components/workspace-shell/src/workspace-shell-footer.scss.js +1 -1
- package/es-custom/components/workspace-shell/src/workspace-shell.d.ts +0 -1
- package/es-custom/components/workspace-shell/src/workspace-shell.js +0 -6
- package/es-custom/components/workspace-shell/src/workspace-shell.js.map +1 -1
- package/es-custom/components/workspace-shell/src/workspace-shell.scss.js +1 -1
- package/es-custom/react/chat-shell.d.ts +3 -3
- package/es-custom/react/chat-shell.js +4 -4
- package/es-custom/react/chat-shell.js.map +1 -1
- package/es-custom/react/panel.d.ts +3 -3
- package/es-custom/react/panel.js +5 -4
- package/es-custom/react/panel.js.map +1 -1
- package/es-custom/react/toolbar.js +1 -1
- package/es-custom/react/toolbar.js.map +1 -1
- package/package.json +13 -10
- package/es/components/chat-shell/src/cds-aichat-panel.js.map +0 -1
- package/es/components/chat-shell/src/cds-aichat-panel.scss.js +0 -13
- package/es/components/chat-shell/src/cds-aichat-panel.scss.js.map +0 -1
- package/es/components/chat-shell/src/cds-aichat-shell.js.map +0 -1
- package/es/components/chat-shell/src/cds-aichat-shell.scss.js +0 -13
- package/es/components/chat-shell/src/cds-aichat-shell.scss.js.map +0 -1
- package/es-custom/components/chat-shell/src/cds-aichat-panel.js.map +0 -1
- package/es-custom/components/chat-shell/src/cds-aichat-panel.scss.js +0 -13
- package/es-custom/components/chat-shell/src/cds-aichat-panel.scss.js.map +0 -1
- package/es-custom/components/chat-shell/src/cds-aichat-shell.js.map +0 -1
- package/es-custom/components/chat-shell/src/cds-aichat-shell.scss.js +0 -13
- package/es-custom/components/chat-shell/src/cds-aichat-shell.scss.js.map +0 -1
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import throttle from 'lodash-es/throttle';
|
|
8
|
+
import throttle from 'lodash-es/throttle.js';
|
|
9
|
+
import { getInlineSizeFromEntry, shouldSkipWorkspaceUpdate, calculateRequiredWidth, isWideEnough, canHostGrow, hasSignificantWidthChange, areWorkspaceAttributesCorrect, getCssLengthFromProperty } from './workspace-manager-utils.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @license
|
|
@@ -18,6 +19,8 @@ import throttle from 'lodash-es/throttle';
|
|
|
18
19
|
const WORKSPACE_MIN_WIDTH_FALLBACK = 640;
|
|
19
20
|
const MESSAGES_MIN_WIDTH_FALLBACK = 320;
|
|
20
21
|
const HISTORY_WIDTH_FALLBACK = 320;
|
|
22
|
+
const EXPANSION_POLL_INTERVAL_MS = 200;
|
|
23
|
+
const EXPANSION_THRESHOLD_PX = 1;
|
|
21
24
|
/**
|
|
22
25
|
* Manages workspace layout, responsive behavior, and transitions for cds-aichat-shell.
|
|
23
26
|
* Handles switching between inline and panel modes based on available width,
|
|
@@ -32,7 +35,9 @@ class WorkspaceManager {
|
|
|
32
35
|
inPanel: false,
|
|
33
36
|
contentVisible: true,
|
|
34
37
|
containerVisible: false,
|
|
38
|
+
isCheckingExpansion: false,
|
|
35
39
|
isExpanding: false,
|
|
40
|
+
isCheckingContracting: false,
|
|
36
41
|
isContracting: false,
|
|
37
42
|
};
|
|
38
43
|
this.lastKnownCssValues = {};
|
|
@@ -45,7 +50,6 @@ class WorkspaceManager {
|
|
|
45
50
|
*/
|
|
46
51
|
connect() {
|
|
47
52
|
if (this.config.showWorkspace) {
|
|
48
|
-
this.observeHostWidth();
|
|
49
53
|
this.handleShowWorkspaceEnabled();
|
|
50
54
|
}
|
|
51
55
|
this.observeCssProperties();
|
|
@@ -60,7 +64,7 @@ class WorkspaceManager {
|
|
|
60
64
|
window.removeEventListener("resize", this.windowResizeHandler);
|
|
61
65
|
}
|
|
62
66
|
this.clearExpansionTimers();
|
|
63
|
-
this.
|
|
67
|
+
this.clearContractionTimers();
|
|
64
68
|
this.cssPropertyObserver?.disconnect();
|
|
65
69
|
}
|
|
66
70
|
/**
|
|
@@ -83,7 +87,7 @@ class WorkspaceManager {
|
|
|
83
87
|
// Handle showWorkspace changes
|
|
84
88
|
if (newConfig.showWorkspace !== undefined) {
|
|
85
89
|
if (newConfig.showWorkspace && !oldConfig.showWorkspace) {
|
|
86
|
-
|
|
90
|
+
// handleShowWorkspaceEnabled will call observeHostWidth at the right time
|
|
87
91
|
this.handleShowWorkspaceEnabled();
|
|
88
92
|
}
|
|
89
93
|
else if (!newConfig.showWorkspace && oldConfig.showWorkspace) {
|
|
@@ -114,13 +118,41 @@ class WorkspaceManager {
|
|
|
114
118
|
* Check if workspace should be rendered inline (side-by-side with messages).
|
|
115
119
|
*/
|
|
116
120
|
shouldRenderInline() {
|
|
117
|
-
|
|
121
|
+
// During checking phase, optimistically render inline
|
|
122
|
+
if (this.state.isCheckingExpansion) {
|
|
123
|
+
return (this.state.containerVisible &&
|
|
124
|
+
!this.state.isContracting &&
|
|
125
|
+
!this.state.isCheckingContracting);
|
|
126
|
+
}
|
|
127
|
+
// During expanding phase, always render inline (will be hidden with CSS)
|
|
128
|
+
if (this.state.isExpanding) {
|
|
129
|
+
return (this.state.containerVisible &&
|
|
130
|
+
!this.state.isContracting &&
|
|
131
|
+
!this.state.isCheckingContracting);
|
|
132
|
+
}
|
|
133
|
+
// During checking-contracting phase, keep rendering inline to maintain layout
|
|
134
|
+
if (this.state.isCheckingContracting) {
|
|
135
|
+
return this.state.containerVisible;
|
|
136
|
+
}
|
|
137
|
+
// During contracting phase, keep rendering inline to maintain layout
|
|
138
|
+
// Container will be removed from DOM at the very end
|
|
139
|
+
if (this.state.isContracting) {
|
|
140
|
+
return this.state.containerVisible;
|
|
141
|
+
}
|
|
142
|
+
// In stable state, render inline if not in panel mode
|
|
143
|
+
const returnValue = this.state.containerVisible && !this.state.inPanel;
|
|
144
|
+
return returnValue;
|
|
118
145
|
}
|
|
119
146
|
/**
|
|
120
147
|
* Check if workspace should be rendered as a panel (overlay).
|
|
121
148
|
*/
|
|
122
149
|
shouldRenderPanel() {
|
|
123
|
-
return this.state.containerVisible &&
|
|
150
|
+
return (this.state.containerVisible &&
|
|
151
|
+
this.state.inPanel &&
|
|
152
|
+
!this.state.isCheckingExpansion &&
|
|
153
|
+
!this.state.isExpanding &&
|
|
154
|
+
!this.state.isCheckingContracting &&
|
|
155
|
+
!this.state.isContracting);
|
|
124
156
|
}
|
|
125
157
|
// ========== Private Methods ==========
|
|
126
158
|
observeHostWidth() {
|
|
@@ -148,10 +180,13 @@ class WorkspaceManager {
|
|
|
148
180
|
}
|
|
149
181
|
createHostResizeObserver() {
|
|
150
182
|
this.hostResizeObserver = new ResizeObserver((entries) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
183
|
+
// Use requestAnimationFrame to avoid ResizeObserver loop errors
|
|
184
|
+
requestAnimationFrame(() => {
|
|
185
|
+
for (const entry of entries) {
|
|
186
|
+
const inlineSize = getInlineSizeFromEntry(entry);
|
|
187
|
+
this.throttledHandleHostResize(inlineSize);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
155
190
|
});
|
|
156
191
|
this.hostResizeObserver.observe(this.hostElement);
|
|
157
192
|
}
|
|
@@ -167,6 +202,10 @@ class WorkspaceManager {
|
|
|
167
202
|
this.setWorkspaceInPanel(false);
|
|
168
203
|
return;
|
|
169
204
|
}
|
|
205
|
+
// Don't perform initial measurement during expansion/contraction
|
|
206
|
+
if (this.state.isExpanding || this.state.isContracting) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
170
209
|
this.handleHostResize(currentWidth);
|
|
171
210
|
}
|
|
172
211
|
handleHostResize(inlineSize) {
|
|
@@ -174,6 +213,10 @@ class WorkspaceManager {
|
|
|
174
213
|
this.trackExpectedExpansion(inlineSize);
|
|
175
214
|
return;
|
|
176
215
|
}
|
|
216
|
+
if (this.state.isCheckingContracting) {
|
|
217
|
+
this.trackExpectedContraction(inlineSize);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
177
220
|
if (this.state.isContracting) {
|
|
178
221
|
return;
|
|
179
222
|
}
|
|
@@ -183,102 +226,240 @@ class WorkspaceManager {
|
|
|
183
226
|
if (!Number.isFinite(inlineSize)) {
|
|
184
227
|
return;
|
|
185
228
|
}
|
|
186
|
-
if (
|
|
187
|
-
!this.state.containerVisible ||
|
|
188
|
-
this.state.isContracting) {
|
|
229
|
+
if (shouldSkipWorkspaceUpdate(this.config, this.state)) {
|
|
189
230
|
this.setWorkspaceInPanel(false);
|
|
190
231
|
return;
|
|
191
232
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const sideBySideMinWidth =
|
|
233
|
+
// Skip during expansion
|
|
234
|
+
if (this.state.isExpanding) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const dimensions = this.getLayoutDimensions();
|
|
238
|
+
const sideBySideMinWidth = calculateRequiredWidth(dimensions);
|
|
198
239
|
const shouldBeInPanel = inlineSize < sideBySideMinWidth;
|
|
199
240
|
this.setWorkspaceInPanel(shouldBeInPanel);
|
|
200
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Get layout dimensions from cache or compute if not available.
|
|
244
|
+
* Uses cached CSS values for performance, falling back to live computation.
|
|
245
|
+
*/
|
|
246
|
+
getLayoutDimensions() {
|
|
247
|
+
// Ensure cache is populated
|
|
248
|
+
if (!this.lastKnownCssValues.workspaceMinWidth) {
|
|
249
|
+
this.updateLastKnownCssValues();
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
workspaceMinWidth: this.lastKnownCssValues.workspaceMinWidth ??
|
|
253
|
+
WORKSPACE_MIN_WIDTH_FALLBACK,
|
|
254
|
+
messagesMinWidth: this.lastKnownCssValues.messagesMinWidth ?? MESSAGES_MIN_WIDTH_FALLBACK,
|
|
255
|
+
historyWidth: this.config.showHistory
|
|
256
|
+
? (this.lastKnownCssValues.historyWidth ?? HISTORY_WIDTH_FALLBACK)
|
|
257
|
+
: 0,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
201
260
|
handleShowWorkspaceEnabled() {
|
|
202
261
|
// Cancel any ongoing closing animation
|
|
203
|
-
this.
|
|
204
|
-
this.setState({ isContracting: false });
|
|
205
|
-
// Show the workspace container immediately
|
|
206
|
-
this.setShowWorkspaceContainer(true);
|
|
262
|
+
this.clearContractionTimers();
|
|
263
|
+
this.setState({ isCheckingContracting: false, isContracting: false });
|
|
207
264
|
const inlineSize = this.hostElement.getBoundingClientRect().width;
|
|
208
265
|
const requiredWidth = this.getRequiredMinWidth();
|
|
209
266
|
// Scenario 1: Already wide enough - show immediately
|
|
210
|
-
if (
|
|
211
|
-
this.
|
|
267
|
+
if (isWideEnough(inlineSize, requiredWidth)) {
|
|
268
|
+
this.initializeImmediateDisplay("container", inlineSize);
|
|
212
269
|
return;
|
|
213
270
|
}
|
|
214
271
|
// Scenario 2: Host can't ever reach required size - go straight to panel
|
|
215
|
-
if (!
|
|
216
|
-
this.
|
|
272
|
+
if (!canHostGrow(requiredWidth)) {
|
|
273
|
+
this.initializeImmediateDisplay("panel", inlineSize);
|
|
217
274
|
return;
|
|
218
275
|
}
|
|
219
276
|
// Scenario 3: Expecting expansion - setup tracking
|
|
277
|
+
this.initializeExpansionTracking();
|
|
278
|
+
}
|
|
279
|
+
initializeImmediateDisplay(mode, inlineSize) {
|
|
280
|
+
const attribute = mode === "container" ? "workspace-in-container" : "workspace-in-panel";
|
|
281
|
+
// Pre-set workspace attribute to prevent layout flash
|
|
282
|
+
this.hostElement.setAttribute(attribute, "");
|
|
283
|
+
// Show the workspace container immediately
|
|
284
|
+
this.setShowWorkspaceContainer(true);
|
|
285
|
+
this.observeHostWidth();
|
|
286
|
+
this.finalizeImmediateDisplay(inlineSize, mode === "panel");
|
|
287
|
+
}
|
|
288
|
+
initializeExpansionTracking() {
|
|
289
|
+
// Set isCheckingExpansion BEFORE showing container AND observing
|
|
290
|
+
// This allows workspace to render inline but remain invisible while we check for expansion
|
|
291
|
+
this.setState({ isCheckingExpansion: true });
|
|
292
|
+
// Don't pre-set workspace mode - let expansion tracking determine it
|
|
293
|
+
// The workspace-checking class on shell will handle the transition state
|
|
294
|
+
this.setShowWorkspaceContainer(true);
|
|
295
|
+
this.observeHostWidth();
|
|
220
296
|
this.setupExpansionTracking();
|
|
221
297
|
}
|
|
222
298
|
handleShowWorkspaceDisabled() {
|
|
223
|
-
// Step 1:
|
|
224
|
-
|
|
225
|
-
this.setState({
|
|
226
|
-
// Step 2:
|
|
227
|
-
// Store the current value so it doesn't change during closing
|
|
228
|
-
const wasInPanel = this.state.inPanel;
|
|
229
|
-
// Step 3: Immediately hide the workspace content (opacity goes to 0 instantly)
|
|
299
|
+
// Step 1: Clear any ongoing expansion tracking first
|
|
300
|
+
this.clearExpansionTimers();
|
|
301
|
+
this.setState({ isCheckingExpansion: false, isExpanding: false });
|
|
302
|
+
// Step 2: Immediately hide the workspace content (opacity goes to 0 instantly)
|
|
230
303
|
this.setWorkspaceContentVisible(false);
|
|
231
|
-
// Step
|
|
232
|
-
|
|
233
|
-
|
|
304
|
+
// Step 3: Check if we need to track contraction
|
|
305
|
+
const currentWidth = this.hostElement.getBoundingClientRect().width;
|
|
306
|
+
const requiredWidth = this.getRequiredMinWidth();
|
|
307
|
+
// If host is currently wide enough for inline workspace, expect contraction
|
|
308
|
+
if (isWideEnough(currentWidth, requiredWidth) &&
|
|
309
|
+
canHostGrow(requiredWidth)) {
|
|
310
|
+
this.setupContractionTracking();
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
// No contraction expected, go straight to closing
|
|
314
|
+
this.initializeImmediateClosing();
|
|
234
315
|
}
|
|
235
|
-
// Step 5: Clear any ongoing expansion tracking
|
|
236
|
-
this.clearExpansionTimers();
|
|
237
|
-
this.setState({ isExpanding: false });
|
|
238
|
-
// Step 6: Disconnect host resize observer to prevent interference during closing
|
|
239
|
-
this.hostResizeObserver?.disconnect();
|
|
240
|
-
this.hostResizeObserver = undefined;
|
|
241
|
-
// Step 7: Poll to check if host has finished contracting
|
|
242
|
-
// Keep the workspace container visible (but empty) during this time
|
|
243
|
-
// to maintain its width and prevent messages area from expanding
|
|
244
|
-
this.clearClosingTimer();
|
|
245
|
-
this.closingLastInlineSize = this.hostElement.getBoundingClientRect().width;
|
|
246
|
-
this.startClosingPolling();
|
|
247
316
|
}
|
|
248
317
|
startExpansionPolling() {
|
|
249
|
-
const initialWidth = this.expansionLastInlineSize;
|
|
250
|
-
|
|
318
|
+
const initialWidth = this.expansionLastInlineSize ?? 0;
|
|
319
|
+
const hasSetContainerMode = { value: false };
|
|
251
320
|
this.expansionCheckInterval = window.setInterval(() => {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (currentWidth === this.expansionLastInlineSize) {
|
|
255
|
-
// If we never saw any movement from initial width, go to panel mode
|
|
256
|
-
const sawMovement = currentWidth !== initialWidth;
|
|
257
|
-
this.finishWorkspaceExpansion(sawMovement);
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
this.expansionLastInlineSize = currentWidth;
|
|
261
|
-
}
|
|
262
|
-
}, 100);
|
|
321
|
+
this.checkExpansionProgress(initialWidth, hasSetContainerMode);
|
|
322
|
+
}, EXPANSION_POLL_INTERVAL_MS);
|
|
263
323
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
324
|
+
/**
|
|
325
|
+
* Check expansion progress and determine if transition is complete.
|
|
326
|
+
*/
|
|
327
|
+
checkExpansionProgress(initialWidth, hasSetContainerMode) {
|
|
328
|
+
const currentWidth = this.hostElement.getBoundingClientRect().width;
|
|
329
|
+
if (currentWidth === this.expansionLastInlineSize) {
|
|
330
|
+
// Width stabilized - expansion complete
|
|
331
|
+
const sawMovement = hasSignificantWidthChange(currentWidth, initialWidth, EXPANSION_THRESHOLD_PX);
|
|
332
|
+
this.finishWorkspaceExpansion(sawMovement);
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
// Width still changing - track ongoing expansion
|
|
336
|
+
this.handleOngoingExpansion(currentWidth, initialWidth, hasSetContainerMode);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Handle ongoing width changes during expansion.
|
|
341
|
+
*/
|
|
342
|
+
handleOngoingExpansion(currentWidth, initialWidth, hasSetContainerMode) {
|
|
343
|
+
// Set workspace-in-container mode on first detected meaningful movement
|
|
344
|
+
if (!hasSetContainerMode.value &&
|
|
345
|
+
hasSignificantWidthChange(currentWidth, initialWidth, EXPANSION_THRESHOLD_PX)) {
|
|
346
|
+
// Transition from checking to confirmed expanding
|
|
347
|
+
this.setState({ isCheckingExpansion: false, isExpanding: true });
|
|
348
|
+
this.setWorkspaceInContainerMode();
|
|
349
|
+
hasSetContainerMode.value = true;
|
|
350
|
+
}
|
|
351
|
+
this.expansionLastInlineSize = currentWidth;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Set workspace to container mode by updating DOM attributes.
|
|
355
|
+
*/
|
|
356
|
+
setWorkspaceInContainerMode() {
|
|
357
|
+
this.hostElement.removeAttribute("workspace-in-panel");
|
|
358
|
+
this.hostElement.setAttribute("workspace-in-container", "");
|
|
359
|
+
}
|
|
360
|
+
setupContractionTracking() {
|
|
361
|
+
// Set isCheckingContracting BEFORE clearing attributes
|
|
362
|
+
// This allows workspace to stay inline while we check for contraction
|
|
363
|
+
this.setState({ isCheckingContracting: true });
|
|
364
|
+
// Store initial width for comparison
|
|
365
|
+
this.contractionInitialInlineSize =
|
|
366
|
+
this.hostElement.getBoundingClientRect().width;
|
|
367
|
+
this.contractionLastInlineSize = this.contractionInitialInlineSize;
|
|
368
|
+
// Keep observing host width to detect contraction
|
|
369
|
+
// Don't disconnect the observer - we need it to track contraction
|
|
370
|
+
this.startContractionPolling();
|
|
371
|
+
}
|
|
372
|
+
initializeImmediateClosing() {
|
|
373
|
+
// Mark as contracting and proceed directly to closing
|
|
374
|
+
this.setState({ isContracting: true });
|
|
375
|
+
// Disconnect host resize observer
|
|
376
|
+
this.hostResizeObserver?.disconnect();
|
|
377
|
+
this.hostResizeObserver = undefined;
|
|
378
|
+
// Immediately finish closing (will clear attributes)
|
|
379
|
+
this.finishWorkspaceClosing();
|
|
380
|
+
}
|
|
381
|
+
startContractionPolling() {
|
|
382
|
+
const initialWidth = this.contractionInitialInlineSize ?? 0;
|
|
383
|
+
const hasSetContractingMode = { value: false };
|
|
384
|
+
this.contractionCheckInterval = window.setInterval(() => {
|
|
385
|
+
this.checkContractionProgress(initialWidth, hasSetContractingMode);
|
|
386
|
+
}, EXPANSION_POLL_INTERVAL_MS);
|
|
387
|
+
}
|
|
388
|
+
checkContractionProgress(initialWidth, hasSetContractingMode) {
|
|
389
|
+
const currentWidth = this.hostElement.getBoundingClientRect().width;
|
|
390
|
+
if (currentWidth === this.contractionLastInlineSize) {
|
|
391
|
+
// Width stabilized - contraction complete
|
|
392
|
+
const sawMovement = hasSignificantWidthChange(currentWidth, initialWidth, EXPANSION_THRESHOLD_PX);
|
|
393
|
+
this.finishWorkspaceContraction(sawMovement);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
// Width still changing - track ongoing contraction
|
|
397
|
+
this.handleOngoingContraction(currentWidth, initialWidth, hasSetContractingMode);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
handleOngoingContraction(currentWidth, initialWidth, hasSetContractingMode) {
|
|
401
|
+
// Set contracting mode on first detected meaningful shrinkage
|
|
402
|
+
if (!hasSetContractingMode.value &&
|
|
403
|
+
hasSignificantWidthChange(currentWidth, initialWidth, EXPANSION_THRESHOLD_PX) &&
|
|
404
|
+
currentWidth < initialWidth) {
|
|
405
|
+
// Transition from checking to confirmed contracting
|
|
406
|
+
// DON'T clear workspace attributes yet - keep them to maintain layout
|
|
407
|
+
this.setState({ isCheckingContracting: false, isContracting: true });
|
|
408
|
+
hasSetContractingMode.value = true;
|
|
409
|
+
}
|
|
410
|
+
this.contractionLastInlineSize = currentWidth;
|
|
411
|
+
}
|
|
412
|
+
finishWorkspaceContraction(sawMovement) {
|
|
413
|
+
// Clear checking-contracting flag and timers
|
|
414
|
+
this.setState({ isCheckingContracting: false });
|
|
415
|
+
this.clearContractionTimers();
|
|
416
|
+
if (!sawMovement) {
|
|
417
|
+
// No contraction happened, safe to close immediately
|
|
418
|
+
this.setState({ isContracting: true });
|
|
419
|
+
this.hostResizeObserver?.disconnect();
|
|
420
|
+
this.hostResizeObserver = undefined;
|
|
421
|
+
// Clear attributes and finish closing
|
|
422
|
+
this.finishWorkspaceClosing();
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
// Contraction happened, need to wait for host to finish contracting
|
|
426
|
+
// before removing workspace from DOM
|
|
427
|
+
this.setState({ isContracting: true });
|
|
428
|
+
// DON'T clear workspace attributes yet - keep them to maintain layout
|
|
429
|
+
// They will be cleared in finishWorkspaceClosing()
|
|
430
|
+
// Disconnect host resize observer to prevent interference
|
|
431
|
+
this.hostResizeObserver?.disconnect();
|
|
432
|
+
this.hostResizeObserver = undefined;
|
|
433
|
+
// Start polling to detect when host has finished contracting
|
|
434
|
+
this.contractionLastInlineSize =
|
|
435
|
+
this.hostElement.getBoundingClientRect().width;
|
|
436
|
+
this.startFinalContractionPolling();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
startFinalContractionPolling() {
|
|
440
|
+
this.contractionCheckInterval = window.setInterval(() => {
|
|
267
441
|
const currentWidth = this.hostElement.getBoundingClientRect().width;
|
|
268
|
-
// If width hasn't changed, the
|
|
269
|
-
if (currentWidth === this.
|
|
442
|
+
// If width hasn't changed, the host has finished contracting
|
|
443
|
+
if (currentWidth === this.contractionLastInlineSize) {
|
|
270
444
|
this.finishWorkspaceClosing();
|
|
271
445
|
}
|
|
272
446
|
else {
|
|
273
|
-
this.
|
|
447
|
+
this.contractionLastInlineSize = currentWidth;
|
|
274
448
|
}
|
|
275
|
-
},
|
|
449
|
+
}, EXPANSION_POLL_INTERVAL_MS);
|
|
450
|
+
}
|
|
451
|
+
trackExpectedContraction(inlineSize) {
|
|
452
|
+
if (!Number.isFinite(inlineSize)) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
// Update the last known size - the polling interval will detect when it stops changing
|
|
456
|
+
this.contractionLastInlineSize = inlineSize;
|
|
276
457
|
}
|
|
277
458
|
finishWorkspaceExpansion(sawMovement) {
|
|
278
459
|
const inlineSize = this.expansionLastInlineSize ??
|
|
279
460
|
this.hostElement.getBoundingClientRect().width;
|
|
280
|
-
// Clear
|
|
281
|
-
this.setState({ isExpanding: false });
|
|
461
|
+
// Clear both checking and expanding flags and timers
|
|
462
|
+
this.setState({ isCheckingExpansion: false, isExpanding: false });
|
|
282
463
|
this.clearExpansionTimers();
|
|
283
464
|
// Determine the correct panel state BEFORE showing content
|
|
284
465
|
if (!sawMovement) {
|
|
@@ -291,15 +472,27 @@ class WorkspaceManager {
|
|
|
291
472
|
this.setWorkspaceContentVisible(true);
|
|
292
473
|
}
|
|
293
474
|
finishWorkspaceClosing() {
|
|
294
|
-
//
|
|
475
|
+
// IMPORTANT: Remove workspace container from DOM FIRST while attributes are still present
|
|
476
|
+
// This prevents input-and-messages from expanding while workspace is still in DOM
|
|
295
477
|
this.setShowWorkspaceContainer(false);
|
|
296
|
-
//
|
|
297
|
-
this.
|
|
298
|
-
this.
|
|
478
|
+
// Now clear attributes AFTER container is removed from DOM
|
|
479
|
+
this.hostElement.removeAttribute("workspace-in-panel");
|
|
480
|
+
this.hostElement.removeAttribute("workspace-in-container");
|
|
481
|
+
// Reset workspace state to original values
|
|
482
|
+
this.setState({
|
|
483
|
+
inPanel: false,
|
|
484
|
+
contentVisible: true,
|
|
485
|
+
containerVisible: false,
|
|
486
|
+
isCheckingExpansion: false,
|
|
487
|
+
isExpanding: false,
|
|
488
|
+
isCheckingContracting: false,
|
|
489
|
+
isContracting: false,
|
|
490
|
+
});
|
|
299
491
|
// Clear the timers
|
|
300
|
-
this.
|
|
492
|
+
this.clearContractionTimers();
|
|
301
493
|
// Reset tracking
|
|
302
|
-
this.
|
|
494
|
+
this.contractionLastInlineSize = undefined;
|
|
495
|
+
this.contractionInitialInlineSize = undefined;
|
|
303
496
|
}
|
|
304
497
|
trackExpectedExpansion(inlineSize) {
|
|
305
498
|
if (!Number.isFinite(inlineSize)) {
|
|
@@ -315,18 +508,56 @@ class WorkspaceManager {
|
|
|
315
508
|
}
|
|
316
509
|
this.expansionLastInlineSize = undefined;
|
|
317
510
|
}
|
|
318
|
-
|
|
319
|
-
if (this.
|
|
320
|
-
clearInterval(this.
|
|
321
|
-
this.
|
|
511
|
+
clearContractionTimers() {
|
|
512
|
+
if (this.contractionCheckInterval) {
|
|
513
|
+
clearInterval(this.contractionCheckInterval);
|
|
514
|
+
this.contractionCheckInterval = undefined;
|
|
322
515
|
}
|
|
516
|
+
this.contractionLastInlineSize = undefined;
|
|
517
|
+
this.contractionInitialInlineSize = undefined;
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Updates workspace DOM attributes to match the panel state.
|
|
521
|
+
* Maintains inverse relationship between panel and container attributes.
|
|
522
|
+
*
|
|
523
|
+
* @param inPanel - True for panel mode, false for container mode
|
|
524
|
+
*/
|
|
525
|
+
updateWorkspaceAttributes(inPanel) {
|
|
526
|
+
this.hostElement.toggleAttribute("workspace-in-panel", inPanel);
|
|
527
|
+
// workspace-in-container is the inverse of workspace-in-panel
|
|
528
|
+
this.hostElement.toggleAttribute("workspace-in-container", !inPanel);
|
|
323
529
|
}
|
|
530
|
+
/**
|
|
531
|
+
* Updates the workspace panel state and corresponding DOM attributes.
|
|
532
|
+
*
|
|
533
|
+
* Synchronizes internal state with DOM attributes that control whether
|
|
534
|
+
* the workspace is displayed as an overlay panel or inline container.
|
|
535
|
+
*
|
|
536
|
+
* Ignores calls during expansion/contraction transitions. Only updates if
|
|
537
|
+
* state or attributes need changes. Maintains inverse relationship between
|
|
538
|
+
* panel and container attributes.
|
|
539
|
+
*
|
|
540
|
+
* @param inPanel - True to display workspace as overlay panel, false for inline
|
|
541
|
+
*/
|
|
324
542
|
setWorkspaceInPanel(inPanel) {
|
|
325
|
-
|
|
543
|
+
// Early exit during transitions
|
|
544
|
+
if (this.state.isExpanding ||
|
|
545
|
+
this.state.isCheckingContracting ||
|
|
546
|
+
this.state.isContracting) {
|
|
326
547
|
return;
|
|
327
548
|
}
|
|
328
|
-
this.
|
|
329
|
-
this.hostElement
|
|
549
|
+
const stateChanged = this.state.inPanel !== inPanel;
|
|
550
|
+
const attributesCorrect = areWorkspaceAttributesCorrect(this.hostElement, inPanel);
|
|
551
|
+
// Nothing to do if state and attributes are already correct
|
|
552
|
+
if (!stateChanged && attributesCorrect) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
// Update state if needed
|
|
556
|
+
if (stateChanged) {
|
|
557
|
+
this.setState({ inPanel });
|
|
558
|
+
}
|
|
559
|
+
// Update attributes
|
|
560
|
+
this.updateWorkspaceAttributes(inPanel);
|
|
330
561
|
this.requestHostUpdate();
|
|
331
562
|
}
|
|
332
563
|
setWorkspaceContentVisible(visible) {
|
|
@@ -348,6 +579,8 @@ class WorkspaceManager {
|
|
|
348
579
|
this.updateShellClasses();
|
|
349
580
|
}
|
|
350
581
|
updateShellClasses() {
|
|
582
|
+
this.shellRoot.classList.toggle("workspace-checking", this.state.isCheckingExpansion);
|
|
583
|
+
this.shellRoot.classList.toggle("workspace-checking-closing", this.state.isCheckingContracting);
|
|
351
584
|
this.shellRoot.classList.toggle("workspace-closing", this.state.isContracting);
|
|
352
585
|
this.shellRoot.classList.toggle("workspace-opening", this.state.isExpanding);
|
|
353
586
|
}
|
|
@@ -358,19 +591,13 @@ class WorkspaceManager {
|
|
|
358
591
|
}
|
|
359
592
|
}
|
|
360
593
|
getRequiredMinWidth() {
|
|
361
|
-
const workspaceMinWidth = this.
|
|
362
|
-
const messagesMinWidth = this.
|
|
594
|
+
const workspaceMinWidth = getCssLengthFromProperty(this.hostElement, "--cds-aichat-workspace-min-width", WORKSPACE_MIN_WIDTH_FALLBACK);
|
|
595
|
+
const messagesMinWidth = getCssLengthFromProperty(this.hostElement, "--cds-aichat-messages-min-width", MESSAGES_MIN_WIDTH_FALLBACK);
|
|
363
596
|
const historyWidth = this.config.showHistory
|
|
364
|
-
? this.
|
|
597
|
+
? getCssLengthFromProperty(this.hostElement, "--cds-aichat-history-width", HISTORY_WIDTH_FALLBACK)
|
|
365
598
|
: 0;
|
|
366
599
|
return workspaceMinWidth + messagesMinWidth + historyWidth;
|
|
367
600
|
}
|
|
368
|
-
isWideEnough(inlineSize, requiredWidth) {
|
|
369
|
-
return typeof window === "undefined" || inlineSize >= requiredWidth;
|
|
370
|
-
}
|
|
371
|
-
canHostGrow(requiredWidth) {
|
|
372
|
-
return typeof window !== "undefined" && window.innerWidth >= requiredWidth;
|
|
373
|
-
}
|
|
374
601
|
finalizeImmediateDisplay(inlineSize, usePanel) {
|
|
375
602
|
this.setWorkspaceContentVisible(true);
|
|
376
603
|
this.setState({ isExpanding: false });
|
|
@@ -383,30 +610,15 @@ class WorkspaceManager {
|
|
|
383
610
|
}
|
|
384
611
|
}
|
|
385
612
|
setupExpansionTracking() {
|
|
386
|
-
|
|
613
|
+
// isExpanding is already set in handleShowWorkspaceEnabled
|
|
387
614
|
this.clearExpansionTimers();
|
|
388
|
-
|
|
615
|
+
// Don't set workspace-in-container early - wait to see if expansion actually happens
|
|
616
|
+
// This prevents the flash of container mode CSS when opening directly to panel mode
|
|
389
617
|
this.setWorkspaceContentVisible(false);
|
|
390
618
|
this.expansionLastInlineSize =
|
|
391
619
|
this.hostElement.getBoundingClientRect().width;
|
|
392
620
|
this.startExpansionPolling();
|
|
393
621
|
}
|
|
394
|
-
getInlineSizeFromEntry(entry) {
|
|
395
|
-
const borderBoxSize = Array.isArray(entry.borderBoxSize)
|
|
396
|
-
? entry.borderBoxSize[0]
|
|
397
|
-
: entry.borderBoxSize;
|
|
398
|
-
return borderBoxSize?.inlineSize ?? entry.contentRect.width;
|
|
399
|
-
}
|
|
400
|
-
getCssLengthFromProperty(propertyName, fallback) {
|
|
401
|
-
const value = getComputedStyle(this.hostElement)
|
|
402
|
-
.getPropertyValue(propertyName)
|
|
403
|
-
.trim();
|
|
404
|
-
if (!value) {
|
|
405
|
-
return fallback;
|
|
406
|
-
}
|
|
407
|
-
const parsed = Number.parseFloat(value);
|
|
408
|
-
return Number.isNaN(parsed) ? fallback : parsed;
|
|
409
|
-
}
|
|
410
622
|
/**
|
|
411
623
|
* Observe CSS custom properties that affect workspace layout.
|
|
412
624
|
* When these properties change, recalculate workspace positioning.
|
|
@@ -431,18 +643,18 @@ class WorkspaceManager {
|
|
|
431
643
|
*/
|
|
432
644
|
updateLastKnownCssValues() {
|
|
433
645
|
this.lastKnownCssValues = {
|
|
434
|
-
workspaceMinWidth: this.
|
|
435
|
-
messagesMinWidth: this.
|
|
436
|
-
historyWidth: this.
|
|
646
|
+
workspaceMinWidth: getCssLengthFromProperty(this.hostElement, "--cds-aichat-workspace-min-width", WORKSPACE_MIN_WIDTH_FALLBACK),
|
|
647
|
+
messagesMinWidth: getCssLengthFromProperty(this.hostElement, "--cds-aichat-messages-min-width", MESSAGES_MIN_WIDTH_FALLBACK),
|
|
648
|
+
historyWidth: getCssLengthFromProperty(this.hostElement, "--cds-aichat-history-width", HISTORY_WIDTH_FALLBACK),
|
|
437
649
|
};
|
|
438
650
|
}
|
|
439
651
|
/**
|
|
440
652
|
* Check if any relevant CSS properties have changed and trigger recalculation.
|
|
441
653
|
*/
|
|
442
654
|
checkCssPropertyChanges() {
|
|
443
|
-
const workspaceMinWidth = this.
|
|
444
|
-
const messagesMinWidth = this.
|
|
445
|
-
const historyWidth = this.
|
|
655
|
+
const workspaceMinWidth = getCssLengthFromProperty(this.hostElement, "--cds-aichat-workspace-min-width", WORKSPACE_MIN_WIDTH_FALLBACK);
|
|
656
|
+
const messagesMinWidth = getCssLengthFromProperty(this.hostElement, "--cds-aichat-messages-min-width", MESSAGES_MIN_WIDTH_FALLBACK);
|
|
657
|
+
const historyWidth = getCssLengthFromProperty(this.hostElement, "--cds-aichat-history-width", HISTORY_WIDTH_FALLBACK);
|
|
446
658
|
const hasChanged = workspaceMinWidth !== this.lastKnownCssValues.workspaceMinWidth ||
|
|
447
659
|
messagesMinWidth !== this.lastKnownCssValues.messagesMinWidth ||
|
|
448
660
|
historyWidth !== this.lastKnownCssValues.historyWidth;
|
|
@@ -452,6 +664,7 @@ class WorkspaceManager {
|
|
|
452
664
|
if (this.config.showWorkspace &&
|
|
453
665
|
this.state.containerVisible &&
|
|
454
666
|
!this.state.isExpanding &&
|
|
667
|
+
!this.state.isCheckingContracting &&
|
|
455
668
|
!this.state.isContracting) {
|
|
456
669
|
const currentWidth = this.hostElement.getBoundingClientRect().width;
|
|
457
670
|
this.updateWorkspaceInPanelState(currentWidth);
|