@noodleseed/assistant 1.25.0 → 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 (45) hide show
  1. package/README.md +63 -24
  2. package/dist/{app-view-element-BM4lral-.d.ts → app-view-element-C_ATXseu.d.ts} +2 -3
  3. package/dist/{app-view-element-9ojWWYov.d.cts → app-view-element-CiFVNIke.d.cts} +2 -3
  4. package/dist/app-view.cjs +41 -213
  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-LNBUYHL7.js → chunk-5NBHHNI3.js} +2 -2
  12. package/dist/chunk-5NBHHNI3.js.map +1 -0
  13. package/dist/{chunk-SGMECNNU.js → chunk-5PW6PXCI.js} +702 -122
  14. package/dist/chunk-5PW6PXCI.js.map +1 -0
  15. package/dist/{chunk-7BQHW37I.js → chunk-FNXAVRL5.js} +7 -1
  16. package/dist/{chunk-7BQHW37I.js.map → chunk-FNXAVRL5.js.map} +1 -1
  17. package/dist/{chunk-P32MAR4K.js → chunk-XKTUXFGX.js} +42 -214
  18. package/dist/chunk-XKTUXFGX.js.map +1 -0
  19. package/dist/{client-BkogON2A.d.ts → client-BheCOERq.d.ts} +51 -2
  20. package/dist/{client-C07ppTU_.d.cts → client-BiMRVxjs.d.cts} +51 -2
  21. package/dist/client.cjs.map +1 -1
  22. package/dist/client.d.cts +2 -2
  23. package/dist/client.d.ts +2 -2
  24. package/dist/client.js +1 -1
  25. package/dist/embed.global.js +210 -161
  26. package/dist/index.cjs +767 -354
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +6 -53
  29. package/dist/index.d.ts +6 -53
  30. package/dist/index.js +3 -3
  31. package/dist/react/client.cjs.map +1 -1
  32. package/dist/react/client.d.cts +2 -2
  33. package/dist/react/client.d.ts +2 -2
  34. package/dist/react/client.js +1 -1
  35. package/dist/react.cjs +767 -354
  36. package/dist/react.cjs.map +1 -1
  37. package/dist/react.d.cts +4 -4
  38. package/dist/react.d.ts +4 -4
  39. package/dist/react.js +4 -4
  40. package/dist/server.d.cts +1 -1
  41. package/dist/server.d.ts +1 -1
  42. package/package.json +1 -1
  43. package/dist/chunk-LNBUYHL7.js.map +0 -1
  44. package/dist/chunk-P32MAR4K.js.map +0 -1
  45. package/dist/chunk-SGMECNNU.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -15197,7 +15197,7 @@ __export(src_exports, {
15197
15197
  });
15198
15198
  module.exports = __toCommonJS(src_exports);
15199
15199
 
15200
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_supports-color@7.2.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
15200
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
15201
15201
  init_v4();
15202
15202
  var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
15203
15203
  var JSONRPC_VERSION = "2.0";
@@ -20594,7 +20594,7 @@ var pipelineType = ZodPipeline.create;
20594
20594
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/mini/parse.js
20595
20595
  init_core2();
20596
20596
 
20597
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_supports-color@7.2.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
20597
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
20598
20598
  function isZ4Schema(s2) {
20599
20599
  const schema = s2;
20600
20600
  return !!schema._zod;
@@ -20657,7 +20657,7 @@ function getLiteralValue(schema) {
20657
20657
  return void 0;
20658
20658
  }
20659
20659
 
20660
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_supports-color@7.2.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
20660
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
20661
20661
  function isTerminal(status) {
20662
20662
  return status === "completed" || status === "failed" || status === "cancelled";
20663
20663
  }
@@ -20665,7 +20665,7 @@ function isTerminal(status) {
20665
20665
  // ../../node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@4.4.3/node_modules/zod-to-json-schema/dist/esm/parsers/string.js
20666
20666
  var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");
20667
20667
 
20668
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_supports-color@7.2.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
20668
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
20669
20669
  function getMethodLiteral(schema) {
20670
20670
  const shape = getObjectShape(schema);
20671
20671
  const methodSchema = shape?.method;
@@ -20686,7 +20686,7 @@ function parseWithCompat(schema, data) {
20686
20686
  return result.data;
20687
20687
  }
20688
20688
 
20689
- // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_supports-color@7.2.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
20689
+ // ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
20690
20690
  var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4;
20691
20691
  var Protocol = class {
20692
20692
  constructor(_options) {
@@ -21798,15 +21798,6 @@ var E = class {
21798
21798
  sessionId;
21799
21799
  setProtocolVersion;
21800
21800
  };
21801
- function AZ(X2) {
21802
- if (!X2) return "";
21803
- let Y = [];
21804
- if (X2.camera) Y.push("camera");
21805
- if (X2.microphone) Y.push("microphone");
21806
- if (X2.geolocation) Y.push("geolocation");
21807
- if (X2.clipboardWrite) Y.push("clipboard-write");
21808
- return Y.join("; ");
21809
- }
21810
21801
  var KX = [j];
21811
21802
  var DX = class extends _ {
21812
21803
  _client;
@@ -22077,131 +22068,34 @@ function GX(X2, Y) {
22077
22068
  return JSON.stringify(X2) === JSON.stringify(Y);
22078
22069
  }
22079
22070
 
22080
- // src/app-sandbox-policy.ts
22081
- var MAX_DOMAINS_PER_DIRECTIVE = 32;
22082
- function isRecord(value) {
22083
- return typeof value === "object" && value !== null && !Array.isArray(value);
22084
- }
22085
- function isLoopback(hostname3) {
22086
- return hostname3 === "localhost" || hostname3 === "127.0.0.1" || hostname3 === "[::1]";
22087
- }
22088
- function normalizeDomain(value, connect) {
22089
- if (value.length > 512 || /[;\r\n'"\s]/u.test(value)) return void 0;
22090
- try {
22091
- const parsed = new URL(value);
22092
- const secure = parsed.protocol === "https:" || connect && parsed.protocol === "wss:";
22093
- const local = isLoopback(parsed.hostname) && (parsed.protocol === "http:" || connect && parsed.protocol === "ws:");
22094
- if (!secure && !local) return void 0;
22095
- if (parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
22096
- return void 0;
22097
- }
22098
- return parsed.origin;
22099
- } catch {
22100
- return void 0;
22101
- }
22102
- }
22103
- function normalizeDomains(values, connect) {
22104
- const normalized = [
22105
- ...new Set(
22106
- values.slice(0, MAX_DOMAINS_PER_DIRECTIVE).map((value) => typeof value === "string" ? normalizeDomain(value, connect) : void 0).filter((value) => value !== void 0)
22107
- )
22108
- ];
22109
- return normalized;
22110
- }
22111
- function resolveAssistantAppSandboxPolicy(resourceMeta) {
22112
- if (!isRecord(resourceMeta)) return {};
22113
- const rawCsp = resourceMeta.csp;
22114
- const csp = isRecord(rawCsp) ? {
22115
- ...Array.isArray(rawCsp.connectDomains) ? { connectDomains: normalizeDomains(rawCsp.connectDomains, true) } : {},
22116
- ...Array.isArray(rawCsp.resourceDomains) ? { resourceDomains: normalizeDomains(rawCsp.resourceDomains, false) } : {},
22117
- ...Array.isArray(rawCsp.frameDomains) ? { frameDomains: normalizeDomains(rawCsp.frameDomains, false) } : {},
22118
- ...Array.isArray(rawCsp.baseUriDomains) ? { baseUriDomains: normalizeDomains(rawCsp.baseUriDomains, false) } : {}
22119
- } : void 0;
22120
- const rawPermissions = resourceMeta.permissions;
22121
- const permissions = isRecord(rawPermissions) ? {
22122
- ...isRecord(rawPermissions.camera) ? { camera: {} } : {},
22123
- ...isRecord(rawPermissions.microphone) ? { microphone: {} } : {},
22124
- ...isRecord(rawPermissions.geolocation) ? { geolocation: {} } : {},
22125
- ...isRecord(rawPermissions.clipboardWrite) ? { clipboardWrite: {} } : {}
22126
- } : void 0;
22127
- const allowAttribute = AZ(permissions);
22128
- return {
22129
- ...csp === void 0 ? {} : { csp },
22130
- ...permissions === void 0 ? {} : { permissions },
22131
- ...allowAttribute ? { allowAttribute } : {}
22132
- };
22133
- }
22134
- function assistantAppSandboxUrl(sandboxUrl, policy) {
22135
- const parsed = new URL(sandboxUrl);
22136
- if (policy.csp) parsed.searchParams.set("csp", JSON.stringify(policy.csp));
22137
- const parentOrigin = globalThis.location?.origin;
22138
- if (parentOrigin && parentOrigin !== "null") {
22139
- parsed.hash = new URLSearchParams({ parentOrigin }).toString();
22140
- }
22141
- return parsed.href;
22142
- }
22143
-
22144
22071
  // src/app-host.ts
22145
- var PROXY_DOCUMENT = `<!doctype html><meta charset="utf-8"><style>html,body,iframe{border:0;margin:0;width:100%;height:100%;overflow:hidden}body{background:transparent}</style><body><script>
22146
- let inner,loaded=false;
22147
- if(self===top)throw new Error('The assistant sandbox relay must be embedded');
22148
- const standardSandbox='allow-scripts allow-same-origin allow-forms';
22149
- const rawParentOrigin=new URLSearchParams(location.hash.slice(1)).get('parentOrigin');
22150
- let expectedParentOrigin;
22151
- try{const parsed=new URL(rawParentOrigin||'');if(parsed.origin===rawParentOrigin&&/^https?:$/.test(parsed.protocol))expectedParentOrigin=parsed.origin}catch{}
22152
- const parentTarget=expectedParentOrigin||'*';
22153
- const ownOrigin=self.origin;
22072
+ var PROXY_DOCUMENT = `<!doctype html><meta charset="utf-8"><style>html,body,iframe{border:0;margin:0;width:100%;height:100%;overflow:hidden}body{background:transparent}</style><script>
22073
+ let inner;
22154
22074
  const ready={jsonrpc:'2.0',method:'ui/notifications/sandbox-proxy-ready',params:{}};
22155
22075
  // Re-announce until the host acknowledges with the resource: when this document is hosted
22156
22076
  // cross-origin, the first announcement can arrive before the host's load handler is listening.
22157
- const announce=setInterval(()=>parent.postMessage(ready,parentTarget),120);
22077
+ const announce=setInterval(()=>parent.postMessage(ready,'*'),120);
22158
22078
  setTimeout(()=>clearInterval(announce),15000);
22159
22079
  addEventListener('message',(event)=>{
22160
22080
  if(event.source===parent){
22161
- if(expectedParentOrigin&&event.origin!==expectedParentOrigin)return;
22162
22081
  const message=event.data;
22163
22082
  if(message?.method==='ui/notifications/sandbox-resource-ready'){
22164
22083
  clearInterval(announce);
22165
- if(loaded)return;
22166
- loaded=true;
22167
- const requestedSandbox=message.params?.sandbox;
22084
+ if(inner)return;
22168
22085
  inner=document.createElement('iframe');
22169
- inner.setAttribute('sandbox',requestedSandbox===standardSandbox?standardSandbox:'allow-scripts');
22086
+ inner.setAttribute('sandbox',message.params?.sandbox||'allow-scripts');
22170
22087
  inner.setAttribute('referrerpolicy','no-referrer');
22171
- const permissions=message.params?.permissions;
22172
- const allow=[];
22173
- if(permissions?.camera)allow.push('camera');
22174
- if(permissions?.microphone)allow.push('microphone');
22175
- if(permissions?.geolocation)allow.push('geolocation');
22176
- if(permissions?.clipboardWrite)allow.push('clipboard-write');
22177
- if(allow.length)inner.setAttribute('allow',allow.join('; '));
22178
- document.body.append(inner);
22179
- let html=String(message.params?.html||'');
22180
- const csp=message.params?.csp;
22181
- if(csp&&typeof csp==='object'){
22182
- const list=(value,fallback)=>Array.isArray(value)&&value.length?value.join(' '):fallback;
22183
- const resources=Array.isArray(csp.resourceDomains)?csp.resourceDomains:[];
22184
- const suffix=resources.length?' '+resources.join(' '):'';
22185
- const policy=["default-src 'none'","script-src 'unsafe-inline'"+suffix,"style-src 'unsafe-inline'"+suffix,'img-src data: blob:'+suffix,'font-src data: blob:'+suffix,'media-src data: blob:'+suffix,'worker-src blob:'+suffix,'connect-src '+list(csp.connectDomains,"'none'"),'frame-src '+list(csp.frameDomains,"'none'"),"object-src 'none'","form-action 'none'",'base-uri '+list(csp.baseUriDomains,"'none'")].join('; ');
22186
- const meta='<meta http-equiv="Content-Security-Policy" content="'+policy.replace(/&/g,'&amp;').replace(/"/g,'&quot;')+'">';
22187
- const doctype=html.match(/^\\s*<!doctype[^>]*>/i);
22188
- html=doctype?html.slice(0,doctype[0].length)+meta+html.slice(doctype[0].length):meta+html;
22189
- }
22190
- try{
22191
- const doc=inner.contentDocument||inner.contentWindow?.document;
22192
- if(!doc)throw new Error('document unavailable');
22193
- doc.open();doc.write(html);doc.close();
22194
- }catch{inner.srcdoc=html}
22088
+ inner.srcdoc=String(message.params?.html||'');
22089
+ document.body.replaceChildren(inner);
22195
22090
  return;
22196
22091
  }
22197
22092
  inner?.contentWindow?.postMessage(message,'*');
22198
22093
  } else if(inner && event.source===inner.contentWindow) {
22199
- if(event.origin!==ownOrigin)return;
22200
- parent.postMessage(event.data,parentTarget);
22094
+ parent.postMessage(event.data,'*');
22201
22095
  }
22202
22096
  });
22203
- parent.postMessage(ready,parentTarget);
22204
- </script></body>`;
22097
+ parent.postMessage(ready,'*');
22098
+ </script>`;
22205
22099
  function isAllowedAppLink(value, allowedDomains, baseUrl) {
22206
22100
  try {
22207
22101
  const parsed = new URL(value, baseUrl);
@@ -22228,42 +22122,16 @@ function createAssistantAppHostContext(theme, allowFullscreen = false, displayMo
22228
22122
  }
22229
22123
  var APP_RENDER_TIMEOUT_MS = 1e4;
22230
22124
  var APP_TEARDOWN_TIMEOUT_MS = 1e3;
22231
- var STANDARD_SANDBOX = "allow-scripts allow-same-origin allow-forms";
22232
22125
  var mountedApps = /* @__PURE__ */ new WeakMap();
22233
22126
  function resolveSandboxUrl(value) {
22234
22127
  if (!value) return void 0;
22235
22128
  try {
22236
22129
  const parsed = new URL(value);
22237
- const loopback = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]";
22238
- const permittedProtocol = parsed.protocol === "https:" || parsed.protocol === "http:" && loopback;
22239
- const embedderOrigin = globalThis.location?.origin;
22240
- if (!permittedProtocol || embedderOrigin && embedderOrigin !== "null" && parsed.origin === embedderOrigin) {
22241
- return void 0;
22242
- }
22243
- return parsed.href;
22130
+ return parsed.protocol === "https:" || parsed.protocol === "http:" ? parsed.href : void 0;
22244
22131
  } catch {
22245
22132
  return void 0;
22246
22133
  }
22247
22134
  }
22248
- function copyAppJson(value, depth = 0) {
22249
- if (depth > 64) throw new Error("App payload nesting exceeds the host limit");
22250
- if (value === null || typeof value === "string" || typeof value === "boolean") return value;
22251
- if (typeof value === "number") {
22252
- if (!Number.isFinite(value)) throw new Error("App payload numbers must be finite");
22253
- return value;
22254
- }
22255
- if (Array.isArray(value)) return value.map((entry) => copyAppJson(entry, depth + 1));
22256
- const copied = {};
22257
- for (const [key, entry] of Object.entries(value)) {
22258
- Object.defineProperty(copied, key, {
22259
- value: copyAppJson(entry, depth + 1),
22260
- enumerable: true,
22261
- configurable: true,
22262
- writable: true
22263
- });
22264
- }
22265
- return copied;
22266
- }
22267
22135
  function mountAssistantApp(detail, actions, options = {}) {
22268
22136
  if (!detail.html) return void 0;
22269
22137
  const card = document.createElement("section");
@@ -22284,25 +22152,16 @@ function mountAssistantApp(detail, actions, options = {}) {
22284
22152
  const frame = document.createElement("iframe");
22285
22153
  frame.className = "noodle-app-frame";
22286
22154
  frame.title = detail.title ?? detail.tool;
22287
- const policy = resolveAssistantAppSandboxPolicy(detail.resourceMeta);
22155
+ frame.setAttribute("sandbox", "allow-scripts");
22288
22156
  frame.setAttribute("referrerpolicy", "no-referrer");
22289
22157
  const sandboxUrl = resolveSandboxUrl(options.sandboxUrl);
22290
- const appliedPolicy = sandboxUrl ? policy : { csp: policy.csp ?? {} };
22291
- if (sandboxUrl) {
22292
- frame.setAttribute("sandbox", STANDARD_SANDBOX);
22293
- if (policy.allowAttribute) frame.setAttribute("allow", policy.allowAttribute);
22294
- frame.src = assistantAppSandboxUrl(sandboxUrl, policy);
22295
- } else {
22296
- frame.setAttribute("sandbox", "allow-scripts");
22297
- frame.srcdoc = PROXY_DOCUMENT;
22298
- }
22158
+ if (sandboxUrl) frame.src = sandboxUrl;
22159
+ else frame.srcdoc = PROXY_DOCUMENT;
22299
22160
  card.append(frame);
22300
22161
  let mountedBridge;
22301
22162
  let mountedTransport;
22302
22163
  let destroyed = false;
22303
22164
  let initialized = false;
22304
- let renderFailed = false;
22305
- let renderStage = "sandbox_load";
22306
22165
  let currentTheme = actions.theme;
22307
22166
  let displayMode = "inline";
22308
22167
  let hostContext = createAssistantAppHostContext(
@@ -22311,15 +22170,6 @@ function mountAssistantApp(detail, actions, options = {}) {
22311
22170
  displayMode
22312
22171
  );
22313
22172
  let destroyPromise;
22314
- let transportClosePromise;
22315
- let renderTimer;
22316
- const closeTransport = () => {
22317
- if (transportClosePromise) return transportClosePromise;
22318
- const closing = mountedTransport?.close().catch(() => {
22319
- }) ?? Promise.resolve();
22320
- transportClosePromise = closing;
22321
- return closing;
22322
- };
22323
22173
  const applyDisplayMode = (requestedMode) => {
22324
22174
  displayMode = requestedMode === "fullscreen" && options.allowFullscreen === true ? "fullscreen" : "inline";
22325
22175
  card.toggleAttribute("data-fullscreen", displayMode === "fullscreen");
@@ -22337,26 +22187,18 @@ function mountAssistantApp(detail, actions, options = {}) {
22337
22187
  applyDisplayMode("inline");
22338
22188
  frame.focus();
22339
22189
  });
22340
- const showRenderFailure = (code) => {
22341
- if (code === "view_render_timeout" && initialized || destroyed || renderFailed || !frame.isConnected)
22342
- return;
22343
- renderFailed = true;
22344
- clearTimeout(renderTimer);
22190
+ const renderTimer = setTimeout(() => {
22191
+ if (initialized || !frame.isConnected) return;
22345
22192
  const note = document.createElement("div");
22346
22193
  note.className = "noodle-app-fallback";
22347
22194
  note.textContent = "This app view could not be displayed.";
22348
22195
  frame.replaceWith(note);
22349
- options.onRenderFailure?.({ code, retryable: false, stage: renderStage });
22350
- void closeTransport();
22351
- };
22352
- renderTimer = setTimeout(() => {
22353
- showRenderFailure("view_render_timeout");
22196
+ options.onRenderFailure?.({ code: "view_render_timeout", retryable: false });
22354
22197
  }, APP_RENDER_TIMEOUT_MS);
22355
22198
  frame.addEventListener(
22356
22199
  "load",
22357
22200
  () => {
22358
- if (destroyed || renderFailed) return;
22359
- renderStage = "sandbox_proxy";
22201
+ if (destroyed) return;
22360
22202
  const target = frame.contentWindow;
22361
22203
  if (!target) return;
22362
22204
  const bridge = new DX(
@@ -22367,11 +22209,7 @@ function mountAssistantApp(detail, actions, options = {}) {
22367
22209
  serverTools: {},
22368
22210
  serverResources: {},
22369
22211
  updateModelContext: { text: {}, structuredContent: {} },
22370
- message: { text: {} },
22371
- sandbox: {
22372
- ...appliedPolicy.csp === void 0 ? {} : { csp: appliedPolicy.csp },
22373
- ...appliedPolicy.permissions === void 0 ? {} : { permissions: appliedPolicy.permissions }
22374
- }
22212
+ message: { text: {} }
22375
22213
  },
22376
22214
  {
22377
22215
  hostContext
@@ -22379,15 +22217,13 @@ function mountAssistantApp(detail, actions, options = {}) {
22379
22217
  );
22380
22218
  mountedBridge = bridge;
22381
22219
  bridge.onsandboxready = () => {
22382
- if (destroyed || renderFailed) return;
22383
- renderStage = "app_initialize";
22220
+ if (destroyed) return;
22384
22221
  const current = options.getDetail?.() ?? detail;
22385
22222
  void bridge.sendSandboxResourceReady({
22386
22223
  html: current.html,
22387
- sandbox: sandboxUrl ? STANDARD_SANDBOX : "allow-scripts",
22388
- ...appliedPolicy.csp === void 0 ? {} : { csp: appliedPolicy.csp },
22389
- ...appliedPolicy.permissions === void 0 ? {} : { permissions: appliedPolicy.permissions }
22390
- }).catch(() => showRenderFailure("view_bridge_error"));
22224
+ sandbox: "allow-scripts",
22225
+ ...current.resourceMeta ?? {}
22226
+ });
22391
22227
  };
22392
22228
  bridge.onsizechange = ({ height }) => {
22393
22229
  if (destroyed) return;
@@ -22451,30 +22287,21 @@ function mountAssistantApp(detail, actions, options = {}) {
22451
22287
  return await actions.client.requestApp("resources/read", params);
22452
22288
  };
22453
22289
  bridge.oninitialized = () => {
22454
- if (destroyed || renderFailed) return;
22455
- try {
22456
- const current = options.getDetail?.() ?? detail;
22457
- const copiedArguments = copyAppJson(current.arguments ?? {});
22458
- const copiedResult = copyAppJson(current.result);
22459
- const argumentsRecord = typeof copiedArguments === "object" && copiedArguments !== null && !Array.isArray(copiedArguments) ? copiedArguments : {};
22460
- initialized = true;
22461
- clearTimeout(renderTimer);
22462
- bridge.setHostContext(hostContext);
22463
- void Promise.all([
22464
- bridge.sendToolInput({ arguments: argumentsRecord }),
22465
- bridge.sendToolResult({
22466
- content: [{ type: "text", text: JSON.stringify(copiedResult) }],
22467
- ...typeof copiedResult === "object" && copiedResult !== null && !Array.isArray(copiedResult) ? { structuredContent: copiedResult } : {}
22468
- })
22469
- ]).catch(() => showRenderFailure("view_bridge_error"));
22470
- } catch {
22471
- showRenderFailure("view_bridge_error");
22472
- }
22290
+ if (destroyed) return;
22291
+ initialized = true;
22292
+ clearTimeout(renderTimer);
22293
+ bridge.setHostContext(hostContext);
22294
+ const current = options.getDetail?.() ?? detail;
22295
+ void bridge.sendToolInput({ arguments: current.arguments ?? {} });
22296
+ void bridge.sendToolResult({
22297
+ content: [{ type: "text", text: JSON.stringify(current.result) }],
22298
+ ...typeof current.result === "object" && current.result !== null && !Array.isArray(current.result) ? { structuredContent: current.result } : {}
22299
+ });
22473
22300
  };
22474
22301
  const transport = new E(target, target);
22475
22302
  mountedTransport = transport;
22476
22303
  void bridge.connect(transport).catch(() => {
22477
- showRenderFailure("view_bridge_error");
22304
+ void destroy();
22478
22305
  });
22479
22306
  },
22480
22307
  { once: true }
@@ -22500,7 +22327,8 @@ function mountAssistantApp(detail, actions, options = {}) {
22500
22327
  }
22501
22328
  } catch {
22502
22329
  }
22503
- await closeTransport();
22330
+ await mountedTransport?.close().catch(() => {
22331
+ });
22504
22332
  card.remove();
22505
22333
  mountedApps.delete(card);
22506
22334
  })();
@@ -22515,7 +22343,7 @@ function updateAssistantAppTheme(card, theme) {
22515
22343
 
22516
22344
  // src/appearance.ts
22517
22345
  var common = {
22518
- fontFamily: "inherit",
22346
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
22519
22347
  monoFontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
22520
22348
  baseFontSize: 15,
22521
22349
  messageFontSize: 15,
@@ -22526,7 +22354,7 @@ var common = {
22526
22354
  buttonRadius: 999,
22527
22355
  launcherRadius: 999,
22528
22356
  spacing: 8,
22529
- backdropBlur: 18,
22357
+ backdropBlur: 6,
22530
22358
  motionScale: 1
22531
22359
  };
22532
22360
  var DEFAULT_APPEARANCE = {
@@ -22546,7 +22374,7 @@ var DEFAULT_APPEARANCE = {
22546
22374
  accent: "#C2410C",
22547
22375
  accentText: "#FFFFFF",
22548
22376
  link: "#9A3412",
22549
- divider: "#E4E4E7",
22377
+ divider: "rgba(0, 0, 0, 0.1)",
22550
22378
  focus: "#C2410C",
22551
22379
  success: "#16835D",
22552
22380
  warning: "#A45B00",
@@ -22554,7 +22382,7 @@ var DEFAULT_APPEARANCE = {
22554
22382
  overlay: "#18181B66",
22555
22383
  code: "#F1F1F3",
22556
22384
  widgetFrame: "#E4E4E7",
22557
- shadow: "0 4px 16px rgba(0, 0, 0, 0.06), 0 24px 70px rgba(0, 0, 0, 0.10)"
22385
+ shadow: "0 4px 16px rgba(0, 0, 0, 0.06)"
22558
22386
  },
22559
22387
  dark: {
22560
22388
  ...common,
@@ -22569,7 +22397,7 @@ var DEFAULT_APPEARANCE = {
22569
22397
  accent: "#FB923C",
22570
22398
  accentText: "#1C0A02",
22571
22399
  link: "#FDBA74",
22572
- divider: "#292524",
22400
+ divider: "rgba(255, 255, 255, 0.1)",
22573
22401
  focus: "#FB923C",
22574
22402
  success: "#58C69A",
22575
22403
  warning: "#F2B45F",
@@ -22577,15 +22405,15 @@ var DEFAULT_APPEARANCE = {
22577
22405
  overlay: "#00000099",
22578
22406
  code: "#26262C",
22579
22407
  widgetFrame: "#34343B",
22580
- shadow: "0 8px 32px rgba(0, 0, 0, 0.48)"
22408
+ shadow: "0 8px 32px rgba(0, 0, 0, 0.4)"
22581
22409
  }
22582
22410
  },
22583
22411
  layout: {
22584
22412
  mode: "floating",
22585
- position: "bottom-right",
22586
- panelWidth: 520,
22587
- panelMinHeight: 540,
22588
- panelMaxHeight: 820,
22413
+ position: "bottom-center",
22414
+ panelWidth: 970,
22415
+ panelMinHeight: 0,
22416
+ panelMaxHeight: 1025,
22589
22417
  edgeOffset: 20,
22590
22418
  zIndex: 2147483e3,
22591
22419
  density: "comfortable",
@@ -22598,11 +22426,13 @@ var DEFAULT_APPEARANCE = {
22598
22426
  showLauncher: true,
22599
22427
  showHeader: true,
22600
22428
  showAvatars: true,
22601
- showTimestamps: false
22429
+ showTimestamps: false,
22430
+ showPoweredBy: true
22602
22431
  },
22603
22432
  labels: {
22604
- welcomeHeading: "How can I help?",
22433
+ welcomeHeading: "",
22605
22434
  welcomeMessage: "",
22435
+ launcherPlaceholder: "Chat with us",
22606
22436
  composerPlaceholder: "Ask a question\u2026",
22607
22437
  thinking: "Thinking",
22608
22438
  send: "Send",
@@ -22636,11 +22466,12 @@ var DEFAULT_APPEARANCE = {
22636
22466
  },
22637
22467
  presentation: {
22638
22468
  panel: {
22639
- surface: "glass",
22469
+ surface: "solid",
22640
22470
  elevation: "soft",
22641
22471
  border: "subtle"
22642
22472
  },
22643
22473
  launcher: {
22474
+ style: "pill",
22644
22475
  icon: "brand-mark",
22645
22476
  size: "md",
22646
22477
  status: "none",
@@ -22650,7 +22481,11 @@ var DEFAULT_APPEARANCE = {
22650
22481
  composer: { leadingIcon: "none", sendIcon: "arrow-up", shape: "pill" },
22651
22482
  messages: { userStyle: "bubble", assistantStyle: "plain" }
22652
22483
  },
22653
- suggestedPrompts: [],
22484
+ suggestedPrompts: [
22485
+ "What products do you have?",
22486
+ "Tell me about your business",
22487
+ "How can I get in touch?"
22488
+ ],
22654
22489
  locale: "en",
22655
22490
  direction: "auto"
22656
22491
  };
@@ -22666,7 +22501,7 @@ function resolveAppearance(input = {}) {
22666
22501
  mark: themedAsset(branding?.mark)
22667
22502
  },
22668
22503
  theme: {
22669
- defaultMode: branding?.colorScheme ?? DEFAULT_APPEARANCE.theme.defaultMode,
22504
+ defaultMode: assistant?.theme ?? branding?.colorScheme ?? DEFAULT_APPEARANCE.theme.defaultMode,
22670
22505
  light: resolveTheme("light", branding),
22671
22506
  dark: resolveTheme("dark", branding)
22672
22507
  },
@@ -22677,9 +22512,17 @@ function resolveAppearance(input = {}) {
22677
22512
  ...assistant?.layout?.edgeOffset === void 0 ? {} : { edgeOffset: boundedNumber(assistant.layout.edgeOffset, 0, 96) }
22678
22513
  },
22679
22514
  behavior: { ...DEFAULT_APPEARANCE.behavior, ...assistant?.behavior },
22680
- labels: { ...DEFAULT_APPEARANCE.labels, ...assistant?.labels },
22515
+ labels: {
22516
+ ...DEFAULT_APPEARANCE.labels,
22517
+ ...branding?.name ? { launcherPlaceholder: `Chat with ${branding.name}` } : {},
22518
+ ...assistant?.labels
22519
+ },
22681
22520
  presentation: resolvePresentation(assistant?.presentation),
22682
- suggestedPrompts: assistant?.suggestedPrompts ?? DEFAULT_APPEARANCE.suggestedPrompts,
22521
+ suggestedPrompts: assistant?.suggestedPrompts ?? (branding?.name ? [
22522
+ "What products do you have?",
22523
+ `Tell me about ${branding.name}`,
22524
+ "How can I get in touch?"
22525
+ ] : DEFAULT_APPEARANCE.suggestedPrompts),
22683
22526
  ...assistant?.privacyUrl ? { privacyUrl: assistant.privacyUrl } : {},
22684
22527
  ...assistant?.termsUrl ? { termsUrl: assistant.termsUrl } : {},
22685
22528
  locale: assistant?.locale ?? DEFAULT_APPEARANCE.locale,
@@ -24950,6 +24793,10 @@ async function resolve(value) {
24950
24793
  return Promise.resolve(value);
24951
24794
  }
24952
24795
 
24796
+ // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/index.js
24797
+ init_external();
24798
+ init_external();
24799
+
24953
24800
  // ../../node_modules/.pnpm/ai@6.0.201_zod@4.4.3/node_modules/ai/dist/index.mjs
24954
24801
  init_v4();
24955
24802
  init_v4();
@@ -27499,14 +27346,14 @@ function toChatError(error51) {
27499
27346
  }
27500
27347
  function readErrorDetail(error51) {
27501
27348
  const detail = Reflect.get(error51, "detail");
27502
- if (!isRecord2(detail) || typeof detail.code !== "string") return void 0;
27349
+ if (!isRecord(detail) || typeof detail.code !== "string") return void 0;
27503
27350
  return {
27504
27351
  code: detail.code,
27505
27352
  ...typeof detail.status === "number" ? { status: detail.status } : {},
27506
27353
  retryable: detail.retryable === true
27507
27354
  };
27508
27355
  }
27509
- function isRecord2(value) {
27356
+ function isRecord(value) {
27510
27357
  return typeof value === "object" && value !== null && !Array.isArray(value);
27511
27358
  }
27512
27359
 
@@ -27533,7 +27380,7 @@ var AssistantClientError = class extends Error {
27533
27380
  }
27534
27381
  };
27535
27382
  function parseSession(value) {
27536
- if (!isRecord3(value) || !isRecord3(value.endpoints)) {
27383
+ if (!isRecord2(value) || !isRecord2(value.endpoints)) {
27537
27384
  throw clientError("session_failed", "Assistant session response is invalid", true);
27538
27385
  }
27539
27386
  const interactions = value.endpoints.interactions;
@@ -27554,11 +27401,11 @@ function parseSession(value) {
27554
27401
  ...typeof sandbox === "string" ? { sandbox } : {},
27555
27402
  ...typeof transcript === "string" ? { transcript } : {}
27556
27403
  },
27557
- ...isRecord3(value.configuration) ? { configuration: value.configuration } : {},
27558
- ...isRecord3(value.resume) && typeof value.resume.tool === "string" ? { resume: { tool: value.resume.tool } } : {}
27404
+ ...isRecord2(value.configuration) ? { configuration: value.configuration } : {},
27405
+ ...isRecord2(value.resume) && typeof value.resume.tool === "string" ? { resume: { tool: value.resume.tool } } : {}
27559
27406
  };
27560
27407
  }
27561
- function isRecord3(value) {
27408
+ function isRecord2(value) {
27562
27409
  return typeof value === "object" && value !== null && !Array.isArray(value);
27563
27410
  }
27564
27411
  async function readStableErrorCode(response) {
@@ -27568,7 +27415,7 @@ async function readStableErrorCode(response) {
27568
27415
  } catch {
27569
27416
  return void 0;
27570
27417
  }
27571
- if (!isRecord3(value) || typeof value.code !== "string") return void 0;
27418
+ if (!isRecord2(value) || typeof value.code !== "string") return void 0;
27572
27419
  return /^[A-Za-z0-9_.-]{1,64}$/.test(value.code) ? value.code : void 0;
27573
27420
  }
27574
27421
  function clientError(code, message, retryable, status, options, serviceCode) {
@@ -27599,12 +27446,12 @@ function toAssistantClientEvent(event) {
27599
27446
  }
27600
27447
  break;
27601
27448
  case "tool_proposed":
27602
- if (hasString(value, "id") && hasString(value, "tool") && hasOptionalString(value.title) && hasOptionalString(value.description) && hasOptionalJson(value.arguments) && (value.reviewSchema === void 0 || isRecord4(value.reviewSchema)) && hasOptionalString(value.expiresAt) && (value.requiresConfirmation === void 0 || value.requiresConfirmation === true) && hasOptionalTurnId(value)) {
27449
+ if (hasString(value, "id") && hasString(value, "tool") && hasOptionalString(value.title) && hasOptionalString(value.description) && hasOptionalJson(value.arguments) && (value.reviewSchema === void 0 || isRecord3(value.reviewSchema)) && hasOptionalString(value.expiresAt) && (value.requiresConfirmation === void 0 || value.requiresConfirmation === true) && hasOptionalTurnId(value)) {
27603
27450
  return event;
27604
27451
  }
27605
27452
  break;
27606
27453
  case "input_requested":
27607
- if (hasString(value, "id") && hasString(value, "message") && isRecord4(value.requestedSchema) && hasString(value, "expiresAt") && hasOptionalTurnId(value)) {
27454
+ if (hasString(value, "id") && hasString(value, "message") && isRecord3(value.requestedSchema) && hasString(value, "expiresAt") && hasOptionalTurnId(value)) {
27608
27455
  return event;
27609
27456
  }
27610
27457
  break;
@@ -27648,7 +27495,7 @@ function toAssistantClientEvent(event) {
27648
27495
  return { event: "unrecognized", data: { name: event.event, payload: value } };
27649
27496
  }
27650
27497
  function isViewAvailableDetail(value) {
27651
- return hasString(value, "id") && hasString(value, "tool") && typeof value.resourceUri === "string" && value.resourceUri.startsWith("ui://") && hasOptionalString(value.title) && (value.replayed === void 0 || value.replayed === true) && isJsonValue(value.result, 0) && hasOptionalJson(value.arguments) && hasOptionalString(value.html) && (value.resourceMeta === void 0 || isRecord4(value.resourceMeta)) && (value.allowedOpenDomains === void 0 || Array.isArray(value.allowedOpenDomains) && value.allowedOpenDomains.every(isHttpsUrl)) && hasOptionalTurnId(value);
27498
+ return hasString(value, "id") && hasString(value, "tool") && typeof value.resourceUri === "string" && value.resourceUri.startsWith("ui://") && hasOptionalString(value.title) && (value.replayed === void 0 || value.replayed === true) && isJsonValue(value.result, 0) && hasOptionalJson(value.arguments) && hasOptionalString(value.html) && (value.resourceMeta === void 0 || isRecord3(value.resourceMeta)) && (value.allowedOpenDomains === void 0 || Array.isArray(value.allowedOpenDomains) && value.allowedOpenDomains.every(isHttpsUrl)) && hasOptionalTurnId(value);
27652
27499
  }
27653
27500
  function isHttpsUrl(value) {
27654
27501
  if (typeof value !== "string") return false;
@@ -27681,17 +27528,22 @@ function isJsonValue(value, depth) {
27681
27528
  if (Array.isArray(value)) {
27682
27529
  return value.length <= 256 && value.every((entry) => isJsonValue(entry, depth + 1));
27683
27530
  }
27684
- if (!isRecord4(value)) return false;
27531
+ if (!isRecord3(value)) return false;
27685
27532
  const entries2 = Object.entries(value);
27686
27533
  return entries2.length <= 256 && entries2.every(([, entry]) => isJsonValue(entry, depth + 1));
27687
27534
  }
27688
- function isRecord4(value) {
27535
+ function isRecord3(value) {
27689
27536
  return typeof value === "object" && value !== null && !Array.isArray(value);
27690
27537
  }
27691
27538
 
27692
27539
  // src/session-source.ts
27693
27540
  var NOODLE_CLOUD_URL = "https://cloud.noodleseed.dev";
27694
27541
  var PUBLIC_SESSION_PATH = "/v1/assistant/public-sessions";
27542
+ var PUBLIC_CONFIGURATION_PATH = "/v1/assistant/public-configurations";
27543
+ function publicConfigurationUrl(serviceUrl, embedId) {
27544
+ const base = (serviceUrl || NOODLE_CLOUD_URL).replace(/\/+$/, "");
27545
+ return `${base}${PUBLIC_CONFIGURATION_PATH}/${encodeURIComponent(embedId)}`;
27546
+ }
27695
27547
  function resolveSessionSource(options) {
27696
27548
  const { sessionEndpoint, embedId, serviceUrl } = options;
27697
27549
  if (Boolean(embedId) === Boolean(sessionEndpoint)) {
@@ -27852,15 +27704,15 @@ function parseFrame(frame) {
27852
27704
  if (event === "done" && !isValidDonePayload(parsed)) {
27853
27705
  throw invalidStream("assistant event stream contained an invalid done event");
27854
27706
  }
27855
- return { event, data: isRecord5(parsed) ? parsed : { value: parsed } };
27707
+ return { event, data: isRecord4(parsed) ? parsed : { value: parsed } };
27856
27708
  }
27857
27709
  function normalizeLineEndings(input) {
27858
27710
  return input.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
27859
27711
  }
27860
27712
  function isValidDonePayload(value) {
27861
- return isRecord5(value) && (value.turnId === void 0 || typeof value.turnId === "string" && value.turnId.length > 0);
27713
+ return isRecord4(value) && (value.turnId === void 0 || typeof value.turnId === "string" && value.turnId.length > 0);
27862
27714
  }
27863
- function isRecord5(value) {
27715
+ function isRecord4(value) {
27864
27716
  return typeof value === "object" && value !== null && !Array.isArray(value);
27865
27717
  }
27866
27718
  function invalidStream(message, cause) {
@@ -28379,10 +28231,10 @@ function authorizedHeaders(token, accept) {
28379
28231
  };
28380
28232
  }
28381
28233
  function parseAppInteraction(value) {
28382
- if (!isRecord3(value) || !isRecord3(value.interaction)) return void 0;
28234
+ if (!isRecord2(value) || !isRecord2(value.interaction)) return void 0;
28383
28235
  const event = value.interaction.event;
28384
28236
  const data = value.interaction.data;
28385
- if (event !== "tool_proposed" && event !== "input_requested" || !isRecord3(data)) {
28237
+ if (event !== "tool_proposed" && event !== "input_requested" || !isRecord2(data)) {
28386
28238
  return void 0;
28387
28239
  }
28388
28240
  const parsed = toAssistantClientEvent({ event, data });
@@ -29203,11 +29055,11 @@ function createDOMPurify() {
29203
29055
  const originalDocument = document2;
29204
29056
  const currentScript = originalDocument.currentScript;
29205
29057
  window2.DocumentFragment;
29206
- const HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
29058
+ const HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element2 = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
29207
29059
  _window$NamedNodeMap === void 0 ? window2.NamedNodeMap || window2.MozNamedAttrMap : _window$NamedNodeMap;
29208
29060
  window2.HTMLFormElement;
29209
29061
  const DOMParser = window2.DOMParser, trustedTypes = window2.trustedTypes;
29210
- const ElementPrototype = Element.prototype;
29062
+ const ElementPrototype = Element2.prototype;
29211
29063
  const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
29212
29064
  const remove = lookupGetter(ElementPrototype, "remove");
29213
29065
  const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
@@ -31581,7 +31433,7 @@ var AssistantElementEventController = class {
31581
31433
  if (event.event === "unrecognized") return;
31582
31434
  const { event: name21, data } = event;
31583
31435
  if (name21 === "session_started") {
31584
- if (isRecord6(data.configuration)) {
31436
+ if (isRecord5(data.configuration)) {
31585
31437
  this.#host.applyConfiguration(data.configuration);
31586
31438
  }
31587
31439
  this.#host.element.dispatchEvent(
@@ -31649,10 +31501,10 @@ var AssistantElementEventController = class {
31649
31501
  this.#appendToolProposal(
31650
31502
  String(data.id ?? ""),
31651
31503
  String(data.tool ?? "action"),
31652
- isRecord6(data.arguments) ? data.arguments : void 0,
31504
+ isRecord5(data.arguments) ? data.arguments : void 0,
31653
31505
  typeof data.title === "string" ? data.title : void 0,
31654
31506
  "description" in data && typeof data.description === "string" ? data.description : void 0,
31655
- "reviewSchema" in data && isRecord6(data.reviewSchema) ? data.reviewSchema : void 0
31507
+ "reviewSchema" in data && isRecord5(data.reviewSchema) ? data.reviewSchema : void 0
31656
31508
  );
31657
31509
  return;
31658
31510
  }
@@ -31665,7 +31517,7 @@ var AssistantElementEventController = class {
31665
31517
  );
31666
31518
  return;
31667
31519
  }
31668
- if (name21 === "input_requested" && isRecord6(data.requestedSchema)) {
31520
+ if (name21 === "input_requested" && isRecord5(data.requestedSchema)) {
31669
31521
  this.#appendInputRequest(
31670
31522
  String(data.id ?? ""),
31671
31523
  String(data.message ?? "More information is required"),
@@ -31836,7 +31688,7 @@ var AssistantElementEventController = class {
31836
31688
  this.#host.revealLatest();
31837
31689
  }
31838
31690
  };
31839
- function isRecord6(value) {
31691
+ function isRecord5(value) {
31840
31692
  return typeof value === "object" && value !== null && !Array.isArray(value);
31841
31693
  }
31842
31694
 
@@ -31857,19 +31709,40 @@ function queryRequired(root, selector) {
31857
31709
  if (!element) throw new Error(`assistant renderer is missing ${selector}`);
31858
31710
  return element;
31859
31711
  }
31860
- function assistantElementMarkup(appearance, open, styles) {
31712
+ function assistantElementMarkup(appearance, styles) {
31861
31713
  return `${styles}
31862
- <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>
31714
+ <div class="launcher">
31715
+ <slot name="launcher-icon"></slot>
31716
+ <span class="launcher-loader" aria-hidden="true"></span>
31717
+ <button class="launcher-trigger" type="button" aria-label="${escapeAttribute(appearance.labels.open)}" aria-controls="assistant-launcher-form" aria-expanded="false">
31718
+ <span class="launcher-label">${escapeAttribute(appearance.labels.launcherPlaceholder)}</span>
31719
+ <span class="launcher-arrow" aria-hidden="true"></span>
31720
+ </button>
31721
+ <form class="launcher-form" id="assistant-launcher-form" aria-hidden="true" autocomplete="off">
31722
+ <input class="launcher-input" type="text" aria-label="${escapeAttribute(appearance.labels.launcherPlaceholder)}" placeholder="${escapeAttribute(appearance.labels.launcherPlaceholder)}" tabindex="-1">
31723
+ <button class="launcher-send" type="submit" aria-label="${escapeAttribute(appearance.labels.send)}"></button>
31724
+ </form>
31725
+ <span class="visually-hidden" data-session-status aria-live="polite" hidden></span>
31726
+ </div>
31863
31727
  <section class="panel" id="assistant-panel" role="dialog" aria-label="${escapeAttribute(appearance.brand.name)}">
31864
31728
  <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>
31865
31729
  <div class="welcome"><slot name="empty-state"></slot></div>
31866
- <div class="suggested-prompts"></div>
31867
31730
  <div class="messages-region">
31868
31731
  <div class="messages" aria-live="polite"></div>
31869
31732
  <button class="new-messages" type="button" aria-label="${escapeAttribute(appearance.labels.newMessages)}" hidden></button>
31870
31733
  </div>
31871
- <form><slot name="composer-leading"></slot><textarea rows="1"></textarea><button class="send" type="submit"></button><slot name="composer-trailing"></slot></form>
31734
+ <div class="input-section">
31735
+ <div class="suggested-prompts"></div>
31736
+ <form class="composer"><slot name="composer-leading"></slot><textarea rows="1"></textarea><button class="send" type="submit"></button><slot name="composer-trailing"></slot></form>
31737
+ </div>
31872
31738
  <nav class="legal" aria-label="Legal"></nav>
31739
+ <div class="powered-by-row">
31740
+ <a class="powered-by" href="https://noodleseed.com" target="_blank" rel="noopener noreferrer">
31741
+ <span>Powered by</span>
31742
+ <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>
31743
+ <span class="powered-by-name">Noodle Seed</span>
31744
+ </a>
31745
+ </div>
31873
31746
  <slot name="conversation-footer"></slot>
31874
31747
  </section>`;
31875
31748
  }
@@ -31888,6 +31761,230 @@ function appendLegalLinks(legal, appearance) {
31888
31761
  }
31889
31762
  }
31890
31763
 
31764
+ // src/element-theme-resolution.ts
31765
+ function resolveAssistantTheme(input) {
31766
+ if (input.configured === "light" || input.configured === "dark") return input.configured;
31767
+ const hostMode = resolveHostTheme(input.host, input.media);
31768
+ return input.configured === "invert" ? opposite(hostMode) : hostMode;
31769
+ }
31770
+ function resolveHostTheme(host, media) {
31771
+ const view = host.ownerDocument.defaultView;
31772
+ for (let node = host.parentElement; node; node = node.parentElement) {
31773
+ const declared = declaredTheme(node);
31774
+ if (declared) return declared;
31775
+ const computed = computedTheme(view?.getComputedStyle(node).colorScheme);
31776
+ if (computed) return computed;
31777
+ }
31778
+ return media?.matches ? "dark" : "light";
31779
+ }
31780
+ function computedTheme(colorScheme) {
31781
+ const modes = colorScheme?.split(/\s+/) ?? [];
31782
+ if (modes.includes("dark") && !modes.includes("light")) return "dark";
31783
+ if (modes.includes("light") && !modes.includes("dark")) return "light";
31784
+ return void 0;
31785
+ }
31786
+ function declaredTheme(element) {
31787
+ const value = element.getAttribute("data-theme") ?? element.getAttribute("data-color-scheme") ?? element.getAttribute("data-mode");
31788
+ if (value === "dark" || value === "light") return value;
31789
+ if (element.classList.contains("dark")) return "dark";
31790
+ if (element.classList.contains("light")) return "light";
31791
+ return void 0;
31792
+ }
31793
+ function opposite(mode) {
31794
+ return mode === "dark" ? "light" : "dark";
31795
+ }
31796
+
31797
+ // src/element-host-environment-controller.ts
31798
+ var AssistantElementHostEnvironmentController = class {
31799
+ #host;
31800
+ #appearance;
31801
+ #applyTheme;
31802
+ #close;
31803
+ #media;
31804
+ #themeObserver;
31805
+ constructor(options) {
31806
+ this.#host = options.host;
31807
+ this.#appearance = options.appearance;
31808
+ this.#applyTheme = options.applyTheme;
31809
+ this.#close = options.close;
31810
+ }
31811
+ get media() {
31812
+ return this.#media;
31813
+ }
31814
+ resolvedTheme() {
31815
+ return resolveAssistantTheme({
31816
+ host: this.#host,
31817
+ configured: this.#host.getAttribute("theme") ?? this.#appearance().theme.defaultMode,
31818
+ ...this.#media ? { media: this.#media } : {}
31819
+ });
31820
+ }
31821
+ connect() {
31822
+ this.disconnect();
31823
+ this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
31824
+ this.#media?.addEventListener("change", this.#applyTheme);
31825
+ this.#themeObserver = new MutationObserver(this.#applyTheme);
31826
+ for (let node = this.#host.parentElement; node; node = node.parentElement) {
31827
+ this.#themeObserver.observe(node, {
31828
+ attributes: true,
31829
+ attributeFilter: ["class", "data-theme", "data-color-scheme", "data-mode", "style"]
31830
+ });
31831
+ }
31832
+ document.addEventListener("keydown", this.#handleDocumentKeydown);
31833
+ document.addEventListener("pointerdown", this.#handleDocumentPointerDown);
31834
+ }
31835
+ disconnect() {
31836
+ this.#media?.removeEventListener("change", this.#applyTheme);
31837
+ this.#media = void 0;
31838
+ this.#themeObserver?.disconnect();
31839
+ this.#themeObserver = void 0;
31840
+ document.removeEventListener("keydown", this.#handleDocumentKeydown);
31841
+ document.removeEventListener("pointerdown", this.#handleDocumentPointerDown);
31842
+ }
31843
+ #handleDocumentKeydown = (event) => {
31844
+ if (event.key === "Escape" && this.#host.hasAttribute("open") && this.#appearance().behavior.closeOnEscape) {
31845
+ this.#close();
31846
+ }
31847
+ };
31848
+ #handleDocumentPointerDown = (event) => {
31849
+ if (this.#host.hasAttribute("open") && this.#appearance().behavior.closeOnOutsideClick && !event.composedPath().includes(this.#host)) {
31850
+ this.#close();
31851
+ }
31852
+ };
31853
+ };
31854
+
31855
+ // src/element-launcher-controller.ts
31856
+ var AssistantElementLauncherController = class {
31857
+ #host;
31858
+ #appearance;
31859
+ #openPanel;
31860
+ #sendMessage;
31861
+ #root;
31862
+ constructor(options) {
31863
+ this.#host = options.host;
31864
+ this.#appearance = options.appearance;
31865
+ this.#openPanel = options.openPanel;
31866
+ this.#sendMessage = options.sendMessage;
31867
+ }
31868
+ connect(root) {
31869
+ this.disconnect();
31870
+ this.#root = root;
31871
+ queryRequired(root, ".launcher").addEventListener(
31872
+ "click",
31873
+ this.#handleLauncherClick
31874
+ );
31875
+ queryRequired(root, ".launcher-trigger").addEventListener(
31876
+ "click",
31877
+ this.#handleTrigger
31878
+ );
31879
+ queryRequired(root, ".launcher-form").addEventListener(
31880
+ "submit",
31881
+ this.#handleSubmit
31882
+ );
31883
+ queryRequired(root, ".launcher-input").addEventListener(
31884
+ "keydown",
31885
+ this.#handleInputKeydown
31886
+ );
31887
+ document.addEventListener("pointerdown", this.#handleDocumentPointerDown);
31888
+ this.sync();
31889
+ }
31890
+ disconnect() {
31891
+ document.removeEventListener("pointerdown", this.#handleDocumentPointerDown);
31892
+ this.#root = void 0;
31893
+ }
31894
+ sync() {
31895
+ const root = this.#root;
31896
+ if (!root) return;
31897
+ const appearance = this.#appearance();
31898
+ const style = appearance.presentation.launcher.style;
31899
+ this.#host.dataset.launcherStyle = style;
31900
+ const trigger = queryRequired(root, ".launcher-trigger");
31901
+ const label = queryRequired(root, ".launcher-label");
31902
+ const input = queryRequired(root, ".launcher-input");
31903
+ label.textContent = appearance.labels.launcherPlaceholder;
31904
+ input.placeholder = appearance.labels.launcherPlaceholder;
31905
+ input.setAttribute("aria-label", appearance.labels.launcherPlaceholder);
31906
+ trigger.setAttribute("aria-label", appearance.labels.open);
31907
+ queryRequired(root, ".launcher-send").setAttribute(
31908
+ "aria-label",
31909
+ appearance.labels.send
31910
+ );
31911
+ queryRequired(root, ".launcher").hidden = !appearance.behavior.showLauncher;
31912
+ queryRequired(root, ".powered-by-row").hidden = !appearance.behavior.showPoweredBy;
31913
+ if (style === "bubble" || this.#host.hasAttribute("open")) this.collapse(true);
31914
+ }
31915
+ collapse(force = false) {
31916
+ const root = this.#root;
31917
+ if (!root) return;
31918
+ const input = queryRequired(root, ".launcher-input");
31919
+ if (!force && input.value.trim()) return;
31920
+ this.#host.removeAttribute("launcher-expanded");
31921
+ const form = queryRequired(root, ".launcher-form");
31922
+ const trigger = queryRequired(root, ".launcher-trigger");
31923
+ form.setAttribute("aria-hidden", "true");
31924
+ trigger.setAttribute("aria-expanded", "false");
31925
+ input.tabIndex = -1;
31926
+ input.blur();
31927
+ }
31928
+ focusTrigger() {
31929
+ this.#root?.querySelector(".launcher-trigger")?.focus();
31930
+ }
31931
+ #expand() {
31932
+ const root = this.#root;
31933
+ if (!root || this.#appearance().presentation.launcher.style === "bubble") return;
31934
+ this.#host.setAttribute("launcher-expanded", "");
31935
+ const form = queryRequired(root, ".launcher-form");
31936
+ const trigger = queryRequired(root, ".launcher-trigger");
31937
+ const input = queryRequired(root, ".launcher-input");
31938
+ form.setAttribute("aria-hidden", "false");
31939
+ trigger.setAttribute("aria-expanded", "true");
31940
+ input.tabIndex = 0;
31941
+ requestAnimationFrame(() => {
31942
+ if (this.#host.hasAttribute("launcher-expanded")) input.focus();
31943
+ });
31944
+ }
31945
+ #handleTrigger = () => {
31946
+ if (this.#appearance().presentation.launcher.style === "bubble") {
31947
+ this.#openPanel();
31948
+ return;
31949
+ }
31950
+ this.#expand();
31951
+ };
31952
+ #handleLauncherClick = (event) => {
31953
+ if (event.composedPath().some(
31954
+ (target) => target instanceof Element && target.matches(".launcher-trigger, .launcher-form")
31955
+ )) {
31956
+ return;
31957
+ }
31958
+ this.#handleTrigger();
31959
+ };
31960
+ #handleSubmit = (event) => {
31961
+ event.preventDefault();
31962
+ const input = this.#root?.querySelector(".launcher-input");
31963
+ const message = input?.value.trim() ?? "";
31964
+ if (input) input.value = "";
31965
+ this.#openPanel();
31966
+ if (message) this.#sendMessage(message);
31967
+ };
31968
+ #handleInputKeydown = (event) => {
31969
+ if (event.key === "Enter" && !event.isComposing) {
31970
+ event.preventDefault();
31971
+ this.#root?.querySelector(".launcher-form")?.requestSubmit();
31972
+ return;
31973
+ }
31974
+ if (event.key !== "Escape") return;
31975
+ event.preventDefault();
31976
+ event.stopPropagation();
31977
+ const input = this.#root?.querySelector(".launcher-input");
31978
+ if (input) input.value = "";
31979
+ this.collapse(true);
31980
+ this.focusTrigger();
31981
+ };
31982
+ #handleDocumentPointerDown = (event) => {
31983
+ if (event.composedPath().includes(this.#host)) return;
31984
+ this.collapse();
31985
+ };
31986
+ };
31987
+
31891
31988
  // src/element-page-context-controller.ts
31892
31989
  var AssistantElementPageContextController = class {
31893
31990
  #snapshot;
@@ -31941,10 +32038,12 @@ var AssistantElementPresentationGate = class {
31941
32038
  syncOpenState() {
31942
32039
  const open = this.#host.hasAttribute("open");
31943
32040
  const presentationReady = this.#host.hasAttribute("data-presentation-ready");
31944
- const launcher = this.#host.shadowRoot?.querySelector(".launcher");
31945
- launcher?.setAttribute("aria-expanded", String(open && presentationReady));
31946
- launcher?.setAttribute("aria-busy", String(open && !presentationReady));
31947
- if (launcher) launcher.disabled = open && !presentationReady;
32041
+ const publicConfigurationLoading = this.#host.hasAttribute("data-public-configuration-loading");
32042
+ const presentationBlocked = !presentationReady && (open || publicConfigurationLoading);
32043
+ const launcher = this.#host.shadowRoot?.querySelector(".launcher-trigger");
32044
+ launcher?.setAttribute("aria-busy", String(presentationBlocked));
32045
+ if (launcher) launcher.disabled = presentationBlocked;
32046
+ this.#host.shadowRoot?.querySelector(".panel")?.setAttribute("aria-hidden", String(!(open && presentationReady)));
31948
32047
  if (open && this.#host.isConnected) this.#primeSession();
31949
32048
  }
31950
32049
  reveal() {
@@ -31955,6 +32054,233 @@ var AssistantElementPresentationGate = class {
31955
32054
  }
31956
32055
  };
31957
32056
 
32057
+ // src/assistant-configuration-schema.ts
32058
+ var hexColorSchema = external_exports.string().regex(/^#[0-9A-Fa-f]{6}$/);
32059
+ var browserAssetUrlSchema = external_exports.string().trim().url().refine(isSecureBrowserAssetUrl, "must use https (http is allowed only for loopback testing)");
32060
+ var httpsUrlSchema = external_exports.string().trim().url().refine(isHttpsUrl2, "must use https");
32061
+ var brandAssetSchema = external_exports.object({
32062
+ uri: browserAssetUrlSchema,
32063
+ darkUri: browserAssetUrlSchema.optional(),
32064
+ alt: external_exports.string().trim().min(1).max(240)
32065
+ }).strict();
32066
+ var brandThemeSchema = external_exports.object({
32067
+ surface: hexColorSchema.optional(),
32068
+ surfaceRaised: hexColorSchema.optional(),
32069
+ surfaceMuted: hexColorSchema.optional(),
32070
+ text: hexColorSchema.optional(),
32071
+ textMuted: hexColorSchema.optional(),
32072
+ accent: hexColorSchema.optional(),
32073
+ accentText: hexColorSchema.optional(),
32074
+ link: hexColorSchema.optional(),
32075
+ border: hexColorSchema.optional(),
32076
+ borderStrong: hexColorSchema.optional(),
32077
+ focus: hexColorSchema.optional(),
32078
+ success: hexColorSchema.optional(),
32079
+ warning: hexColorSchema.optional(),
32080
+ danger: hexColorSchema.optional(),
32081
+ code: hexColorSchema.optional()
32082
+ }).strict();
32083
+ var brandingSchema = external_exports.object({
32084
+ name: external_exports.string().trim().min(1).max(160).optional(),
32085
+ accent: hexColorSchema.optional(),
32086
+ surface: hexColorSchema.optional(),
32087
+ surfaceDark: hexColorSchema.optional(),
32088
+ logo: brandAssetSchema.optional(),
32089
+ mark: brandAssetSchema.optional(),
32090
+ avatar: brandAssetSchema.optional(),
32091
+ theme: external_exports.object({ light: brandThemeSchema.optional(), dark: brandThemeSchema.optional() }).strict().optional(),
32092
+ radius: external_exports.enum(["none", "sm", "md", "lg"]).optional(),
32093
+ density: external_exports.enum(["compact", "comfortable"]).optional(),
32094
+ typography: external_exports.enum(["system", "serif", "mono"]).optional(),
32095
+ colorScheme: external_exports.enum(["auto", "light", "dark"]).optional()
32096
+ }).strict();
32097
+ var labelsSchema = external_exports.object({
32098
+ welcomeHeading: external_exports.string().trim().min(1).max(160).optional(),
32099
+ welcomeMessage: external_exports.string().max(1e3).optional(),
32100
+ launcherPlaceholder: external_exports.string().trim().min(1).max(160).optional(),
32101
+ composerPlaceholder: external_exports.string().trim().min(1).max(160).optional(),
32102
+ thinking: external_exports.string().trim().min(1).max(80).optional(),
32103
+ send: external_exports.string().trim().min(1).max(80).optional(),
32104
+ stop: external_exports.string().trim().min(1).max(80).optional(),
32105
+ close: external_exports.string().trim().min(1).max(80).optional(),
32106
+ open: external_exports.string().trim().min(1).max(80).optional(),
32107
+ confirm: external_exports.string().trim().min(1).max(80).optional(),
32108
+ decline: external_exports.string().trim().min(1).max(80).optional(),
32109
+ cancel: external_exports.string().trim().min(1).max(80).optional(),
32110
+ confirmationHeading: external_exports.string().trim().min(1).max(120).optional(),
32111
+ additionalDetails: external_exports.string().trim().min(1).max(120).optional(),
32112
+ redacted: external_exports.string().trim().min(1).max(120).optional(),
32113
+ completed: external_exports.string().trim().min(1).max(80).optional(),
32114
+ stopped: external_exports.string().trim().min(1).max(80).optional(),
32115
+ copy: external_exports.string().trim().min(1).max(80).optional(),
32116
+ newMessages: external_exports.string().trim().min(1).max(80).optional(),
32117
+ reconnect: external_exports.string().trim().min(1).max(80).optional(),
32118
+ newConversation: external_exports.string().trim().min(1).max(80).optional(),
32119
+ retry: external_exports.string().trim().min(1).max(80).optional(),
32120
+ unavailable: external_exports.string().trim().min(1).max(240).optional(),
32121
+ sessionExpired: external_exports.string().trim().min(1).max(240).optional(),
32122
+ sessionIdle: external_exports.string().trim().min(1).max(120).optional(),
32123
+ sessionLoading: external_exports.string().trim().min(1).max(120).optional(),
32124
+ sessionReady: external_exports.string().trim().min(1).max(120).optional(),
32125
+ sessionError: external_exports.string().trim().min(1).max(120).optional(),
32126
+ signInHeading: external_exports.string().trim().min(1).max(120).optional(),
32127
+ signInBody: external_exports.string().max(240).optional(),
32128
+ signInAction: external_exports.string().trim().min(1).max(80).optional(),
32129
+ signUpAction: external_exports.string().trim().min(1).max(80).optional()
32130
+ }).strict();
32131
+ var presentationSchema = external_exports.object({
32132
+ panel: external_exports.object({
32133
+ surface: external_exports.enum(["solid", "glass"]).optional(),
32134
+ elevation: external_exports.enum(["soft", "dramatic"]).optional(),
32135
+ border: external_exports.enum(["subtle", "strong"]).optional(),
32136
+ radius: external_exports.number().int().min(0).max(64).optional()
32137
+ }).strict().optional(),
32138
+ launcher: external_exports.object({
32139
+ style: external_exports.enum(["pill", "bubble"]).optional(),
32140
+ icon: external_exports.enum(["brand-mark", "chat", "none"]).optional(),
32141
+ size: external_exports.enum(["md", "lg"]).optional(),
32142
+ status: external_exports.enum(["none", "session"]).optional(),
32143
+ effect: external_exports.enum(["none", "pulse"]).optional()
32144
+ }).strict().optional(),
32145
+ header: external_exports.object({
32146
+ mark: external_exports.enum(["none", "brand-mark", "status"]).optional(),
32147
+ badge: external_exports.object({
32148
+ text: external_exports.string().trim().min(1).max(80),
32149
+ tone: external_exports.enum(["neutral", "success", "warning", "danger"]).optional(),
32150
+ indicator: external_exports.boolean().optional()
32151
+ }).strict().optional()
32152
+ }).strict().optional(),
32153
+ composer: external_exports.object({
32154
+ leadingIcon: external_exports.enum(["none", "brand-mark"]).optional(),
32155
+ sendIcon: external_exports.enum(["arrow-up", "paper-plane"]).optional(),
32156
+ shape: external_exports.enum(["rounded", "pill"]).optional()
32157
+ }).strict().optional(),
32158
+ messages: external_exports.object({
32159
+ userStyle: external_exports.enum(["bubble", "accent"]).optional(),
32160
+ assistantStyle: external_exports.enum(["plain", "bubble"]).optional()
32161
+ }).strict().optional()
32162
+ }).strict();
32163
+ var assistantUiSchema = external_exports.object({
32164
+ theme: external_exports.enum(["auto", "light", "dark", "invert"]).optional(),
32165
+ layout: external_exports.object({
32166
+ mode: external_exports.enum(["floating", "inline", "drawer"]).optional(),
32167
+ position: external_exports.enum(["bottom-left", "bottom-center", "bottom-right"]).optional(),
32168
+ panelWidth: external_exports.number().int().min(280).max(1200).optional(),
32169
+ panelMinHeight: external_exports.number().int().min(240).max(1600).optional(),
32170
+ panelMaxHeight: external_exports.number().int().min(320).max(2400).optional(),
32171
+ edgeOffset: external_exports.number().int().min(0).max(96).optional(),
32172
+ zIndex: external_exports.number().int().min(0).max(2147483647).optional(),
32173
+ density: external_exports.enum(["compact", "comfortable"]).optional(),
32174
+ mobileFullscreen: external_exports.boolean().optional()
32175
+ }).strict().optional(),
32176
+ behavior: external_exports.object({
32177
+ startOpen: external_exports.boolean().optional(),
32178
+ closeOnEscape: external_exports.boolean().optional(),
32179
+ closeOnOutsideClick: external_exports.boolean().optional(),
32180
+ showLauncher: external_exports.boolean().optional(),
32181
+ showHeader: external_exports.boolean().optional(),
32182
+ showAvatars: external_exports.boolean().optional(),
32183
+ showTimestamps: external_exports.boolean().optional(),
32184
+ showPoweredBy: external_exports.boolean().optional()
32185
+ }).strict().optional(),
32186
+ labels: labelsSchema.optional(),
32187
+ presentation: presentationSchema.optional(),
32188
+ suggestedPrompts: external_exports.array(external_exports.string().trim().min(1).max(240)).max(8).optional(),
32189
+ privacyUrl: httpsUrlSchema.optional(),
32190
+ termsUrl: httpsUrlSchema.optional(),
32191
+ locale: external_exports.string().trim().min(2).max(35).optional(),
32192
+ direction: external_exports.enum(["ltr", "rtl", "auto"]).optional()
32193
+ }).strict();
32194
+ var assistantConfigurationSchema = external_exports.object({ branding: brandingSchema.optional(), assistant: assistantUiSchema.optional() }).strict();
32195
+ function parseAssistantConfiguration(value) {
32196
+ const parsed = assistantConfigurationSchema.safeParse(value);
32197
+ return parsed.success ? parsed.data : void 0;
32198
+ }
32199
+ function isHttpsUrl2(value) {
32200
+ try {
32201
+ return new URL(value).protocol === "https:";
32202
+ } catch {
32203
+ return false;
32204
+ }
32205
+ }
32206
+ function isSecureBrowserAssetUrl(value) {
32207
+ try {
32208
+ const url2 = new URL(value);
32209
+ if (url2.protocol === "https:") return true;
32210
+ if (url2.protocol !== "http:") return false;
32211
+ return url2.hostname === "localhost" || url2.hostname === "127.0.0.1" || url2.hostname === "[::1]";
32212
+ } catch {
32213
+ return false;
32214
+ }
32215
+ }
32216
+
32217
+ // src/element-public-configuration-controller.ts
32218
+ var AssistantElementPublicConfigurationController = class {
32219
+ #source;
32220
+ #apply;
32221
+ #loadingChanged;
32222
+ #generation = 0;
32223
+ #bootstrap;
32224
+ #abort;
32225
+ constructor(source, apply2, loadingChanged) {
32226
+ this.#source = source;
32227
+ this.#apply = apply2;
32228
+ this.#loadingChanged = loadingChanged;
32229
+ }
32230
+ connect() {
32231
+ this.#restart();
32232
+ }
32233
+ refresh() {
32234
+ this.#restart();
32235
+ }
32236
+ disconnect() {
32237
+ this.#generation += 1;
32238
+ this.#abort?.abort();
32239
+ this.#abort = void 0;
32240
+ this.#bootstrap = void 0;
32241
+ }
32242
+ #restart() {
32243
+ this.disconnect();
32244
+ this.#prime();
32245
+ }
32246
+ #prime() {
32247
+ const source = this.#source();
32248
+ if (!source.embedId || this.#bootstrap) return;
32249
+ const generation = this.#generation;
32250
+ const abort = new AbortController();
32251
+ this.#abort = abort;
32252
+ const fetcher = source.fetch ?? ((input, init) => globalThis.fetch(input, init));
32253
+ this.#loadingChanged(true);
32254
+ const bootstrap = Promise.resolve().then(
32255
+ () => fetcher(publicConfigurationUrl(source.serviceUrl, source.embedId), {
32256
+ method: "GET",
32257
+ headers: { accept: "application/json" },
32258
+ credentials: "omit",
32259
+ cache: "no-cache",
32260
+ signal: abort.signal
32261
+ })
32262
+ ).then(async (response) => {
32263
+ if (!response.ok) return;
32264
+ const value = await response.json();
32265
+ if (generation !== this.#generation || !isRecord6(value)) {
32266
+ return;
32267
+ }
32268
+ const configuration = parseAssistantConfiguration(value.configuration);
32269
+ if (configuration !== void 0) this.#apply(configuration);
32270
+ }).catch(() => void 0).finally(() => {
32271
+ if (generation === this.#generation) this.#loadingChanged(false);
32272
+ if (this.#bootstrap === bootstrap) {
32273
+ this.#bootstrap = void 0;
32274
+ this.#abort = void 0;
32275
+ }
32276
+ });
32277
+ this.#bootstrap = bootstrap;
32278
+ }
32279
+ };
32280
+ function isRecord6(value) {
32281
+ return typeof value === "object" && value !== null && !Array.isArray(value);
32282
+ }
32283
+
31958
32284
  // src/element-scroll-controller.ts
31959
32285
  var AssistantElementScrollController = class {
31960
32286
  #region;
@@ -32084,44 +32410,69 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
32084
32410
  *, *::before, *::after { box-sizing: border-box; }
32085
32411
  .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; }
32086
32412
  .visually-hidden[hidden] { display: none !important; }
32087
- :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; }
32088
- :host([data-mode="floating"]) { position: fixed; bottom: 20px; right: 20px; }
32089
- :host([data-mode="floating"][data-position="bottom-left"]) { right: auto; left: 20px; }
32413
+ :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; }
32414
+ :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; }
32415
+ :host([data-mode="floating"]) { position: fixed; }
32416
+ :host([data-mode="floating"][open]) { width: min(var(--ns-assistant-panel-width, var(--ns-assistant-default-panel-width)), 100vw); }
32090
32417
  button { font: inherit; }
32091
32418
  button, textarea, input, select { -webkit-tap-highlight-color: transparent; }
32092
- .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; }
32093
- .launcher:hover { transform: translateY(-2px) scale(1.03); box-shadow: 0 8px 26px rgba(0,0,0,.16); }
32094
- .launcher img { width: 28px; height: 28px; border-radius: 50%; object-fit: contain; }
32095
- .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; }
32419
+ .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; }
32420
+ :host([data-presentation-ready]) .launcher { padding: 10px 12px; }
32421
+ .launcher[hidden] { display: none; }
32422
+ .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); }
32423
+ .launcher-trigger, .launcher-form { min-width: 0; margin: 0; padding: 0; border: 0; background: transparent; font: inherit; }
32424
+ .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; }
32425
+ .launcher-label { padding-left: 6px; white-space: nowrap; font-size: 15px; line-height: 20px; }
32426
+ .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; }
32427
+ .launcher-arrow:hover, .launcher-send:hover { background: var(--ns-assistant-launcher-action-hover, var(--ns-assistant-halo-launcher-action-hover)); }
32428
+ .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; }
32429
+ .launcher-form { display: none; flex: 1; align-items: center; gap: 12px; opacity: 0; pointer-events: none; }
32430
+ .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; }
32431
+ .launcher-input::placeholder { color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); }
32432
+ :host([launcher-expanded]) .launcher { width: min(400px, calc(100vw - 40px)); justify-content: flex-start; }
32433
+ :host([launcher-expanded]) .launcher-trigger { display: none; }
32434
+ :host([launcher-expanded]) .launcher-form { display: flex; opacity: 1; pointer-events: auto; }
32435
+ :host([data-launcher-style="bubble"]) .launcher { gap: 0; min-height: 0; padding: 6px; cursor: pointer; }
32436
+ :host([data-launcher-style="bubble"]) .launcher-trigger { position: absolute; inset: 0; display: block; width: 100%; height: 100%; cursor: pointer; }
32437
+ :host([data-launcher-style="bubble"]) .launcher-label, :host([data-launcher-style="bubble"]) .launcher-arrow, :host([data-launcher-style="bubble"]) .launcher-form { display: none; }
32438
+ :host([data-launcher-style="bubble"]) .launcher-visual { width: 32px; height: 32px; flex-basis: 32px; }
32439
+ .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; }
32440
+ :host([data-session-state="loading"]:not([data-presentation-ready])) .launcher-loader, :host([data-public-configuration-loading]:not([data-presentation-ready])) .launcher-loader { display: block; }
32441
+ :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; }
32442
+ .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; }
32443
+ :host([data-mode="floating"][open]) .panel { width: calc(100% - 40px); margin-inline: 20px; }
32096
32444
  :host([open][data-presentation-ready]) .panel { display: flex; }
32097
32445
  .panel { position: relative; }
32098
32446
  :host([open][data-presentation-ready]) .launcher { display: none; }
32099
32447
  :host([open]:not([data-presentation-ready])) .launcher { cursor: progress; opacity: .72; transform: none; }
32100
- 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); }
32448
+ 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))); }
32101
32449
  header strong { flex: 1; font-size: 16px; font-weight: 600; letter-spacing: 0; }
32102
32450
  .brand-logo { display: block; max-width: 120px; max-height: 28px; object-fit: contain; }
32103
32451
  .close, .send { display: grid; place-items: center; border: 0; border-radius: var(--ns-assistant-button-radius, var(--ns-assistant-default-button-radius)); cursor: pointer; }
32104
- .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; }
32105
- .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; }
32452
+ .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; }
32453
+ .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; }
32106
32454
  .close:hover { color: var(--ns-assistant-text, var(--ns-assistant-default-text)); background: var(--ns-assistant-elevated, var(--ns-assistant-default-elevated)); }
32107
32455
  .close:active { transform: scale(.96); }
32108
- .welcome { flex: 0 0 auto; padding: 30px 22px 18px; }
32456
+ .welcome { flex: 0 0 auto; padding: 24px 20px 8px; }
32109
32457
  .welcome h2 { margin: 0; font-size: 1.35em; line-height: 1.25; letter-spacing: 0; }
32110
32458
  .welcome p { max-width: 42ch; margin: 7px 0 0; color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); }
32111
- :host([has-messages]) .welcome, :host([has-messages]) .suggested-prompts { display: none; }
32112
- .suggested-prompts { display: flex; flex: 0 0 auto; flex-wrap: wrap; gap: 8px; padding: 0 20px 16px; }
32459
+ .welcome:has(.empty-state[hidden]) { display: none; }
32460
+ :host([has-messages]) .welcome { display: none; }
32461
+ .input-section { flex: 0 0 auto; padding: 8px 16px 16px; }
32462
+ .suggested-prompts { display: flex; flex: 0 0 auto; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
32113
32463
  .suggested-prompts:empty { display: none; }
32114
- .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; }
32115
- .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); }
32464
+ .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; }
32465
+ .suggested-prompts button::before { content: ''; position: absolute; inset: -6px -2px; }
32466
+ .suggested-prompts button:hover { color: var(--ns-assistant-text, var(--ns-assistant-default-text)); background: var(--ns-assistant-elevated, var(--ns-assistant-default-elevated)); }
32116
32467
  .messages-region { position: relative; display: flex; flex: 1 1 auto; min-height: 0; overflow: hidden; }
32117
32468
  .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; }
32118
32469
  .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); }
32119
32470
  .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); }
32120
32471
  .messages-region[data-can-scroll-up]::before, .messages-region[data-can-scroll-down]::after { opacity: 1; }
32121
- .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; }
32472
+ .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; }
32122
32473
  .messages::-webkit-scrollbar { width: 6px; }
32123
32474
  .messages::-webkit-scrollbar-thumb { background: var(--ns-assistant-divider, var(--ns-assistant-default-divider)); border-radius: 999px; }
32124
- .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; }
32475
+ .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; }
32125
32476
  .message > span, .message > .markdown { overflow-wrap: anywhere; }
32126
32477
  .message .avatar { float: left; width: 24px; height: 24px; margin-right: 9px; border-radius: 50%; object-fit: cover; }
32127
32478
  .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; }
@@ -32134,7 +32485,7 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
32134
32485
  .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; }
32135
32486
  .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; }
32136
32487
  .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); }
32137
- .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; }
32488
+ .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; }
32138
32489
  .message.user time { margin-top: 0; }
32139
32490
  .markdown { white-space: normal; }
32140
32491
  .markdown p { margin: 0 0 8px; }
@@ -32184,31 +32535,39 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
32184
32535
  .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)); }
32185
32536
  .input-request .proposal-actions { margin-top: 2px; }
32186
32537
  ${ASSISTANT_APP_VIEW_CSS}
32187
- 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; }
32188
- 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)); }
32538
+ .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; }
32539
+ .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); }
32189
32540
  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; }
32190
32541
  textarea::placeholder { color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); }
32191
32542
  textarea:focus, textarea:focus-visible { outline: none; box-shadow: none; }
32192
32543
  button:focus-visible { outline: 3px solid var(--ns-assistant-focus, var(--ns-assistant-default-focus)); outline-offset: 2px; }
32193
- .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; }
32194
- .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; }
32195
- .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%)); }
32544
+ .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; }
32545
+ .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%); }
32546
+ .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; }
32547
+ .send:hover::after { background: var(--ns-assistant-hover, var(--ns-assistant-default-hover)); }
32196
32548
  :host([busy]) .send::before { width: 13px; height: 13px; border-radius: 3px; -webkit-mask: none; mask: none; }
32197
32549
  .send:disabled, textarea:disabled { opacity: .55; cursor: not-allowed; }
32198
- .legal { display: flex; gap: 12px; justify-content: center; padding: 0 12px 9px; font-size: .75em; }
32550
+ .legal { display: flex; gap: 12px; justify-content: center; padding: 0 12px 8px; font-size: .75em; }
32199
32551
  .legal:empty { display: none; }
32200
32552
  .legal a { color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); text-underline-offset: 2px; }
32553
+ .powered-by-row { display: flex; flex: 0 0 auto; justify-content: center; padding-bottom: 12px; }
32554
+ .powered-by-row[hidden] { display: none; }
32555
+ .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; }
32556
+ .powered-by:hover { color: var(--ns-assistant-text, var(--ns-assistant-default-text)); opacity: 1; }
32557
+ .powered-by-icon { display: inline-flex; width: 14px; height: 14px; }
32558
+ .powered-by-name { font-weight: 500; }
32201
32559
  ::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; }
32202
- @keyframes halo-panel-in { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }
32560
+ @keyframes halo-panel-in { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: none; } }
32203
32561
  @keyframes halo-message-in { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
32204
32562
  @keyframes halo-shimmer { from { background-position: 100% center; } to { background-position: 0 center; } }
32205
32563
  @keyframes halo-caret-blink { 50% { opacity: 0; } }
32564
+ @keyframes halo-spin { to { transform: rotate(360deg); } }
32206
32565
  :host([data-density="compact"]) .welcome { padding: 20px 18px 12px; }
32207
- @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)); } }
32566
+ @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)); } }
32208
32567
  @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; } }
32209
32568
  @media (forced-colors: active) { .message.thinking > span { color: CanvasText; background: none; } }
32210
32569
  @media (hover: none) { .message-copy { opacity: 1; } }
32211
- @media (max-width: 560px) { :host([data-mode="floating"]) { inset: auto 12px 12px; } :host([open]) .panel { width: calc(100vw - 24px); max-height: calc(100vh - 24px); } }
32570
+ @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); } }
32212
32571
  </style>`;
32213
32572
 
32214
32573
  // src/host-appearance.ts
@@ -32317,6 +32676,7 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
32317
32676
  host.dataset.panelSurface = presentation.panel.surface;
32318
32677
  host.dataset.panelElevation = presentation.panel.elevation;
32319
32678
  host.dataset.panelBorder = presentation.panel.border;
32679
+ host.dataset.launcherStyle = presentation.launcher.style;
32320
32680
  host.dataset.launcherEffect = presentation.launcher.effect;
32321
32681
  host.dataset.messageUserStyle = presentation.messages.userStyle;
32322
32682
  host.dataset.messageAssistantStyle = presentation.messages.assistantStyle;
@@ -32329,11 +32689,11 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
32329
32689
  }
32330
32690
  host.style.setProperty(
32331
32691
  "--ns-assistant-presentation-launcher-size",
32332
- presentation.launcher.size === "lg" ? "56px" : "48px"
32692
+ presentation.launcher.size === "lg" ? "56px" : "44px"
32333
32693
  );
32334
32694
  host.style.setProperty(
32335
32695
  "--ns-assistant-presentation-panel-background",
32336
- 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)"
32696
+ 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)"
32337
32697
  );
32338
32698
  host.style.setProperty(
32339
32699
  "--ns-assistant-presentation-shadow",
@@ -32341,7 +32701,7 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
32341
32701
  );
32342
32702
  host.style.setProperty(
32343
32703
  "--ns-assistant-presentation-border",
32344
- panelBorder(presentation.panel.border)
32704
+ panelBorder(presentation.panel.border, mode)
32345
32705
  );
32346
32706
  replaceFallback(root, "launcher-icon", [launcherVisual(appearance, mode, sessionState)]);
32347
32707
  const accessibleStatus = root.querySelector("[data-session-status]");
@@ -32354,7 +32714,7 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
32354
32714
  replaceFallback(root, "empty-state", [emptyState(appearance)]);
32355
32715
  replaceFallback(root, "composer-leading", composerLeading(appearance, mode));
32356
32716
  replaceFallback(root, "conversation-footer", []);
32357
- const form = root.querySelector("form");
32717
+ const form = root.querySelector(".composer");
32358
32718
  if (form) form.dataset.shape = presentation.composer.shape;
32359
32719
  const send = root.querySelector(".send");
32360
32720
  if (send) send.dataset.icon = presentation.composer.sendIcon;
@@ -32374,18 +32734,21 @@ function panelShadow(elevation) {
32374
32734
  }
32375
32735
  return "var(--ns-assistant-default-shadow)";
32376
32736
  }
32377
- function panelBorder(border) {
32737
+ function panelBorder(border, mode) {
32378
32738
  if (border === "strong") {
32379
32739
  return "1px solid var(--ns-assistant-divider, var(--ns-assistant-default-divider))";
32380
32740
  }
32381
- return "1px solid color-mix(in srgb, var(--ns-assistant-divider, var(--ns-assistant-default-divider)) 74%, transparent)";
32741
+ const opacity = mode === "light" ? 60 : 100;
32742
+ return `1px solid color-mix(in srgb, var(--ns-assistant-divider, var(--ns-assistant-default-divider)) ${opacity}%, transparent)`;
32382
32743
  }
32383
32744
  function launcherVisual(appearance, mode, sessionState) {
32384
32745
  const launcher = appearance.presentation.launcher;
32385
32746
  const visual = document.createElement("span");
32386
32747
  visual.className = "launcher-visual";
32387
32748
  visual.setAttribute("aria-hidden", "true");
32388
- visual.append(icon(launcher.icon, appearance, mode, "launcher-glyph"));
32749
+ const visualKind = launcher.icon === "brand-mark" && !(appearance.brand.mark[mode] ?? appearance.brand.logo[mode]) ? launcher.style === "bubble" ? "chat" : "none" : launcher.icon;
32750
+ visual.hidden = visualKind === "none";
32751
+ visual.append(icon(visualKind, appearance, mode, "launcher-glyph"));
32389
32752
  if (launcher.status === "session") {
32390
32753
  const status = document.createElement("span");
32391
32754
  status.className = "launcher-status";
@@ -32435,9 +32798,12 @@ function headerBadge(appearance) {
32435
32798
  function emptyState(appearance) {
32436
32799
  const section = document.createElement("section");
32437
32800
  section.className = "empty-state";
32438
- const heading = document.createElement("h2");
32439
- heading.textContent = appearance.labels.welcomeHeading;
32440
- section.append(heading);
32801
+ section.hidden = !appearance.labels.welcomeHeading && !appearance.labels.welcomeMessage;
32802
+ if (appearance.labels.welcomeHeading) {
32803
+ const heading = document.createElement("h2");
32804
+ heading.textContent = appearance.labels.welcomeHeading;
32805
+ section.append(heading);
32806
+ }
32441
32807
  const description = appearance.labels.welcomeMessage;
32442
32808
  if (description) {
32443
32809
  const paragraph = document.createElement("p");
@@ -32464,7 +32830,7 @@ function toneIndicator(tone) {
32464
32830
  return indicator;
32465
32831
  }
32466
32832
  function icon(kind, appearance, mode, className) {
32467
- const mark = appearance.brand.mark[mode];
32833
+ const mark = appearance.brand.mark[mode] ?? appearance.brand.logo[mode];
32468
32834
  if (kind === "brand-mark" && mark) {
32469
32835
  const image = document.createElement("img");
32470
32836
  image.className = className;
@@ -32516,11 +32882,17 @@ function applyAssistantElementTheme(input) {
32516
32882
  var presentationStyles = `<style>
32517
32883
  :host([data-mode="floating"]) {
32518
32884
  bottom: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
32519
- right: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
32885
+ left: 50%;
32886
+ transform: translateX(-50%);
32520
32887
  }
32521
32888
  :host([data-mode="floating"][data-position="bottom-left"]) {
32522
- right: auto;
32523
32889
  left: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
32890
+ transform: none;
32891
+ }
32892
+ :host([data-mode="floating"][data-position="bottom-right"]) {
32893
+ right: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
32894
+ left: auto;
32895
+ transform: none;
32524
32896
  }
32525
32897
  .panel {
32526
32898
  background: var(--ns-assistant-panel-background, var(--ns-assistant-presentation-panel-background));
@@ -32537,22 +32909,26 @@ var presentationStyles = `<style>
32537
32909
 
32538
32910
  .launcher {
32539
32911
  position: relative;
32540
- width: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
32541
- height: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
32542
32912
  border-radius: var(--ns-assistant-launcher-radius, var(--ns-assistant-default-launcher-radius));
32543
32913
  isolation: isolate;
32544
32914
  }
32915
+ :host([data-launcher-style="bubble"]) .launcher {
32916
+ width: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
32917
+ height: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
32918
+ }
32545
32919
  :host([data-launcher-effect="pulse"]) .launcher {
32546
32920
  animation: assistant-launcher-pulse 2s ease-in-out infinite;
32547
32921
  }
32548
32922
  .launcher-visual {
32549
32923
  position: relative;
32550
32924
  display: grid;
32551
- width: 100%;
32552
- height: 100%;
32925
+ width: 28px;
32926
+ height: 28px;
32927
+ flex: 0 0 28px;
32553
32928
  place-items: center;
32554
32929
  border-radius: inherit;
32555
32930
  }
32931
+ .launcher-visual[hidden] { display: none; }
32556
32932
  .launcher-glyph, .composer-leading-glyph {
32557
32933
  display: block;
32558
32934
  width: 18px;
@@ -32580,10 +32956,28 @@ var presentationStyles = `<style>
32580
32956
  -webkit-mask: none;
32581
32957
  mask: none;
32582
32958
  }
32959
+ .launcher-visual .launcher-glyph:is(img) {
32960
+ width: 28px;
32961
+ height: 28px;
32962
+ border-radius: 50%;
32963
+ filter: grayscale(100%);
32964
+ opacity: .6;
32965
+ 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);
32966
+ }
32967
+ .launcher:hover .launcher-glyph:is(img), :host([launcher-expanded]) .launcher-glyph:is(img) {
32968
+ filter: none;
32969
+ opacity: 1;
32970
+ }
32971
+ :host([data-launcher-style="bubble"]) .launcher-glyph:is(img) {
32972
+ width: 32px;
32973
+ height: 32px;
32974
+ filter: none;
32975
+ opacity: 1;
32976
+ }
32583
32977
  .launcher-status {
32584
32978
  position: absolute;
32585
- right: 1px;
32586
- bottom: 1px;
32979
+ right: -1px;
32980
+ bottom: -1px;
32587
32981
  width: 10px;
32588
32982
  height: 10px;
32589
32983
  border: 2px solid var(--ns-assistant-panel, var(--ns-assistant-default-panel));
@@ -32712,7 +33106,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32712
33106
  #conversationGeneration = 0;
32713
33107
  #messages;
32714
33108
  #thinking;
32715
- #media;
32716
33109
  #context;
32717
33110
  #modelContext;
32718
33111
  #pageContext;
@@ -32724,11 +33117,17 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32724
33117
  () => this.#primeSession(),
32725
33118
  () => this.focusComposer()
32726
33119
  );
33120
+ #publicConfiguration = new AssistantElementPublicConfigurationController(
33121
+ () => ({ embedId: this.embedId, serviceUrl: this.serviceUrl, fetch: this.#fetch }),
33122
+ (configuration) => this.#applyConfiguration(configuration),
33123
+ (loading) => this.#setPublicConfigurationLoading(loading)
33124
+ );
32727
33125
  #pendingUserEcho;
32728
33126
  #sessionState = "idle";
32729
33127
  #startOpenApplied = false;
32730
33128
  #readyDispatched = false;
32731
33129
  #turnInFlight = false;
33130
+ #fetch;
32732
33131
  #hostAppearance;
32733
33132
  #appearanceWarningKeys = /* @__PURE__ */ new Set();
32734
33133
  #appearanceStyleSnapshot = /* @__PURE__ */ new Map();
@@ -32738,6 +33137,17 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32738
33137
  embedId = "";
32739
33138
  /** Only for a dev or self-hosted service; the published snippet needs no origin. */
32740
33139
  serviceUrl = "";
33140
+ get fetch() {
33141
+ return this.#fetch;
33142
+ }
33143
+ set fetch(value) {
33144
+ if (this.#fetch === value) return;
33145
+ this.#fetch = value;
33146
+ if (this.#client) this.resetSession();
33147
+ if (!this.isConnected) return;
33148
+ if (this.embedId) this.#publicConfiguration.refresh();
33149
+ else this.#primeSession();
33150
+ }
32741
33151
  get appearance() {
32742
33152
  return this.#hostAppearance;
32743
33153
  }
@@ -32756,11 +33166,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32756
33166
  }
32757
33167
  return this.#appendMessage(role, text3);
32758
33168
  },
32759
- applyConfiguration: (configuration) => {
32760
- this.#appearance = resolveAppearance(configuration);
32761
- this.#syncAppearance();
32762
- this.#applyStartOpenPolicy();
32763
- },
33169
+ applyConfiguration: (configuration) => this.#applyConfiguration(configuration),
32764
33170
  labels: () => this.#appearance.labels,
32765
33171
  respond: (id, response) => this.respond(id, response),
32766
33172
  dispatchError: (detail) => this.#dispatchError(detail),
@@ -32776,7 +33182,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32776
33182
  client,
32777
33183
  sendMessage: (message) => this.sendMessage(message),
32778
33184
  updateModelContext: (update) => this.updateModelContext(update),
32779
- theme: this.#resolvedMode()
33185
+ theme: this.#environment.resolvedTheme()
32780
33186
  },
32781
33187
  {
32782
33188
  ...sandboxUrl === void 0 ? {} : { sandboxUrl },
@@ -32789,18 +33195,28 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32789
33195
  this.#revealLatest();
32790
33196
  }
32791
33197
  });
33198
+ #launcher = new AssistantElementLauncherController({
33199
+ host: this,
33200
+ appearance: () => this.#appearance,
33201
+ openPanel: () => this.open(),
33202
+ sendMessage: (message) => this.#sendGuarded(message)
33203
+ });
33204
+ #environment = new AssistantElementHostEnvironmentController({
33205
+ host: this,
33206
+ appearance: () => this.#appearance,
33207
+ applyTheme: () => this.#applyTheme(),
33208
+ close: () => this.#closeAndRestoreFocus()
33209
+ });
32792
33210
  connectedCallback() {
32793
33211
  if (!this.shadowRoot) this.attachShadow({ mode: "open" });
32794
33212
  if (!this.sessionEndpoint) this.sessionEndpoint = this.getAttribute("session-endpoint") ?? "";
32795
33213
  if (!this.embedId) this.embedId = this.getAttribute("embed-id") ?? "";
32796
33214
  if (!this.serviceUrl) this.serviceUrl = this.getAttribute("service-url") ?? "";
32797
33215
  if (!this.sessionEndpoint && !this.embedId) this.#presentationGate.reveal();
32798
- this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
32799
- this.#media?.addEventListener("change", this.#handleSystemTheme);
32800
- document.addEventListener("keydown", this.#handleDocumentKeydown);
32801
- document.addEventListener("pointerdown", this.#handleDocumentPointerDown);
33216
+ this.#environment.connect();
32802
33217
  if (this.#appearance.behavior.startOpen) this.#applyStartOpenPolicy();
32803
33218
  this.#render();
33219
+ if (this.embedId) this.#publicConfiguration.connect();
32804
33220
  if (!this.#readyDispatched) {
32805
33221
  this.#readyDispatched = true;
32806
33222
  this.dispatchEvent(new CustomEvent("assistant-ready"));
@@ -32821,22 +33237,25 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32821
33237
  this.#clientEndpoint = "";
32822
33238
  this.#unsubscribeClient = void 0;
32823
33239
  this.#sessionBootstrap = void 0;
33240
+ this.#publicConfiguration.disconnect();
32824
33241
  this.#scroll.disconnect();
33242
+ this.#launcher.disconnect();
32825
33243
  this.#setBusy(false);
32826
- this.#media?.removeEventListener("change", this.#handleSystemTheme);
32827
- document.removeEventListener("keydown", this.#handleDocumentKeydown);
32828
- document.removeEventListener("pointerdown", this.#handleDocumentPointerDown);
33244
+ this.#environment.disconnect();
32829
33245
  }
32830
33246
  attributeChangedCallback(name21) {
32831
33247
  if (name21 === "theme") this.#applyTheme();
32832
33248
  if (name21 === "open") this.#presentationGate.syncOpenState();
32833
33249
  }
32834
33250
  open() {
33251
+ this.#launcher.collapse(true);
32835
33252
  this.setAttribute("open", "");
33253
+ if (this.hasAttribute("data-presentation-ready")) queueMicrotask(() => this.focusComposer());
32836
33254
  this.dispatchEvent(new CustomEvent("assistant-open"));
32837
33255
  }
32838
33256
  close() {
32839
33257
  this.removeAttribute("open");
33258
+ this.#launcher.collapse(true);
32840
33259
  this.dispatchEvent(new CustomEvent("assistant-close"));
32841
33260
  }
32842
33261
  toggle() {
@@ -32973,17 +33392,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32973
33392
  if (generation === this.#conversationGeneration) this.#setBusy(false);
32974
33393
  }
32975
33394
  }
32976
- #handleSystemTheme = () => this.#applyTheme();
32977
- #handleDocumentKeydown = (event) => {
32978
- if (event.key === "Escape" && this.hasAttribute("open") && this.#appearance.behavior.closeOnEscape) {
32979
- this.#closeAndRestoreFocus();
32980
- }
32981
- };
32982
- #handleDocumentPointerDown = (event) => {
32983
- if (this.hasAttribute("open") && this.#appearance.behavior.closeOnOutsideClick && !event.composedPath().includes(this)) {
32984
- this.close();
32985
- }
32986
- };
32987
33395
  #ensureClient() {
32988
33396
  const key = sessionSourceKey(this);
32989
33397
  if (this.#client && this.#clientEndpoint === key) return this.#client;
@@ -32999,8 +33407,13 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32999
33407
  const client = this.embedId ? createAssistantClient({
33000
33408
  embedId: this.embedId,
33001
33409
  ...this.serviceUrl ? { serviceUrl: this.serviceUrl } : {},
33410
+ ...this.#fetch ? { fetch: this.#fetch } : {},
33411
+ ...behavior
33412
+ }) : createAssistantClient({
33413
+ sessionEndpoint: this.sessionEndpoint,
33414
+ ...this.#fetch ? { fetch: this.#fetch } : {},
33002
33415
  ...behavior
33003
- }) : createAssistantClient({ sessionEndpoint: this.sessionEndpoint, ...behavior });
33416
+ });
33004
33417
  this.#client = client;
33005
33418
  this.#clientEndpoint = key;
33006
33419
  this.#unsubscribeClient = client.subscribe((event) => {
@@ -33029,6 +33442,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33029
33442
  this.dispatchEvent(new CustomEvent("assistant-event", { detail: event }));
33030
33443
  this.#events.handle(event);
33031
33444
  if (event.event === "session_started") {
33445
+ this.#publicConfiguration.disconnect();
33446
+ this.removeAttribute("data-public-configuration-loading");
33032
33447
  this.#presentationGate.reveal();
33033
33448
  this.#setSessionState("ready");
33034
33449
  this.#messages?.querySelector(".conversation-error")?.remove();
@@ -33078,10 +33493,10 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33078
33493
  if (!this.#messages) return void 0;
33079
33494
  const message = document.createElement("div");
33080
33495
  message.className = `message ${role}`;
33081
- if (role === "assistant" && this.#appearance.behavior.showAvatars && this.#appearance.brand.avatar[this.#resolvedMode()]) {
33496
+ if (role === "assistant" && this.#appearance.behavior.showAvatars && this.#appearance.brand.avatar[this.#environment.resolvedTheme()]) {
33082
33497
  const avatar = document.createElement("img");
33083
33498
  avatar.className = "avatar";
33084
- avatar.src = this.#appearance.brand.avatar[this.#resolvedMode()] ?? "";
33499
+ avatar.src = this.#appearance.brand.avatar[this.#environment.resolvedTheme()] ?? "";
33085
33500
  avatar.alt = "";
33086
33501
  message.append(avatar);
33087
33502
  }
@@ -33162,12 +33577,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33162
33577
  this.#primeSession();
33163
33578
  this.dispatchEvent(new CustomEvent("assistant-new-conversation"));
33164
33579
  }
33165
- #resolvedMode() {
33166
- const configured = this.getAttribute("theme") ?? this.#appearance.theme.defaultMode;
33167
- return configured === "auto" ? this.#media?.matches ? "dark" : "light" : configured;
33168
- }
33169
33580
  #applyTheme() {
33170
- const mode = this.#resolvedMode();
33581
+ const mode = this.#environment.resolvedTheme();
33171
33582
  for (const warning of applyAssistantElementTheme({
33172
33583
  host: this,
33173
33584
  root: this.shadowRoot,
@@ -33190,13 +33601,23 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33190
33601
  updateAssistantAppTheme(app, mode);
33191
33602
  }
33192
33603
  }
33604
+ #applyConfiguration(configuration) {
33605
+ this.#appearance = resolveAppearance(configuration);
33606
+ this.#syncAppearance();
33607
+ this.#applyStartOpenPolicy();
33608
+ }
33609
+ #setPublicConfigurationLoading(loading) {
33610
+ this.toggleAttribute("data-public-configuration-loading", loading);
33611
+ if (loading) this.#presentationGate.syncOpenState();
33612
+ else this.#presentationGate.reveal();
33613
+ }
33193
33614
  #setSessionState(state) {
33194
33615
  this.#sessionState = state;
33195
33616
  syncAssistantSessionVisualState(this, this.shadowRoot, state, this.#appearance);
33196
33617
  }
33197
33618
  #closeAndRestoreFocus() {
33198
33619
  this.close();
33199
- this.shadowRoot?.querySelector(".launcher")?.focus();
33620
+ this.#launcher.focusTrigger();
33200
33621
  }
33201
33622
  #applyStartOpenPolicy() {
33202
33623
  if (this.#startOpenApplied) return;
@@ -33222,10 +33643,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33222
33643
  textarea.placeholder = appearance.labels.composerPlaceholder;
33223
33644
  textarea.setAttribute("aria-label", appearance.labels.composerPlaceholder);
33224
33645
  queryRequired(this.shadowRoot, ".send").textContent = appearance.labels.send;
33225
- queryRequired(this.shadowRoot, ".launcher").setAttribute(
33226
- "aria-label",
33227
- appearance.labels.open
33228
- );
33229
33646
  queryRequired(this.shadowRoot, ".close").setAttribute(
33230
33647
  "aria-label",
33231
33648
  appearance.labels.close
@@ -33239,11 +33656,11 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33239
33656
  button2.addEventListener("click", () => this.#sendGuarded(prompt));
33240
33657
  prompts.append(button2);
33241
33658
  }
33242
- queryRequired(this.shadowRoot, ".launcher").hidden = !appearance.behavior.showLauncher;
33243
33659
  queryRequired(this.shadowRoot, "header").hidden = !appearance.behavior.showHeader;
33244
33660
  const legal = queryRequired(this.shadowRoot, ".legal");
33245
33661
  legal.replaceChildren();
33246
33662
  appendLegalLinks(legal, this.#appearance);
33663
+ this.#launcher.sync();
33247
33664
  this.#applyTheme();
33248
33665
  }
33249
33666
  #render() {
@@ -33261,12 +33678,11 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33261
33678
  this.toggleAttribute("mobile-fullscreen", appearance.layout.mobileFullscreen);
33262
33679
  this.shadowRoot.innerHTML = assistantElementMarkup(
33263
33680
  appearance,
33264
- this.hasAttribute("open"),
33265
33681
  `${ASSISTANT_ELEMENT_STYLES}${presentationStyles}`
33266
33682
  );
33267
33683
  queryRequired(this.shadowRoot, "strong").textContent = appearance.brand.name;
33268
33684
  const logo = queryRequired(this.shadowRoot, ".brand-logo");
33269
- const logoUrl = appearance.brand.logo[this.#resolvedMode()];
33685
+ const logoUrl = appearance.brand.logo[this.#environment.resolvedTheme()];
33270
33686
  if (logoUrl) {
33271
33687
  logo.src = logoUrl;
33272
33688
  logo.alt = appearance.brand.name;
@@ -33294,12 +33710,9 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33294
33710
  this.#messages = this.shadowRoot.querySelector(".messages") ?? void 0;
33295
33711
  this.#messages?.append(...conversation);
33296
33712
  this.#thinking = this.#messages?.querySelector(".thinking") ?? void 0;
33297
- this.shadowRoot.querySelector(".launcher")?.addEventListener("click", () => {
33298
- this.open();
33299
- this.focusComposer();
33300
- });
33713
+ this.#launcher.connect(this.shadowRoot);
33301
33714
  this.shadowRoot.querySelector(".close")?.addEventListener("click", () => this.#closeAndRestoreFocus());
33302
- const form = queryRequired(this.shadowRoot, "form");
33715
+ const form = queryRequired(this.shadowRoot, ".composer");
33303
33716
  form.addEventListener("submit", (event) => {
33304
33717
  event.preventDefault();
33305
33718
  if (this.hasAttribute("busy")) {