@handled-ai/design-system 0.18.58 → 0.19.0-rc.1
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/components/badge.d.ts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/case-panel-activity-timeline.d.ts +2 -0
- package/dist/components/case-panel-activity-timeline.js +22 -1
- package/dist/components/case-panel-activity-timeline.js.map +1 -1
- package/dist/components/comment-composer.d.ts +29 -0
- package/dist/components/comment-composer.js +102 -0
- package/dist/components/comment-composer.js.map +1 -0
- package/dist/components/conversation-panel.d.ts +95 -0
- package/dist/components/conversation-panel.js +636 -0
- package/dist/components/conversation-panel.js.map +1 -0
- package/dist/components/detail-view.js +1 -1
- package/dist/components/detail-view.js.map +1 -1
- package/dist/components/owner-chips.d.ts +59 -0
- package/dist/components/owner-chips.js +256 -0
- package/dist/components/owner-chips.js.map +1 -0
- package/dist/components/pill.d.ts +1 -1
- package/dist/components/score-why-chips.d.ts +1 -1
- package/dist/components/signal-priority-popover.d.ts +1 -1
- package/dist/components/signal-priority-popover.js +16 -7
- package/dist/components/signal-priority-popover.js.map +1 -1
- package/dist/components/tabs.d.ts +1 -1
- package/dist/components/timeline-activity.d.ts +7 -0
- package/dist/components/timeline-activity.js +22 -1
- package/dist/components/timeline-activity.js.map +1 -1
- package/dist/components/virtualized-data-table.js +4 -4
- package/dist/components/virtualized-data-table.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/safe-html.d.ts +11 -0
- package/dist/internal/safe-html.js +222 -0
- package/dist/internal/safe-html.js.map +1 -0
- package/dist/prototype/index.d.ts +1 -1
- package/dist/prototype/prototype-accounts-view.d.ts +1 -1
- package/dist/prototype/prototype-admin-view.d.ts +1 -1
- package/dist/prototype/prototype-config.d.ts +1 -1
- package/dist/prototype/prototype-inbox-view.d.ts +1 -1
- package/dist/prototype/prototype-inbox-view.js +2 -0
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/dist/prototype/prototype-insights-view.d.ts +1 -1
- package/dist/prototype/prototype-shell.d.ts +1 -1
- package/dist/{signal-priority-popover-QJngMAj7.d.ts → signal-priority-popover-CZitE9xq.d.ts} +11 -2
- package/package.json +1 -1
- package/src/components/__tests__/comment-composer.test.tsx +57 -0
- package/src/components/__tests__/conversation-panel.test.tsx +157 -0
- package/src/components/__tests__/owner-chips.test.tsx +100 -0
- package/src/components/__tests__/signal-priority-popover.test.tsx +41 -4
- package/src/components/__tests__/timeline-activity.test.tsx +55 -0
- package/src/components/__tests__/virtualized-data-table-resize.test.tsx +18 -0
- package/src/components/case-panel-activity-timeline.tsx +20 -0
- package/src/components/comment-composer.tsx +119 -0
- package/src/components/conversation-panel.tsx +790 -0
- package/src/components/detail-view.tsx +3 -1
- package/src/components/owner-chips.tsx +335 -0
- package/src/components/signal-priority-popover.tsx +19 -6
- package/src/components/timeline-activity.tsx +37 -3
- package/src/components/virtualized-data-table.tsx +4 -4
- package/src/index.ts +4 -1
- package/src/internal/__tests__/safe-html.test.ts +53 -0
- package/src/internal/safe-html.ts +284 -0
- package/src/prototype/__tests__/detail-view-score-why.test.tsx +34 -0
- package/src/prototype/prototype-config.ts +5 -1
- package/src/prototype/prototype-inbox-view.tsx +2 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
const DANGEROUS_BLOCK_TAGS = /* @__PURE__ */ new Set([
|
|
2
|
+
"script",
|
|
3
|
+
"style",
|
|
4
|
+
"iframe",
|
|
5
|
+
"object",
|
|
6
|
+
"embed",
|
|
7
|
+
"svg",
|
|
8
|
+
"math",
|
|
9
|
+
"template",
|
|
10
|
+
"noscript",
|
|
11
|
+
"textarea",
|
|
12
|
+
"select"
|
|
13
|
+
]);
|
|
14
|
+
const ALLOWED_TAGS = /* @__PURE__ */ new Set([
|
|
15
|
+
"a",
|
|
16
|
+
"b",
|
|
17
|
+
"blockquote",
|
|
18
|
+
"br",
|
|
19
|
+
"code",
|
|
20
|
+
"del",
|
|
21
|
+
"div",
|
|
22
|
+
"em",
|
|
23
|
+
"hr",
|
|
24
|
+
"i",
|
|
25
|
+
"img",
|
|
26
|
+
"li",
|
|
27
|
+
"ol",
|
|
28
|
+
"p",
|
|
29
|
+
"pre",
|
|
30
|
+
"s",
|
|
31
|
+
"span",
|
|
32
|
+
"strong",
|
|
33
|
+
"table",
|
|
34
|
+
"tbody",
|
|
35
|
+
"td",
|
|
36
|
+
"th",
|
|
37
|
+
"thead",
|
|
38
|
+
"tr",
|
|
39
|
+
"u",
|
|
40
|
+
"ul"
|
|
41
|
+
]);
|
|
42
|
+
const VOID_TAGS = /* @__PURE__ */ new Set(["br", "hr", "img"]);
|
|
43
|
+
const SAFE_GLOBAL_ATTRS = /* @__PURE__ */ new Set(["aria-label", "role", "title"]);
|
|
44
|
+
const SAFE_URL_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
|
|
45
|
+
function escapeHtml(value) {
|
|
46
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
47
|
+
}
|
|
48
|
+
function escapeAttribute(value) {
|
|
49
|
+
return escapeHtml(value).replace(/"/g, """);
|
|
50
|
+
}
|
|
51
|
+
function safeCodePoint(value) {
|
|
52
|
+
return Number.isInteger(value) && value >= 0 && value <= 1114111 ? String.fromCodePoint(value) : "";
|
|
53
|
+
}
|
|
54
|
+
function decodeHtmlEntities(value) {
|
|
55
|
+
const namedEntities = {
|
|
56
|
+
amp: "&",
|
|
57
|
+
apos: "'",
|
|
58
|
+
colon: ":",
|
|
59
|
+
gt: ">",
|
|
60
|
+
lt: "<",
|
|
61
|
+
newline: "\n",
|
|
62
|
+
quot: '"',
|
|
63
|
+
tab: " "
|
|
64
|
+
};
|
|
65
|
+
let decoded = value;
|
|
66
|
+
for (let i = 0; i < 4; i += 1) {
|
|
67
|
+
const next = decoded.replace(/&#x([0-9a-f]+);?/gi, (_match, hex) => {
|
|
68
|
+
const codePoint = Number.parseInt(hex, 16);
|
|
69
|
+
return safeCodePoint(codePoint);
|
|
70
|
+
}).replace(/&#(\d+);?/g, (_match, decimal) => {
|
|
71
|
+
const codePoint = Number.parseInt(decimal, 10);
|
|
72
|
+
return safeCodePoint(codePoint);
|
|
73
|
+
}).replace(/&([a-z]+);/gi, (match, name) => {
|
|
74
|
+
var _a;
|
|
75
|
+
return (_a = namedEntities[name.toLowerCase()]) != null ? _a : match;
|
|
76
|
+
});
|
|
77
|
+
if (next === decoded) return decoded;
|
|
78
|
+
decoded = next;
|
|
79
|
+
}
|
|
80
|
+
return decoded;
|
|
81
|
+
}
|
|
82
|
+
function isSafeUrl(value) {
|
|
83
|
+
const decoded = decodeHtmlEntities(value).replace(/[\u0000-\u001f\u007f\s]+/g, "").trim();
|
|
84
|
+
if (!decoded) return false;
|
|
85
|
+
if (decoded.startsWith("//")) return false;
|
|
86
|
+
if (decoded.startsWith("#") || decoded.startsWith("/") || decoded.startsWith("./") || decoded.startsWith("../")) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
return SAFE_URL_PROTOCOLS.has(new URL(decoded, "https://handled.local").protocol);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function sanitizeClassName(value) {
|
|
96
|
+
const safeTokens = value.split(/\s+/).map((token) => token.trim()).filter((token) => /^[A-Za-z0-9_-]+$/.test(token));
|
|
97
|
+
return safeTokens.length ? safeTokens.join(" ") : null;
|
|
98
|
+
}
|
|
99
|
+
function sanitizeAttribute(tagName, name, value) {
|
|
100
|
+
const attr = name.toLowerCase();
|
|
101
|
+
if (attr.startsWith("on") || attr === "style" || attr === "srcdoc" || attr === "formaction" || attr === "xlink:href" || attr === "xmlns") {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
if (attr === "class") {
|
|
105
|
+
const safeClassName = sanitizeClassName(value);
|
|
106
|
+
return safeClassName ? `class="${escapeAttribute(safeClassName)}"` : null;
|
|
107
|
+
}
|
|
108
|
+
if (SAFE_GLOBAL_ATTRS.has(attr) || attr.startsWith("aria-")) {
|
|
109
|
+
return `${attr}="${escapeAttribute(value)}"`;
|
|
110
|
+
}
|
|
111
|
+
if (tagName === "a" && attr === "href" && isSafeUrl(value)) {
|
|
112
|
+
return `href="${escapeAttribute(value)}"`;
|
|
113
|
+
}
|
|
114
|
+
if (tagName === "img" && attr === "src" && isSafeUrl(value)) {
|
|
115
|
+
return `src="${escapeAttribute(value)}"`;
|
|
116
|
+
}
|
|
117
|
+
if (tagName === "img" && (attr === "alt" || attr === "width" || attr === "height")) {
|
|
118
|
+
return `${attr}="${escapeAttribute(value)}"`;
|
|
119
|
+
}
|
|
120
|
+
if ((tagName === "td" || tagName === "th") && (attr === "colspan" || attr === "rowspan")) {
|
|
121
|
+
return `${attr}="${escapeAttribute(value)}"`;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
function sanitizeAttributes(tagName, rawAttributes = "") {
|
|
126
|
+
var _a, _b;
|
|
127
|
+
const attributes = [];
|
|
128
|
+
const attrPattern = /([A-Za-z_:][-A-Za-z0-9_:.]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
|
129
|
+
let match;
|
|
130
|
+
while ((match = attrPattern.exec(rawAttributes)) !== null) {
|
|
131
|
+
const [, name, doubleQuotedValue, singleQuotedValue, unquotedValue] = match;
|
|
132
|
+
const value = (_b = (_a = doubleQuotedValue != null ? doubleQuotedValue : singleQuotedValue) != null ? _a : unquotedValue) != null ? _b : "";
|
|
133
|
+
const safeAttribute = sanitizeAttribute(tagName, name, value);
|
|
134
|
+
if (safeAttribute) attributes.push(safeAttribute);
|
|
135
|
+
}
|
|
136
|
+
if (tagName === "a" && attributes.some((attr) => attr.startsWith("href="))) {
|
|
137
|
+
attributes.push('target="_blank"', 'rel="noopener noreferrer"');
|
|
138
|
+
}
|
|
139
|
+
return attributes.length ? ` ${attributes.join(" ")}` : "";
|
|
140
|
+
}
|
|
141
|
+
function findTagEnd(html, startIndex) {
|
|
142
|
+
let quote = null;
|
|
143
|
+
for (let i = startIndex + 1; i < html.length; i += 1) {
|
|
144
|
+
const char = html[i];
|
|
145
|
+
if (quote) {
|
|
146
|
+
if (char === quote) quote = null;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (char === '"' || char === "'") {
|
|
150
|
+
quote = char;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (char === ">") return i;
|
|
154
|
+
}
|
|
155
|
+
return -1;
|
|
156
|
+
}
|
|
157
|
+
function parseTag(rawTag) {
|
|
158
|
+
var _a;
|
|
159
|
+
const match = rawTag.match(/^<\s*(\/)?\s*([A-Za-z][A-Za-z0-9:-]*)\b([\s\S]*?)\/?>$/);
|
|
160
|
+
if (!match) return null;
|
|
161
|
+
return {
|
|
162
|
+
closing: !!match[1],
|
|
163
|
+
name: match[2].toLowerCase(),
|
|
164
|
+
attributes: (_a = match[3]) != null ? _a : ""
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function findDangerousClose(html, tagName, fromIndex) {
|
|
168
|
+
const closePattern = new RegExp(`</\\s*${tagName}\\s*>`, "ig");
|
|
169
|
+
closePattern.lastIndex = fromIndex;
|
|
170
|
+
const match = closePattern.exec(html);
|
|
171
|
+
return match ? closePattern.lastIndex : -1;
|
|
172
|
+
}
|
|
173
|
+
function sanitizeHtml(html) {
|
|
174
|
+
let output = "";
|
|
175
|
+
let cursor = 0;
|
|
176
|
+
while (cursor < html.length) {
|
|
177
|
+
const tagStart = html.indexOf("<", cursor);
|
|
178
|
+
if (tagStart === -1) {
|
|
179
|
+
output += html.slice(cursor);
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
output += html.slice(cursor, tagStart);
|
|
183
|
+
if (html.startsWith("<!--", tagStart)) {
|
|
184
|
+
const commentEnd = html.indexOf("-->", tagStart + 4);
|
|
185
|
+
cursor = commentEnd === -1 ? html.length : commentEnd + 3;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const tagEnd = findTagEnd(html, tagStart);
|
|
189
|
+
if (tagEnd === -1) {
|
|
190
|
+
output += escapeHtml(html.slice(tagStart));
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
const rawTag = html.slice(tagStart, tagEnd + 1);
|
|
194
|
+
const parsed = parseTag(rawTag);
|
|
195
|
+
if (!parsed) {
|
|
196
|
+
cursor = tagEnd + 1;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (DANGEROUS_BLOCK_TAGS.has(parsed.name)) {
|
|
200
|
+
const closeEnd = parsed.closing ? -1 : findDangerousClose(html, parsed.name, tagEnd + 1);
|
|
201
|
+
cursor = closeEnd === -1 ? tagEnd + 1 : closeEnd;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if (ALLOWED_TAGS.has(parsed.name)) {
|
|
205
|
+
if (parsed.closing) {
|
|
206
|
+
if (!VOID_TAGS.has(parsed.name)) output += `</${parsed.name}>`;
|
|
207
|
+
} else {
|
|
208
|
+
output += `<${parsed.name}${sanitizeAttributes(parsed.name, parsed.attributes)}>`;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
cursor = tagEnd + 1;
|
|
212
|
+
}
|
|
213
|
+
return output;
|
|
214
|
+
}
|
|
215
|
+
function htmlToTextSnippet(html, maxLength = 140) {
|
|
216
|
+
return sanitizeHtml(html).replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim().slice(0, maxLength);
|
|
217
|
+
}
|
|
218
|
+
export {
|
|
219
|
+
htmlToTextSnippet,
|
|
220
|
+
sanitizeHtml
|
|
221
|
+
};
|
|
222
|
+
//# sourceMappingURL=safe-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/internal/safe-html.ts"],"sourcesContent":["const DANGEROUS_BLOCK_TAGS = new Set([\n \"script\",\n \"style\",\n \"iframe\",\n \"object\",\n \"embed\",\n \"svg\",\n \"math\",\n \"template\",\n \"noscript\",\n \"textarea\",\n \"select\",\n])\n\nconst ALLOWED_TAGS = new Set([\n \"a\",\n \"b\",\n \"blockquote\",\n \"br\",\n \"code\",\n \"del\",\n \"div\",\n \"em\",\n \"hr\",\n \"i\",\n \"img\",\n \"li\",\n \"ol\",\n \"p\",\n \"pre\",\n \"s\",\n \"span\",\n \"strong\",\n \"table\",\n \"tbody\",\n \"td\",\n \"th\",\n \"thead\",\n \"tr\",\n \"u\",\n \"ul\",\n])\n\nconst VOID_TAGS = new Set([\"br\", \"hr\", \"img\"])\nconst SAFE_GLOBAL_ATTRS = new Set([\"aria-label\", \"role\", \"title\"])\nconst SAFE_URL_PROTOCOLS = new Set([\"http:\", \"https:\", \"mailto:\", \"tel:\"])\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n}\n\nfunction escapeAttribute(value: string): string {\n return escapeHtml(value).replace(/\"/g, \""\")\n}\n\nfunction safeCodePoint(value: number): string {\n return Number.isInteger(value) && value >= 0 && value <= 0x10ffff ? String.fromCodePoint(value) : \"\"\n}\n\nfunction decodeHtmlEntities(value: string): string {\n const namedEntities: Record<string, string> = {\n amp: \"&\",\n apos: \"'\",\n colon: \":\",\n gt: \">\",\n lt: \"<\",\n newline: \"\\n\",\n quot: '\"',\n tab: \"\\t\",\n }\n\n let decoded = value\n for (let i = 0; i < 4; i += 1) {\n const next = decoded\n .replace(/&#x([0-9a-f]+);?/gi, (_match, hex: string) => {\n const codePoint = Number.parseInt(hex, 16)\n return safeCodePoint(codePoint)\n })\n .replace(/&#(\\d+);?/g, (_match, decimal: string) => {\n const codePoint = Number.parseInt(decimal, 10)\n return safeCodePoint(codePoint)\n })\n .replace(/&([a-z]+);/gi, (match, name: string) => namedEntities[name.toLowerCase()] ?? match)\n\n if (next === decoded) return decoded\n decoded = next\n }\n\n return decoded\n}\n\nfunction isSafeUrl(value: string): boolean {\n const decoded = decodeHtmlEntities(value).replace(/[\\u0000-\\u001f\\u007f\\s]+/g, \"\").trim()\n if (!decoded) return false\n if (decoded.startsWith(\"//\")) return false\n if (decoded.startsWith(\"#\") || decoded.startsWith(\"/\") || decoded.startsWith(\"./\") || decoded.startsWith(\"../\")) {\n return true\n }\n\n try {\n return SAFE_URL_PROTOCOLS.has(new URL(decoded, \"https://handled.local\").protocol)\n } catch {\n return false\n }\n}\n\nfunction sanitizeClassName(value: string): string | null {\n const safeTokens = value\n .split(/\\s+/)\n .map((token) => token.trim())\n .filter((token) => /^[A-Za-z0-9_-]+$/.test(token))\n\n return safeTokens.length ? safeTokens.join(\" \") : null\n}\n\nfunction sanitizeAttribute(tagName: string, name: string, value: string): string | null {\n const attr = name.toLowerCase()\n\n if (\n attr.startsWith(\"on\") ||\n attr === \"style\" ||\n attr === \"srcdoc\" ||\n attr === \"formaction\" ||\n attr === \"xlink:href\" ||\n attr === \"xmlns\"\n ) {\n return null\n }\n\n if (attr === \"class\") {\n const safeClassName = sanitizeClassName(value)\n return safeClassName ? `class=\"${escapeAttribute(safeClassName)}\"` : null\n }\n\n if (SAFE_GLOBAL_ATTRS.has(attr) || attr.startsWith(\"aria-\")) {\n return `${attr}=\"${escapeAttribute(value)}\"`\n }\n\n if (tagName === \"a\" && attr === \"href\" && isSafeUrl(value)) {\n return `href=\"${escapeAttribute(value)}\"`\n }\n\n if (tagName === \"img\" && attr === \"src\" && isSafeUrl(value)) {\n return `src=\"${escapeAttribute(value)}\"`\n }\n\n if (tagName === \"img\" && (attr === \"alt\" || attr === \"width\" || attr === \"height\")) {\n return `${attr}=\"${escapeAttribute(value)}\"`\n }\n\n if ((tagName === \"td\" || tagName === \"th\") && (attr === \"colspan\" || attr === \"rowspan\")) {\n return `${attr}=\"${escapeAttribute(value)}\"`\n }\n\n return null\n}\n\nfunction sanitizeAttributes(tagName: string, rawAttributes = \"\"): string {\n const attributes: string[] = []\n const attrPattern = /([A-Za-z_:][-A-Za-z0-9_:.]*)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'=<>`]+)))?/g\n let match: RegExpExecArray | null\n\n while ((match = attrPattern.exec(rawAttributes)) !== null) {\n const [, name, doubleQuotedValue, singleQuotedValue, unquotedValue] = match\n const value = doubleQuotedValue ?? singleQuotedValue ?? unquotedValue ?? \"\"\n const safeAttribute = sanitizeAttribute(tagName, name, value)\n if (safeAttribute) attributes.push(safeAttribute)\n }\n\n if (tagName === \"a\" && attributes.some((attr) => attr.startsWith(\"href=\"))) {\n attributes.push('target=\"_blank\"', 'rel=\"noopener noreferrer\"')\n }\n\n return attributes.length ? ` ${attributes.join(\" \")}` : \"\"\n}\n\nfunction findTagEnd(html: string, startIndex: number): number {\n let quote: '\"' | \"'\" | null = null\n\n for (let i = startIndex + 1; i < html.length; i += 1) {\n const char = html[i]\n if (quote) {\n if (char === quote) quote = null\n continue\n }\n\n if (char === '\"' || char === \"'\") {\n quote = char\n continue\n }\n\n if (char === \">\") return i\n }\n\n return -1\n}\n\nfunction parseTag(rawTag: string): { closing: boolean; name: string; attributes: string } | null {\n const match = rawTag.match(/^<\\s*(\\/)?\\s*([A-Za-z][A-Za-z0-9:-]*)\\b([\\s\\S]*?)\\/?>$/)\n if (!match) return null\n\n return {\n closing: !!match[1],\n name: match[2].toLowerCase(),\n attributes: match[3] ?? \"\",\n }\n}\n\nfunction findDangerousClose(html: string, tagName: string, fromIndex: number): number {\n const closePattern = new RegExp(`</\\\\s*${tagName}\\\\s*>`, \"ig\")\n closePattern.lastIndex = fromIndex\n const match = closePattern.exec(html)\n return match ? closePattern.lastIndex : -1\n}\n\n/**\n * Conservative, deterministic sanitizer for user/email supplied HTML rendered by\n * design-system components. It keeps common email formatting tags while removing\n * executable tags, event handlers, inline styles, and unsafe URLs. This stays\n * dependency-free for the shared package and intentionally favors stripping\n * ambiguous email content over preserving every possible HTML feature.\n */\nexport function sanitizeHtml(html: string): string {\n let output = \"\"\n let cursor = 0\n\n while (cursor < html.length) {\n const tagStart = html.indexOf(\"<\", cursor)\n if (tagStart === -1) {\n output += html.slice(cursor)\n break\n }\n\n output += html.slice(cursor, tagStart)\n\n if (html.startsWith(\"<!--\", tagStart)) {\n const commentEnd = html.indexOf(\"-->\", tagStart + 4)\n cursor = commentEnd === -1 ? html.length : commentEnd + 3\n continue\n }\n\n const tagEnd = findTagEnd(html, tagStart)\n if (tagEnd === -1) {\n output += escapeHtml(html.slice(tagStart))\n break\n }\n\n const rawTag = html.slice(tagStart, tagEnd + 1)\n const parsed = parseTag(rawTag)\n if (!parsed) {\n cursor = tagEnd + 1\n continue\n }\n\n if (DANGEROUS_BLOCK_TAGS.has(parsed.name)) {\n const closeEnd = parsed.closing ? -1 : findDangerousClose(html, parsed.name, tagEnd + 1)\n cursor = closeEnd === -1 ? tagEnd + 1 : closeEnd\n continue\n }\n\n if (ALLOWED_TAGS.has(parsed.name)) {\n if (parsed.closing) {\n if (!VOID_TAGS.has(parsed.name)) output += `</${parsed.name}>`\n } else {\n output += `<${parsed.name}${sanitizeAttributes(parsed.name, parsed.attributes)}>`\n }\n }\n\n cursor = tagEnd + 1\n }\n\n return output\n}\n\nexport function htmlToTextSnippet(html: string, maxLength = 140): string {\n return sanitizeHtml(html)\n .replace(/<[^>]+>/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim()\n .slice(0, maxLength)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AccountFilterTab, a as AccountsViewConfig, b as AdminTab, c as AdminViewConfig, B as BriefStyleVariant, E as EntityPanelConfig, d as EntityPanelSection, I as InboxDetailSections, e as InboxSortOption, f as InboxViewConfig, g as InsightsCustomTab, h as InsightsViewConfig, i as PrototypeBrandConfig, j as PrototypeConfig, Q as QueueItem,
|
|
1
|
+
export { A as AccountFilterTab, a as AccountsViewConfig, b as AdminTab, c as AdminViewConfig, B as BriefStyleVariant, E as EntityPanelConfig, d as EntityPanelSection, I as InboxDetailSections, e as InboxSortOption, f as InboxViewConfig, g as InsightsCustomTab, h as InsightsViewConfig, i as PrototypeBrandConfig, j as PrototypeConfig, Q as QueueItem, m as SignalScoreData, n as SignalScoreExplanationBucket, o as SignalScoreExplanationSignal, p as SignalScoreUrgencyLabel, T as TimelineSystemEventsConfig, W as WorkQueueViewConfig } from '../signal-priority-popover-CZitE9xq.js';
|
|
2
2
|
export { PrototypeShell, PrototypeShellProps } from './prototype-shell.js';
|
|
3
3
|
export { DetailView, DetailViewProps, PrototypeInboxView, PrototypeInboxViewProps } from './prototype-inbox-view.js';
|
|
4
4
|
export { PrototypeInsightsView, PrototypeInsightsViewProps } from './prototype-insights-view.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { a as AccountsViewConfig } from '../signal-priority-popover-
|
|
2
|
+
import { a as AccountsViewConfig } from '../signal-priority-popover-CZitE9xq.js';
|
|
3
3
|
import { DataRow } from '../components/data-table.js';
|
|
4
4
|
import '../components/feedback-primitives.js';
|
|
5
5
|
import '../components/quick-action-sidebar-nav.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { c as AdminViewConfig } from '../signal-priority-popover-
|
|
2
|
+
import { c as AdminViewConfig } from '../signal-priority-popover-CZitE9xq.js';
|
|
3
3
|
import '../components/feedback-primitives.js';
|
|
4
4
|
import '../components/quick-action-sidebar-nav.js';
|
|
5
5
|
import '../components/quick-action-modal.js';
|
|
@@ -11,7 +11,7 @@ import '../charts/pipeline-overview.js';
|
|
|
11
11
|
import '../components/timeline-activity.js';
|
|
12
12
|
import '../components/signal-feedback-inline.js';
|
|
13
13
|
import 'lucide-react';
|
|
14
|
-
export { A as AccountFilterTab, a as AccountsViewConfig, b as AdminTab, c as AdminViewConfig, B as BriefStyleVariant, E as EntityPanelConfig, d as EntityPanelSection, I as InboxDetailSections, e as InboxSortOption, f as InboxViewConfig, g as InsightsCustomTab, h as InsightsViewConfig, i as PrototypeBrandConfig, j as PrototypeConfig, Q as QueueItem,
|
|
14
|
+
export { A as AccountFilterTab, a as AccountsViewConfig, b as AdminTab, c as AdminViewConfig, B as BriefStyleVariant, E as EntityPanelConfig, d as EntityPanelSection, I as InboxDetailSections, e as InboxSortOption, f as InboxViewConfig, g as InsightsCustomTab, h as InsightsViewConfig, i as PrototypeBrandConfig, j as PrototypeConfig, Q as QueueItem, m as SignalScoreData, n as SignalScoreExplanationBucket, o as SignalScoreExplanationSignal, p as SignalScoreUrgencyLabel, T as TimelineSystemEventsConfig, W as WorkQueueViewConfig } from '../signal-priority-popover-CZitE9xq.js';
|
|
15
15
|
import '../components/feedback-primitives.js';
|
|
16
16
|
import '../components/quick-action-modal.js';
|
|
17
17
|
import '../components/score-semantics.js';
|
|
@@ -3,7 +3,7 @@ import { OpportunityDraft, ApprovalState, OpportunityPreview } from '../componen
|
|
|
3
3
|
import { SourceDef } from '../components/detail-view.js';
|
|
4
4
|
import { SuggestedAction, SuggestedContact } from '../components/suggested-actions.js';
|
|
5
5
|
import { TimelineEvent } from '../components/timeline-activity.js';
|
|
6
|
-
import { Q as QueueItem, I as InboxDetailSections,
|
|
6
|
+
import { Q as QueueItem, I as InboxDetailSections, m as SignalScoreData, f as InboxViewConfig, B as BriefStyleVariant, T as TimelineSystemEventsConfig } from '../signal-priority-popover-CZitE9xq.js';
|
|
7
7
|
import '../components/feedback-primitives.js';
|
|
8
8
|
import '../components/quick-action-sidebar-nav.js';
|
|
9
9
|
import '../components/quick-action-modal.js';
|
|
@@ -362,6 +362,8 @@ function DetailView({
|
|
|
362
362
|
score: signalData.score,
|
|
363
363
|
urgencyLabel: signalData.urgencyLabel,
|
|
364
364
|
urgencyExplanation: (_b = signalData.urgencyExplanation) != null ? _b : signalData.signalBrief,
|
|
365
|
+
scoreDisplay: signalData.priorityScoreDisplay,
|
|
366
|
+
formulaLabel: signalData.priorityFormulaLabel,
|
|
365
367
|
factors: (_c = signalData.priorityFactors) != null ? _c : [],
|
|
366
368
|
metaText: void 0,
|
|
367
369
|
feedbackChips: signalData.priorityFeedbackChips,
|