@syntrologie/adapt-chatbot 2.8.0-canary.43 → 2.8.0-canary.430

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/AdaptiveChatBar.d.ts +175 -0
  2. package/dist/AdaptiveChatBar.d.ts.map +1 -0
  3. package/dist/AdaptiveChatBar.js +11 -0
  4. package/dist/AdaptiveChatBar.js.map +7 -0
  5. package/dist/AdaptiveChatBarMountable.d.ts +80 -0
  6. package/dist/AdaptiveChatBarMountable.d.ts.map +1 -0
  7. package/dist/AdaptiveChatTrail.anchor-scroll.test.d.ts +2 -0
  8. package/dist/AdaptiveChatTrail.anchor-scroll.test.d.ts.map +1 -0
  9. package/dist/AdaptiveChatTrail.d.ts +415 -0
  10. package/dist/AdaptiveChatTrail.d.ts.map +1 -0
  11. package/dist/AdaptiveChatTrail.js +11 -0
  12. package/dist/AdaptiveChatTrail.js.map +7 -0
  13. package/dist/AdaptiveChatTrail.test.d.ts +2 -0
  14. package/dist/AdaptiveChatTrail.test.d.ts.map +1 -0
  15. package/dist/AdaptiveChipsStrip.d.ts +1150 -0
  16. package/dist/AdaptiveChipsStrip.d.ts.map +1 -0
  17. package/dist/AdaptiveChipsStrip.js +11 -0
  18. package/dist/AdaptiveChipsStrip.js.map +7 -0
  19. package/dist/AdaptiveChipsStripMountable.d.ts +24 -0
  20. package/dist/AdaptiveChipsStripMountable.d.ts.map +1 -0
  21. package/dist/ChatAssistantLit.d.ts +60 -0
  22. package/dist/ChatAssistantLit.d.ts.map +1 -0
  23. package/dist/ChatAssistantLit.js +13 -0
  24. package/dist/ChatAssistantLit.js.map +7 -0
  25. package/dist/ChatSession.d.ts +230 -0
  26. package/dist/ChatSession.d.ts.map +1 -0
  27. package/dist/ChatTransport.d.ts +360 -0
  28. package/dist/ChatTransport.d.ts.map +1 -0
  29. package/dist/InlineRegistryWidgetMount.test.d.ts +2 -0
  30. package/dist/InlineRegistryWidgetMount.test.d.ts.map +1 -0
  31. package/dist/InlineWidgetLiveMount.test.d.ts +2 -0
  32. package/dist/InlineWidgetLiveMount.test.d.ts.map +1 -0
  33. package/dist/NavLinkMountable.d.ts +25 -0
  34. package/dist/NavLinkMountable.d.ts.map +1 -0
  35. package/dist/NavLinkMountable.test.d.ts +2 -0
  36. package/dist/NavLinkMountable.test.d.ts.map +1 -0
  37. package/dist/TextAnswerMountable.d.ts +17 -0
  38. package/dist/TextAnswerMountable.d.ts.map +1 -0
  39. package/dist/TextAnswerMountable.test.d.ts +2 -0
  40. package/dist/TextAnswerMountable.test.d.ts.map +1 -0
  41. package/dist/Turnstile.d.ts +83 -0
  42. package/dist/Turnstile.d.ts.map +1 -0
  43. package/dist/cdn.d.ts +2 -6
  44. package/dist/cdn.d.ts.map +1 -1
  45. package/dist/chunk-5W75LR6M.js +460 -0
  46. package/dist/chunk-5W75LR6M.js.map +7 -0
  47. package/dist/chunk-7WSMNAKW.js +3824 -0
  48. package/dist/chunk-7WSMNAKW.js.map +7 -0
  49. package/dist/chunk-EWPPVPJ4.js +14 -0
  50. package/dist/chunk-EWPPVPJ4.js.map +7 -0
  51. package/dist/chunk-GP4OSTZT.js +641 -0
  52. package/dist/chunk-GP4OSTZT.js.map +7 -0
  53. package/dist/chunk-HELUC5JI.js +8971 -0
  54. package/dist/chunk-HELUC5JI.js.map +7 -0
  55. package/dist/chunk-HVGQJZMP.js +1520 -0
  56. package/dist/chunk-HVGQJZMP.js.map +7 -0
  57. package/dist/chunk-KPWW7N27.js +235 -0
  58. package/dist/chunk-KPWW7N27.js.map +7 -0
  59. package/dist/chunk-UVKRO5ER.js +13 -0
  60. package/dist/chunk-UVKRO5ER.js.map +7 -0
  61. package/dist/editor-lit.d.ts +37 -0
  62. package/dist/editor-lit.d.ts.map +1 -0
  63. package/dist/editor-lit.js +134 -0
  64. package/dist/editor-lit.js.map +7 -0
  65. package/dist/elements/ActionHandler.d.ts +34 -0
  66. package/dist/elements/ActionHandler.d.ts.map +1 -0
  67. package/dist/elements/ElementInstanceStore.d.ts +207 -0
  68. package/dist/elements/ElementInstanceStore.d.ts.map +1 -0
  69. package/dist/elements/ElementInstanceStore.test.d.ts +2 -0
  70. package/dist/elements/ElementInstanceStore.test.d.ts.map +1 -0
  71. package/dist/elements/ElementTypeHandler.d.ts +77 -0
  72. package/dist/elements/ElementTypeHandler.d.ts.map +1 -0
  73. package/dist/elements/ItemHandler.d.ts +60 -0
  74. package/dist/elements/ItemHandler.d.ts.map +1 -0
  75. package/dist/elements/ItemHandler.test.d.ts +2 -0
  76. package/dist/elements/ItemHandler.test.d.ts.map +1 -0
  77. package/dist/elements/TileHandler.d.ts +52 -0
  78. package/dist/elements/TileHandler.d.ts.map +1 -0
  79. package/dist/elements/TileHandler.test.d.ts +2 -0
  80. package/dist/elements/TileHandler.test.d.ts.map +1 -0
  81. package/dist/elements/__tests__/ElementInstanceStore.recall.test.d.ts +2 -0
  82. package/dist/elements/__tests__/ElementInstanceStore.recall.test.d.ts.map +1 -0
  83. package/dist/elements/blockRenderer.d.ts +46 -0
  84. package/dist/elements/blockRenderer.d.ts.map +1 -0
  85. package/dist/elements/blockRenderer.test.d.ts +13 -0
  86. package/dist/elements/blockRenderer.test.d.ts.map +1 -0
  87. package/dist/elements/blocks.d.ts +58 -0
  88. package/dist/elements/blocks.d.ts.map +1 -0
  89. package/dist/elements/envelope.d.ts +24 -0
  90. package/dist/elements/envelope.d.ts.map +1 -0
  91. package/dist/elements/fetcher.d.ts +40 -0
  92. package/dist/elements/fetcher.d.ts.map +1 -0
  93. package/dist/elements/index.d.ts +33 -0
  94. package/dist/elements/index.d.ts.map +1 -0
  95. package/dist/elements/store-singleton.d.ts +4 -0
  96. package/dist/elements/store-singleton.d.ts.map +1 -0
  97. package/dist/elements/types.d.ts +106 -0
  98. package/dist/elements/types.d.ts.map +1 -0
  99. package/dist/observer/__tests__/allowlist.test.d.ts +9 -0
  100. package/dist/observer/__tests__/allowlist.test.d.ts.map +1 -0
  101. package/dist/observer/__tests__/bus-translate.test.d.ts +16 -0
  102. package/dist/observer/__tests__/bus-translate.test.d.ts.map +1 -0
  103. package/dist/observer/__tests__/lifecycle.test.d.ts +2 -0
  104. package/dist/observer/__tests__/lifecycle.test.d.ts.map +1 -0
  105. package/dist/observer/__tests__/observer-enrich.test.d.ts +10 -0
  106. package/dist/observer/__tests__/observer-enrich.test.d.ts.map +1 -0
  107. package/dist/observer/__tests__/observer-isolation.test.d.ts +13 -0
  108. package/dist/observer/__tests__/observer-isolation.test.d.ts.map +1 -0
  109. package/dist/observer/__tests__/queue.test.d.ts +2 -0
  110. package/dist/observer/__tests__/queue.test.d.ts.map +1 -0
  111. package/dist/observer/__tests__/transport.test.d.ts +2 -0
  112. package/dist/observer/__tests__/transport.test.d.ts.map +1 -0
  113. package/dist/observer/allowlist.d.ts +30 -0
  114. package/dist/observer/allowlist.d.ts.map +1 -0
  115. package/dist/observer/bus-translate.d.ts +33 -0
  116. package/dist/observer/bus-translate.d.ts.map +1 -0
  117. package/dist/observer/index.d.ts +44 -0
  118. package/dist/observer/index.d.ts.map +1 -0
  119. package/dist/observer/queue.d.ts +63 -0
  120. package/dist/observer/queue.d.ts.map +1 -0
  121. package/dist/observer/transport.d.ts +27 -0
  122. package/dist/observer/transport.d.ts.map +1 -0
  123. package/dist/runtime-lit.d.ts +8 -0
  124. package/dist/runtime-lit.d.ts.map +1 -0
  125. package/dist/runtime.d.ts +10 -3
  126. package/dist/runtime.d.ts.map +1 -1
  127. package/dist/runtime.js +2232 -29
  128. package/dist/runtime.js.map +7 -0
  129. package/dist/schema.d.ts +3340 -11
  130. package/dist/schema.d.ts.map +1 -1
  131. package/dist/schema.js +220 -31
  132. package/dist/schema.js.map +7 -0
  133. package/dist/test/setup.d.ts +1 -0
  134. package/dist/test/setup.d.ts.map +1 -0
  135. package/dist/types.d.ts +61 -40
  136. package/dist/types.d.ts.map +1 -1
  137. package/package.json +44 -23
  138. package/dist/ChatAssistant.d.ts +0 -18
  139. package/dist/ChatAssistant.d.ts.map +0 -1
  140. package/dist/ChatAssistant.js +0 -178
  141. package/dist/SyntroChatbot.d.ts +0 -14
  142. package/dist/SyntroChatbot.d.ts.map +0 -1
  143. package/dist/SyntroChatbot.js +0 -56
  144. package/dist/actionParser.d.ts +0 -15
  145. package/dist/actionParser.d.ts.map +0 -1
  146. package/dist/actionParser.js +0 -52
  147. package/dist/apiClient.d.ts +0 -23
  148. package/dist/apiClient.d.ts.map +0 -1
  149. package/dist/apiClient.js +0 -51
  150. package/dist/cdn.js +0 -37
  151. package/dist/editor.d.ts +0 -17
  152. package/dist/editor.d.ts.map +0 -1
  153. package/dist/editor.js +0 -36
  154. package/dist/tools/bridge.d.ts +0 -26
  155. package/dist/tools/bridge.d.ts.map +0 -1
  156. package/dist/tools/bridge.js +0 -25
  157. package/dist/tools/expanders.d.ts +0 -17
  158. package/dist/tools/expanders.d.ts.map +0 -1
  159. package/dist/tools/expanders.js +0 -41
  160. package/dist/tools/index.d.ts +0 -14
  161. package/dist/tools/index.d.ts.map +0 -1
  162. package/dist/tools/index.js +0 -11
  163. package/dist/tools/registrations/GetContextTool.d.ts +0 -7
  164. package/dist/tools/registrations/GetContextTool.d.ts.map +0 -1
  165. package/dist/tools/registrations/GetContextTool.js +0 -18
  166. package/dist/tools/registrations/ScrollToTool.d.ts +0 -7
  167. package/dist/tools/registrations/ScrollToTool.d.ts.map +0 -1
  168. package/dist/tools/registrations/ScrollToTool.js +0 -38
  169. package/dist/tools/registrations/ShowOverlayTool.d.ts +0 -7
  170. package/dist/tools/registrations/ShowOverlayTool.d.ts.map +0 -1
  171. package/dist/tools/registrations/ShowOverlayTool.js +0 -47
  172. package/dist/tools/registrations/index.d.ts +0 -19
  173. package/dist/tools/registrations/index.d.ts.map +0 -1
  174. package/dist/tools/registrations/index.js +0 -7
  175. package/dist/types.js +0 -6
  176. package/dist/useChat.d.ts +0 -25
  177. package/dist/useChat.d.ts.map +0 -1
  178. package/dist/useChat.js +0 -106
@@ -0,0 +1,14 @@
1
+ // src/elements/store-singleton.ts
2
+ var _activeStore = null;
3
+ function setElementInstanceStore(store) {
4
+ _activeStore = store;
5
+ }
6
+ function getElementInstanceStore() {
7
+ return _activeStore;
8
+ }
9
+
10
+ export {
11
+ setElementInstanceStore,
12
+ getElementInstanceStore
13
+ };
14
+ //# sourceMappingURL=chunk-EWPPVPJ4.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/elements/store-singleton.ts"],
4
+ "sourcesContent": ["import type { ElementInstanceStore } from './ElementInstanceStore';\n\nlet _activeStore: ElementInstanceStore | null = null;\n\nexport function setElementInstanceStore(store: ElementInstanceStore | null): void {\n _activeStore = store;\n}\n\nexport function getElementInstanceStore(): ElementInstanceStore | null {\n return _activeStore;\n}\n"],
5
+ "mappings": ";AAEA,IAAI,eAA4C;AAEzC,SAAS,wBAAwB,OAA0C;AAChF,iBAAe;AACjB;AAEO,SAAS,0BAAuD;AACrE,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,641 @@
1
+ import {
2
+ AuthoringFieldsZ,
3
+ NotifyZ,
4
+ TriggerWhenZ
5
+ } from "./chunk-KPWW7N27.js";
6
+
7
+ // src/AdaptiveChipsStrip.ts
8
+ import { html, LitElement, nothing } from "lit";
9
+ import { styleMap } from "lit/directives/style-map.js";
10
+ import { z } from "zod";
11
+ var ChipPayloadZ = z.object({
12
+ widget: z.string().min(1).describe(
13
+ "Widget id from the runtime registry \u2014 same vocabulary as a tile's `widget` field. Click \u2192 strip mounts this widget into the chip drawer. Built-ins shipped by adaptive-chatbot: `adaptive-chatbot:text-answer`. Other adaptives register their own (e.g. `adaptive-faq:question-card`, `adaptive-product:mini`)."
14
+ ),
15
+ props: z.record(z.unknown()).optional().describe("Props passed to the mounted widget. The widget owns its own props schema.")
16
+ }).strict().describe(
17
+ "The content that mounts in the chip's drawer when it opens. Same { widget, props } shape used by slots.lid and tile configs \u2014 chips are tiles by another name."
18
+ );
19
+ var SuggestionChipSchema = z.object({
20
+ ...AuthoringFieldsZ,
21
+ kind: z.literal("suggestions:chip").describe(
22
+ "Compositional action type for a single chip in the AdaptiveChipsStrip. Rendered by the strip widget; not executed by the runtime action engine."
23
+ ),
24
+ config: z.object({
25
+ id: z.string().min(1).describe("Stable identity for keyed rendering and event payloads."),
26
+ title: z.string().min(1).describe("Chip label \u2014 single line, ~32ch typical, ellipsis-truncates beyond."),
27
+ payload: ChipPayloadZ
28
+ }).describe(
29
+ "Per-chip configuration: identity, label, and the payload that opens in the drawer."
30
+ ),
31
+ triggerWhen: TriggerWhenZ.describe(
32
+ "Conditional visibility strategy. When null or omitted, the chip is always shown. Use a rules strategy to show/hide based on page URL, event counts, or session metrics \u2014 same evaluator that powers tile activation."
33
+ ),
34
+ notify: NotifyZ.describe(
35
+ "Toast notification shown when triggerWhen transitions false \u2192 true. Required when triggerWhen is set \u2014 pass null to opt out of the toast."
36
+ )
37
+ }).refine((data) => !data.triggerWhen || data.notify !== void 0, {
38
+ message: "notify is required when triggerWhen is present (use null to opt out of notifications)"
39
+ }).describe(
40
+ "A single suggestion chip in the chips strip. Same authoring shape as faq:question \u2014 discriminator + config body + optional conditional gating + optional proactive notification."
41
+ );
42
+ var AdaptiveChipsStrip = class extends LitElement {
43
+ constructor() {
44
+ super(...arguments);
45
+ this.chips = [];
46
+ /**
47
+ * Runtime reference — provides `widgets.has(id)` and
48
+ * `widgets.mount(id, container, props)` so the strip can render
49
+ * chip payloads through the same registry that mounts tile widgets.
50
+ * Null until the host wires it (the AdaptiveChipsStripMountable
51
+ * passes it in from the runtime).
52
+ */
53
+ this.runtimeRef = null;
54
+ /**
55
+ * Strip-level chrome toggle — applies uniformly to all chips +
56
+ * the payload drawer. Resolved upstream from `tile.chromeless`
57
+ * (which itself defaults from `slot.theme.chromeless`). Per-chip
58
+ * inconsistency would break the mini-canvas visual rhythm so
59
+ * there's no per-chip flag — one strip, one style.
60
+ */
61
+ this.chromeless = false;
62
+ this._openId = null;
63
+ /**
64
+ * Horizontal-scroll affordance state. The strip is a single row that
65
+ * overflows horizontally when there are more chips than the panel
66
+ * width can hold; the left/right chevron buttons are visible only
67
+ * when the user can actually scroll in that direction. ResizeObserver
68
+ * + scroll listener keep these in sync.
69
+ */
70
+ this._canScrollLeft = false;
71
+ this._canScrollRight = false;
72
+ this._stripResizeObserver = null;
73
+ /** Unsubscribe handle for the compositional-append/patch/remove event
74
+ * subscription on `runtime.events`. Null when the strip hasn't yet
75
+ * resolved its tile id (no parent container yet) or the runtime
76
+ * doesn't expose an event bus. */
77
+ this._compositionalUnsub = null;
78
+ /** Tile id this strip occupies. Resolved on first `updated()` pass by
79
+ * walking the DOM ancestor chain for `data-tile-id`. Cached because
80
+ * the lookup is stable for the lifetime of the mount. Null while
81
+ * unresolved (or when not running inside a SyntroTileCard, e.g.
82
+ * in standalone test fixtures). */
83
+ this._tileId = null;
84
+ /** Instance ids of chips appended by LLM mounts (vs. authored chip
85
+ * configs from `props.chips`). Tracked so `removeItem(instance_id)`
86
+ * can find and prune by id. */
87
+ this._llmAppendedIds = /* @__PURE__ */ new Set();
88
+ /**
89
+ * Tracks the currently-mounted payload widget so we can swap on chip
90
+ * change + unmount on close. Same lifecycle pattern as
91
+ * `SyntroCanvasOverlay._lidMountHandle`.
92
+ */
93
+ this._payloadMount = null;
94
+ this._onStripScroll = () => {
95
+ this._updateScrollAffordances();
96
+ };
97
+ this._onPayloadClose = () => {
98
+ this._openId = null;
99
+ };
100
+ }
101
+ createRenderRoot() {
102
+ return this;
103
+ }
104
+ disconnectedCallback() {
105
+ super.disconnectedCallback();
106
+ this._unmountPayload();
107
+ this._stripResizeObserver?.disconnect();
108
+ this._stripResizeObserver = null;
109
+ this._compositionalUnsub?.();
110
+ this._compositionalUnsub = null;
111
+ }
112
+ updated(changed) {
113
+ super.updated(changed);
114
+ if (changed.has("_openId") || changed.has("chips") || changed.has("runtimeRef")) {
115
+ this._syncPayloadMount();
116
+ }
117
+ this._syncStripObserver();
118
+ this._updateScrollAffordances();
119
+ if (changed.has("runtimeRef")) {
120
+ this._compositionalUnsub?.();
121
+ this._compositionalUnsub = null;
122
+ this._tileId = this._resolveTileId();
123
+ }
124
+ if (!this._compositionalUnsub && this._tileId == null) {
125
+ this._tileId = this._resolveTileId();
126
+ }
127
+ if (!this._compositionalUnsub) {
128
+ this._subscribeCompositional();
129
+ }
130
+ }
131
+ _resolveTileId() {
132
+ const anchor = this.closest("[data-tile-id]");
133
+ return anchor?.getAttribute("data-tile-id") ?? null;
134
+ }
135
+ /**
136
+ * Subscribe to the runtime's event bus for compositional-item events
137
+ * targeting this strip's tile id. The subscription survives until
138
+ * disconnect; when an event arrives with a non-matching tile_id, it's
139
+ * a no-op (the same subscription handles all strips on the page —
140
+ * filter is just per-event check).
141
+ */
142
+ _subscribeCompositional() {
143
+ const bus = this.runtimeRef?.events;
144
+ if (!bus?.subscribe || !this._tileId) return;
145
+ const tileId = this._tileId;
146
+ this._compositionalUnsub = bus.subscribe((event) => {
147
+ const props = event.props ?? {};
148
+ if (props.tile_id !== tileId) return;
149
+ if (event.name === "element.compositional_append") {
150
+ this.insertItem(
151
+ String(props.instance_id ?? ""),
152
+ props.item,
153
+ props.position ?? "append"
154
+ );
155
+ } else if (event.name === "element.compositional_patch") {
156
+ this.patchItem(String(props.instance_id ?? ""), props.item);
157
+ } else if (event.name === "element.compositional_remove") {
158
+ this.removeItem(String(props.instance_id ?? ""));
159
+ }
160
+ });
161
+ bus.publish?.("element.compositional_replay_request", { tile_id: tileId });
162
+ }
163
+ // ─────────────────────────────────────────────────────────────────────────
164
+ // CompositionalContainer contract — `ItemHandler` translates wire
165
+ // mutations into events that call these methods. The wire `item`
166
+ // shape is fully formed (`{kind, config: {id, title, payload, …}}`);
167
+ // the strip just appends, patches, or removes by `instance_id`.
168
+ // `instance_id` is the canonical id; chip.config.id is set to it by
169
+ // the backend, so they're the same value. We store the id on
170
+ // `chip.config.id` and use that as the lookup key.
171
+ // ─────────────────────────────────────────────────────────────────────────
172
+ /** Append (or prepend) a new chip authored by the LLM. */
173
+ insertItem(instanceId, item, position) {
174
+ if (!instanceId) return;
175
+ if (this._llmAppendedIds.has(instanceId)) return;
176
+ const incoming = item;
177
+ if (!incoming?.config?.id) return;
178
+ const chip = {
179
+ ...incoming,
180
+ config: { ...incoming.config, id: instanceId }
181
+ };
182
+ this._llmAppendedIds.add(instanceId);
183
+ this.chips = position === "prepend" ? [chip, ...this.chips] : [...this.chips, chip];
184
+ }
185
+ /** Replace the existing chip's content. v1: full replacement (no
186
+ * in-place merge); the chip widget already re-renders on chip data
187
+ * changes. */
188
+ patchItem(instanceId, item) {
189
+ if (!instanceId) return;
190
+ const idx = this.chips.findIndex((c) => c.config.id === instanceId);
191
+ if (idx < 0) return;
192
+ const next = [...this.chips];
193
+ next[idx] = item;
194
+ this.chips = next;
195
+ }
196
+ /** Remove a chip by instance id. Closes the payload drawer if the
197
+ * removed chip was the open one. */
198
+ removeItem(instanceId) {
199
+ if (!instanceId) return;
200
+ const next = this.chips.filter((c) => c.config.id !== instanceId);
201
+ if (next.length === this.chips.length) return;
202
+ this.chips = next;
203
+ this._llmAppendedIds.delete(instanceId);
204
+ if (this._openId === instanceId) this._openId = null;
205
+ }
206
+ /**
207
+ * Attach a ResizeObserver to the scroll container so we recompute
208
+ * left/right arrow visibility when chips are added/removed or the
209
+ * panel width changes. The scroll listener on the strip element
210
+ * handles user-driven scrolls.
211
+ */
212
+ _syncStripObserver() {
213
+ const strip = this.querySelector("[data-chips-strip]");
214
+ if (!strip) {
215
+ this._stripResizeObserver?.disconnect();
216
+ this._stripResizeObserver = null;
217
+ return;
218
+ }
219
+ if (typeof ResizeObserver === "undefined") return;
220
+ if (!this._stripResizeObserver) {
221
+ this._stripResizeObserver = new ResizeObserver(() => this._updateScrollAffordances());
222
+ }
223
+ this._stripResizeObserver.disconnect();
224
+ this._stripResizeObserver.observe(strip);
225
+ }
226
+ /**
227
+ * Recompute whether the left/right scroll arrows should be visible.
228
+ * Left arrow ⇐ scrollLeft > 0. Right arrow ⇐ there are more chips
229
+ * to the right (scrollLeft + clientWidth < scrollWidth). Threshold
230
+ * of 1px tolerance handles sub-pixel rounding on scaled displays.
231
+ */
232
+ _updateScrollAffordances() {
233
+ const strip = this.querySelector("[data-chips-strip]");
234
+ if (!strip) return;
235
+ const canLeft = strip.scrollLeft > 1;
236
+ const canRight = strip.scrollLeft + strip.clientWidth < strip.scrollWidth - 1;
237
+ if (canLeft !== this._canScrollLeft) this._canScrollLeft = canLeft;
238
+ if (canRight !== this._canScrollRight) this._canScrollRight = canRight;
239
+ }
240
+ /**
241
+ * Scroll the strip by ~80% of its visible width, so each arrow click
242
+ * advances the user past most-but-not-all of the current chips
243
+ * (a small overlap helps the user keep their place in the row).
244
+ */
245
+ _scrollBy(direction) {
246
+ const strip = this.querySelector("[data-chips-strip]");
247
+ if (!strip) return;
248
+ const delta = strip.clientWidth * 0.8 * (direction === "left" ? -1 : 1);
249
+ strip.scrollBy({ left: delta, behavior: "smooth" });
250
+ }
251
+ _unmountPayload() {
252
+ if (this._payloadMount) {
253
+ try {
254
+ this._payloadMount.handle.unmount();
255
+ } catch (err) {
256
+ console.warn("[adaptive-chips-strip] payload widget unmount threw:", err);
257
+ }
258
+ this._payloadMount = null;
259
+ }
260
+ }
261
+ /**
262
+ * Reconcile the payload mount with the current `_openId`:
263
+ * - drawer closed / no runtime / widget id unknown → tear down
264
+ * - same chip + same widget id → no-op (or props update)
265
+ * - different chip → unmount old, mount new
266
+ */
267
+ _syncPayloadMount() {
268
+ const chip = this.chips.find((c) => c.config.id === this._openId) ?? null;
269
+ const widgets = this.runtimeRef?.widgets;
270
+ if (!chip || !widgets || !widgets.has(chip.config.payload.widget)) {
271
+ this._unmountPayload();
272
+ return;
273
+ }
274
+ const nextWidget = chip.config.payload.widget;
275
+ const nextProps = chip.config.payload.props ?? {};
276
+ if (this._payloadMount?.chipId === chip.config.id && this._payloadMount.widgetId === nextWidget) {
277
+ this._payloadMount.handle.update?.(nextProps);
278
+ return;
279
+ }
280
+ this._unmountPayload();
281
+ const container = this.querySelector("[data-chip-payload-mount]");
282
+ if (!container) {
283
+ requestAnimationFrame(() => {
284
+ if (this._openId !== chip.config.id) return;
285
+ if (this._payloadMount?.chipId === chip.config.id && this._payloadMount.widgetId === nextWidget) {
286
+ return;
287
+ }
288
+ const retry = this.querySelector("[data-chip-payload-mount]");
289
+ if (!retry) return;
290
+ const handle2 = widgets.mount(nextWidget, retry, nextProps);
291
+ this._payloadMount = { chipId: chip.config.id, widgetId: nextWidget, handle: handle2 };
292
+ });
293
+ return;
294
+ }
295
+ const handle = widgets.mount(nextWidget, container, nextProps);
296
+ this._payloadMount = { chipId: chip.config.id, widgetId: nextWidget, handle };
297
+ }
298
+ _onChipClick(chip) {
299
+ if (this._openId === chip.config.id) {
300
+ this._openId = null;
301
+ return;
302
+ }
303
+ this._openId = chip.config.id;
304
+ this.dispatchEvent(
305
+ new CustomEvent("chip-revealed", {
306
+ detail: { id: chip.config.id, payload: chip.config.payload },
307
+ bubbles: true,
308
+ composed: true
309
+ })
310
+ );
311
+ }
312
+ _onChipDismiss(chip, e) {
313
+ e.stopPropagation();
314
+ if (this._openId === chip.config.id) this._openId = null;
315
+ this.dispatchEvent(
316
+ new CustomEvent("chip-dismissed", {
317
+ detail: { id: chip.config.id },
318
+ bubbles: true,
319
+ composed: true
320
+ })
321
+ );
322
+ }
323
+ render() {
324
+ const open = this.chips.find((c) => c.config.id === this._openId) ?? null;
325
+ return html`
326
+ <div data-syntro-chips-tile style=${styleMap(rootStyles())}>
327
+ <div data-chips-strip-frame style=${styleMap(stripFrameStyles())}>
328
+ ${this._canScrollLeft ? html`<button
329
+ type="button"
330
+ data-chips-scroll="left"
331
+ aria-label="Scroll suggestions left"
332
+ @click=${() => this._scrollBy("left")}
333
+ style=${styleMap(scrollButtonStyles("left"))}
334
+ >‹</button>` : nothing}
335
+ <div
336
+ data-chips-strip
337
+ style=${styleMap(stripStyles())}
338
+ @scroll=${this._onStripScroll}
339
+ >
340
+ ${this.chips.map(
341
+ (chip) => html`<button
342
+ type="button"
343
+ data-chip-id=${chip.config.id}
344
+ data-syntro-element-id=${chip.config.id}
345
+ data-state=${this._openId === chip.config.id ? "open" : "closed"}
346
+ data-chromeless=${this.chromeless ? "true" : "false"}
347
+ style=${styleMap(chipStyles(this._openId === chip.config.id, this.chromeless))}
348
+ @click=${() => this._onChipClick(chip)}
349
+ >
350
+ <span style=${styleMap(chipTitleStyles())}>${chip.config.title}</span>
351
+ <span style=${styleMap(chipGlyphStyles())}>
352
+ ${this._openId === chip.config.id ? "\u25BC" : "\u203A"}
353
+ </span>
354
+ ${// Per-chip dismiss × is suppressed when the strip is
355
+ // chromeless — LLM-curated suggestions in a mini-
356
+ // canvas surface shouldn't be user-dismissible (the
357
+ // model picks them; the user can't "remove" them
358
+ // any more than they can remove a paragraph of
359
+ // prose). Also suppressed on the open chip in any
360
+ // mode (the chip's button-toggle handles close).
361
+ this.chromeless || this._openId === chip.config.id ? nothing : html`<span
362
+ role="button"
363
+ tabindex="0"
364
+ data-chip-dismiss=${chip.config.id}
365
+ aria-label="Dismiss suggestion"
366
+ title="Dismiss"
367
+ style=${styleMap(chipDismissStyles())}
368
+ @click=${(e) => this._onChipDismiss(chip, e)}
369
+ @keydown=${(e) => {
370
+ if (e.key === "Enter" || e.key === " ") {
371
+ e.preventDefault();
372
+ this._onChipDismiss(chip, e);
373
+ }
374
+ }}
375
+ >×</span>`}
376
+ </button>`
377
+ )}
378
+ </div>
379
+ ${this._canScrollRight ? html`<button
380
+ type="button"
381
+ data-chips-scroll="right"
382
+ aria-label="Scroll suggestions right"
383
+ @click=${() => this._scrollBy("right")}
384
+ style=${styleMap(scrollButtonStyles("right"))}
385
+ >›</button>` : nothing}
386
+ </div>
387
+ ${open ? html`<div
388
+ data-chip-payload
389
+ data-for-chip=${open.config.id}
390
+ data-chromeless=${this.chromeless ? "true" : "false"}
391
+ style=${styleMap(payloadStyles(this.chromeless))}
392
+ >
393
+ <!--
394
+ Drawer header — title (the Q in Q&A) ALWAYS renders
395
+ because it's the actual content of the answer
396
+ pairing, not chrome. Chromeless mode drops the
397
+ surrounding chrome (border, background, ⌃ close)
398
+ but the title stays so the chip → answer link is
399
+ visible above the body. User closes by re-clicking
400
+ the chip.
401
+ -->
402
+ <div
403
+ data-chip-payload-header
404
+ style=${styleMap(payloadHeaderStyles(this.chromeless))}
405
+ >
406
+ <span>${open.config.title}</span>
407
+ ${this.chromeless ? nothing : html`<button
408
+ type="button"
409
+ data-chip-payload-close
410
+ aria-label="Collapse"
411
+ @click=${this._onPayloadClose}
412
+ style=${styleMap(payloadCloseStyles())}
413
+ >⌃</button>`}
414
+ </div>
415
+ <div style=${styleMap(payloadBodyStyles(this.chromeless))}>
416
+ <!--
417
+ Payload widget mounts here imperatively in updated().
418
+ The mount container is a stable element across renders so
419
+ the runtime registry can track + update + unmount
420
+ properly. If the widget id is unknown to the registry,
421
+ we leave a labeled placeholder so the gap is visible
422
+ in development rather than silently empty.
423
+ -->
424
+ ${this.runtimeRef?.widgets?.has(open.config.payload.widget) ? html`<div data-chip-payload-mount></div>` : html`<p style=${styleMap(unresolvedStyles())}>
425
+ Widget <code>${open.config.payload.widget}</code> not registered.
426
+ </p>`}
427
+ </div>
428
+ </div>` : nothing}
429
+ </div>
430
+ `;
431
+ }
432
+ };
433
+ AdaptiveChipsStrip.properties = {
434
+ chips: { attribute: false },
435
+ runtimeRef: { attribute: false },
436
+ chromeless: { type: Boolean, reflect: true },
437
+ _openId: { state: true },
438
+ _canScrollLeft: { state: true },
439
+ _canScrollRight: { state: true }
440
+ };
441
+ function rootStyles() {
442
+ return {
443
+ display: "flex",
444
+ flexDirection: "column",
445
+ gap: "10px",
446
+ width: "100%"
447
+ };
448
+ }
449
+ function stripFrameStyles() {
450
+ return {
451
+ position: "relative",
452
+ display: "flex",
453
+ alignItems: "center",
454
+ width: "100%"
455
+ };
456
+ }
457
+ function stripStyles() {
458
+ return {
459
+ display: "flex",
460
+ flexDirection: "row",
461
+ // Single row — chips never wrap. Horizontal overflow becomes
462
+ // a scrollable region with left/right arrow affordances on either
463
+ // side of the frame. `flex-wrap: wrap` (the old behavior) packed
464
+ // chips into multiple rows, which broke the mini-canvas surface's
465
+ // visual rhythm — chat-bar above, ONE row of chips, payload below.
466
+ flexWrap: "nowrap",
467
+ gap: "8px",
468
+ width: "100%",
469
+ overflowX: "auto",
470
+ overflowY: "hidden",
471
+ // Hide the native scrollbar — the chevron buttons are the
472
+ // affordance. Firefox: `scrollbarWidth: 'none'`. WebKit: the
473
+ // global `::-webkit-scrollbar { display: none }` rule in
474
+ // chatTrailGlobalStyles.ts (which is shared chat-surface CSS) is
475
+ // what hides it across all our scroll surfaces.
476
+ scrollbarWidth: "none",
477
+ scrollSnapType: "x proximity",
478
+ scrollBehavior: "smooth",
479
+ // Don't let touch scroll bubble up to the page when the strip
480
+ // can scroll horizontally — keeps wheel scroll on the canvas
481
+ // intact while letting the strip move under the cursor.
482
+ overscrollBehavior: "contain"
483
+ };
484
+ }
485
+ function scrollButtonStyles(side) {
486
+ return {
487
+ position: "absolute",
488
+ [side]: "0",
489
+ top: "50%",
490
+ transform: "translateY(-50%)",
491
+ zIndex: "2",
492
+ width: "24px",
493
+ height: "24px",
494
+ display: "inline-flex",
495
+ alignItems: "center",
496
+ justifyContent: "center",
497
+ padding: "0",
498
+ borderRadius: "9999px",
499
+ border: "1px solid hsl(var(--sc-tile-border-color, 0 0% 100%) / 0.18)",
500
+ background: "hsl(var(--sc-accent-color) / 0.18)",
501
+ backdropFilter: "blur(6px)",
502
+ WebkitBackdropFilter: "blur(6px)",
503
+ color: "var(--sc-tile-title-color, currentColor)",
504
+ fontSize: "14px",
505
+ lineHeight: "1",
506
+ cursor: "pointer",
507
+ opacity: "0.85",
508
+ transition: "opacity 150ms ease, background 150ms ease"
509
+ };
510
+ }
511
+ function chipStyles(isOpen, _chromeless) {
512
+ return {
513
+ display: "inline-flex",
514
+ alignItems: "center",
515
+ gap: "6px",
516
+ // Flex to text width up to a sane cap. The title span inside owns
517
+ // the ellipsis safety net for genuinely-long titles.
518
+ maxWidth: "32ch",
519
+ color: isOpen ? "var(--sc-tile-title-color, currentColor)" : "var(--sc-tile-text-color, currentColor)",
520
+ fontSize: "12px",
521
+ fontWeight: "600",
522
+ cursor: "pointer",
523
+ transition: "background 180ms ease, border-color 180ms ease, color 180ms ease",
524
+ padding: "6px 12px",
525
+ borderRadius: "9999px",
526
+ border: isOpen ? "1px solid hsl(var(--sc-accent-color) / 0.45)" : "1px solid hsl(var(--sc-tile-border-color, 0 0% 100%) / 0.18)",
527
+ background: isOpen ? "hsl(var(--sc-accent-color) / 0.10)" : "transparent"
528
+ };
529
+ }
530
+ function chipTitleStyles() {
531
+ return {
532
+ whiteSpace: "nowrap",
533
+ overflow: "hidden",
534
+ textOverflow: "ellipsis",
535
+ minWidth: "0"
536
+ };
537
+ }
538
+ function chipDismissStyles() {
539
+ return {
540
+ display: "inline-flex",
541
+ alignItems: "center",
542
+ justifyContent: "center",
543
+ width: "16px",
544
+ height: "16px",
545
+ marginLeft: "2px",
546
+ borderRadius: "9999px",
547
+ color: "inherit",
548
+ fontSize: "12px",
549
+ lineHeight: "1",
550
+ cursor: "pointer",
551
+ opacity: "0.45",
552
+ flexShrink: "0",
553
+ transition: "opacity 150ms ease, background 150ms ease"
554
+ };
555
+ }
556
+ function chipGlyphStyles() {
557
+ return {
558
+ fontSize: "10px",
559
+ opacity: "0.7",
560
+ flexShrink: "0"
561
+ };
562
+ }
563
+ function payloadStyles(chromeless) {
564
+ const base = {
565
+ display: "flex",
566
+ flexDirection: "column",
567
+ gap: "6px",
568
+ color: "var(--sc-tile-title-color, currentColor)"
569
+ };
570
+ if (chromeless) {
571
+ return {
572
+ ...base,
573
+ padding: "4px 0 0",
574
+ borderRadius: "0",
575
+ border: "none",
576
+ background: "transparent"
577
+ };
578
+ }
579
+ return {
580
+ ...base,
581
+ padding: "10px 12px",
582
+ borderRadius: "var(--sc-tile-border-radius, 12px)",
583
+ border: "1px solid hsl(var(--sc-accent-color) / 0.18)",
584
+ background: "hsl(var(--sc-accent-color) / 0.04)"
585
+ };
586
+ }
587
+ function payloadHeaderStyles(chromeless) {
588
+ return {
589
+ display: "flex",
590
+ alignItems: "center",
591
+ justifyContent: "space-between",
592
+ gap: "8px",
593
+ fontSize: "12px",
594
+ fontWeight: "600",
595
+ // In chromeless mode the header is the only visible heading for
596
+ // the answer below (no surrounding box/border). Use the accent
597
+ // color so the Q reads distinctly as the prompt for the A.
598
+ ...chromeless ? { color: "hsl(var(--sc-accent-color) / 0.95)" } : {}
599
+ };
600
+ }
601
+ function payloadCloseStyles() {
602
+ return {
603
+ width: "22px",
604
+ height: "22px",
605
+ display: "inline-flex",
606
+ alignItems: "center",
607
+ justifyContent: "center",
608
+ borderRadius: "9999px",
609
+ background: "transparent",
610
+ border: "1px solid hsl(var(--sc-tile-border-color, 0 0% 100%) / 0.18)",
611
+ color: "inherit",
612
+ fontSize: "11px",
613
+ cursor: "pointer",
614
+ opacity: "0.65"
615
+ };
616
+ }
617
+ function payloadBodyStyles(chromeless) {
618
+ return {
619
+ fontSize: "12px",
620
+ lineHeight: "1.55",
621
+ color: "var(--sc-tile-text-color, currentColor)",
622
+ // No top divider needed when the header is suppressed.
623
+ ...chromeless ? {} : {}
624
+ };
625
+ }
626
+ function unresolvedStyles() {
627
+ return {
628
+ margin: "0",
629
+ fontStyle: "italic",
630
+ opacity: "0.7"
631
+ };
632
+ }
633
+ if (!customElements.get("adaptive-chips-strip")) {
634
+ customElements.define("adaptive-chips-strip", AdaptiveChipsStrip);
635
+ }
636
+
637
+ export {
638
+ SuggestionChipSchema,
639
+ AdaptiveChipsStrip
640
+ };
641
+ //# sourceMappingURL=chunk-GP4OSTZT.js.map