@noodleseed/assistant 1.25.1 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +54 -20
  2. package/dist/{app-view-element-BNkQab1B.d.ts → app-view-element-C_ATXseu.d.ts} +1 -1
  3. package/dist/{app-view-element-DhuiNW_x.d.cts → app-view-element-CiFVNIke.d.cts} +1 -1
  4. package/dist/app-view.cjs +5 -5
  5. package/dist/app-view.cjs.map +1 -1
  6. package/dist/app-view.d.cts +3 -3
  7. package/dist/app-view.d.ts +3 -3
  8. package/dist/app-view.js +2 -2
  9. package/dist/{appearance-B6vSg8Ub.d.cts → appearance-C24W6BRj.d.cts} +7 -3
  10. package/dist/{appearance-B6vSg8Ub.d.ts → appearance-C24W6BRj.d.ts} +7 -3
  11. package/dist/{chunk-5TYS2Q6S.js → chunk-5NBHHNI3.js} +2 -2
  12. package/dist/{chunk-FJYPE4GC.js → chunk-5PW6PXCI.js} +702 -122
  13. package/dist/chunk-5PW6PXCI.js.map +1 -0
  14. package/dist/{chunk-7BQHW37I.js → chunk-FNXAVRL5.js} +7 -1
  15. package/dist/{chunk-7BQHW37I.js.map → chunk-FNXAVRL5.js.map} +1 -1
  16. package/dist/{chunk-CF2SCDLF.js → chunk-XKTUXFGX.js} +6 -6
  17. package/dist/chunk-XKTUXFGX.js.map +1 -0
  18. package/dist/{client-BkogON2A.d.ts → client-BheCOERq.d.ts} +51 -2
  19. package/dist/{client-C07ppTU_.d.cts → client-BiMRVxjs.d.cts} +51 -2
  20. package/dist/client.cjs.map +1 -1
  21. package/dist/client.d.cts +2 -2
  22. package/dist/client.d.ts +2 -2
  23. package/dist/client.js +1 -1
  24. package/dist/embed.global.js +171 -89
  25. package/dist/index.cjs +709 -124
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +6 -53
  28. package/dist/index.d.ts +6 -53
  29. package/dist/index.js +3 -3
  30. package/dist/react/client.cjs.map +1 -1
  31. package/dist/react/client.d.cts +2 -2
  32. package/dist/react/client.d.ts +2 -2
  33. package/dist/react/client.js +1 -1
  34. package/dist/react.cjs +709 -124
  35. package/dist/react.cjs.map +1 -1
  36. package/dist/react.d.cts +4 -4
  37. package/dist/react.d.ts +4 -4
  38. package/dist/react.js +4 -4
  39. package/dist/server.d.cts +1 -1
  40. package/dist/server.d.ts +1 -1
  41. package/package.json +1 -1
  42. package/dist/chunk-CF2SCDLF.js.map +0 -1
  43. package/dist/chunk-FJYPE4GC.js.map +0 -1
  44. /package/dist/{chunk-5TYS2Q6S.js.map → chunk-5NBHHNI3.js.map} +0 -0
@@ -2,18 +2,22 @@ import {
2
2
  ASSISTANT_APP_VIEW_CSS,
3
3
  mountAssistantApp,
4
4
  updateAssistantAppTheme
5
- } from "./chunk-CF2SCDLF.js";
5
+ } from "./chunk-XKTUXFGX.js";
6
6
  import {
7
7
  AssistantClientError,
8
8
  copyAssistantModelContext,
9
9
  copyAssistantPageContext,
10
10
  createAssistantClient,
11
+ publicConfigurationUrl,
11
12
  sessionSourceKey
12
- } from "./chunk-7BQHW37I.js";
13
+ } from "./chunk-FNXAVRL5.js";
14
+ import {
15
+ external_exports
16
+ } from "./chunk-3YYTUJPJ.js";
13
17
 
14
18
  // src/appearance.ts
15
19
  var common = {
16
- fontFamily: "inherit",
20
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
17
21
  monoFontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
18
22
  baseFontSize: 15,
19
23
  messageFontSize: 15,
@@ -24,7 +28,7 @@ var common = {
24
28
  buttonRadius: 999,
25
29
  launcherRadius: 999,
26
30
  spacing: 8,
27
- backdropBlur: 18,
31
+ backdropBlur: 6,
28
32
  motionScale: 1
29
33
  };
30
34
  var DEFAULT_APPEARANCE = {
@@ -44,7 +48,7 @@ var DEFAULT_APPEARANCE = {
44
48
  accent: "#C2410C",
45
49
  accentText: "#FFFFFF",
46
50
  link: "#9A3412",
47
- divider: "#E4E4E7",
51
+ divider: "rgba(0, 0, 0, 0.1)",
48
52
  focus: "#C2410C",
49
53
  success: "#16835D",
50
54
  warning: "#A45B00",
@@ -52,7 +56,7 @@ var DEFAULT_APPEARANCE = {
52
56
  overlay: "#18181B66",
53
57
  code: "#F1F1F3",
54
58
  widgetFrame: "#E4E4E7",
55
- shadow: "0 4px 16px rgba(0, 0, 0, 0.06), 0 24px 70px rgba(0, 0, 0, 0.10)"
59
+ shadow: "0 4px 16px rgba(0, 0, 0, 0.06)"
56
60
  },
57
61
  dark: {
58
62
  ...common,
@@ -67,7 +71,7 @@ var DEFAULT_APPEARANCE = {
67
71
  accent: "#FB923C",
68
72
  accentText: "#1C0A02",
69
73
  link: "#FDBA74",
70
- divider: "#292524",
74
+ divider: "rgba(255, 255, 255, 0.1)",
71
75
  focus: "#FB923C",
72
76
  success: "#58C69A",
73
77
  warning: "#F2B45F",
@@ -75,15 +79,15 @@ var DEFAULT_APPEARANCE = {
75
79
  overlay: "#00000099",
76
80
  code: "#26262C",
77
81
  widgetFrame: "#34343B",
78
- shadow: "0 8px 32px rgba(0, 0, 0, 0.48)"
82
+ shadow: "0 8px 32px rgba(0, 0, 0, 0.4)"
79
83
  }
80
84
  },
81
85
  layout: {
82
86
  mode: "floating",
83
- position: "bottom-right",
84
- panelWidth: 520,
85
- panelMinHeight: 540,
86
- panelMaxHeight: 820,
87
+ position: "bottom-center",
88
+ panelWidth: 970,
89
+ panelMinHeight: 0,
90
+ panelMaxHeight: 1025,
87
91
  edgeOffset: 20,
88
92
  zIndex: 2147483e3,
89
93
  density: "comfortable",
@@ -96,11 +100,13 @@ var DEFAULT_APPEARANCE = {
96
100
  showLauncher: true,
97
101
  showHeader: true,
98
102
  showAvatars: true,
99
- showTimestamps: false
103
+ showTimestamps: false,
104
+ showPoweredBy: true
100
105
  },
101
106
  labels: {
102
- welcomeHeading: "How can I help?",
107
+ welcomeHeading: "",
103
108
  welcomeMessage: "",
109
+ launcherPlaceholder: "Chat with us",
104
110
  composerPlaceholder: "Ask a question\u2026",
105
111
  thinking: "Thinking",
106
112
  send: "Send",
@@ -134,11 +140,12 @@ var DEFAULT_APPEARANCE = {
134
140
  },
135
141
  presentation: {
136
142
  panel: {
137
- surface: "glass",
143
+ surface: "solid",
138
144
  elevation: "soft",
139
145
  border: "subtle"
140
146
  },
141
147
  launcher: {
148
+ style: "pill",
142
149
  icon: "brand-mark",
143
150
  size: "md",
144
151
  status: "none",
@@ -148,7 +155,11 @@ var DEFAULT_APPEARANCE = {
148
155
  composer: { leadingIcon: "none", sendIcon: "arrow-up", shape: "pill" },
149
156
  messages: { userStyle: "bubble", assistantStyle: "plain" }
150
157
  },
151
- suggestedPrompts: [],
158
+ suggestedPrompts: [
159
+ "What products do you have?",
160
+ "Tell me about your business",
161
+ "How can I get in touch?"
162
+ ],
152
163
  locale: "en",
153
164
  direction: "auto"
154
165
  };
@@ -164,7 +175,7 @@ function resolveAppearance(input = {}) {
164
175
  mark: themedAsset(branding?.mark)
165
176
  },
166
177
  theme: {
167
- defaultMode: branding?.colorScheme ?? DEFAULT_APPEARANCE.theme.defaultMode,
178
+ defaultMode: assistant?.theme ?? branding?.colorScheme ?? DEFAULT_APPEARANCE.theme.defaultMode,
168
179
  light: resolveTheme("light", branding),
169
180
  dark: resolveTheme("dark", branding)
170
181
  },
@@ -175,9 +186,17 @@ function resolveAppearance(input = {}) {
175
186
  ...assistant?.layout?.edgeOffset === void 0 ? {} : { edgeOffset: boundedNumber(assistant.layout.edgeOffset, 0, 96) }
176
187
  },
177
188
  behavior: { ...DEFAULT_APPEARANCE.behavior, ...assistant?.behavior },
178
- labels: { ...DEFAULT_APPEARANCE.labels, ...assistant?.labels },
189
+ labels: {
190
+ ...DEFAULT_APPEARANCE.labels,
191
+ ...branding?.name ? { launcherPlaceholder: `Chat with ${branding.name}` } : {},
192
+ ...assistant?.labels
193
+ },
179
194
  presentation: resolvePresentation(assistant?.presentation),
180
- suggestedPrompts: assistant?.suggestedPrompts ?? DEFAULT_APPEARANCE.suggestedPrompts,
195
+ suggestedPrompts: assistant?.suggestedPrompts ?? (branding?.name ? [
196
+ "What products do you have?",
197
+ `Tell me about ${branding.name}`,
198
+ "How can I get in touch?"
199
+ ] : DEFAULT_APPEARANCE.suggestedPrompts),
181
200
  ...assistant?.privacyUrl ? { privacyUrl: assistant.privacyUrl } : {},
182
201
  ...assistant?.termsUrl ? { termsUrl: assistant.termsUrl } : {},
183
202
  locale: assistant?.locale ?? DEFAULT_APPEARANCE.locale,
@@ -1199,11 +1218,11 @@ function createDOMPurify() {
1199
1218
  const originalDocument = document2;
1200
1219
  const currentScript = originalDocument.currentScript;
1201
1220
  window2.DocumentFragment;
1202
- const HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
1221
+ const HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element2 = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
1203
1222
  _window$NamedNodeMap === void 0 ? window2.NamedNodeMap || window2.MozNamedAttrMap : _window$NamedNodeMap;
1204
1223
  window2.HTMLFormElement;
1205
1224
  const DOMParser = window2.DOMParser, trustedTypes = window2.trustedTypes;
1206
- const ElementPrototype = Element.prototype;
1225
+ const ElementPrototype = Element2.prototype;
1207
1226
  const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
1208
1227
  const remove = lookupGetter(ElementPrototype, "remove");
1209
1228
  const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
@@ -3853,19 +3872,40 @@ function queryRequired(root, selector) {
3853
3872
  if (!element) throw new Error(`assistant renderer is missing ${selector}`);
3854
3873
  return element;
3855
3874
  }
3856
- function assistantElementMarkup(appearance, open, styles) {
3875
+ function assistantElementMarkup(appearance, styles) {
3857
3876
  return `${styles}
3858
- <button class="launcher" type="button" aria-label="${escapeAttribute(appearance.labels.open)}" aria-controls="assistant-panel" aria-expanded="${String(open)}"><slot name="launcher-icon"></slot><span class="visually-hidden" data-session-status aria-live="polite" hidden></span></button>
3877
+ <div class="launcher">
3878
+ <slot name="launcher-icon"></slot>
3879
+ <span class="launcher-loader" aria-hidden="true"></span>
3880
+ <button class="launcher-trigger" type="button" aria-label="${escapeAttribute(appearance.labels.open)}" aria-controls="assistant-launcher-form" aria-expanded="false">
3881
+ <span class="launcher-label">${escapeAttribute(appearance.labels.launcherPlaceholder)}</span>
3882
+ <span class="launcher-arrow" aria-hidden="true"></span>
3883
+ </button>
3884
+ <form class="launcher-form" id="assistant-launcher-form" aria-hidden="true" autocomplete="off">
3885
+ <input class="launcher-input" type="text" aria-label="${escapeAttribute(appearance.labels.launcherPlaceholder)}" placeholder="${escapeAttribute(appearance.labels.launcherPlaceholder)}" tabindex="-1">
3886
+ <button class="launcher-send" type="submit" aria-label="${escapeAttribute(appearance.labels.send)}"></button>
3887
+ </form>
3888
+ <span class="visually-hidden" data-session-status aria-live="polite" hidden></span>
3889
+ </div>
3859
3890
  <section class="panel" id="assistant-panel" role="dialog" aria-label="${escapeAttribute(appearance.brand.name)}">
3860
3891
  <header><slot name="header-leading"></slot><img class="brand-logo" alt=""><strong></strong><slot name="header-actions"></slot><button class="close" type="button" aria-label="${escapeAttribute(appearance.labels.close)}"></button></header>
3861
3892
  <div class="welcome"><slot name="empty-state"></slot></div>
3862
- <div class="suggested-prompts"></div>
3863
3893
  <div class="messages-region">
3864
3894
  <div class="messages" aria-live="polite"></div>
3865
3895
  <button class="new-messages" type="button" aria-label="${escapeAttribute(appearance.labels.newMessages)}" hidden></button>
3866
3896
  </div>
3867
- <form><slot name="composer-leading"></slot><textarea rows="1"></textarea><button class="send" type="submit"></button><slot name="composer-trailing"></slot></form>
3897
+ <div class="input-section">
3898
+ <div class="suggested-prompts"></div>
3899
+ <form class="composer"><slot name="composer-leading"></slot><textarea rows="1"></textarea><button class="send" type="submit"></button><slot name="composer-trailing"></slot></form>
3900
+ </div>
3868
3901
  <nav class="legal" aria-label="Legal"></nav>
3902
+ <div class="powered-by-row">
3903
+ <a class="powered-by" href="https://noodleseed.com" target="_blank" rel="noopener noreferrer">
3904
+ <span>Powered by</span>
3905
+ <span class="powered-by-icon" aria-hidden="true"><svg width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="16" height="16" rx="8" fill="currentColor" fill-opacity="0.12"></rect><path d="M8.58023 12.8485C6.27833 12.8463 4.41315 11.0141 4.42117 8.75681L4.43588 4.61714L8.66254 4.62118C10.9644 4.62339 12.8296 6.45561 12.8216 8.71287C12.8135 10.9975 10.9128 12.8507 8.58023 12.8485Z" fill="currentColor"></path><path d="M8.82627 4.76869C11.1282 4.7709 12.9933 6.60312 12.9853 8.86038L12.9706 13L8.74395 12.996C6.44206 12.9938 4.57687 11.1616 4.5849 8.90432C4.59302 6.6197 6.49364 4.76646 8.82627 4.76869Z" fill="currentColor"></path><ellipse cx="3.69536" cy="3.68106" rx="0.695363" ry="0.681056" fill="currentColor"></ellipse></svg></span>
3906
+ <span class="powered-by-name">Noodle Seed</span>
3907
+ </a>
3908
+ </div>
3869
3909
  <slot name="conversation-footer"></slot>
3870
3910
  </section>`;
3871
3911
  }
@@ -3884,6 +3924,230 @@ function appendLegalLinks(legal, appearance) {
3884
3924
  }
3885
3925
  }
3886
3926
 
3927
+ // src/element-theme-resolution.ts
3928
+ function resolveAssistantTheme(input) {
3929
+ if (input.configured === "light" || input.configured === "dark") return input.configured;
3930
+ const hostMode = resolveHostTheme(input.host, input.media);
3931
+ return input.configured === "invert" ? opposite(hostMode) : hostMode;
3932
+ }
3933
+ function resolveHostTheme(host, media) {
3934
+ const view = host.ownerDocument.defaultView;
3935
+ for (let node = host.parentElement; node; node = node.parentElement) {
3936
+ const declared = declaredTheme(node);
3937
+ if (declared) return declared;
3938
+ const computed = computedTheme(view?.getComputedStyle(node).colorScheme);
3939
+ if (computed) return computed;
3940
+ }
3941
+ return media?.matches ? "dark" : "light";
3942
+ }
3943
+ function computedTheme(colorScheme) {
3944
+ const modes = colorScheme?.split(/\s+/) ?? [];
3945
+ if (modes.includes("dark") && !modes.includes("light")) return "dark";
3946
+ if (modes.includes("light") && !modes.includes("dark")) return "light";
3947
+ return void 0;
3948
+ }
3949
+ function declaredTheme(element) {
3950
+ const value = element.getAttribute("data-theme") ?? element.getAttribute("data-color-scheme") ?? element.getAttribute("data-mode");
3951
+ if (value === "dark" || value === "light") return value;
3952
+ if (element.classList.contains("dark")) return "dark";
3953
+ if (element.classList.contains("light")) return "light";
3954
+ return void 0;
3955
+ }
3956
+ function opposite(mode) {
3957
+ return mode === "dark" ? "light" : "dark";
3958
+ }
3959
+
3960
+ // src/element-host-environment-controller.ts
3961
+ var AssistantElementHostEnvironmentController = class {
3962
+ #host;
3963
+ #appearance;
3964
+ #applyTheme;
3965
+ #close;
3966
+ #media;
3967
+ #themeObserver;
3968
+ constructor(options) {
3969
+ this.#host = options.host;
3970
+ this.#appearance = options.appearance;
3971
+ this.#applyTheme = options.applyTheme;
3972
+ this.#close = options.close;
3973
+ }
3974
+ get media() {
3975
+ return this.#media;
3976
+ }
3977
+ resolvedTheme() {
3978
+ return resolveAssistantTheme({
3979
+ host: this.#host,
3980
+ configured: this.#host.getAttribute("theme") ?? this.#appearance().theme.defaultMode,
3981
+ ...this.#media ? { media: this.#media } : {}
3982
+ });
3983
+ }
3984
+ connect() {
3985
+ this.disconnect();
3986
+ this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
3987
+ this.#media?.addEventListener("change", this.#applyTheme);
3988
+ this.#themeObserver = new MutationObserver(this.#applyTheme);
3989
+ for (let node = this.#host.parentElement; node; node = node.parentElement) {
3990
+ this.#themeObserver.observe(node, {
3991
+ attributes: true,
3992
+ attributeFilter: ["class", "data-theme", "data-color-scheme", "data-mode", "style"]
3993
+ });
3994
+ }
3995
+ document.addEventListener("keydown", this.#handleDocumentKeydown);
3996
+ document.addEventListener("pointerdown", this.#handleDocumentPointerDown);
3997
+ }
3998
+ disconnect() {
3999
+ this.#media?.removeEventListener("change", this.#applyTheme);
4000
+ this.#media = void 0;
4001
+ this.#themeObserver?.disconnect();
4002
+ this.#themeObserver = void 0;
4003
+ document.removeEventListener("keydown", this.#handleDocumentKeydown);
4004
+ document.removeEventListener("pointerdown", this.#handleDocumentPointerDown);
4005
+ }
4006
+ #handleDocumentKeydown = (event) => {
4007
+ if (event.key === "Escape" && this.#host.hasAttribute("open") && this.#appearance().behavior.closeOnEscape) {
4008
+ this.#close();
4009
+ }
4010
+ };
4011
+ #handleDocumentPointerDown = (event) => {
4012
+ if (this.#host.hasAttribute("open") && this.#appearance().behavior.closeOnOutsideClick && !event.composedPath().includes(this.#host)) {
4013
+ this.#close();
4014
+ }
4015
+ };
4016
+ };
4017
+
4018
+ // src/element-launcher-controller.ts
4019
+ var AssistantElementLauncherController = class {
4020
+ #host;
4021
+ #appearance;
4022
+ #openPanel;
4023
+ #sendMessage;
4024
+ #root;
4025
+ constructor(options) {
4026
+ this.#host = options.host;
4027
+ this.#appearance = options.appearance;
4028
+ this.#openPanel = options.openPanel;
4029
+ this.#sendMessage = options.sendMessage;
4030
+ }
4031
+ connect(root) {
4032
+ this.disconnect();
4033
+ this.#root = root;
4034
+ queryRequired(root, ".launcher").addEventListener(
4035
+ "click",
4036
+ this.#handleLauncherClick
4037
+ );
4038
+ queryRequired(root, ".launcher-trigger").addEventListener(
4039
+ "click",
4040
+ this.#handleTrigger
4041
+ );
4042
+ queryRequired(root, ".launcher-form").addEventListener(
4043
+ "submit",
4044
+ this.#handleSubmit
4045
+ );
4046
+ queryRequired(root, ".launcher-input").addEventListener(
4047
+ "keydown",
4048
+ this.#handleInputKeydown
4049
+ );
4050
+ document.addEventListener("pointerdown", this.#handleDocumentPointerDown);
4051
+ this.sync();
4052
+ }
4053
+ disconnect() {
4054
+ document.removeEventListener("pointerdown", this.#handleDocumentPointerDown);
4055
+ this.#root = void 0;
4056
+ }
4057
+ sync() {
4058
+ const root = this.#root;
4059
+ if (!root) return;
4060
+ const appearance = this.#appearance();
4061
+ const style = appearance.presentation.launcher.style;
4062
+ this.#host.dataset.launcherStyle = style;
4063
+ const trigger = queryRequired(root, ".launcher-trigger");
4064
+ const label = queryRequired(root, ".launcher-label");
4065
+ const input = queryRequired(root, ".launcher-input");
4066
+ label.textContent = appearance.labels.launcherPlaceholder;
4067
+ input.placeholder = appearance.labels.launcherPlaceholder;
4068
+ input.setAttribute("aria-label", appearance.labels.launcherPlaceholder);
4069
+ trigger.setAttribute("aria-label", appearance.labels.open);
4070
+ queryRequired(root, ".launcher-send").setAttribute(
4071
+ "aria-label",
4072
+ appearance.labels.send
4073
+ );
4074
+ queryRequired(root, ".launcher").hidden = !appearance.behavior.showLauncher;
4075
+ queryRequired(root, ".powered-by-row").hidden = !appearance.behavior.showPoweredBy;
4076
+ if (style === "bubble" || this.#host.hasAttribute("open")) this.collapse(true);
4077
+ }
4078
+ collapse(force = false) {
4079
+ const root = this.#root;
4080
+ if (!root) return;
4081
+ const input = queryRequired(root, ".launcher-input");
4082
+ if (!force && input.value.trim()) return;
4083
+ this.#host.removeAttribute("launcher-expanded");
4084
+ const form = queryRequired(root, ".launcher-form");
4085
+ const trigger = queryRequired(root, ".launcher-trigger");
4086
+ form.setAttribute("aria-hidden", "true");
4087
+ trigger.setAttribute("aria-expanded", "false");
4088
+ input.tabIndex = -1;
4089
+ input.blur();
4090
+ }
4091
+ focusTrigger() {
4092
+ this.#root?.querySelector(".launcher-trigger")?.focus();
4093
+ }
4094
+ #expand() {
4095
+ const root = this.#root;
4096
+ if (!root || this.#appearance().presentation.launcher.style === "bubble") return;
4097
+ this.#host.setAttribute("launcher-expanded", "");
4098
+ const form = queryRequired(root, ".launcher-form");
4099
+ const trigger = queryRequired(root, ".launcher-trigger");
4100
+ const input = queryRequired(root, ".launcher-input");
4101
+ form.setAttribute("aria-hidden", "false");
4102
+ trigger.setAttribute("aria-expanded", "true");
4103
+ input.tabIndex = 0;
4104
+ requestAnimationFrame(() => {
4105
+ if (this.#host.hasAttribute("launcher-expanded")) input.focus();
4106
+ });
4107
+ }
4108
+ #handleTrigger = () => {
4109
+ if (this.#appearance().presentation.launcher.style === "bubble") {
4110
+ this.#openPanel();
4111
+ return;
4112
+ }
4113
+ this.#expand();
4114
+ };
4115
+ #handleLauncherClick = (event) => {
4116
+ if (event.composedPath().some(
4117
+ (target) => target instanceof Element && target.matches(".launcher-trigger, .launcher-form")
4118
+ )) {
4119
+ return;
4120
+ }
4121
+ this.#handleTrigger();
4122
+ };
4123
+ #handleSubmit = (event) => {
4124
+ event.preventDefault();
4125
+ const input = this.#root?.querySelector(".launcher-input");
4126
+ const message = input?.value.trim() ?? "";
4127
+ if (input) input.value = "";
4128
+ this.#openPanel();
4129
+ if (message) this.#sendMessage(message);
4130
+ };
4131
+ #handleInputKeydown = (event) => {
4132
+ if (event.key === "Enter" && !event.isComposing) {
4133
+ event.preventDefault();
4134
+ this.#root?.querySelector(".launcher-form")?.requestSubmit();
4135
+ return;
4136
+ }
4137
+ if (event.key !== "Escape") return;
4138
+ event.preventDefault();
4139
+ event.stopPropagation();
4140
+ const input = this.#root?.querySelector(".launcher-input");
4141
+ if (input) input.value = "";
4142
+ this.collapse(true);
4143
+ this.focusTrigger();
4144
+ };
4145
+ #handleDocumentPointerDown = (event) => {
4146
+ if (event.composedPath().includes(this.#host)) return;
4147
+ this.collapse();
4148
+ };
4149
+ };
4150
+
3887
4151
  // src/element-page-context-controller.ts
3888
4152
  var AssistantElementPageContextController = class {
3889
4153
  #snapshot;
@@ -3937,10 +4201,12 @@ var AssistantElementPresentationGate = class {
3937
4201
  syncOpenState() {
3938
4202
  const open = this.#host.hasAttribute("open");
3939
4203
  const presentationReady = this.#host.hasAttribute("data-presentation-ready");
3940
- const launcher = this.#host.shadowRoot?.querySelector(".launcher");
3941
- launcher?.setAttribute("aria-expanded", String(open && presentationReady));
3942
- launcher?.setAttribute("aria-busy", String(open && !presentationReady));
3943
- if (launcher) launcher.disabled = open && !presentationReady;
4204
+ const publicConfigurationLoading = this.#host.hasAttribute("data-public-configuration-loading");
4205
+ const presentationBlocked = !presentationReady && (open || publicConfigurationLoading);
4206
+ const launcher = this.#host.shadowRoot?.querySelector(".launcher-trigger");
4207
+ launcher?.setAttribute("aria-busy", String(presentationBlocked));
4208
+ if (launcher) launcher.disabled = presentationBlocked;
4209
+ this.#host.shadowRoot?.querySelector(".panel")?.setAttribute("aria-hidden", String(!(open && presentationReady)));
3944
4210
  if (open && this.#host.isConnected) this.#primeSession();
3945
4211
  }
3946
4212
  reveal() {
@@ -3951,6 +4217,233 @@ var AssistantElementPresentationGate = class {
3951
4217
  }
3952
4218
  };
3953
4219
 
4220
+ // src/assistant-configuration-schema.ts
4221
+ var hexColorSchema = external_exports.string().regex(/^#[0-9A-Fa-f]{6}$/);
4222
+ var browserAssetUrlSchema = external_exports.string().trim().url().refine(isSecureBrowserAssetUrl, "must use https (http is allowed only for loopback testing)");
4223
+ var httpsUrlSchema = external_exports.string().trim().url().refine(isHttpsUrl, "must use https");
4224
+ var brandAssetSchema = external_exports.object({
4225
+ uri: browserAssetUrlSchema,
4226
+ darkUri: browserAssetUrlSchema.optional(),
4227
+ alt: external_exports.string().trim().min(1).max(240)
4228
+ }).strict();
4229
+ var brandThemeSchema = external_exports.object({
4230
+ surface: hexColorSchema.optional(),
4231
+ surfaceRaised: hexColorSchema.optional(),
4232
+ surfaceMuted: hexColorSchema.optional(),
4233
+ text: hexColorSchema.optional(),
4234
+ textMuted: hexColorSchema.optional(),
4235
+ accent: hexColorSchema.optional(),
4236
+ accentText: hexColorSchema.optional(),
4237
+ link: hexColorSchema.optional(),
4238
+ border: hexColorSchema.optional(),
4239
+ borderStrong: hexColorSchema.optional(),
4240
+ focus: hexColorSchema.optional(),
4241
+ success: hexColorSchema.optional(),
4242
+ warning: hexColorSchema.optional(),
4243
+ danger: hexColorSchema.optional(),
4244
+ code: hexColorSchema.optional()
4245
+ }).strict();
4246
+ var brandingSchema = external_exports.object({
4247
+ name: external_exports.string().trim().min(1).max(160).optional(),
4248
+ accent: hexColorSchema.optional(),
4249
+ surface: hexColorSchema.optional(),
4250
+ surfaceDark: hexColorSchema.optional(),
4251
+ logo: brandAssetSchema.optional(),
4252
+ mark: brandAssetSchema.optional(),
4253
+ avatar: brandAssetSchema.optional(),
4254
+ theme: external_exports.object({ light: brandThemeSchema.optional(), dark: brandThemeSchema.optional() }).strict().optional(),
4255
+ radius: external_exports.enum(["none", "sm", "md", "lg"]).optional(),
4256
+ density: external_exports.enum(["compact", "comfortable"]).optional(),
4257
+ typography: external_exports.enum(["system", "serif", "mono"]).optional(),
4258
+ colorScheme: external_exports.enum(["auto", "light", "dark"]).optional()
4259
+ }).strict();
4260
+ var labelsSchema = external_exports.object({
4261
+ welcomeHeading: external_exports.string().trim().min(1).max(160).optional(),
4262
+ welcomeMessage: external_exports.string().max(1e3).optional(),
4263
+ launcherPlaceholder: external_exports.string().trim().min(1).max(160).optional(),
4264
+ composerPlaceholder: external_exports.string().trim().min(1).max(160).optional(),
4265
+ thinking: external_exports.string().trim().min(1).max(80).optional(),
4266
+ send: external_exports.string().trim().min(1).max(80).optional(),
4267
+ stop: external_exports.string().trim().min(1).max(80).optional(),
4268
+ close: external_exports.string().trim().min(1).max(80).optional(),
4269
+ open: external_exports.string().trim().min(1).max(80).optional(),
4270
+ confirm: external_exports.string().trim().min(1).max(80).optional(),
4271
+ decline: external_exports.string().trim().min(1).max(80).optional(),
4272
+ cancel: external_exports.string().trim().min(1).max(80).optional(),
4273
+ confirmationHeading: external_exports.string().trim().min(1).max(120).optional(),
4274
+ additionalDetails: external_exports.string().trim().min(1).max(120).optional(),
4275
+ redacted: external_exports.string().trim().min(1).max(120).optional(),
4276
+ completed: external_exports.string().trim().min(1).max(80).optional(),
4277
+ stopped: external_exports.string().trim().min(1).max(80).optional(),
4278
+ copy: external_exports.string().trim().min(1).max(80).optional(),
4279
+ newMessages: external_exports.string().trim().min(1).max(80).optional(),
4280
+ reconnect: external_exports.string().trim().min(1).max(80).optional(),
4281
+ newConversation: external_exports.string().trim().min(1).max(80).optional(),
4282
+ retry: external_exports.string().trim().min(1).max(80).optional(),
4283
+ unavailable: external_exports.string().trim().min(1).max(240).optional(),
4284
+ sessionExpired: external_exports.string().trim().min(1).max(240).optional(),
4285
+ sessionIdle: external_exports.string().trim().min(1).max(120).optional(),
4286
+ sessionLoading: external_exports.string().trim().min(1).max(120).optional(),
4287
+ sessionReady: external_exports.string().trim().min(1).max(120).optional(),
4288
+ sessionError: external_exports.string().trim().min(1).max(120).optional(),
4289
+ signInHeading: external_exports.string().trim().min(1).max(120).optional(),
4290
+ signInBody: external_exports.string().max(240).optional(),
4291
+ signInAction: external_exports.string().trim().min(1).max(80).optional(),
4292
+ signUpAction: external_exports.string().trim().min(1).max(80).optional()
4293
+ }).strict();
4294
+ var presentationSchema = external_exports.object({
4295
+ panel: external_exports.object({
4296
+ surface: external_exports.enum(["solid", "glass"]).optional(),
4297
+ elevation: external_exports.enum(["soft", "dramatic"]).optional(),
4298
+ border: external_exports.enum(["subtle", "strong"]).optional(),
4299
+ radius: external_exports.number().int().min(0).max(64).optional()
4300
+ }).strict().optional(),
4301
+ launcher: external_exports.object({
4302
+ style: external_exports.enum(["pill", "bubble"]).optional(),
4303
+ icon: external_exports.enum(["brand-mark", "chat", "none"]).optional(),
4304
+ size: external_exports.enum(["md", "lg"]).optional(),
4305
+ status: external_exports.enum(["none", "session"]).optional(),
4306
+ effect: external_exports.enum(["none", "pulse"]).optional()
4307
+ }).strict().optional(),
4308
+ header: external_exports.object({
4309
+ mark: external_exports.enum(["none", "brand-mark", "status"]).optional(),
4310
+ badge: external_exports.object({
4311
+ text: external_exports.string().trim().min(1).max(80),
4312
+ tone: external_exports.enum(["neutral", "success", "warning", "danger"]).optional(),
4313
+ indicator: external_exports.boolean().optional()
4314
+ }).strict().optional()
4315
+ }).strict().optional(),
4316
+ composer: external_exports.object({
4317
+ leadingIcon: external_exports.enum(["none", "brand-mark"]).optional(),
4318
+ sendIcon: external_exports.enum(["arrow-up", "paper-plane"]).optional(),
4319
+ shape: external_exports.enum(["rounded", "pill"]).optional()
4320
+ }).strict().optional(),
4321
+ messages: external_exports.object({
4322
+ userStyle: external_exports.enum(["bubble", "accent"]).optional(),
4323
+ assistantStyle: external_exports.enum(["plain", "bubble"]).optional()
4324
+ }).strict().optional()
4325
+ }).strict();
4326
+ var assistantUiSchema = external_exports.object({
4327
+ theme: external_exports.enum(["auto", "light", "dark", "invert"]).optional(),
4328
+ layout: external_exports.object({
4329
+ mode: external_exports.enum(["floating", "inline", "drawer"]).optional(),
4330
+ position: external_exports.enum(["bottom-left", "bottom-center", "bottom-right"]).optional(),
4331
+ panelWidth: external_exports.number().int().min(280).max(1200).optional(),
4332
+ panelMinHeight: external_exports.number().int().min(240).max(1600).optional(),
4333
+ panelMaxHeight: external_exports.number().int().min(320).max(2400).optional(),
4334
+ edgeOffset: external_exports.number().int().min(0).max(96).optional(),
4335
+ zIndex: external_exports.number().int().min(0).max(2147483647).optional(),
4336
+ density: external_exports.enum(["compact", "comfortable"]).optional(),
4337
+ mobileFullscreen: external_exports.boolean().optional()
4338
+ }).strict().optional(),
4339
+ behavior: external_exports.object({
4340
+ startOpen: external_exports.boolean().optional(),
4341
+ closeOnEscape: external_exports.boolean().optional(),
4342
+ closeOnOutsideClick: external_exports.boolean().optional(),
4343
+ showLauncher: external_exports.boolean().optional(),
4344
+ showHeader: external_exports.boolean().optional(),
4345
+ showAvatars: external_exports.boolean().optional(),
4346
+ showTimestamps: external_exports.boolean().optional(),
4347
+ showPoweredBy: external_exports.boolean().optional()
4348
+ }).strict().optional(),
4349
+ labels: labelsSchema.optional(),
4350
+ presentation: presentationSchema.optional(),
4351
+ suggestedPrompts: external_exports.array(external_exports.string().trim().min(1).max(240)).max(8).optional(),
4352
+ privacyUrl: httpsUrlSchema.optional(),
4353
+ termsUrl: httpsUrlSchema.optional(),
4354
+ locale: external_exports.string().trim().min(2).max(35).optional(),
4355
+ direction: external_exports.enum(["ltr", "rtl", "auto"]).optional()
4356
+ }).strict();
4357
+ var assistantConfigurationSchema = external_exports.object({ branding: brandingSchema.optional(), assistant: assistantUiSchema.optional() }).strict();
4358
+ function parseAssistantConfiguration(value) {
4359
+ const parsed = assistantConfigurationSchema.safeParse(value);
4360
+ return parsed.success ? parsed.data : void 0;
4361
+ }
4362
+ function isHttpsUrl(value) {
4363
+ try {
4364
+ return new URL(value).protocol === "https:";
4365
+ } catch {
4366
+ return false;
4367
+ }
4368
+ }
4369
+ function isSecureBrowserAssetUrl(value) {
4370
+ try {
4371
+ const url = new URL(value);
4372
+ if (url.protocol === "https:") return true;
4373
+ if (url.protocol !== "http:") return false;
4374
+ return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
4375
+ } catch {
4376
+ return false;
4377
+ }
4378
+ }
4379
+
4380
+ // src/element-public-configuration-controller.ts
4381
+ var AssistantElementPublicConfigurationController = class {
4382
+ #source;
4383
+ #apply;
4384
+ #loadingChanged;
4385
+ #generation = 0;
4386
+ #bootstrap;
4387
+ #abort;
4388
+ constructor(source, apply2, loadingChanged) {
4389
+ this.#source = source;
4390
+ this.#apply = apply2;
4391
+ this.#loadingChanged = loadingChanged;
4392
+ }
4393
+ connect() {
4394
+ this.#restart();
4395
+ }
4396
+ refresh() {
4397
+ this.#restart();
4398
+ }
4399
+ disconnect() {
4400
+ this.#generation += 1;
4401
+ this.#abort?.abort();
4402
+ this.#abort = void 0;
4403
+ this.#bootstrap = void 0;
4404
+ }
4405
+ #restart() {
4406
+ this.disconnect();
4407
+ this.#prime();
4408
+ }
4409
+ #prime() {
4410
+ const source = this.#source();
4411
+ if (!source.embedId || this.#bootstrap) return;
4412
+ const generation = this.#generation;
4413
+ const abort = new AbortController();
4414
+ this.#abort = abort;
4415
+ const fetcher = source.fetch ?? ((input, init) => globalThis.fetch(input, init));
4416
+ this.#loadingChanged(true);
4417
+ const bootstrap = Promise.resolve().then(
4418
+ () => fetcher(publicConfigurationUrl(source.serviceUrl, source.embedId), {
4419
+ method: "GET",
4420
+ headers: { accept: "application/json" },
4421
+ credentials: "omit",
4422
+ cache: "no-cache",
4423
+ signal: abort.signal
4424
+ })
4425
+ ).then(async (response) => {
4426
+ if (!response.ok) return;
4427
+ const value = await response.json();
4428
+ if (generation !== this.#generation || !isRecord2(value)) {
4429
+ return;
4430
+ }
4431
+ const configuration = parseAssistantConfiguration(value.configuration);
4432
+ if (configuration !== void 0) this.#apply(configuration);
4433
+ }).catch(() => void 0).finally(() => {
4434
+ if (generation === this.#generation) this.#loadingChanged(false);
4435
+ if (this.#bootstrap === bootstrap) {
4436
+ this.#bootstrap = void 0;
4437
+ this.#abort = void 0;
4438
+ }
4439
+ });
4440
+ this.#bootstrap = bootstrap;
4441
+ }
4442
+ };
4443
+ function isRecord2(value) {
4444
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4445
+ }
4446
+
3954
4447
  // src/element-scroll-controller.ts
3955
4448
  var AssistantElementScrollController = class {
3956
4449
  #region;
@@ -4057,44 +4550,69 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
4057
4550
  *, *::before, *::after { box-sizing: border-box; }
4058
4551
  .visually-hidden { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border: 0 !important; }
4059
4552
  .visually-hidden[hidden] { display: none !important; }
4060
- :host { color-scheme: light dark; font-family: var(--ns-assistant-font-family, var(--ns-assistant-default-font-family)); font-size: var(--ns-assistant-base-font-size, var(--ns-assistant-default-base-font-size)); line-height: var(--ns-assistant-line-height, var(--ns-assistant-default-line-height)); z-index: var(--ns-assistant-z-index, var(--ns-assistant-default-z-index)); -webkit-font-smoothing: antialiased; }
4061
- :host([data-mode="floating"]) { position: fixed; bottom: 20px; right: 20px; }
4062
- :host([data-mode="floating"][data-position="bottom-left"]) { right: auto; left: 20px; }
4553
+ :host { --ns-assistant-halo-launcher-action: #f4f4f5; --ns-assistant-halo-launcher-action-hover: #e4e4e7; --ns-assistant-halo-launcher-border: rgba(0,0,0,.04); --ns-assistant-halo-launcher-shadow: 0 1px 8px rgba(0,0,0,.04); --ns-assistant-halo-launcher-shadow-hover: 0 2px 12px rgba(0,0,0,.06); --ns-assistant-halo-send-button: #e4e4e7; --ns-assistant-halo-send-button-text: #09090b; box-sizing: border-box; color-scheme: light dark; font-family: var(--ns-assistant-font-family, var(--ns-assistant-default-font-family)); font-size: var(--ns-assistant-base-font-size, var(--ns-assistant-default-base-font-size)); line-height: var(--ns-assistant-line-height, var(--ns-assistant-default-line-height)); z-index: var(--ns-assistant-z-index, var(--ns-assistant-default-z-index)); -webkit-font-smoothing: antialiased; }
4554
+ :host([data-theme="dark"]) { --ns-assistant-halo-launcher-action: #3f3f46; --ns-assistant-halo-launcher-action-hover: #52525b; --ns-assistant-halo-launcher-border: rgba(255,255,255,.1); --ns-assistant-halo-launcher-shadow: 0 4px 20px rgba(0,0,0,.3); --ns-assistant-halo-launcher-shadow-hover: 0 6px 24px rgba(0,0,0,.4); --ns-assistant-halo-send-button: #3f3f46; --ns-assistant-halo-send-button-text: #fff; }
4555
+ :host([data-mode="floating"]) { position: fixed; }
4556
+ :host([data-mode="floating"][open]) { width: min(var(--ns-assistant-panel-width, var(--ns-assistant-default-panel-width)), 100vw); }
4063
4557
  button { font: inherit; }
4064
4558
  button, textarea, input, select { -webkit-tap-highlight-color: transparent; }
4065
- .launcher { display: grid; place-items: center; width: 48px; height: 48px; border: 1px solid var(--ns-assistant-launcher-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))); border-radius: 999px; color: var(--ns-assistant-launcher-text, var(--ns-assistant-text, var(--ns-assistant-default-text))); background: var(--ns-assistant-launcher, var(--ns-assistant-panel, var(--ns-assistant-default-panel))); box-shadow: 0 8px 28px rgba(0,0,0,.14); cursor: pointer; transition: transform .24s cubic-bezier(.25,1,.5,1), box-shadow .2s ease; }
4066
- .launcher:hover { transform: translateY(-2px) scale(1.03); box-shadow: 0 8px 26px rgba(0,0,0,.16); }
4067
- .launcher img { width: 28px; height: 28px; border-radius: 50%; object-fit: contain; }
4068
- .panel { display: none; flex-direction: column; width: min(var(--ns-assistant-panel-width, var(--ns-assistant-default-panel-width)), calc(100vw - 40px)); min-height: var(--ns-assistant-min-height, var(--ns-assistant-default-min-height)); max-height: min(var(--ns-assistant-max-height, var(--ns-assistant-default-max-height)), calc(100vh - 40px)); overflow: hidden; color: var(--ns-assistant-panel-text, var(--ns-assistant-text, var(--ns-assistant-default-text))); background: var(--ns-assistant-panel, var(--ns-assistant-default-panel)); border: 1px solid var(--ns-assistant-panel-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))); border-radius: var(--ns-assistant-panel-radius, var(--ns-assistant-default-panel-radius)); box-shadow: var(--ns-assistant-shadow, var(--ns-assistant-default-shadow)); transform-origin: bottom right; isolation: isolate; animation: halo-panel-in .28s cubic-bezier(.23,1,.32,1) both; }
4559
+ .launcher { display: flex; align-items: center; justify-content: center; gap: 12px; width: max-content; min-height: 44px; padding: 10px 14px; color: var(--ns-assistant-launcher-text, var(--ns-assistant-text, var(--ns-assistant-default-text))); background: var(--ns-assistant-launcher, color-mix(in srgb, var(--ns-assistant-panel, var(--ns-assistant-default-panel)) 50%, transparent)); border: 1px solid var(--ns-assistant-launcher-border, var(--ns-assistant-halo-launcher-border)); border-radius: 999px; box-shadow: var(--ns-assistant-halo-launcher-shadow), -6px 0 18px color-mix(in srgb, var(--ns-assistant-accent, var(--ns-assistant-default-accent)) 20%, transparent), 6px 0 18px color-mix(in srgb, var(--ns-assistant-accent, var(--ns-assistant-default-accent)) 20%, transparent), 0 0 40px color-mix(in srgb, var(--ns-assistant-accent, var(--ns-assistant-default-accent)) 10%, transparent); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); transition: width .5s cubic-bezier(.25,1,.5,1), height .5s cubic-bezier(.25,1,.5,1), padding .5s cubic-bezier(.25,1,.5,1), gap .35s cubic-bezier(.25,1,.5,1), transform .6s cubic-bezier(.25,1,.5,1), background-color .3s ease, box-shadow .3s ease; }
4560
+ :host([data-presentation-ready]) .launcher { padding: 10px 12px; }
4561
+ .launcher[hidden] { display: none; }
4562
+ .launcher:hover { transform: scale(1.02); box-shadow: var(--ns-assistant-halo-launcher-shadow-hover), -6px 0 18px color-mix(in srgb, var(--ns-assistant-accent, var(--ns-assistant-default-accent)) 20%, transparent), 6px 0 18px color-mix(in srgb, var(--ns-assistant-accent, var(--ns-assistant-default-accent)) 20%, transparent); }
4563
+ .launcher-trigger, .launcher-form { min-width: 0; margin: 0; padding: 0; border: 0; background: transparent; font: inherit; }
4564
+ .launcher-trigger { display: flex; align-items: center; gap: 12px; min-height: 28px; color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); cursor: text; }
4565
+ .launcher-label { padding-left: 6px; white-space: nowrap; font-size: 15px; line-height: 20px; }
4566
+ .launcher-arrow, .launcher-send { position: relative; display: grid; width: 28px; height: 28px; flex: 0 0 28px; place-items: center; border: 0; border-radius: 50%; color: var(--ns-assistant-text, var(--ns-assistant-default-text)); background: var(--ns-assistant-launcher-action, var(--ns-assistant-halo-launcher-action)); cursor: pointer; }
4567
+ .launcher-arrow:hover, .launcher-send:hover { background: var(--ns-assistant-launcher-action-hover, var(--ns-assistant-halo-launcher-action-hover)); }
4568
+ .launcher-arrow::before, .launcher-send::before { content: ''; width: 16px; height: 16px; background: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 19V5M5 12l7-7 7 7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 19V5M5 12l7-7 7 7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; }
4569
+ .launcher-form { display: none; flex: 1; align-items: center; gap: 12px; opacity: 0; pointer-events: none; }
4570
+ .launcher-input { width: 100%; min-width: 0; height: 28px; padding: 0 0 0 6px; color: var(--ns-assistant-text, var(--ns-assistant-default-text)); background: transparent; border: 0; outline: 0; box-shadow: none; appearance: none; font: inherit; font-size: 16px; line-height: 20px; }
4571
+ .launcher-input::placeholder { color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); }
4572
+ :host([launcher-expanded]) .launcher { width: min(400px, calc(100vw - 40px)); justify-content: flex-start; }
4573
+ :host([launcher-expanded]) .launcher-trigger { display: none; }
4574
+ :host([launcher-expanded]) .launcher-form { display: flex; opacity: 1; pointer-events: auto; }
4575
+ :host([data-launcher-style="bubble"]) .launcher { gap: 0; min-height: 0; padding: 6px; cursor: pointer; }
4576
+ :host([data-launcher-style="bubble"]) .launcher-trigger { position: absolute; inset: 0; display: block; width: 100%; height: 100%; cursor: pointer; }
4577
+ :host([data-launcher-style="bubble"]) .launcher-label, :host([data-launcher-style="bubble"]) .launcher-arrow, :host([data-launcher-style="bubble"]) .launcher-form { display: none; }
4578
+ :host([data-launcher-style="bubble"]) .launcher-visual { width: 32px; height: 32px; flex-basis: 32px; }
4579
+ .launcher-loader { display: none; width: 16px; height: 16px; flex: 0 0 16px; border: 2px solid var(--ns-assistant-divider, var(--ns-assistant-default-divider)); border-top-color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); border-radius: 50%; animation: halo-spin .8s linear infinite; }
4580
+ :host([data-session-state="loading"]:not([data-presentation-ready])) .launcher-loader, :host([data-public-configuration-loading]:not([data-presentation-ready])) .launcher-loader { display: block; }
4581
+ :host([data-session-state="loading"]:not([data-presentation-ready])) .launcher > :not(.launcher-loader):not([data-session-status]), :host([data-public-configuration-loading]:not([data-presentation-ready])) .launcher > :not(.launcher-loader):not([data-session-status]) { display: none; }
4582
+ .panel { display: none; flex-direction: column; width: 100%; max-width: 970px; height: min(85dvh, var(--ns-assistant-max-height, var(--ns-assistant-default-max-height)), calc(100dvh - 40px)); min-height: min(var(--ns-assistant-min-height, var(--ns-assistant-default-min-height)), calc(100dvh - 40px)); max-height: min(var(--ns-assistant-max-height, var(--ns-assistant-default-max-height)), calc(100dvh - 40px)); overflow: hidden; color: var(--ns-assistant-panel-text, var(--ns-assistant-text, var(--ns-assistant-default-text))); background: var(--ns-assistant-panel, var(--ns-assistant-default-panel)); border: 1px solid var(--ns-assistant-panel-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))); border-radius: var(--ns-assistant-panel-radius, var(--ns-assistant-default-panel-radius)); box-shadow: var(--ns-assistant-shadow, var(--ns-assistant-default-shadow)); transform-origin: bottom center; isolation: isolate; animation: halo-panel-in .3s ease both; }
4583
+ :host([data-mode="floating"][open]) .panel { width: calc(100% - 40px); margin-inline: 20px; }
4069
4584
  :host([open][data-presentation-ready]) .panel { display: flex; }
4070
4585
  .panel { position: relative; }
4071
4586
  :host([open][data-presentation-ready]) .launcher { display: none; }
4072
4587
  :host([open]:not([data-presentation-ready])) .launcher { cursor: progress; opacity: .72; transform: none; }
4073
- header { display: flex; flex: 0 0 auto; gap: 10px; align-items: center; min-height: 64px; padding: 12px 18px; color: var(--ns-assistant-header-text, inherit); background: var(--ns-assistant-header, transparent); border-bottom: 1px solid color-mix(in srgb, var(--ns-assistant-header-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))) 78%, transparent); }
4588
+ header { display: flex; flex: 0 0 auto; gap: 10px; align-items: center; min-height: 64px; padding: 16px 20px; color: var(--ns-assistant-header-text, inherit); background: var(--ns-assistant-header, transparent); border-bottom: 1px solid var(--ns-assistant-header-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))); }
4074
4589
  header strong { flex: 1; font-size: 16px; font-weight: 600; letter-spacing: 0; }
4075
4590
  .brand-logo { display: block; max-width: 120px; max-height: 28px; object-fit: contain; }
4076
4591
  .close, .send { display: grid; place-items: center; border: 0; border-radius: var(--ns-assistant-button-radius, var(--ns-assistant-default-button-radius)); cursor: pointer; }
4077
- .close { position: relative; width: 44px; height: 44px; padding: 0; color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); background: transparent; transition: background .15s ease, color .15s ease, transform .15s ease; }
4078
- .close::before { content: ''; width: 18px; height: 18px; background: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m6 6 12 12M18 6 6 18' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m6 6 12 12M18 6 6 18' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat; }
4592
+ .close { position: relative; width: 44px; height: 44px; margin: -6px; padding: 0; color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); background: transparent; transition: background .15s ease, color .15s ease, transform .15s ease; }
4593
+ .close::before { content: ''; width: 20px; height: 20px; background: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m6 6 12 12M18 6 6 18' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m6 6 12 12M18 6 6 18' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat; }
4079
4594
  .close:hover { color: var(--ns-assistant-text, var(--ns-assistant-default-text)); background: var(--ns-assistant-elevated, var(--ns-assistant-default-elevated)); }
4080
4595
  .close:active { transform: scale(.96); }
4081
- .welcome { flex: 0 0 auto; padding: 30px 22px 18px; }
4596
+ .welcome { flex: 0 0 auto; padding: 24px 20px 8px; }
4082
4597
  .welcome h2 { margin: 0; font-size: 1.35em; line-height: 1.25; letter-spacing: 0; }
4083
4598
  .welcome p { max-width: 42ch; margin: 7px 0 0; color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); }
4084
- :host([has-messages]) .welcome, :host([has-messages]) .suggested-prompts { display: none; }
4085
- .suggested-prompts { display: flex; flex: 0 0 auto; flex-wrap: wrap; gap: 8px; padding: 0 20px 16px; }
4599
+ .welcome:has(.empty-state[hidden]) { display: none; }
4600
+ :host([has-messages]) .welcome { display: none; }
4601
+ .input-section { flex: 0 0 auto; padding: 8px 16px 16px; }
4602
+ .suggested-prompts { display: flex; flex: 0 0 auto; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
4086
4603
  .suggested-prompts:empty { display: none; }
4087
- .suggested-prompts button { min-height: 44px; padding: 8px 14px; color: var(--ns-assistant-suggestion-text, var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text))); background: var(--ns-assistant-suggestion, color-mix(in srgb, var(--ns-assistant-input, var(--ns-assistant-default-input)) 58%, transparent)); border: 1px solid var(--ns-assistant-suggestion-border, color-mix(in srgb, var(--ns-assistant-divider, var(--ns-assistant-default-divider)) 82%, transparent)); border-radius: 999px; cursor: pointer; font-size: .86em; transition: background .2s ease, color .2s ease, border-color .2s ease, transform .2s ease; }
4088
- .suggested-prompts button:hover { color: var(--ns-assistant-text, var(--ns-assistant-default-text)); background: var(--ns-assistant-elevated, var(--ns-assistant-default-elevated)); transform: translateY(-1px); }
4604
+ .suggested-prompts button { position: relative; display: inline-flex; align-items: center; padding: 6px 12px; color: var(--ns-assistant-suggestion-text, var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text))); background: var(--ns-assistant-suggestion, transparent); border: 1px solid var(--ns-assistant-suggestion-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))); border-radius: 999px; cursor: pointer; font-size: 12px; white-space: nowrap; transition: background .2s ease, color .2s ease; }
4605
+ .suggested-prompts button::before { content: ''; position: absolute; inset: -6px -2px; }
4606
+ .suggested-prompts button:hover { color: var(--ns-assistant-text, var(--ns-assistant-default-text)); background: var(--ns-assistant-elevated, var(--ns-assistant-default-elevated)); }
4089
4607
  .messages-region { position: relative; display: flex; flex: 1 1 auto; min-height: 0; overflow: hidden; }
4090
4608
  .messages-region::before, .messages-region::after { content: ''; position: absolute; right: 0; left: 0; z-index: 1; height: 18px; pointer-events: none; opacity: 0; transition: opacity .18s ease; }
4091
4609
  .messages-region::before { top: 0; background: linear-gradient(to bottom, color-mix(in srgb, var(--ns-assistant-panel, var(--ns-assistant-default-panel)) 92%, transparent), transparent); }
4092
4610
  .messages-region::after { bottom: 0; background: linear-gradient(to top, color-mix(in srgb, var(--ns-assistant-panel, var(--ns-assistant-default-panel)) 92%, transparent), transparent); }
4093
4611
  .messages-region[data-can-scroll-up]::before, .messages-region[data-can-scroll-down]::after { opacity: 1; }
4094
- .messages { flex: 1 1 auto; width: 100%; min-height: 0; padding: 18px 20px 24px; overflow-y: auto; background: var(--ns-assistant-canvas, transparent); overscroll-behavior: contain; scroll-behavior: smooth; }
4612
+ .messages { flex: 1 1 auto; width: 100%; min-height: 0; padding: 16px 20px; overflow-y: auto; background: var(--ns-assistant-canvas, transparent); overscroll-behavior: contain; scroll-behavior: smooth; }
4095
4613
  .messages::-webkit-scrollbar { width: 6px; }
4096
4614
  .messages::-webkit-scrollbar-thumb { background: var(--ns-assistant-divider, var(--ns-assistant-default-divider)); border-radius: 999px; }
4097
- .message { width: fit-content; max-width: 88%; margin: 0 0 14px; padding: 0; border-radius: var(--ns-assistant-card-radius, var(--ns-assistant-default-card-radius)); background: transparent; white-space: pre-wrap; animation: halo-message-in .2s ease both; }
4615
+ .message { width: fit-content; max-width: 88%; margin: 0 0 12px; padding: 0; border-radius: var(--ns-assistant-card-radius, var(--ns-assistant-default-card-radius)); background: transparent; white-space: pre-wrap; animation: halo-message-in .2s ease both; }
4098
4616
  .message > span, .message > .markdown { overflow-wrap: anywhere; }
4099
4617
  .message .avatar { float: left; width: 24px; height: 24px; margin-right: 9px; border-radius: 50%; object-fit: cover; }
4100
4618
  .message time { display: block; margin-top: 4px; color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); font-size: .75em; line-height: 1.2; }
@@ -4107,7 +4625,7 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
4107
4625
  .new-messages { position: absolute; left: 50%; bottom: 14px; z-index: 2; width: 44px; height: 44px; padding: 0; color: var(--ns-assistant-secondary-button-text, var(--ns-assistant-text, var(--ns-assistant-default-text))); background: var(--ns-assistant-secondary-button, color-mix(in srgb, var(--ns-assistant-input, var(--ns-assistant-default-input)) 92%, transparent)); border: 1px solid var(--ns-assistant-secondary-button-border, var(--ns-assistant-divider, var(--ns-assistant-default-divider))); border-radius: var(--ns-assistant-button-radius, var(--ns-assistant-default-button-radius)); box-shadow: 0 8px 24px rgba(0,0,0,.12); transform: translateX(-50%); cursor: pointer; }
4108
4626
  .new-messages::before { content: ''; display: block; width: 18px; height: 18px; margin: auto; background: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m6 9 6 6 6-6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m6 9 6 6 6-6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; }
4109
4627
  .message.assistant { max-width: 100%; color: var(--ns-assistant-assistant-message-text, inherit); background: var(--ns-assistant-assistant-message, transparent); border: 1px solid var(--ns-assistant-assistant-message-border, transparent); }
4110
- .message.user { display: grid; max-width: 85%; justify-items: start; gap: 3px; margin-left: auto; padding: 10px 16px 8px; color: var(--ns-assistant-user-message-text, var(--ns-assistant-text, var(--ns-assistant-default-text))); background: var(--ns-assistant-user-message, var(--ns-assistant-elevated, var(--ns-assistant-default-elevated))); border: 1px solid var(--ns-assistant-user-message-border, transparent); border-radius: var(--ns-assistant-card-radius, var(--ns-assistant-default-card-radius)); font-size: .94em; }
4628
+ .message.user { display: grid; max-width: 85%; justify-items: start; gap: 3px; margin-left: auto; padding: 10px 16px; color: var(--ns-assistant-user-message-text, var(--ns-assistant-text, var(--ns-assistant-default-text))); background: var(--ns-assistant-user-message, var(--ns-assistant-elevated, var(--ns-assistant-default-elevated))); border: 1px solid var(--ns-assistant-user-message-border, transparent); border-radius: var(--ns-assistant-card-radius, var(--ns-assistant-default-card-radius)); font-size: 14px; }
4111
4629
  .message.user time { margin-top: 0; }
4112
4630
  .markdown { white-space: normal; }
4113
4631
  .markdown p { margin: 0 0 8px; }
@@ -4157,31 +4675,39 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
4157
4675
  .input-request input:focus-visible, .input-request select:focus-visible { border-color: var(--ns-assistant-focus, var(--ns-assistant-default-focus)); box-shadow: 0 0 0 2px var(--ns-assistant-focus, var(--ns-assistant-default-focus)); }
4158
4676
  .input-request .proposal-actions { margin-top: 2px; }
4159
4677
  ${ASSISTANT_APP_VIEW_CSS}
4160
- form { display: flex; flex: 0 0 auto; gap: 8px; align-items: center; min-height: 56px; margin: 8px 16px 16px; padding: 6px 6px 6px 16px; color: var(--ns-assistant-composer-text, inherit); border: 1px solid var(--ns-assistant-composer-border, color-mix(in srgb, var(--ns-assistant-divider, var(--ns-assistant-default-divider)) 86%, transparent)); border-radius: var(--ns-assistant-input-radius, var(--ns-assistant-default-input-radius)); background: var(--ns-assistant-composer, color-mix(in srgb, var(--ns-assistant-input, var(--ns-assistant-default-input)) 92%, transparent)); box-shadow: 0 1px 2px rgba(0,0,0,.04), 0 8px 24px rgba(0,0,0,.035); transition: border-color .15s ease, box-shadow .15s ease; }
4161
- form:focus-within { box-shadow: inset 0 0 0 1px var(--ns-assistant-focus, var(--ns-assistant-default-focus)), 0 0 0 2px var(--ns-assistant-focus, var(--ns-assistant-default-focus)); }
4678
+ .composer { display: flex; flex: 0 0 auto; gap: 8px; align-items: center; min-height: 44px; margin: 0; padding: 8px 8px 8px 16px; color: var(--ns-assistant-composer-text, inherit); border: 1px solid var(--ns-assistant-composer-border, transparent); border-radius: var(--ns-assistant-input-radius, var(--ns-assistant-default-input-radius)); background: var(--ns-assistant-composer, var(--ns-assistant-input, var(--ns-assistant-default-input))); transition: border-color .15s ease, box-shadow .15s ease; }
4679
+ .composer:focus-within { border-color: var(--ns-assistant-focus, var(--ns-assistant-default-focus)); box-shadow: 0 0 0 2px color-mix(in srgb, var(--ns-assistant-focus, var(--ns-assistant-default-focus)) 32%, transparent); }
4162
4680
  textarea { flex: 1; min-height: 24px; max-height: 120px; resize: none; padding: 0; color: var(--ns-assistant-text, var(--ns-assistant-default-text)); background: transparent; border: 0; border-radius: 0; outline: 0; box-shadow: none; appearance: none; -webkit-appearance: none; font: inherit; font-size: 16px; line-height: 24px; }
4163
4681
  textarea::placeholder { color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); }
4164
4682
  textarea:focus, textarea:focus-visible { outline: none; box-shadow: none; }
4165
4683
  button:focus-visible { outline: 3px solid var(--ns-assistant-focus, var(--ns-assistant-default-focus)); outline-offset: 2px; }
4166
- .send { position: relative; display: block; width: 44px; height: 44px; flex: 0 0 44px; padding: 0; overflow: hidden; color: transparent; border-radius: 999px; background: var(--ns-assistant-primary-button, var(--ns-assistant-accent, var(--ns-assistant-default-accent))); font-size: 0; line-height: 0; transition: transform .15s ease, background .15s ease; }
4167
- .send::before { content: ''; position: absolute; top: 50%; left: 50%; width: 17px; height: 17px; background: var(--ns-assistant-primary-button-text, var(--ns-assistant-accent-text, var(--ns-assistant-default-accent-text))); transform: translate(-50%, -50%); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 19V5M5 12l7-7 7 7' fill='none' stroke='black' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 19V5M5 12l7-7 7 7' fill='none' stroke='black' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; }
4168
- .send:hover { transform: scale(1.05); background: var(--ns-assistant-hover, color-mix(in srgb, var(--ns-assistant-primary-button, var(--ns-assistant-accent, var(--ns-assistant-default-accent))) 88%, var(--ns-assistant-primary-button-text, var(--ns-assistant-accent-text, var(--ns-assistant-default-accent-text))) 12%)); }
4684
+ .send { position: relative; display: block; width: 44px; height: 44px; flex: 0 0 44px; margin: -8px; padding: 0; overflow: visible; color: var(--ns-assistant-primary-button-text, var(--ns-assistant-halo-send-button-text)); border-radius: 999px; background: transparent; font-size: 0; line-height: 0; }
4685
+ .send::after { content: ''; position: absolute; top: 50%; left: 50%; z-index: 0; width: 28px; height: 28px; border-radius: 50%; background: var(--ns-assistant-primary-button, var(--ns-assistant-halo-send-button)); transform: translate(-50%, -50%); }
4686
+ .send::before { content: ''; position: absolute; top: 50%; left: 50%; z-index: 1; width: 16px; height: 16px; background: currentColor; transform: translate(-50%, -50%); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 19V5M5 12l7-7 7 7' fill='none' stroke='black' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 19V5M5 12l7-7 7 7' fill='none' stroke='black' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; }
4687
+ .send:hover::after { background: var(--ns-assistant-hover, var(--ns-assistant-default-hover)); }
4169
4688
  :host([busy]) .send::before { width: 13px; height: 13px; border-radius: 3px; -webkit-mask: none; mask: none; }
4170
4689
  .send:disabled, textarea:disabled { opacity: .55; cursor: not-allowed; }
4171
- .legal { display: flex; gap: 12px; justify-content: center; padding: 0 12px 9px; font-size: .75em; }
4690
+ .legal { display: flex; gap: 12px; justify-content: center; padding: 0 12px 8px; font-size: .75em; }
4172
4691
  .legal:empty { display: none; }
4173
4692
  .legal a { color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); text-underline-offset: 2px; }
4693
+ .powered-by-row { display: flex; flex: 0 0 auto; justify-content: center; padding-bottom: 12px; }
4694
+ .powered-by-row[hidden] { display: none; }
4695
+ .powered-by { display: inline-flex; align-items: center; gap: 6px; margin: 0 auto 12px; padding: 0 16px; color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); font-size: 11px; line-height: 1; text-decoration: none; opacity: .7; transition: opacity .12s ease, color .12s ease; }
4696
+ .powered-by:hover { color: var(--ns-assistant-text, var(--ns-assistant-default-text)); opacity: 1; }
4697
+ .powered-by-icon { display: inline-flex; width: 14px; height: 14px; }
4698
+ .powered-by-name { font-weight: 500; }
4174
4699
  ::slotted([slot="conversation-footer"]) { display: block; padding: 0 16px 12px; color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); font-size: 11px; text-align: center; }
4175
- @keyframes halo-panel-in { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }
4700
+ @keyframes halo-panel-in { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: none; } }
4176
4701
  @keyframes halo-message-in { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
4177
4702
  @keyframes halo-shimmer { from { background-position: 100% center; } to { background-position: 0 center; } }
4178
4703
  @keyframes halo-caret-blink { 50% { opacity: 0; } }
4704
+ @keyframes halo-spin { to { transform: rotate(360deg); } }
4179
4705
  :host([data-density="compact"]) .welcome { padding: 20px 18px 12px; }
4180
- @media (max-width: 640px) { :host([mobile-fullscreen][open]) { inset: 0; } :host([mobile-fullscreen][open]) .panel { width: 100vw; min-height: 100vh; max-height: 100vh; border: 0; border-radius: 0; } :host([mobile-fullscreen]) header { padding-top: max(12px, env(safe-area-inset-top)); padding-right: max(18px, env(safe-area-inset-right)); padding-left: max(18px, env(safe-area-inset-left)); } :host([mobile-fullscreen]) form { margin-right: max(16px, env(safe-area-inset-right)); margin-bottom: max(16px, env(safe-area-inset-bottom)); margin-left: max(16px, env(safe-area-inset-left)); } :host([mobile-fullscreen]) .legal, :host([mobile-fullscreen]) ::slotted([slot="conversation-footer"]) { padding-bottom: max(10px, env(safe-area-inset-bottom)); } }
4706
+ @media (max-width: 640px) { :host([mobile-fullscreen][open]) { inset: 0; width: 100vw; transform: none; } :host([mobile-fullscreen][open]) .panel { width: 100vw; max-width: none; height: 100dvh; min-height: 100dvh; max-height: 100dvh; margin: 0; border: 0; border-radius: 0; } :host([mobile-fullscreen]) header { padding-top: calc(16px + env(safe-area-inset-top)); padding-right: calc(20px + env(safe-area-inset-right)); padding-left: calc(20px + env(safe-area-inset-left)); } :host([mobile-fullscreen]) .messages { padding-right: calc(20px + env(safe-area-inset-right)); padding-left: calc(20px + env(safe-area-inset-left)); } :host([mobile-fullscreen]) .input-section { padding-right: calc(16px + env(safe-area-inset-right)); padding-bottom: calc(16px + env(safe-area-inset-bottom)); padding-left: calc(16px + env(safe-area-inset-left)); } :host([mobile-fullscreen]) .legal, :host([mobile-fullscreen]) ::slotted([slot="conversation-footer"]) { padding-bottom: max(10px, env(safe-area-inset-bottom)); } }
4181
4707
  @media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: .01ms !important; animation-duration: .01ms !important; } .messages { scroll-behavior: auto; } .message.assistant.streaming .markdown::after { animation: none; } }
4182
4708
  @media (forced-colors: active) { .message.thinking > span { color: CanvasText; background: none; } }
4183
4709
  @media (hover: none) { .message-copy { opacity: 1; } }
4184
- @media (max-width: 560px) { :host([data-mode="floating"]) { inset: auto 12px 12px; } :host([open]) .panel { width: calc(100vw - 24px); max-height: calc(100vh - 24px); } }
4710
+ @media (max-width: 560px) { :host([data-mode="floating"]:not([open])) { bottom: 20px; } :host([open]:not([mobile-fullscreen])) .panel { width: calc(100vw - 40px); max-height: calc(100dvh - 40px); } }
4185
4711
  </style>`;
4186
4712
 
4187
4713
  // src/host-appearance.ts
@@ -4290,6 +4816,7 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
4290
4816
  host.dataset.panelSurface = presentation.panel.surface;
4291
4817
  host.dataset.panelElevation = presentation.panel.elevation;
4292
4818
  host.dataset.panelBorder = presentation.panel.border;
4819
+ host.dataset.launcherStyle = presentation.launcher.style;
4293
4820
  host.dataset.launcherEffect = presentation.launcher.effect;
4294
4821
  host.dataset.messageUserStyle = presentation.messages.userStyle;
4295
4822
  host.dataset.messageAssistantStyle = presentation.messages.assistantStyle;
@@ -4302,11 +4829,11 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
4302
4829
  }
4303
4830
  host.style.setProperty(
4304
4831
  "--ns-assistant-presentation-launcher-size",
4305
- presentation.launcher.size === "lg" ? "56px" : "48px"
4832
+ presentation.launcher.size === "lg" ? "56px" : "44px"
4306
4833
  );
4307
4834
  host.style.setProperty(
4308
4835
  "--ns-assistant-presentation-panel-background",
4309
- presentation.panel.surface === "solid" ? "var(--ns-assistant-panel, var(--ns-assistant-default-panel))" : "color-mix(in srgb, var(--ns-assistant-panel, var(--ns-assistant-default-panel)) 88%, transparent)"
4836
+ presentation.panel.surface === "solid" ? "var(--ns-assistant-panel, var(--ns-assistant-default-panel))" : "color-mix(in srgb, var(--ns-assistant-panel, var(--ns-assistant-default-panel)) 50%, transparent)"
4310
4837
  );
4311
4838
  host.style.setProperty(
4312
4839
  "--ns-assistant-presentation-shadow",
@@ -4314,7 +4841,7 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
4314
4841
  );
4315
4842
  host.style.setProperty(
4316
4843
  "--ns-assistant-presentation-border",
4317
- panelBorder(presentation.panel.border)
4844
+ panelBorder(presentation.panel.border, mode)
4318
4845
  );
4319
4846
  replaceFallback(root, "launcher-icon", [launcherVisual(appearance, mode, sessionState)]);
4320
4847
  const accessibleStatus = root.querySelector("[data-session-status]");
@@ -4327,7 +4854,7 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
4327
4854
  replaceFallback(root, "empty-state", [emptyState(appearance)]);
4328
4855
  replaceFallback(root, "composer-leading", composerLeading(appearance, mode));
4329
4856
  replaceFallback(root, "conversation-footer", []);
4330
- const form = root.querySelector("form");
4857
+ const form = root.querySelector(".composer");
4331
4858
  if (form) form.dataset.shape = presentation.composer.shape;
4332
4859
  const send = root.querySelector(".send");
4333
4860
  if (send) send.dataset.icon = presentation.composer.sendIcon;
@@ -4347,18 +4874,21 @@ function panelShadow(elevation) {
4347
4874
  }
4348
4875
  return "var(--ns-assistant-default-shadow)";
4349
4876
  }
4350
- function panelBorder(border) {
4877
+ function panelBorder(border, mode) {
4351
4878
  if (border === "strong") {
4352
4879
  return "1px solid var(--ns-assistant-divider, var(--ns-assistant-default-divider))";
4353
4880
  }
4354
- return "1px solid color-mix(in srgb, var(--ns-assistant-divider, var(--ns-assistant-default-divider)) 74%, transparent)";
4881
+ const opacity = mode === "light" ? 60 : 100;
4882
+ return `1px solid color-mix(in srgb, var(--ns-assistant-divider, var(--ns-assistant-default-divider)) ${opacity}%, transparent)`;
4355
4883
  }
4356
4884
  function launcherVisual(appearance, mode, sessionState) {
4357
4885
  const launcher = appearance.presentation.launcher;
4358
4886
  const visual = document.createElement("span");
4359
4887
  visual.className = "launcher-visual";
4360
4888
  visual.setAttribute("aria-hidden", "true");
4361
- visual.append(icon(launcher.icon, appearance, mode, "launcher-glyph"));
4889
+ const visualKind = launcher.icon === "brand-mark" && !(appearance.brand.mark[mode] ?? appearance.brand.logo[mode]) ? launcher.style === "bubble" ? "chat" : "none" : launcher.icon;
4890
+ visual.hidden = visualKind === "none";
4891
+ visual.append(icon(visualKind, appearance, mode, "launcher-glyph"));
4362
4892
  if (launcher.status === "session") {
4363
4893
  const status = document.createElement("span");
4364
4894
  status.className = "launcher-status";
@@ -4408,9 +4938,12 @@ function headerBadge(appearance) {
4408
4938
  function emptyState(appearance) {
4409
4939
  const section = document.createElement("section");
4410
4940
  section.className = "empty-state";
4411
- const heading = document.createElement("h2");
4412
- heading.textContent = appearance.labels.welcomeHeading;
4413
- section.append(heading);
4941
+ section.hidden = !appearance.labels.welcomeHeading && !appearance.labels.welcomeMessage;
4942
+ if (appearance.labels.welcomeHeading) {
4943
+ const heading = document.createElement("h2");
4944
+ heading.textContent = appearance.labels.welcomeHeading;
4945
+ section.append(heading);
4946
+ }
4414
4947
  const description = appearance.labels.welcomeMessage;
4415
4948
  if (description) {
4416
4949
  const paragraph = document.createElement("p");
@@ -4437,7 +4970,7 @@ function toneIndicator(tone) {
4437
4970
  return indicator;
4438
4971
  }
4439
4972
  function icon(kind, appearance, mode, className) {
4440
- const mark = appearance.brand.mark[mode];
4973
+ const mark = appearance.brand.mark[mode] ?? appearance.brand.logo[mode];
4441
4974
  if (kind === "brand-mark" && mark) {
4442
4975
  const image = document.createElement("img");
4443
4976
  image.className = className;
@@ -4489,11 +5022,17 @@ function applyAssistantElementTheme(input) {
4489
5022
  var presentationStyles = `<style>
4490
5023
  :host([data-mode="floating"]) {
4491
5024
  bottom: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
4492
- right: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
5025
+ left: 50%;
5026
+ transform: translateX(-50%);
4493
5027
  }
4494
5028
  :host([data-mode="floating"][data-position="bottom-left"]) {
4495
- right: auto;
4496
5029
  left: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
5030
+ transform: none;
5031
+ }
5032
+ :host([data-mode="floating"][data-position="bottom-right"]) {
5033
+ right: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
5034
+ left: auto;
5035
+ transform: none;
4497
5036
  }
4498
5037
  .panel {
4499
5038
  background: var(--ns-assistant-panel-background, var(--ns-assistant-presentation-panel-background));
@@ -4510,22 +5049,26 @@ var presentationStyles = `<style>
4510
5049
 
4511
5050
  .launcher {
4512
5051
  position: relative;
4513
- width: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
4514
- height: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
4515
5052
  border-radius: var(--ns-assistant-launcher-radius, var(--ns-assistant-default-launcher-radius));
4516
5053
  isolation: isolate;
4517
5054
  }
5055
+ :host([data-launcher-style="bubble"]) .launcher {
5056
+ width: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
5057
+ height: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
5058
+ }
4518
5059
  :host([data-launcher-effect="pulse"]) .launcher {
4519
5060
  animation: assistant-launcher-pulse 2s ease-in-out infinite;
4520
5061
  }
4521
5062
  .launcher-visual {
4522
5063
  position: relative;
4523
5064
  display: grid;
4524
- width: 100%;
4525
- height: 100%;
5065
+ width: 28px;
5066
+ height: 28px;
5067
+ flex: 0 0 28px;
4526
5068
  place-items: center;
4527
5069
  border-radius: inherit;
4528
5070
  }
5071
+ .launcher-visual[hidden] { display: none; }
4529
5072
  .launcher-glyph, .composer-leading-glyph {
4530
5073
  display: block;
4531
5074
  width: 18px;
@@ -4553,10 +5096,28 @@ var presentationStyles = `<style>
4553
5096
  -webkit-mask: none;
4554
5097
  mask: none;
4555
5098
  }
5099
+ .launcher-visual .launcher-glyph:is(img) {
5100
+ width: 28px;
5101
+ height: 28px;
5102
+ border-radius: 50%;
5103
+ filter: grayscale(100%);
5104
+ opacity: .6;
5105
+ transition: width .5s cubic-bezier(.25,1,.5,1), height .5s cubic-bezier(.25,1,.5,1), filter .5s cubic-bezier(.25,1,.5,1), opacity .5s cubic-bezier(.25,1,.5,1);
5106
+ }
5107
+ .launcher:hover .launcher-glyph:is(img), :host([launcher-expanded]) .launcher-glyph:is(img) {
5108
+ filter: none;
5109
+ opacity: 1;
5110
+ }
5111
+ :host([data-launcher-style="bubble"]) .launcher-glyph:is(img) {
5112
+ width: 32px;
5113
+ height: 32px;
5114
+ filter: none;
5115
+ opacity: 1;
5116
+ }
4556
5117
  .launcher-status {
4557
5118
  position: absolute;
4558
- right: 1px;
4559
- bottom: 1px;
5119
+ right: -1px;
5120
+ bottom: -1px;
4560
5121
  width: 10px;
4561
5122
  height: 10px;
4562
5123
  border: 2px solid var(--ns-assistant-panel, var(--ns-assistant-default-panel));
@@ -4685,7 +5246,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
4685
5246
  #conversationGeneration = 0;
4686
5247
  #messages;
4687
5248
  #thinking;
4688
- #media;
4689
5249
  #context;
4690
5250
  #modelContext;
4691
5251
  #pageContext;
@@ -4697,11 +5257,17 @@ var NoodleAssistantElement = class extends HTMLElementBase {
4697
5257
  () => this.#primeSession(),
4698
5258
  () => this.focusComposer()
4699
5259
  );
5260
+ #publicConfiguration = new AssistantElementPublicConfigurationController(
5261
+ () => ({ embedId: this.embedId, serviceUrl: this.serviceUrl, fetch: this.#fetch }),
5262
+ (configuration) => this.#applyConfiguration(configuration),
5263
+ (loading) => this.#setPublicConfigurationLoading(loading)
5264
+ );
4700
5265
  #pendingUserEcho;
4701
5266
  #sessionState = "idle";
4702
5267
  #startOpenApplied = false;
4703
5268
  #readyDispatched = false;
4704
5269
  #turnInFlight = false;
5270
+ #fetch;
4705
5271
  #hostAppearance;
4706
5272
  #appearanceWarningKeys = /* @__PURE__ */ new Set();
4707
5273
  #appearanceStyleSnapshot = /* @__PURE__ */ new Map();
@@ -4711,6 +5277,17 @@ var NoodleAssistantElement = class extends HTMLElementBase {
4711
5277
  embedId = "";
4712
5278
  /** Only for a dev or self-hosted service; the published snippet needs no origin. */
4713
5279
  serviceUrl = "";
5280
+ get fetch() {
5281
+ return this.#fetch;
5282
+ }
5283
+ set fetch(value) {
5284
+ if (this.#fetch === value) return;
5285
+ this.#fetch = value;
5286
+ if (this.#client) this.resetSession();
5287
+ if (!this.isConnected) return;
5288
+ if (this.embedId) this.#publicConfiguration.refresh();
5289
+ else this.#primeSession();
5290
+ }
4714
5291
  get appearance() {
4715
5292
  return this.#hostAppearance;
4716
5293
  }
@@ -4729,11 +5306,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
4729
5306
  }
4730
5307
  return this.#appendMessage(role, text2);
4731
5308
  },
4732
- applyConfiguration: (configuration) => {
4733
- this.#appearance = resolveAppearance(configuration);
4734
- this.#syncAppearance();
4735
- this.#applyStartOpenPolicy();
4736
- },
5309
+ applyConfiguration: (configuration) => this.#applyConfiguration(configuration),
4737
5310
  labels: () => this.#appearance.labels,
4738
5311
  respond: (id, response) => this.respond(id, response),
4739
5312
  dispatchError: (detail) => this.#dispatchError(detail),
@@ -4749,7 +5322,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
4749
5322
  client,
4750
5323
  sendMessage: (message) => this.sendMessage(message),
4751
5324
  updateModelContext: (update) => this.updateModelContext(update),
4752
- theme: this.#resolvedMode()
5325
+ theme: this.#environment.resolvedTheme()
4753
5326
  },
4754
5327
  {
4755
5328
  ...sandboxUrl === void 0 ? {} : { sandboxUrl },
@@ -4762,18 +5335,28 @@ var NoodleAssistantElement = class extends HTMLElementBase {
4762
5335
  this.#revealLatest();
4763
5336
  }
4764
5337
  });
5338
+ #launcher = new AssistantElementLauncherController({
5339
+ host: this,
5340
+ appearance: () => this.#appearance,
5341
+ openPanel: () => this.open(),
5342
+ sendMessage: (message) => this.#sendGuarded(message)
5343
+ });
5344
+ #environment = new AssistantElementHostEnvironmentController({
5345
+ host: this,
5346
+ appearance: () => this.#appearance,
5347
+ applyTheme: () => this.#applyTheme(),
5348
+ close: () => this.#closeAndRestoreFocus()
5349
+ });
4765
5350
  connectedCallback() {
4766
5351
  if (!this.shadowRoot) this.attachShadow({ mode: "open" });
4767
5352
  if (!this.sessionEndpoint) this.sessionEndpoint = this.getAttribute("session-endpoint") ?? "";
4768
5353
  if (!this.embedId) this.embedId = this.getAttribute("embed-id") ?? "";
4769
5354
  if (!this.serviceUrl) this.serviceUrl = this.getAttribute("service-url") ?? "";
4770
5355
  if (!this.sessionEndpoint && !this.embedId) this.#presentationGate.reveal();
4771
- this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
4772
- this.#media?.addEventListener("change", this.#handleSystemTheme);
4773
- document.addEventListener("keydown", this.#handleDocumentKeydown);
4774
- document.addEventListener("pointerdown", this.#handleDocumentPointerDown);
5356
+ this.#environment.connect();
4775
5357
  if (this.#appearance.behavior.startOpen) this.#applyStartOpenPolicy();
4776
5358
  this.#render();
5359
+ if (this.embedId) this.#publicConfiguration.connect();
4777
5360
  if (!this.#readyDispatched) {
4778
5361
  this.#readyDispatched = true;
4779
5362
  this.dispatchEvent(new CustomEvent("assistant-ready"));
@@ -4794,22 +5377,25 @@ var NoodleAssistantElement = class extends HTMLElementBase {
4794
5377
  this.#clientEndpoint = "";
4795
5378
  this.#unsubscribeClient = void 0;
4796
5379
  this.#sessionBootstrap = void 0;
5380
+ this.#publicConfiguration.disconnect();
4797
5381
  this.#scroll.disconnect();
5382
+ this.#launcher.disconnect();
4798
5383
  this.#setBusy(false);
4799
- this.#media?.removeEventListener("change", this.#handleSystemTheme);
4800
- document.removeEventListener("keydown", this.#handleDocumentKeydown);
4801
- document.removeEventListener("pointerdown", this.#handleDocumentPointerDown);
5384
+ this.#environment.disconnect();
4802
5385
  }
4803
5386
  attributeChangedCallback(name) {
4804
5387
  if (name === "theme") this.#applyTheme();
4805
5388
  if (name === "open") this.#presentationGate.syncOpenState();
4806
5389
  }
4807
5390
  open() {
5391
+ this.#launcher.collapse(true);
4808
5392
  this.setAttribute("open", "");
5393
+ if (this.hasAttribute("data-presentation-ready")) queueMicrotask(() => this.focusComposer());
4809
5394
  this.dispatchEvent(new CustomEvent("assistant-open"));
4810
5395
  }
4811
5396
  close() {
4812
5397
  this.removeAttribute("open");
5398
+ this.#launcher.collapse(true);
4813
5399
  this.dispatchEvent(new CustomEvent("assistant-close"));
4814
5400
  }
4815
5401
  toggle() {
@@ -4946,17 +5532,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
4946
5532
  if (generation === this.#conversationGeneration) this.#setBusy(false);
4947
5533
  }
4948
5534
  }
4949
- #handleSystemTheme = () => this.#applyTheme();
4950
- #handleDocumentKeydown = (event) => {
4951
- if (event.key === "Escape" && this.hasAttribute("open") && this.#appearance.behavior.closeOnEscape) {
4952
- this.#closeAndRestoreFocus();
4953
- }
4954
- };
4955
- #handleDocumentPointerDown = (event) => {
4956
- if (this.hasAttribute("open") && this.#appearance.behavior.closeOnOutsideClick && !event.composedPath().includes(this)) {
4957
- this.close();
4958
- }
4959
- };
4960
5535
  #ensureClient() {
4961
5536
  const key = sessionSourceKey(this);
4962
5537
  if (this.#client && this.#clientEndpoint === key) return this.#client;
@@ -4972,8 +5547,13 @@ var NoodleAssistantElement = class extends HTMLElementBase {
4972
5547
  const client = this.embedId ? createAssistantClient({
4973
5548
  embedId: this.embedId,
4974
5549
  ...this.serviceUrl ? { serviceUrl: this.serviceUrl } : {},
5550
+ ...this.#fetch ? { fetch: this.#fetch } : {},
4975
5551
  ...behavior
4976
- }) : createAssistantClient({ sessionEndpoint: this.sessionEndpoint, ...behavior });
5552
+ }) : createAssistantClient({
5553
+ sessionEndpoint: this.sessionEndpoint,
5554
+ ...this.#fetch ? { fetch: this.#fetch } : {},
5555
+ ...behavior
5556
+ });
4977
5557
  this.#client = client;
4978
5558
  this.#clientEndpoint = key;
4979
5559
  this.#unsubscribeClient = client.subscribe((event) => {
@@ -5002,6 +5582,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
5002
5582
  this.dispatchEvent(new CustomEvent("assistant-event", { detail: event }));
5003
5583
  this.#events.handle(event);
5004
5584
  if (event.event === "session_started") {
5585
+ this.#publicConfiguration.disconnect();
5586
+ this.removeAttribute("data-public-configuration-loading");
5005
5587
  this.#presentationGate.reveal();
5006
5588
  this.#setSessionState("ready");
5007
5589
  this.#messages?.querySelector(".conversation-error")?.remove();
@@ -5051,10 +5633,10 @@ var NoodleAssistantElement = class extends HTMLElementBase {
5051
5633
  if (!this.#messages) return void 0;
5052
5634
  const message = document.createElement("div");
5053
5635
  message.className = `message ${role}`;
5054
- if (role === "assistant" && this.#appearance.behavior.showAvatars && this.#appearance.brand.avatar[this.#resolvedMode()]) {
5636
+ if (role === "assistant" && this.#appearance.behavior.showAvatars && this.#appearance.brand.avatar[this.#environment.resolvedTheme()]) {
5055
5637
  const avatar = document.createElement("img");
5056
5638
  avatar.className = "avatar";
5057
- avatar.src = this.#appearance.brand.avatar[this.#resolvedMode()] ?? "";
5639
+ avatar.src = this.#appearance.brand.avatar[this.#environment.resolvedTheme()] ?? "";
5058
5640
  avatar.alt = "";
5059
5641
  message.append(avatar);
5060
5642
  }
@@ -5135,12 +5717,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
5135
5717
  this.#primeSession();
5136
5718
  this.dispatchEvent(new CustomEvent("assistant-new-conversation"));
5137
5719
  }
5138
- #resolvedMode() {
5139
- const configured = this.getAttribute("theme") ?? this.#appearance.theme.defaultMode;
5140
- return configured === "auto" ? this.#media?.matches ? "dark" : "light" : configured;
5141
- }
5142
5720
  #applyTheme() {
5143
- const mode = this.#resolvedMode();
5721
+ const mode = this.#environment.resolvedTheme();
5144
5722
  for (const warning of applyAssistantElementTheme({
5145
5723
  host: this,
5146
5724
  root: this.shadowRoot,
@@ -5163,13 +5741,23 @@ var NoodleAssistantElement = class extends HTMLElementBase {
5163
5741
  updateAssistantAppTheme(app, mode);
5164
5742
  }
5165
5743
  }
5744
+ #applyConfiguration(configuration) {
5745
+ this.#appearance = resolveAppearance(configuration);
5746
+ this.#syncAppearance();
5747
+ this.#applyStartOpenPolicy();
5748
+ }
5749
+ #setPublicConfigurationLoading(loading) {
5750
+ this.toggleAttribute("data-public-configuration-loading", loading);
5751
+ if (loading) this.#presentationGate.syncOpenState();
5752
+ else this.#presentationGate.reveal();
5753
+ }
5166
5754
  #setSessionState(state) {
5167
5755
  this.#sessionState = state;
5168
5756
  syncAssistantSessionVisualState(this, this.shadowRoot, state, this.#appearance);
5169
5757
  }
5170
5758
  #closeAndRestoreFocus() {
5171
5759
  this.close();
5172
- this.shadowRoot?.querySelector(".launcher")?.focus();
5760
+ this.#launcher.focusTrigger();
5173
5761
  }
5174
5762
  #applyStartOpenPolicy() {
5175
5763
  if (this.#startOpenApplied) return;
@@ -5195,10 +5783,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
5195
5783
  textarea.placeholder = appearance.labels.composerPlaceholder;
5196
5784
  textarea.setAttribute("aria-label", appearance.labels.composerPlaceholder);
5197
5785
  queryRequired(this.shadowRoot, ".send").textContent = appearance.labels.send;
5198
- queryRequired(this.shadowRoot, ".launcher").setAttribute(
5199
- "aria-label",
5200
- appearance.labels.open
5201
- );
5202
5786
  queryRequired(this.shadowRoot, ".close").setAttribute(
5203
5787
  "aria-label",
5204
5788
  appearance.labels.close
@@ -5212,11 +5796,11 @@ var NoodleAssistantElement = class extends HTMLElementBase {
5212
5796
  button2.addEventListener("click", () => this.#sendGuarded(prompt));
5213
5797
  prompts.append(button2);
5214
5798
  }
5215
- queryRequired(this.shadowRoot, ".launcher").hidden = !appearance.behavior.showLauncher;
5216
5799
  queryRequired(this.shadowRoot, "header").hidden = !appearance.behavior.showHeader;
5217
5800
  const legal = queryRequired(this.shadowRoot, ".legal");
5218
5801
  legal.replaceChildren();
5219
5802
  appendLegalLinks(legal, this.#appearance);
5803
+ this.#launcher.sync();
5220
5804
  this.#applyTheme();
5221
5805
  }
5222
5806
  #render() {
@@ -5234,12 +5818,11 @@ var NoodleAssistantElement = class extends HTMLElementBase {
5234
5818
  this.toggleAttribute("mobile-fullscreen", appearance.layout.mobileFullscreen);
5235
5819
  this.shadowRoot.innerHTML = assistantElementMarkup(
5236
5820
  appearance,
5237
- this.hasAttribute("open"),
5238
5821
  `${ASSISTANT_ELEMENT_STYLES}${presentationStyles}`
5239
5822
  );
5240
5823
  queryRequired(this.shadowRoot, "strong").textContent = appearance.brand.name;
5241
5824
  const logo = queryRequired(this.shadowRoot, ".brand-logo");
5242
- const logoUrl = appearance.brand.logo[this.#resolvedMode()];
5825
+ const logoUrl = appearance.brand.logo[this.#environment.resolvedTheme()];
5243
5826
  if (logoUrl) {
5244
5827
  logo.src = logoUrl;
5245
5828
  logo.alt = appearance.brand.name;
@@ -5267,12 +5850,9 @@ var NoodleAssistantElement = class extends HTMLElementBase {
5267
5850
  this.#messages = this.shadowRoot.querySelector(".messages") ?? void 0;
5268
5851
  this.#messages?.append(...conversation);
5269
5852
  this.#thinking = this.#messages?.querySelector(".thinking") ?? void 0;
5270
- this.shadowRoot.querySelector(".launcher")?.addEventListener("click", () => {
5271
- this.open();
5272
- this.focusComposer();
5273
- });
5853
+ this.#launcher.connect(this.shadowRoot);
5274
5854
  this.shadowRoot.querySelector(".close")?.addEventListener("click", () => this.#closeAndRestoreFocus());
5275
- const form = queryRequired(this.shadowRoot, "form");
5855
+ const form = queryRequired(this.shadowRoot, ".composer");
5276
5856
  form.addEventListener("submit", (event) => {
5277
5857
  event.preventDefault();
5278
5858
  if (this.hasAttribute("busy")) {
@@ -5319,4 +5899,4 @@ export {
5319
5899
  dompurify/dist/purify.es.mjs:
5320
5900
  (*! @license DOMPurify 3.4.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.11/LICENSE *)
5321
5901
  */
5322
- //# sourceMappingURL=chunk-FJYPE4GC.js.map
5902
+ //# sourceMappingURL=chunk-5PW6PXCI.js.map