@syntrologie/adapt-chatbot 2.8.0-canary.417 → 2.8.0-canary.418
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/AdaptiveChatBar.js +2 -2
- package/dist/AdaptiveChatTrail.anchor-scroll.test.d.ts +2 -0
- package/dist/AdaptiveChatTrail.anchor-scroll.test.d.ts.map +1 -0
- package/dist/AdaptiveChatTrail.d.ts +43 -0
- package/dist/AdaptiveChatTrail.d.ts.map +1 -1
- package/dist/AdaptiveChatTrail.js +2 -2
- package/dist/ChatAssistantLit.js +1 -1
- package/dist/{chunk-LTG5G6WN.js → chunk-LSJFN2BR.js} +185 -15
- package/dist/chunk-LSJFN2BR.js.map +7 -0
- package/dist/{chunk-5PW6T3QF.js → chunk-QF7ZTKXA.js} +106 -23
- package/dist/chunk-QF7ZTKXA.js.map +7 -0
- package/dist/runtime.js +2 -2
- package/dist/schema.d.ts +18 -18
- package/package.json +1 -1
- package/dist/chunk-5PW6T3QF.js.map +0 -7
- package/dist/chunk-LTG5G6WN.js.map +0 -7
package/dist/AdaptiveChatBar.js
CHANGED
|
@@ -2,8 +2,8 @@ import {
|
|
|
2
2
|
AdaptiveChatBar
|
|
3
3
|
} from "./chunk-2NX6765N.js";
|
|
4
4
|
import "./chunk-EWPPVPJ4.js";
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-LSJFN2BR.js";
|
|
6
|
+
import "./chunk-QF7ZTKXA.js";
|
|
7
7
|
import "./chunk-UVKRO5ER.js";
|
|
8
8
|
export {
|
|
9
9
|
AdaptiveChatBar
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdaptiveChatTrail.anchor-scroll.test.d.ts","sourceRoot":"","sources":["../src/AdaptiveChatTrail.anchor-scroll.test.ts"],"names":[],"mappings":"AA0BA,OAAO,qBAAqB,CAAC"}
|
|
@@ -171,6 +171,9 @@ export declare class AdaptiveChatTrail extends LitElement {
|
|
|
171
171
|
mountInlineWidget: {
|
|
172
172
|
attribute: boolean;
|
|
173
173
|
};
|
|
174
|
+
_messagesBelow: {
|
|
175
|
+
state: boolean;
|
|
176
|
+
};
|
|
174
177
|
};
|
|
175
178
|
messages: TrailMessage[];
|
|
176
179
|
tileReceipts: TileReceipt[];
|
|
@@ -192,6 +195,46 @@ export declare class AdaptiveChatTrail extends LitElement {
|
|
|
192
195
|
* automatically when the first real message arrives.
|
|
193
196
|
*/
|
|
194
197
|
greeting: string | undefined;
|
|
198
|
+
/** Follow the bottom on content growth. On by default (history
|
|
199
|
+
* browsing); off from a new user message until the visitor opts back in
|
|
200
|
+
* (pill tap or a user-gesture scroll that reaches the bottom). */
|
|
201
|
+
private _stickToBottom;
|
|
202
|
+
/** The user message the current turn is anchored to. */
|
|
203
|
+
private _anchorMsgId;
|
|
204
|
+
/** Whether a turn anchor is live (drives expand-mid-turn landing). */
|
|
205
|
+
private _anchorActive;
|
|
206
|
+
/** Keep enforcing chip-at-top on updates (late geometry, mode flips)
|
|
207
|
+
* until the user scrolls or opts into following. */
|
|
208
|
+
private _anchorHold;
|
|
209
|
+
/** Number of messages whose BOTTOM edge is still below the viewport —
|
|
210
|
+
* messages the visitor hasn't scrolled past yet. Drives the
|
|
211
|
+
* "⌄ N messages" pill and stays accurate as they scroll (one long
|
|
212
|
+
* streaming reply counts as 1 until its end has been read). Reactive. */
|
|
213
|
+
private _messagesBelow;
|
|
214
|
+
/** Timestamp of the last user scroll gesture (wheel/touch): scroll
|
|
215
|
+
* events outside this window are programmatic (our own pin/anchor) and
|
|
216
|
+
* must not re-engage following — anchoring the newest message clamps AT
|
|
217
|
+
* the bottom in any longer conversation. */
|
|
218
|
+
private _lastUserScrollIntentTs;
|
|
219
|
+
private static readonly _AT_BOTTOM_EPSILON_PX;
|
|
220
|
+
private static readonly _USER_SCROLL_INTENT_WINDOW_MS;
|
|
221
|
+
private get _scrollable();
|
|
222
|
+
private _trailContainer;
|
|
223
|
+
private _isAtBottom;
|
|
224
|
+
/** Count chips whose bottom edge hasn't entered the viewport yet. */
|
|
225
|
+
private _countMessagesBelow;
|
|
226
|
+
private _syncContentBelow;
|
|
227
|
+
private _markUserScrollIntent;
|
|
228
|
+
private _onTrailScroll;
|
|
229
|
+
/** Pill tap: jump to the newest content and follow for the rest of the turn. */
|
|
230
|
+
private _onScrollToRecent;
|
|
231
|
+
/** Scroll so the anchored user chip sits at the top of the trail
|
|
232
|
+
* viewport. Idempotent — safe to re-apply on every update while the
|
|
233
|
+
* hold is live (content grows BELOW the anchor, so the target is
|
|
234
|
+
* stable; re-applying also covers late geometry, e.g. expanding
|
|
235
|
+
* mid-turn from the ambient strip). */
|
|
236
|
+
private _applyAnchor;
|
|
237
|
+
private _pinToBottom;
|
|
195
238
|
private _onExpand;
|
|
196
239
|
/**
|
|
197
240
|
* Approve a pending client tool call. The trail emits a generic
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdaptiveChatTrail.d.ts","sourceRoot":"","sources":["../src/AdaptiveChatTrail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAa,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAIrD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACpE;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,6GAA6G;IAC7G,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;IAC5C,+EAA+E;IAC/E,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;CAC7B;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GACvE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC3B;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,IAAI,CAAC;AAyC1D,qBAAa,iBAAkB,SAAQ,UAAU;IAsB/C,OAAgB,MAAM,0BA8FpB;IAEF,OAAgB,UAAU;;;;;;;;;;;;;QAKxB;;;0EAGkE;;;;QAElE;;;;;;;WAOG;;;;QAEH;;;;;;WAMG;;;;QAEH;;;;;;;WAOG;;;;QAEH;;;;WAIG;;;;QAEH;;;;WAIG;;;;QAEH;;;WAGG
|
|
1
|
+
{"version":3,"file":"AdaptiveChatTrail.d.ts","sourceRoot":"","sources":["../src/AdaptiveChatTrail.ts"],"names":[],"mappings":"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"}
|
package/dist/ChatAssistantLit.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
renderMarkdown
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QF7ZTKXA.js";
|
|
4
4
|
|
|
5
5
|
// src/AdaptiveChatTrail.ts
|
|
6
6
|
import { css, html, LitElement, nothing } from "lit";
|
|
@@ -13,7 +13,7 @@ var OPACITY_FLOOR = 0.18;
|
|
|
13
13
|
var Y_DRIFT_PX = -2;
|
|
14
14
|
var TRAIL_BLUR_VEIL_PX = 72;
|
|
15
15
|
var TRAIL_BLUR_MAX_PX = 5;
|
|
16
|
-
var
|
|
16
|
+
var _AdaptiveChatTrail = class _AdaptiveChatTrail extends LitElement {
|
|
17
17
|
constructor() {
|
|
18
18
|
super(...arguments);
|
|
19
19
|
this.messages = [];
|
|
@@ -33,6 +33,59 @@ var AdaptiveChatTrail = class extends LitElement {
|
|
|
33
33
|
* automatically when the first real message arrives.
|
|
34
34
|
*/
|
|
35
35
|
this.greeting = void 0;
|
|
36
|
+
// ── Anchor-scroll model (FEAT-1783535427, shared contract with
|
|
37
|
+
// <syntro-chat>) — applies to the SCROLLABLE modes (expanded /
|
|
38
|
+
// forceExpanded). The ambient peek strip keeps its bottom pin: it is
|
|
39
|
+
// overflow-hidden (not user-scrollable) and the pin is what keeps the
|
|
40
|
+
// latest line visible under the blur veil.
|
|
41
|
+
/** Follow the bottom on content growth. On by default (history
|
|
42
|
+
* browsing); off from a new user message until the visitor opts back in
|
|
43
|
+
* (pill tap or a user-gesture scroll that reaches the bottom). */
|
|
44
|
+
this._stickToBottom = true;
|
|
45
|
+
/** The user message the current turn is anchored to. */
|
|
46
|
+
this._anchorMsgId = null;
|
|
47
|
+
/** Whether a turn anchor is live (drives expand-mid-turn landing). */
|
|
48
|
+
this._anchorActive = false;
|
|
49
|
+
/** Keep enforcing chip-at-top on updates (late geometry, mode flips)
|
|
50
|
+
* until the user scrolls or opts into following. */
|
|
51
|
+
this._anchorHold = false;
|
|
52
|
+
/** Number of messages whose BOTTOM edge is still below the viewport —
|
|
53
|
+
* messages the visitor hasn't scrolled past yet. Drives the
|
|
54
|
+
* "⌄ N messages" pill and stays accurate as they scroll (one long
|
|
55
|
+
* streaming reply counts as 1 until its end has been read). Reactive. */
|
|
56
|
+
this._messagesBelow = 0;
|
|
57
|
+
/** Timestamp of the last user scroll gesture (wheel/touch): scroll
|
|
58
|
+
* events outside this window are programmatic (our own pin/anchor) and
|
|
59
|
+
* must not re-engage following — anchoring the newest message clamps AT
|
|
60
|
+
* the bottom in any longer conversation. */
|
|
61
|
+
this._lastUserScrollIntentTs = 0;
|
|
62
|
+
this._markUserScrollIntent = () => {
|
|
63
|
+
this._lastUserScrollIntentTs = Date.now();
|
|
64
|
+
};
|
|
65
|
+
this._onTrailScroll = () => {
|
|
66
|
+
const container = this._trailContainer();
|
|
67
|
+
if (!container) return;
|
|
68
|
+
const atBottom = this._isAtBottom(container);
|
|
69
|
+
this._messagesBelow = this._scrollable && !atBottom ? this._countMessagesBelow(container) : 0;
|
|
70
|
+
const isUserDriven = Date.now() - this._lastUserScrollIntentTs < _AdaptiveChatTrail._USER_SCROLL_INTENT_WINDOW_MS;
|
|
71
|
+
if (isUserDriven) {
|
|
72
|
+
this._anchorHold = false;
|
|
73
|
+
this._stickToBottom = atBottom;
|
|
74
|
+
if (atBottom) this._anchorActive = false;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
/** Pill tap: jump to the newest content and follow for the rest of the turn. */
|
|
78
|
+
this._onScrollToRecent = () => {
|
|
79
|
+
this._stickToBottom = true;
|
|
80
|
+
this._anchorHold = false;
|
|
81
|
+
this._anchorActive = false;
|
|
82
|
+
requestAnimationFrame(() => {
|
|
83
|
+
const container = this._trailContainer();
|
|
84
|
+
if (!container) return;
|
|
85
|
+
container.scrollTop = container.scrollHeight;
|
|
86
|
+
this._syncContentBelow();
|
|
87
|
+
});
|
|
88
|
+
};
|
|
36
89
|
// No `createRenderRoot()` override → default open shadow root. CSS
|
|
37
90
|
// custom properties (--sc-tile-text-color, --sc-content-bubble-*,
|
|
38
91
|
// etc.) inherit through the shadow boundary, so canvas-level theming
|
|
@@ -143,6 +196,55 @@ var AdaptiveChatTrail = class extends LitElement {
|
|
|
143
196
|
);
|
|
144
197
|
};
|
|
145
198
|
}
|
|
199
|
+
get _scrollable() {
|
|
200
|
+
return this.expanded || this.forceExpanded;
|
|
201
|
+
}
|
|
202
|
+
_trailContainer() {
|
|
203
|
+
return this.renderRoot.querySelector("[data-syntro-chat-trail]");
|
|
204
|
+
}
|
|
205
|
+
_isAtBottom(container) {
|
|
206
|
+
return container.scrollHeight - container.scrollTop - container.clientHeight <= _AdaptiveChatTrail._AT_BOTTOM_EPSILON_PX;
|
|
207
|
+
}
|
|
208
|
+
/** Count chips whose bottom edge hasn't entered the viewport yet. */
|
|
209
|
+
_countMessagesBelow(container) {
|
|
210
|
+
const viewportBottom = container.getBoundingClientRect().top + container.clientHeight;
|
|
211
|
+
let count = 0;
|
|
212
|
+
for (const chip of this.renderRoot.querySelectorAll("[data-trail-chip]")) {
|
|
213
|
+
if (chip.getBoundingClientRect().bottom > viewportBottom + 4) count += 1;
|
|
214
|
+
}
|
|
215
|
+
return count > 0 ? count : 1;
|
|
216
|
+
}
|
|
217
|
+
_syncContentBelow() {
|
|
218
|
+
const container = this._trailContainer();
|
|
219
|
+
if (!container) return;
|
|
220
|
+
this._messagesBelow = this._scrollable && !this._isAtBottom(container) ? this._countMessagesBelow(container) : 0;
|
|
221
|
+
}
|
|
222
|
+
/** Scroll so the anchored user chip sits at the top of the trail
|
|
223
|
+
* viewport. Idempotent — safe to re-apply on every update while the
|
|
224
|
+
* hold is live (content grows BELOW the anchor, so the target is
|
|
225
|
+
* stable; re-applying also covers late geometry, e.g. expanding
|
|
226
|
+
* mid-turn from the ambient strip). */
|
|
227
|
+
_applyAnchor() {
|
|
228
|
+
requestAnimationFrame(() => {
|
|
229
|
+
const container = this._trailContainer();
|
|
230
|
+
if (!container || this._anchorMsgId === null) return;
|
|
231
|
+
const chip = this.renderRoot.querySelector(
|
|
232
|
+
`[data-trail-chip-id="${String(this._anchorMsgId)}"]`
|
|
233
|
+
);
|
|
234
|
+
if (!chip) return;
|
|
235
|
+
const top = chip.getBoundingClientRect().top - container.getBoundingClientRect().top + container.scrollTop;
|
|
236
|
+
container.scrollTop = top;
|
|
237
|
+
this._syncContentBelow();
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
_pinToBottom() {
|
|
241
|
+
requestAnimationFrame(() => {
|
|
242
|
+
const container = this._trailContainer();
|
|
243
|
+
if (!container) return;
|
|
244
|
+
container.scrollTop = container.scrollHeight;
|
|
245
|
+
this._syncContentBelow();
|
|
246
|
+
});
|
|
247
|
+
}
|
|
146
248
|
/**
|
|
147
249
|
* Whether to render the thinking-dots chip after the last message.
|
|
148
250
|
* True iff a request is in flight AND the assistant hasn't started
|
|
@@ -245,15 +347,36 @@ var AdaptiveChatTrail = class extends LitElement {
|
|
|
245
347
|
entry.propsTarget.props = w.props;
|
|
246
348
|
}
|
|
247
349
|
updated(changed) {
|
|
248
|
-
if (changed.has("expanded")
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
350
|
+
if (!changed.has("expanded") && !changed.has("forceExpanded") && !changed.has("messages")) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (changed.has("messages")) {
|
|
354
|
+
let lastUserIdx = -1;
|
|
355
|
+
for (let i = this.messages.length - 1; i >= 0; i--) {
|
|
356
|
+
if (this.messages[i].role === "user") {
|
|
357
|
+
lastUserIdx = i;
|
|
358
|
+
break;
|
|
255
359
|
}
|
|
256
|
-
}
|
|
360
|
+
}
|
|
361
|
+
const lastUser = lastUserIdx >= 0 ? this.messages[lastUserIdx] : void 0;
|
|
362
|
+
const isLiveTurn = lastUser !== void 0 && this.messages.slice(lastUserIdx + 1).every((m) => m.role !== "user" && m.status === "streaming");
|
|
363
|
+
if (lastUser && isLiveTurn && lastUser.id !== this._anchorMsgId) {
|
|
364
|
+
this._anchorMsgId = lastUser.id;
|
|
365
|
+
this._anchorActive = true;
|
|
366
|
+
this._anchorHold = true;
|
|
367
|
+
this._stickToBottom = false;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (!this._scrollable) {
|
|
371
|
+
this._pinToBottom();
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (this._anchorActive && this._anchorHold) {
|
|
375
|
+
this._applyAnchor();
|
|
376
|
+
} else if (this._stickToBottom) {
|
|
377
|
+
this._pinToBottom();
|
|
378
|
+
} else {
|
|
379
|
+
requestAnimationFrame(() => this._syncContentBelow());
|
|
257
380
|
}
|
|
258
381
|
}
|
|
259
382
|
/** Render a set of inline widgets (reco carousel etc.) as
|
|
@@ -507,7 +630,13 @@ var AdaptiveChatTrail = class extends LitElement {
|
|
|
507
630
|
style=${styleMap(moreStyles())}
|
|
508
631
|
>${hidden > 0 ? html`↑ ${hidden} more · expand` : html`↑ expand`}</button>`}
|
|
509
632
|
<div data-syntro-chat-trail-frame style=${styleMap(frameStyles)}>
|
|
510
|
-
<div
|
|
633
|
+
<div
|
|
634
|
+
data-syntro-chat-trail
|
|
635
|
+
style=${styleMap(containerStyles)}
|
|
636
|
+
@scroll=${this._onTrailScroll}
|
|
637
|
+
@wheel=${this._markUserScrollIntent}
|
|
638
|
+
@touchmove=${this._markUserScrollIntent}
|
|
639
|
+
>
|
|
511
640
|
${visible.map((m, i) => {
|
|
512
641
|
const pos = this.expanded || this.forceExpanded ? 0 : visible.length - 1 - i;
|
|
513
642
|
const state = chipVisualState(m);
|
|
@@ -529,6 +658,7 @@ var AdaptiveChatTrail = class extends LitElement {
|
|
|
529
658
|
} : chipStyles(state, pos);
|
|
530
659
|
return html`<div
|
|
531
660
|
data-trail-chip
|
|
661
|
+
data-trail-chip-id=${String(m.id)}
|
|
532
662
|
data-role=${m.role}
|
|
533
663
|
data-status=${m.status ?? "complete"}
|
|
534
664
|
style=${styleMap(chipStyle)}
|
|
@@ -626,6 +756,18 @@ var AdaptiveChatTrail = class extends LitElement {
|
|
|
626
756
|
aria-hidden="true"
|
|
627
757
|
style=${styleMap(blurVeilStyles())}
|
|
628
758
|
></div>`}
|
|
759
|
+
${// "Scroll to most recent" pill (FEAT-1783535427): shown only in
|
|
760
|
+
// the scrollable modes when content extends below the viewport —
|
|
761
|
+
// the visitor is reading the reply top-down and this is the
|
|
762
|
+
// one-tap opt-in back to the newest content. Absolute inside the
|
|
763
|
+
// positioning frame so it floats over the scroller's bottom edge.
|
|
764
|
+
this._scrollable && this._messagesBelow > 0 ? html`<button
|
|
765
|
+
type="button"
|
|
766
|
+
data-trail-scroll-recent
|
|
767
|
+
aria-label="Scroll to most recent — ${this._messagesBelow} ${this._messagesBelow === 1 ? "message" : "messages"} below"
|
|
768
|
+
@click=${this._onScrollToRecent}
|
|
769
|
+
style=${styleMap(scrollRecentPillStyles())}
|
|
770
|
+
><span aria-hidden="true" style="font-size:11px;line-height:1;">⌄</span><span>${this._messagesBelow} ${this._messagesBelow === 1 ? "message" : "messages"}</span></button>` : nothing}
|
|
629
771
|
</div>
|
|
630
772
|
`;
|
|
631
773
|
}
|
|
@@ -651,7 +793,7 @@ var AdaptiveChatTrail = class extends LitElement {
|
|
|
651
793
|
// workaround. As a bonus the markdown reset rules can't leak out
|
|
652
794
|
// onto the host page, and the keyframes are owned by the trail's
|
|
653
795
|
// own tree scope where the chip + dot elements live.
|
|
654
|
-
|
|
796
|
+
_AdaptiveChatTrail.styles = css`
|
|
655
797
|
[data-syntro-chat-trail] > :first-child {
|
|
656
798
|
margin-top: auto;
|
|
657
799
|
}
|
|
@@ -746,7 +888,7 @@ AdaptiveChatTrail.styles = css`
|
|
|
746
888
|
}
|
|
747
889
|
}
|
|
748
890
|
`;
|
|
749
|
-
|
|
891
|
+
_AdaptiveChatTrail.properties = {
|
|
750
892
|
messages: { attribute: false },
|
|
751
893
|
visibleCount: { type: Number },
|
|
752
894
|
expanded: { type: Boolean },
|
|
@@ -798,8 +940,36 @@ AdaptiveChatTrail.properties = {
|
|
|
798
940
|
* Registry-backed mounter for inline widgets whose id is not their
|
|
799
941
|
* custom-element tag. Injected from the chat-bar mountable.
|
|
800
942
|
*/
|
|
801
|
-
mountInlineWidget: { attribute: false }
|
|
943
|
+
mountInlineWidget: { attribute: false },
|
|
944
|
+
_messagesBelow: { state: true }
|
|
802
945
|
};
|
|
946
|
+
_AdaptiveChatTrail._AT_BOTTOM_EPSILON_PX = 50;
|
|
947
|
+
_AdaptiveChatTrail._USER_SCROLL_INTENT_WINDOW_MS = 1200;
|
|
948
|
+
var AdaptiveChatTrail = _AdaptiveChatTrail;
|
|
949
|
+
function scrollRecentPillStyles() {
|
|
950
|
+
return {
|
|
951
|
+
position: "absolute",
|
|
952
|
+
bottom: "8px",
|
|
953
|
+
left: "50%",
|
|
954
|
+
transform: "translateX(-50%)",
|
|
955
|
+
display: "flex",
|
|
956
|
+
alignItems: "center",
|
|
957
|
+
gap: "5px",
|
|
958
|
+
height: "26px",
|
|
959
|
+
padding: "0 10px",
|
|
960
|
+
borderRadius: "9999px",
|
|
961
|
+
border: "var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))",
|
|
962
|
+
background: "var(--sc-content-bubble-bg, rgba(20, 22, 28, 0.85))",
|
|
963
|
+
color: "var(--sc-tile-text-color, rgba(255, 255, 255, 0.92))",
|
|
964
|
+
fontSize: "12px",
|
|
965
|
+
fontWeight: "500",
|
|
966
|
+
lineHeight: "1",
|
|
967
|
+
whiteSpace: "nowrap",
|
|
968
|
+
cursor: "pointer",
|
|
969
|
+
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.25)",
|
|
970
|
+
zIndex: "3"
|
|
971
|
+
};
|
|
972
|
+
}
|
|
803
973
|
function blurVeilStyles() {
|
|
804
974
|
const mask = "linear-gradient(to bottom, black 0%, black 40%, transparent 100%)";
|
|
805
975
|
return {
|
|
@@ -1330,4 +1500,4 @@ export {
|
|
|
1330
1500
|
AdaptiveChatTrail,
|
|
1331
1501
|
looksLikeSerializedModelResponse
|
|
1332
1502
|
};
|
|
1333
|
-
//# sourceMappingURL=chunk-
|
|
1503
|
+
//# sourceMappingURL=chunk-LSJFN2BR.js.map
|