@syntrologie/adapt-chatbot 2.8.0-canary.327 → 2.8.0-canary.329
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 +1 -1
- package/dist/AdaptiveChatTrail.d.ts.map +1 -1
- package/dist/AdaptiveChatTrail.js +1 -1
- package/dist/{chunk-D2AF7DDD.js → chunk-JTZ36YJM.js} +43 -18
- package/dist/chunk-JTZ36YJM.js.map +7 -0
- package/dist/runtime.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-D2AF7DDD.js.map +0 -7
package/dist/AdaptiveChatBar.js
CHANGED
|
@@ -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;CACrE;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;AAqBD,qBAAa,iBAAkB,SAAQ,UAAU;IAsB/C,OAAgB,MAAM,0BA8FpB;IAEF,OAAgB,UAAU;;;;;;;;;;;;;QAKxB;;;0EAGkE;;;;QAElE;;;;;;;WAOG;;;;QAEH;;;;;;WAMG;;;;QAEH;;;;;;;WAOG;;;;MAEH;IAEF,QAAQ,EAAE,YAAY,EAAE,CAAM;IAC9B,YAAY,SAAmB;IAC/B,QAAQ,UAAS;IACjB,aAAa,UAAS;IACtB,QAAQ,UAAS;IACjB,YAAY,SAAM;IAClB,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAa;IAC3E;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAa;IAQzC,OAAO,CAAC,SAAS,CAMf;IAEF;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB,CAStB;IAEF,OAAO,CAAC,WAAW,CAMjB;IAEF;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;IASlB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAyBrD,OAAO,CAAC,uBAAuB,CAU7B;IAEO,MAAM;
|
|
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;CACrE;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;AAqBD,qBAAa,iBAAkB,SAAQ,UAAU;IAsB/C,OAAgB,MAAM,0BA8FpB;IAEF,OAAgB,UAAU;;;;;;;;;;;;;QAKxB;;;0EAGkE;;;;QAElE;;;;;;;WAOG;;;;QAEH;;;;;;WAMG;;;;QAEH;;;;;;;WAOG;;;;MAEH;IAEF,QAAQ,EAAE,YAAY,EAAE,CAAM;IAC9B,YAAY,SAAmB;IAC/B,QAAQ,UAAS;IACjB,aAAa,UAAS;IACtB,QAAQ,UAAS;IACjB,YAAY,SAAM;IAClB,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAa;IAC3E;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAa;IAQzC,OAAO,CAAC,SAAS,CAMf;IAEF;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB,CAStB;IAEF,OAAO,CAAC,WAAW,CAMjB;IAEF;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;IASlB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAyBrD,OAAO,CAAC,uBAAuB,CAU7B;IAEO,MAAM;CA4ThB;AA+BD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;CAAE,CAAC;AAwX7D,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,qBAAqB,EAAE,iBAAiB,CAAC;KAC1C;CACF"}
|
|
@@ -102,21 +102,23 @@ var AdaptiveChatTrail = class extends LitElement {
|
|
|
102
102
|
render() {
|
|
103
103
|
if (this.messages.length === 0 && !this.greeting && !this.introSuggestion) return nothing;
|
|
104
104
|
if (this.messages.length === 0 && (this.greeting || this.introSuggestion)) {
|
|
105
|
-
return html`<div data-syntro-chat-trail style=${styleMap({
|
|
105
|
+
return html`<div data-syntro-chat-trail data-state="empty" style=${styleMap({
|
|
106
106
|
display: "flex",
|
|
107
107
|
flexDirection: "column",
|
|
108
|
-
|
|
109
|
-
justifyContent: "
|
|
108
|
+
alignItems: "center",
|
|
109
|
+
justifyContent: "center",
|
|
110
|
+
textAlign: "center",
|
|
111
|
+
gap: "12px",
|
|
112
|
+
flex: "1 1 auto",
|
|
110
113
|
width: "100%",
|
|
111
|
-
padding: "
|
|
114
|
+
padding: "24px 16px",
|
|
112
115
|
pointerEvents: "auto"
|
|
113
116
|
})}>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
data-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
>${unsafeHTML(renderMarkdown(stripTrailingWhitespace(this.greeting)))}</div>` : nothing}
|
|
117
|
+
<div data-trail-empty-icon style=${styleMap(emptyIconStyles())} aria-hidden="true">✦</div>
|
|
118
|
+
${this.greeting ? html`<h2
|
|
119
|
+
data-trail-empty-title
|
|
120
|
+
style=${styleMap(emptyTitleStyles())}
|
|
121
|
+
>${unsafeHTML(renderMarkdown(stripTrailingWhitespace(this.greeting)))}</h2>` : nothing}
|
|
120
122
|
${this.introSuggestion ? html`<button
|
|
121
123
|
type="button"
|
|
122
124
|
data-trail-intro-suggestion
|
|
@@ -689,20 +691,43 @@ function thinkingTextStyles() {
|
|
|
689
691
|
}
|
|
690
692
|
function introSuggestionStyles() {
|
|
691
693
|
return {
|
|
692
|
-
alignSelf: "flex-start",
|
|
693
694
|
display: "inline-flex",
|
|
694
695
|
alignItems: "center",
|
|
695
696
|
gap: "6px",
|
|
696
|
-
|
|
697
|
+
marginTop: "4px",
|
|
698
|
+
padding: "10px 18px",
|
|
697
699
|
borderRadius: "9999px",
|
|
698
|
-
border: "
|
|
699
|
-
background: "var(--sc-
|
|
700
|
-
color: "
|
|
700
|
+
border: "0",
|
|
701
|
+
background: "linear-gradient(135deg, var(--sc-color-primary, #5faf7d) 0%, var(--sc-color-primary-hover, #3d8a5e) 100%)",
|
|
702
|
+
color: "#fff",
|
|
701
703
|
cursor: "pointer",
|
|
702
704
|
font: "inherit",
|
|
703
705
|
fontSize: "14px",
|
|
704
|
-
fontWeight: "
|
|
705
|
-
transition: "
|
|
706
|
+
fontWeight: "600",
|
|
707
|
+
transition: "transform 120ms ease, filter 160ms ease"
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
function emptyIconStyles() {
|
|
711
|
+
return {
|
|
712
|
+
display: "inline-flex",
|
|
713
|
+
alignItems: "center",
|
|
714
|
+
justifyContent: "center",
|
|
715
|
+
width: "48px",
|
|
716
|
+
height: "48px",
|
|
717
|
+
borderRadius: "9999px",
|
|
718
|
+
background: "var(--sc-tile-icon-background, linear-gradient(135deg, var(--sc-color-primary, #5faf7d) 0%, var(--sc-color-primary-hover, #3d8a5e) 100%))",
|
|
719
|
+
color: "#fff",
|
|
720
|
+
fontSize: "22px"
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function emptyTitleStyles() {
|
|
724
|
+
return {
|
|
725
|
+
margin: "0",
|
|
726
|
+
fontSize: "15px",
|
|
727
|
+
fontWeight: "600",
|
|
728
|
+
lineHeight: "1.35",
|
|
729
|
+
color: "var(--sc-content-text-color, inherit)",
|
|
730
|
+
maxWidth: "32ch"
|
|
706
731
|
};
|
|
707
732
|
}
|
|
708
733
|
if (!customElements.get("adaptive-chat-trail")) {
|
|
@@ -712,4 +737,4 @@ if (!customElements.get("adaptive-chat-trail")) {
|
|
|
712
737
|
export {
|
|
713
738
|
AdaptiveChatTrail
|
|
714
739
|
};
|
|
715
|
-
//# sourceMappingURL=chunk-
|
|
740
|
+
//# sourceMappingURL=chunk-JTZ36YJM.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/AdaptiveChatTrail.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * AdaptiveChatTrail \u2014 the \"bubble-up\" message column that sits above the\n * chat bar in the canvas lid.\n *\n * Messages appear immediately above the chat bar and drift upward as\n * newer ones arrive. Each stack position carries an opacity + blur\n * falloff so older messages read as fading from the page. Beyond a\n * `visibleCount` cap (default 3) the trail collapses into an \"N more \u00B7\n * expand\" affordance \u2014 clicking emits `trail-expand`.\n *\n * Shadow DOM (default Lit open root) so chip styles, markdown reset\n * rules, and animation @keyframes are scoped to this element's tree.\n * Theme CSS variables (--sc-tile-text-color, --sc-content-bubble-*,\n * etc.) inherit through the shadow boundary, so canvas-level theming\n * still reaches the chips. The earlier light-DOM render-root override\n * silently broke @keyframes lookup once the trail was hosted inside\n * any shadow root (sc-mount, velvet-mobile, etc.) \u2014 keyframes injected\n * into `document.head` aren't visible from another tree scope.\n *\n * See PRD \u00A74.3 (chat trail) for the canonical motion + falloff spec.\n */\n\nimport { renderMarkdown } from '@syntrologie/chat';\nimport { css, html, LitElement, nothing } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\n\nexport interface TrailToolCall {\n id: string;\n name: string;\n status: 'args-streaming' | 'pending' | 'running' | 'done' | 'error';\n}\n\nexport interface TrailMessage {\n /** Stable identity for keyed rendering (must be unique within the trail). */\n id: number | string;\n role: 'user' | 'assistant' | 'system';\n text: string;\n /** Streaming \u2192 assistant text still arriving from backend. Complete \u2192 final. Error \u2192 fatal during stream. */\n status?: 'streaming' | 'complete' | 'error';\n /** Tool calls attached to an assistant message (rendered as compact chips). */\n toolCalls?: TrailToolCall[];\n}\n\n/** PRD \u00A74.3 constants. */\nconst DEFAULT_VISIBLE = 3;\nconst OPACITY_STEP = 0.22;\nconst OPACITY_FLOOR = 0.18;\nconst Y_DRIFT_PX = -2;\n/**\n * Height of the absolute blur veil at the top of the trail's scroll\n * area. Pixel-based, NOT message-count-based: with the old per-chip\n * `filter: blur(pos * step)` model, a single long markdown response\n * at pos=1 would have its entire body uniformly blurred \u2014 including\n * the chunk visually adjacent to the crisp newest message \u2014 because\n * blur is a property of the chip element, not of pixels. Switching\n * to a fixed-height backdrop-blur veil keeps the bottom of the\n * visible area (newest content) crisp regardless of message length.\n */\nconst TRAIL_BLUR_VEIL_PX = 72;\n/** Max blur applied at the very top of the veil; tapers to 0 at the bottom edge. */\nconst TRAIL_BLUR_MAX_PX = 5;\n\nexport class AdaptiveChatTrail extends LitElement {\n // Trail-local styles. The chat-trail used to render into light DOM\n // (`createRenderRoot() { return this }`) and inject these rules into\n // `document.head` so chip + markdown styles could find their selectors.\n // That broke the moment the trail was hosted inside any shadow root\n // (sc-mount \u2192 custom-canvas \u2192 velvet-mobile \u2192 sc-mount \u2192 chat-bar \u2192\n // chat-trail is the production chain): CSS animations require the\n // @keyframes rule to be in the SAME TreeScope as the element using\n // it, and document-level keyframes don't cross a shadow boundary.\n // Result was silent \u2014 `animation-name` parsed fine, the property got\n // the right resolved value, but the browser never instantiated an\n // Animation object because no matching keyframe was visible from\n // inside the shadow tree. `getAnimations()` returned `[]`, dots sat\n // frozen at base scale, the typing indicator looked dead.\n //\n // Shadow DOM with `static styles` is the right architecture here.\n // CSS custom properties (--sc-tile-text-color, --sc-content-bubble-*,\n // etc.) inherit through shadow boundaries by default, so the canvas\n // theme cascade still reaches the chips without the light-DOM\n // workaround. As a bonus the markdown reset rules can't leak out\n // onto the host page, and the keyframes are owned by the trail's\n // own tree scope where the chip + dot elements live.\n static override styles = css`\n [data-syntro-chat-trail] > :first-child {\n margin-top: auto;\n }\n [data-trail-chip] > p:first-child {\n margin-top: 0;\n }\n [data-trail-chip] > p:last-child {\n margin-bottom: 0;\n }\n [data-trail-chip] p {\n margin: 4px 0;\n }\n [data-trail-chip] br + br {\n display: none;\n }\n [data-trail-chip] ul,\n [data-trail-chip] ol {\n margin: 4px 0;\n padding-left: 1.1em;\n }\n [data-trail-chip] li {\n margin: 0;\n }\n [data-trail-chip] li + li {\n margin-top: 1px;\n }\n [data-trail-chip] pre {\n margin: 6px 0;\n padding: 6px 8px;\n background: var(--sc-content-code-background-block, rgba(0, 0, 0, 0.35));\n border-radius: 6px;\n overflow-x: auto;\n font-size: 11px;\n }\n [data-trail-chip] code {\n font-family: ui-monospace, SF Mono, Menlo, monospace;\n font-size: 11px;\n background: var(--sc-content-code-background, rgba(0, 0, 0, 0.25));\n padding: 1px 4px;\n border-radius: 3px;\n }\n [data-trail-chip] pre code {\n background: transparent;\n padding: 0;\n }\n [data-trail-chip] a {\n color: var(--sc-content-link-color, var(--sc-color-primary, #b72e2a));\n text-decoration: underline;\n }\n [data-trail-chip] strong {\n font-weight: 600;\n }\n /* Streaming-caret blink for in-progress assistant chips. */\n @keyframes syntro-trail-caret {\n 0% {\n opacity: 1;\n }\n 50% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n }\n /* Thinking-dot scale + opacity pulse. The naked-dots redesign\n relies entirely on this motion to signal \"alive\" \u2014 there's no\n chip frame around the dots, so a subtle animation reads as\n broken. 0.4 \u2192 1.0 scale with 0.25 \u2192 1.0 opacity gives an\n unmistakable breathing cue. */\n @keyframes syntro-trail-thinking-pulse {\n 0%,\n 100% {\n transform: scale(0.4);\n opacity: 0.25;\n }\n 45% {\n transform: scale(1);\n opacity: 1;\n }\n }\n /* Chip entrance \u2014 fades a freshly-mounted chip up from below\n so the thinking \u2192 first-streaming-chip handoff feels like one\n continuous motion instead of an element swap. */\n @keyframes syntro-trail-chip-enter {\n from {\n opacity: 0;\n transform: translateY(6px) scale(0.97);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n `;\n\n static override properties = {\n messages: { attribute: false },\n visibleCount: { type: Number },\n expanded: { type: Boolean },\n greeting: { type: String },\n /** When true: trail renders fully expanded AND the\n * expand/collapse affordance is hidden. Used by hosts that mount\n * the chat in a full-screen surface (the mobile panel) where\n * collapse makes no sense \u2014 there's nothing to collapse INTO. */\n forceExpanded: { type: Boolean },\n /**\n * Mirrors `chatSession.inFlight` \u2014 true between `send()` and the\n * assistant's first `receiveStart`/`receiveDelta`, OR while a\n * stream is in progress. Drives the \"thinking\" indicator that\n * fills the gap between the user's just-sent message and the\n * first assistant token. Wired through AdaptiveChatBar (it owns\n * the chatSession subscription and forwards inFlight as a prop).\n */\n inFlight: { type: Boolean },\n /**\n * Live reasoning narration streamed from the model via AG-UI\n * `THINKING_TEXT_MESSAGE_CONTENT`. When non-empty, the thinking\n * chip renders this in place of the bouncing dots so the visitor\n * sees what the model is actually doing instead of staring at\n * silent loading state during a tool-call gap.\n */\n thinkingText: { attribute: false },\n /**\n * Pre-conversation suggestion chip. When the visitor opens the\n * chat and `messages` is empty, the trail renders this as a\n * button beneath the greeting. Clicking it dispatches a\n * `trail-intro-suggestion` event with the configured prompt \u2014\n * the parent (chat-bar) forwards that to chatSession.send().\n * Disappears the moment the first message lands.\n */\n introSuggestion: { attribute: false },\n };\n\n messages: TrailMessage[] = [];\n visibleCount = DEFAULT_VISIBLE;\n expanded = false;\n forceExpanded = false;\n inFlight = false;\n thinkingText = '';\n introSuggestion: { label: string; prompt: string } | undefined = undefined;\n /**\n * Pre-conversation phantom assistant message. Rendered only when\n * `messages` is empty. NOT injected into chatSession state \u2014 the\n * greeting is configuration, not conversation, so it disappears\n * automatically when the first real message arrives.\n */\n greeting: string | undefined = undefined;\n\n // No `createRenderRoot()` override \u2192 default open shadow root. CSS\n // custom properties (--sc-tile-text-color, --sc-content-bubble-*,\n // etc.) inherit through the shadow boundary, so canvas-level theming\n // continues to cascade into the chips without the light-DOM hack.\n // See the `static styles` block above for the in-shadow stylesheet.\n\n private _onExpand = (): void => {\n // Self-manage: clicking the inline link expands the trail in place.\n // We still dispatch the event so any parent that wants to react\n // (telemetry, mirror state to a side panel, etc.) can listen.\n this.expanded = true;\n this.dispatchEvent(new CustomEvent('trail-expand', { bubbles: true, composed: true }));\n };\n\n /**\n * Approve a pending client tool call. The trail emits a generic\n * event so the host can decide whether to forward to chatSession\n * (the common path) or override. Keeps the trail itself free of\n * direct chatSession coupling \u2014 it's a pure view component.\n */\n private _onToolCallClick = (tc: TrailToolCall): void => {\n if (tc.status !== 'pending') return;\n this.dispatchEvent(\n new CustomEvent<{ toolCallId: string; approved: boolean }>('trail-toolcall-approved', {\n detail: { toolCallId: tc.id, approved: true },\n bubbles: true,\n composed: true,\n })\n );\n };\n\n private _onCollapse = (): void => {\n // Mirror of _onExpand for the minimize affordance shown while\n // expanded. Self-manages + dispatches `trail-collapse` for parent\n // observers (telemetry, side-panel mirrors).\n this.expanded = false;\n this.dispatchEvent(new CustomEvent('trail-collapse', { bubbles: true, composed: true }));\n };\n\n /**\n * Whether to render the thinking-dots chip after the last message.\n * True iff a request is in flight AND the assistant hasn't started\n * streaming a reply yet \u2014 once the first delta lands, the streaming\n * chip's caret takes over and the thinking indicator hides so the\n * visitor isn't shown both at once. Also hidden when the last\n * message is an error/system message, because the request that\n * \"errored\" is already over from the user's perspective.\n */\n private _shouldShowThinking(): boolean {\n if (!this.inFlight) return false;\n const last = this.messages[this.messages.length - 1];\n if (!last) return true;\n if (last.role === 'assistant' && last.status === 'streaming') return false;\n if (last.role === 'system' || last.status === 'error') return false;\n return true;\n }\n\n override updated(changed: Map<string, unknown>): void {\n // Anchor the scroll to the bottom whenever:\n // - the trail flips into expanded mode (UX continuity), OR\n // - new messages or text deltas arrive (so a long streaming chip\n // stays pinned to the latest line, not the start of the chip).\n // Skip when the user has manually scrolled UP into history \u2014 we'd\n // rather lose pinning than yank them out of what they're reading.\n if (changed.has('expanded') || changed.has('messages')) {\n requestAnimationFrame(() => {\n const container = this.renderRoot.querySelector<HTMLElement>('[data-syntro-chat-trail]');\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n // 200px tolerance \u2014 streaming deltas can append a paragraph in\n // one update, easily 80-150px. A tighter window would strand\n // the viewer at the top of the chip mid-stream because the\n // first delta blew past the threshold. User-driven scroll-up\n // of >200px disables auto-pin until they scroll back down.\n if (distanceFromBottom < 200 || changed.has('expanded')) {\n container.scrollTop = container.scrollHeight;\n }\n });\n }\n }\n\n private _onIntroSuggestionClick = (): void => {\n const s = this.introSuggestion;\n if (!s) return;\n this.dispatchEvent(\n new CustomEvent<{ prompt: string }>('trail-intro-suggestion', {\n detail: { prompt: s.prompt },\n bubbles: true,\n composed: true,\n })\n );\n };\n\n override render() {\n if (this.messages.length === 0 && !this.greeting && !this.introSuggestion) return nothing;\n if (this.messages.length === 0 && (this.greeting || this.introSuggestion)) {\n // Empty-state hero. Mirrors the platform chats' empty-state\n // pattern (icon disc + title + pill button) instead of pretending\n // the greeting is an assistant message \u2014 that reads as a chat\n // bubble the visitor must reply to. A centered hero reads as\n // configured page chrome: \"this is how the surface starts.\"\n return html`<div data-syntro-chat-trail data-state=\"empty\" style=${styleMap({\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n textAlign: 'center',\n gap: '12px',\n flex: '1 1 auto',\n width: '100%',\n padding: '24px 16px',\n pointerEvents: 'auto',\n })}>\n <div data-trail-empty-icon style=${styleMap(emptyIconStyles())} aria-hidden=\"true\">\u2726</div>\n ${\n this.greeting\n ? html`<h2\n data-trail-empty-title\n style=${styleMap(emptyTitleStyles())}\n >${unsafeHTML(renderMarkdown(stripTrailingWhitespace(this.greeting)))}</h2>`\n : nothing\n }\n ${\n this.introSuggestion\n ? html`<button\n type=\"button\"\n data-trail-intro-suggestion\n @click=${this._onIntroSuggestionClick}\n style=${styleMap(introSuggestionStyles())}\n >${this.introSuggestion.label}</button>`\n : nothing\n }\n </div>`;\n }\n\n // Choose the slice we actually paint. Newest are at the END of the\n // array (closest to the chat bar). When not expanded, take the last\n // `visibleCount`; when expanded, take all.\n const visible =\n this.expanded || this.forceExpanded ? this.messages : this.messages.slice(-this.visibleCount);\n const hidden = this.messages.length - visible.length;\n\n const baseStyles: Record<string, string> = {\n display: 'flex',\n flexDirection: 'column',\n gap: '6px',\n // NO `justifyContent: flex-end` here \u2014 combined with `overflow: auto`\n // it's a known cross-browser bug: oversized flex children get\n // pushed ABOVE the container without contributing to scrollHeight,\n // so the scrollbar never appears. We rely on `:first-child {\n // margin-top: auto }` (injected as global CSS) to push short\n // content to the bottom of the box while leaving overflow handling\n // to the native scroll container.\n width: '100%',\n // The trail itself has no chrome \u2014 chips sit on the host page bg.\n padding: '0',\n pointerEvents: 'auto',\n };\n\n // Expanded mode: cap height, scroll, and add a visible top border so\n // the panel reads as a bounded region. NO background tint or shadow\n // \u2014 the ambient (collapsed) treatment is fine and we don't want to\n // suddenly introduce panel chrome that wasn't there before. The\n // border alone marks the panel's top edge.\n // Falloff is turned off per-chip below \u2014 readability wins once the\n // user has explicitly asked for history. See PRD \u00A74.3.\n // Collapsed mode also gets a (looser) cap + scroll so a single long\n // markdown chip doesn't blow the chat-bar off-screen with no\n // scroll affordance. Tighter than expanded so the bubble-up trail\n // doesn't dominate the surface visually.\n const modeStyles: Record<string, string> = this.forceExpanded\n ? {\n // Full-screen host: fill all available vertical space and\n // scroll the overflow. ``flex: 1 1 0`` (basis 0, not auto)\n // makes the trail shrink to 0 first when the parent runs\n // out of space \u2014 so the header above and the input row\n // below stay pinned at their natural sizes instead of one\n // of them being pushed off-screen.\n flex: '1 1 0',\n minHeight: '0',\n overflowY: 'auto',\n // ``contain`` prevents scroll from bubbling out to the host\n // page when the trail hits top or bottom. Required for the\n // panel-scroll-leak regression test in\n // SyntroBottomSheet.test.ts.\n overscrollBehavior: 'contain',\n paddingTop: '8px',\n borderTop: 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))',\n scrollBehavior: 'smooth',\n scrollbarWidth: 'thin',\n }\n : this.expanded\n ? {\n maxHeight: 'min(320px, 40vh)',\n overflowY: 'auto',\n paddingTop: '8px',\n borderTop: 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))',\n scrollBehavior: 'smooth',\n scrollbarWidth: 'thin',\n }\n : {\n // Collapsed mode = ambient peek surface. Never a scrollbar:\n // long messages slide up behind the blur veil instead of\n // exposing a scrollbar. The \"\u2191 N more \u00B7 expand\" affordance\n // is the only entry into history; clicking it switches to\n // expanded mode where scroll IS allowed.\n // Height kept short so the trail reads as a peek strip above\n // the chat bar, not a panel \u2014 long single replies fade into\n // the blur veil at the top, user expands to read the rest.\n maxHeight: 'min(140px, 22vh)',\n overflow: 'hidden',\n };\n\n const containerStyles = { ...baseStyles, ...modeStyles };\n\n // Wrap the scroll container in a positioning frame so the absolute\n // blur veil (rendered at the top) anchors to the scroll viewport's\n // top edge \u2014 NOT to a chip that scrolls under it. Pixel-anchored\n // blur means: the top TRAIL_BLUR_VEIL_PX of the visible area is\n // veiled regardless of how many messages occupy those pixels.\n // The frame wraps the scroll container so the absolute blur veil\n // anchors to the scroll viewport's top edge. In ``forceExpanded``\n // mode it ALSO has to participate in the flex column laid out by\n // the trail host \u2014 otherwise the scroll container's ``flex: 1 1\n // 0`` has no flex context, falls back to content height, and the\n // trail's overflow:hidden clips at the TOP of the scroll\n // (showing oldest messages, hiding the newest at the bottom).\n const frameStyles: Record<string, string> = this.forceExpanded\n ? {\n position: 'relative',\n width: '100%',\n display: 'flex',\n flexDirection: 'column',\n flex: '1 1 0',\n minHeight: '0',\n }\n : {\n position: 'relative',\n width: '100%',\n };\n\n return html`\n ${\n // Single toggle button above the frame. Same DOM node morphs:\n // collapsed \u2192 \"\u2191 N more \u00B7 expand\" / \"\u2191 expand\" (calls _onExpand)\n // expanded \u2192 \"\u2304 minimize\" (calls _onCollapse)\n // Lives OUTSIDE the frame so the absolute blur veil (top:0,\n // z-index:2 inside the frame) doesn't cover it. Shown whenever\n // there's anything in the trail \u2014 the user expects this affordance\n // available regardless of how many chips are on screen.\n // When ``forceExpanded`` is set by the host (full-screen panel,\n // etc.), there's nowhere to collapse INTO and the affordance\n // would just confuse the user \u2014 suppress it entirely.\n this.messages.length === 0 || this.forceExpanded\n ? nothing\n : this.expanded\n ? html`<button\n type=\"button\"\n data-trail-toggle\n data-trail-collapse\n @click=${this._onCollapse}\n style=${styleMap(moreStyles())}\n >\u2304 minimize</button>`\n : html`<button\n type=\"button\"\n data-trail-toggle\n data-trail-more\n @click=${this._onExpand}\n style=${styleMap(moreStyles())}\n >${hidden > 0 ? html`\u2191 ${hidden} more \u00B7 expand` : html`\u2191 expand`}</button>`\n }\n <div data-syntro-chat-trail-frame style=${styleMap(frameStyles)}>\n <div data-syntro-chat-trail style=${styleMap(containerStyles)}>\n ${visible.map((m, i) => {\n // Stack position from the bar: 0 = closest, N-1 = oldest.\n // visible[last] is closest, so reverse the index from the\n // tail of the visible slice. When expanded, the ambient\n // falloff is suppressed (pos = 0) so every chip reads at\n // full opacity \u2014 explicit history view, not ambient trail.\n const pos = this.expanded || this.forceExpanded ? 0 : visible.length - 1 - i;\n const state = chipVisualState(m);\n const isStreaming = state.kind === 'streaming';\n // Only surface tool-call chips that NEED the visitor (pending\n // = a client-side approval required to proceed). Running /\n // done / error / args-streaming chips are internal mechanics\n // \u2014 exposing them turned the chat trail into a debug\n // surface, made the conversation read as \"look at all this\n // plumbing I'm running\", and bloated short replies with\n // status pills the visitor can't act on. Pending stays\n // because dropping it would strand a tool call that\n // literally cannot proceed without a tap.\n const toolCalls = (m.toolCalls ?? []).filter((tc) => tc.status === 'pending');\n // Assistant text is markdown-formatted by the agent (lists,\n // code, bold, links). renderMarkdown sanitizes via DOMPurify,\n // then unsafeHTML injects the safe HTML. User + system chips\n // stay plain text \u2014 no markdown risk + no expansion attack\n // surface on user-typed input.\n const renderedText =\n m.role === 'assistant'\n ? html`${unsafeHTML(renderMarkdown(stripTrailingWhitespace(m.text)))}`\n : html`${m.text}`;\n // Entrance animation on streaming chips only. Lit mounts the\n // chip element once per message id, so the animation fires\n // when the streaming chip first appears (replacing the\n // thinking chip in the same visual slot) and does NOT fire\n // again on subsequent token deltas \u2014 those just update the\n // text inside the already-mounted element. User messages\n // and settled history don't get an entrance: the user just\n // tapped Send so they expect immediate feedback, and old\n // history doesn't need re-attention every refresh.\n const chipStyle = isStreaming\n ? {\n ...chipStyles(state, pos),\n animation: 'syntro-trail-chip-enter 220ms cubic-bezier(0.22, 1, 0.36, 1) both',\n }\n : chipStyles(state, pos);\n return html`<div\n data-trail-chip\n data-role=${m.role}\n data-status=${m.status ?? 'complete'}\n style=${styleMap(chipStyle)}\n >${renderedText}${\n isStreaming\n ? html`<span\n data-trail-caret\n aria-hidden=\"true\"\n style=${styleMap(caretStyles())}\n ></span>`\n : nothing\n }${\n toolCalls.length > 0\n ? html`<div data-trail-toolcalls style=${styleMap(toolCallStripStyles())}>\n ${toolCalls.map(\n (tc) => html`<button\n type=\"button\"\n data-trail-toolcall\n data-tool-id=${tc.id}\n data-tool-status=${tc.status}\n @click=${() => this._onToolCallClick(tc)}\n ?disabled=${tc.status !== 'pending'}\n style=${styleMap(toolCallChipStyles(tc.status))}\n title=\"${tc.name} \u00B7 ${tc.status}\"\n >${toolCallIcon(tc.status)} ${tc.name}</button>`\n )}\n </div>`\n : nothing\n }</div>`;\n })}\n ${\n // Thinking indicator \u2014 fills the gap between the user's\n // most recent message and the assistant's first token.\n // Shows when chatSession.inFlight is true AND the last\n // message is NOT a streaming assistant message (the\n // assistant's own caret takes over once tokens arrive).\n // Styled as an assistant chip with three bouncing dots\n // instead of text, so the thread reads naturally \u2014 the\n // visitor sees an \"assistant\" bubble that's clearly busy.\n this._shouldShowThinking()\n ? html`<div\n data-trail-thinking\n data-role=\"assistant\"\n data-status=\"thinking\"\n aria-label=${\n this.thinkingText\n ? `Assistant is thinking: ${this.thinkingText}`\n : 'Assistant is thinking'\n }\n style=${styleMap(thinkingSlotStyles())}\n >\n ${\n // When the backend has streamed reasoning narration into\n // `thinkingText`, render the tail of it as a small italic\n // line \u2014 the model's own intent (\"Let me check what we have\n // for student athletes\u2026\") narrates the gap better than\n // silent dots. Falls back to the bouncing-dot pulse when\n // no narration has arrived yet.\n this.thinkingText\n ? html`<span\n data-trail-thinking-text\n style=${styleMap(thinkingTextStyles())}\n >${truncateThinkingTail(this.thinkingText)}</span\n >`\n : html`<span data-trail-thinking-dots style=${styleMap(thinkingDotsStyles())}>\n <span style=${styleMap(thinkingDotStyles(0))}></span>\n <span style=${styleMap(thinkingDotStyles(1))}></span>\n <span style=${styleMap(thinkingDotStyles(2))}></span>\n </span>`\n }\n </div>`\n : nothing\n }\n </div>\n ${\n // The blur veil only fires in collapsed (ambient) mode.\n // Expanded = the user explicitly asked for history; blurring\n // the top of the panel would hide content (including the\n // minimize button just above the trail's top edge) the user\n // came here to see.\n this.expanded || this.forceExpanded\n ? nothing\n : html`<div\n data-trail-blur-veil\n aria-hidden=\"true\"\n style=${styleMap(blurVeilStyles())}\n ></div>`\n }\n </div>\n `;\n }\n}\n\n/**\n * Absolute blur veil at the top of the scroll viewport. Uses\n * `backdrop-filter: blur(...)` so whatever pixels sit beneath the veil\n * (top of the scroll content) become blurred, regardless of which chip\n * those pixels belong to. A linear mask fades the blur effect from full\n * at the top to zero at the bottom of the veil \u2014 newest content stays\n * crisp because it lives below the veil's height.\n *\n * Note: `mask-image` controls where the BACKDROP-FILTER applies (via\n * masking the veil element), so the bottom edge of the veil naturally\n * dissolves into the unblurred scroll area below.\n */\nfunction blurVeilStyles(): Record<string, string> {\n const mask = 'linear-gradient(to bottom, black 0%, black 40%, transparent 100%)';\n return {\n position: 'absolute',\n top: '0',\n left: '0',\n right: '0',\n height: `${TRAIL_BLUR_VEIL_PX}px`,\n pointerEvents: 'none',\n backdropFilter: `blur(${TRAIL_BLUR_MAX_PX}px)`,\n WebkitBackdropFilter: `blur(${TRAIL_BLUR_MAX_PX}px)`,\n maskImage: mask,\n WebkitMaskImage: mask,\n zIndex: '2',\n };\n}\n\n/**\n * Discriminated visual state for a chip. Replaces the older\n * `(role, { isStreaming, isError })` triple whose interaction order\n * could produce nonsense combinations (e.g. an assistant chip\n * accidentally reading as a user bubble because `isStreaming` was\n * checked before role). Each kind below is a real conversational\n * state with a single visual identity:\n *\n * - `settled` \u2014 a complete message; the user-vs-assistant\n * identity comes from `role`\n * - `streaming` \u2014 the assistant chip currently receiving tokens;\n * keeps assistant identity (the blinking caret\n * inside the chip carries \"in progress\")\n * - `pending` \u2014 the thinking chip shown between the user's\n * just-sent message and the assistant's first\n * token; assistant identity (the bouncing dots\n * inside carry \"in progress\")\n * - `error` \u2014 a chip whose stream failed mid-flight, OR any\n * system-role message (system messages render\n * with the error identity by convention)\n *\n * Bug history: an earlier branch-ladder design checked `isStreaming`\n * before `role`, so the `pending`/thinking chip inherited the user\n * bubble border. On hosts whose chat panel sits over a light surface\n * that read as a \"white div\" pasted into the assistant slot. The\n * discriminated state prevents that combination from being expressible.\n */\nexport type ChipVisualState =\n | { kind: 'settled'; role: 'user' | 'assistant' | 'system' }\n | { kind: 'streaming' }\n | { kind: 'pending' }\n | { kind: 'error'; role: 'user' | 'assistant' | 'system' };\n\n/**\n * Map a trail message + the trail's `inFlight` flag onto its visual\n * state. The thinking chip is rendered separately at the render-tree\n * level \u2014 this helper covers settled / streaming / error messages and\n * leaves the `pending` state to the dedicated thinking-chip render\n * path.\n */\nfunction chipVisualState(m: TrailMessage): ChipVisualState {\n // System messages and explicit error status both render as error\n // chips regardless of role (the original `isError` definition).\n if (m.status === 'error' || m.role === 'system') {\n return { kind: 'error', role: m.role };\n }\n if (m.role === 'assistant' && m.status === 'streaming') {\n return { kind: 'streaming' };\n }\n return { kind: 'settled', role: m.role };\n}\n\nfunction chipStyles(state: ChipVisualState, pos: number): Record<string, string> {\n const opacity = Math.max(OPACITY_FLOOR, 1 - pos * OPACITY_STEP);\n const yPx = pos * Y_DRIFT_PX;\n\n // Per-state visual tokens. Fallbacks are conservative neutrals so\n // the chip still renders sensibly on a host page that hasn't set\n // its design-system tokens yet. The contract: changing one state's\n // border MUST NOT silently affect another state's border.\n let border: string;\n let background: string;\n let color: string;\n let alignSelf: 'flex-start' | 'flex-end';\n\n switch (state.kind) {\n case 'settled':\n if (state.role === 'user') {\n border = 'var(--sc-content-bubble-border-user, 1px solid rgba(255, 255, 255, 0.28))';\n background = 'var(--sc-content-bubble-background-user, rgba(255, 255, 255, 0.10))';\n alignSelf = 'flex-end';\n } else {\n border = 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))';\n background = 'var(--sc-content-bubble-background, rgba(20, 22, 24, 0.35))';\n alignSelf = 'flex-start';\n }\n color = 'var(--sc-tile-title-color, inherit)';\n break;\n case 'streaming':\n case 'pending':\n // Assistant identity. The caret (streaming) or bouncing dots\n // (pending) inside the chip carry the in-progress signal \u2014 the\n // chip's outer identity stays consistent with the assistant's\n // settled reply, so the visitor sees the SAME entity speaking\n // throughout the response lifecycle. This is the bug fix: prior\n // versions borrowed the user border for these states and broke\n // identity continuity.\n border = 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))';\n background = 'var(--sc-content-bubble-background, rgba(20, 22, 24, 0.35))';\n color = 'var(--sc-tile-title-color, inherit)';\n alignSelf = 'flex-start';\n break;\n case 'error':\n border = 'var(--sc-content-bubble-border-error, 1px solid rgba(220, 80, 80, 0.55))';\n background = 'var(--sc-content-bubble-background-error, rgba(140, 40, 40, 0.42))';\n color = 'var(--sc-content-bubble-text-error, rgba(255, 220, 220, 0.95))';\n // Errors keep their originating role's alignment so a failed\n // user-side send still sits on the right where the user expects\n // their message to land.\n alignSelf = state.role === 'user' ? 'flex-end' : 'flex-start';\n break;\n }\n\n return {\n alignSelf,\n maxWidth: '85%',\n // Bumped from 11px \u2192 13px after the chat lid in production read as\n // cramped at the smaller size \u2014 body chat copy needs to be visitor-\n // legible on a phone without zoom, and 11px reads as a footnote\n // rather than the primary conversation surface. Code blocks inside\n // chip body scale proportionally (10px \u2192 11px) below.\n fontSize: '13px',\n lineHeight: '1.45',\n padding: '5px 11px',\n borderRadius: '10px',\n border,\n background,\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n color,\n transition:\n 'opacity 240ms cubic-bezier(0.22, 1, 0.36, 1), transform 240ms cubic-bezier(0.22, 1, 0.36, 1)',\n // Per-position opacity + y-drift falloff is still per-chip \u2014 those\n // operate naturally per element. Blur is NOT per-chip anymore: it's\n // applied via an absolute-positioned veil at the top of the scroll\n // viewport (TRAIL_BLUR_VEIL_PX above), so a single long message\n // doesn't get uniformly blurred across its full body.\n opacity: String(toFixedTrim(opacity)),\n transform: `translateY(${yPx}px)`,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n };\n}\n\nfunction toolCallStripStyles(): Record<string, string> {\n return {\n display: 'flex',\n flexWrap: 'wrap',\n gap: '4px',\n marginTop: '4px',\n paddingTop: '4px',\n borderTop: '1px dashed var(--sc-content-divider-color, rgba(255, 255, 255, 0.12))',\n };\n}\n\nfunction toolCallChipStyles(status: TrailToolCall['status']): Record<string, string> {\n const isPending = status === 'pending';\n const isError = status === 'error';\n const isDone = status === 'done';\n return {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '4px',\n padding: '2px 7px',\n border: isError\n ? 'var(--sc-content-bubble-border-error, 1px solid rgba(220, 80, 80, 0.45))'\n : isPending\n ? 'var(--sc-content-bubble-border-user, 1px solid rgba(255, 255, 255, 0.28))'\n : 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))',\n background: isError\n ? 'var(--sc-content-bubble-background-error, rgba(140, 40, 40, 0.35))'\n : isPending\n ? 'var(--sc-content-bubble-background-user, rgba(255, 255, 255, 0.10))'\n : isDone\n ? 'var(--sc-content-bubble-background-idle, rgba(40, 44, 50, 0.4))'\n : 'var(--sc-content-bubble-background, rgba(20, 22, 24, 0.35))',\n color: 'var(--sc-tile-text-color, inherit)',\n borderRadius: '999px',\n fontSize: '10px',\n fontWeight: '500',\n fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',\n cursor: isPending ? 'pointer' : 'default',\n opacity: isDone ? '0.7' : '1',\n };\n}\n\nfunction toolCallIcon(status: TrailToolCall['status']): string {\n switch (status) {\n case 'args-streaming':\n case 'running':\n return '\u22EF';\n case 'pending':\n return '?';\n case 'done':\n return '\u2713';\n case 'error':\n return '\u2717';\n default:\n return '\u00B7';\n }\n}\n\nfunction caretStyles(): Record<string, string> {\n return {\n display: 'inline-block',\n width: '6px',\n height: '12px',\n marginLeft: '3px',\n verticalAlign: '-1px',\n background: 'hsl(var(--sc-accent-color) / 0.85)',\n borderRadius: '1px',\n animation: 'syntro-trail-caret 1s steps(2, end) infinite',\n };\n}\n\n/**\n * Layout slot for the naked thinking indicator. No bubble \u2014 no\n * background, no border, no padding, no backdrop blur. The chip\n * decoration around an \"empty assistant message\" was reading as a\n * placeholder card on host themes that drew the chip background as a\n * solid color; stripping it lets the dots carry the entire signal.\n * What this style does keep:\n * - left alignment in the trail's flex column (assistant slot)\n * - the inherited chip text color, which the dots' `currentColor`\n * fill picks up so they tint with the assistant identity\n * - a fade-up entrance so the indicator arrives, not pops, into\n * view next to the user's just-sent message\n * - a small inset so the dots sit at the same baseline a text chip\n * would, preventing a vertical jump when the first token arrives\n * and the slot is replaced by the streaming chip\n */\nfunction thinkingSlotStyles(): Record<string, string> {\n return {\n alignSelf: 'flex-start',\n color: 'var(--sc-tile-title-color, inherit)',\n padding: '4px 4px',\n animation: 'syntro-trail-chip-enter 220ms cubic-bezier(0.22, 1, 0.36, 1) both',\n };\n}\n\nfunction thinkingDotsStyles(): Record<string, string> {\n return {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '6px',\n // Line height matches what a single-line text chip would compute\n // to, so swapping out the slot for the streaming chip doesn't\n // shift the next chip up or down by a couple of pixels.\n lineHeight: '14px',\n };\n}\n\nfunction thinkingDotStyles(index: number): Record<string, string> {\n return {\n display: 'inline-block',\n width: '7px',\n height: '7px',\n borderRadius: '50%',\n background: 'currentColor',\n // The dots are now the entire visual \u2014 no bubble framing them \u2014\n // so the pulse has to do all the work the chip used to do. Scale\n // 0.4 \u2192 1.0 with opacity 0.25 \u2192 1.0 gives an unmistakable\n // breathing motion: at the trough each dot reads like a faint\n // ghost, at the peak like a solid dot. 940ms cycle is a touch\n // faster than the original 1.05s; without the chip frame the\n // shorter cycle feels purposeful instead of agitated. Stagger\n // 130ms = clear rolling cadence across all three.\n transformOrigin: 'center',\n animation: `syntro-trail-thinking-pulse 940ms cubic-bezier(0.4, 0, 0.6, 1) ${index * 0.13}s infinite both`,\n };\n}\n\nfunction moreStyles(): Record<string, string> {\n return {\n // Center horizontally regardless of the parent's display model.\n // ``alignSelf: center`` only works when the parent is a flex/grid\n // container with cross-axis alignment; the chat-bar's flex column\n // stretches us full-width instead. ``display: block`` + auto inline\n // margins centers the auto-sized button without needing the parent\n // to opt in.\n display: 'block',\n marginLeft: 'auto',\n marginRight: 'auto',\n marginBottom: '6px',\n fontSize: '10px',\n fontWeight: '500',\n letterSpacing: '0.06em',\n padding: '4px 10px',\n border: '1px solid hsl(var(--sc-accent-color) / 0.32)',\n borderRadius: '999px',\n background: 'hsl(var(--sc-accent-color) / 0.10)',\n color: 'var(--sc-tile-text-color, currentColor)',\n cursor: 'pointer',\n opacity: '0.85',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n transition: 'opacity 150ms ease, background 150ms ease',\n };\n}\n\n// The trail used to inject these rules into `document.head` via a\n// helper. With the move to shadow DOM (see `static styles` on\n// AdaptiveChatTrail above), the rules are owned by Lit and scoped to\n// the trail's own tree. document.head injection is the wrong choice\n// from inside a shadow-rooted hierarchy \u2014 see the static-styles\n// comment for the failure mode.\n\n/**\n * Pre-process LLM markdown before rendering. Three common artifacts:\n *\n * 1. Trailing \" \" (two spaces = markdown hard-break) sprinkled at\n * end-of-line, even inside list items where it just leaks\n * invisible whitespace into the DOM.\n * 2. Runs of 3+ blank lines used as visual separators. Markdown\n * treats any number of blank lines as a single paragraph break,\n * so the extras add nothing semantically \u2014 but combined with\n * paragraph margins they create huge gaps in the tiny chip layout.\n * 3. Leading / trailing newlines on the whole message. Trailing\n * `\\n\\n` is the common one \u2014 marked turns it into a final empty\n * <p> that still occupies a full line-height of vertical space,\n * leaving a visible gap between the last text and the chat bar.\n *\n * Collapsing all three keeps the rendered HTML tight without changing\n * the agent's semantic intent.\n */\nfunction stripTrailingWhitespace(text: string): string {\n return text\n .replace(/[ \\t]+$/gm, '') // trailing whitespace per line\n .replace(/\\n{3,}/g, '\\n\\n') // collapse runs of blank lines\n .trim(); // drop leading/trailing whitespace+newlines on the whole message\n}\n\n/** Two-decimal trim that avoids trailing zeros (\".50\" \u2192 \".5\"). */\nfunction toFixedTrim(n: number): string {\n const s = n.toFixed(2);\n return s.replace(/\\.?0+$/, '') || '0';\n}\n\nconst THINKING_TAIL_MAX = 120;\n\n/**\n * Render the tail end of a streaming thinking buffer \u2014 the model's\n * narration grows monotonically as it reasons, but we only ever want\n * to display the last sentence (or so) to keep the chip compact while\n * still feeling alive as new content lands. Returns the input verbatim\n * if it's already short; otherwise prepends an ellipsis to signal the\n * head was trimmed.\n */\nfunction truncateThinkingTail(text: string): string {\n const flat = text.replace(/\\s+/g, ' ').trim();\n if (flat.length <= THINKING_TAIL_MAX) return flat;\n return `\u2026${flat.slice(-THINKING_TAIL_MAX)}`;\n}\n\nfunction thinkingTextStyles(): Record<string, string> {\n return {\n fontStyle: 'italic',\n opacity: '0.75',\n fontSize: '13px',\n lineHeight: '18px',\n maxWidth: '32ch',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n };\n}\n\nfunction introSuggestionStyles(): Record<string, string> {\n return {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '6px',\n marginTop: '4px',\n padding: '10px 18px',\n borderRadius: '9999px',\n border: '0',\n background:\n 'linear-gradient(135deg, var(--sc-color-primary, #5faf7d) 0%, var(--sc-color-primary-hover, #3d8a5e) 100%)',\n color: '#fff',\n cursor: 'pointer',\n font: 'inherit',\n fontSize: '14px',\n fontWeight: '600',\n transition: 'transform 120ms ease, filter 160ms ease',\n };\n}\n\nfunction emptyIconStyles(): Record<string, string> {\n return {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '48px',\n height: '48px',\n borderRadius: '9999px',\n background:\n 'var(--sc-tile-icon-background, linear-gradient(135deg, var(--sc-color-primary, #5faf7d) 0%, var(--sc-color-primary-hover, #3d8a5e) 100%))',\n color: '#fff',\n fontSize: '22px',\n };\n}\n\nfunction emptyTitleStyles(): Record<string, string> {\n return {\n margin: '0',\n fontSize: '15px',\n fontWeight: '600',\n lineHeight: '1.35',\n color: 'var(--sc-content-text-color, inherit)',\n maxWidth: '32ch',\n };\n}\n\nif (!customElements.get('adaptive-chat-trail')) {\n customElements.define('adaptive-chat-trail', AdaptiveChatTrail);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'adaptive-chat-trail': AdaptiveChatTrail;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/runtime.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
renderFallbackHtml
|
|
12
12
|
} from "./chunk-67B5T3V5.js";
|
|
13
13
|
import "./chunk-AIPLC647.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-JTZ36YJM.js";
|
|
15
15
|
import "./chunk-ONGGPQER.js";
|
|
16
16
|
import "./chunk-FVNSOP7B.js";
|
|
17
17
|
import "./chunk-VLJ3WOEX.js";
|
package/package.json
CHANGED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/AdaptiveChatTrail.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * AdaptiveChatTrail \u2014 the \"bubble-up\" message column that sits above the\n * chat bar in the canvas lid.\n *\n * Messages appear immediately above the chat bar and drift upward as\n * newer ones arrive. Each stack position carries an opacity + blur\n * falloff so older messages read as fading from the page. Beyond a\n * `visibleCount` cap (default 3) the trail collapses into an \"N more \u00B7\n * expand\" affordance \u2014 clicking emits `trail-expand`.\n *\n * Shadow DOM (default Lit open root) so chip styles, markdown reset\n * rules, and animation @keyframes are scoped to this element's tree.\n * Theme CSS variables (--sc-tile-text-color, --sc-content-bubble-*,\n * etc.) inherit through the shadow boundary, so canvas-level theming\n * still reaches the chips. The earlier light-DOM render-root override\n * silently broke @keyframes lookup once the trail was hosted inside\n * any shadow root (sc-mount, velvet-mobile, etc.) \u2014 keyframes injected\n * into `document.head` aren't visible from another tree scope.\n *\n * See PRD \u00A74.3 (chat trail) for the canonical motion + falloff spec.\n */\n\nimport { renderMarkdown } from '@syntrologie/chat';\nimport { css, html, LitElement, nothing } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\n\nexport interface TrailToolCall {\n id: string;\n name: string;\n status: 'args-streaming' | 'pending' | 'running' | 'done' | 'error';\n}\n\nexport interface TrailMessage {\n /** Stable identity for keyed rendering (must be unique within the trail). */\n id: number | string;\n role: 'user' | 'assistant' | 'system';\n text: string;\n /** Streaming \u2192 assistant text still arriving from backend. Complete \u2192 final. Error \u2192 fatal during stream. */\n status?: 'streaming' | 'complete' | 'error';\n /** Tool calls attached to an assistant message (rendered as compact chips). */\n toolCalls?: TrailToolCall[];\n}\n\n/** PRD \u00A74.3 constants. */\nconst DEFAULT_VISIBLE = 3;\nconst OPACITY_STEP = 0.22;\nconst OPACITY_FLOOR = 0.18;\nconst Y_DRIFT_PX = -2;\n/**\n * Height of the absolute blur veil at the top of the trail's scroll\n * area. Pixel-based, NOT message-count-based: with the old per-chip\n * `filter: blur(pos * step)` model, a single long markdown response\n * at pos=1 would have its entire body uniformly blurred \u2014 including\n * the chunk visually adjacent to the crisp newest message \u2014 because\n * blur is a property of the chip element, not of pixels. Switching\n * to a fixed-height backdrop-blur veil keeps the bottom of the\n * visible area (newest content) crisp regardless of message length.\n */\nconst TRAIL_BLUR_VEIL_PX = 72;\n/** Max blur applied at the very top of the veil; tapers to 0 at the bottom edge. */\nconst TRAIL_BLUR_MAX_PX = 5;\n\nexport class AdaptiveChatTrail extends LitElement {\n // Trail-local styles. The chat-trail used to render into light DOM\n // (`createRenderRoot() { return this }`) and inject these rules into\n // `document.head` so chip + markdown styles could find their selectors.\n // That broke the moment the trail was hosted inside any shadow root\n // (sc-mount \u2192 custom-canvas \u2192 velvet-mobile \u2192 sc-mount \u2192 chat-bar \u2192\n // chat-trail is the production chain): CSS animations require the\n // @keyframes rule to be in the SAME TreeScope as the element using\n // it, and document-level keyframes don't cross a shadow boundary.\n // Result was silent \u2014 `animation-name` parsed fine, the property got\n // the right resolved value, but the browser never instantiated an\n // Animation object because no matching keyframe was visible from\n // inside the shadow tree. `getAnimations()` returned `[]`, dots sat\n // frozen at base scale, the typing indicator looked dead.\n //\n // Shadow DOM with `static styles` is the right architecture here.\n // CSS custom properties (--sc-tile-text-color, --sc-content-bubble-*,\n // etc.) inherit through shadow boundaries by default, so the canvas\n // theme cascade still reaches the chips without the light-DOM\n // workaround. As a bonus the markdown reset rules can't leak out\n // onto the host page, and the keyframes are owned by the trail's\n // own tree scope where the chip + dot elements live.\n static override styles = css`\n [data-syntro-chat-trail] > :first-child {\n margin-top: auto;\n }\n [data-trail-chip] > p:first-child {\n margin-top: 0;\n }\n [data-trail-chip] > p:last-child {\n margin-bottom: 0;\n }\n [data-trail-chip] p {\n margin: 4px 0;\n }\n [data-trail-chip] br + br {\n display: none;\n }\n [data-trail-chip] ul,\n [data-trail-chip] ol {\n margin: 4px 0;\n padding-left: 1.1em;\n }\n [data-trail-chip] li {\n margin: 0;\n }\n [data-trail-chip] li + li {\n margin-top: 1px;\n }\n [data-trail-chip] pre {\n margin: 6px 0;\n padding: 6px 8px;\n background: var(--sc-content-code-background-block, rgba(0, 0, 0, 0.35));\n border-radius: 6px;\n overflow-x: auto;\n font-size: 11px;\n }\n [data-trail-chip] code {\n font-family: ui-monospace, SF Mono, Menlo, monospace;\n font-size: 11px;\n background: var(--sc-content-code-background, rgba(0, 0, 0, 0.25));\n padding: 1px 4px;\n border-radius: 3px;\n }\n [data-trail-chip] pre code {\n background: transparent;\n padding: 0;\n }\n [data-trail-chip] a {\n color: var(--sc-content-link-color, var(--sc-color-primary, #b72e2a));\n text-decoration: underline;\n }\n [data-trail-chip] strong {\n font-weight: 600;\n }\n /* Streaming-caret blink for in-progress assistant chips. */\n @keyframes syntro-trail-caret {\n 0% {\n opacity: 1;\n }\n 50% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n }\n /* Thinking-dot scale + opacity pulse. The naked-dots redesign\n relies entirely on this motion to signal \"alive\" \u2014 there's no\n chip frame around the dots, so a subtle animation reads as\n broken. 0.4 \u2192 1.0 scale with 0.25 \u2192 1.0 opacity gives an\n unmistakable breathing cue. */\n @keyframes syntro-trail-thinking-pulse {\n 0%,\n 100% {\n transform: scale(0.4);\n opacity: 0.25;\n }\n 45% {\n transform: scale(1);\n opacity: 1;\n }\n }\n /* Chip entrance \u2014 fades a freshly-mounted chip up from below\n so the thinking \u2192 first-streaming-chip handoff feels like one\n continuous motion instead of an element swap. */\n @keyframes syntro-trail-chip-enter {\n from {\n opacity: 0;\n transform: translateY(6px) scale(0.97);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n }\n `;\n\n static override properties = {\n messages: { attribute: false },\n visibleCount: { type: Number },\n expanded: { type: Boolean },\n greeting: { type: String },\n /** When true: trail renders fully expanded AND the\n * expand/collapse affordance is hidden. Used by hosts that mount\n * the chat in a full-screen surface (the mobile panel) where\n * collapse makes no sense \u2014 there's nothing to collapse INTO. */\n forceExpanded: { type: Boolean },\n /**\n * Mirrors `chatSession.inFlight` \u2014 true between `send()` and the\n * assistant's first `receiveStart`/`receiveDelta`, OR while a\n * stream is in progress. Drives the \"thinking\" indicator that\n * fills the gap between the user's just-sent message and the\n * first assistant token. Wired through AdaptiveChatBar (it owns\n * the chatSession subscription and forwards inFlight as a prop).\n */\n inFlight: { type: Boolean },\n /**\n * Live reasoning narration streamed from the model via AG-UI\n * `THINKING_TEXT_MESSAGE_CONTENT`. When non-empty, the thinking\n * chip renders this in place of the bouncing dots so the visitor\n * sees what the model is actually doing instead of staring at\n * silent loading state during a tool-call gap.\n */\n thinkingText: { attribute: false },\n /**\n * Pre-conversation suggestion chip. When the visitor opens the\n * chat and `messages` is empty, the trail renders this as a\n * button beneath the greeting. Clicking it dispatches a\n * `trail-intro-suggestion` event with the configured prompt \u2014\n * the parent (chat-bar) forwards that to chatSession.send().\n * Disappears the moment the first message lands.\n */\n introSuggestion: { attribute: false },\n };\n\n messages: TrailMessage[] = [];\n visibleCount = DEFAULT_VISIBLE;\n expanded = false;\n forceExpanded = false;\n inFlight = false;\n thinkingText = '';\n introSuggestion: { label: string; prompt: string } | undefined = undefined;\n /**\n * Pre-conversation phantom assistant message. Rendered only when\n * `messages` is empty. NOT injected into chatSession state \u2014 the\n * greeting is configuration, not conversation, so it disappears\n * automatically when the first real message arrives.\n */\n greeting: string | undefined = undefined;\n\n // No `createRenderRoot()` override \u2192 default open shadow root. CSS\n // custom properties (--sc-tile-text-color, --sc-content-bubble-*,\n // etc.) inherit through the shadow boundary, so canvas-level theming\n // continues to cascade into the chips without the light-DOM hack.\n // See the `static styles` block above for the in-shadow stylesheet.\n\n private _onExpand = (): void => {\n // Self-manage: clicking the inline link expands the trail in place.\n // We still dispatch the event so any parent that wants to react\n // (telemetry, mirror state to a side panel, etc.) can listen.\n this.expanded = true;\n this.dispatchEvent(new CustomEvent('trail-expand', { bubbles: true, composed: true }));\n };\n\n /**\n * Approve a pending client tool call. The trail emits a generic\n * event so the host can decide whether to forward to chatSession\n * (the common path) or override. Keeps the trail itself free of\n * direct chatSession coupling \u2014 it's a pure view component.\n */\n private _onToolCallClick = (tc: TrailToolCall): void => {\n if (tc.status !== 'pending') return;\n this.dispatchEvent(\n new CustomEvent<{ toolCallId: string; approved: boolean }>('trail-toolcall-approved', {\n detail: { toolCallId: tc.id, approved: true },\n bubbles: true,\n composed: true,\n })\n );\n };\n\n private _onCollapse = (): void => {\n // Mirror of _onExpand for the minimize affordance shown while\n // expanded. Self-manages + dispatches `trail-collapse` for parent\n // observers (telemetry, side-panel mirrors).\n this.expanded = false;\n this.dispatchEvent(new CustomEvent('trail-collapse', { bubbles: true, composed: true }));\n };\n\n /**\n * Whether to render the thinking-dots chip after the last message.\n * True iff a request is in flight AND the assistant hasn't started\n * streaming a reply yet \u2014 once the first delta lands, the streaming\n * chip's caret takes over and the thinking indicator hides so the\n * visitor isn't shown both at once. Also hidden when the last\n * message is an error/system message, because the request that\n * \"errored\" is already over from the user's perspective.\n */\n private _shouldShowThinking(): boolean {\n if (!this.inFlight) return false;\n const last = this.messages[this.messages.length - 1];\n if (!last) return true;\n if (last.role === 'assistant' && last.status === 'streaming') return false;\n if (last.role === 'system' || last.status === 'error') return false;\n return true;\n }\n\n override updated(changed: Map<string, unknown>): void {\n // Anchor the scroll to the bottom whenever:\n // - the trail flips into expanded mode (UX continuity), OR\n // - new messages or text deltas arrive (so a long streaming chip\n // stays pinned to the latest line, not the start of the chip).\n // Skip when the user has manually scrolled UP into history \u2014 we'd\n // rather lose pinning than yank them out of what they're reading.\n if (changed.has('expanded') || changed.has('messages')) {\n requestAnimationFrame(() => {\n const container = this.renderRoot.querySelector<HTMLElement>('[data-syntro-chat-trail]');\n if (!container) return;\n const distanceFromBottom =\n container.scrollHeight - container.scrollTop - container.clientHeight;\n // 200px tolerance \u2014 streaming deltas can append a paragraph in\n // one update, easily 80-150px. A tighter window would strand\n // the viewer at the top of the chip mid-stream because the\n // first delta blew past the threshold. User-driven scroll-up\n // of >200px disables auto-pin until they scroll back down.\n if (distanceFromBottom < 200 || changed.has('expanded')) {\n container.scrollTop = container.scrollHeight;\n }\n });\n }\n }\n\n private _onIntroSuggestionClick = (): void => {\n const s = this.introSuggestion;\n if (!s) return;\n this.dispatchEvent(\n new CustomEvent<{ prompt: string }>('trail-intro-suggestion', {\n detail: { prompt: s.prompt },\n bubbles: true,\n composed: true,\n })\n );\n };\n\n override render() {\n if (this.messages.length === 0 && !this.greeting && !this.introSuggestion) return nothing;\n if (this.messages.length === 0 && (this.greeting || this.introSuggestion)) {\n // Render the greeting as a pure-display assistant chip. No\n // tool-calls, no streaming caret, single chip at pos=0. When\n // an intro suggestion is configured, render a button under\n // the greeting so the visitor has a one-tap path into the\n // conversation in addition to the input.\n return html`<div data-syntro-chat-trail style=${styleMap({\n display: 'flex',\n flexDirection: 'column',\n gap: '6px',\n justifyContent: 'flex-end',\n width: '100%',\n padding: '0',\n pointerEvents: 'auto',\n })}>\n ${\n this.greeting\n ? html`<div\n data-trail-chip\n data-role=\"assistant\"\n data-status=\"greeting\"\n style=${styleMap(chipStyles({ kind: 'settled', role: 'assistant' }, 0))}\n >${unsafeHTML(renderMarkdown(stripTrailingWhitespace(this.greeting)))}</div>`\n : nothing\n }\n ${\n this.introSuggestion\n ? html`<button\n type=\"button\"\n data-trail-intro-suggestion\n @click=${this._onIntroSuggestionClick}\n style=${styleMap(introSuggestionStyles())}\n >${this.introSuggestion.label}</button>`\n : nothing\n }\n </div>`;\n }\n\n // Choose the slice we actually paint. Newest are at the END of the\n // array (closest to the chat bar). When not expanded, take the last\n // `visibleCount`; when expanded, take all.\n const visible =\n this.expanded || this.forceExpanded ? this.messages : this.messages.slice(-this.visibleCount);\n const hidden = this.messages.length - visible.length;\n\n const baseStyles: Record<string, string> = {\n display: 'flex',\n flexDirection: 'column',\n gap: '6px',\n // NO `justifyContent: flex-end` here \u2014 combined with `overflow: auto`\n // it's a known cross-browser bug: oversized flex children get\n // pushed ABOVE the container without contributing to scrollHeight,\n // so the scrollbar never appears. We rely on `:first-child {\n // margin-top: auto }` (injected as global CSS) to push short\n // content to the bottom of the box while leaving overflow handling\n // to the native scroll container.\n width: '100%',\n // The trail itself has no chrome \u2014 chips sit on the host page bg.\n padding: '0',\n pointerEvents: 'auto',\n };\n\n // Expanded mode: cap height, scroll, and add a visible top border so\n // the panel reads as a bounded region. NO background tint or shadow\n // \u2014 the ambient (collapsed) treatment is fine and we don't want to\n // suddenly introduce panel chrome that wasn't there before. The\n // border alone marks the panel's top edge.\n // Falloff is turned off per-chip below \u2014 readability wins once the\n // user has explicitly asked for history. See PRD \u00A74.3.\n // Collapsed mode also gets a (looser) cap + scroll so a single long\n // markdown chip doesn't blow the chat-bar off-screen with no\n // scroll affordance. Tighter than expanded so the bubble-up trail\n // doesn't dominate the surface visually.\n const modeStyles: Record<string, string> = this.forceExpanded\n ? {\n // Full-screen host: fill all available vertical space and\n // scroll the overflow. ``flex: 1 1 0`` (basis 0, not auto)\n // makes the trail shrink to 0 first when the parent runs\n // out of space \u2014 so the header above and the input row\n // below stay pinned at their natural sizes instead of one\n // of them being pushed off-screen.\n flex: '1 1 0',\n minHeight: '0',\n overflowY: 'auto',\n // ``contain`` prevents scroll from bubbling out to the host\n // page when the trail hits top or bottom. Required for the\n // panel-scroll-leak regression test in\n // SyntroBottomSheet.test.ts.\n overscrollBehavior: 'contain',\n paddingTop: '8px',\n borderTop: 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))',\n scrollBehavior: 'smooth',\n scrollbarWidth: 'thin',\n }\n : this.expanded\n ? {\n maxHeight: 'min(320px, 40vh)',\n overflowY: 'auto',\n paddingTop: '8px',\n borderTop: 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))',\n scrollBehavior: 'smooth',\n scrollbarWidth: 'thin',\n }\n : {\n // Collapsed mode = ambient peek surface. Never a scrollbar:\n // long messages slide up behind the blur veil instead of\n // exposing a scrollbar. The \"\u2191 N more \u00B7 expand\" affordance\n // is the only entry into history; clicking it switches to\n // expanded mode where scroll IS allowed.\n // Height kept short so the trail reads as a peek strip above\n // the chat bar, not a panel \u2014 long single replies fade into\n // the blur veil at the top, user expands to read the rest.\n maxHeight: 'min(140px, 22vh)',\n overflow: 'hidden',\n };\n\n const containerStyles = { ...baseStyles, ...modeStyles };\n\n // Wrap the scroll container in a positioning frame so the absolute\n // blur veil (rendered at the top) anchors to the scroll viewport's\n // top edge \u2014 NOT to a chip that scrolls under it. Pixel-anchored\n // blur means: the top TRAIL_BLUR_VEIL_PX of the visible area is\n // veiled regardless of how many messages occupy those pixels.\n // The frame wraps the scroll container so the absolute blur veil\n // anchors to the scroll viewport's top edge. In ``forceExpanded``\n // mode it ALSO has to participate in the flex column laid out by\n // the trail host \u2014 otherwise the scroll container's ``flex: 1 1\n // 0`` has no flex context, falls back to content height, and the\n // trail's overflow:hidden clips at the TOP of the scroll\n // (showing oldest messages, hiding the newest at the bottom).\n const frameStyles: Record<string, string> = this.forceExpanded\n ? {\n position: 'relative',\n width: '100%',\n display: 'flex',\n flexDirection: 'column',\n flex: '1 1 0',\n minHeight: '0',\n }\n : {\n position: 'relative',\n width: '100%',\n };\n\n return html`\n ${\n // Single toggle button above the frame. Same DOM node morphs:\n // collapsed \u2192 \"\u2191 N more \u00B7 expand\" / \"\u2191 expand\" (calls _onExpand)\n // expanded \u2192 \"\u2304 minimize\" (calls _onCollapse)\n // Lives OUTSIDE the frame so the absolute blur veil (top:0,\n // z-index:2 inside the frame) doesn't cover it. Shown whenever\n // there's anything in the trail \u2014 the user expects this affordance\n // available regardless of how many chips are on screen.\n // When ``forceExpanded`` is set by the host (full-screen panel,\n // etc.), there's nowhere to collapse INTO and the affordance\n // would just confuse the user \u2014 suppress it entirely.\n this.messages.length === 0 || this.forceExpanded\n ? nothing\n : this.expanded\n ? html`<button\n type=\"button\"\n data-trail-toggle\n data-trail-collapse\n @click=${this._onCollapse}\n style=${styleMap(moreStyles())}\n >\u2304 minimize</button>`\n : html`<button\n type=\"button\"\n data-trail-toggle\n data-trail-more\n @click=${this._onExpand}\n style=${styleMap(moreStyles())}\n >${hidden > 0 ? html`\u2191 ${hidden} more \u00B7 expand` : html`\u2191 expand`}</button>`\n }\n <div data-syntro-chat-trail-frame style=${styleMap(frameStyles)}>\n <div data-syntro-chat-trail style=${styleMap(containerStyles)}>\n ${visible.map((m, i) => {\n // Stack position from the bar: 0 = closest, N-1 = oldest.\n // visible[last] is closest, so reverse the index from the\n // tail of the visible slice. When expanded, the ambient\n // falloff is suppressed (pos = 0) so every chip reads at\n // full opacity \u2014 explicit history view, not ambient trail.\n const pos = this.expanded || this.forceExpanded ? 0 : visible.length - 1 - i;\n const state = chipVisualState(m);\n const isStreaming = state.kind === 'streaming';\n // Only surface tool-call chips that NEED the visitor (pending\n // = a client-side approval required to proceed). Running /\n // done / error / args-streaming chips are internal mechanics\n // \u2014 exposing them turned the chat trail into a debug\n // surface, made the conversation read as \"look at all this\n // plumbing I'm running\", and bloated short replies with\n // status pills the visitor can't act on. Pending stays\n // because dropping it would strand a tool call that\n // literally cannot proceed without a tap.\n const toolCalls = (m.toolCalls ?? []).filter((tc) => tc.status === 'pending');\n // Assistant text is markdown-formatted by the agent (lists,\n // code, bold, links). renderMarkdown sanitizes via DOMPurify,\n // then unsafeHTML injects the safe HTML. User + system chips\n // stay plain text \u2014 no markdown risk + no expansion attack\n // surface on user-typed input.\n const renderedText =\n m.role === 'assistant'\n ? html`${unsafeHTML(renderMarkdown(stripTrailingWhitespace(m.text)))}`\n : html`${m.text}`;\n // Entrance animation on streaming chips only. Lit mounts the\n // chip element once per message id, so the animation fires\n // when the streaming chip first appears (replacing the\n // thinking chip in the same visual slot) and does NOT fire\n // again on subsequent token deltas \u2014 those just update the\n // text inside the already-mounted element. User messages\n // and settled history don't get an entrance: the user just\n // tapped Send so they expect immediate feedback, and old\n // history doesn't need re-attention every refresh.\n const chipStyle = isStreaming\n ? {\n ...chipStyles(state, pos),\n animation: 'syntro-trail-chip-enter 220ms cubic-bezier(0.22, 1, 0.36, 1) both',\n }\n : chipStyles(state, pos);\n return html`<div\n data-trail-chip\n data-role=${m.role}\n data-status=${m.status ?? 'complete'}\n style=${styleMap(chipStyle)}\n >${renderedText}${\n isStreaming\n ? html`<span\n data-trail-caret\n aria-hidden=\"true\"\n style=${styleMap(caretStyles())}\n ></span>`\n : nothing\n }${\n toolCalls.length > 0\n ? html`<div data-trail-toolcalls style=${styleMap(toolCallStripStyles())}>\n ${toolCalls.map(\n (tc) => html`<button\n type=\"button\"\n data-trail-toolcall\n data-tool-id=${tc.id}\n data-tool-status=${tc.status}\n @click=${() => this._onToolCallClick(tc)}\n ?disabled=${tc.status !== 'pending'}\n style=${styleMap(toolCallChipStyles(tc.status))}\n title=\"${tc.name} \u00B7 ${tc.status}\"\n >${toolCallIcon(tc.status)} ${tc.name}</button>`\n )}\n </div>`\n : nothing\n }</div>`;\n })}\n ${\n // Thinking indicator \u2014 fills the gap between the user's\n // most recent message and the assistant's first token.\n // Shows when chatSession.inFlight is true AND the last\n // message is NOT a streaming assistant message (the\n // assistant's own caret takes over once tokens arrive).\n // Styled as an assistant chip with three bouncing dots\n // instead of text, so the thread reads naturally \u2014 the\n // visitor sees an \"assistant\" bubble that's clearly busy.\n this._shouldShowThinking()\n ? html`<div\n data-trail-thinking\n data-role=\"assistant\"\n data-status=\"thinking\"\n aria-label=${\n this.thinkingText\n ? `Assistant is thinking: ${this.thinkingText}`\n : 'Assistant is thinking'\n }\n style=${styleMap(thinkingSlotStyles())}\n >\n ${\n // When the backend has streamed reasoning narration into\n // `thinkingText`, render the tail of it as a small italic\n // line \u2014 the model's own intent (\"Let me check what we have\n // for student athletes\u2026\") narrates the gap better than\n // silent dots. Falls back to the bouncing-dot pulse when\n // no narration has arrived yet.\n this.thinkingText\n ? html`<span\n data-trail-thinking-text\n style=${styleMap(thinkingTextStyles())}\n >${truncateThinkingTail(this.thinkingText)}</span\n >`\n : html`<span data-trail-thinking-dots style=${styleMap(thinkingDotsStyles())}>\n <span style=${styleMap(thinkingDotStyles(0))}></span>\n <span style=${styleMap(thinkingDotStyles(1))}></span>\n <span style=${styleMap(thinkingDotStyles(2))}></span>\n </span>`\n }\n </div>`\n : nothing\n }\n </div>\n ${\n // The blur veil only fires in collapsed (ambient) mode.\n // Expanded = the user explicitly asked for history; blurring\n // the top of the panel would hide content (including the\n // minimize button just above the trail's top edge) the user\n // came here to see.\n this.expanded || this.forceExpanded\n ? nothing\n : html`<div\n data-trail-blur-veil\n aria-hidden=\"true\"\n style=${styleMap(blurVeilStyles())}\n ></div>`\n }\n </div>\n `;\n }\n}\n\n/**\n * Absolute blur veil at the top of the scroll viewport. Uses\n * `backdrop-filter: blur(...)` so whatever pixels sit beneath the veil\n * (top of the scroll content) become blurred, regardless of which chip\n * those pixels belong to. A linear mask fades the blur effect from full\n * at the top to zero at the bottom of the veil \u2014 newest content stays\n * crisp because it lives below the veil's height.\n *\n * Note: `mask-image` controls where the BACKDROP-FILTER applies (via\n * masking the veil element), so the bottom edge of the veil naturally\n * dissolves into the unblurred scroll area below.\n */\nfunction blurVeilStyles(): Record<string, string> {\n const mask = 'linear-gradient(to bottom, black 0%, black 40%, transparent 100%)';\n return {\n position: 'absolute',\n top: '0',\n left: '0',\n right: '0',\n height: `${TRAIL_BLUR_VEIL_PX}px`,\n pointerEvents: 'none',\n backdropFilter: `blur(${TRAIL_BLUR_MAX_PX}px)`,\n WebkitBackdropFilter: `blur(${TRAIL_BLUR_MAX_PX}px)`,\n maskImage: mask,\n WebkitMaskImage: mask,\n zIndex: '2',\n };\n}\n\n/**\n * Discriminated visual state for a chip. Replaces the older\n * `(role, { isStreaming, isError })` triple whose interaction order\n * could produce nonsense combinations (e.g. an assistant chip\n * accidentally reading as a user bubble because `isStreaming` was\n * checked before role). Each kind below is a real conversational\n * state with a single visual identity:\n *\n * - `settled` \u2014 a complete message; the user-vs-assistant\n * identity comes from `role`\n * - `streaming` \u2014 the assistant chip currently receiving tokens;\n * keeps assistant identity (the blinking caret\n * inside the chip carries \"in progress\")\n * - `pending` \u2014 the thinking chip shown between the user's\n * just-sent message and the assistant's first\n * token; assistant identity (the bouncing dots\n * inside carry \"in progress\")\n * - `error` \u2014 a chip whose stream failed mid-flight, OR any\n * system-role message (system messages render\n * with the error identity by convention)\n *\n * Bug history: an earlier branch-ladder design checked `isStreaming`\n * before `role`, so the `pending`/thinking chip inherited the user\n * bubble border. On hosts whose chat panel sits over a light surface\n * that read as a \"white div\" pasted into the assistant slot. The\n * discriminated state prevents that combination from being expressible.\n */\nexport type ChipVisualState =\n | { kind: 'settled'; role: 'user' | 'assistant' | 'system' }\n | { kind: 'streaming' }\n | { kind: 'pending' }\n | { kind: 'error'; role: 'user' | 'assistant' | 'system' };\n\n/**\n * Map a trail message + the trail's `inFlight` flag onto its visual\n * state. The thinking chip is rendered separately at the render-tree\n * level \u2014 this helper covers settled / streaming / error messages and\n * leaves the `pending` state to the dedicated thinking-chip render\n * path.\n */\nfunction chipVisualState(m: TrailMessage): ChipVisualState {\n // System messages and explicit error status both render as error\n // chips regardless of role (the original `isError` definition).\n if (m.status === 'error' || m.role === 'system') {\n return { kind: 'error', role: m.role };\n }\n if (m.role === 'assistant' && m.status === 'streaming') {\n return { kind: 'streaming' };\n }\n return { kind: 'settled', role: m.role };\n}\n\nfunction chipStyles(state: ChipVisualState, pos: number): Record<string, string> {\n const opacity = Math.max(OPACITY_FLOOR, 1 - pos * OPACITY_STEP);\n const yPx = pos * Y_DRIFT_PX;\n\n // Per-state visual tokens. Fallbacks are conservative neutrals so\n // the chip still renders sensibly on a host page that hasn't set\n // its design-system tokens yet. The contract: changing one state's\n // border MUST NOT silently affect another state's border.\n let border: string;\n let background: string;\n let color: string;\n let alignSelf: 'flex-start' | 'flex-end';\n\n switch (state.kind) {\n case 'settled':\n if (state.role === 'user') {\n border = 'var(--sc-content-bubble-border-user, 1px solid rgba(255, 255, 255, 0.28))';\n background = 'var(--sc-content-bubble-background-user, rgba(255, 255, 255, 0.10))';\n alignSelf = 'flex-end';\n } else {\n border = 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))';\n background = 'var(--sc-content-bubble-background, rgba(20, 22, 24, 0.35))';\n alignSelf = 'flex-start';\n }\n color = 'var(--sc-tile-title-color, inherit)';\n break;\n case 'streaming':\n case 'pending':\n // Assistant identity. The caret (streaming) or bouncing dots\n // (pending) inside the chip carry the in-progress signal \u2014 the\n // chip's outer identity stays consistent with the assistant's\n // settled reply, so the visitor sees the SAME entity speaking\n // throughout the response lifecycle. This is the bug fix: prior\n // versions borrowed the user border for these states and broke\n // identity continuity.\n border = 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))';\n background = 'var(--sc-content-bubble-background, rgba(20, 22, 24, 0.35))';\n color = 'var(--sc-tile-title-color, inherit)';\n alignSelf = 'flex-start';\n break;\n case 'error':\n border = 'var(--sc-content-bubble-border-error, 1px solid rgba(220, 80, 80, 0.55))';\n background = 'var(--sc-content-bubble-background-error, rgba(140, 40, 40, 0.42))';\n color = 'var(--sc-content-bubble-text-error, rgba(255, 220, 220, 0.95))';\n // Errors keep their originating role's alignment so a failed\n // user-side send still sits on the right where the user expects\n // their message to land.\n alignSelf = state.role === 'user' ? 'flex-end' : 'flex-start';\n break;\n }\n\n return {\n alignSelf,\n maxWidth: '85%',\n // Bumped from 11px \u2192 13px after the chat lid in production read as\n // cramped at the smaller size \u2014 body chat copy needs to be visitor-\n // legible on a phone without zoom, and 11px reads as a footnote\n // rather than the primary conversation surface. Code blocks inside\n // chip body scale proportionally (10px \u2192 11px) below.\n fontSize: '13px',\n lineHeight: '1.45',\n padding: '5px 11px',\n borderRadius: '10px',\n border,\n background,\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n color,\n transition:\n 'opacity 240ms cubic-bezier(0.22, 1, 0.36, 1), transform 240ms cubic-bezier(0.22, 1, 0.36, 1)',\n // Per-position opacity + y-drift falloff is still per-chip \u2014 those\n // operate naturally per element. Blur is NOT per-chip anymore: it's\n // applied via an absolute-positioned veil at the top of the scroll\n // viewport (TRAIL_BLUR_VEIL_PX above), so a single long message\n // doesn't get uniformly blurred across its full body.\n opacity: String(toFixedTrim(opacity)),\n transform: `translateY(${yPx}px)`,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n };\n}\n\nfunction toolCallStripStyles(): Record<string, string> {\n return {\n display: 'flex',\n flexWrap: 'wrap',\n gap: '4px',\n marginTop: '4px',\n paddingTop: '4px',\n borderTop: '1px dashed var(--sc-content-divider-color, rgba(255, 255, 255, 0.12))',\n };\n}\n\nfunction toolCallChipStyles(status: TrailToolCall['status']): Record<string, string> {\n const isPending = status === 'pending';\n const isError = status === 'error';\n const isDone = status === 'done';\n return {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '4px',\n padding: '2px 7px',\n border: isError\n ? 'var(--sc-content-bubble-border-error, 1px solid rgba(220, 80, 80, 0.45))'\n : isPending\n ? 'var(--sc-content-bubble-border-user, 1px solid rgba(255, 255, 255, 0.28))'\n : 'var(--sc-content-bubble-border, 1px solid rgba(255, 255, 255, 0.18))',\n background: isError\n ? 'var(--sc-content-bubble-background-error, rgba(140, 40, 40, 0.35))'\n : isPending\n ? 'var(--sc-content-bubble-background-user, rgba(255, 255, 255, 0.10))'\n : isDone\n ? 'var(--sc-content-bubble-background-idle, rgba(40, 44, 50, 0.4))'\n : 'var(--sc-content-bubble-background, rgba(20, 22, 24, 0.35))',\n color: 'var(--sc-tile-text-color, inherit)',\n borderRadius: '999px',\n fontSize: '10px',\n fontWeight: '500',\n fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',\n cursor: isPending ? 'pointer' : 'default',\n opacity: isDone ? '0.7' : '1',\n };\n}\n\nfunction toolCallIcon(status: TrailToolCall['status']): string {\n switch (status) {\n case 'args-streaming':\n case 'running':\n return '\u22EF';\n case 'pending':\n return '?';\n case 'done':\n return '\u2713';\n case 'error':\n return '\u2717';\n default:\n return '\u00B7';\n }\n}\n\nfunction caretStyles(): Record<string, string> {\n return {\n display: 'inline-block',\n width: '6px',\n height: '12px',\n marginLeft: '3px',\n verticalAlign: '-1px',\n background: 'hsl(var(--sc-accent-color) / 0.85)',\n borderRadius: '1px',\n animation: 'syntro-trail-caret 1s steps(2, end) infinite',\n };\n}\n\n/**\n * Layout slot for the naked thinking indicator. No bubble \u2014 no\n * background, no border, no padding, no backdrop blur. The chip\n * decoration around an \"empty assistant message\" was reading as a\n * placeholder card on host themes that drew the chip background as a\n * solid color; stripping it lets the dots carry the entire signal.\n * What this style does keep:\n * - left alignment in the trail's flex column (assistant slot)\n * - the inherited chip text color, which the dots' `currentColor`\n * fill picks up so they tint with the assistant identity\n * - a fade-up entrance so the indicator arrives, not pops, into\n * view next to the user's just-sent message\n * - a small inset so the dots sit at the same baseline a text chip\n * would, preventing a vertical jump when the first token arrives\n * and the slot is replaced by the streaming chip\n */\nfunction thinkingSlotStyles(): Record<string, string> {\n return {\n alignSelf: 'flex-start',\n color: 'var(--sc-tile-title-color, inherit)',\n padding: '4px 4px',\n animation: 'syntro-trail-chip-enter 220ms cubic-bezier(0.22, 1, 0.36, 1) both',\n };\n}\n\nfunction thinkingDotsStyles(): Record<string, string> {\n return {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '6px',\n // Line height matches what a single-line text chip would compute\n // to, so swapping out the slot for the streaming chip doesn't\n // shift the next chip up or down by a couple of pixels.\n lineHeight: '14px',\n };\n}\n\nfunction thinkingDotStyles(index: number): Record<string, string> {\n return {\n display: 'inline-block',\n width: '7px',\n height: '7px',\n borderRadius: '50%',\n background: 'currentColor',\n // The dots are now the entire visual \u2014 no bubble framing them \u2014\n // so the pulse has to do all the work the chip used to do. Scale\n // 0.4 \u2192 1.0 with opacity 0.25 \u2192 1.0 gives an unmistakable\n // breathing motion: at the trough each dot reads like a faint\n // ghost, at the peak like a solid dot. 940ms cycle is a touch\n // faster than the original 1.05s; without the chip frame the\n // shorter cycle feels purposeful instead of agitated. Stagger\n // 130ms = clear rolling cadence across all three.\n transformOrigin: 'center',\n animation: `syntro-trail-thinking-pulse 940ms cubic-bezier(0.4, 0, 0.6, 1) ${index * 0.13}s infinite both`,\n };\n}\n\nfunction moreStyles(): Record<string, string> {\n return {\n // Center horizontally regardless of the parent's display model.\n // ``alignSelf: center`` only works when the parent is a flex/grid\n // container with cross-axis alignment; the chat-bar's flex column\n // stretches us full-width instead. ``display: block`` + auto inline\n // margins centers the auto-sized button without needing the parent\n // to opt in.\n display: 'block',\n marginLeft: 'auto',\n marginRight: 'auto',\n marginBottom: '6px',\n fontSize: '10px',\n fontWeight: '500',\n letterSpacing: '0.06em',\n padding: '4px 10px',\n border: '1px solid hsl(var(--sc-accent-color) / 0.32)',\n borderRadius: '999px',\n background: 'hsl(var(--sc-accent-color) / 0.10)',\n color: 'var(--sc-tile-text-color, currentColor)',\n cursor: 'pointer',\n opacity: '0.85',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n transition: 'opacity 150ms ease, background 150ms ease',\n };\n}\n\n// The trail used to inject these rules into `document.head` via a\n// helper. With the move to shadow DOM (see `static styles` on\n// AdaptiveChatTrail above), the rules are owned by Lit and scoped to\n// the trail's own tree. document.head injection is the wrong choice\n// from inside a shadow-rooted hierarchy \u2014 see the static-styles\n// comment for the failure mode.\n\n/**\n * Pre-process LLM markdown before rendering. Three common artifacts:\n *\n * 1. Trailing \" \" (two spaces = markdown hard-break) sprinkled at\n * end-of-line, even inside list items where it just leaks\n * invisible whitespace into the DOM.\n * 2. Runs of 3+ blank lines used as visual separators. Markdown\n * treats any number of blank lines as a single paragraph break,\n * so the extras add nothing semantically \u2014 but combined with\n * paragraph margins they create huge gaps in the tiny chip layout.\n * 3. Leading / trailing newlines on the whole message. Trailing\n * `\\n\\n` is the common one \u2014 marked turns it into a final empty\n * <p> that still occupies a full line-height of vertical space,\n * leaving a visible gap between the last text and the chat bar.\n *\n * Collapsing all three keeps the rendered HTML tight without changing\n * the agent's semantic intent.\n */\nfunction stripTrailingWhitespace(text: string): string {\n return text\n .replace(/[ \\t]+$/gm, '') // trailing whitespace per line\n .replace(/\\n{3,}/g, '\\n\\n') // collapse runs of blank lines\n .trim(); // drop leading/trailing whitespace+newlines on the whole message\n}\n\n/** Two-decimal trim that avoids trailing zeros (\".50\" \u2192 \".5\"). */\nfunction toFixedTrim(n: number): string {\n const s = n.toFixed(2);\n return s.replace(/\\.?0+$/, '') || '0';\n}\n\nconst THINKING_TAIL_MAX = 120;\n\n/**\n * Render the tail end of a streaming thinking buffer \u2014 the model's\n * narration grows monotonically as it reasons, but we only ever want\n * to display the last sentence (or so) to keep the chip compact while\n * still feeling alive as new content lands. Returns the input verbatim\n * if it's already short; otherwise prepends an ellipsis to signal the\n * head was trimmed.\n */\nfunction truncateThinkingTail(text: string): string {\n const flat = text.replace(/\\s+/g, ' ').trim();\n if (flat.length <= THINKING_TAIL_MAX) return flat;\n return `\u2026${flat.slice(-THINKING_TAIL_MAX)}`;\n}\n\nfunction thinkingTextStyles(): Record<string, string> {\n return {\n fontStyle: 'italic',\n opacity: '0.75',\n fontSize: '13px',\n lineHeight: '18px',\n maxWidth: '32ch',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n };\n}\n\nfunction introSuggestionStyles(): Record<string, string> {\n return {\n alignSelf: 'flex-start',\n display: 'inline-flex',\n alignItems: 'center',\n gap: '6px',\n padding: '8px 14px',\n borderRadius: '9999px',\n border: '1px solid var(--sc-tile-border, rgba(0, 0, 0, 0.12))',\n background: 'var(--sc-tile-background, transparent)',\n color: 'var(--sc-color-primary, inherit)',\n cursor: 'pointer',\n font: 'inherit',\n fontSize: '14px',\n fontWeight: '500',\n transition: 'background 160ms ease, transform 120ms ease',\n };\n}\n\nif (!customElements.get('adaptive-chat-trail')) {\n customElements.define('adaptive-chat-trail', AdaptiveChatTrail);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'adaptive-chat-trail': AdaptiveChatTrail;\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|