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.
- package/dist/cjs/{index-fFSp-Z_h.js → index-DMXmZhVD.js} +3 -3
- package/dist/cjs/index-DMXmZhVD.js.map +1 -0
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/cjs/open-chat-studio-widget.cjs.entry.js +1251 -314
- package/dist/cjs/open-chat-studio-widget.cjs.entry.js.map +1 -1
- package/dist/cjs/open-chat-studio-widget.cjs.js +2 -2
- package/dist/cjs/open-chat-studio-widget.entry.cjs.js.map +1 -1
- package/dist/collection/components/ocs-chat/ocs-chat.js +90 -5
- package/dist/collection/components/ocs-chat/ocs-chat.js.map +1 -1
- package/dist/collection/services/chat-session-service.js +5 -0
- package/dist/collection/services/chat-session-service.js.map +1 -1
- package/dist/components/open-chat-studio-widget.js +1251 -313
- package/dist/components/open-chat-studio-widget.js.map +1 -1
- package/dist/esm/{index-ythTKHg-.js → index-JDApwJx_.js} +3 -3
- package/dist/esm/index-JDApwJx_.js.map +1 -0
- package/dist/esm/loader.js +3 -3
- package/dist/esm/open-chat-studio-widget.entry.js +1251 -314
- package/dist/esm/open-chat-studio-widget.entry.js.map +1 -1
- package/dist/esm/open-chat-studio-widget.js +3 -3
- package/dist/open-chat-studio-widget/open-chat-studio-widget.entry.esm.js.map +1 -1
- package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js +1 -1
- package/dist/open-chat-studio-widget/{p-ythTKHg-.js → p-JDApwJx_.js} +2 -2
- package/dist/open-chat-studio-widget/p-JDApwJx_.js.map +1 -0
- package/dist/open-chat-studio-widget/p-c2d3b2d1.entry.js +4 -0
- package/dist/open-chat-studio-widget/p-c2d3b2d1.entry.js.map +1 -0
- package/dist/types/components/ocs-chat/ocs-chat.d.ts +15 -0
- package/dist/types/services/chat-session-service.d.ts +2 -0
- package/package.json +2 -2
- package/dist/cjs/index-fFSp-Z_h.js.map +0 -1
- package/dist/esm/index-ythTKHg-.js.map +0 -1
- package/dist/open-chat-studio-widget/p-2d31a15c.entry.js +0 -4
- package/dist/open-chat-studio-widget/p-2d31a15c.entry.js.map +0 -1
- 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-
|
|
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.
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
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
|
|
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 (
|
|
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'
|
|
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
|
-
|
|
3152
|
-
const
|
|
3153
|
-
const
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
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
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
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
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
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',
|
|
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
|
-
|
|
3451
|
-
let
|
|
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 =
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
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
|
|
3518
|
-
NAMESPACE = cfg.NAMESPACE
|
|
3519
|
-
MATHML_TEXT_INTEGRATION_POINTS = cfg.MATHML_TEXT_INTEGRATION_POINTS
|
|
3520
|
-
HTML_INTEGRATION_POINTS = cfg.HTML_INTEGRATION_POINTS
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
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 =
|
|
3856
|
+
trustedTypesPolicy = _getDefaultTrustedTypesPolicy();
|
|
3629
3857
|
}
|
|
3630
|
-
//
|
|
3631
|
-
|
|
3632
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ?
|
|
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
|
-
|
|
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
|
|
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
|
|
4368
|
+
* @return true if value is a DOM node from any realm
|
|
3848
4369
|
*/
|
|
3849
4370
|
const _isNode = function _isNode(value) {
|
|
3850
|
-
|
|
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
|
-
/*
|
|
3883
|
-
if (
|
|
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
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
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(
|
|
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 &&
|
|
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
|
|
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
|
-
|
|
4040
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
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 (
|
|
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
|
-
/*
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
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
|
-
|
|
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 ?
|
|
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(
|
|
4243
|
-
/* Now start iterating over the created document
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
4297
|
-
serializedHTML = stringReplace(serializedHTML, expr, ' ');
|
|
4298
|
-
});
|
|
5114
|
+
serializedHTML = _stripTemplateExpressions(serializedHTML);
|
|
4299
5115
|
}
|
|
4300
|
-
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ?
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|