@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/react.cjs CHANGED
@@ -15197,7 +15197,7 @@ __export(react_exports, {
15197
15197
  module.exports = __toCommonJS(react_exports);
15198
15198
  var import_react2 = require("react");
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
  })();
@@ -22518,7 +22346,7 @@ function unmountAssistantApp(card) {
22518
22346
 
22519
22347
  // src/appearance.ts
22520
22348
  var common = {
22521
- fontFamily: "inherit",
22349
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
22522
22350
  monoFontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
22523
22351
  baseFontSize: 15,
22524
22352
  messageFontSize: 15,
@@ -22529,7 +22357,7 @@ var common = {
22529
22357
  buttonRadius: 999,
22530
22358
  launcherRadius: 999,
22531
22359
  spacing: 8,
22532
- backdropBlur: 18,
22360
+ backdropBlur: 6,
22533
22361
  motionScale: 1
22534
22362
  };
22535
22363
  var DEFAULT_APPEARANCE = {
@@ -22549,7 +22377,7 @@ var DEFAULT_APPEARANCE = {
22549
22377
  accent: "#C2410C",
22550
22378
  accentText: "#FFFFFF",
22551
22379
  link: "#9A3412",
22552
- divider: "#E4E4E7",
22380
+ divider: "rgba(0, 0, 0, 0.1)",
22553
22381
  focus: "#C2410C",
22554
22382
  success: "#16835D",
22555
22383
  warning: "#A45B00",
@@ -22557,7 +22385,7 @@ var DEFAULT_APPEARANCE = {
22557
22385
  overlay: "#18181B66",
22558
22386
  code: "#F1F1F3",
22559
22387
  widgetFrame: "#E4E4E7",
22560
- shadow: "0 4px 16px rgba(0, 0, 0, 0.06), 0 24px 70px rgba(0, 0, 0, 0.10)"
22388
+ shadow: "0 4px 16px rgba(0, 0, 0, 0.06)"
22561
22389
  },
22562
22390
  dark: {
22563
22391
  ...common,
@@ -22572,7 +22400,7 @@ var DEFAULT_APPEARANCE = {
22572
22400
  accent: "#FB923C",
22573
22401
  accentText: "#1C0A02",
22574
22402
  link: "#FDBA74",
22575
- divider: "#292524",
22403
+ divider: "rgba(255, 255, 255, 0.1)",
22576
22404
  focus: "#FB923C",
22577
22405
  success: "#58C69A",
22578
22406
  warning: "#F2B45F",
@@ -22580,15 +22408,15 @@ var DEFAULT_APPEARANCE = {
22580
22408
  overlay: "#00000099",
22581
22409
  code: "#26262C",
22582
22410
  widgetFrame: "#34343B",
22583
- shadow: "0 8px 32px rgba(0, 0, 0, 0.48)"
22411
+ shadow: "0 8px 32px rgba(0, 0, 0, 0.4)"
22584
22412
  }
22585
22413
  },
22586
22414
  layout: {
22587
22415
  mode: "floating",
22588
- position: "bottom-right",
22589
- panelWidth: 520,
22590
- panelMinHeight: 540,
22591
- panelMaxHeight: 820,
22416
+ position: "bottom-center",
22417
+ panelWidth: 970,
22418
+ panelMinHeight: 0,
22419
+ panelMaxHeight: 1025,
22592
22420
  edgeOffset: 20,
22593
22421
  zIndex: 2147483e3,
22594
22422
  density: "comfortable",
@@ -22601,11 +22429,13 @@ var DEFAULT_APPEARANCE = {
22601
22429
  showLauncher: true,
22602
22430
  showHeader: true,
22603
22431
  showAvatars: true,
22604
- showTimestamps: false
22432
+ showTimestamps: false,
22433
+ showPoweredBy: true
22605
22434
  },
22606
22435
  labels: {
22607
- welcomeHeading: "How can I help?",
22436
+ welcomeHeading: "",
22608
22437
  welcomeMessage: "",
22438
+ launcherPlaceholder: "Chat with us",
22609
22439
  composerPlaceholder: "Ask a question\u2026",
22610
22440
  thinking: "Thinking",
22611
22441
  send: "Send",
@@ -22639,11 +22469,12 @@ var DEFAULT_APPEARANCE = {
22639
22469
  },
22640
22470
  presentation: {
22641
22471
  panel: {
22642
- surface: "glass",
22472
+ surface: "solid",
22643
22473
  elevation: "soft",
22644
22474
  border: "subtle"
22645
22475
  },
22646
22476
  launcher: {
22477
+ style: "pill",
22647
22478
  icon: "brand-mark",
22648
22479
  size: "md",
22649
22480
  status: "none",
@@ -22653,7 +22484,11 @@ var DEFAULT_APPEARANCE = {
22653
22484
  composer: { leadingIcon: "none", sendIcon: "arrow-up", shape: "pill" },
22654
22485
  messages: { userStyle: "bubble", assistantStyle: "plain" }
22655
22486
  },
22656
- suggestedPrompts: [],
22487
+ suggestedPrompts: [
22488
+ "What products do you have?",
22489
+ "Tell me about your business",
22490
+ "How can I get in touch?"
22491
+ ],
22657
22492
  locale: "en",
22658
22493
  direction: "auto"
22659
22494
  };
@@ -22669,7 +22504,7 @@ function resolveAppearance(input = {}) {
22669
22504
  mark: themedAsset(branding?.mark)
22670
22505
  },
22671
22506
  theme: {
22672
- defaultMode: branding?.colorScheme ?? DEFAULT_APPEARANCE.theme.defaultMode,
22507
+ defaultMode: assistant?.theme ?? branding?.colorScheme ?? DEFAULT_APPEARANCE.theme.defaultMode,
22673
22508
  light: resolveTheme("light", branding),
22674
22509
  dark: resolveTheme("dark", branding)
22675
22510
  },
@@ -22680,9 +22515,17 @@ function resolveAppearance(input = {}) {
22680
22515
  ...assistant?.layout?.edgeOffset === void 0 ? {} : { edgeOffset: boundedNumber(assistant.layout.edgeOffset, 0, 96) }
22681
22516
  },
22682
22517
  behavior: { ...DEFAULT_APPEARANCE.behavior, ...assistant?.behavior },
22683
- labels: { ...DEFAULT_APPEARANCE.labels, ...assistant?.labels },
22518
+ labels: {
22519
+ ...DEFAULT_APPEARANCE.labels,
22520
+ ...branding?.name ? { launcherPlaceholder: `Chat with ${branding.name}` } : {},
22521
+ ...assistant?.labels
22522
+ },
22684
22523
  presentation: resolvePresentation(assistant?.presentation),
22685
- suggestedPrompts: assistant?.suggestedPrompts ?? DEFAULT_APPEARANCE.suggestedPrompts,
22524
+ suggestedPrompts: assistant?.suggestedPrompts ?? (branding?.name ? [
22525
+ "What products do you have?",
22526
+ `Tell me about ${branding.name}`,
22527
+ "How can I get in touch?"
22528
+ ] : DEFAULT_APPEARANCE.suggestedPrompts),
22686
22529
  ...assistant?.privacyUrl ? { privacyUrl: assistant.privacyUrl } : {},
22687
22530
  ...assistant?.termsUrl ? { termsUrl: assistant.termsUrl } : {},
22688
22531
  locale: assistant?.locale ?? DEFAULT_APPEARANCE.locale,
@@ -24953,6 +24796,10 @@ async function resolve(value) {
24953
24796
  return Promise.resolve(value);
24954
24797
  }
24955
24798
 
24799
+ // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/index.js
24800
+ init_external();
24801
+ init_external();
24802
+
24956
24803
  // ../../node_modules/.pnpm/ai@6.0.201_zod@4.4.3/node_modules/ai/dist/index.mjs
24957
24804
  init_v4();
24958
24805
  init_v4();
@@ -27502,14 +27349,14 @@ function toChatError(error51) {
27502
27349
  }
27503
27350
  function readErrorDetail(error51) {
27504
27351
  const detail = Reflect.get(error51, "detail");
27505
- if (!isRecord2(detail) || typeof detail.code !== "string") return void 0;
27352
+ if (!isRecord(detail) || typeof detail.code !== "string") return void 0;
27506
27353
  return {
27507
27354
  code: detail.code,
27508
27355
  ...typeof detail.status === "number" ? { status: detail.status } : {},
27509
27356
  retryable: detail.retryable === true
27510
27357
  };
27511
27358
  }
27512
- function isRecord2(value) {
27359
+ function isRecord(value) {
27513
27360
  return typeof value === "object" && value !== null && !Array.isArray(value);
27514
27361
  }
27515
27362
 
@@ -27536,7 +27383,7 @@ var AssistantClientError = class extends Error {
27536
27383
  }
27537
27384
  };
27538
27385
  function parseSession(value) {
27539
- if (!isRecord3(value) || !isRecord3(value.endpoints)) {
27386
+ if (!isRecord2(value) || !isRecord2(value.endpoints)) {
27540
27387
  throw clientError("session_failed", "Assistant session response is invalid", true);
27541
27388
  }
27542
27389
  const interactions = value.endpoints.interactions;
@@ -27557,11 +27404,11 @@ function parseSession(value) {
27557
27404
  ...typeof sandbox === "string" ? { sandbox } : {},
27558
27405
  ...typeof transcript === "string" ? { transcript } : {}
27559
27406
  },
27560
- ...isRecord3(value.configuration) ? { configuration: value.configuration } : {},
27561
- ...isRecord3(value.resume) && typeof value.resume.tool === "string" ? { resume: { tool: value.resume.tool } } : {}
27407
+ ...isRecord2(value.configuration) ? { configuration: value.configuration } : {},
27408
+ ...isRecord2(value.resume) && typeof value.resume.tool === "string" ? { resume: { tool: value.resume.tool } } : {}
27562
27409
  };
27563
27410
  }
27564
- function isRecord3(value) {
27411
+ function isRecord2(value) {
27565
27412
  return typeof value === "object" && value !== null && !Array.isArray(value);
27566
27413
  }
27567
27414
  async function readStableErrorCode(response) {
@@ -27571,7 +27418,7 @@ async function readStableErrorCode(response) {
27571
27418
  } catch {
27572
27419
  return void 0;
27573
27420
  }
27574
- if (!isRecord3(value) || typeof value.code !== "string") return void 0;
27421
+ if (!isRecord2(value) || typeof value.code !== "string") return void 0;
27575
27422
  return /^[A-Za-z0-9_.-]{1,64}$/.test(value.code) ? value.code : void 0;
27576
27423
  }
27577
27424
  function clientError(code, message, retryable, status, options, serviceCode) {
@@ -27602,12 +27449,12 @@ function toAssistantClientEvent(event) {
27602
27449
  }
27603
27450
  break;
27604
27451
  case "tool_proposed":
27605
- 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)) {
27452
+ 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)) {
27606
27453
  return event;
27607
27454
  }
27608
27455
  break;
27609
27456
  case "input_requested":
27610
- if (hasString(value, "id") && hasString(value, "message") && isRecord4(value.requestedSchema) && hasString(value, "expiresAt") && hasOptionalTurnId(value)) {
27457
+ if (hasString(value, "id") && hasString(value, "message") && isRecord3(value.requestedSchema) && hasString(value, "expiresAt") && hasOptionalTurnId(value)) {
27611
27458
  return event;
27612
27459
  }
27613
27460
  break;
@@ -27651,7 +27498,7 @@ function toAssistantClientEvent(event) {
27651
27498
  return { event: "unrecognized", data: { name: event.event, payload: value } };
27652
27499
  }
27653
27500
  function isViewAvailableDetail(value) {
27654
- 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);
27501
+ 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);
27655
27502
  }
27656
27503
  function isHttpsUrl(value) {
27657
27504
  if (typeof value !== "string") return false;
@@ -27684,17 +27531,22 @@ function isJsonValue(value, depth) {
27684
27531
  if (Array.isArray(value)) {
27685
27532
  return value.length <= 256 && value.every((entry) => isJsonValue(entry, depth + 1));
27686
27533
  }
27687
- if (!isRecord4(value)) return false;
27534
+ if (!isRecord3(value)) return false;
27688
27535
  const entries2 = Object.entries(value);
27689
27536
  return entries2.length <= 256 && entries2.every(([, entry]) => isJsonValue(entry, depth + 1));
27690
27537
  }
27691
- function isRecord4(value) {
27538
+ function isRecord3(value) {
27692
27539
  return typeof value === "object" && value !== null && !Array.isArray(value);
27693
27540
  }
27694
27541
 
27695
27542
  // src/session-source.ts
27696
27543
  var NOODLE_CLOUD_URL = "https://cloud.noodleseed.dev";
27697
27544
  var PUBLIC_SESSION_PATH = "/v1/assistant/public-sessions";
27545
+ var PUBLIC_CONFIGURATION_PATH = "/v1/assistant/public-configurations";
27546
+ function publicConfigurationUrl(serviceUrl, embedId) {
27547
+ const base = (serviceUrl || NOODLE_CLOUD_URL).replace(/\/+$/, "");
27548
+ return `${base}${PUBLIC_CONFIGURATION_PATH}/${encodeURIComponent(embedId)}`;
27549
+ }
27698
27550
  function resolveSessionSource(options) {
27699
27551
  const { sessionEndpoint, embedId, serviceUrl } = options;
27700
27552
  if (Boolean(embedId) === Boolean(sessionEndpoint)) {
@@ -27855,15 +27707,15 @@ function parseFrame(frame) {
27855
27707
  if (event === "done" && !isValidDonePayload(parsed)) {
27856
27708
  throw invalidStream("assistant event stream contained an invalid done event");
27857
27709
  }
27858
- return { event, data: isRecord5(parsed) ? parsed : { value: parsed } };
27710
+ return { event, data: isRecord4(parsed) ? parsed : { value: parsed } };
27859
27711
  }
27860
27712
  function normalizeLineEndings(input) {
27861
27713
  return input.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
27862
27714
  }
27863
27715
  function isValidDonePayload(value) {
27864
- return isRecord5(value) && (value.turnId === void 0 || typeof value.turnId === "string" && value.turnId.length > 0);
27716
+ return isRecord4(value) && (value.turnId === void 0 || typeof value.turnId === "string" && value.turnId.length > 0);
27865
27717
  }
27866
- function isRecord5(value) {
27718
+ function isRecord4(value) {
27867
27719
  return typeof value === "object" && value !== null && !Array.isArray(value);
27868
27720
  }
27869
27721
  function invalidStream(message, cause) {
@@ -28382,10 +28234,10 @@ function authorizedHeaders(token, accept) {
28382
28234
  };
28383
28235
  }
28384
28236
  function parseAppInteraction(value) {
28385
- if (!isRecord3(value) || !isRecord3(value.interaction)) return void 0;
28237
+ if (!isRecord2(value) || !isRecord2(value.interaction)) return void 0;
28386
28238
  const event = value.interaction.event;
28387
28239
  const data = value.interaction.data;
28388
- if (event !== "tool_proposed" && event !== "input_requested" || !isRecord3(data)) {
28240
+ if (event !== "tool_proposed" && event !== "input_requested" || !isRecord2(data)) {
28389
28241
  return void 0;
28390
28242
  }
28391
28243
  const parsed = toAssistantClientEvent({ event, data });
@@ -29206,11 +29058,11 @@ function createDOMPurify() {
29206
29058
  const originalDocument = document2;
29207
29059
  const currentScript = originalDocument.currentScript;
29208
29060
  window2.DocumentFragment;
29209
- const HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
29061
+ const HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element2 = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
29210
29062
  _window$NamedNodeMap === void 0 ? window2.NamedNodeMap || window2.MozNamedAttrMap : _window$NamedNodeMap;
29211
29063
  window2.HTMLFormElement;
29212
29064
  const DOMParser = window2.DOMParser, trustedTypes = window2.trustedTypes;
29213
- const ElementPrototype = Element.prototype;
29065
+ const ElementPrototype = Element2.prototype;
29214
29066
  const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
29215
29067
  const remove = lookupGetter(ElementPrototype, "remove");
29216
29068
  const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
@@ -31584,7 +31436,7 @@ var AssistantElementEventController = class {
31584
31436
  if (event.event === "unrecognized") return;
31585
31437
  const { event: name21, data } = event;
31586
31438
  if (name21 === "session_started") {
31587
- if (isRecord6(data.configuration)) {
31439
+ if (isRecord5(data.configuration)) {
31588
31440
  this.#host.applyConfiguration(data.configuration);
31589
31441
  }
31590
31442
  this.#host.element.dispatchEvent(
@@ -31652,10 +31504,10 @@ var AssistantElementEventController = class {
31652
31504
  this.#appendToolProposal(
31653
31505
  String(data.id ?? ""),
31654
31506
  String(data.tool ?? "action"),
31655
- isRecord6(data.arguments) ? data.arguments : void 0,
31507
+ isRecord5(data.arguments) ? data.arguments : void 0,
31656
31508
  typeof data.title === "string" ? data.title : void 0,
31657
31509
  "description" in data && typeof data.description === "string" ? data.description : void 0,
31658
- "reviewSchema" in data && isRecord6(data.reviewSchema) ? data.reviewSchema : void 0
31510
+ "reviewSchema" in data && isRecord5(data.reviewSchema) ? data.reviewSchema : void 0
31659
31511
  );
31660
31512
  return;
31661
31513
  }
@@ -31668,7 +31520,7 @@ var AssistantElementEventController = class {
31668
31520
  );
31669
31521
  return;
31670
31522
  }
31671
- if (name21 === "input_requested" && isRecord6(data.requestedSchema)) {
31523
+ if (name21 === "input_requested" && isRecord5(data.requestedSchema)) {
31672
31524
  this.#appendInputRequest(
31673
31525
  String(data.id ?? ""),
31674
31526
  String(data.message ?? "More information is required"),
@@ -31839,7 +31691,7 @@ var AssistantElementEventController = class {
31839
31691
  this.#host.revealLatest();
31840
31692
  }
31841
31693
  };
31842
- function isRecord6(value) {
31694
+ function isRecord5(value) {
31843
31695
  return typeof value === "object" && value !== null && !Array.isArray(value);
31844
31696
  }
31845
31697
 
@@ -31860,19 +31712,40 @@ function queryRequired(root, selector) {
31860
31712
  if (!element) throw new Error(`assistant renderer is missing ${selector}`);
31861
31713
  return element;
31862
31714
  }
31863
- function assistantElementMarkup(appearance, open, styles) {
31715
+ function assistantElementMarkup(appearance, styles) {
31864
31716
  return `${styles}
31865
- <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>
31717
+ <div class="launcher">
31718
+ <slot name="launcher-icon"></slot>
31719
+ <span class="launcher-loader" aria-hidden="true"></span>
31720
+ <button class="launcher-trigger" type="button" aria-label="${escapeAttribute(appearance.labels.open)}" aria-controls="assistant-launcher-form" aria-expanded="false">
31721
+ <span class="launcher-label">${escapeAttribute(appearance.labels.launcherPlaceholder)}</span>
31722
+ <span class="launcher-arrow" aria-hidden="true"></span>
31723
+ </button>
31724
+ <form class="launcher-form" id="assistant-launcher-form" aria-hidden="true" autocomplete="off">
31725
+ <input class="launcher-input" type="text" aria-label="${escapeAttribute(appearance.labels.launcherPlaceholder)}" placeholder="${escapeAttribute(appearance.labels.launcherPlaceholder)}" tabindex="-1">
31726
+ <button class="launcher-send" type="submit" aria-label="${escapeAttribute(appearance.labels.send)}"></button>
31727
+ </form>
31728
+ <span class="visually-hidden" data-session-status aria-live="polite" hidden></span>
31729
+ </div>
31866
31730
  <section class="panel" id="assistant-panel" role="dialog" aria-label="${escapeAttribute(appearance.brand.name)}">
31867
31731
  <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>
31868
31732
  <div class="welcome"><slot name="empty-state"></slot></div>
31869
- <div class="suggested-prompts"></div>
31870
31733
  <div class="messages-region">
31871
31734
  <div class="messages" aria-live="polite"></div>
31872
31735
  <button class="new-messages" type="button" aria-label="${escapeAttribute(appearance.labels.newMessages)}" hidden></button>
31873
31736
  </div>
31874
- <form><slot name="composer-leading"></slot><textarea rows="1"></textarea><button class="send" type="submit"></button><slot name="composer-trailing"></slot></form>
31737
+ <div class="input-section">
31738
+ <div class="suggested-prompts"></div>
31739
+ <form class="composer"><slot name="composer-leading"></slot><textarea rows="1"></textarea><button class="send" type="submit"></button><slot name="composer-trailing"></slot></form>
31740
+ </div>
31875
31741
  <nav class="legal" aria-label="Legal"></nav>
31742
+ <div class="powered-by-row">
31743
+ <a class="powered-by" href="https://noodleseed.com" target="_blank" rel="noopener noreferrer">
31744
+ <span>Powered by</span>
31745
+ <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>
31746
+ <span class="powered-by-name">Noodle Seed</span>
31747
+ </a>
31748
+ </div>
31876
31749
  <slot name="conversation-footer"></slot>
31877
31750
  </section>`;
31878
31751
  }
@@ -31891,6 +31764,230 @@ function appendLegalLinks(legal, appearance) {
31891
31764
  }
31892
31765
  }
31893
31766
 
31767
+ // src/element-theme-resolution.ts
31768
+ function resolveAssistantTheme(input) {
31769
+ if (input.configured === "light" || input.configured === "dark") return input.configured;
31770
+ const hostMode = resolveHostTheme(input.host, input.media);
31771
+ return input.configured === "invert" ? opposite(hostMode) : hostMode;
31772
+ }
31773
+ function resolveHostTheme(host, media) {
31774
+ const view = host.ownerDocument.defaultView;
31775
+ for (let node = host.parentElement; node; node = node.parentElement) {
31776
+ const declared = declaredTheme(node);
31777
+ if (declared) return declared;
31778
+ const computed = computedTheme(view?.getComputedStyle(node).colorScheme);
31779
+ if (computed) return computed;
31780
+ }
31781
+ return media?.matches ? "dark" : "light";
31782
+ }
31783
+ function computedTheme(colorScheme) {
31784
+ const modes = colorScheme?.split(/\s+/) ?? [];
31785
+ if (modes.includes("dark") && !modes.includes("light")) return "dark";
31786
+ if (modes.includes("light") && !modes.includes("dark")) return "light";
31787
+ return void 0;
31788
+ }
31789
+ function declaredTheme(element) {
31790
+ const value = element.getAttribute("data-theme") ?? element.getAttribute("data-color-scheme") ?? element.getAttribute("data-mode");
31791
+ if (value === "dark" || value === "light") return value;
31792
+ if (element.classList.contains("dark")) return "dark";
31793
+ if (element.classList.contains("light")) return "light";
31794
+ return void 0;
31795
+ }
31796
+ function opposite(mode) {
31797
+ return mode === "dark" ? "light" : "dark";
31798
+ }
31799
+
31800
+ // src/element-host-environment-controller.ts
31801
+ var AssistantElementHostEnvironmentController = class {
31802
+ #host;
31803
+ #appearance;
31804
+ #applyTheme;
31805
+ #close;
31806
+ #media;
31807
+ #themeObserver;
31808
+ constructor(options) {
31809
+ this.#host = options.host;
31810
+ this.#appearance = options.appearance;
31811
+ this.#applyTheme = options.applyTheme;
31812
+ this.#close = options.close;
31813
+ }
31814
+ get media() {
31815
+ return this.#media;
31816
+ }
31817
+ resolvedTheme() {
31818
+ return resolveAssistantTheme({
31819
+ host: this.#host,
31820
+ configured: this.#host.getAttribute("theme") ?? this.#appearance().theme.defaultMode,
31821
+ ...this.#media ? { media: this.#media } : {}
31822
+ });
31823
+ }
31824
+ connect() {
31825
+ this.disconnect();
31826
+ this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
31827
+ this.#media?.addEventListener("change", this.#applyTheme);
31828
+ this.#themeObserver = new MutationObserver(this.#applyTheme);
31829
+ for (let node = this.#host.parentElement; node; node = node.parentElement) {
31830
+ this.#themeObserver.observe(node, {
31831
+ attributes: true,
31832
+ attributeFilter: ["class", "data-theme", "data-color-scheme", "data-mode", "style"]
31833
+ });
31834
+ }
31835
+ document.addEventListener("keydown", this.#handleDocumentKeydown);
31836
+ document.addEventListener("pointerdown", this.#handleDocumentPointerDown);
31837
+ }
31838
+ disconnect() {
31839
+ this.#media?.removeEventListener("change", this.#applyTheme);
31840
+ this.#media = void 0;
31841
+ this.#themeObserver?.disconnect();
31842
+ this.#themeObserver = void 0;
31843
+ document.removeEventListener("keydown", this.#handleDocumentKeydown);
31844
+ document.removeEventListener("pointerdown", this.#handleDocumentPointerDown);
31845
+ }
31846
+ #handleDocumentKeydown = (event) => {
31847
+ if (event.key === "Escape" && this.#host.hasAttribute("open") && this.#appearance().behavior.closeOnEscape) {
31848
+ this.#close();
31849
+ }
31850
+ };
31851
+ #handleDocumentPointerDown = (event) => {
31852
+ if (this.#host.hasAttribute("open") && this.#appearance().behavior.closeOnOutsideClick && !event.composedPath().includes(this.#host)) {
31853
+ this.#close();
31854
+ }
31855
+ };
31856
+ };
31857
+
31858
+ // src/element-launcher-controller.ts
31859
+ var AssistantElementLauncherController = class {
31860
+ #host;
31861
+ #appearance;
31862
+ #openPanel;
31863
+ #sendMessage;
31864
+ #root;
31865
+ constructor(options) {
31866
+ this.#host = options.host;
31867
+ this.#appearance = options.appearance;
31868
+ this.#openPanel = options.openPanel;
31869
+ this.#sendMessage = options.sendMessage;
31870
+ }
31871
+ connect(root) {
31872
+ this.disconnect();
31873
+ this.#root = root;
31874
+ queryRequired(root, ".launcher").addEventListener(
31875
+ "click",
31876
+ this.#handleLauncherClick
31877
+ );
31878
+ queryRequired(root, ".launcher-trigger").addEventListener(
31879
+ "click",
31880
+ this.#handleTrigger
31881
+ );
31882
+ queryRequired(root, ".launcher-form").addEventListener(
31883
+ "submit",
31884
+ this.#handleSubmit
31885
+ );
31886
+ queryRequired(root, ".launcher-input").addEventListener(
31887
+ "keydown",
31888
+ this.#handleInputKeydown
31889
+ );
31890
+ document.addEventListener("pointerdown", this.#handleDocumentPointerDown);
31891
+ this.sync();
31892
+ }
31893
+ disconnect() {
31894
+ document.removeEventListener("pointerdown", this.#handleDocumentPointerDown);
31895
+ this.#root = void 0;
31896
+ }
31897
+ sync() {
31898
+ const root = this.#root;
31899
+ if (!root) return;
31900
+ const appearance = this.#appearance();
31901
+ const style = appearance.presentation.launcher.style;
31902
+ this.#host.dataset.launcherStyle = style;
31903
+ const trigger = queryRequired(root, ".launcher-trigger");
31904
+ const label = queryRequired(root, ".launcher-label");
31905
+ const input = queryRequired(root, ".launcher-input");
31906
+ label.textContent = appearance.labels.launcherPlaceholder;
31907
+ input.placeholder = appearance.labels.launcherPlaceholder;
31908
+ input.setAttribute("aria-label", appearance.labels.launcherPlaceholder);
31909
+ trigger.setAttribute("aria-label", appearance.labels.open);
31910
+ queryRequired(root, ".launcher-send").setAttribute(
31911
+ "aria-label",
31912
+ appearance.labels.send
31913
+ );
31914
+ queryRequired(root, ".launcher").hidden = !appearance.behavior.showLauncher;
31915
+ queryRequired(root, ".powered-by-row").hidden = !appearance.behavior.showPoweredBy;
31916
+ if (style === "bubble" || this.#host.hasAttribute("open")) this.collapse(true);
31917
+ }
31918
+ collapse(force = false) {
31919
+ const root = this.#root;
31920
+ if (!root) return;
31921
+ const input = queryRequired(root, ".launcher-input");
31922
+ if (!force && input.value.trim()) return;
31923
+ this.#host.removeAttribute("launcher-expanded");
31924
+ const form = queryRequired(root, ".launcher-form");
31925
+ const trigger = queryRequired(root, ".launcher-trigger");
31926
+ form.setAttribute("aria-hidden", "true");
31927
+ trigger.setAttribute("aria-expanded", "false");
31928
+ input.tabIndex = -1;
31929
+ input.blur();
31930
+ }
31931
+ focusTrigger() {
31932
+ this.#root?.querySelector(".launcher-trigger")?.focus();
31933
+ }
31934
+ #expand() {
31935
+ const root = this.#root;
31936
+ if (!root || this.#appearance().presentation.launcher.style === "bubble") return;
31937
+ this.#host.setAttribute("launcher-expanded", "");
31938
+ const form = queryRequired(root, ".launcher-form");
31939
+ const trigger = queryRequired(root, ".launcher-trigger");
31940
+ const input = queryRequired(root, ".launcher-input");
31941
+ form.setAttribute("aria-hidden", "false");
31942
+ trigger.setAttribute("aria-expanded", "true");
31943
+ input.tabIndex = 0;
31944
+ requestAnimationFrame(() => {
31945
+ if (this.#host.hasAttribute("launcher-expanded")) input.focus();
31946
+ });
31947
+ }
31948
+ #handleTrigger = () => {
31949
+ if (this.#appearance().presentation.launcher.style === "bubble") {
31950
+ this.#openPanel();
31951
+ return;
31952
+ }
31953
+ this.#expand();
31954
+ };
31955
+ #handleLauncherClick = (event) => {
31956
+ if (event.composedPath().some(
31957
+ (target) => target instanceof Element && target.matches(".launcher-trigger, .launcher-form")
31958
+ )) {
31959
+ return;
31960
+ }
31961
+ this.#handleTrigger();
31962
+ };
31963
+ #handleSubmit = (event) => {
31964
+ event.preventDefault();
31965
+ const input = this.#root?.querySelector(".launcher-input");
31966
+ const message = input?.value.trim() ?? "";
31967
+ if (input) input.value = "";
31968
+ this.#openPanel();
31969
+ if (message) this.#sendMessage(message);
31970
+ };
31971
+ #handleInputKeydown = (event) => {
31972
+ if (event.key === "Enter" && !event.isComposing) {
31973
+ event.preventDefault();
31974
+ this.#root?.querySelector(".launcher-form")?.requestSubmit();
31975
+ return;
31976
+ }
31977
+ if (event.key !== "Escape") return;
31978
+ event.preventDefault();
31979
+ event.stopPropagation();
31980
+ const input = this.#root?.querySelector(".launcher-input");
31981
+ if (input) input.value = "";
31982
+ this.collapse(true);
31983
+ this.focusTrigger();
31984
+ };
31985
+ #handleDocumentPointerDown = (event) => {
31986
+ if (event.composedPath().includes(this.#host)) return;
31987
+ this.collapse();
31988
+ };
31989
+ };
31990
+
31894
31991
  // src/element-page-context-controller.ts
31895
31992
  var AssistantElementPageContextController = class {
31896
31993
  #snapshot;
@@ -31944,10 +32041,12 @@ var AssistantElementPresentationGate = class {
31944
32041
  syncOpenState() {
31945
32042
  const open = this.#host.hasAttribute("open");
31946
32043
  const presentationReady = this.#host.hasAttribute("data-presentation-ready");
31947
- const launcher = this.#host.shadowRoot?.querySelector(".launcher");
31948
- launcher?.setAttribute("aria-expanded", String(open && presentationReady));
31949
- launcher?.setAttribute("aria-busy", String(open && !presentationReady));
31950
- if (launcher) launcher.disabled = open && !presentationReady;
32044
+ const publicConfigurationLoading = this.#host.hasAttribute("data-public-configuration-loading");
32045
+ const presentationBlocked = !presentationReady && (open || publicConfigurationLoading);
32046
+ const launcher = this.#host.shadowRoot?.querySelector(".launcher-trigger");
32047
+ launcher?.setAttribute("aria-busy", String(presentationBlocked));
32048
+ if (launcher) launcher.disabled = presentationBlocked;
32049
+ this.#host.shadowRoot?.querySelector(".panel")?.setAttribute("aria-hidden", String(!(open && presentationReady)));
31951
32050
  if (open && this.#host.isConnected) this.#primeSession();
31952
32051
  }
31953
32052
  reveal() {
@@ -31958,6 +32057,233 @@ var AssistantElementPresentationGate = class {
31958
32057
  }
31959
32058
  };
31960
32059
 
32060
+ // src/assistant-configuration-schema.ts
32061
+ var hexColorSchema = external_exports.string().regex(/^#[0-9A-Fa-f]{6}$/);
32062
+ var browserAssetUrlSchema = external_exports.string().trim().url().refine(isSecureBrowserAssetUrl, "must use https (http is allowed only for loopback testing)");
32063
+ var httpsUrlSchema = external_exports.string().trim().url().refine(isHttpsUrl2, "must use https");
32064
+ var brandAssetSchema = external_exports.object({
32065
+ uri: browserAssetUrlSchema,
32066
+ darkUri: browserAssetUrlSchema.optional(),
32067
+ alt: external_exports.string().trim().min(1).max(240)
32068
+ }).strict();
32069
+ var brandThemeSchema = external_exports.object({
32070
+ surface: hexColorSchema.optional(),
32071
+ surfaceRaised: hexColorSchema.optional(),
32072
+ surfaceMuted: hexColorSchema.optional(),
32073
+ text: hexColorSchema.optional(),
32074
+ textMuted: hexColorSchema.optional(),
32075
+ accent: hexColorSchema.optional(),
32076
+ accentText: hexColorSchema.optional(),
32077
+ link: hexColorSchema.optional(),
32078
+ border: hexColorSchema.optional(),
32079
+ borderStrong: hexColorSchema.optional(),
32080
+ focus: hexColorSchema.optional(),
32081
+ success: hexColorSchema.optional(),
32082
+ warning: hexColorSchema.optional(),
32083
+ danger: hexColorSchema.optional(),
32084
+ code: hexColorSchema.optional()
32085
+ }).strict();
32086
+ var brandingSchema = external_exports.object({
32087
+ name: external_exports.string().trim().min(1).max(160).optional(),
32088
+ accent: hexColorSchema.optional(),
32089
+ surface: hexColorSchema.optional(),
32090
+ surfaceDark: hexColorSchema.optional(),
32091
+ logo: brandAssetSchema.optional(),
32092
+ mark: brandAssetSchema.optional(),
32093
+ avatar: brandAssetSchema.optional(),
32094
+ theme: external_exports.object({ light: brandThemeSchema.optional(), dark: brandThemeSchema.optional() }).strict().optional(),
32095
+ radius: external_exports.enum(["none", "sm", "md", "lg"]).optional(),
32096
+ density: external_exports.enum(["compact", "comfortable"]).optional(),
32097
+ typography: external_exports.enum(["system", "serif", "mono"]).optional(),
32098
+ colorScheme: external_exports.enum(["auto", "light", "dark"]).optional()
32099
+ }).strict();
32100
+ var labelsSchema = external_exports.object({
32101
+ welcomeHeading: external_exports.string().trim().min(1).max(160).optional(),
32102
+ welcomeMessage: external_exports.string().max(1e3).optional(),
32103
+ launcherPlaceholder: external_exports.string().trim().min(1).max(160).optional(),
32104
+ composerPlaceholder: external_exports.string().trim().min(1).max(160).optional(),
32105
+ thinking: external_exports.string().trim().min(1).max(80).optional(),
32106
+ send: external_exports.string().trim().min(1).max(80).optional(),
32107
+ stop: external_exports.string().trim().min(1).max(80).optional(),
32108
+ close: external_exports.string().trim().min(1).max(80).optional(),
32109
+ open: external_exports.string().trim().min(1).max(80).optional(),
32110
+ confirm: external_exports.string().trim().min(1).max(80).optional(),
32111
+ decline: external_exports.string().trim().min(1).max(80).optional(),
32112
+ cancel: external_exports.string().trim().min(1).max(80).optional(),
32113
+ confirmationHeading: external_exports.string().trim().min(1).max(120).optional(),
32114
+ additionalDetails: external_exports.string().trim().min(1).max(120).optional(),
32115
+ redacted: external_exports.string().trim().min(1).max(120).optional(),
32116
+ completed: external_exports.string().trim().min(1).max(80).optional(),
32117
+ stopped: external_exports.string().trim().min(1).max(80).optional(),
32118
+ copy: external_exports.string().trim().min(1).max(80).optional(),
32119
+ newMessages: external_exports.string().trim().min(1).max(80).optional(),
32120
+ reconnect: external_exports.string().trim().min(1).max(80).optional(),
32121
+ newConversation: external_exports.string().trim().min(1).max(80).optional(),
32122
+ retry: external_exports.string().trim().min(1).max(80).optional(),
32123
+ unavailable: external_exports.string().trim().min(1).max(240).optional(),
32124
+ sessionExpired: external_exports.string().trim().min(1).max(240).optional(),
32125
+ sessionIdle: external_exports.string().trim().min(1).max(120).optional(),
32126
+ sessionLoading: external_exports.string().trim().min(1).max(120).optional(),
32127
+ sessionReady: external_exports.string().trim().min(1).max(120).optional(),
32128
+ sessionError: external_exports.string().trim().min(1).max(120).optional(),
32129
+ signInHeading: external_exports.string().trim().min(1).max(120).optional(),
32130
+ signInBody: external_exports.string().max(240).optional(),
32131
+ signInAction: external_exports.string().trim().min(1).max(80).optional(),
32132
+ signUpAction: external_exports.string().trim().min(1).max(80).optional()
32133
+ }).strict();
32134
+ var presentationSchema = external_exports.object({
32135
+ panel: external_exports.object({
32136
+ surface: external_exports.enum(["solid", "glass"]).optional(),
32137
+ elevation: external_exports.enum(["soft", "dramatic"]).optional(),
32138
+ border: external_exports.enum(["subtle", "strong"]).optional(),
32139
+ radius: external_exports.number().int().min(0).max(64).optional()
32140
+ }).strict().optional(),
32141
+ launcher: external_exports.object({
32142
+ style: external_exports.enum(["pill", "bubble"]).optional(),
32143
+ icon: external_exports.enum(["brand-mark", "chat", "none"]).optional(),
32144
+ size: external_exports.enum(["md", "lg"]).optional(),
32145
+ status: external_exports.enum(["none", "session"]).optional(),
32146
+ effect: external_exports.enum(["none", "pulse"]).optional()
32147
+ }).strict().optional(),
32148
+ header: external_exports.object({
32149
+ mark: external_exports.enum(["none", "brand-mark", "status"]).optional(),
32150
+ badge: external_exports.object({
32151
+ text: external_exports.string().trim().min(1).max(80),
32152
+ tone: external_exports.enum(["neutral", "success", "warning", "danger"]).optional(),
32153
+ indicator: external_exports.boolean().optional()
32154
+ }).strict().optional()
32155
+ }).strict().optional(),
32156
+ composer: external_exports.object({
32157
+ leadingIcon: external_exports.enum(["none", "brand-mark"]).optional(),
32158
+ sendIcon: external_exports.enum(["arrow-up", "paper-plane"]).optional(),
32159
+ shape: external_exports.enum(["rounded", "pill"]).optional()
32160
+ }).strict().optional(),
32161
+ messages: external_exports.object({
32162
+ userStyle: external_exports.enum(["bubble", "accent"]).optional(),
32163
+ assistantStyle: external_exports.enum(["plain", "bubble"]).optional()
32164
+ }).strict().optional()
32165
+ }).strict();
32166
+ var assistantUiSchema = external_exports.object({
32167
+ theme: external_exports.enum(["auto", "light", "dark", "invert"]).optional(),
32168
+ layout: external_exports.object({
32169
+ mode: external_exports.enum(["floating", "inline", "drawer"]).optional(),
32170
+ position: external_exports.enum(["bottom-left", "bottom-center", "bottom-right"]).optional(),
32171
+ panelWidth: external_exports.number().int().min(280).max(1200).optional(),
32172
+ panelMinHeight: external_exports.number().int().min(240).max(1600).optional(),
32173
+ panelMaxHeight: external_exports.number().int().min(320).max(2400).optional(),
32174
+ edgeOffset: external_exports.number().int().min(0).max(96).optional(),
32175
+ zIndex: external_exports.number().int().min(0).max(2147483647).optional(),
32176
+ density: external_exports.enum(["compact", "comfortable"]).optional(),
32177
+ mobileFullscreen: external_exports.boolean().optional()
32178
+ }).strict().optional(),
32179
+ behavior: external_exports.object({
32180
+ startOpen: external_exports.boolean().optional(),
32181
+ closeOnEscape: external_exports.boolean().optional(),
32182
+ closeOnOutsideClick: external_exports.boolean().optional(),
32183
+ showLauncher: external_exports.boolean().optional(),
32184
+ showHeader: external_exports.boolean().optional(),
32185
+ showAvatars: external_exports.boolean().optional(),
32186
+ showTimestamps: external_exports.boolean().optional(),
32187
+ showPoweredBy: external_exports.boolean().optional()
32188
+ }).strict().optional(),
32189
+ labels: labelsSchema.optional(),
32190
+ presentation: presentationSchema.optional(),
32191
+ suggestedPrompts: external_exports.array(external_exports.string().trim().min(1).max(240)).max(8).optional(),
32192
+ privacyUrl: httpsUrlSchema.optional(),
32193
+ termsUrl: httpsUrlSchema.optional(),
32194
+ locale: external_exports.string().trim().min(2).max(35).optional(),
32195
+ direction: external_exports.enum(["ltr", "rtl", "auto"]).optional()
32196
+ }).strict();
32197
+ var assistantConfigurationSchema = external_exports.object({ branding: brandingSchema.optional(), assistant: assistantUiSchema.optional() }).strict();
32198
+ function parseAssistantConfiguration(value) {
32199
+ const parsed = assistantConfigurationSchema.safeParse(value);
32200
+ return parsed.success ? parsed.data : void 0;
32201
+ }
32202
+ function isHttpsUrl2(value) {
32203
+ try {
32204
+ return new URL(value).protocol === "https:";
32205
+ } catch {
32206
+ return false;
32207
+ }
32208
+ }
32209
+ function isSecureBrowserAssetUrl(value) {
32210
+ try {
32211
+ const url2 = new URL(value);
32212
+ if (url2.protocol === "https:") return true;
32213
+ if (url2.protocol !== "http:") return false;
32214
+ return url2.hostname === "localhost" || url2.hostname === "127.0.0.1" || url2.hostname === "[::1]";
32215
+ } catch {
32216
+ return false;
32217
+ }
32218
+ }
32219
+
32220
+ // src/element-public-configuration-controller.ts
32221
+ var AssistantElementPublicConfigurationController = class {
32222
+ #source;
32223
+ #apply;
32224
+ #loadingChanged;
32225
+ #generation = 0;
32226
+ #bootstrap;
32227
+ #abort;
32228
+ constructor(source, apply2, loadingChanged) {
32229
+ this.#source = source;
32230
+ this.#apply = apply2;
32231
+ this.#loadingChanged = loadingChanged;
32232
+ }
32233
+ connect() {
32234
+ this.#restart();
32235
+ }
32236
+ refresh() {
32237
+ this.#restart();
32238
+ }
32239
+ disconnect() {
32240
+ this.#generation += 1;
32241
+ this.#abort?.abort();
32242
+ this.#abort = void 0;
32243
+ this.#bootstrap = void 0;
32244
+ }
32245
+ #restart() {
32246
+ this.disconnect();
32247
+ this.#prime();
32248
+ }
32249
+ #prime() {
32250
+ const source = this.#source();
32251
+ if (!source.embedId || this.#bootstrap) return;
32252
+ const generation = this.#generation;
32253
+ const abort = new AbortController();
32254
+ this.#abort = abort;
32255
+ const fetcher = source.fetch ?? ((input, init) => globalThis.fetch(input, init));
32256
+ this.#loadingChanged(true);
32257
+ const bootstrap = Promise.resolve().then(
32258
+ () => fetcher(publicConfigurationUrl(source.serviceUrl, source.embedId), {
32259
+ method: "GET",
32260
+ headers: { accept: "application/json" },
32261
+ credentials: "omit",
32262
+ cache: "no-cache",
32263
+ signal: abort.signal
32264
+ })
32265
+ ).then(async (response) => {
32266
+ if (!response.ok) return;
32267
+ const value = await response.json();
32268
+ if (generation !== this.#generation || !isRecord6(value)) {
32269
+ return;
32270
+ }
32271
+ const configuration = parseAssistantConfiguration(value.configuration);
32272
+ if (configuration !== void 0) this.#apply(configuration);
32273
+ }).catch(() => void 0).finally(() => {
32274
+ if (generation === this.#generation) this.#loadingChanged(false);
32275
+ if (this.#bootstrap === bootstrap) {
32276
+ this.#bootstrap = void 0;
32277
+ this.#abort = void 0;
32278
+ }
32279
+ });
32280
+ this.#bootstrap = bootstrap;
32281
+ }
32282
+ };
32283
+ function isRecord6(value) {
32284
+ return typeof value === "object" && value !== null && !Array.isArray(value);
32285
+ }
32286
+
31961
32287
  // src/element-scroll-controller.ts
31962
32288
  var AssistantElementScrollController = class {
31963
32289
  #region;
@@ -32087,44 +32413,69 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
32087
32413
  *, *::before, *::after { box-sizing: border-box; }
32088
32414
  .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; }
32089
32415
  .visually-hidden[hidden] { display: none !important; }
32090
- :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; }
32091
- :host([data-mode="floating"]) { position: fixed; bottom: 20px; right: 20px; }
32092
- :host([data-mode="floating"][data-position="bottom-left"]) { right: auto; left: 20px; }
32416
+ :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; }
32417
+ :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; }
32418
+ :host([data-mode="floating"]) { position: fixed; }
32419
+ :host([data-mode="floating"][open]) { width: min(var(--ns-assistant-panel-width, var(--ns-assistant-default-panel-width)), 100vw); }
32093
32420
  button { font: inherit; }
32094
32421
  button, textarea, input, select { -webkit-tap-highlight-color: transparent; }
32095
- .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; }
32096
- .launcher:hover { transform: translateY(-2px) scale(1.03); box-shadow: 0 8px 26px rgba(0,0,0,.16); }
32097
- .launcher img { width: 28px; height: 28px; border-radius: 50%; object-fit: contain; }
32098
- .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; }
32422
+ .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; }
32423
+ :host([data-presentation-ready]) .launcher { padding: 10px 12px; }
32424
+ .launcher[hidden] { display: none; }
32425
+ .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); }
32426
+ .launcher-trigger, .launcher-form { min-width: 0; margin: 0; padding: 0; border: 0; background: transparent; font: inherit; }
32427
+ .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; }
32428
+ .launcher-label { padding-left: 6px; white-space: nowrap; font-size: 15px; line-height: 20px; }
32429
+ .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; }
32430
+ .launcher-arrow:hover, .launcher-send:hover { background: var(--ns-assistant-launcher-action-hover, var(--ns-assistant-halo-launcher-action-hover)); }
32431
+ .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; }
32432
+ .launcher-form { display: none; flex: 1; align-items: center; gap: 12px; opacity: 0; pointer-events: none; }
32433
+ .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; }
32434
+ .launcher-input::placeholder { color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); }
32435
+ :host([launcher-expanded]) .launcher { width: min(400px, calc(100vw - 40px)); justify-content: flex-start; }
32436
+ :host([launcher-expanded]) .launcher-trigger { display: none; }
32437
+ :host([launcher-expanded]) .launcher-form { display: flex; opacity: 1; pointer-events: auto; }
32438
+ :host([data-launcher-style="bubble"]) .launcher { gap: 0; min-height: 0; padding: 6px; cursor: pointer; }
32439
+ :host([data-launcher-style="bubble"]) .launcher-trigger { position: absolute; inset: 0; display: block; width: 100%; height: 100%; cursor: pointer; }
32440
+ :host([data-launcher-style="bubble"]) .launcher-label, :host([data-launcher-style="bubble"]) .launcher-arrow, :host([data-launcher-style="bubble"]) .launcher-form { display: none; }
32441
+ :host([data-launcher-style="bubble"]) .launcher-visual { width: 32px; height: 32px; flex-basis: 32px; }
32442
+ .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; }
32443
+ :host([data-session-state="loading"]:not([data-presentation-ready])) .launcher-loader, :host([data-public-configuration-loading]:not([data-presentation-ready])) .launcher-loader { display: block; }
32444
+ :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; }
32445
+ .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; }
32446
+ :host([data-mode="floating"][open]) .panel { width: calc(100% - 40px); margin-inline: 20px; }
32099
32447
  :host([open][data-presentation-ready]) .panel { display: flex; }
32100
32448
  .panel { position: relative; }
32101
32449
  :host([open][data-presentation-ready]) .launcher { display: none; }
32102
32450
  :host([open]:not([data-presentation-ready])) .launcher { cursor: progress; opacity: .72; transform: none; }
32103
- 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); }
32451
+ 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))); }
32104
32452
  header strong { flex: 1; font-size: 16px; font-weight: 600; letter-spacing: 0; }
32105
32453
  .brand-logo { display: block; max-width: 120px; max-height: 28px; object-fit: contain; }
32106
32454
  .close, .send { display: grid; place-items: center; border: 0; border-radius: var(--ns-assistant-button-radius, var(--ns-assistant-default-button-radius)); cursor: pointer; }
32107
- .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; }
32108
- .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; }
32455
+ .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; }
32456
+ .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; }
32109
32457
  .close:hover { color: var(--ns-assistant-text, var(--ns-assistant-default-text)); background: var(--ns-assistant-elevated, var(--ns-assistant-default-elevated)); }
32110
32458
  .close:active { transform: scale(.96); }
32111
- .welcome { flex: 0 0 auto; padding: 30px 22px 18px; }
32459
+ .welcome { flex: 0 0 auto; padding: 24px 20px 8px; }
32112
32460
  .welcome h2 { margin: 0; font-size: 1.35em; line-height: 1.25; letter-spacing: 0; }
32113
32461
  .welcome p { max-width: 42ch; margin: 7px 0 0; color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); }
32114
- :host([has-messages]) .welcome, :host([has-messages]) .suggested-prompts { display: none; }
32115
- .suggested-prompts { display: flex; flex: 0 0 auto; flex-wrap: wrap; gap: 8px; padding: 0 20px 16px; }
32462
+ .welcome:has(.empty-state[hidden]) { display: none; }
32463
+ :host([has-messages]) .welcome { display: none; }
32464
+ .input-section { flex: 0 0 auto; padding: 8px 16px 16px; }
32465
+ .suggested-prompts { display: flex; flex: 0 0 auto; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
32116
32466
  .suggested-prompts:empty { display: none; }
32117
- .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; }
32118
- .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); }
32467
+ .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; }
32468
+ .suggested-prompts button::before { content: ''; position: absolute; inset: -6px -2px; }
32469
+ .suggested-prompts button:hover { color: var(--ns-assistant-text, var(--ns-assistant-default-text)); background: var(--ns-assistant-elevated, var(--ns-assistant-default-elevated)); }
32119
32470
  .messages-region { position: relative; display: flex; flex: 1 1 auto; min-height: 0; overflow: hidden; }
32120
32471
  .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; }
32121
32472
  .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); }
32122
32473
  .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); }
32123
32474
  .messages-region[data-can-scroll-up]::before, .messages-region[data-can-scroll-down]::after { opacity: 1; }
32124
- .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; }
32475
+ .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; }
32125
32476
  .messages::-webkit-scrollbar { width: 6px; }
32126
32477
  .messages::-webkit-scrollbar-thumb { background: var(--ns-assistant-divider, var(--ns-assistant-default-divider)); border-radius: 999px; }
32127
- .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; }
32478
+ .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; }
32128
32479
  .message > span, .message > .markdown { overflow-wrap: anywhere; }
32129
32480
  .message .avatar { float: left; width: 24px; height: 24px; margin-right: 9px; border-radius: 50%; object-fit: cover; }
32130
32481
  .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; }
@@ -32137,7 +32488,7 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
32137
32488
  .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; }
32138
32489
  .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; }
32139
32490
  .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); }
32140
- .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; }
32491
+ .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; }
32141
32492
  .message.user time { margin-top: 0; }
32142
32493
  .markdown { white-space: normal; }
32143
32494
  .markdown p { margin: 0 0 8px; }
@@ -32187,31 +32538,39 @@ var ASSISTANT_ELEMENT_STYLES = `<style>
32187
32538
  .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)); }
32188
32539
  .input-request .proposal-actions { margin-top: 2px; }
32189
32540
  ${ASSISTANT_APP_VIEW_CSS}
32190
- 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; }
32191
- 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)); }
32541
+ .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; }
32542
+ .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); }
32192
32543
  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; }
32193
32544
  textarea::placeholder { color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); }
32194
32545
  textarea:focus, textarea:focus-visible { outline: none; box-shadow: none; }
32195
32546
  button:focus-visible { outline: 3px solid var(--ns-assistant-focus, var(--ns-assistant-default-focus)); outline-offset: 2px; }
32196
- .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; }
32197
- .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; }
32198
- .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%)); }
32547
+ .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; }
32548
+ .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%); }
32549
+ .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; }
32550
+ .send:hover::after { background: var(--ns-assistant-hover, var(--ns-assistant-default-hover)); }
32199
32551
  :host([busy]) .send::before { width: 13px; height: 13px; border-radius: 3px; -webkit-mask: none; mask: none; }
32200
32552
  .send:disabled, textarea:disabled { opacity: .55; cursor: not-allowed; }
32201
- .legal { display: flex; gap: 12px; justify-content: center; padding: 0 12px 9px; font-size: .75em; }
32553
+ .legal { display: flex; gap: 12px; justify-content: center; padding: 0 12px 8px; font-size: .75em; }
32202
32554
  .legal:empty { display: none; }
32203
32555
  .legal a { color: var(--ns-assistant-muted-text, var(--ns-assistant-default-muted-text)); text-underline-offset: 2px; }
32556
+ .powered-by-row { display: flex; flex: 0 0 auto; justify-content: center; padding-bottom: 12px; }
32557
+ .powered-by-row[hidden] { display: none; }
32558
+ .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; }
32559
+ .powered-by:hover { color: var(--ns-assistant-text, var(--ns-assistant-default-text)); opacity: 1; }
32560
+ .powered-by-icon { display: inline-flex; width: 14px; height: 14px; }
32561
+ .powered-by-name { font-weight: 500; }
32204
32562
  ::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; }
32205
- @keyframes halo-panel-in { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }
32563
+ @keyframes halo-panel-in { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: none; } }
32206
32564
  @keyframes halo-message-in { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
32207
32565
  @keyframes halo-shimmer { from { background-position: 100% center; } to { background-position: 0 center; } }
32208
32566
  @keyframes halo-caret-blink { 50% { opacity: 0; } }
32567
+ @keyframes halo-spin { to { transform: rotate(360deg); } }
32209
32568
  :host([data-density="compact"]) .welcome { padding: 20px 18px 12px; }
32210
- @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)); } }
32569
+ @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)); } }
32211
32570
  @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; } }
32212
32571
  @media (forced-colors: active) { .message.thinking > span { color: CanvasText; background: none; } }
32213
32572
  @media (hover: none) { .message-copy { opacity: 1; } }
32214
- @media (max-width: 560px) { :host([data-mode="floating"]) { inset: auto 12px 12px; } :host([open]) .panel { width: calc(100vw - 24px); max-height: calc(100vh - 24px); } }
32573
+ @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); } }
32215
32574
  </style>`;
32216
32575
 
32217
32576
  // src/host-appearance.ts
@@ -32320,6 +32679,7 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
32320
32679
  host.dataset.panelSurface = presentation.panel.surface;
32321
32680
  host.dataset.panelElevation = presentation.panel.elevation;
32322
32681
  host.dataset.panelBorder = presentation.panel.border;
32682
+ host.dataset.launcherStyle = presentation.launcher.style;
32323
32683
  host.dataset.launcherEffect = presentation.launcher.effect;
32324
32684
  host.dataset.messageUserStyle = presentation.messages.userStyle;
32325
32685
  host.dataset.messageAssistantStyle = presentation.messages.assistantStyle;
@@ -32332,11 +32692,11 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
32332
32692
  }
32333
32693
  host.style.setProperty(
32334
32694
  "--ns-assistant-presentation-launcher-size",
32335
- presentation.launcher.size === "lg" ? "56px" : "48px"
32695
+ presentation.launcher.size === "lg" ? "56px" : "44px"
32336
32696
  );
32337
32697
  host.style.setProperty(
32338
32698
  "--ns-assistant-presentation-panel-background",
32339
- 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)"
32699
+ 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)"
32340
32700
  );
32341
32701
  host.style.setProperty(
32342
32702
  "--ns-assistant-presentation-shadow",
@@ -32344,7 +32704,7 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
32344
32704
  );
32345
32705
  host.style.setProperty(
32346
32706
  "--ns-assistant-presentation-border",
32347
- panelBorder(presentation.panel.border)
32707
+ panelBorder(presentation.panel.border, mode)
32348
32708
  );
32349
32709
  replaceFallback(root, "launcher-icon", [launcherVisual(appearance, mode, sessionState)]);
32350
32710
  const accessibleStatus = root.querySelector("[data-session-status]");
@@ -32357,7 +32717,7 @@ function syncAssistantPresentation(host, root, appearance, mode, sessionState) {
32357
32717
  replaceFallback(root, "empty-state", [emptyState(appearance)]);
32358
32718
  replaceFallback(root, "composer-leading", composerLeading(appearance, mode));
32359
32719
  replaceFallback(root, "conversation-footer", []);
32360
- const form = root.querySelector("form");
32720
+ const form = root.querySelector(".composer");
32361
32721
  if (form) form.dataset.shape = presentation.composer.shape;
32362
32722
  const send = root.querySelector(".send");
32363
32723
  if (send) send.dataset.icon = presentation.composer.sendIcon;
@@ -32377,18 +32737,21 @@ function panelShadow(elevation) {
32377
32737
  }
32378
32738
  return "var(--ns-assistant-default-shadow)";
32379
32739
  }
32380
- function panelBorder(border) {
32740
+ function panelBorder(border, mode) {
32381
32741
  if (border === "strong") {
32382
32742
  return "1px solid var(--ns-assistant-divider, var(--ns-assistant-default-divider))";
32383
32743
  }
32384
- return "1px solid color-mix(in srgb, var(--ns-assistant-divider, var(--ns-assistant-default-divider)) 74%, transparent)";
32744
+ const opacity = mode === "light" ? 60 : 100;
32745
+ return `1px solid color-mix(in srgb, var(--ns-assistant-divider, var(--ns-assistant-default-divider)) ${opacity}%, transparent)`;
32385
32746
  }
32386
32747
  function launcherVisual(appearance, mode, sessionState) {
32387
32748
  const launcher = appearance.presentation.launcher;
32388
32749
  const visual = document.createElement("span");
32389
32750
  visual.className = "launcher-visual";
32390
32751
  visual.setAttribute("aria-hidden", "true");
32391
- visual.append(icon(launcher.icon, appearance, mode, "launcher-glyph"));
32752
+ const visualKind = launcher.icon === "brand-mark" && !(appearance.brand.mark[mode] ?? appearance.brand.logo[mode]) ? launcher.style === "bubble" ? "chat" : "none" : launcher.icon;
32753
+ visual.hidden = visualKind === "none";
32754
+ visual.append(icon(visualKind, appearance, mode, "launcher-glyph"));
32392
32755
  if (launcher.status === "session") {
32393
32756
  const status = document.createElement("span");
32394
32757
  status.className = "launcher-status";
@@ -32438,9 +32801,12 @@ function headerBadge(appearance) {
32438
32801
  function emptyState(appearance) {
32439
32802
  const section = document.createElement("section");
32440
32803
  section.className = "empty-state";
32441
- const heading = document.createElement("h2");
32442
- heading.textContent = appearance.labels.welcomeHeading;
32443
- section.append(heading);
32804
+ section.hidden = !appearance.labels.welcomeHeading && !appearance.labels.welcomeMessage;
32805
+ if (appearance.labels.welcomeHeading) {
32806
+ const heading = document.createElement("h2");
32807
+ heading.textContent = appearance.labels.welcomeHeading;
32808
+ section.append(heading);
32809
+ }
32444
32810
  const description = appearance.labels.welcomeMessage;
32445
32811
  if (description) {
32446
32812
  const paragraph = document.createElement("p");
@@ -32467,7 +32833,7 @@ function toneIndicator(tone) {
32467
32833
  return indicator;
32468
32834
  }
32469
32835
  function icon(kind, appearance, mode, className) {
32470
- const mark = appearance.brand.mark[mode];
32836
+ const mark = appearance.brand.mark[mode] ?? appearance.brand.logo[mode];
32471
32837
  if (kind === "brand-mark" && mark) {
32472
32838
  const image = document.createElement("img");
32473
32839
  image.className = className;
@@ -32519,11 +32885,17 @@ function applyAssistantElementTheme(input) {
32519
32885
  var presentationStyles = `<style>
32520
32886
  :host([data-mode="floating"]) {
32521
32887
  bottom: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
32522
- right: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
32888
+ left: 50%;
32889
+ transform: translateX(-50%);
32523
32890
  }
32524
32891
  :host([data-mode="floating"][data-position="bottom-left"]) {
32525
- right: auto;
32526
32892
  left: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
32893
+ transform: none;
32894
+ }
32895
+ :host([data-mode="floating"][data-position="bottom-right"]) {
32896
+ right: var(--ns-assistant-edge-offset, var(--ns-assistant-default-edge-offset));
32897
+ left: auto;
32898
+ transform: none;
32527
32899
  }
32528
32900
  .panel {
32529
32901
  background: var(--ns-assistant-panel-background, var(--ns-assistant-presentation-panel-background));
@@ -32540,22 +32912,26 @@ var presentationStyles = `<style>
32540
32912
 
32541
32913
  .launcher {
32542
32914
  position: relative;
32543
- width: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
32544
- height: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
32545
32915
  border-radius: var(--ns-assistant-launcher-radius, var(--ns-assistant-default-launcher-radius));
32546
32916
  isolation: isolate;
32547
32917
  }
32918
+ :host([data-launcher-style="bubble"]) .launcher {
32919
+ width: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
32920
+ height: var(--ns-assistant-launcher-size, var(--ns-assistant-presentation-launcher-size));
32921
+ }
32548
32922
  :host([data-launcher-effect="pulse"]) .launcher {
32549
32923
  animation: assistant-launcher-pulse 2s ease-in-out infinite;
32550
32924
  }
32551
32925
  .launcher-visual {
32552
32926
  position: relative;
32553
32927
  display: grid;
32554
- width: 100%;
32555
- height: 100%;
32928
+ width: 28px;
32929
+ height: 28px;
32930
+ flex: 0 0 28px;
32556
32931
  place-items: center;
32557
32932
  border-radius: inherit;
32558
32933
  }
32934
+ .launcher-visual[hidden] { display: none; }
32559
32935
  .launcher-glyph, .composer-leading-glyph {
32560
32936
  display: block;
32561
32937
  width: 18px;
@@ -32583,10 +32959,28 @@ var presentationStyles = `<style>
32583
32959
  -webkit-mask: none;
32584
32960
  mask: none;
32585
32961
  }
32962
+ .launcher-visual .launcher-glyph:is(img) {
32963
+ width: 28px;
32964
+ height: 28px;
32965
+ border-radius: 50%;
32966
+ filter: grayscale(100%);
32967
+ opacity: .6;
32968
+ 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);
32969
+ }
32970
+ .launcher:hover .launcher-glyph:is(img), :host([launcher-expanded]) .launcher-glyph:is(img) {
32971
+ filter: none;
32972
+ opacity: 1;
32973
+ }
32974
+ :host([data-launcher-style="bubble"]) .launcher-glyph:is(img) {
32975
+ width: 32px;
32976
+ height: 32px;
32977
+ filter: none;
32978
+ opacity: 1;
32979
+ }
32586
32980
  .launcher-status {
32587
32981
  position: absolute;
32588
- right: 1px;
32589
- bottom: 1px;
32982
+ right: -1px;
32983
+ bottom: -1px;
32590
32984
  width: 10px;
32591
32985
  height: 10px;
32592
32986
  border: 2px solid var(--ns-assistant-panel, var(--ns-assistant-default-panel));
@@ -32715,7 +33109,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32715
33109
  #conversationGeneration = 0;
32716
33110
  #messages;
32717
33111
  #thinking;
32718
- #media;
32719
33112
  #context;
32720
33113
  #modelContext;
32721
33114
  #pageContext;
@@ -32727,11 +33120,17 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32727
33120
  () => this.#primeSession(),
32728
33121
  () => this.focusComposer()
32729
33122
  );
33123
+ #publicConfiguration = new AssistantElementPublicConfigurationController(
33124
+ () => ({ embedId: this.embedId, serviceUrl: this.serviceUrl, fetch: this.#fetch }),
33125
+ (configuration) => this.#applyConfiguration(configuration),
33126
+ (loading) => this.#setPublicConfigurationLoading(loading)
33127
+ );
32730
33128
  #pendingUserEcho;
32731
33129
  #sessionState = "idle";
32732
33130
  #startOpenApplied = false;
32733
33131
  #readyDispatched = false;
32734
33132
  #turnInFlight = false;
33133
+ #fetch;
32735
33134
  #hostAppearance;
32736
33135
  #appearanceWarningKeys = /* @__PURE__ */ new Set();
32737
33136
  #appearanceStyleSnapshot = /* @__PURE__ */ new Map();
@@ -32741,6 +33140,17 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32741
33140
  embedId = "";
32742
33141
  /** Only for a dev or self-hosted service; the published snippet needs no origin. */
32743
33142
  serviceUrl = "";
33143
+ get fetch() {
33144
+ return this.#fetch;
33145
+ }
33146
+ set fetch(value) {
33147
+ if (this.#fetch === value) return;
33148
+ this.#fetch = value;
33149
+ if (this.#client) this.resetSession();
33150
+ if (!this.isConnected) return;
33151
+ if (this.embedId) this.#publicConfiguration.refresh();
33152
+ else this.#primeSession();
33153
+ }
32744
33154
  get appearance() {
32745
33155
  return this.#hostAppearance;
32746
33156
  }
@@ -32759,11 +33169,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32759
33169
  }
32760
33170
  return this.#appendMessage(role, text3);
32761
33171
  },
32762
- applyConfiguration: (configuration) => {
32763
- this.#appearance = resolveAppearance(configuration);
32764
- this.#syncAppearance();
32765
- this.#applyStartOpenPolicy();
32766
- },
33172
+ applyConfiguration: (configuration) => this.#applyConfiguration(configuration),
32767
33173
  labels: () => this.#appearance.labels,
32768
33174
  respond: (id, response) => this.respond(id, response),
32769
33175
  dispatchError: (detail) => this.#dispatchError(detail),
@@ -32779,7 +33185,7 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32779
33185
  client,
32780
33186
  sendMessage: (message) => this.sendMessage(message),
32781
33187
  updateModelContext: (update) => this.updateModelContext(update),
32782
- theme: this.#resolvedMode()
33188
+ theme: this.#environment.resolvedTheme()
32783
33189
  },
32784
33190
  {
32785
33191
  ...sandboxUrl === void 0 ? {} : { sandboxUrl },
@@ -32792,18 +33198,28 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32792
33198
  this.#revealLatest();
32793
33199
  }
32794
33200
  });
33201
+ #launcher = new AssistantElementLauncherController({
33202
+ host: this,
33203
+ appearance: () => this.#appearance,
33204
+ openPanel: () => this.open(),
33205
+ sendMessage: (message) => this.#sendGuarded(message)
33206
+ });
33207
+ #environment = new AssistantElementHostEnvironmentController({
33208
+ host: this,
33209
+ appearance: () => this.#appearance,
33210
+ applyTheme: () => this.#applyTheme(),
33211
+ close: () => this.#closeAndRestoreFocus()
33212
+ });
32795
33213
  connectedCallback() {
32796
33214
  if (!this.shadowRoot) this.attachShadow({ mode: "open" });
32797
33215
  if (!this.sessionEndpoint) this.sessionEndpoint = this.getAttribute("session-endpoint") ?? "";
32798
33216
  if (!this.embedId) this.embedId = this.getAttribute("embed-id") ?? "";
32799
33217
  if (!this.serviceUrl) this.serviceUrl = this.getAttribute("service-url") ?? "";
32800
33218
  if (!this.sessionEndpoint && !this.embedId) this.#presentationGate.reveal();
32801
- this.#media = globalThis.matchMedia?.("(prefers-color-scheme: dark)");
32802
- this.#media?.addEventListener("change", this.#handleSystemTheme);
32803
- document.addEventListener("keydown", this.#handleDocumentKeydown);
32804
- document.addEventListener("pointerdown", this.#handleDocumentPointerDown);
33219
+ this.#environment.connect();
32805
33220
  if (this.#appearance.behavior.startOpen) this.#applyStartOpenPolicy();
32806
33221
  this.#render();
33222
+ if (this.embedId) this.#publicConfiguration.connect();
32807
33223
  if (!this.#readyDispatched) {
32808
33224
  this.#readyDispatched = true;
32809
33225
  this.dispatchEvent(new CustomEvent("assistant-ready"));
@@ -32824,22 +33240,25 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32824
33240
  this.#clientEndpoint = "";
32825
33241
  this.#unsubscribeClient = void 0;
32826
33242
  this.#sessionBootstrap = void 0;
33243
+ this.#publicConfiguration.disconnect();
32827
33244
  this.#scroll.disconnect();
33245
+ this.#launcher.disconnect();
32828
33246
  this.#setBusy(false);
32829
- this.#media?.removeEventListener("change", this.#handleSystemTheme);
32830
- document.removeEventListener("keydown", this.#handleDocumentKeydown);
32831
- document.removeEventListener("pointerdown", this.#handleDocumentPointerDown);
33247
+ this.#environment.disconnect();
32832
33248
  }
32833
33249
  attributeChangedCallback(name21) {
32834
33250
  if (name21 === "theme") this.#applyTheme();
32835
33251
  if (name21 === "open") this.#presentationGate.syncOpenState();
32836
33252
  }
32837
33253
  open() {
33254
+ this.#launcher.collapse(true);
32838
33255
  this.setAttribute("open", "");
33256
+ if (this.hasAttribute("data-presentation-ready")) queueMicrotask(() => this.focusComposer());
32839
33257
  this.dispatchEvent(new CustomEvent("assistant-open"));
32840
33258
  }
32841
33259
  close() {
32842
33260
  this.removeAttribute("open");
33261
+ this.#launcher.collapse(true);
32843
33262
  this.dispatchEvent(new CustomEvent("assistant-close"));
32844
33263
  }
32845
33264
  toggle() {
@@ -32976,17 +33395,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
32976
33395
  if (generation === this.#conversationGeneration) this.#setBusy(false);
32977
33396
  }
32978
33397
  }
32979
- #handleSystemTheme = () => this.#applyTheme();
32980
- #handleDocumentKeydown = (event) => {
32981
- if (event.key === "Escape" && this.hasAttribute("open") && this.#appearance.behavior.closeOnEscape) {
32982
- this.#closeAndRestoreFocus();
32983
- }
32984
- };
32985
- #handleDocumentPointerDown = (event) => {
32986
- if (this.hasAttribute("open") && this.#appearance.behavior.closeOnOutsideClick && !event.composedPath().includes(this)) {
32987
- this.close();
32988
- }
32989
- };
32990
33398
  #ensureClient() {
32991
33399
  const key = sessionSourceKey(this);
32992
33400
  if (this.#client && this.#clientEndpoint === key) return this.#client;
@@ -33002,8 +33410,13 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33002
33410
  const client = this.embedId ? createAssistantClient({
33003
33411
  embedId: this.embedId,
33004
33412
  ...this.serviceUrl ? { serviceUrl: this.serviceUrl } : {},
33413
+ ...this.#fetch ? { fetch: this.#fetch } : {},
33414
+ ...behavior
33415
+ }) : createAssistantClient({
33416
+ sessionEndpoint: this.sessionEndpoint,
33417
+ ...this.#fetch ? { fetch: this.#fetch } : {},
33005
33418
  ...behavior
33006
- }) : createAssistantClient({ sessionEndpoint: this.sessionEndpoint, ...behavior });
33419
+ });
33007
33420
  this.#client = client;
33008
33421
  this.#clientEndpoint = key;
33009
33422
  this.#unsubscribeClient = client.subscribe((event) => {
@@ -33032,6 +33445,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33032
33445
  this.dispatchEvent(new CustomEvent("assistant-event", { detail: event }));
33033
33446
  this.#events.handle(event);
33034
33447
  if (event.event === "session_started") {
33448
+ this.#publicConfiguration.disconnect();
33449
+ this.removeAttribute("data-public-configuration-loading");
33035
33450
  this.#presentationGate.reveal();
33036
33451
  this.#setSessionState("ready");
33037
33452
  this.#messages?.querySelector(".conversation-error")?.remove();
@@ -33081,10 +33496,10 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33081
33496
  if (!this.#messages) return void 0;
33082
33497
  const message = document.createElement("div");
33083
33498
  message.className = `message ${role}`;
33084
- if (role === "assistant" && this.#appearance.behavior.showAvatars && this.#appearance.brand.avatar[this.#resolvedMode()]) {
33499
+ if (role === "assistant" && this.#appearance.behavior.showAvatars && this.#appearance.brand.avatar[this.#environment.resolvedTheme()]) {
33085
33500
  const avatar = document.createElement("img");
33086
33501
  avatar.className = "avatar";
33087
- avatar.src = this.#appearance.brand.avatar[this.#resolvedMode()] ?? "";
33502
+ avatar.src = this.#appearance.brand.avatar[this.#environment.resolvedTheme()] ?? "";
33088
33503
  avatar.alt = "";
33089
33504
  message.append(avatar);
33090
33505
  }
@@ -33165,12 +33580,8 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33165
33580
  this.#primeSession();
33166
33581
  this.dispatchEvent(new CustomEvent("assistant-new-conversation"));
33167
33582
  }
33168
- #resolvedMode() {
33169
- const configured = this.getAttribute("theme") ?? this.#appearance.theme.defaultMode;
33170
- return configured === "auto" ? this.#media?.matches ? "dark" : "light" : configured;
33171
- }
33172
33583
  #applyTheme() {
33173
- const mode = this.#resolvedMode();
33584
+ const mode = this.#environment.resolvedTheme();
33174
33585
  for (const warning of applyAssistantElementTheme({
33175
33586
  host: this,
33176
33587
  root: this.shadowRoot,
@@ -33193,13 +33604,23 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33193
33604
  updateAssistantAppTheme(app, mode);
33194
33605
  }
33195
33606
  }
33607
+ #applyConfiguration(configuration) {
33608
+ this.#appearance = resolveAppearance(configuration);
33609
+ this.#syncAppearance();
33610
+ this.#applyStartOpenPolicy();
33611
+ }
33612
+ #setPublicConfigurationLoading(loading) {
33613
+ this.toggleAttribute("data-public-configuration-loading", loading);
33614
+ if (loading) this.#presentationGate.syncOpenState();
33615
+ else this.#presentationGate.reveal();
33616
+ }
33196
33617
  #setSessionState(state) {
33197
33618
  this.#sessionState = state;
33198
33619
  syncAssistantSessionVisualState(this, this.shadowRoot, state, this.#appearance);
33199
33620
  }
33200
33621
  #closeAndRestoreFocus() {
33201
33622
  this.close();
33202
- this.shadowRoot?.querySelector(".launcher")?.focus();
33623
+ this.#launcher.focusTrigger();
33203
33624
  }
33204
33625
  #applyStartOpenPolicy() {
33205
33626
  if (this.#startOpenApplied) return;
@@ -33225,10 +33646,6 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33225
33646
  textarea.placeholder = appearance.labels.composerPlaceholder;
33226
33647
  textarea.setAttribute("aria-label", appearance.labels.composerPlaceholder);
33227
33648
  queryRequired(this.shadowRoot, ".send").textContent = appearance.labels.send;
33228
- queryRequired(this.shadowRoot, ".launcher").setAttribute(
33229
- "aria-label",
33230
- appearance.labels.open
33231
- );
33232
33649
  queryRequired(this.shadowRoot, ".close").setAttribute(
33233
33650
  "aria-label",
33234
33651
  appearance.labels.close
@@ -33242,11 +33659,11 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33242
33659
  button2.addEventListener("click", () => this.#sendGuarded(prompt));
33243
33660
  prompts.append(button2);
33244
33661
  }
33245
- queryRequired(this.shadowRoot, ".launcher").hidden = !appearance.behavior.showLauncher;
33246
33662
  queryRequired(this.shadowRoot, "header").hidden = !appearance.behavior.showHeader;
33247
33663
  const legal = queryRequired(this.shadowRoot, ".legal");
33248
33664
  legal.replaceChildren();
33249
33665
  appendLegalLinks(legal, this.#appearance);
33666
+ this.#launcher.sync();
33250
33667
  this.#applyTheme();
33251
33668
  }
33252
33669
  #render() {
@@ -33264,12 +33681,11 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33264
33681
  this.toggleAttribute("mobile-fullscreen", appearance.layout.mobileFullscreen);
33265
33682
  this.shadowRoot.innerHTML = assistantElementMarkup(
33266
33683
  appearance,
33267
- this.hasAttribute("open"),
33268
33684
  `${ASSISTANT_ELEMENT_STYLES}${presentationStyles}`
33269
33685
  );
33270
33686
  queryRequired(this.shadowRoot, "strong").textContent = appearance.brand.name;
33271
33687
  const logo = queryRequired(this.shadowRoot, ".brand-logo");
33272
- const logoUrl = appearance.brand.logo[this.#resolvedMode()];
33688
+ const logoUrl = appearance.brand.logo[this.#environment.resolvedTheme()];
33273
33689
  if (logoUrl) {
33274
33690
  logo.src = logoUrl;
33275
33691
  logo.alt = appearance.brand.name;
@@ -33297,12 +33713,9 @@ var NoodleAssistantElement = class extends HTMLElementBase {
33297
33713
  this.#messages = this.shadowRoot.querySelector(".messages") ?? void 0;
33298
33714
  this.#messages?.append(...conversation);
33299
33715
  this.#thinking = this.#messages?.querySelector(".thinking") ?? void 0;
33300
- this.shadowRoot.querySelector(".launcher")?.addEventListener("click", () => {
33301
- this.open();
33302
- this.focusComposer();
33303
- });
33716
+ this.#launcher.connect(this.shadowRoot);
33304
33717
  this.shadowRoot.querySelector(".close")?.addEventListener("click", () => this.#closeAndRestoreFocus());
33305
- const form = queryRequired(this.shadowRoot, "form");
33718
+ const form = queryRequired(this.shadowRoot, ".composer");
33306
33719
  form.addEventListener("submit", (event) => {
33307
33720
  event.preventDefault();
33308
33721
  if (this.hasAttribute("busy")) {