open-chat-studio-widget 0.9.1 → 0.10.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 (33) hide show
  1. package/dist/cjs/{index-fFSp-Z_h.js → index-DMXmZhVD.js} +3 -3
  2. package/dist/cjs/index-DMXmZhVD.js.map +1 -0
  3. package/dist/cjs/loader.cjs.js +2 -2
  4. package/dist/cjs/open-chat-studio-widget.cjs.entry.js +1251 -314
  5. package/dist/cjs/open-chat-studio-widget.cjs.entry.js.map +1 -1
  6. package/dist/cjs/open-chat-studio-widget.cjs.js +2 -2
  7. package/dist/cjs/open-chat-studio-widget.entry.cjs.js.map +1 -1
  8. package/dist/collection/components/ocs-chat/ocs-chat.js +90 -5
  9. package/dist/collection/components/ocs-chat/ocs-chat.js.map +1 -1
  10. package/dist/collection/services/chat-session-service.js +5 -0
  11. package/dist/collection/services/chat-session-service.js.map +1 -1
  12. package/dist/components/open-chat-studio-widget.js +1251 -313
  13. package/dist/components/open-chat-studio-widget.js.map +1 -1
  14. package/dist/esm/{index-ythTKHg-.js → index-JDApwJx_.js} +3 -3
  15. package/dist/esm/index-JDApwJx_.js.map +1 -0
  16. package/dist/esm/loader.js +3 -3
  17. package/dist/esm/open-chat-studio-widget.entry.js +1251 -314
  18. package/dist/esm/open-chat-studio-widget.entry.js.map +1 -1
  19. package/dist/esm/open-chat-studio-widget.js +3 -3
  20. package/dist/open-chat-studio-widget/open-chat-studio-widget.entry.esm.js.map +1 -1
  21. package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js +1 -1
  22. package/dist/open-chat-studio-widget/{p-ythTKHg-.js → p-JDApwJx_.js} +2 -2
  23. package/dist/open-chat-studio-widget/p-JDApwJx_.js.map +1 -0
  24. package/dist/open-chat-studio-widget/p-c2d3b2d1.entry.js +4 -0
  25. package/dist/open-chat-studio-widget/p-c2d3b2d1.entry.js.map +1 -0
  26. package/dist/types/components/ocs-chat/ocs-chat.d.ts +15 -0
  27. package/dist/types/services/chat-session-service.d.ts +2 -0
  28. package/package.json +2 -2
  29. package/dist/cjs/index-fFSp-Z_h.js.map +0 -1
  30. package/dist/esm/index-ythTKHg-.js.map +0 -1
  31. package/dist/open-chat-studio-widget/p-2d31a15c.entry.js +0 -4
  32. package/dist/open-chat-studio-widget/p-2d31a15c.entry.js.map +0 -1
  33. package/dist/open-chat-studio-widget/p-ythTKHg-.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-fFSp-Z_h.js');
3
+ var index = require('./index-DMXmZhVD.js');
4
4
 
5
5
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
6
6
  const OcsWidgetAvatar = () => {
@@ -2944,24 +2944,65 @@ marked.Slugger = Slugger;
2944
2944
  marked.Hooks = Hooks;
2945
2945
  marked.parse = marked;
2946
2946
 
2947
- /*! @license DOMPurify 3.4.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.0/LICENSE */
2948
-
2949
- const {
2950
- entries,
2951
- setPrototypeOf,
2952
- isFrozen,
2953
- getPrototypeOf,
2954
- getOwnPropertyDescriptor
2955
- } = Object;
2956
- let {
2957
- freeze,
2958
- seal,
2959
- create
2960
- } = Object; // eslint-disable-line import/no-mutable-exports
2961
- let {
2962
- apply,
2963
- construct
2964
- } = typeof Reflect !== 'undefined' && Reflect;
2947
+ /*! @license DOMPurify 3.4.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.11/LICENSE */
2948
+
2949
+ function _arrayLikeToArray(r, a) {
2950
+ (null == a || a > r.length) && (a = r.length);
2951
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
2952
+ return n;
2953
+ }
2954
+ function _arrayWithHoles(r) {
2955
+ if (Array.isArray(r)) return r;
2956
+ }
2957
+ function _iterableToArrayLimit(r, l) {
2958
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
2959
+ if (null != t) {
2960
+ var e,
2961
+ n,
2962
+ i,
2963
+ u,
2964
+ a = [],
2965
+ f = true,
2966
+ o = false;
2967
+ try {
2968
+ if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = true);
2969
+ } catch (r) {
2970
+ o = true, n = r;
2971
+ } finally {
2972
+ try {
2973
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
2974
+ } finally {
2975
+ if (o) throw n;
2976
+ }
2977
+ }
2978
+ return a;
2979
+ }
2980
+ }
2981
+ function _nonIterableRest() {
2982
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
2983
+ }
2984
+ function _slicedToArray(r, e) {
2985
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
2986
+ }
2987
+ function _unsupportedIterableToArray(r, a) {
2988
+ if (r) {
2989
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
2990
+ var t = {}.toString.call(r).slice(8, -1);
2991
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
2992
+ }
2993
+ }
2994
+
2995
+ const entries = Object.entries,
2996
+ setPrototypeOf = Object.setPrototypeOf,
2997
+ isFrozen = Object.isFrozen,
2998
+ getPrototypeOf = Object.getPrototypeOf,
2999
+ getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
3000
+ let freeze = Object.freeze,
3001
+ seal = Object.seal,
3002
+ create = Object.create; // eslint-disable-line import/no-mutable-exports
3003
+ let _ref = typeof Reflect !== 'undefined' && Reflect,
3004
+ apply = _ref.apply,
3005
+ construct = _ref.construct;
2965
3006
  if (!freeze) {
2966
3007
  freeze = function freeze(x) {
2967
3008
  return x;
@@ -2993,13 +3034,19 @@ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
2993
3034
  const arrayPop = unapply(Array.prototype.pop);
2994
3035
  const arrayPush = unapply(Array.prototype.push);
2995
3036
  const arraySplice = unapply(Array.prototype.splice);
3037
+ const arrayIsArray = Array.isArray;
2996
3038
  const stringToLowerCase = unapply(String.prototype.toLowerCase);
2997
3039
  const stringToString = unapply(String.prototype.toString);
2998
3040
  const stringMatch = unapply(String.prototype.match);
2999
3041
  const stringReplace = unapply(String.prototype.replace);
3000
3042
  const stringIndexOf = unapply(String.prototype.indexOf);
3001
3043
  const stringTrim = unapply(String.prototype.trim);
3044
+ const numberToString = unapply(Number.prototype.toString);
3045
+ const booleanToString = unapply(Boolean.prototype.toString);
3046
+ const bigintToString = typeof BigInt === 'undefined' ? null : unapply(BigInt.prototype.toString);
3047
+ const symbolToString = typeof Symbol === 'undefined' ? null : unapply(Symbol.prototype.toString);
3002
3048
  const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
3049
+ const objectToString = unapply(Object.prototype.toString);
3003
3050
  const regExpTest = unapply(RegExp.prototype.test);
3004
3051
  const typeErrorCreate = unconstruct(TypeError);
3005
3052
  /**
@@ -3049,6 +3096,9 @@ function addToSet(set, array) {
3049
3096
  // Prevent prototype setters from intercepting set as a this value.
3050
3097
  setPrototypeOf(set, null);
3051
3098
  }
3099
+ if (!arrayIsArray(array)) {
3100
+ return set;
3101
+ }
3052
3102
  let l = array.length;
3053
3103
  while (l--) {
3054
3104
  let element = array[l];
@@ -3089,10 +3139,13 @@ function cleanArray(array) {
3089
3139
  */
3090
3140
  function clone(object) {
3091
3141
  const newObject = create(null);
3092
- for (const [property, value] of entries(object)) {
3142
+ for (const _ref2 of entries(object)) {
3143
+ var _ref3 = _slicedToArray(_ref2, 2);
3144
+ const property = _ref3[0];
3145
+ const value = _ref3[1];
3093
3146
  const isPropertyExist = objectHasOwnProperty(object, property);
3094
3147
  if (isPropertyExist) {
3095
- if (Array.isArray(value)) {
3148
+ if (arrayIsArray(value)) {
3096
3149
  newObject[property] = cleanArray(value);
3097
3150
  } else if (value && typeof value === 'object' && value.constructor === Object) {
3098
3151
  newObject[property] = clone(value);
@@ -3103,6 +3156,58 @@ function clone(object) {
3103
3156
  }
3104
3157
  return newObject;
3105
3158
  }
3159
+ /**
3160
+ * Convert non-node values into strings without depending on direct property access.
3161
+ *
3162
+ * @param value - The value to stringify.
3163
+ * @returns A string representation of the provided value.
3164
+ */
3165
+ function stringifyValue(value) {
3166
+ switch (typeof value) {
3167
+ case 'string':
3168
+ {
3169
+ return value;
3170
+ }
3171
+ case 'number':
3172
+ {
3173
+ return numberToString(value);
3174
+ }
3175
+ case 'boolean':
3176
+ {
3177
+ return booleanToString(value);
3178
+ }
3179
+ case 'bigint':
3180
+ {
3181
+ return bigintToString ? bigintToString(value) : '0';
3182
+ }
3183
+ case 'symbol':
3184
+ {
3185
+ return symbolToString ? symbolToString(value) : 'Symbol()';
3186
+ }
3187
+ case 'undefined':
3188
+ {
3189
+ return objectToString(value);
3190
+ }
3191
+ case 'function':
3192
+ case 'object':
3193
+ {
3194
+ if (value === null) {
3195
+ return objectToString(value);
3196
+ }
3197
+ const valueAsRecord = value;
3198
+ const valueToString = lookupGetter(valueAsRecord, 'toString');
3199
+ if (typeof valueToString === 'function') {
3200
+ const stringified = valueToString(valueAsRecord);
3201
+ return typeof stringified === 'string' ? stringified : objectToString(stringified);
3202
+ }
3203
+ return objectToString(value);
3204
+ }
3205
+ default:
3206
+ {
3207
+ return objectToString(value);
3208
+ }
3209
+ }
3210
+ }
3106
3211
  /**
3107
3212
  * This method automatically checks if the prop is function or getter and behaves accordingly.
3108
3213
  *
@@ -3128,6 +3233,14 @@ function lookupGetter(object, prop) {
3128
3233
  }
3129
3234
  return fallbackValue;
3130
3235
  }
3236
+ function isRegex(value) {
3237
+ try {
3238
+ regExpTest(value, '');
3239
+ return true;
3240
+ } catch (_unused) {
3241
+ return false;
3242
+ }
3243
+ }
3131
3244
 
3132
3245
  const html$1 = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'search', 'section', 'select', 'shadow', 'slot', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);
3133
3246
  const svg$1 = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'enterkeyhint', 'exportparts', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'inputmode', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'part', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);
@@ -3143,15 +3256,14 @@ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mgly
3143
3256
  const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
3144
3257
  const text = freeze(['#text']);
3145
3258
 
3146
- const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns', 'slot']);
3259
+ const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
3147
3260
  const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
3148
3261
  const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
3149
3262
  const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
3150
3263
 
3151
- // eslint-disable-next-line unicorn/better-regex
3152
- const MUSTACHE_EXPR = seal(/\{\{[\w\W]*|[\w\W]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
3153
- const ERB_EXPR = seal(/<%[\w\W]*|[\w\W]*%>/gm);
3154
- const TMPLIT_EXPR = seal(/\$\{[\w\W]*/gm); // eslint-disable-line unicorn/better-regex
3264
+ const MUSTACHE_EXPR = seal(/{{[\w\W]*|^[\w\W]*}}/g);
3265
+ const ERB_EXPR = seal(/<%[\w\W]*|^[\w\W]*%>/g);
3266
+ const TMPLIT_EXPR = seal(/\${[\w\W]*/g);
3155
3267
  const DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]+$/); // eslint-disable-line no-useless-escape
3156
3268
  const ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
3157
3269
  const IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
@@ -3161,30 +3273,23 @@ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205
3161
3273
  );
3162
3274
  const DOCTYPE_NAME = seal(/^html$/i);
3163
3275
  const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
3276
+ // Markup-significant character probes used by _sanitizeElements.
3277
+ // Shared module-level instances are safe despite the sticky /g flags:
3278
+ // unapply() resets lastIndex for RegExp receivers before every call.
3279
+ const ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
3280
+ const COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
3281
+ const FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
3282
+ const SELF_CLOSING_TAG = seal(/\/>/i);
3164
3283
 
3165
- var EXPRESSIONS = /*#__PURE__*/Object.freeze({
3166
- __proto__: null,
3167
- ARIA_ATTR: ARIA_ATTR,
3168
- ATTR_WHITESPACE: ATTR_WHITESPACE,
3169
- CUSTOM_ELEMENT: CUSTOM_ELEMENT,
3170
- DATA_ATTR: DATA_ATTR,
3171
- DOCTYPE_NAME: DOCTYPE_NAME,
3172
- ERB_EXPR: ERB_EXPR,
3173
- IS_ALLOWED_URI: IS_ALLOWED_URI,
3174
- IS_SCRIPT_OR_DATA: IS_SCRIPT_OR_DATA,
3175
- MUSTACHE_EXPR: MUSTACHE_EXPR,
3176
- TMPLIT_EXPR: TMPLIT_EXPR
3177
- });
3178
-
3179
- /* eslint-disable @typescript-eslint/indent */
3180
3284
  // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
3181
3285
  const NODE_TYPE = {
3182
3286
  element: 1,
3183
3287
  text: 3,
3184
3288
  // Deprecated
3185
- progressingInstruction: 7,
3289
+ processingInstruction: 7,
3186
3290
  comment: 8,
3187
- document: 9};
3291
+ document: 9,
3292
+ documentFragment: 11};
3188
3293
  const getGlobal = function getGlobal() {
3189
3294
  return typeof window === 'undefined' ? null : window;
3190
3295
  };
@@ -3239,10 +3344,25 @@ const _createHooksMap = function _createHooksMap() {
3239
3344
  uponSanitizeShadowNode: []
3240
3345
  };
3241
3346
  };
3347
+ /**
3348
+ * Resolve a set-valued configuration option: a fresh set built from
3349
+ * cfg[key] when it is an own array property (seeded with a clone of
3350
+ * options.base when given, case-normalized via options.transform),
3351
+ * the fallback set otherwise.
3352
+ *
3353
+ * @param cfg the cloned, prototype-free configuration object
3354
+ * @param key the configuration property to read
3355
+ * @param fallback the set to use when the option is absent or not an array
3356
+ * @param options transform and optional base set to merge into
3357
+ * @returns the resolved set
3358
+ */
3359
+ const _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) {
3360
+ return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
3361
+ };
3242
3362
  function createDOMPurify() {
3243
3363
  let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
3244
3364
  const DOMPurify = root => createDOMPurify(root);
3245
- DOMPurify.version = '3.4.0';
3365
+ DOMPurify.version = '3.4.11';
3246
3366
  DOMPurify.removed = [];
3247
3367
  if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
3248
3368
  // Not running in a browser, provide a factory function
@@ -3250,28 +3370,25 @@ function createDOMPurify() {
3250
3370
  DOMPurify.isSupported = false;
3251
3371
  return DOMPurify;
3252
3372
  }
3253
- let {
3254
- document
3255
- } = window;
3373
+ let document = window.document;
3256
3374
  const originalDocument = document;
3257
3375
  const currentScript = originalDocument.currentScript;
3258
- const {
3259
- DocumentFragment,
3260
- HTMLTemplateElement,
3261
- Node,
3262
- Element,
3263
- NodeFilter,
3264
- NamedNodeMap = window.NamedNodeMap || window.MozNamedAttrMap,
3265
- HTMLFormElement,
3266
- DOMParser,
3267
- trustedTypes
3268
- } = window;
3376
+ const HTMLTemplateElement = window.HTMLTemplateElement,
3377
+ Node = window.Node,
3378
+ Element = window.Element,
3379
+ NodeFilter = window.NodeFilter;
3380
+ const DOMParser = window.DOMParser,
3381
+ trustedTypes = window.trustedTypes;
3269
3382
  const ElementPrototype = Element.prototype;
3270
3383
  const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
3271
3384
  const remove = lookupGetter(ElementPrototype, 'remove');
3272
3385
  const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
3273
3386
  const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
3274
3387
  const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
3388
+ const getShadowRoot = lookupGetter(ElementPrototype, 'shadowRoot');
3389
+ const getAttributes = lookupGetter(ElementPrototype, 'attributes');
3390
+ const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
3391
+ const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;
3275
3392
  // As per issue #47, the web-components registry is inherited by a
3276
3393
  // new document created via createHTMLDocument. As per the spec
3277
3394
  // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
@@ -3286,33 +3403,74 @@ function createDOMPurify() {
3286
3403
  }
3287
3404
  let trustedTypesPolicy;
3288
3405
  let emptyHTML = '';
3289
- const {
3290
- implementation,
3291
- createNodeIterator,
3292
- createDocumentFragment,
3293
- getElementsByTagName
3294
- } = document;
3295
- const {
3296
- importNode
3297
- } = originalDocument;
3406
+ // The instance's own internal Trusted Types policy. Unlike a caller-supplied
3407
+ // `TRUSTED_TYPES_POLICY`, this is created at most once — Trusted Types throws
3408
+ // on duplicate policy names — and is the only policy allowed to persist
3409
+ // across configurations and survive `clearConfig()`.
3410
+ let defaultTrustedTypesPolicy;
3411
+ let defaultTrustedTypesPolicyResolved = false;
3412
+ // Tracks whether we are already inside a call to the configured Trusted Types
3413
+ // policy (`createHTML` or `createScriptURL`). If a supplied policy callback
3414
+ // itself calls `DOMPurify.sanitize` (the cause of #1422), `sanitize` would
3415
+ // re-enter the policy and recurse until the stack overflows. We detect that
3416
+ // re-entry and throw a clear, actionable error instead. The guard is shared
3417
+ // across both callbacks, because either one re-entering `sanitize` triggers
3418
+ // the same unbounded recursion.
3419
+ let IN_TRUSTED_TYPES_POLICY = 0;
3420
+ const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy() {
3421
+ if (IN_TRUSTED_TYPES_POLICY > 0) {
3422
+ throw typeErrorCreate('A configured TRUSTED_TYPES_POLICY callback (createHTML or ' + 'createScriptURL) must not call DOMPurify.sanitize, as that causes ' + 'infinite recursion. Do not pass a policy whose callbacks wrap ' + 'DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted ' + 'Types" section of the README.');
3423
+ }
3424
+ };
3425
+ const _createTrustedHTML = function _createTrustedHTML(html) {
3426
+ _assertNotInTrustedTypesPolicy();
3427
+ IN_TRUSTED_TYPES_POLICY++;
3428
+ try {
3429
+ return trustedTypesPolicy.createHTML(html);
3430
+ } finally {
3431
+ IN_TRUSTED_TYPES_POLICY--;
3432
+ }
3433
+ };
3434
+ const _createTrustedScriptURL = function _createTrustedScriptURL(scriptUrl) {
3435
+ _assertNotInTrustedTypesPolicy();
3436
+ IN_TRUSTED_TYPES_POLICY++;
3437
+ try {
3438
+ return trustedTypesPolicy.createScriptURL(scriptUrl);
3439
+ } finally {
3440
+ IN_TRUSTED_TYPES_POLICY--;
3441
+ }
3442
+ };
3443
+ // Lazily resolve (and cache) the instance's internal default policy.
3444
+ // Resolution is attempted at most once: a successful `createPolicy` cannot be
3445
+ // repeated (Trusted Types throws on duplicate names), and a failed or
3446
+ // unsupported attempt must not be retried on every parse.
3447
+ const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy() {
3448
+ if (!defaultTrustedTypesPolicyResolved) {
3449
+ defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
3450
+ defaultTrustedTypesPolicyResolved = true;
3451
+ }
3452
+ return defaultTrustedTypesPolicy;
3453
+ };
3454
+ const _document = document,
3455
+ implementation = _document.implementation,
3456
+ createNodeIterator = _document.createNodeIterator,
3457
+ createDocumentFragment = _document.createDocumentFragment,
3458
+ getElementsByTagName = _document.getElementsByTagName;
3459
+ const importNode = originalDocument.importNode;
3298
3460
  let hooks = _createHooksMap();
3299
3461
  /**
3300
3462
  * Expose whether this browser supports running the full DOMPurify.
3301
3463
  */
3302
3464
  DOMPurify.isSupported = typeof entries === 'function' && typeof getParentNode === 'function' && implementation && implementation.createHTMLDocument !== undefined;
3303
- const {
3304
- MUSTACHE_EXPR,
3305
- ERB_EXPR,
3306
- TMPLIT_EXPR,
3307
- DATA_ATTR,
3308
- ARIA_ATTR,
3309
- IS_SCRIPT_OR_DATA,
3310
- ATTR_WHITESPACE,
3311
- CUSTOM_ELEMENT
3312
- } = EXPRESSIONS;
3313
- let {
3314
- IS_ALLOWED_URI: IS_ALLOWED_URI$1
3315
- } = EXPRESSIONS;
3465
+ const MUSTACHE_EXPR$1 = MUSTACHE_EXPR,
3466
+ ERB_EXPR$1 = ERB_EXPR,
3467
+ TMPLIT_EXPR$1 = TMPLIT_EXPR,
3468
+ DATA_ATTR$1 = DATA_ATTR,
3469
+ ARIA_ATTR$1 = ARIA_ATTR,
3470
+ IS_SCRIPT_OR_DATA$1 = IS_SCRIPT_OR_DATA,
3471
+ ATTR_WHITESPACE$1 = ATTR_WHITESPACE,
3472
+ CUSTOM_ELEMENT$1 = CUSTOM_ELEMENT;
3473
+ let IS_ALLOWED_URI$1 = IS_ALLOWED_URI;
3316
3474
  /**
3317
3475
  * We consider the elements and attributes below to be safe. Ideally
3318
3476
  * don't add any new ones but feel free to remove unwanted ones.
@@ -3389,6 +3547,13 @@ function createDOMPurify() {
3389
3547
  let WHOLE_DOCUMENT = false;
3390
3548
  /* Track whether config is already set on this instance of DOMPurify. */
3391
3549
  let SET_CONFIG = false;
3550
+ /* Pristine allowlist bindings captured at setConfig() time. On the
3551
+ * persistent-config path sanitize() restores the sets from these before
3552
+ * the per-walk hook clone-guard, so a hook's in-call widening cannot
3553
+ * carry across calls. Null until setConfig() is called; reset by
3554
+ * clearConfig(). */
3555
+ let SET_CONFIG_ALLOWED_TAGS = null;
3556
+ let SET_CONFIG_ALLOWED_ATTR = null;
3392
3557
  /* Decide if all elements (e.g. style, script) must be children of
3393
3558
  * document.body. By default, browsers might move them to document.head */
3394
3559
  let FORCE_BODY = false;
@@ -3431,7 +3596,17 @@ function createDOMPurify() {
3431
3596
  let USE_PROFILES = {};
3432
3597
  /* Tags to ignore content of when KEEP_CONTENT is true */
3433
3598
  let FORBID_CONTENTS = null;
3434
- const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
3599
+ const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script',
3600
+ // <selectedcontent> mirrors the selected <option>'s subtree, cloned by
3601
+ // the UA (customizable <select>) — including any on* handlers — and the
3602
+ // engine re-mirrors synchronously whenever a removal changes which
3603
+ // option/selectedcontent is current, even inside DOMPurify's inert
3604
+ // DOMParser document. Hoisting its children on removal re-inserts a fresh
3605
+ // mirror target ahead of the walk, which the engine refills, looping
3606
+ // forever (DoS) and amplifying output. Dropping its content on removal
3607
+ // (rather than hoisting) breaks that cascade; the content is a duplicate
3608
+ // of the option, which is sanitized on its own. See campaign-3 F1/F6.
3609
+ 'selectedcontent', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
3435
3610
  /* Tags that are safe for data: URIs */
3436
3611
  let DATA_URI_TAGS = null;
3437
3612
  const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
@@ -3447,8 +3622,10 @@ function createDOMPurify() {
3447
3622
  /* Allowed XHTML+XML namespaces */
3448
3623
  let ALLOWED_NAMESPACES = null;
3449
3624
  const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
3450
- let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
3451
- let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
3625
+ const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze(['mi', 'mo', 'mn', 'ms', 'mtext']);
3626
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
3627
+ const DEFAULT_HTML_INTEGRATION_POINTS = freeze(['annotation-xml']);
3628
+ let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
3452
3629
  // Certain elements are allowed in both SVG and HTML
3453
3630
  // namespace. We need to specify them explicitly
3454
3631
  // so that they don't get erroneously deleted from
@@ -3490,15 +3667,33 @@ function createDOMPurify() {
3490
3667
  // HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
3491
3668
  transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
3492
3669
  /* Set configuration parameters */
3493
- ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
3494
- ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
3495
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
3496
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
3497
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
3498
- FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
3499
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
3500
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
3501
- USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES : false;
3670
+ ALLOWED_TAGS = _resolveSetOption(cfg, 'ALLOWED_TAGS', DEFAULT_ALLOWED_TAGS, {
3671
+ transform: transformCaseFunc
3672
+ });
3673
+ ALLOWED_ATTR = _resolveSetOption(cfg, 'ALLOWED_ATTR', DEFAULT_ALLOWED_ATTR, {
3674
+ transform: transformCaseFunc
3675
+ });
3676
+ ALLOWED_NAMESPACES = _resolveSetOption(cfg, 'ALLOWED_NAMESPACES', DEFAULT_ALLOWED_NAMESPACES, {
3677
+ transform: stringToString
3678
+ });
3679
+ URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, 'ADD_URI_SAFE_ATTR', DEFAULT_URI_SAFE_ATTRIBUTES, {
3680
+ transform: transformCaseFunc,
3681
+ base: DEFAULT_URI_SAFE_ATTRIBUTES
3682
+ });
3683
+ DATA_URI_TAGS = _resolveSetOption(cfg, 'ADD_DATA_URI_TAGS', DEFAULT_DATA_URI_TAGS, {
3684
+ transform: transformCaseFunc,
3685
+ base: DEFAULT_DATA_URI_TAGS
3686
+ });
3687
+ FORBID_CONTENTS = _resolveSetOption(cfg, 'FORBID_CONTENTS', DEFAULT_FORBID_CONTENTS, {
3688
+ transform: transformCaseFunc
3689
+ });
3690
+ FORBID_TAGS = _resolveSetOption(cfg, 'FORBID_TAGS', clone({}), {
3691
+ transform: transformCaseFunc
3692
+ });
3693
+ FORBID_ATTR = _resolveSetOption(cfg, 'FORBID_ATTR', clone({}), {
3694
+ transform: transformCaseFunc
3695
+ });
3696
+ USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
3502
3697
  ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
3503
3698
  ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
3504
3699
  ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
@@ -3514,20 +3709,22 @@ function createDOMPurify() {
3514
3709
  SANITIZE_NAMED_PROPS = cfg.SANITIZE_NAMED_PROPS || false; // Default false
3515
3710
  KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
3516
3711
  IN_PLACE = cfg.IN_PLACE || false; // Default false
3517
- IS_ALLOWED_URI$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI;
3518
- NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE;
3519
- MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS || MATHML_TEXT_INTEGRATION_POINTS;
3520
- HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS || HTML_INTEGRATION_POINTS;
3521
- CUSTOM_ELEMENT_HANDLING = cfg.CUSTOM_ELEMENT_HANDLING || create(null);
3522
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck)) {
3523
- CUSTOM_ELEMENT_HANDLING.tagNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.tagNameCheck;
3524
- }
3525
- if (cfg.CUSTOM_ELEMENT_HANDLING && isRegexOrFunction(cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)) {
3526
- CUSTOM_ELEMENT_HANDLING.attributeNameCheck = cfg.CUSTOM_ELEMENT_HANDLING.attributeNameCheck;
3527
- }
3528
- if (cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements === 'boolean') {
3529
- CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = cfg.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements;
3530
- }
3712
+ IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
3713
+ NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
3714
+ MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS); // Default built-in map
3715
+ HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS); // Default built-in map
3716
+ const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
3717
+ CUSTOM_ELEMENT_HANDLING = create(null);
3718
+ if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
3719
+ CUSTOM_ELEMENT_HANDLING.tagNameCheck = customElementHandling.tagNameCheck; // Default undefined
3720
+ }
3721
+ if (objectHasOwnProperty(customElementHandling, 'attributeNameCheck') && isRegexOrFunction(customElementHandling.attributeNameCheck)) {
3722
+ CUSTOM_ELEMENT_HANDLING.attributeNameCheck = customElementHandling.attributeNameCheck; // Default undefined
3723
+ }
3724
+ if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
3725
+ CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
3726
+ }
3727
+ seal(CUSTOM_ELEMENT_HANDLING);
3531
3728
  if (SAFE_FOR_TEMPLATES) {
3532
3729
  ALLOW_DATA_ATTR = false;
3533
3730
  }
@@ -3563,36 +3760,36 @@ function createDOMPurify() {
3563
3760
  EXTRA_ELEMENT_HANDLING.tagCheck = null;
3564
3761
  EXTRA_ELEMENT_HANDLING.attributeCheck = null;
3565
3762
  /* Merge configuration parameters */
3566
- if (cfg.ADD_TAGS) {
3763
+ if (objectHasOwnProperty(cfg, 'ADD_TAGS')) {
3567
3764
  if (typeof cfg.ADD_TAGS === 'function') {
3568
3765
  EXTRA_ELEMENT_HANDLING.tagCheck = cfg.ADD_TAGS;
3569
- } else {
3766
+ } else if (arrayIsArray(cfg.ADD_TAGS)) {
3570
3767
  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
3571
3768
  ALLOWED_TAGS = clone(ALLOWED_TAGS);
3572
3769
  }
3573
3770
  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS, transformCaseFunc);
3574
3771
  }
3575
3772
  }
3576
- if (cfg.ADD_ATTR) {
3773
+ if (objectHasOwnProperty(cfg, 'ADD_ATTR')) {
3577
3774
  if (typeof cfg.ADD_ATTR === 'function') {
3578
3775
  EXTRA_ELEMENT_HANDLING.attributeCheck = cfg.ADD_ATTR;
3579
- } else {
3776
+ } else if (arrayIsArray(cfg.ADD_ATTR)) {
3580
3777
  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
3581
3778
  ALLOWED_ATTR = clone(ALLOWED_ATTR);
3582
3779
  }
3583
3780
  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR, transformCaseFunc);
3584
3781
  }
3585
3782
  }
3586
- if (cfg.ADD_URI_SAFE_ATTR) {
3783
+ if (objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR)) {
3587
3784
  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR, transformCaseFunc);
3588
3785
  }
3589
- if (cfg.FORBID_CONTENTS) {
3786
+ if (objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS)) {
3590
3787
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
3591
3788
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
3592
3789
  }
3593
3790
  addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS, transformCaseFunc);
3594
3791
  }
3595
- if (cfg.ADD_FORBID_CONTENTS) {
3792
+ if (objectHasOwnProperty(cfg, 'ADD_FORBID_CONTENTS') && arrayIsArray(cfg.ADD_FORBID_CONTENTS)) {
3596
3793
  if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) {
3597
3794
  FORBID_CONTENTS = clone(FORBID_CONTENTS);
3598
3795
  }
@@ -3611,6 +3808,13 @@ function createDOMPurify() {
3611
3808
  addToSet(ALLOWED_TAGS, ['tbody']);
3612
3809
  delete FORBID_TAGS.tbody;
3613
3810
  }
3811
+ // Re-derive the active Trusted Types policy from this configuration on
3812
+ // every parse. The active policy must never be sticky closure state that
3813
+ // outlives the config that set it: a caller-supplied policy left in place
3814
+ // after `clearConfig()` — or after a later call that supplied none, or
3815
+ // `TRUSTED_TYPES_POLICY: null` — could sign a subsequent "default"
3816
+ // `RETURN_TRUSTED_TYPE` result with a foreign, possibly unsafe policy.
3817
+ // See GHSA-vxr8-fq34-vvx9.
3614
3818
  if (cfg.TRUSTED_TYPES_POLICY) {
3615
3819
  if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
3616
3820
  throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
@@ -3618,18 +3822,45 @@ function createDOMPurify() {
3618
3822
  if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
3619
3823
  throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
3620
3824
  }
3621
- // Overwrite existing TrustedTypes policy.
3825
+ // A caller-supplied policy applies to this configuration only.
3826
+ const previousTrustedTypesPolicy = trustedTypesPolicy;
3622
3827
  trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
3623
- // Sign local variables required by `sanitize`.
3624
- emptyHTML = trustedTypesPolicy.createHTML('');
3828
+ // Sign local variables required by `sanitize`. If the supplied policy's
3829
+ // `createHTML` is circular (i.e. it calls `DOMPurify.sanitize`), this
3830
+ // throws via the re-entrancy guard. Restore the previous policy first so
3831
+ // the instance is not left in a poisoned state. See #1422.
3832
+ try {
3833
+ emptyHTML = _createTrustedHTML('');
3834
+ } catch (error) {
3835
+ trustedTypesPolicy = previousTrustedTypesPolicy;
3836
+ throw error;
3837
+ }
3838
+ } else if (cfg.TRUSTED_TYPES_POLICY === null) {
3839
+ // Explicit opt-out for this call: perform no Trusted Types signing and
3840
+ // create nothing (so a strict `trusted-types` CSP that disallows a
3841
+ // `dompurify` policy can still call `sanitize` from inside its own
3842
+ // policy — see #1422). Resetting to `undefined` rather than a sticky
3843
+ // `null` also drops any previously retained caller policy, so it cannot
3844
+ // resurface on a later call, while still allowing the next config-less
3845
+ // call to restore the internal default policy. See GHSA-vxr8-fq34-vvx9.
3846
+ trustedTypesPolicy = undefined;
3847
+ emptyHTML = '';
3625
3848
  } else {
3626
- // Uninitialized policy, attempt to initialize the internal dompurify policy.
3849
+ // No policy supplied: keep the currently active policy if one is set — a
3850
+ // previously supplied policy is intentionally sticky across config-less
3851
+ // calls — otherwise fall back to the instance's own internal policy,
3852
+ // created at most once. (A policy supplied for a *single* call still
3853
+ // lingers by design; what must not linger is a policy whose configuration
3854
+ // has been torn down via `clearConfig()`, which restores the default.)
3627
3855
  if (trustedTypesPolicy === undefined) {
3628
- trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
3856
+ trustedTypesPolicy = _getDefaultTrustedTypesPolicy();
3629
3857
  }
3630
- // If creating the internal policy succeeded sign internal variables.
3631
- if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
3632
- emptyHTML = trustedTypesPolicy.createHTML('');
3858
+ // Sign internal variables only when a policy is active. A falsy policy
3859
+ // (Trusted Types unsupported, creation failed, or an explicit opt-out)
3860
+ // leaves `emptyHTML` as a plain string, so we never call `.createHTML` on
3861
+ // a non-policy and throw. See #1422.
3862
+ if (trustedTypesPolicy && typeof emptyHTML === 'string') {
3863
+ emptyHTML = _createTrustedHTML('');
3633
3864
  }
3634
3865
  }
3635
3866
  // Prevent further manipulation of configuration.
@@ -3644,6 +3875,77 @@ function createDOMPurify() {
3644
3875
  * correctly. */
3645
3876
  const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
3646
3877
  const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
3878
+ /**
3879
+ * Namespace rules for an element in the SVG namespace.
3880
+ *
3881
+ * @param tagName the element's lowercase tag name
3882
+ * @param parent the (possibly simulated) parent node
3883
+ * @param parentTagName the parent's lowercase tag name
3884
+ * @returns true if a spec-compliant parser could produce this element
3885
+ */
3886
+ const _checkSvgNamespace = function _checkSvgNamespace(tagName, parent, parentTagName) {
3887
+ // The only way to switch from HTML namespace to SVG
3888
+ // is via <svg>. If it happens via any other tag, then
3889
+ // it should be killed.
3890
+ if (parent.namespaceURI === HTML_NAMESPACE) {
3891
+ return tagName === 'svg';
3892
+ }
3893
+ // The only way to switch from MathML to SVG is via <svg>
3894
+ // if the parent is either <annotation-xml> or a MathML
3895
+ // text integration point.
3896
+ if (parent.namespaceURI === MATHML_NAMESPACE) {
3897
+ return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
3898
+ }
3899
+ // We only allow elements that are defined in SVG
3900
+ // spec. All others are disallowed in SVG namespace.
3901
+ return Boolean(ALL_SVG_TAGS[tagName]);
3902
+ };
3903
+ /**
3904
+ * Namespace rules for an element in the MathML namespace.
3905
+ *
3906
+ * @param tagName the element's lowercase tag name
3907
+ * @param parent the (possibly simulated) parent node
3908
+ * @param parentTagName the parent's lowercase tag name
3909
+ * @returns true if a spec-compliant parser could produce this element
3910
+ */
3911
+ const _checkMathMlNamespace = function _checkMathMlNamespace(tagName, parent, parentTagName) {
3912
+ // The only way to switch from HTML namespace to MathML
3913
+ // is via <math>. If it happens via any other tag, then
3914
+ // it should be killed.
3915
+ if (parent.namespaceURI === HTML_NAMESPACE) {
3916
+ return tagName === 'math';
3917
+ }
3918
+ // The only way to switch from SVG to MathML is via
3919
+ // <math> and HTML integration points
3920
+ if (parent.namespaceURI === SVG_NAMESPACE) {
3921
+ return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
3922
+ }
3923
+ // We only allow elements that are defined in MathML
3924
+ // spec. All others are disallowed in MathML namespace.
3925
+ return Boolean(ALL_MATHML_TAGS[tagName]);
3926
+ };
3927
+ /**
3928
+ * Namespace rules for an element in the HTML namespace.
3929
+ *
3930
+ * @param tagName the element's lowercase tag name
3931
+ * @param parent the (possibly simulated) parent node
3932
+ * @param parentTagName the parent's lowercase tag name
3933
+ * @returns true if a spec-compliant parser could produce this element
3934
+ */
3935
+ const _checkHtmlNamespace = function _checkHtmlNamespace(tagName, parent, parentTagName) {
3936
+ // The only way to switch from SVG to HTML is via
3937
+ // HTML integration points, and from MathML to HTML
3938
+ // is via MathML text integration points
3939
+ if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
3940
+ return false;
3941
+ }
3942
+ if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
3943
+ return false;
3944
+ }
3945
+ // We disallow tags that are specific for MathML
3946
+ // or SVG and should never appear in HTML namespace
3947
+ return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
3948
+ };
3647
3949
  /**
3648
3950
  * @param element a DOM element whose namespace is being checked
3649
3951
  * @returns Return false if the element has a
@@ -3666,51 +3968,13 @@ function createDOMPurify() {
3666
3968
  return false;
3667
3969
  }
3668
3970
  if (element.namespaceURI === SVG_NAMESPACE) {
3669
- // The only way to switch from HTML namespace to SVG
3670
- // is via <svg>. If it happens via any other tag, then
3671
- // it should be killed.
3672
- if (parent.namespaceURI === HTML_NAMESPACE) {
3673
- return tagName === 'svg';
3674
- }
3675
- // The only way to switch from MathML to SVG is via`
3676
- // svg if parent is either <annotation-xml> or MathML
3677
- // text integration points.
3678
- if (parent.namespaceURI === MATHML_NAMESPACE) {
3679
- return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
3680
- }
3681
- // We only allow elements that are defined in SVG
3682
- // spec. All others are disallowed in SVG namespace.
3683
- return Boolean(ALL_SVG_TAGS[tagName]);
3971
+ return _checkSvgNamespace(tagName, parent, parentTagName);
3684
3972
  }
3685
3973
  if (element.namespaceURI === MATHML_NAMESPACE) {
3686
- // The only way to switch from HTML namespace to MathML
3687
- // is via <math>. If it happens via any other tag, then
3688
- // it should be killed.
3689
- if (parent.namespaceURI === HTML_NAMESPACE) {
3690
- return tagName === 'math';
3691
- }
3692
- // The only way to switch from SVG to MathML is via
3693
- // <math> and HTML integration points
3694
- if (parent.namespaceURI === SVG_NAMESPACE) {
3695
- return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
3696
- }
3697
- // We only allow elements that are defined in MathML
3698
- // spec. All others are disallowed in MathML namespace.
3699
- return Boolean(ALL_MATHML_TAGS[tagName]);
3974
+ return _checkMathMlNamespace(tagName, parent, parentTagName);
3700
3975
  }
3701
3976
  if (element.namespaceURI === HTML_NAMESPACE) {
3702
- // The only way to switch from SVG to HTML is via
3703
- // HTML integration points, and from MathML to HTML
3704
- // is via MathML text integration points
3705
- if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
3706
- return false;
3707
- }
3708
- if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
3709
- return false;
3710
- }
3711
- // We disallow tags that are specific for MathML
3712
- // or SVG and should never appear in HTML namespace
3713
- return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
3977
+ return _checkHtmlNamespace(tagName, parent, parentTagName);
3714
3978
  }
3715
3979
  // For XHTML and XML documents that support custom namespaces
3716
3980
  if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
@@ -3735,7 +3999,74 @@ function createDOMPurify() {
3735
3999
  // eslint-disable-next-line unicorn/prefer-dom-node-remove
3736
4000
  getParentNode(node).removeChild(node);
3737
4001
  } catch (_) {
4002
+ /* The normal detach failed — this is reached for a parentless node
4003
+ (getParentNode() is null, so .removeChild throws). Element.prototype
4004
+ .remove() is itself a spec no-op on a parentless node, so a recorded
4005
+ "removal" would otherwise hand the caller back an intact,
4006
+ payload-bearing node (e.g. a detached IN_PLACE root the mXSS canary or
4007
+ the style-with-element-child rule decided to kill). Fail closed by
4008
+ throwing — exactly as a clobbered root does at the IN_PLACE entry —
4009
+ rather than trying to "neutralize" the node via its own methods.
4010
+ Neutralizing would mean calling getAttributeNames()/removeAttribute()
4011
+ on the node, both of which a <form> root can clobber via a named child
4012
+ (and _isClobbered does not even probe getAttributeNames), so the
4013
+ neutralize step could itself be silently defeated, leaving the payload
4014
+ intact. A throw touches only the cached, clobber-safe remove() and
4015
+ getParentNode(). Generalizes GHSA-r47g-fvhr-h676 (clobbered-form root)
4016
+ to every root-kill reason. REPORT-3.
4017
+ This lives inside the catch, so it never fires for a normally-removed
4018
+ in-tree node: those have a parent, removeChild() succeeds, and the
4019
+ catch is not entered. Only a kept (parentless) root reaches here. */
3738
4020
  remove(node);
4021
+ if (!getParentNode(node)) {
4022
+ throw typeErrorCreate('a node selected for removal could not be detached from its tree ' + 'and cannot be safely returned; refusing to sanitize in place');
4023
+ }
4024
+ }
4025
+ };
4026
+ /**
4027
+ * _neutralizeRoot
4028
+ *
4029
+ * Fail-closed teardown of an in-place root after the sanitize walk aborts
4030
+ * (campaign-3 F2). An internal throw mid-walk — e.g. a page-registered
4031
+ * custom element's reaction detaches a node so `_forceRemove`'s deliberate
4032
+ * parentless guard throws, or any other re-entrant engine mutation — would
4033
+ * otherwise leave the caller's *live* tree half-sanitized, with everything
4034
+ * after the abort point still carrying its handlers. There is no safe way
4035
+ * to resume the walk (the tree mutated under us), so we strip the root bare:
4036
+ * remove every child and every attribute, then let the caller's catch see
4037
+ * the original error. Clobber-safe (cached `remove`/`childNodes`/`attributes`
4038
+ * getters; the root was already clobber-pre-flighted at the IN_PLACE entry).
4039
+ *
4040
+ * @param root the in-place root to empty
4041
+ */
4042
+ const _neutralizeRoot = function _neutralizeRoot(root) {
4043
+ const childNodes = getChildNodes(root);
4044
+ if (childNodes) {
4045
+ const snapshot = [];
4046
+ arrayForEach(childNodes, child => {
4047
+ arrayPush(snapshot, child);
4048
+ });
4049
+ arrayForEach(snapshot, child => {
4050
+ try {
4051
+ remove(child);
4052
+ } catch (_) {
4053
+ /* Best-effort teardown; a still-attached child is handled below */
4054
+ }
4055
+ });
4056
+ }
4057
+ const attributes = getAttributes(root);
4058
+ if (attributes) {
4059
+ for (let i = attributes.length - 1; i >= 0; --i) {
4060
+ const attribute = attributes[i];
4061
+ const name = attribute && attribute.name;
4062
+ if (typeof name === 'string') {
4063
+ try {
4064
+ root.removeAttribute(name);
4065
+ } catch (_) {
4066
+ /* Clobbered removeAttribute — ignore (fail-closed best effort) */
4067
+ }
4068
+ }
4069
+ }
3739
4070
  }
3740
4071
  };
3741
4072
  /**
@@ -3770,6 +4101,72 @@ function createDOMPurify() {
3770
4101
  }
3771
4102
  }
3772
4103
  };
4104
+ /**
4105
+ * _stripDisallowedAttributes
4106
+ *
4107
+ * Removes every attribute the active configuration does not allow from a
4108
+ * single element, using the same allowlist as the main attribute pass (so
4109
+ * `on*` handlers go, but no `/^on/` blocklist is introduced). Used only to
4110
+ * neutralise nodes that are being discarded from an in-place tree.
4111
+ *
4112
+ * @param element the element to strip
4113
+ */
4114
+ const _stripDisallowedAttributes = function _stripDisallowedAttributes(element) {
4115
+ const attributes = getAttributes(element);
4116
+ if (!attributes) {
4117
+ return;
4118
+ }
4119
+ for (let i = attributes.length - 1; i >= 0; --i) {
4120
+ const attribute = attributes[i];
4121
+ const name = attribute && attribute.name;
4122
+ if (typeof name !== 'string' || ALLOWED_ATTR[transformCaseFunc(name)]) {
4123
+ continue;
4124
+ }
4125
+ try {
4126
+ element.removeAttribute(name);
4127
+ } catch (_) {
4128
+ /* Clobbered removeAttribute on a doomed node — ignore */
4129
+ }
4130
+ }
4131
+ };
4132
+ /**
4133
+ * _neutralizeSubtree
4134
+ *
4135
+ * Completes the audit-5 F1 fix across every removal path. The KEEP_CONTENT
4136
+ * move-hoist neutralises only disallowed-tag removals; clobber, mXSS-canary,
4137
+ * namespace, comment, processing-instruction and KEEP_CONTENT:false removals
4138
+ * all drop their subtree wholesale via `_forceRemove`. On the IN_PLACE path
4139
+ * those dropped nodes are detached from the caller's LIVE tree but a
4140
+ * handler-bearing original among them (an `<img onerror>`/`<video>` that was
4141
+ * loading) keeps its queued resource event, which fires in page scope after
4142
+ * sanitize returns. This walks a removed subtree and strips every attribute
4143
+ * the active configuration does not allow — so `on*` handlers are cancelled
4144
+ * through the SAME allowlist that governs kept nodes, not a separate `/^on/`
4145
+ * blocklist. Run synchronously before sanitize returns, i.e. before any
4146
+ * queued event can fire. Hook-free by design: these nodes leave the output,
4147
+ * so firing attribute hooks for them would be surprising. Clobber-safe reads;
4148
+ * a doomed clobbered node may shadow `removeAttribute` (its own attributes are
4149
+ * irrelevant — it is discarded — while its non-clobbered descendants, e.g.
4150
+ * the `<img>`, are reached and scrubbed).
4151
+ *
4152
+ * @param root the root of a removed subtree to neutralise
4153
+ */
4154
+ const _neutralizeSubtree = function _neutralizeSubtree(root) {
4155
+ const stack = [root];
4156
+ while (stack.length > 0) {
4157
+ const node = stack.pop();
4158
+ const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
4159
+ if (nodeType === NODE_TYPE.element) {
4160
+ _stripDisallowedAttributes(node);
4161
+ }
4162
+ const childNodes = getChildNodes(node);
4163
+ if (childNodes) {
4164
+ for (let i = childNodes.length - 1; i >= 0; --i) {
4165
+ stack.push(childNodes[i]);
4166
+ }
4167
+ }
4168
+ }
4169
+ };
3773
4170
  /**
3774
4171
  * _initDocument
3775
4172
  *
@@ -3791,7 +4188,7 @@ function createDOMPurify() {
3791
4188
  // Root of XHTML doc must contain xmlns declaration (see https://www.w3.org/TR/xhtml1/normative.html#strict)
3792
4189
  dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + '</body></html>';
3793
4190
  }
3794
- const dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
4191
+ const dirtyPayload = trustedTypesPolicy ? _createTrustedHTML(dirty) : dirty;
3795
4192
  /*
3796
4193
  * Use the DOMParser API by default, fallback later if needs be
3797
4194
  * DOMParser not work for svg when has multiple root element.
@@ -3831,29 +4228,254 @@ function createDOMPurify() {
3831
4228
  // eslint-disable-next-line no-bitwise
3832
4229
  NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
3833
4230
  };
4231
+ /**
4232
+ * Replace template expression syntax (mustache, ERB, template
4233
+ * literal) with a space; shared by all SAFE_FOR_TEMPLATES scrub
4234
+ * sites. Order matters: mustache, then ERB, then template literal.
4235
+ *
4236
+ * @param value the string to scrub
4237
+ * @returns the scrubbed string
4238
+ */
4239
+ const _stripTemplateExpressions = function _stripTemplateExpressions(value) {
4240
+ value = stringReplace(value, MUSTACHE_EXPR$1, ' ');
4241
+ value = stringReplace(value, ERB_EXPR$1, ' ');
4242
+ value = stringReplace(value, TMPLIT_EXPR$1, ' ');
4243
+ return value;
4244
+ };
4245
+ /**
4246
+ * Strip template-engine expressions ({{...}}, ${...}, <%...%>) from the
4247
+ * character data of an element subtree. Used as the final safety net for
4248
+ * SAFE_FOR_TEMPLATES on every DOM-returning code path so that expressions
4249
+ * which only form after text-node normalization (e.g. fragments split across
4250
+ * stripped elements) cannot survive into a template-evaluating framework.
4251
+ *
4252
+ * Walks text/comment/CDATA/processing-instruction nodes and mutates `.data`
4253
+ * in place rather than round-tripping through innerHTML. This preserves
4254
+ * descendant node references (important for IN_PLACE callers), avoids a
4255
+ * serialize/reparse cycle, and reads literal character data — which means
4256
+ * `<%...%>` in text content matches the ERB regex against its real bytes
4257
+ * instead of the HTML-entity-escaped form innerHTML would produce.
4258
+ *
4259
+ * Attribute values are not visited here; SAFE_FOR_TEMPLATES handling for
4260
+ * attributes is performed during the per-node `_sanitizeAttributes` pass.
4261
+ *
4262
+ * @param node The root element whose character data should be scrubbed.
4263
+ */
4264
+ const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
4265
+ var _node$querySelectorAl;
4266
+ node.normalize();
4267
+ const walker = createNodeIterator.call(node.ownerDocument || node, node,
4268
+ // eslint-disable-next-line no-bitwise
4269
+ NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
4270
+ let currentNode = walker.nextNode();
4271
+ while (currentNode) {
4272
+ currentNode.data = _stripTemplateExpressions(currentNode.data);
4273
+ currentNode = walker.nextNode();
4274
+ }
4275
+ // NodeIterator does not descend into <template>.content per the DOM spec,
4276
+ // so we must explicitly recurse into each template's content fragment,
4277
+ // mirroring the approach used by _sanitizeShadowDOM.
4278
+ const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, 'template');
4279
+ if (templates) {
4280
+ arrayForEach(templates, tmpl => {
4281
+ if (_isDocumentFragment(tmpl.content)) {
4282
+ _scrubTemplateExpressions2(tmpl.content);
4283
+ }
4284
+ });
4285
+ }
4286
+ };
3834
4287
  /**
3835
4288
  * _isClobbered
3836
4289
  *
4290
+ * Detect DOM-clobbering on HTMLFormElement nodes. Form is the only HTML
4291
+ * interface with [LegacyOverrideBuiltIns]; a descendant element with a
4292
+ * `name` attribute matching a prototype property shadows that property
4293
+ * on direct reads. We use this check at the IN_PLACE entry-point and
4294
+ * during attribute sanitization to refuse clobbered forms.
4295
+ *
3837
4296
  * @param element element to check for clobbering attacks
3838
4297
  * @return true if clobbered, false if safe
3839
4298
  */
3840
4299
  const _isClobbered = function _isClobbered(element) {
3841
- return element instanceof HTMLFormElement && (typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' || !(element.attributes instanceof NamedNodeMap) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function');
4300
+ // Realm-independent tag-name probe. If we can't determine the tag
4301
+ // name at all, we can't reason about clobbering — return false
4302
+ // (the caller's other defences still apply).
4303
+ const realTagName = getNodeName ? getNodeName(element) : null;
4304
+ if (typeof realTagName !== 'string') {
4305
+ return false;
4306
+ }
4307
+ if (transformCaseFunc(realTagName) !== 'form') {
4308
+ return false;
4309
+ }
4310
+ return typeof element.nodeName !== 'string' || typeof element.textContent !== 'string' || typeof element.removeChild !== 'function' ||
4311
+ // Realm-safe NamedNodeMap detection: equality against the cached
4312
+ // prototype getter. Clobbered .attributes (e.g. <input name="attributes">)
4313
+ // makes the direct read diverge from the cached read; a clean form
4314
+ // (same-realm OR foreign-realm) has both reads pointing at the same
4315
+ // canonical NamedNodeMap.
4316
+ element.attributes !== getAttributes(element) || typeof element.removeAttribute !== 'function' || typeof element.setAttribute !== 'function' || typeof element.namespaceURI !== 'string' || typeof element.insertBefore !== 'function' || typeof element.hasChildNodes !== 'function' ||
4317
+ // NodeType clobbering probe. Cached Node.prototype.nodeType getter
4318
+ // returns the integer 1 for any Element regardless of realm; direct
4319
+ // read on a clobbered form (e.g. <input name="nodeType">) returns
4320
+ // the named child element. Cheap addition — nodeType is read from
4321
+ // an internal slot, no serialization cost — and removes a residual
4322
+ // clobbering surface used by several mXSS / PI / comment branches
4323
+ // in _sanitizeElements that compare currentNode.nodeType directly.
4324
+ element.nodeType !== getNodeType(element) ||
4325
+ // HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
4326
+ // "childNodes" shadows the prototype getter. Direct reads of
4327
+ // form.childNodes from a clobbered form return the named child
4328
+ // instead of the real NodeList, so any walk that reads it directly
4329
+ // skips the form's real children. Compare the direct read to the
4330
+ // cached Node.prototype getter — when the form's named-property
4331
+ // getter intercepts the read, the two values differ and we flag
4332
+ // the form. This catches every clobbering child type (input,
4333
+ // select, etc.) regardless of whether the named child happens to
4334
+ // carry a numeric .length, which a typeof-based probe would miss
4335
+ // (e.g. HTMLSelectElement.length is a defined unsigned-long).
4336
+ element.childNodes !== getChildNodes(element);
3842
4337
  };
3843
4338
  /**
3844
- * Checks whether the given object is a DOM node.
4339
+ * Checks whether the given value is a DocumentFragment from any realm.
4340
+ *
4341
+ * The realm-independent replacement reads `nodeType` through the cached
4342
+ * Node.prototype getter and compares to the DOCUMENT_FRAGMENT_NODE
4343
+ * constant (11). nodeType is a numeric value resolved from the node's
4344
+ * internal slot, identical across realms for the same kind of node.
4345
+ *
4346
+ * @param value object to check
4347
+ * @return true if value is a DocumentFragment-shaped node from any realm
4348
+ */
4349
+ const _isDocumentFragment = function _isDocumentFragment(value) {
4350
+ if (!getNodeType || typeof value !== 'object' || value === null) {
4351
+ return false;
4352
+ }
4353
+ try {
4354
+ return getNodeType(value) === NODE_TYPE.documentFragment;
4355
+ } catch (_) {
4356
+ return false;
4357
+ }
4358
+ };
4359
+ /**
4360
+ * Checks whether the given object is a DOM node, including nodes that
4361
+ * originate from a different window/realm (e.g. an iframe's
4362
+ * contentDocument). The previous `value instanceof Node` check was
4363
+ * realm-bound: nodes from a different window failed it, causing
4364
+ * sanitize() to silently stringify them and reset IN_PLACE to false,
4365
+ * returning the original node unsanitized. See GHSA-4w3q-35jp-p934.
3845
4366
  *
3846
4367
  * @param value object to check whether it's a DOM node
3847
- * @return true is object is a DOM node
4368
+ * @return true if value is a DOM node from any realm
3848
4369
  */
3849
4370
  const _isNode = function _isNode(value) {
3850
- return typeof Node === 'function' && value instanceof Node;
4371
+ if (!getNodeType || typeof value !== 'object' || value === null) {
4372
+ return false;
4373
+ }
4374
+ try {
4375
+ return typeof getNodeType(value) === 'number';
4376
+ } catch (_) {
4377
+ return false;
4378
+ }
3851
4379
  };
3852
4380
  function _executeHooks(hooks, currentNode, data) {
4381
+ if (hooks.length === 0) {
4382
+ return;
4383
+ }
3853
4384
  arrayForEach(hooks, hook => {
3854
4385
  hook.call(DOMPurify, currentNode, data, CONFIG);
3855
4386
  });
3856
4387
  }
4388
+ /**
4389
+ * Structural-threat checks that condemn a node regardless of the
4390
+ * allowlists: mXSS via namespace confusion, risky CSS construction,
4391
+ * processing instructions, markup-bearing comments. Pure predicate;
4392
+ * the caller removes. Check order is load-bearing.
4393
+ *
4394
+ * @param currentNode the node to inspect
4395
+ * @param tagName the node's transformCaseFunc'd tag name
4396
+ * @return true if the node must be removed
4397
+ */
4398
+ const _isUnsafeNode = function _isUnsafeNode(currentNode, tagName) {
4399
+ /* Detect mXSS attempts abusing namespace confusion */
4400
+ if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) {
4401
+ return true;
4402
+ }
4403
+ /* Remove risky CSS construction leading to mXSS */
4404
+ if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
4405
+ return true;
4406
+ }
4407
+ /* Remove any occurrence of processing instructions */
4408
+ if (currentNode.nodeType === NODE_TYPE.processingInstruction) {
4409
+ return true;
4410
+ }
4411
+ /* Remove any kind of possibly harmful comments */
4412
+ if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) {
4413
+ return true;
4414
+ }
4415
+ return false;
4416
+ };
4417
+ /**
4418
+ * Handle a node whose tag is forbidden or not allowlisted: keep
4419
+ * allowed custom elements (false return exits _sanitizeElements
4420
+ * early - namespace/fallback checks and the afterSanitizeElements
4421
+ * hook are intentionally skipped for kept custom elements), else
4422
+ * hoist content per KEEP_CONTENT and remove.
4423
+ *
4424
+ * @param currentNode the disallowed node
4425
+ * @param tagName the node's transformCaseFunc'd tag name
4426
+ * @return true if the node was removed, false if kept
4427
+ */
4428
+ const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName) {
4429
+ /* Check if we have a custom element to handle */
4430
+ if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
4431
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
4432
+ return false;
4433
+ }
4434
+ if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
4435
+ return false;
4436
+ }
4437
+ }
4438
+ /* Keep content except for bad-listed elements.
4439
+ Use the cached prototype getters exclusively — the previous code
4440
+ had `|| currentNode.parentNode` / `|| currentNode.childNodes`
4441
+ fallbacks, but the cached getters always return the canonical
4442
+ value (or null for a real parent-less node), so the fallback
4443
+ path was dead in safe cases and a clobbering surface in unsafe
4444
+ ones. Falsy cached results stay falsy; the `if (childNodes &&
4445
+ parentNode)` check already gates correctly. */
4446
+ if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
4447
+ const parentNode = getParentNode(currentNode);
4448
+ const childNodes = getChildNodes(currentNode);
4449
+ if (childNodes && parentNode) {
4450
+ const childCount = childNodes.length;
4451
+ /* In-place: hoist the *original* children so the iterator visits
4452
+ and sanitises them through the same allowlist pass as every other
4453
+ node. The caller built the tree in the live document, so the
4454
+ originals carry already-queued resource events (`<img onerror>`,
4455
+ `<video>`/`<audio>` error, lazy/`onload`, …); cloning would leave
4456
+ those originals detached but still armed, firing in page scope
4457
+ while the returned tree looked clean. Moving is safe in-place: the
4458
+ root is pre-validated as an allowed tag and so is never the node
4459
+ being removed, which keeps `parentNode` inside the iterator root
4460
+ and the relocated child inside the serialised tree.
4461
+ Otherwise (string / DOM-copy paths): clone. The iterator is rooted
4462
+ at — and the result serialised from — `body`, so a restrictive
4463
+ ALLOWED_TAGS that removes `body` itself must leave its content in
4464
+ place, which only cloning does; and those paths parse into an
4465
+ inert document, so their discarded originals never had a queued
4466
+ event to neutralise.
4467
+ `childNodes` is live; a tail-to-head walk keeps `childNodes[i]`
4468
+ valid whether we move (drops the trailing entry) or clone (leaves
4469
+ the list intact). */
4470
+ for (let i = childCount - 1; i >= 0; --i) {
4471
+ const hoisted = IN_PLACE ? childNodes[i] : cloneNode(childNodes[i], true);
4472
+ parentNode.insertBefore(hoisted, getNextSibling(currentNode));
4473
+ }
4474
+ }
4475
+ }
4476
+ _forceRemove(currentNode);
4477
+ return true;
4478
+ };
3857
4479
  /**
3858
4480
  * _sanitizeElements
3859
4481
  *
@@ -3864,7 +4486,6 @@ function createDOMPurify() {
3864
4486
  * @return true if node was killed, false if left alive
3865
4487
  */
3866
4488
  const _sanitizeElements = function _sanitizeElements(currentNode) {
3867
- let content = null;
3868
4489
  /* Execute a hook if present */
3869
4490
  _executeHooks(hooks.beforeSanitizeElements, currentNode, null);
3870
4491
  /* Check if element is clobbered or can clobber */
@@ -3873,76 +4494,41 @@ function createDOMPurify() {
3873
4494
  return true;
3874
4495
  }
3875
4496
  /* Now let's check the element's type and name */
3876
- const tagName = transformCaseFunc(currentNode.nodeName);
4497
+ const tagName = transformCaseFunc(getNodeName ? getNodeName(currentNode) : currentNode.nodeName);
3877
4498
  /* Execute a hook if present */
3878
4499
  _executeHooks(hooks.uponSanitizeElement, currentNode, {
3879
4500
  tagName,
3880
4501
  allowedTags: ALLOWED_TAGS
3881
4502
  });
3882
- /* Detect mXSS attempts abusing namespace confusion */
3883
- if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
3884
- _forceRemove(currentNode);
3885
- return true;
3886
- }
3887
- /* Remove risky CSS construction leading to mXSS */
3888
- if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === 'style' && _isNode(currentNode.firstElementChild)) {
3889
- _forceRemove(currentNode);
3890
- return true;
3891
- }
3892
- /* Remove any occurrence of processing instructions */
3893
- if (currentNode.nodeType === NODE_TYPE.progressingInstruction) {
3894
- _forceRemove(currentNode);
3895
- return true;
3896
- }
3897
- /* Remove any kind of possibly harmful comments */
3898
- if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(/<[/\w]/g, currentNode.data)) {
4503
+ /* Remove mXSS vectors, processing instructions and risky comments */
4504
+ if (_isUnsafeNode(currentNode, tagName)) {
3899
4505
  _forceRemove(currentNode);
3900
4506
  return true;
3901
4507
  }
3902
4508
  /* Remove element if anything forbids its presence */
3903
4509
  if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
3904
- /* Check if we have a custom element to handle */
3905
- if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
3906
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
3907
- return false;
3908
- }
3909
- if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
3910
- return false;
3911
- }
3912
- }
3913
- /* Keep content except for bad-listed elements */
3914
- if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
3915
- const parentNode = getParentNode(currentNode) || currentNode.parentNode;
3916
- const childNodes = getChildNodes(currentNode) || currentNode.childNodes;
3917
- if (childNodes && parentNode) {
3918
- const childCount = childNodes.length;
3919
- for (let i = childCount - 1; i >= 0; --i) {
3920
- const childClone = cloneNode(childNodes[i], true);
3921
- childClone.__removalCount = (currentNode.__removalCount || 0) + 1;
3922
- parentNode.insertBefore(childClone, getNextSibling(currentNode));
3923
- }
3924
- }
3925
- }
3926
- _forceRemove(currentNode);
3927
- return true;
3928
- }
3929
- /* Check whether element has a valid namespace */
3930
- if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
4510
+ return _sanitizeDisallowedNode(currentNode, tagName);
4511
+ }
4512
+ /* Check whether element has a valid namespace.
4513
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype
4514
+ nodeType getter rather than `instanceof Element`, which is realm-
4515
+ bound and short-circuits to false for any node minted in a different
4516
+ realm — letting a foreign-realm element with a forbidden namespace
4517
+ slip past the namespace check entirely. */
4518
+ const nt = getNodeType ? getNodeType(currentNode) : currentNode.nodeType;
4519
+ if (nt === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
3931
4520
  _forceRemove(currentNode);
3932
4521
  return true;
3933
4522
  }
3934
4523
  /* Make sure that older browsers don't get fallback-tag mXSS */
3935
- if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(/<\/no(script|embed|frames)/i, currentNode.innerHTML)) {
4524
+ if ((tagName === 'noscript' || tagName === 'noembed' || tagName === 'noframes') && regExpTest(FALLBACK_TAG_CLOSE, currentNode.innerHTML)) {
3936
4525
  _forceRemove(currentNode);
3937
4526
  return true;
3938
4527
  }
3939
4528
  /* Sanitize element content to be template-safe */
3940
4529
  if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
3941
4530
  /* Get the element's text content */
3942
- content = currentNode.textContent;
3943
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
3944
- content = stringReplace(content, expr, ' ');
3945
- });
4531
+ const content = _stripTemplateExpressions(currentNode.textContent);
3946
4532
  if (currentNode.textContent !== content) {
3947
4533
  arrayPush(DOMPurify.removed, {
3948
4534
  element: currentNode.cloneNode()
@@ -3972,11 +4558,12 @@ function createDOMPurify() {
3972
4558
  if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
3973
4559
  return false;
3974
4560
  }
4561
+ const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
3975
4562
  /* Allow valid data-* attributes: At least one character after "-"
3976
4563
  (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
3977
4564
  XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
3978
4565
  We don't need to check the value; it's always URI safe. */
3979
- if (ALLOW_DATA_ATTR && !FORBID_ATTR[lcName] && regExpTest(DATA_ATTR, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR, lcName)) ; else if (EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
4566
+ if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ; else if (!nameIsPermitted) {
3980
4567
  if (
3981
4568
  // First condition does a very basic check if a) it's basically a valid custom element tagname AND
3982
4569
  // b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
@@ -3988,11 +4575,15 @@ function createDOMPurify() {
3988
4575
  return false;
3989
4576
  }
3990
4577
  /* Check value is safe. First, is attr inert? If so, is safe */
3991
- } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA, stringReplace(value, ATTR_WHITESPACE, ''))) ; else if (value) {
4578
+ } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$1, stringReplace(value, ATTR_WHITESPACE$1, ''))) ; else if (value) {
3992
4579
  return false;
3993
4580
  } else ;
3994
4581
  return true;
3995
4582
  };
4583
+ /* Names the HTML spec reserves from valid-custom-element-name; these must
4584
+ * never be treated as basic custom elements even when a permissive
4585
+ * CUSTOM_ELEMENT_HANDLING.tagNameCheck is configured. */
4586
+ const RESERVED_CUSTOM_ELEMENT_NAMES = addToSet({}, ['annotation-xml', 'color-profile', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'missing-glyph']);
3996
4587
  /**
3997
4588
  * _isBasicCustomElement
3998
4589
  * checks if at least one dash is included in tagName, and it's not the first char
@@ -4002,7 +4593,64 @@ function createDOMPurify() {
4002
4593
  * @returns Returns true if the tag name meets the basic criteria for a custom element, otherwise false.
4003
4594
  */
4004
4595
  const _isBasicCustomElement = function _isBasicCustomElement(tagName) {
4005
- return tagName !== 'annotation-xml' && stringMatch(tagName, CUSTOM_ELEMENT);
4596
+ return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
4597
+ };
4598
+ /**
4599
+ * Wrap an attribute value in the matching Trusted Types object when
4600
+ * the active policy requires it. Namespaced attributes pass through
4601
+ * unchanged (no TT support yet, see
4602
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=1305293).
4603
+ *
4604
+ * @param lcTag lowercase tag name of the containing element
4605
+ * @param lcName lowercase attribute name
4606
+ * @param namespaceURI the attribute's namespace, if any
4607
+ * @param value the attribute value to wrap
4608
+ * @return the value, wrapped when Trusted Types demand it
4609
+ */
4610
+ const _applyTrustedTypesToAttribute = function _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value) {
4611
+ if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function' && !namespaceURI) {
4612
+ switch (trustedTypes.getAttributeType(lcTag, lcName)) {
4613
+ case 'TrustedHTML':
4614
+ {
4615
+ return _createTrustedHTML(value);
4616
+ }
4617
+ case 'TrustedScriptURL':
4618
+ {
4619
+ return _createTrustedScriptURL(value);
4620
+ }
4621
+ }
4622
+ }
4623
+ return value;
4624
+ };
4625
+ /**
4626
+ * Write a modified attribute value back onto the element. On
4627
+ * success, re-probe for clobbering introduced by the new value and
4628
+ * remove the element when found; otherwise pop the removal entry
4629
+ * recorded by the earlier _removeAttribute (long-standing pairing
4630
+ * with the SANITIZE_NAMED_PROPS path - do not "fix" casually). On
4631
+ * failure, remove the attribute instead.
4632
+ *
4633
+ * @param currentNode the element carrying the attribute
4634
+ * @param name the attribute name as present on the element
4635
+ * @param namespaceURI the attribute's namespace, if any
4636
+ * @param value the new attribute value
4637
+ */
4638
+ const _setAttributeValue = function _setAttributeValue(currentNode, name, namespaceURI, value) {
4639
+ try {
4640
+ if (namespaceURI) {
4641
+ currentNode.setAttributeNS(namespaceURI, name, value);
4642
+ } else {
4643
+ /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
4644
+ currentNode.setAttribute(name, value);
4645
+ }
4646
+ if (_isClobbered(currentNode)) {
4647
+ _forceRemove(currentNode);
4648
+ } else {
4649
+ arrayPop(DOMPurify.removed);
4650
+ }
4651
+ } catch (_) {
4652
+ _removeAttribute(name, currentNode);
4653
+ }
4006
4654
  };
4007
4655
  /**
4008
4656
  * _sanitizeAttributes
@@ -4017,9 +4665,7 @@ function createDOMPurify() {
4017
4665
  const _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
4018
4666
  /* Execute a hook if present */
4019
4667
  _executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
4020
- const {
4021
- attributes
4022
- } = currentNode;
4668
+ const attributes = currentNode.attributes;
4023
4669
  /* Check if we have attributes; if not we might have a text node */
4024
4670
  if (!attributes || _isClobbered(currentNode)) {
4025
4671
  return;
@@ -4032,14 +4678,13 @@ function createDOMPurify() {
4032
4678
  forceKeepAttr: undefined
4033
4679
  };
4034
4680
  let l = attributes.length;
4681
+ const lcTag = transformCaseFunc(currentNode.nodeName);
4035
4682
  /* Go backwards over all attributes; safely remove bad ones */
4036
4683
  while (l--) {
4037
4684
  const attr = attributes[l];
4038
- const {
4039
- name,
4040
- namespaceURI,
4041
- value: attrValue
4042
- } = attr;
4685
+ const name = attr.name,
4686
+ namespaceURI = attr.namespaceURI,
4687
+ attrValue = attr.value;
4043
4688
  const lcName = transformCaseFunc(name);
4044
4689
  const initValue = attrValue;
4045
4690
  let value = name === 'value' ? initValue : stringTrim(initValue);
@@ -4053,12 +4698,14 @@ function createDOMPurify() {
4053
4698
  /* Full DOM Clobbering protection via namespace isolation,
4054
4699
  * Prefix id and name attributes with `user-content-`
4055
4700
  */
4056
- if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name')) {
4701
+ if (SANITIZE_NAMED_PROPS && (lcName === 'id' || lcName === 'name') && stringIndexOf(value, SANITIZE_NAMED_PROPS_PREFIX) !== 0) {
4057
4702
  // Remove the attribute with this value
4058
4703
  _removeAttribute(name, currentNode);
4059
4704
  // Prefix the value and later re-create the attribute with the sanitized value
4060
4705
  value = SANITIZE_NAMED_PROPS_PREFIX + value;
4061
4706
  }
4707
+ // Else: already prefixed, leave the attribute alone — the prefix is
4708
+ // itself the clobbering protection, and re-applying it is incorrect.
4062
4709
  /* Work around a security issue with comments inside attributes */
4063
4710
  if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) {
4064
4711
  _removeAttribute(name, currentNode);
@@ -4069,7 +4716,7 @@ function createDOMPurify() {
4069
4716
  _removeAttribute(name, currentNode);
4070
4717
  continue;
4071
4718
  }
4072
- /* Did the hooks approve of the attribute? */
4719
+ /* Did the hooks force-keep the attribute? */
4073
4720
  if (hookEvent.forceKeepAttr) {
4074
4721
  continue;
4075
4722
  }
@@ -4079,56 +4726,24 @@ function createDOMPurify() {
4079
4726
  continue;
4080
4727
  }
4081
4728
  /* Work around a security issue in jQuery 3.0 */
4082
- if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(/\/>/i, value)) {
4729
+ if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
4083
4730
  _removeAttribute(name, currentNode);
4084
4731
  continue;
4085
4732
  }
4086
4733
  /* Sanitize attribute content to be template-safe */
4087
4734
  if (SAFE_FOR_TEMPLATES) {
4088
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
4089
- value = stringReplace(value, expr, ' ');
4090
- });
4735
+ value = _stripTemplateExpressions(value);
4091
4736
  }
4092
4737
  /* Is `value` valid for this attribute? */
4093
- const lcTag = transformCaseFunc(currentNode.nodeName);
4094
4738
  if (!_isValidAttribute(lcTag, lcName, value)) {
4095
4739
  _removeAttribute(name, currentNode);
4096
4740
  continue;
4097
4741
  }
4098
4742
  /* Handle attributes that require Trusted Types */
4099
- if (trustedTypesPolicy && typeof trustedTypes === 'object' && typeof trustedTypes.getAttributeType === 'function') {
4100
- if (namespaceURI) ; else {
4101
- switch (trustedTypes.getAttributeType(lcTag, lcName)) {
4102
- case 'TrustedHTML':
4103
- {
4104
- value = trustedTypesPolicy.createHTML(value);
4105
- break;
4106
- }
4107
- case 'TrustedScriptURL':
4108
- {
4109
- value = trustedTypesPolicy.createScriptURL(value);
4110
- break;
4111
- }
4112
- }
4113
- }
4114
- }
4743
+ value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
4115
4744
  /* Handle invalid data-* attribute set by try-catching it */
4116
4745
  if (value !== initValue) {
4117
- try {
4118
- if (namespaceURI) {
4119
- currentNode.setAttributeNS(namespaceURI, name, value);
4120
- } else {
4121
- /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
4122
- currentNode.setAttribute(name, value);
4123
- }
4124
- if (_isClobbered(currentNode)) {
4125
- _forceRemove(currentNode);
4126
- } else {
4127
- arrayPop(DOMPurify.removed);
4128
- }
4129
- } catch (_) {
4130
- _removeAttribute(name, currentNode);
4131
- }
4746
+ _setAttributeValue(currentNode, name, namespaceURI, value);
4132
4747
  }
4133
4748
  }
4134
4749
  /* Execute a hook if present */
@@ -4151,14 +4766,133 @@ function createDOMPurify() {
4151
4766
  _sanitizeElements(shadowNode);
4152
4767
  /* Check attributes next */
4153
4768
  _sanitizeAttributes(shadowNode);
4154
- /* Deep shadow DOM detected */
4155
- if (shadowNode.content instanceof DocumentFragment) {
4769
+ /* Deep shadow DOM detected.
4770
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): use nodeType against the
4771
+ DOCUMENT_FRAGMENT_NODE constant rather than instanceof, so we
4772
+ recurse into <template>.content from foreign realms too. */
4773
+ if (_isDocumentFragment(shadowNode.content)) {
4156
4774
  _sanitizeShadowDOM2(shadowNode.content);
4157
4775
  }
4776
+ /* An element iterated here may itself host an attached
4777
+ shadow root. The default NodeIterator does not enter shadow
4778
+ trees, so a shadow root nested inside template.content was
4779
+ previously reached by no walk at all (the pre-pass at
4780
+ _sanitizeAttachedShadowRoots descends via childNodes, which
4781
+ doesn't enter template.content; the template-content recursion
4782
+ above iterates the content but never inspected shadowRoot).
4783
+ Walk it explicitly. The nodeType guard avoids reading
4784
+ shadowRoot off text / comment / CDATA / PI nodes that the
4785
+ iterator also surfaces. */
4786
+ const shadowNodeType = getNodeType ? getNodeType(shadowNode) : shadowNode.nodeType;
4787
+ if (shadowNodeType === NODE_TYPE.element) {
4788
+ const innerSr = getShadowRoot(shadowNode);
4789
+ if (_isDocumentFragment(innerSr)) {
4790
+ _sanitizeAttachedShadowRoots(innerSr);
4791
+ _sanitizeShadowDOM2(innerSr);
4792
+ }
4793
+ }
4158
4794
  }
4159
4795
  /* Execute a hook if present */
4160
4796
  _executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
4161
4797
  };
4798
+ /**
4799
+ * _sanitizeAttachedShadowRoots
4800
+ *
4801
+ * Walks `root` and feeds every attached shadow root we encounter into
4802
+ * the existing _sanitizeShadowDOM pipeline. The default node iterator
4803
+ * does not descend into shadow trees, so nodes inside an attached
4804
+ * shadow root would otherwise be skipped entirely.
4805
+ *
4806
+ * Two real input paths put attached shadow roots in front of us:
4807
+ * 1. IN_PLACE on a DOM node that already has shadow roots attached.
4808
+ * 2. DOM-node input where importNode(dirty, true) deep-clones the
4809
+ * shadow root because it was created with `clonable: true`.
4810
+ *
4811
+ * This pass runs once, up front, so the main iteration loop (and the
4812
+ * existing _sanitizeShadowDOM template-content recursion) stay
4813
+ * untouched — string-input paths are not affected.
4814
+ *
4815
+ * @param root the subtree root to walk for attached shadow roots
4816
+ */
4817
+ const _sanitizeAttachedShadowRoots = function _sanitizeAttachedShadowRoots(root) {
4818
+ /* Iterative (explicit stack) rather than per-child recursion. DOM APIs
4819
+ impose no depth cap, so an attacker-shaped tree (JSON/CRDT/editor data
4820
+ built straight into the DOM — the IN_PLACE surface) deeper than the JS
4821
+ call-stack budget would otherwise overflow native recursion here and
4822
+ throw at the IN_PLACE entry pre-pass, before a single node is
4823
+ sanitized, leaving the caller's live tree untouched (fail-open). See
4824
+ campaign-3 F4. A heap stack keeps depth off the call stack.
4825
+ Each work item is either a node to descend into, or a deferred
4826
+ `_sanitizeShadowDOM` for an already-walked shadow root. The deferred
4827
+ form preserves the original post-order discipline: a shadow root's
4828
+ nested shadow roots are discovered before the outer shadow is
4829
+ sanitized (which may remove hosts). Pushes are in reverse of the
4830
+ desired processing order (LIFO): template content, then children, then
4831
+ the shadow-sanitize, then the shadow walk — so the order matches the
4832
+ previous recursion exactly. */
4833
+ const stack = [{
4834
+ node: root,
4835
+ shadow: null
4836
+ }];
4837
+ while (stack.length > 0) {
4838
+ const item = stack.pop();
4839
+ /* Deferred shadow-DOM sanitisation: runs after its subtree was walked. */
4840
+ if (item.shadow) {
4841
+ _sanitizeShadowDOM2(item.shadow);
4842
+ continue;
4843
+ }
4844
+ const node = item.node;
4845
+ const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
4846
+ const isElement = nodeType === NODE_TYPE.element;
4847
+ /* (pushed last → processed first) Children, snapshotted in reverse so
4848
+ the first child is processed first. Snapshotting matters because a
4849
+ hook may detach siblings mid-walk. */
4850
+ const childNodes = getChildNodes(node);
4851
+ if (childNodes) {
4852
+ for (let i = childNodes.length - 1; i >= 0; --i) {
4853
+ stack.push({
4854
+ node: childNodes[i],
4855
+ shadow: null
4856
+ });
4857
+ }
4858
+ }
4859
+ /* (pushed before children → processed after them, matching the old
4860
+ "template content last" order) When the node is a <template>,
4861
+ descend into its content. */
4862
+ if (isElement) {
4863
+ const rootName = getNodeName ? getNodeName(node) : null;
4864
+ if (typeof rootName === 'string' && transformCaseFunc(rootName) === 'template') {
4865
+ const content = node.content;
4866
+ if (_isDocumentFragment(content)) {
4867
+ stack.push({
4868
+ node: content,
4869
+ shadow: null
4870
+ });
4871
+ }
4872
+ }
4873
+ }
4874
+ /* Shadow root (processed first): walk its subtree, then sanitise it.
4875
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): nodeType-based detection
4876
+ rather than `instanceof DocumentFragment`, which is realm-bound and
4877
+ silently skipped foreign-realm shadow roots (e.g.
4878
+ iframe.contentDocument attachShadow). */
4879
+ if (isElement) {
4880
+ const sr = getShadowRoot(node);
4881
+ if (_isDocumentFragment(sr)) {
4882
+ /* Push the deferred sanitise first so it pops after the shadow
4883
+ walk we push next, i.e. nested shadow roots are discovered
4884
+ before this one is sanitised. */
4885
+ stack.push({
4886
+ node: null,
4887
+ shadow: sr
4888
+ }, {
4889
+ node: sr,
4890
+ shadow: null
4891
+ });
4892
+ }
4893
+ }
4894
+ }
4895
+ };
4162
4896
  // eslint-disable-next-line complexity
4163
4897
  DOMPurify.sanitize = function (dirty) {
4164
4898
  let cfg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -4175,13 +4909,9 @@ function createDOMPurify() {
4175
4909
  }
4176
4910
  /* Stringify, in case dirty is an object */
4177
4911
  if (typeof dirty !== 'string' && !_isNode(dirty)) {
4178
- if (typeof dirty.toString === 'function') {
4179
- dirty = dirty.toString();
4180
- if (typeof dirty !== 'string') {
4181
- throw typeErrorCreate('dirty is not a string, aborting');
4182
- }
4183
- } else {
4184
- throw typeErrorCreate('toString is not a function');
4912
+ dirty = stringifyValue(dirty);
4913
+ if (typeof dirty !== 'string') {
4914
+ throw typeErrorCreate('dirty is not a string, aborting');
4185
4915
  }
4186
4916
  }
4187
4917
  /* Return dirty HTML if DOMPurify cannot run */
@@ -4189,24 +4919,78 @@ function createDOMPurify() {
4189
4919
  return dirty;
4190
4920
  }
4191
4921
  /* Assign config vars */
4192
- if (!SET_CONFIG) {
4922
+ if (SET_CONFIG) {
4923
+ /* Persistent setConfig() path: _parseConfig is skipped, so the sets are
4924
+ * not re-derived per call. Restore them from the pristine bindings
4925
+ * captured at setConfig() time so a previous call's hook clone (mutated
4926
+ * below) does not carry over. */
4927
+ ALLOWED_TAGS = SET_CONFIG_ALLOWED_TAGS;
4928
+ ALLOWED_ATTR = SET_CONFIG_ALLOWED_ATTR;
4929
+ } else {
4193
4930
  _parseConfig(cfg);
4194
4931
  }
4932
+ /* Clone the hook-mutable allowlists before the walk whenever an
4933
+ * uponSanitize* hook is registered. The hook event exposes ALLOWED_TAGS
4934
+ * and ALLOWED_ATTR by reference (as allowedTags / allowedAttributes), so
4935
+ * a hook that widens them would otherwise mutate the shared set
4936
+ * permanently: across later calls and across every element. Cloning per
4937
+ * walk keeps documented in-call widening working while scoping it to the
4938
+ * call. A single guard for both config paths - the per-call path rebinds
4939
+ * the sets in _parseConfig each call, the persistent path restores them
4940
+ * from the captured bindings just above - so the two cannot diverge. */
4941
+ if (hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) {
4942
+ ALLOWED_TAGS = clone(ALLOWED_TAGS);
4943
+ }
4944
+ if (hooks.uponSanitizeAttribute.length > 0) {
4945
+ ALLOWED_ATTR = clone(ALLOWED_ATTR);
4946
+ }
4195
4947
  /* Clean up removed elements */
4196
4948
  DOMPurify.removed = [];
4197
- /* Check if dirty is correctly typed for IN_PLACE */
4198
- if (typeof dirty === 'string') {
4199
- IN_PLACE = false;
4200
- }
4201
- if (IN_PLACE) {
4202
- /* Do some early pre-sanitization to avoid unsafe root nodes */
4203
- if (dirty.nodeName) {
4204
- const tagName = transformCaseFunc(dirty.nodeName);
4949
+ /* Resolve IN_PLACE for this call without mutating persistent config.
4950
+ Writing the IN_PLACE closure variable here leaks under setConfig(),
4951
+ where _parseConfig is skipped on later calls: a single string call would
4952
+ disable in-place mode for every subsequent node call, returning a
4953
+ sanitized copy while leaving the caller's node — which in-place callers
4954
+ keep using and whose return value they ignore unsanitized. REPORT-2. */
4955
+ const inPlace = IN_PLACE && typeof dirty !== 'string' && _isNode(dirty);
4956
+ if (inPlace) {
4957
+ /* Do some early pre-sanitization to avoid unsafe root nodes.
4958
+ Read nodeName through the cached prototype getter — a clobbering
4959
+ child named "nodeName" on the form root would otherwise shadow
4960
+ the property and let this check skip the root-allowlist
4961
+ validation entirely. */
4962
+ const nn = getNodeName ? getNodeName(dirty) : dirty.nodeName;
4963
+ if (typeof nn === 'string') {
4964
+ const tagName = transformCaseFunc(nn);
4205
4965
  if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
4206
4966
  throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
4207
4967
  }
4208
4968
  }
4209
- } else if (dirty instanceof Node) {
4969
+ /* Pre-flight the root through _isClobbered. The iterator-driven
4970
+ removal path can not detach a parent-less root: _forceRemove
4971
+ falls through to Element.prototype.remove(), which per spec
4972
+ is a no-op on a node with no parent. A clobbered root would
4973
+ then survive the main loop with its attributes uninspected,
4974
+ because _sanitizeAttributes early-returns on _isClobbered. The
4975
+ result would be an attacker-controlled form, complete with any
4976
+ event-handler attributes the caller passed in, handed back to
4977
+ the application unsanitized. Refuse to sanitize such a root
4978
+ the same way we refuse a forbidden tag. GHSA-r47g-fvhr-h676. */
4979
+ if (_isClobbered(dirty)) {
4980
+ throw typeErrorCreate('root node is clobbered and cannot be sanitized in-place');
4981
+ }
4982
+ /* Sanitize attached shadow roots before the main iterator runs.
4983
+ The iterator does not descend into shadow trees. Same fail-closed
4984
+ barrier as the main walk (campaign-3 F2): a custom-element reaction
4985
+ inside a shadow root could abort this pre-pass before the walk runs,
4986
+ which would otherwise leave the entire live tree unsanitized. */
4987
+ try {
4988
+ _sanitizeAttachedShadowRoots(dirty);
4989
+ } catch (error) {
4990
+ _neutralizeRoot(dirty);
4991
+ throw error;
4992
+ }
4993
+ } else if (_isNode(dirty)) {
4210
4994
  /* If dirty is a DOM element, append to an empty document to avoid
4211
4995
  elements being stripped by the parser */
4212
4996
  body = _initDocument('<!---->');
@@ -4220,12 +5004,18 @@ function createDOMPurify() {
4220
5004
  // eslint-disable-next-line unicorn/prefer-dom-node-append
4221
5005
  body.appendChild(importedNode);
4222
5006
  }
5007
+ /* Clonable shadow roots are deep-cloned by importNode(); sanitize
5008
+ them before the main iterator runs, since the iterator does not
5009
+ descend into shadow trees. The walk routes every read through a
5010
+ cached prototype getter so clobbering descendants on a form root
5011
+ cannot hide a shadow host from this pass. */
5012
+ _sanitizeAttachedShadowRoots(importedNode);
4223
5013
  } else {
4224
5014
  /* Exit directly if we have nothing to do */
4225
5015
  if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
4226
5016
  // eslint-disable-next-line unicorn/prefer-includes
4227
5017
  dirty.indexOf('<') === -1) {
4228
- return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
5018
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
4229
5019
  }
4230
5020
  /* Initialize the document to work on */
4231
5021
  body = _initDocument(dirty);
@@ -4239,31 +5029,59 @@ function createDOMPurify() {
4239
5029
  _forceRemove(body.firstChild);
4240
5030
  }
4241
5031
  /* Get node iterator */
4242
- const nodeIterator = _createNodeIterator(IN_PLACE ? dirty : body);
4243
- /* Now start iterating over the created document */
4244
- while (currentNode = nodeIterator.nextNode()) {
4245
- /* Sanitize tags and elements */
4246
- _sanitizeElements(currentNode);
4247
- /* Check attributes next */
4248
- _sanitizeAttributes(currentNode);
4249
- /* Shadow DOM detected, sanitize it */
4250
- if (currentNode.content instanceof DocumentFragment) {
4251
- _sanitizeShadowDOM2(currentNode.content);
5032
+ const nodeIterator = _createNodeIterator(inPlace ? dirty : body);
5033
+ /* Now start iterating over the created document.
5034
+ The walk runs inside an exception barrier (campaign-3 F2): a re-entrant
5035
+ engine/custom-element mutation can detach a node mid-walk so
5036
+ `_forceRemove`'s parentless guard throws, aborting the loop. Without the
5037
+ barrier the caller's in-place tree would be left half-sanitized with the
5038
+ unvisited tail still armed. On any throw we fail closed — strip the
5039
+ in-place root bare then rethrow so the existing throw contract is
5040
+ preserved. (String/DOM-copy paths never return the partial body, so the
5041
+ propagating throw is already fail-closed there.) */
5042
+ try {
5043
+ while (currentNode = nodeIterator.nextNode()) {
5044
+ /* Sanitize tags and elements */
5045
+ _sanitizeElements(currentNode);
5046
+ /* Check attributes next */
5047
+ _sanitizeAttributes(currentNode);
5048
+ /* Shadow DOM detected, sanitize it.
5049
+ Realm-safe check (GHSA-hpcv-96wg-7vj8): nodeType-based detection
5050
+ instead of instanceof, so foreign-realm <template>.content is
5051
+ walked correctly. */
5052
+ if (_isDocumentFragment(currentNode.content)) {
5053
+ _sanitizeShadowDOM2(currentNode.content);
5054
+ }
4252
5055
  }
5056
+ } catch (error) {
5057
+ if (inPlace) {
5058
+ _neutralizeRoot(dirty);
5059
+ }
5060
+ throw error;
4253
5061
  }
4254
5062
  /* If we sanitized `dirty` in-place, return it. */
4255
- if (IN_PLACE) {
5063
+ if (inPlace) {
5064
+ /* Fail-closed completion of the audit-5 F1 fix: every node removed from
5065
+ the caller's live tree is detached but may still hold a queued
5066
+ resource-event handler that fires in page scope after we return. The
5067
+ move-hoist covers only disallowed-tag KEEP_CONTENT removals; strip the
5068
+ non-allow-listed attributes off every other removed subtree (clobber,
5069
+ mXSS, namespace, comments, KEEP_CONTENT:false, …) so those handlers are
5070
+ cancelled before any event can fire. Runs synchronously, pre-return. */
5071
+ arrayForEach(DOMPurify.removed, entry => {
5072
+ if (entry.element) {
5073
+ _neutralizeSubtree(entry.element);
5074
+ }
5075
+ });
5076
+ if (SAFE_FOR_TEMPLATES) {
5077
+ _scrubTemplateExpressions2(dirty);
5078
+ }
4256
5079
  return dirty;
4257
5080
  }
4258
5081
  /* Return sanitized string or DOM */
4259
5082
  if (RETURN_DOM) {
4260
5083
  if (SAFE_FOR_TEMPLATES) {
4261
- body.normalize();
4262
- let html = body.innerHTML;
4263
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
4264
- html = stringReplace(html, expr, ' ');
4265
- });
4266
- body.innerHTML = html;
5084
+ _scrubTemplateExpressions2(body);
4267
5085
  }
4268
5086
  if (RETURN_DOM_FRAGMENT) {
4269
5087
  returnNode = createDocumentFragment.call(body.ownerDocument);
@@ -4293,20 +5111,28 @@ function createDOMPurify() {
4293
5111
  }
4294
5112
  /* Sanitize final string template-safe */
4295
5113
  if (SAFE_FOR_TEMPLATES) {
4296
- arrayForEach([MUSTACHE_EXPR, ERB_EXPR, TMPLIT_EXPR], expr => {
4297
- serializedHTML = stringReplace(serializedHTML, expr, ' ');
4298
- });
5114
+ serializedHTML = _stripTemplateExpressions(serializedHTML);
4299
5115
  }
4300
- return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
5116
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
4301
5117
  };
4302
5118
  DOMPurify.setConfig = function () {
4303
5119
  let cfg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
4304
5120
  _parseConfig(cfg);
4305
5121
  SET_CONFIG = true;
5122
+ SET_CONFIG_ALLOWED_TAGS = ALLOWED_TAGS;
5123
+ SET_CONFIG_ALLOWED_ATTR = ALLOWED_ATTR;
4306
5124
  };
4307
5125
  DOMPurify.clearConfig = function () {
4308
5126
  CONFIG = null;
4309
5127
  SET_CONFIG = false;
5128
+ SET_CONFIG_ALLOWED_TAGS = null;
5129
+ SET_CONFIG_ALLOWED_ATTR = null;
5130
+ // Drop any caller-supplied Trusted Types policy so it cannot poison later
5131
+ // `RETURN_TRUSTED_TYPE` output. The internal default policy (cached, and
5132
+ // never recreated — Trusted Types throws on duplicate names) is restored by
5133
+ // the next `_parseConfig`. See GHSA-vxr8-fq34-vvx9.
5134
+ trustedTypesPolicy = defaultTrustedTypesPolicy;
5135
+ emptyHTML = '';
4310
5136
  };
4311
5137
  DOMPurify.isValidAttribute = function (tag, attr, value) {
4312
5138
  /* Initialize shared config vars if necessary. */
@@ -4321,9 +5147,19 @@ function createDOMPurify() {
4321
5147
  if (typeof hookFunction !== 'function') {
4322
5148
  return;
4323
5149
  }
5150
+ /* Reject unknown entry points. Without this, a non-hook key (e.g.
5151
+ * '__proto__') indexes off the prototype chain rather than a real
5152
+ * hook array, and arrayPush then writes to Object.prototype. Guard
5153
+ * with an own-property check against the known hook names. */
5154
+ if (!objectHasOwnProperty(hooks, entryPoint)) {
5155
+ return;
5156
+ }
4324
5157
  arrayPush(hooks[entryPoint], hookFunction);
4325
5158
  };
4326
5159
  DOMPurify.removeHook = function (entryPoint, hookFunction) {
5160
+ if (!objectHasOwnProperty(hooks, entryPoint)) {
5161
+ return undefined;
5162
+ }
4327
5163
  if (hookFunction !== undefined) {
4328
5164
  const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
4329
5165
  return index === -1 ? undefined : arraySplice(hooks[entryPoint], index, 1)[0];
@@ -4331,6 +5167,9 @@ function createDOMPurify() {
4331
5167
  return arrayPop(hooks[entryPoint]);
4332
5168
  };
4333
5169
  DOMPurify.removeHooks = function (entryPoint) {
5170
+ if (!objectHasOwnProperty(hooks, entryPoint)) {
5171
+ return;
5172
+ }
4334
5173
  hooks[entryPoint] = [];
4335
5174
  };
4336
5175
  DOMPurify.removeAllHooks = function () {
@@ -4472,6 +5311,7 @@ var ar = {
4472
5311
  "status.uploading": "جارٍ التحميل",
4473
5312
  "status.sessionExpired": "انتهت صلاحية جلسة الدردشة. سيتم بدء محادثة جديدة — يُرجى إعادة إرسال رسالتك.",
4474
5313
  "status.sessionError": "لم تعد جلسة الدردشة هذه متاحة.",
5314
+ "status.chatEnded": "انتهت هذه المحادثة.",
4475
5315
  "modal.newChatTitle": "بدء محادثة جديدة",
4476
5316
  "modal.newChatBody": "بدء محادثة جديدة سيؤدي إلى مسح المحادثة الحالية. هل ترغب بالمتابعة؟",
4477
5317
  "modal.cancel": "إلغاء",
@@ -4506,6 +5346,7 @@ var en = {
4506
5346
  "status.uploading": "Uploading",
4507
5347
  "status.sessionExpired": "Your chat session expired. Starting a new chat — please resend your message.",
4508
5348
  "status.sessionError": "This chat session is no longer available.",
5349
+ "status.chatEnded": "This chat has ended.",
4509
5350
  "modal.newChatTitle": "Start New Chat",
4510
5351
  "modal.newChatBody": "Starting a new chat will clear your current conversation. Continue?",
4511
5352
  "modal.cancel": "Cancel",
@@ -4540,6 +5381,7 @@ var es = {
4540
5381
  "status.uploading": "Subiendo",
4541
5382
  "status.sessionExpired": "Tu sesión de chat ha expirado. Se iniciará un nuevo chat: vuelve a enviar tu mensaje.",
4542
5383
  "status.sessionError": "Esta sesión de chat ya no está disponible.",
5384
+ "status.chatEnded": "Este chat ha finalizado.",
4543
5385
  "modal.newChatTitle": "Iniciar Nuevo Chat",
4544
5386
  "modal.newChatBody": "Iniciar un nuevo chat borrará tu conversación actual. ¿Continuar?",
4545
5387
  "modal.cancel": "Cancelar",
@@ -4574,6 +5416,7 @@ var fr = {
4574
5416
  "status.uploading": "Téléversement",
4575
5417
  "status.sessionExpired": "Votre session de chat a expiré. Un nouveau chat va démarrer — veuillez renvoyer votre message.",
4576
5418
  "status.sessionError": "Cette session de chat n'est plus disponible.",
5419
+ "status.chatEnded": "Cette conversation est terminée.",
4577
5420
  "modal.newChatTitle": "Nouveau Chat",
4578
5421
  "modal.newChatBody": "Démarrer un nouveau chat effacera votre conversation actuelle. Continuer?",
4579
5422
  "modal.cancel": "Annuler",
@@ -4608,6 +5451,7 @@ var hi = {
4608
5451
  "status.uploading": "अपलोड हो रहा है",
4609
5452
  "status.sessionExpired": "आपका चैट सत्र समाप्त हो गया है। एक नई चैट शुरू हो रही है — कृपया अपना संदेश फिर से भेजें।",
4610
5453
  "status.sessionError": "यह चैट सत्र अब उपलब्ध नहीं है।",
5454
+ "status.chatEnded": "यह चैट समाप्त हो गई है।",
4611
5455
  "modal.newChatTitle": "नई चैट शुरू करें",
4612
5456
  "modal.newChatBody": "नई चैट शुरू करने से आपकी मौजूदा बातचीत हट जाएगी। क्या आप जारी रखना चाहते हैं?",
4613
5457
  "modal.cancel": "रद्द करें",
@@ -4642,6 +5486,7 @@ var it = {
4642
5486
  "status.uploading": "Caricamento",
4643
5487
  "status.sessionExpired": "La tua sessione di chat è scaduta. Verrà avviata una nuova chat: invia di nuovo il tuo messaggio.",
4644
5488
  "status.sessionError": "Questa sessione di chat non è più disponibile.",
5489
+ "status.chatEnded": "Questa chat è terminata.",
4645
5490
  "modal.newChatTitle": "Avvia nuova chat",
4646
5491
  "modal.newChatBody": "Avviare una nuova chat cancellerà la conversazione attuale. Continuare?",
4647
5492
  "modal.cancel": "Annulla",
@@ -4676,6 +5521,7 @@ var pt = {
4676
5521
  "status.uploading": "Carregando",
4677
5522
  "status.sessionExpired": "Sua sessão de chat expirou. Iniciando um novo chat — reenvie sua mensagem.",
4678
5523
  "status.sessionError": "Esta sessão de chat não está mais disponível.",
5524
+ "status.chatEnded": "Este chat foi encerrado.",
4679
5525
  "modal.newChatTitle": "Iniciar novo chat",
4680
5526
  "modal.newChatBody": "Iniciar um novo chat apagará sua conversa atual. Deseja continuar?",
4681
5527
  "modal.cancel": "Cancelar",
@@ -4710,6 +5556,7 @@ var sw = {
4710
5556
  "status.uploading": "Inapakia",
4711
5557
  "status.sessionExpired": "Kipindi chako cha gumzo kimeisha. Gumzo jipya linaanza — tafadhali tuma tena ujumbe wako.",
4712
5558
  "status.sessionError": "Kipindi hiki cha gumzo hakipatikani tena.",
5559
+ "status.chatEnded": "Mazungumzo haya yamekamilika.",
4713
5560
  "modal.newChatTitle": "Anza gumzo jipya",
4714
5561
  "modal.newChatBody": "Kuanza gumzo jipya kutafuta gumzo lako la sasa. Je, ungependa kuendelea?",
4715
5562
  "modal.cancel": "Ghairi",
@@ -4744,6 +5591,7 @@ var uk = {
4744
5591
  "status.uploading": "Завантаження",
4745
5592
  "status.sessionExpired": "Ваш сеанс чату завершився. Розпочинається новий чат — будь ласка, надішліть повідомлення ще раз.",
4746
5593
  "status.sessionError": "Цей сеанс чату більше недоступний.",
5594
+ "status.chatEnded": "Цей чат завершено.",
4747
5595
  "modal.newChatTitle": "Почати новий чат",
4748
5596
  "modal.newChatBody": "Початок нового чату видалить вашу поточну розмову. Продовжити?",
4749
5597
  "modal.cancel": "Скасувати",
@@ -5140,12 +5988,17 @@ class ChatSessionService {
5140
5988
  }
5141
5989
  startMessagePolling(sessionId, callbacks) {
5142
5990
  const poll = async () => {
5991
+ var _a;
5143
5992
  try {
5144
5993
  const since = callbacks.getSince();
5145
5994
  const data = await this.fetchMessages(sessionId, since);
5146
5995
  if (data.messages.length > 0) {
5147
5996
  callbacks.onMessages(data.messages);
5148
5997
  }
5998
+ if (data.session_status === 'ended') {
5999
+ this.stopMessagePolling();
6000
+ (_a = callbacks.onSessionEnded) === null || _a === void 0 ? void 0 : _a.call(callbacks);
6001
+ }
5149
6002
  }
5150
6003
  catch (error) {
5151
6004
  if (callbacks.onError) {
@@ -5391,7 +6244,7 @@ class FileAttachmentManager {
5391
6244
  }
5392
6245
  }
5393
6246
 
5394
- const ocsChatCss = "/*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */@layer properties{@supports ((-webkit-hyphens:none) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,::backdrop,:after,:before{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:host,:root{--font-sans:ui-sans-serif,system-ui,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace;--color-blue-300:oklch(80.9% .105 251.813);--color-slate-500:oklch(55.4% .046 257.417);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--spacing:.25rem;--breakpoint-lg:64rem;--container-sm:24rem;--font-weight-light:300;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--animate-progress:progress 3s infinite linear;--animate-dots:dots 1s steps(5,end)infinite;--transform-origin-left-right:0% 50%}}@layer base{*,::backdrop,:after,:before{border:0 solid;border-color:var(--color-gray-200,currentcolor);box-sizing:border-box;margin:0;padding:0}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button;background-color:#0000;border:0 solid;border-color:var(--color-gray-200,currentcolor);border-radius:0;box-sizing:border-box;color:inherit;font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;margin:0;margin-inline-end:4px;opacity:1;padding:0}:host,html{-webkit-text-size-adjust:100%;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-size:1em;font-variation-settings:var(--default-mono-font-variation-settings,normal)}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}menu,ol,ul{list-style:none}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}button,input,optgroup,select,textarea{background-color:#0000;border-radius:0;color:inherit;font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;opacity:1}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::-moz-placeholder{opacity:1}::placeholder{opacity:1}@supports (not (-webkit-appearance:-apple-pay-button)) or (contain-intrinsic-size:1px){::-moz-placeholder{color:currentColor}::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::-moz-placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex;padding-block:0}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.fixed{position:fixed}.relative{position:relative}.static{position:static}.container{width:100%}.block{display:block}.contents{display:contents}.flex{display:flex}.hidden{display:none}.w-full{width:100%}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.resize{resize:both}.items-center{align-items:center}.justify-center{justify-content:center}.gap-\\[0\\.5em\\]{gap:.5em}:where(.space-y-\\[0\\.25em\\]>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(.25em*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(.25em*var(--tw-space-y-reverse))}.border{border-style:var(--tw-border-style);border-width:1px}.py-\\[2px\\]{padding-block:2px}.text-\\[0\\.8em\\]{font-size:.8em}.font-light{--tw-font-weight:var(--font-weight-light);font-weight:var(--font-weight-light)}.text-slate-500{color:var(--color-slate-500)}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a)}.ring,.shadow{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor)}} /*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */@layer properties{}@layer base{}@layer components; /*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */@layer properties{}@layer base{}@layer components{#ocs-chat-window{font-size:var(--chat-window-font-size);z-index:var(--chat-z-index)}.starter-question{border-radius:var(--radius-lg);text-align:left;--tw-duration:.2s;background-color:var(--starter-question-bg-color);border:1px solid var(--starter-question-border-color);color:var(--starter-question-text-color);padding:.75em;transition-duration:.2s}.starter-question:hover{background-color:var(--starter-question-bg-hover-color);border-color:var(--starter-question-border-hover-color)}.chat-btn-text{border-radius:var(--radius-lg);border-style:var(--tw-border-style);transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;--tw-ease:var(--ease-in-out);align-items:center;background-color:var(--button-background-color,#fff);border-width:0;border:1px solid var(--button-border-color);color:var(--button-text-color,#111827);display:flex;font-size:var(--button-font-size);gap:8px;padding:.5em;transition-duration:.2s;transition-timing-function:var(--ease-in-out);z-index:var(--chat-z-index,50)}.chat-btn-text:hover{border:1px solid var(--button-border-color-hover);color:var(--button-text-color-hover,#1d4ed8)}.chat-btn-text span{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);white-space:nowrap}.chat-btn-text img{flex-shrink:0;height:var(--button-icon-size);-o-object-fit:contain;object-fit:contain;width:var(--button-icon-size)}.chat-btn-text.round{border-radius:3.40282e+38px}.chat-btn-icon{border-radius:var(--radius-lg);border-style:var(--tw-border-style);transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;--tw-ease:var(--ease-in-out);background-color:var(--button-background-color,#fff);border-width:0;border:1px solid var(--button-border-color);font-size:var(--button-font-size);padding:.5em;transition-duration:.2s;transition-timing-function:var(--ease-in-out);z-index:var(--chat-z-index,50)}.chat-btn-icon:hover{border:1px solid var(--button-border-color-hover);color:var(--button-text-color-hover,#1d4ed8)}.chat-btn-icon img{height:var(--button-icon-size);-o-object-fit:contain;object-fit:contain;width:var(--button-icon-size)}.chat-btn-icon.round,.round .chat-btn-icon,.round.chat-btn-text{border-radius:3.40282e+38px}.error-message{color:var(--error-text-color);padding:.5em}.chat-window-fullscreen{border-style:var(--tw-border-style);inset:calc(var(--spacing)*0);z-index:9999;--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);height:100%;max-height:100%;transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));width:100%;--tw-duration:.2s;border-radius:0;border-width:0;max-width:var(--chat-window-fullscreen-width);transition-duration:.2s}.chat-window-fullscreen,.chat-window-normal{background-color:var(--chat-window-bg-color);display:flex;flex-direction:column;overflow:hidden;position:fixed}.chat-window-normal{border:1px solid var(--chat-window-border-color);border-radius:var(--radius-lg);height:100vh;max-width:var(--breakpoint-lg);min-height:300px;min-width:300px;width:100vw}.chat-window-normal:not(.chat-window-dragging){--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;transition-duration:.2s}.chat-window-dragging,.chat-window-normal:not(.chat-window-dragging){box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.chat-window-dragging{cursor:grabbing;--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040)}.chat-window-kiosk{background-color:var(--chat-window-bg-color);border-radius:0;box-shadow:none;display:flex;flex-direction:column;font-size:var(--chat-window-font-size);height:100%;inset:calc(var(--spacing)*0);overflow:hidden;position:absolute;width:100%;z-index:var(--chat-z-index)}.chat-header{transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.15s;align-items:center;background-color:var(--header-bg-color);border-bottom:1px solid var(--header-border-color);display:flex;font-size:var(--header-font-size);justify-content:space-between;padding:.5em;transition-duration:.15s}.chat-header:active,.chat-header:hover{background-color:var(--header-bg-hover-color)}.header-text{align-items:center;color:var(--header-text-color);display:flex;font-size:var(--header-text-font-size);justify-content:center}.chat-header-draggable{cursor:grab}.chat-header-dragging{cursor:grabbing}.drag-indicator{display:none}.drag-dots{display:flex;gap:2px;margin-left:2px;pointer-events:none}.header-buttons{align-items:center;display:flex;gap:4px}.header-button{border-radius:var(--radius-md);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;color:var(--header-button-text-color);padding:.375em;transition-duration:.2s}.header-button svg{height:var(--header-button-icon-size);width:var(--header-button-icon-size)}.header-button:hover{background-color:var(--header-button-bg-hover-color)}.fullscreen-button{display:none}.chat-content{display:flex;flex-direction:column;flex-grow:1;overflow:hidden}.loading-container{align-items:center;display:flex;flex-grow:1;justify-content:center}.loading-text{color:var(--loading-text-color);margin-left:2px}.messages-container{flex-grow:1;overflow-y:auto;padding:1em}:where(.messages-container>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(var(--spacing)*2*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(var(--spacing)*2*var(--tw-space-y-reverse))}.message-row{display:flex}.message-row-user{justify-content:flex-end}.message-row-assistant{justify-content:flex-start}.message-bubble{border-radius:var(--radius-lg);padding:.5em 1em}.message-bubble-user{background-color:var(--message-user-bg-color);color:var(--message-user-text-color)}.message-bubble-assistant{background-color:var(--message-assistant-bg-color);color:var(--message-assistant-text-color)}.message-bubble-system{background-color:var(--message-system-bg-color);color:var(--message-system-text-color)}.message-timestamp{font-size:var(--chat-window-font-size-sm);margin-top:4px;opacity:.7}.message-attachments{margin-top:8px}:where(.message-attachments>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(var(--spacing)*1*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(var(--spacing)*1*var(--tw-space-y-reverse))}.attachment-link{display:block;text-decoration-line:underline}:where(.welcome-messages>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(var(--spacing)*2*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(var(--spacing)*2*var(--tw-space-y-reverse))}.typing-indicator{height:calc(var(--spacing)*1.5);overflow:hidden;width:100%}.typing-progress{animation:var(--animate-progress);background-color:var(--typing-progress-bg-color);border-radius:var(--radius-lg);height:100%;transform-origin:var(--transform-origin-left-right);width:100%}.typing-text{font-size:var(--chat-window-font-size-sm);justify-content:center;opacity:.7;width:100%}.typing-dots{animation:var(--animate-dots)}:where(.starter-questions>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(var(--spacing)*2*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(var(--spacing)*2*var(--tw-space-y-reverse))}.starter-questions{padding:1em}.starter-question-row{display:flex;justify-content:flex-end}.input-area{background-color:var(--input-bg-color);border-top:1px solid var(--input-border-color);padding:1em 1em 0}.input-container{display:flex;gap:8px}.message-textarea{background-color:var(--input-bg-color);border-color:var(--color-gray-300);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border:1px solid var(--input-border-color);color:var(--input-text-color);flex-grow:1;padding:.5em .75em;resize:none}.message-textarea:focus{outline-color:var(--input-outline-focus-color)}.send-button{border-radius:var(--radius-md);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;padding:.5em 1em;transition-duration:.2s}.send-button-enabled{background-color:var(--send-button-bg-color);color:var(--send-button-text-color)}.send-button-enabled:hover{background-color:var(--send-button-bg-hover-color)}.send-button-disabled{background-color:var(--send-button-bg-disabled-color);color:var(--send-button-text-disabled-color);cursor:not-allowed}.confirmation-overlay{align-items:center;background-color:var(--confirmation-overlay-bg-color);display:flex;inset:calc(var(--spacing)*0);justify-content:center;position:fixed;z-index:9999}.confirmation-dialog{background-color:var(--confirmation-dialog-bg-color);border:1px solid var(--confirmation-dialog-border-color);border-radius:.75em;box-shadow:0 .625em 1.5625em var(--confirmation-dialog-shadow-color);margin-inline:calc(var(--spacing)*4);max-width:var(--container-sm);width:100%}.confirmation-content{padding:1.5em}.confirmation-title{margin-bottom:calc(var(--spacing)*2);--tw-font-weight:var(--font-weight-semibold);color:var(--confirmation-title-color);font-size:var(--confirmation-title-font-size);font-weight:var(--font-weight-semibold)}.confirmation-message{color:var(--confirmation-message-color);font-size:var(--confirmation-message-font-size);margin-bottom:calc(var(--spacing)*4)}.confirmation-buttons{display:flex;gap:.75em;justify-content:flex-end}.confirmation-button{border-radius:var(--radius-md);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;padding:.5em 1em;transition-duration:.2s}.confirmation-button-cancel{background-color:var(--confirmation-button-cancel-bg-color);color:var(--confirmation-button-cancel-text-color)}.confirmation-button-cancel:hover{background-color:var(--confirmation-button-cancel-bg-hover-color)}.confirmation-button-confirm{background-color:var(--confirmation-button-confirm-bg-color);color:var(--confirmation-button-confirm-text-color)}.confirmation-button-confirm:hover{background-color:var(--confirmation-button-confirm-bg-hover-color)}}@layer utilities{:host{--chat-z-index:50;--button-background-color:#fff;--button-background-color-hover:#f3f4f6;--button-text-color:#111827;--button-text-color-hover:#1d4ed8;--button-border-color:#d1d5db;--button-border-color-hover:#6b7280;--button-font-size:1em;--button-icon-size:1.5em;--chat-window-height:60%;--chat-window-width:25%;--chat-window-fullscreen-width:80%;--chat-window-bg-color:#fff;--chat-window-border-color:#d1d5db;--chat-window-shadow-color:#0000001a;--chat-window-font-size:.875em;--chat-window-font-size-sm:.75em;--header-bg-color:transparent;--header-bg-hover-color:#f9fafb;--header-border-color:#f3f4f6;--header-button-text-color:#6b7280;--header-button-bg-hover-color:#f3f4f6;--header-font-size:1em;--header-text-font-size:1em;--header-text-color:#525762;--header-button-icon-size:1.5em;--starter-question-bg-color:transparent;--starter-question-bg-hover-color:#eff6ff;--starter-question-text-color:#3b82f6;--starter-question-border-color:#3b82f6;--starter-question-border-hover-color:#2563eb;--message-user-bg-color:#e4edfb;--message-user-text-color:#1f2937;--message-user-link-color:#155dfc;--message-assistant-bg-color:#eae7e8;--message-assistant-text-color:var(--message-user-text-color);--message-assistant-link-color:var(--message-user-link-color);--message-system-bg-color:#fbe4f8;--message-system-text-color:var(--message-user-text-color);--message-system-link-color:var(--message-user-link-color);--message-timestamp-color:#ffffffb3;--message-timestamp-assistant-color:#4b5563b3;--input-bg-color:transparent;--input-border-color:#d1d5db;--input-text-color:#111827;--input-placeholder-color:#6b7280;--input-outline-focus-color:#3b82f6;--send-button-bg-color:#3b82f6;--send-button-bg-hover-color:#2563eb;--send-button-text-color:#fff;--send-button-bg-disabled-color:#d1d5db;--send-button-text-disabled-color:#6b7280;--loading-text-color:#6b7280;--loading-spinner-track-color:#e5e7eb;--loading-spinner-fill-color:#3b82f6;--loading-spinner-size:1.25em;--typing-progress-bg-color:#ade3ff;--scrollbar-track-color:#f3f4f6;--scrollbar-thumb-color:#d1d5db;--scrollbar-thumb-hover-color:#9ca3af;--error-text-color:#ef4444;--success-text-color:#10b981;--code-bg-user-color:var(--message-user-bg-color);--code-text-user-color:var(--message-user-text-color);--code-border-user-color:var(--message-user-bg-color);--code-bg-assistant-color:var(--message-assistant-bg-color);--code-text-assistant-color:var(--message-assistant-text-color);--code-border-assistant-color:var(--message-assistant-bg-color);--confirmation-overlay-bg-color:#00000080;--confirmation-dialog-bg-color:var(--chat-window-bg-color);--confirmation-dialog-border-color:var(--chat-window-border-color);--confirmation-dialog-shadow-color:var(--chat-window-shadow-color);--confirmation-title-color:#111827;--confirmation-title-font-size:1.125em;--confirmation-message-color:var(--loading-text-color);--confirmation-message-font-size:1em;--confirmation-button-cancel-bg-color:var(--button-background-color-hover);--confirmation-button-cancel-bg-hover-color:#e5e7eb;--confirmation-button-cancel-text-color:var(--header-button-text-color);--confirmation-button-confirm-bg-color:var(--error-text-color);--confirmation-button-confirm-bg-hover-color:var(--error-text-color);--confirmation-button-confirm-text-color:var(--send-button-text-color);--file-attachment-button-bg-color:transparent;--file-attachment-button-bg-hover-color:var(--header-button-bg-hover-color);--file-attachment-button-text-color:var(--header-button-text-color);--file-attachment-button-text-disabled-color:var(--send-button-text-disabled-color);--selected-files-bg-color:var(--chat-window-bg-color);--selected-files-border-color:var(--header-border-color);--selected-file-bg-color:var(--button-background-color-hover);--selected-file-font-size:var(--chat-window-font-size-sm);--selected-file-name-color:var(--message-assistant-text-color);--selected-file-size-color:var(--input-placeholder-color);--selected-file-icon-size:1.25em;--selected-file-remove-icon-color:var(--error-text-color);--selected-file-remove-icon-hover-color:#dc2626;--message-attachment-icon-size:1em;bottom:30px;display:block;position:fixed;right:30px}@supports (color:color-mix(in lab,red,red)){:host{--code-bg-user-color:color-mix(in srgb,var(--message-user-bg-color)80%,#fff 20%);--code-border-user-color:color-mix(in srgb,var(--message-user-bg-color)90%,#000 10%);--code-bg-assistant-color:color-mix(in srgb,var(--message-assistant-bg-color)50%,#fff 50%);--code-border-assistant-color:color-mix(in srgb,var(--message-assistant-bg-color)90%,#000 10%)}}:host([mode=kiosk]){height:100%;inset:0 auto auto 0;position:absolute;width:100%}}textarea{max-height:calc(var(--spacing)*32);min-height:calc(var(--spacing)*10);overflow-y:auto;resize:none}.loading-spinner{animation:var(--animate-spin);border-color:var(--loading-spinner-track-color);border-radius:3.40282e+38px;border-style:var(--tw-border-style);border-top-color:var(--loading-spinner-fill-color);border-width:2px;height:var(--loading-spinner-size);width:var(--loading-spinner-size)}.overflow-y-auto::-webkit-scrollbar{height:calc(var(--spacing)*1.5);width:calc(var(--spacing)*1.5)}.overflow-y-auto::-webkit-scrollbar-track{background-color:var(--scrollbar-track-color);border-radius:var(--radius-sm)}.overflow-y-auto::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-color);border-radius:var(--radius-sm)}.overflow-y-auto::-webkit-scrollbar-thumb:hover{background-color:var(--scrollbar-thumb-hover-color)}.chat-markdown{color:var(--tw-prose-body);font-size:1rem;font-size:.875rem;line-height:1.75;line-height:1.71429;max-width:65ch;--tw-prose-body:oklch(37.3% .034 259.733);--tw-prose-headings:oklch(21% .034 264.665);--tw-prose-lead:oklch(44.6% .03 256.802);--tw-prose-links:oklch(21% .034 264.665);--tw-prose-bold:oklch(21% .034 264.665);--tw-prose-counters:oklch(55.1% .027 264.364);--tw-prose-bullets:oklch(87.2% .01 258.338);--tw-prose-hr:oklch(92.8% .006 264.531);--tw-prose-quotes:oklch(21% .034 264.665);--tw-prose-quote-borders:oklch(92.8% .006 264.531);--tw-prose-captions:oklch(55.1% .027 264.364);--tw-prose-kbd:oklch(21% .034 264.665);--tw-prose-kbd-shadows:NaN NaN NaN;--tw-prose-code:oklch(21% .034 264.665);--tw-prose-pre-code:oklch(92.8% .006 264.531);--tw-prose-pre-bg:oklch(27.8% .033 256.848);--tw-prose-th-borders:oklch(87.2% .01 258.338);--tw-prose-td-borders:oklch(92.8% .006 264.531);--tw-prose-invert-body:oklch(87.2% .01 258.338);--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:oklch(70.7% .022 261.325);--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:oklch(70.7% .022 261.325);--tw-prose-invert-bullets:oklch(44.6% .03 256.802);--tw-prose-invert-hr:oklch(37.3% .034 259.733);--tw-prose-invert-quotes:oklch(96.7% .003 264.542);--tw-prose-invert-quote-borders:oklch(37.3% .034 259.733);--tw-prose-invert-captions:oklch(70.7% .022 261.325);--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:255 255 255;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:oklch(87.2% .01 258.338);--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:oklch(44.6% .03 256.802);--tw-prose-invert-td-borders:oklch(37.3% .034 259.733);font-size:1em;max-width:none}.chat-markdown :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;font-size:1.28571em;line-height:1.6;line-height:1.55556;margin-bottom:.888889em;margin-top:.888889em}.chat-markdown :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);font-weight:500;text-decoration:underline;text-decoration-line:none}.chat-markdown :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.chat-markdown :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.chat-markdown :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-bottom:1.25em;margin-top:1.25em;padding-inline-start:1.625em}.chat-markdown :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.chat-markdown :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.chat-markdown :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.chat-markdown :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.chat-markdown :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.chat-markdown :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.chat-markdown :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.chat-markdown :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.chat-markdown :where(ol[type=\"1\"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.chat-markdown :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-bottom:1.25em;margin-top:1.25em;padding-inline-start:1.625em}.chat-markdown :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-counters);font-weight:400}.chat-markdown :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.chat-markdown :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.14286em}.chat-markdown :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-bottom:2.85714em;margin-top:2.85714em}.chat-markdown :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){border-inline-start-color:var(--tw-prose-quote-borders);border-inline-start-width:.25rem;color:var(--tw-prose-quotes);font-style:italic;font-weight:500;margin-bottom:1.33333em;margin-top:1.33333em;padding-inline-start:1em;padding-inline-start:1.11111em;quotes:\"\u201C\"\"\u201D\"\"\u2018\"\"\u2019\"}.chat-markdown :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.chat-markdown :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.chat-markdown :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-size:2.25em;font-size:2.14286em;font-weight:800;line-height:1.11111;line-height:1.2;margin-bottom:.8em;margin-top:0}.chat-markdown :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:900}.chat-markdown :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-size:1.5em;font-size:1.42857em;font-weight:700;line-height:1.33333;line-height:1.4;margin-bottom:.8em;margin-top:1.6em}.chat-markdown :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:800}.chat-markdown :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-size:1.25em;font-size:1.28571em;font-weight:600;line-height:1.6;line-height:1.55556;margin-bottom:.444444em;margin-top:1.55556em}.chat-markdown :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.chat-markdown :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;line-height:1.5;line-height:1.42857;margin-bottom:.571429em;margin-top:1.42857em}.chat-markdown :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.chat-markdown :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-bottom:2em;margin-top:2em}.chat-markdown :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){border-radius:.3125rem;box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows)/10%),0 3px 0 rgb(var(--tw-prose-kbd-shadows)/10%);color:var(--tw-prose-kbd);font-family:inherit;font-size:.875em;font-size:.857143em;font-weight:500;padding-inline-end:.375em;padding-inline-end:.357143em;padding-bottom:.142857em;padding-inline-start:.375em;padding-top:.142857em;padding-inline-start:.357143em}.chat-markdown :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-size:.875em;font-size:.857143em;font-weight:600}.chat-markdown :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after,.chat-markdown :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:\"\\`\"}.chat-markdown :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.chat-markdown :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em;font-size:.9em}.chat-markdown :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em;font-size:.888889em}.chat-markdown :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.chat-markdown :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:var(--tw-prose-pre-bg);border-radius:.375rem;border-radius:.25rem;color:var(--tw-prose-pre-code);font-size:.875em;font-size:.857143em;font-weight:400;line-height:1.71429;line-height:1.66667;margin-bottom:1.66667em;margin-top:1.66667em;overflow-x:auto;padding-inline-end:1.14286em;padding-inline-end:1em;padding-bottom:.666667em;padding-inline-start:1.14286em;padding-top:.666667em;padding-inline-start:1em}.chat-markdown :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:#0000;border-radius:0;border-width:0;color:inherit;font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;padding:0}.chat-markdown :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after,.chat-markdown :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.chat-markdown :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;font-size:.857143em;line-height:1.71429;line-height:1.5;margin-bottom:2em;margin-top:2em;table-layout:auto;width:100%}.chat-markdown :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-color:var(--tw-prose-th-borders);border-bottom-width:1px}.chat-markdown :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;padding-inline-end:.571429em;padding-inline-end:1em;padding-bottom:.666667em;padding-inline-start:.571429em;padding-inline-start:1em;vertical-align:bottom}.chat-markdown :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-color:var(--tw-prose-td-borders);border-bottom-width:1px}.chat-markdown :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.chat-markdown :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.chat-markdown :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-color:var(--tw-prose-th-borders);border-top-width:1px}.chat-markdown :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.chat-markdown :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.chat-markdown :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;font-size:.857143em;line-height:1.42857;line-height:1.33333;margin-top:.666667em}.chat-markdown :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.75em;margin-top:.75em}.chat-markdown :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.chat-markdown :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.chat-markdown :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.chat-markdown :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.chat-markdown :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.chat-markdown :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.chat-markdown :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em;margin-top:1.14286em}.chat-markdown :where(img):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.71429em;margin-top:1.71429em}.chat-markdown :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0;margin-top:0}.chat-markdown :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.71429em;margin-top:1.71429em}.chat-markdown :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em;margin-top:1.14286em;padding-inline-start:1.57143em}.chat-markdown :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.285714em;margin-top:.285714em}.chat-markdown :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.428571em}.chat-markdown :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.571429em;margin-top:.571429em}.chat-markdown :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.14286em}.chat-markdown :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em}.chat-markdown :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.14286em}.chat-markdown :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em}.chat-markdown :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.571429em;margin-top:.571429em}.chat-markdown :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em;margin-top:1.14286em}.chat-markdown :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.285714em;padding-inline-start:1.57143em}.chat-markdown :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.chat-markdown :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.chat-markdown :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.chat-markdown :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.666667em;padding-top:.666667em;padding-inline-start:1em}.chat-markdown :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.chat-markdown :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.chat-markdown :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.71429em;margin-top:1.71429em}.chat-markdown :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0;margin-top:0}.chat-markdown :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.chat-markdown :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.chat-markdown>*{margin-bottom:.1em;margin-top:.1em}.message-bubble-assistant .chat-markdown{--tw-prose-body:var(--message-assistant-text-color);--tw-prose-headings:var(--message-assistant-text-color);--tw-prose-lead:var(--message-assistant-text-color);--tw-prose-links:var(--message-assistant-link-color);--tw-prose-bold:var(--message-assistant-text-color);--tw-prose-counters:var(--message-assistant-text-color);--tw-prose-bullets:var(--message-assistant-text-color);--tw-prose-hr:var(--message-assistant-text-color);--tw-prose-quotes:var(--message-assistant-text-color);--tw-prose-quote-borders:var(--message-assistant-text-color);--tw-prose-captions:var(--message-assistant-text-color);--tw-prose-kbd:var(--message-assistant-text-color);--tw-prose-kbd-shadows:var(--message-assistant-text-color);--tw-prose-code:var(--code-text-assistant-color);--tw-prose-pre-code:var(--code-text-assistant-color);--tw-prose-pre-bg:var(--code-bg-assistant-color);--tw-prose-th-borders:var(--message-assistant-text-color);--tw-prose-td-borders:var(--message-assistant-text-color)}.message-bubble-user .chat-markdown{--tw-prose-body:var(--message-user-text-color);--tw-prose-headings:var(--message-user-text-color);--tw-prose-lead:var(--message-user-text-color);--tw-prose-links:var(--message-user-link-color);--tw-prose-bold:var(--message-user-text-color);--tw-prose-counters:var(--message-user-text-color);--tw-prose-bullets:var(--message-user-text-color);--tw-prose-hr:var(--message-user-text-color);--tw-prose-quotes:var(--message-user-text-color);--tw-prose-quote-borders:var(--message-user-text-color);--tw-prose-captions:var(--message-user-text-color);--tw-prose-kbd:var(--message-user-text-color);--tw-prose-kbd-shadows:var(--message-user-text-color);--tw-prose-code:var(--code-text-user-color);--tw-prose-pre-code:var(--code-text-user-color);--tw-prose-pre-bg:var(--code-bg-user-color);--tw-prose-th-borders:var(--message-user-text-color);--tw-prose-td-borders:var(--message-user-text-color)}.message-bubble-system .chat-markdown{--tw-prose-body:var(--message-system-text-color);--tw-prose-headings:var(--message-system-text-color);--tw-prose-lead:var(--message-system-text-color);--tw-prose-links:var(--message-system-link-color);--tw-prose-bold:var(--message-system-text-color);--tw-prose-counters:var(--message-system-text-color);--tw-prose-bullets:var(--message-system-text-color);--tw-prose-hr:var(--message-system-text-color);--tw-prose-quotes:var(--message-system-text-color);--tw-prose-quote-borders:var(--message-system-text-color);--tw-prose-captions:var(--message-system-text-color);--tw-prose-kbd:var(--message-system-text-color);--tw-prose-kbd-shadows:var(--message-system-text-color);--tw-prose-code:var(--message-system-text-color);--tw-prose-pre-code:var(--message-system-text-color);--tw-prose-pre-bg:var(--message-system-text-color);--tw-prose-th-borders:var(--message-system-text-color);--tw-prose-td-borders:var(--message-system-text-color)}.message-bubble-user .chat-markdown pre{border:1px solid var(--code-border-user-color)}.message-bubble-assistant .chat-markdown pre{border:1px solid var(--code-border-assistant-color)}.loading:after{content:\" .\"}.file-attachment-button{border-radius:var(--radius-md);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;background-color:var(--file-attachment-button-bg-color);color:var(--file-attachment-button-text-color);padding:.375em;transition-duration:.2s}.file-attachment-button:disabled{color:var(--file-attachment-button-text-disabled-color);cursor:not-allowed;opacity:.5}.file-attachment-button svg{height:1.5em;width:1.5em}.file-attachment-button:hover:not(:disabled){background-color:var(--file-attachment-button-bg-hover-color)}.selected-files-container{background-color:var(--selected-files-bg-color);border-top:1px solid var(--selected-files-border-color);padding:1em 1em .5em}.selected-file-item{align-items:center;background-color:var(--selected-file-bg-color);border-radius:.375em;color:var(--selected-file-name-color);display:flex;font-size:var(--selected-file-font-size);justify-content:space-between;padding:.25em .5em}.selected-file-icon{align-items:center;display:flex;justify-content:center}.selected-file-icon svg{height:var(--selected-file-icon-size);width:var(--selected-file-icon-size)}.selected-file-size{color:var(--selected-file-size-color)}.selected-file-error{color:var(--error-text-color)}.selected-file-success-icon{align-items:center;color:var(--success-text-color);display:flex;height:var(--selected-file-icon-size);justify-content:center;width:var(--selected-file-icon-size)}.selected-file-remove-button{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;color:var(--selected-file-remove-icon-color);padding:.375em;transition-duration:.2s}.selected-file-remove-button svg{height:var(--selected-file-icon-size);width:var(--selected-file-icon-size)}.selected-file-remove-button:hover{color:var(--selected-file-remove-icon-hover-color)}.message-attachments{font-size:var(--chat-window-font-size-sm);margin-top:.5em}:where(.message-attachments>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(.25em*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(.25em*var(--tw-space-y-reverse))}.message-attachment-icon{align-items:center;display:flex;height:var(--message-attachment-icon-size);justify-content:center;width:var(--message-attachment-icon-size)}.send-button-disabled{background-color:var(--send-button-bg-disabled-color);color:var(--send-button-text-disabled-color);cursor:not-allowed}@property --tw-rotate-x{syntax:\"*\";inherits:false}@property --tw-rotate-y{syntax:\"*\";inherits:false}@property --tw-rotate-z{syntax:\"*\";inherits:false}@property --tw-skew-x{syntax:\"*\";inherits:false}@property --tw-skew-y{syntax:\"*\";inherits:false}@property --tw-space-y-reverse{syntax:\"*\";inherits:false;initial-value:0}@property --tw-border-style{syntax:\"*\";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:\"*\";inherits:false}@property --tw-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:\"*\";inherits:false}@property --tw-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:\"*\";inherits:false}@property --tw-inset-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:\"*\";inherits:false}@property --tw-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:\"*\";inherits:false}@property --tw-inset-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:\"*\";inherits:false}@property --tw-ring-offset-width{syntax:\"<length>\";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:\"*\";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-duration{syntax:\"*\";inherits:false}@property --tw-ease{syntax:\"*\";inherits:false}@property --tw-scale-x{syntax:\"*\";inherits:false;initial-value:1}@property --tw-scale-y{syntax:\"*\";inherits:false;initial-value:1}@property --tw-scale-z{syntax:\"*\";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(1turn)}}@keyframes progress{0%{transform:translate(0)scaleX(0)}10%{transform:translate(0)scaleX(.3)}50%{transform:translate(100%)scaleX(.3)}90%{transform:translate(0)scaleX(.3)}to{transform:translate(0)scaleX(0)}}@keyframes dots{0%,20%{color:#0000;text-shadow:.25em 0 #0000,.5em 0 #0000}40%{color:#000;text-shadow:.25em 0 #0000,.5em 0 #0000}60%{text-shadow:.25em 0 #000,.5em 0 #0000}80%,to{text-shadow:.25em 0 #000,.5em 0 #000}}@media (min-width:40rem){.container{max-width:40rem}.chat-window-normal{height:var(--chat-window-height);width:var(--chat-window-width)}.drag-indicator{display:flex}.fullscreen-button{display:block}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}@media (hover:hover){.chat-btn-icon:hover,.chat-btn-text:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x)var(--tw-scale-y)}.attachment-link:hover{text-decoration-line:none}.chat-markdown :where(a):not(:where([class~=not-prose],[class~=not-prose] *)):hover{text-decoration-line:underline}}";
6247
+ const ocsChatCss = "/*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */@layer properties{@supports ((-webkit-hyphens:none) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,::backdrop,:after,:before{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:host,:root{--font-sans:ui-sans-serif,system-ui,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace;--color-blue-300:oklch(80.9% .105 251.813);--color-slate-500:oklch(55.4% .046 257.417);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--spacing:.25rem;--breakpoint-lg:64rem;--container-sm:24rem;--font-weight-light:300;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--animate-progress:progress 3s infinite linear;--animate-dots:dots 1s steps(5,end)infinite;--transform-origin-left-right:0% 50%}}@layer base{*,::backdrop,:after,:before{border:0 solid;border-color:var(--color-gray-200,currentcolor);box-sizing:border-box;margin:0;padding:0}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button;background-color:#0000;border:0 solid;border-color:var(--color-gray-200,currentcolor);border-radius:0;box-sizing:border-box;color:inherit;font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;margin:0;margin-inline-end:4px;opacity:1;padding:0}:host,html{-webkit-text-size-adjust:100%;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-size:1em;font-variation-settings:var(--default-mono-font-variation-settings,normal)}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}menu,ol,ul{list-style:none}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}button,input,optgroup,select,textarea{background-color:#0000;border-radius:0;color:inherit;font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;opacity:1}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::-moz-placeholder{opacity:1}::placeholder{opacity:1}@supports (not (-webkit-appearance:-apple-pay-button)) or (contain-intrinsic-size:1px){::-moz-placeholder{color:currentColor}::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::-moz-placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex;padding-block:0}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.fixed{position:fixed}.relative{position:relative}.static{position:static}.container{width:100%}.block{display:block}.contents{display:contents}.flex{display:flex}.hidden{display:none}.w-full{width:100%}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.resize{resize:both}.items-center{align-items:center}.justify-center{justify-content:center}.gap-\\[0\\.5em\\]{gap:.5em}:where(.space-y-\\[0\\.25em\\]>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(.25em*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(.25em*var(--tw-space-y-reverse))}.border{border-style:var(--tw-border-style);border-width:1px}.py-\\[2px\\]{padding-block:2px}.text-\\[0\\.8em\\]{font-size:.8em}.font-light{--tw-font-weight:var(--font-weight-light);font-weight:var(--font-weight-light)}.text-slate-500{color:var(--color-slate-500)}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a)}.ring,.shadow{box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor)}.transition{transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,backdrop-filter,display,visibility,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))}} /*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */@layer properties{}@layer base{}@layer components; /*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */@layer properties{}@layer base{}@layer components{#ocs-chat-window{font-size:var(--chat-window-font-size);z-index:var(--chat-z-index)}.starter-question{border-radius:var(--radius-lg);text-align:left;--tw-duration:.2s;background-color:var(--starter-question-bg-color);border:1px solid var(--starter-question-border-color);color:var(--starter-question-text-color);padding:.75em;transition-duration:.2s}.starter-question:hover{background-color:var(--starter-question-bg-hover-color);border-color:var(--starter-question-border-hover-color)}.chat-btn-text{border-radius:var(--radius-lg);border-style:var(--tw-border-style);transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;--tw-ease:var(--ease-in-out);align-items:center;background-color:var(--button-background-color,#fff);border-width:0;border:1px solid var(--button-border-color);color:var(--button-text-color,#111827);display:flex;font-size:var(--button-font-size);gap:8px;padding:.5em;transition-duration:.2s;transition-timing-function:var(--ease-in-out);z-index:var(--chat-z-index,50)}.chat-btn-text:hover{border:1px solid var(--button-border-color-hover);color:var(--button-text-color-hover,#1d4ed8)}.chat-btn-text span{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);white-space:nowrap}.chat-btn-text img{flex-shrink:0;height:var(--button-icon-size);-o-object-fit:contain;object-fit:contain;width:var(--button-icon-size)}.chat-btn-text.round{border-radius:3.40282e+38px}.chat-btn-icon{border-radius:var(--radius-lg);border-style:var(--tw-border-style);transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,);--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;--tw-ease:var(--ease-in-out);background-color:var(--button-background-color,#fff);border-width:0;border:1px solid var(--button-border-color);font-size:var(--button-font-size);padding:.5em;transition-duration:.2s;transition-timing-function:var(--ease-in-out);z-index:var(--chat-z-index,50)}.chat-btn-icon:hover{border:1px solid var(--button-border-color-hover);color:var(--button-text-color-hover,#1d4ed8)}.chat-btn-icon img{height:var(--button-icon-size);-o-object-fit:contain;object-fit:contain;width:var(--button-icon-size)}.chat-btn-icon.round,.round .chat-btn-icon,.round.chat-btn-text{border-radius:3.40282e+38px}.error-message{color:var(--error-text-color);padding:.5em}.chat-window-fullscreen{border-style:var(--tw-border-style);inset:calc(var(--spacing)*0);z-index:9999;--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);height:100%;max-height:100%;transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));width:100%;--tw-duration:.2s;border-radius:0;border-width:0;max-width:var(--chat-window-fullscreen-width);transition-duration:.2s}.chat-window-fullscreen,.chat-window-normal{background-color:var(--chat-window-bg-color);display:flex;flex-direction:column;overflow:hidden;position:fixed}.chat-window-normal{border:1px solid var(--chat-window-border-color);border-radius:var(--radius-lg);height:100vh;max-width:var(--breakpoint-lg);min-height:300px;min-width:300px;width:100vw}.chat-window-normal:not(.chat-window-dragging){--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;transition-duration:.2s}.chat-window-dragging,.chat-window-normal:not(.chat-window-dragging){box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.chat-window-dragging{cursor:grabbing;--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040)}.chat-window-kiosk{background-color:var(--chat-window-bg-color);border-radius:0;box-shadow:none;display:flex;flex-direction:column;font-size:var(--chat-window-font-size);height:100%;inset:calc(var(--spacing)*0);overflow:hidden;position:absolute;width:100%;z-index:var(--chat-z-index)}.chat-header{transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.15s;align-items:center;background-color:var(--header-bg-color);border-bottom:1px solid var(--header-border-color);display:flex;font-size:var(--header-font-size);justify-content:space-between;padding:.5em;transition-duration:.15s}.chat-header:active,.chat-header:hover{background-color:var(--header-bg-hover-color)}.header-text{align-items:center;color:var(--header-text-color);display:flex;font-size:var(--header-text-font-size);justify-content:center}.chat-header-draggable{cursor:grab}.chat-header-dragging{cursor:grabbing}.drag-indicator{display:none}.drag-dots{display:flex;gap:2px;margin-left:2px;pointer-events:none}.header-buttons{align-items:center;display:flex;gap:4px}.header-button{border-radius:var(--radius-md);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;color:var(--header-button-text-color);padding:.375em;transition-duration:.2s}.header-button svg{height:var(--header-button-icon-size);width:var(--header-button-icon-size)}.header-button:hover{background-color:var(--header-button-bg-hover-color)}.fullscreen-button{display:none}.chat-content{display:flex;flex-direction:column;flex-grow:1;overflow:hidden}.loading-container{align-items:center;display:flex;flex-grow:1;justify-content:center}.loading-text{color:var(--loading-text-color);margin-left:2px}.messages-container{flex-grow:1;overflow-y:auto;padding:1em}:where(.messages-container>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(var(--spacing)*2*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(var(--spacing)*2*var(--tw-space-y-reverse))}.message-row{display:flex}.message-row-user{justify-content:flex-end}.message-row-assistant{justify-content:flex-start}.message-bubble{border-radius:var(--radius-lg);padding:.5em 1em}.message-bubble-user{background-color:var(--message-user-bg-color);color:var(--message-user-text-color)}.message-bubble-assistant{background-color:var(--message-assistant-bg-color);color:var(--message-assistant-text-color)}.message-bubble-system{background-color:var(--message-system-bg-color);color:var(--message-system-text-color)}.message-timestamp{font-size:var(--chat-window-font-size-sm);margin-top:4px;opacity:.7}.message-attachments{margin-top:8px}:where(.message-attachments>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(var(--spacing)*1*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(var(--spacing)*1*var(--tw-space-y-reverse))}.attachment-link{display:block;text-decoration-line:underline}:where(.welcome-messages>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(var(--spacing)*2*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(var(--spacing)*2*var(--tw-space-y-reverse))}.typing-indicator{height:calc(var(--spacing)*1.5);overflow:hidden;width:100%}.typing-progress{animation:var(--animate-progress);background-color:var(--typing-progress-bg-color);border-radius:var(--radius-lg);height:100%;transform-origin:var(--transform-origin-left-right);width:100%}.typing-text{font-size:var(--chat-window-font-size-sm);justify-content:center;opacity:.7;width:100%}.typing-dots{animation:var(--animate-dots)}:where(.starter-questions>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(var(--spacing)*2*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(var(--spacing)*2*var(--tw-space-y-reverse))}.starter-questions{padding:1em}.starter-question-row{display:flex;justify-content:flex-end}.input-area{background-color:var(--input-bg-color);border-top:1px solid var(--input-border-color);padding:1em 1em 0}.input-container{display:flex;gap:8px}.message-textarea{background-color:var(--input-bg-color);border-color:var(--color-gray-300);border-radius:var(--radius-md);border-style:var(--tw-border-style);border-width:1px;border:1px solid var(--input-border-color);color:var(--input-text-color);flex-grow:1;padding:.5em .75em;resize:none}.message-textarea:focus{outline-color:var(--input-outline-focus-color)}.send-button{border-radius:var(--radius-md);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;padding:.5em 1em;transition-duration:.2s}.send-button-enabled{background-color:var(--send-button-bg-color);color:var(--send-button-text-color)}.send-button-enabled:hover{background-color:var(--send-button-bg-hover-color)}.send-button-disabled{background-color:var(--send-button-bg-disabled-color);color:var(--send-button-text-disabled-color);cursor:not-allowed}.confirmation-overlay{align-items:center;background-color:var(--confirmation-overlay-bg-color);display:flex;inset:calc(var(--spacing)*0);justify-content:center;position:fixed;z-index:9999}.confirmation-dialog{background-color:var(--confirmation-dialog-bg-color);border:1px solid var(--confirmation-dialog-border-color);border-radius:.75em;box-shadow:0 .625em 1.5625em var(--confirmation-dialog-shadow-color);margin-inline:calc(var(--spacing)*4);max-width:var(--container-sm);width:100%}.confirmation-content{padding:1.5em}.confirmation-title{margin-bottom:calc(var(--spacing)*2);--tw-font-weight:var(--font-weight-semibold);color:var(--confirmation-title-color);font-size:var(--confirmation-title-font-size);font-weight:var(--font-weight-semibold)}.confirmation-message{color:var(--confirmation-message-color);font-size:var(--confirmation-message-font-size);margin-bottom:calc(var(--spacing)*4)}.confirmation-buttons{display:flex;gap:.75em;justify-content:flex-end}.confirmation-button{border-radius:var(--radius-md);--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;padding:.5em 1em;transition-duration:.2s}.confirmation-button-cancel{background-color:var(--confirmation-button-cancel-bg-color);color:var(--confirmation-button-cancel-text-color)}.confirmation-button-cancel:hover{background-color:var(--confirmation-button-cancel-bg-hover-color)}.confirmation-button-confirm{background-color:var(--confirmation-button-confirm-bg-color);color:var(--confirmation-button-confirm-text-color)}.confirmation-button-confirm:hover{background-color:var(--confirmation-button-confirm-bg-hover-color)}}@layer utilities{:host{--chat-z-index:50;--button-background-color:#fff;--button-background-color-hover:#f3f4f6;--button-text-color:#111827;--button-text-color-hover:#1d4ed8;--button-border-color:#d1d5db;--button-border-color-hover:#6b7280;--button-font-size:1em;--button-icon-size:1.5em;--chat-window-height:60%;--chat-window-width:25%;--chat-window-fullscreen-width:80%;--chat-window-bg-color:#fff;--chat-window-border-color:#d1d5db;--chat-window-shadow-color:#0000001a;--chat-window-font-size:.875em;--chat-window-font-size-sm:.75em;--header-bg-color:transparent;--header-bg-hover-color:#f9fafb;--header-border-color:#f3f4f6;--header-button-text-color:#6b7280;--header-button-bg-hover-color:#f3f4f6;--header-font-size:1em;--header-text-font-size:1em;--header-text-color:#525762;--header-button-icon-size:1.5em;--starter-question-bg-color:transparent;--starter-question-bg-hover-color:#eff6ff;--starter-question-text-color:#3b82f6;--starter-question-border-color:#3b82f6;--starter-question-border-hover-color:#2563eb;--message-user-bg-color:#e4edfb;--message-user-text-color:#1f2937;--message-user-link-color:#155dfc;--message-assistant-bg-color:#eae7e8;--message-assistant-text-color:var(--message-user-text-color);--message-assistant-link-color:var(--message-user-link-color);--message-system-bg-color:#fbe4f8;--message-system-text-color:var(--message-user-text-color);--message-system-link-color:var(--message-user-link-color);--message-timestamp-color:#ffffffb3;--message-timestamp-assistant-color:#4b5563b3;--input-bg-color:transparent;--input-border-color:#d1d5db;--input-text-color:#111827;--input-placeholder-color:#6b7280;--input-outline-focus-color:#3b82f6;--send-button-bg-color:#3b82f6;--send-button-bg-hover-color:#2563eb;--send-button-text-color:#fff;--send-button-bg-disabled-color:#d1d5db;--send-button-text-disabled-color:#6b7280;--loading-text-color:#6b7280;--loading-spinner-track-color:#e5e7eb;--loading-spinner-fill-color:#3b82f6;--loading-spinner-size:1.25em;--typing-progress-bg-color:#ade3ff;--scrollbar-track-color:#f3f4f6;--scrollbar-thumb-color:#d1d5db;--scrollbar-thumb-hover-color:#9ca3af;--error-text-color:#ef4444;--success-text-color:#10b981;--code-bg-user-color:var(--message-user-bg-color);--code-text-user-color:var(--message-user-text-color);--code-border-user-color:var(--message-user-bg-color);--code-bg-assistant-color:var(--message-assistant-bg-color);--code-text-assistant-color:var(--message-assistant-text-color);--code-border-assistant-color:var(--message-assistant-bg-color);--confirmation-overlay-bg-color:#00000080;--confirmation-dialog-bg-color:var(--chat-window-bg-color);--confirmation-dialog-border-color:var(--chat-window-border-color);--confirmation-dialog-shadow-color:var(--chat-window-shadow-color);--confirmation-title-color:#111827;--confirmation-title-font-size:1.125em;--confirmation-message-color:var(--loading-text-color);--confirmation-message-font-size:1em;--confirmation-button-cancel-bg-color:var(--button-background-color-hover);--confirmation-button-cancel-bg-hover-color:#e5e7eb;--confirmation-button-cancel-text-color:var(--header-button-text-color);--confirmation-button-confirm-bg-color:var(--error-text-color);--confirmation-button-confirm-bg-hover-color:var(--error-text-color);--confirmation-button-confirm-text-color:var(--send-button-text-color);--file-attachment-button-bg-color:transparent;--file-attachment-button-bg-hover-color:var(--header-button-bg-hover-color);--file-attachment-button-text-color:var(--header-button-text-color);--file-attachment-button-text-disabled-color:var(--send-button-text-disabled-color);--selected-files-bg-color:var(--chat-window-bg-color);--selected-files-border-color:var(--header-border-color);--selected-file-bg-color:var(--button-background-color-hover);--selected-file-font-size:var(--chat-window-font-size-sm);--selected-file-name-color:var(--message-assistant-text-color);--selected-file-size-color:var(--input-placeholder-color);--selected-file-icon-size:1.25em;--selected-file-remove-icon-color:var(--error-text-color);--selected-file-remove-icon-hover-color:#dc2626;--message-attachment-icon-size:1em;bottom:30px;display:block;position:fixed;right:30px}@supports (color:color-mix(in lab,red,red)){:host{--code-bg-user-color:color-mix(in srgb,var(--message-user-bg-color)80%,#fff 20%);--code-border-user-color:color-mix(in srgb,var(--message-user-bg-color)90%,#000 10%);--code-bg-assistant-color:color-mix(in srgb,var(--message-assistant-bg-color)50%,#fff 50%);--code-border-assistant-color:color-mix(in srgb,var(--message-assistant-bg-color)90%,#000 10%)}}:host([mode=kiosk]){height:100%;inset:0 auto auto 0;position:absolute;width:100%}}textarea{max-height:calc(var(--spacing)*32);min-height:calc(var(--spacing)*10);overflow-y:auto;resize:none}.loading-spinner{animation:var(--animate-spin);border-color:var(--loading-spinner-track-color);border-radius:3.40282e+38px;border-style:var(--tw-border-style);border-top-color:var(--loading-spinner-fill-color);border-width:2px;height:var(--loading-spinner-size);width:var(--loading-spinner-size)}.overflow-y-auto::-webkit-scrollbar{height:calc(var(--spacing)*1.5);width:calc(var(--spacing)*1.5)}.overflow-y-auto::-webkit-scrollbar-track{background-color:var(--scrollbar-track-color);border-radius:var(--radius-sm)}.overflow-y-auto::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-color);border-radius:var(--radius-sm)}.overflow-y-auto::-webkit-scrollbar-thumb:hover{background-color:var(--scrollbar-thumb-hover-color)}.chat-markdown{color:var(--tw-prose-body);font-size:1rem;font-size:.875rem;line-height:1.75;line-height:1.71429;max-width:65ch;--tw-prose-body:oklch(37.3% .034 259.733);--tw-prose-headings:oklch(21% .034 264.665);--tw-prose-lead:oklch(44.6% .03 256.802);--tw-prose-links:oklch(21% .034 264.665);--tw-prose-bold:oklch(21% .034 264.665);--tw-prose-counters:oklch(55.1% .027 264.364);--tw-prose-bullets:oklch(87.2% .01 258.338);--tw-prose-hr:oklch(92.8% .006 264.531);--tw-prose-quotes:oklch(21% .034 264.665);--tw-prose-quote-borders:oklch(92.8% .006 264.531);--tw-prose-captions:oklch(55.1% .027 264.364);--tw-prose-kbd:oklch(21% .034 264.665);--tw-prose-kbd-shadows:NaN NaN NaN;--tw-prose-code:oklch(21% .034 264.665);--tw-prose-pre-code:oklch(92.8% .006 264.531);--tw-prose-pre-bg:oklch(27.8% .033 256.848);--tw-prose-th-borders:oklch(87.2% .01 258.338);--tw-prose-td-borders:oklch(92.8% .006 264.531);--tw-prose-invert-body:oklch(87.2% .01 258.338);--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:oklch(70.7% .022 261.325);--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:oklch(70.7% .022 261.325);--tw-prose-invert-bullets:oklch(44.6% .03 256.802);--tw-prose-invert-hr:oklch(37.3% .034 259.733);--tw-prose-invert-quotes:oklch(96.7% .003 264.542);--tw-prose-invert-quote-borders:oklch(37.3% .034 259.733);--tw-prose-invert-captions:oklch(70.7% .022 261.325);--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:255 255 255;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:oklch(87.2% .01 258.338);--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:oklch(44.6% .03 256.802);--tw-prose-invert-td-borders:oklch(37.3% .034 259.733);font-size:1em;max-width:none}.chat-markdown :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;font-size:1.28571em;line-height:1.6;line-height:1.55556;margin-bottom:.888889em;margin-top:.888889em}.chat-markdown :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);font-weight:500;text-decoration:underline;text-decoration-line:none}.chat-markdown :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.chat-markdown :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.chat-markdown :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-bottom:1.25em;margin-top:1.25em;padding-inline-start:1.625em}.chat-markdown :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.chat-markdown :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.chat-markdown :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.chat-markdown :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.chat-markdown :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.chat-markdown :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.chat-markdown :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.chat-markdown :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.chat-markdown :where(ol[type=\"1\"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.chat-markdown :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-bottom:1.25em;margin-top:1.25em;padding-inline-start:1.625em}.chat-markdown :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-counters);font-weight:400}.chat-markdown :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.chat-markdown :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.14286em}.chat-markdown :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-bottom:2.85714em;margin-top:2.85714em}.chat-markdown :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){border-inline-start-color:var(--tw-prose-quote-borders);border-inline-start-width:.25rem;color:var(--tw-prose-quotes);font-style:italic;font-weight:500;margin-bottom:1.33333em;margin-top:1.33333em;padding-inline-start:1em;padding-inline-start:1.11111em;quotes:\"\u201C\"\"\u201D\"\"\u2018\"\"\u2019\"}.chat-markdown :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.chat-markdown :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.chat-markdown :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-size:2.25em;font-size:2.14286em;font-weight:800;line-height:1.11111;line-height:1.2;margin-bottom:.8em;margin-top:0}.chat-markdown :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:900}.chat-markdown :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-size:1.5em;font-size:1.42857em;font-weight:700;line-height:1.33333;line-height:1.4;margin-bottom:.8em;margin-top:1.6em}.chat-markdown :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:800}.chat-markdown :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-size:1.25em;font-size:1.28571em;font-weight:600;line-height:1.6;line-height:1.55556;margin-bottom:.444444em;margin-top:1.55556em}.chat-markdown :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.chat-markdown :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;line-height:1.5;line-height:1.42857;margin-bottom:.571429em;margin-top:1.42857em}.chat-markdown :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.chat-markdown :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-bottom:2em;margin-top:2em}.chat-markdown :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){border-radius:.3125rem;box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows)/10%),0 3px 0 rgb(var(--tw-prose-kbd-shadows)/10%);color:var(--tw-prose-kbd);font-family:inherit;font-size:.875em;font-size:.857143em;font-weight:500;padding-inline-end:.375em;padding-inline-end:.357143em;padding-bottom:.142857em;padding-inline-start:.375em;padding-top:.142857em;padding-inline-start:.357143em}.chat-markdown :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-size:.875em;font-size:.857143em;font-weight:600}.chat-markdown :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after,.chat-markdown :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:\"\\`\"}.chat-markdown :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.chat-markdown :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em;font-size:.9em}.chat-markdown :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em;font-size:.888889em}.chat-markdown :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.chat-markdown :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:var(--tw-prose-pre-bg);border-radius:.375rem;border-radius:.25rem;color:var(--tw-prose-pre-code);font-size:.875em;font-size:.857143em;font-weight:400;line-height:1.71429;line-height:1.66667;margin-bottom:1.66667em;margin-top:1.66667em;overflow-x:auto;padding-inline-end:1.14286em;padding-inline-end:1em;padding-bottom:.666667em;padding-inline-start:1.14286em;padding-top:.666667em;padding-inline-start:1em}.chat-markdown :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:#0000;border-radius:0;border-width:0;color:inherit;font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;padding:0}.chat-markdown :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after,.chat-markdown :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.chat-markdown :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;font-size:.857143em;line-height:1.71429;line-height:1.5;margin-bottom:2em;margin-top:2em;table-layout:auto;width:100%}.chat-markdown :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-color:var(--tw-prose-th-borders);border-bottom-width:1px}.chat-markdown :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;padding-inline-end:.571429em;padding-inline-end:1em;padding-bottom:.666667em;padding-inline-start:.571429em;padding-inline-start:1em;vertical-align:bottom}.chat-markdown :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-color:var(--tw-prose-td-borders);border-bottom-width:1px}.chat-markdown :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.chat-markdown :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:baseline}.chat-markdown :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-color:var(--tw-prose-th-borders);border-top-width:1px}.chat-markdown :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.chat-markdown :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.chat-markdown :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;font-size:.857143em;line-height:1.42857;line-height:1.33333;margin-top:.666667em}.chat-markdown :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.75em;margin-top:.75em}.chat-markdown :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.chat-markdown :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.chat-markdown :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.chat-markdown :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.chat-markdown :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.chat-markdown :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.chat-markdown :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em;margin-top:1.14286em}.chat-markdown :where(img):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.71429em;margin-top:1.71429em}.chat-markdown :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0;margin-top:0}.chat-markdown :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.71429em;margin-top:1.71429em}.chat-markdown :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em;margin-top:1.14286em;padding-inline-start:1.57143em}.chat-markdown :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.285714em;margin-top:.285714em}.chat-markdown :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.428571em}.chat-markdown :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.571429em;margin-top:.571429em}.chat-markdown :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.14286em}.chat-markdown :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em}.chat-markdown :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.14286em}.chat-markdown :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em}.chat-markdown :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.571429em;margin-top:.571429em}.chat-markdown :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.14286em;margin-top:1.14286em}.chat-markdown :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.285714em;padding-inline-start:1.57143em}.chat-markdown :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)),.chat-markdown :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.chat-markdown :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.chat-markdown :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.chat-markdown :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.666667em;padding-top:.666667em;padding-inline-start:1em}.chat-markdown :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.chat-markdown :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.chat-markdown :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.71429em;margin-top:1.71429em}.chat-markdown :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0;margin-top:0}.chat-markdown :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.chat-markdown :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.chat-markdown>*{margin-bottom:.1em;margin-top:.1em}.message-bubble-assistant .chat-markdown{--tw-prose-body:var(--message-assistant-text-color);--tw-prose-headings:var(--message-assistant-text-color);--tw-prose-lead:var(--message-assistant-text-color);--tw-prose-links:var(--message-assistant-link-color);--tw-prose-bold:var(--message-assistant-text-color);--tw-prose-counters:var(--message-assistant-text-color);--tw-prose-bullets:var(--message-assistant-text-color);--tw-prose-hr:var(--message-assistant-text-color);--tw-prose-quotes:var(--message-assistant-text-color);--tw-prose-quote-borders:var(--message-assistant-text-color);--tw-prose-captions:var(--message-assistant-text-color);--tw-prose-kbd:var(--message-assistant-text-color);--tw-prose-kbd-shadows:var(--message-assistant-text-color);--tw-prose-code:var(--code-text-assistant-color);--tw-prose-pre-code:var(--code-text-assistant-color);--tw-prose-pre-bg:var(--code-bg-assistant-color);--tw-prose-th-borders:var(--message-assistant-text-color);--tw-prose-td-borders:var(--message-assistant-text-color)}.message-bubble-user .chat-markdown{--tw-prose-body:var(--message-user-text-color);--tw-prose-headings:var(--message-user-text-color);--tw-prose-lead:var(--message-user-text-color);--tw-prose-links:var(--message-user-link-color);--tw-prose-bold:var(--message-user-text-color);--tw-prose-counters:var(--message-user-text-color);--tw-prose-bullets:var(--message-user-text-color);--tw-prose-hr:var(--message-user-text-color);--tw-prose-quotes:var(--message-user-text-color);--tw-prose-quote-borders:var(--message-user-text-color);--tw-prose-captions:var(--message-user-text-color);--tw-prose-kbd:var(--message-user-text-color);--tw-prose-kbd-shadows:var(--message-user-text-color);--tw-prose-code:var(--code-text-user-color);--tw-prose-pre-code:var(--code-text-user-color);--tw-prose-pre-bg:var(--code-bg-user-color);--tw-prose-th-borders:var(--message-user-text-color);--tw-prose-td-borders:var(--message-user-text-color)}.message-bubble-system .chat-markdown{--tw-prose-body:var(--message-system-text-color);--tw-prose-headings:var(--message-system-text-color);--tw-prose-lead:var(--message-system-text-color);--tw-prose-links:var(--message-system-link-color);--tw-prose-bold:var(--message-system-text-color);--tw-prose-counters:var(--message-system-text-color);--tw-prose-bullets:var(--message-system-text-color);--tw-prose-hr:var(--message-system-text-color);--tw-prose-quotes:var(--message-system-text-color);--tw-prose-quote-borders:var(--message-system-text-color);--tw-prose-captions:var(--message-system-text-color);--tw-prose-kbd:var(--message-system-text-color);--tw-prose-kbd-shadows:var(--message-system-text-color);--tw-prose-code:var(--message-system-text-color);--tw-prose-pre-code:var(--message-system-text-color);--tw-prose-pre-bg:var(--message-system-text-color);--tw-prose-th-borders:var(--message-system-text-color);--tw-prose-td-borders:var(--message-system-text-color)}.message-bubble-user .chat-markdown pre{border:1px solid var(--code-border-user-color)}.message-bubble-assistant .chat-markdown pre{border:1px solid var(--code-border-assistant-color)}.loading:after{content:\" .\"}.file-attachment-button{border-radius:var(--radius-md);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;background-color:var(--file-attachment-button-bg-color);color:var(--file-attachment-button-text-color);padding:.375em;transition-duration:.2s}.file-attachment-button:disabled{color:var(--file-attachment-button-text-disabled-color);cursor:not-allowed;opacity:.5}.file-attachment-button svg{height:1.5em;width:1.5em}.file-attachment-button:hover:not(:disabled){background-color:var(--file-attachment-button-bg-hover-color)}.selected-files-container{background-color:var(--selected-files-bg-color);border-top:1px solid var(--selected-files-border-color);padding:1em 1em .5em}.selected-file-item{align-items:center;background-color:var(--selected-file-bg-color);border-radius:.375em;color:var(--selected-file-name-color);display:flex;font-size:var(--selected-file-font-size);justify-content:space-between;padding:.25em .5em}.selected-file-icon{align-items:center;display:flex;justify-content:center}.selected-file-icon svg{height:var(--selected-file-icon-size);width:var(--selected-file-icon-size)}.selected-file-size{color:var(--selected-file-size-color)}.selected-file-error{color:var(--error-text-color)}.selected-file-success-icon{align-items:center;color:var(--success-text-color);display:flex;height:var(--selected-file-icon-size);justify-content:center;width:var(--selected-file-icon-size)}.selected-file-remove-button{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold);transition-duration:var(--tw-duration,var(--default-transition-duration));transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));--tw-duration:.2s;color:var(--selected-file-remove-icon-color);padding:.375em;transition-duration:.2s}.selected-file-remove-button svg{height:var(--selected-file-icon-size);width:var(--selected-file-icon-size)}.selected-file-remove-button:hover{color:var(--selected-file-remove-icon-hover-color)}.message-attachments{font-size:var(--chat-window-font-size-sm);margin-top:.5em}:where(.message-attachments>:not(:last-child)){--tw-space-y-reverse:0;margin-block-end:calc(.25em*(1 - var(--tw-space-y-reverse)));margin-block-start:calc(.25em*var(--tw-space-y-reverse))}.message-attachment-icon{align-items:center;display:flex;height:var(--message-attachment-icon-size);justify-content:center;width:var(--message-attachment-icon-size)}.send-button-disabled{background-color:var(--send-button-bg-disabled-color);color:var(--send-button-text-disabled-color);cursor:not-allowed}@property --tw-rotate-x{syntax:\"*\";inherits:false}@property --tw-rotate-y{syntax:\"*\";inherits:false}@property --tw-rotate-z{syntax:\"*\";inherits:false}@property --tw-skew-x{syntax:\"*\";inherits:false}@property --tw-skew-y{syntax:\"*\";inherits:false}@property --tw-space-y-reverse{syntax:\"*\";inherits:false;initial-value:0}@property --tw-border-style{syntax:\"*\";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:\"*\";inherits:false}@property --tw-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:\"*\";inherits:false}@property --tw-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:\"*\";inherits:false}@property --tw-inset-shadow-alpha{syntax:\"<percentage>\";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:\"*\";inherits:false}@property --tw-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:\"*\";inherits:false}@property --tw-inset-ring-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:\"*\";inherits:false}@property --tw-ring-offset-width{syntax:\"<length>\";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:\"*\";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:\"*\";inherits:false;initial-value:0 0 #0000}@property --tw-duration{syntax:\"*\";inherits:false}@property --tw-ease{syntax:\"*\";inherits:false}@property --tw-scale-x{syntax:\"*\";inherits:false;initial-value:1}@property --tw-scale-y{syntax:\"*\";inherits:false;initial-value:1}@property --tw-scale-z{syntax:\"*\";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(1turn)}}@keyframes progress{0%{transform:translate(0)scaleX(0)}10%{transform:translate(0)scaleX(.3)}50%{transform:translate(100%)scaleX(.3)}90%{transform:translate(0)scaleX(.3)}to{transform:translate(0)scaleX(0)}}@keyframes dots{0%,20%{color:#0000;text-shadow:.25em 0 #0000,.5em 0 #0000}40%{color:#000;text-shadow:.25em 0 #0000,.5em 0 #0000}60%{text-shadow:.25em 0 #000,.5em 0 #0000}80%,to{text-shadow:.25em 0 #000,.5em 0 #000}}@media (min-width:40rem){.container{max-width:40rem}.chat-window-normal{height:var(--chat-window-height);width:var(--chat-window-width)}.drag-indicator{display:flex}.fullscreen-button{display:block}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}@media (hover:hover){.chat-btn-icon:hover,.chat-btn-text:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x)var(--tw-scale-y)}.attachment-link:hover{text-decoration-line:none}.chat-markdown :where(a):not(:where([class~=not-prose],[class~=not-prose] *)):hover{text-decoration-line:underline}}";
5395
6248
 
5396
6249
  const OcsChat = class {
5397
6250
  constructor(hostRef) {
@@ -5457,6 +6310,7 @@ const OcsChat = class {
5457
6310
  this.parsedStarterQuestions = [];
5458
6311
  this.isFullscreen = false;
5459
6312
  this.showNewChatConfirmation = false;
6313
+ this.sessionEnded = false;
5460
6314
  this.selectedFiles = [];
5461
6315
  this.isUploadingFiles = false;
5462
6316
  this.buttonPosition = { x: 30, y: 30 };
@@ -5682,6 +6536,23 @@ const OcsChat = class {
5682
6536
  this.removeButtonEventListeners();
5683
6537
  window.removeEventListener('resize', this.handleWindowResize);
5684
6538
  }
6539
+ // ---------------------------------------------------------------------------
6540
+ // Public event API
6541
+ // ---------------------------------------------------------------------------
6542
+ /**
6543
+ * Dispatch a composed, bubbling CustomEvent on the host element so that
6544
+ * embedders can listen with plain addEventListener outside the shadow root.
6545
+ *
6546
+ * All events are dispatched synchronously so that handlers can update
6547
+ * reactive props (e.g. `pageContext`) before the calling code reads them.
6548
+ */
6549
+ dispatchWidgetEvent(name, detail = null) {
6550
+ this.host.dispatchEvent(new CustomEvent(name, {
6551
+ bubbles: true,
6552
+ composed: true,
6553
+ detail,
6554
+ }));
6555
+ }
5685
6556
  applySessionToken(token) {
5686
6557
  var _a;
5687
6558
  this.currentSessionToken = token;
@@ -5733,6 +6604,38 @@ const OcsChat = class {
5733
6604
  this.clearSessionStorage();
5734
6605
  this.addErrorMessage(this.translationManager.get('status.sessionExpired', 'Your chat session expired. Starting a new chat — please resend your message.'));
5735
6606
  }
6607
+ /**
6608
+ * The server reported the session has ended (e.g. closed from another tab or
6609
+ * by the bot). Polling has already stopped; disable the composer and tell the
6610
+ * user. Unbound widgets can recover via the "new chat" button (clearSession).
6611
+ */
6612
+ handleSessionEnded() {
6613
+ var _a;
6614
+ if (this.sessionEnded) {
6615
+ return;
6616
+ }
6617
+ this.sessionEnded = true;
6618
+ this.dispatchWidgetEvent('ocs:session:ended', { sessionId: this.activeSessionId });
6619
+ this.stopMessagePolling();
6620
+ this.isTyping = false;
6621
+ this.typingProgressMessage = '';
6622
+ const content = (_a = this.translationManager.get('status.chatEnded')) !== null && _a !== void 0 ? _a : 'This chat has ended.';
6623
+ // An unbound widget restores persisted messages and re-polls after a reload,
6624
+ // so the notice may already be the last message.
6625
+ const last = this.messages.at(-1);
6626
+ if ((last === null || last === void 0 ? void 0 : last.role) === 'system' && last.content === content) {
6627
+ return;
6628
+ }
6629
+ const notice = {
6630
+ created_at: new Date().toISOString(),
6631
+ role: 'system',
6632
+ content,
6633
+ attachments: [],
6634
+ };
6635
+ this.messages = [...this.messages, notice];
6636
+ this.saveSessionToStorage();
6637
+ this.scrollToBottom();
6638
+ }
5736
6639
  handleError(errorText) {
5737
6640
  // show as system message
5738
6641
  this.addErrorMessage(errorText);
@@ -5831,6 +6734,7 @@ const OcsChat = class {
5831
6734
  this.activeSessionId = data.session_id;
5832
6735
  this.applySessionToken((_a = data.session_token) !== null && _a !== void 0 ? _a : undefined);
5833
6736
  this.saveSessionToStorage();
6737
+ this.dispatchWidgetEvent('ocs:session:started', { sessionId: this.activeSessionId });
5834
6738
  this.startMessagePolling();
5835
6739
  }
5836
6740
  catch (_error) {
@@ -5894,7 +6798,8 @@ const OcsChat = class {
5894
6798
  }
5895
6799
  }
5896
6800
  async sendMessage(message) {
5897
- if (!message.trim())
6801
+ var _a;
6802
+ if (!message.trim() || this.sessionEnded)
5898
6803
  return;
5899
6804
  const epoch = this.sessionEpoch;
5900
6805
  // Start session if we don't have one yet
@@ -5956,6 +6861,12 @@ const OcsChat = class {
5956
6861
  this.selectedFiles = []; // Clear selected files after sending
5957
6862
  }
5958
6863
  this.scrollToBottom();
6864
+ // Fire before-send first so handlers can update pageContext synchronously
6865
+ // before we read internalPageContext into the request body.
6866
+ this.dispatchWidgetEvent('ocs:message:before-send', {
6867
+ message: message.trim(),
6868
+ sessionId: (_a = this.activeSessionId) !== null && _a !== void 0 ? _a : '',
6869
+ });
5959
6870
  const requestBody = { message: message.trim() };
5960
6871
  if (this.allowAttachments && attachmentIds.length > 0) {
5961
6872
  requestBody.attachment_ids = attachmentIds;
@@ -5973,6 +6884,10 @@ const OcsChat = class {
5973
6884
  throw new Error(data.error || 'Failed to send message');
5974
6885
  }
5975
6886
  this.internalPageContext = undefined;
6887
+ this.dispatchWidgetEvent('ocs:message:sent', {
6888
+ message: message.trim(),
6889
+ sessionId: this.activeSessionId,
6890
+ });
5976
6891
  this.startTaskPolling(data.task_id);
5977
6892
  }
5978
6893
  catch (error) {
@@ -6088,6 +7003,7 @@ const OcsChat = class {
6088
7003
  return;
6089
7004
  }
6090
7005
  this.saveVisibleState(visible);
7006
+ this.dispatchWidgetEvent(visible ? 'ocs:open' : 'ocs:close');
6091
7007
  if (this.isButtonDragging) {
6092
7008
  this.isButtonDragging = false;
6093
7009
  this.buttonWasDragged = false;
@@ -6124,8 +7040,13 @@ const OcsChat = class {
6124
7040
  }
6125
7041
  this.taskPollingHandle = this.getChatService().pollTask(this.activeSessionId, taskId, {
6126
7042
  onMessage: message => {
7043
+ var _a;
6127
7044
  this.messages = [...this.messages, message];
6128
7045
  this.saveSessionToStorage();
7046
+ this.dispatchWidgetEvent('ocs:message:received', {
7047
+ message: Object.assign({}, message),
7048
+ sessionId: (_a = this.activeSessionId) !== null && _a !== void 0 ? _a : '',
7049
+ });
6129
7050
  this.scrollToBottom();
6130
7051
  this.isTyping = false;
6131
7052
  this.typingProgressMessage = '';
@@ -6167,7 +7088,7 @@ const OcsChat = class {
6167
7088
  });
6168
7089
  }
6169
7090
  startMessagePolling() {
6170
- if (!this.activeSessionId || this.currentPollTaskId || !this.visible) {
7091
+ if (!this.activeSessionId || this.currentPollTaskId || !this.visible || this.sessionEnded) {
6171
7092
  return;
6172
7093
  }
6173
7094
  if (this.messagePollingHandle) {
@@ -6176,13 +7097,26 @@ const OcsChat = class {
6176
7097
  this.messagePollingHandle = this.getChatService().startMessagePolling(this.activeSessionId, {
6177
7098
  getSince: () => { var _a; return (this.messages.length > 0 ? (_a = this.messages.at(-1)) === null || _a === void 0 ? void 0 : _a.created_at : undefined); },
6178
7099
  onMessages: messages => {
7100
+ var _a;
6179
7101
  if (messages.length === 0)
6180
7102
  return;
6181
7103
  this.messages = [...this.messages, ...messages];
6182
7104
  this.saveSessionToStorage();
7105
+ for (const message of messages) {
7106
+ if (message.role !== 'user') {
7107
+ this.dispatchWidgetEvent('ocs:message:received', {
7108
+ message: Object.assign({}, message),
7109
+ sessionId: (_a = this.activeSessionId) !== null && _a !== void 0 ? _a : '',
7110
+ });
7111
+ }
7112
+ }
6183
7113
  this.scrollToBottom();
6184
7114
  this.focusInput();
6185
7115
  },
7116
+ onSessionEnded: () => {
7117
+ this.messagePollingHandle = undefined;
7118
+ this.handleSessionEnded();
7119
+ },
6186
7120
  onError: () => {
6187
7121
  // Silently ignore polling errors to match previous behaviour
6188
7122
  },
@@ -6621,7 +7555,9 @@ const OcsChat = class {
6621
7555
  return newUserId;
6622
7556
  }
6623
7557
  saveVisibleState(visible) {
6624
- if (!this.persistentSession)
7558
+ // Kiosk visibility is forced, so persisting it would only leak into a
7559
+ // standard-mode widget for the same chatbot on another page.
7560
+ if (!this.persistentSession || this.isKioskMode())
6625
7561
  return;
6626
7562
  try {
6627
7563
  const keys = this.getStorageKeys();
@@ -6695,6 +7631,7 @@ const OcsChat = class {
6695
7631
  this.applySessionToken(this.isSessionBound() ? this.sessionToken : undefined);
6696
7632
  this.messages = [];
6697
7633
  this.isTyping = false;
7634
+ this.sessionEnded = false;
6698
7635
  this.currentPollTaskId = '';
6699
7636
  if (this.allowAttachments) {
6700
7637
  this.selectedFiles = [];
@@ -6716,12 +7653,12 @@ const OcsChat = class {
6716
7653
  if (this.error && !this.activeSessionId) {
6717
7654
  return (index.h(index.Host, null, index.h("p", { class: "error-message" }, this.error)));
6718
7655
  }
6719
- return (index.h(index.Host, null, this.renderButton(), this.visible && (index.h("div", { ref: el => (this.chatWindowRef = el), id: "ocs-chat-window", class: this.getPositionClasses(), style: this.getPositionStyles() }, !this.isKioskMode() && (index.h("div", { class: `chat-header ${this.isDragging ? 'chat-header-dragging' : 'chat-header-draggable'}`, onMouseDown: this.handleMouseDown, onTouchStart: this.handleTouchStart }, index.h("div", { class: "drag-indicator" }, index.h("div", { class: "drag-dots header-button" }, index.h(GripDotsVerticalIcon, null))), index.h("div", { class: "header-text" }, this.translationManager.get('branding.headerText', this.headerText)), index.h("div", { class: "header-buttons" }, this.messages.length > 0 && !this.isSessionBound() && (index.h("button", { class: "header-button", onClick: () => this.showConfirmationDialog(), title: this.translationManager.get('window.newChat'), "aria-label": this.translationManager.get('window.newChat') }, index.h(PlusWithCircleIcon, null))), this.allowFullScreen && (index.h("button", { class: "header-button fullscreen-button", onClick: () => this.toggleFullscreen(), title: this.isFullscreen ? this.translationManager.get('window.exitFullscreen') : this.translationManager.get('window.fullscreen'), "aria-label": this.isFullscreen ? this.translationManager.get('window.exitFullscreen') : this.translationManager.get('window.fullscreen') }, this.isFullscreen ? index.h(ArrowsPointingInIcon, null) : index.h(ArrowsPointingOutIcon, null))), index.h("button", { class: "header-button", onClick: () => (this.visible = false), "aria-label": this.translationManager.get('window.close') }, index.h(XMarkIcon, null))))), !this.isKioskMode() && this.showNewChatConfirmation && (index.h("div", { class: "confirmation-overlay" }, index.h("div", { class: "confirmation-dialog" }, index.h("div", { class: "confirmation-content" }, index.h("h3", { class: "confirmation-title" }, this.translationManager.get('modal.newChatTitle')), index.h("p", { class: "confirmation-message" }, this.translationManager.get('modal.newChatBody', this.newChatConfirmationMessage)), index.h("div", { class: "confirmation-buttons" }, index.h("button", { class: "confirmation-button confirmation-button-cancel", onClick: () => this.hideConfirmationDialog() }, this.translationManager.get('modal.cancel')), index.h("button", { class: "confirmation-button confirmation-button-confirm", onClick: () => this.confirmNewChat() }, this.translationManager.get('modal.confirm'))))))), index.h("div", { class: "chat-content" }, this.isLoading && !this.activeSessionId && (index.h("div", { class: "loading-container" }, index.h("div", { class: "loading-spinner" }), index.h("span", { class: "loading-text" }, this.translationManager.get('status.starting')))), index.h("div", { ref: el => (this.messageListRef = el), class: "messages-container" }, this.messages.length === 0 && this.getWelcomeMessages().length > 0 && (index.h("div", { class: "welcome-messages" }, this.getWelcomeMessages().map((message, index$1) => (index.h("div", { key: `welcome-${index$1}`, class: "message-row message-row-assistant" }, index.h("div", { class: "message-bubble message-bubble-assistant" }, index.h("div", { class: "chat-markdown", innerHTML: renderMarkdownSync(message) }))))))), this.messages.map((message, index$1) => (index.h("div", { key: index$1, class: `message-row ${message.role === 'user' ? 'message-row-user' : 'message-row-assistant'}` }, index.h("div", { class: `message-bubble ${message.role === 'user' ? 'message-bubble-user' : message.role === 'assistant' ? 'message-bubble-assistant' : 'message-bubble-system'}` }, index.h("div", { class: "chat-markdown", innerHTML: renderMarkdownSync(message.content) }), message.attachments && message.attachments.length > 0 && (index.h("div", { class: "message-attachments" }, message.attachments.map((attachment, attachmentIndex) => (index.h("div", { key: attachmentIndex, class: "flex items-center gap-[0.5em]" }, index.h("span", { class: "message-attachment-icon" }, index.h(PaperClipIcon, null)), index.h("span", { class: "message-attachment-name" }, attachment.name)))))), index.h("div", { class: "message-timestamp" }, this.formatTime(message.created_at)))))), this.isTyping && (index.h("div", null, index.h("div", { class: "typing-indicator" }, index.h("div", { class: "typing-progress" })), index.h("div", { class: "typing-text" }, index.h("span", null, this.typingProgressMessage || this.translationManager.get('status.typing', this.typingIndicatorText)), index.h("span", { class: "typing-dots loading" }))))), this.messages.length === 0 && this.getStarterQuestions().length > 0 && (index.h("div", { class: "starter-questions" }, this.getStarterQuestions().map((question, index$1) => (index.h("div", { key: `starter-${index$1}`, class: "starter-question-row" }, index.h("button", { class: "starter-question", onClick: () => this.handleStarterQuestionClick(question) }, question)))))), this.allowAttachments && this.selectedFiles.length > 0 && (index.h("div", { class: "selected-files-container" }, index.h("div", { class: "space-y-[0.25em]" }, this.selectedFiles.map((selectedFile, index$1) => (index.h("div", { key: index$1, class: "selected-file-item" }, index.h("div", { class: "flex items-center gap-[0.5em]" }, index.h("span", { class: "selected-file-icon" }, index.h(PaperClipIcon, null)), index.h("span", null, selectedFile.file.name), index.h("span", { class: "selected-file-size" }, "(", this.formatFileSize(selectedFile.file.size), ")"), selectedFile.error && index.h("span", { class: "selected-file-error" }, selectedFile.error), selectedFile.uploaded && (index.h("span", { class: "selected-file-success-icon" }, index.h(CheckDocumentIcon, null)))), index.h("button", { onClick: () => this.removeSelectedFile(index$1), class: "selected-file-remove-button", "aria-label": this.translationManager.get('attach.remove') }, index.h(XIcon, null)))))))), index.h("div", { class: "input-area" }, index.h("div", { class: "input-container" }, index.h("textarea", { ref: el => (this.textareaRef = el), class: "message-textarea", rows: 1, placeholder: this.translationManager.get('composer.placeholder'), value: this.messageInput, onInput: e => this.handleInputChange(e), onKeyPress: e => this.handleKeyPress(e), disabled: this.isTyping || this.isUploadingFiles || this.isLoading }), this.allowAttachments && (index.h("input", { ref: el => {
7656
+ return (index.h(index.Host, null, this.renderButton(), this.visible && (index.h("div", { ref: el => (this.chatWindowRef = el), id: "ocs-chat-window", class: this.getPositionClasses(), style: this.getPositionStyles() }, !this.isKioskMode() && (index.h("div", { class: `chat-header ${this.isDragging ? 'chat-header-dragging' : 'chat-header-draggable'}`, onMouseDown: this.handleMouseDown, onTouchStart: this.handleTouchStart }, index.h("div", { class: "drag-indicator" }, index.h("div", { class: "drag-dots header-button" }, index.h(GripDotsVerticalIcon, null))), index.h("div", { class: "header-text" }, this.translationManager.get('branding.headerText', this.headerText)), index.h("div", { class: "header-buttons" }, this.messages.length > 0 && !this.isSessionBound() && (index.h("button", { class: "header-button", onClick: () => this.showConfirmationDialog(), title: this.translationManager.get('window.newChat'), "aria-label": this.translationManager.get('window.newChat') }, index.h(PlusWithCircleIcon, null))), this.allowFullScreen && (index.h("button", { class: "header-button fullscreen-button", onClick: () => this.toggleFullscreen(), title: this.isFullscreen ? this.translationManager.get('window.exitFullscreen') : this.translationManager.get('window.fullscreen'), "aria-label": this.isFullscreen ? this.translationManager.get('window.exitFullscreen') : this.translationManager.get('window.fullscreen') }, this.isFullscreen ? index.h(ArrowsPointingInIcon, null) : index.h(ArrowsPointingOutIcon, null))), index.h("button", { class: "header-button", onClick: () => (this.visible = false), "aria-label": this.translationManager.get('window.close') }, index.h(XMarkIcon, null))))), !this.isKioskMode() && this.showNewChatConfirmation && (index.h("div", { class: "confirmation-overlay" }, index.h("div", { class: "confirmation-dialog" }, index.h("div", { class: "confirmation-content" }, index.h("h3", { class: "confirmation-title" }, this.translationManager.get('modal.newChatTitle')), index.h("p", { class: "confirmation-message" }, this.translationManager.get('modal.newChatBody', this.newChatConfirmationMessage)), index.h("div", { class: "confirmation-buttons" }, index.h("button", { class: "confirmation-button confirmation-button-cancel", onClick: () => this.hideConfirmationDialog() }, this.translationManager.get('modal.cancel')), index.h("button", { class: "confirmation-button confirmation-button-confirm", onClick: () => this.confirmNewChat() }, this.translationManager.get('modal.confirm'))))))), index.h("div", { class: "chat-content" }, this.isLoading && !this.activeSessionId && (index.h("div", { class: "loading-container" }, index.h("div", { class: "loading-spinner" }), index.h("span", { class: "loading-text" }, this.translationManager.get('status.starting')))), index.h("div", { ref: el => (this.messageListRef = el), class: "messages-container" }, this.messages.length === 0 && this.getWelcomeMessages().length > 0 && (index.h("div", { class: "welcome-messages" }, this.getWelcomeMessages().map((message, index$1) => (index.h("div", { key: `welcome-${index$1}`, class: "message-row message-row-assistant" }, index.h("div", { class: "message-bubble message-bubble-assistant" }, index.h("div", { class: "chat-markdown", innerHTML: renderMarkdownSync(message) }))))))), this.messages.map((message, index$1) => (index.h("div", { key: index$1, class: `message-row ${message.role === 'user' ? 'message-row-user' : 'message-row-assistant'}` }, index.h("div", { class: `message-bubble ${message.role === 'user' ? 'message-bubble-user' : message.role === 'assistant' ? 'message-bubble-assistant' : 'message-bubble-system'}` }, index.h("div", { class: "chat-markdown", innerHTML: renderMarkdownSync(message.content) }), message.attachments && message.attachments.length > 0 && (index.h("div", { class: "message-attachments" }, message.attachments.map((attachment, attachmentIndex) => (index.h("div", { key: attachmentIndex, class: "flex items-center gap-[0.5em]" }, index.h("span", { class: "message-attachment-icon" }, index.h(PaperClipIcon, null)), index.h("span", { class: "message-attachment-name" }, attachment.name)))))), index.h("div", { class: "message-timestamp" }, this.formatTime(message.created_at)))))), this.isTyping && (index.h("div", null, index.h("div", { class: "typing-indicator" }, index.h("div", { class: "typing-progress" })), index.h("div", { class: "typing-text" }, index.h("span", null, this.typingProgressMessage || this.translationManager.get('status.typing', this.typingIndicatorText)), index.h("span", { class: "typing-dots loading" }))))), this.messages.length === 0 && this.getStarterQuestions().length > 0 && (index.h("div", { class: "starter-questions" }, this.getStarterQuestions().map((question, index$1) => (index.h("div", { key: `starter-${index$1}`, class: "starter-question-row" }, index.h("button", { class: "starter-question", onClick: () => this.handleStarterQuestionClick(question) }, question)))))), this.allowAttachments && this.selectedFiles.length > 0 && (index.h("div", { class: "selected-files-container" }, index.h("div", { class: "space-y-[0.25em]" }, this.selectedFiles.map((selectedFile, index$1) => (index.h("div", { key: index$1, class: "selected-file-item" }, index.h("div", { class: "flex items-center gap-[0.5em]" }, index.h("span", { class: "selected-file-icon" }, index.h(PaperClipIcon, null)), index.h("span", null, selectedFile.file.name), index.h("span", { class: "selected-file-size" }, "(", this.formatFileSize(selectedFile.file.size), ")"), selectedFile.error && index.h("span", { class: "selected-file-error" }, selectedFile.error), selectedFile.uploaded && (index.h("span", { class: "selected-file-success-icon" }, index.h(CheckDocumentIcon, null)))), index.h("button", { onClick: () => this.removeSelectedFile(index$1), class: "selected-file-remove-button", "aria-label": this.translationManager.get('attach.remove') }, index.h(XIcon, null)))))))), index.h("div", { class: "input-area" }, index.h("div", { class: "input-container" }, index.h("textarea", { ref: el => (this.textareaRef = el), class: "message-textarea", rows: 1, placeholder: this.sessionEnded ? this.translationManager.get('status.chatEnded') : this.translationManager.get('composer.placeholder'), value: this.messageInput, onInput: e => this.handleInputChange(e), onKeyPress: e => this.handleKeyPress(e), disabled: this.isTyping || this.isUploadingFiles || this.isLoading || this.sessionEnded }), this.allowAttachments && (index.h("input", { ref: el => {
6720
7657
  // Unclear why but after removing all attachments this is being set to `null`.
6721
7658
  if (el) {
6722
7659
  this.fileInputRef = el;
6723
7660
  }
6724
- }, id: "ocs-file-input", type: "file", multiple: true, accept: OcsChat.SUPPORTED_FILE_EXTENSIONS.join(',') + ',text/*', onChange: e => this.handleFileSelect(e), class: "hidden" })), this.allowAttachments && (index.h("button", { class: "file-attachment-button", onClick: () => { var _a; return (_a = this.fileInputRef) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: this.isTyping || this.isUploadingFiles || this.isLoading, title: this.translationManager.get('attach.add'), "aria-label": this.translationManager.get('attach.add') }, index.h(PaperClipIcon, null))), index.h("button", { class: `send-button ${!this.isTyping && !this.isLoading && !!this.messageInput.trim() ? 'send-button-enabled' : 'send-button-disabled'}`, onClick: () => this.sendMessage(this.messageInput), disabled: this.isTyping || this.isUploadingFiles || this.isLoading || !this.messageInput.trim() }, this.isUploadingFiles ? `${this.translationManager.get('status.uploading')}...` : this.translationManager.get('composer.send')))), index.h("div", { class: "flex items-center justify-center text-[0.8em] font-light w-full text-slate-500 py-[2px]" }, index.h("p", null, this.translationManager.get('branding.poweredBy'), ' ', index.h("a", { class: "underline", href: "https://www.dimagi.com", target: "_blank", rel: "noopener noreferrer" }, "Dimagi"))))))));
7661
+ }, id: "ocs-file-input", type: "file", multiple: true, accept: OcsChat.SUPPORTED_FILE_EXTENSIONS.join(',') + ',text/*', onChange: e => this.handleFileSelect(e), class: "hidden" })), this.allowAttachments && (index.h("button", { class: "file-attachment-button", onClick: () => { var _a; return (_a = this.fileInputRef) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: this.isTyping || this.isUploadingFiles || this.isLoading || this.sessionEnded, title: this.translationManager.get('attach.add'), "aria-label": this.translationManager.get('attach.add') }, index.h(PaperClipIcon, null))), index.h("button", { class: `send-button ${!this.isTyping && !this.isLoading && !this.sessionEnded && !!this.messageInput.trim() ? 'send-button-enabled' : 'send-button-disabled'}`, onClick: () => this.sendMessage(this.messageInput), disabled: this.isTyping || this.isUploadingFiles || this.isLoading || this.sessionEnded || !this.messageInput.trim() }, this.isUploadingFiles ? `${this.translationManager.get('status.uploading')}...` : this.translationManager.get('composer.send')))), index.h("div", { class: "flex items-center justify-center text-[0.8em] font-light w-full text-slate-500 py-[2px]" }, index.h("p", null, this.translationManager.get('branding.poweredBy'), ' ', index.h("a", { class: "underline", href: "https://www.dimagi.com", target: "_blank", rel: "noopener noreferrer" }, "Dimagi"))))))));
6725
7662
  }
6726
7663
  get host() { return index.getElement(this); }
6727
7664
  static get watchers() { return {