@handled-ai/design-system 0.20.23 → 0.20.26
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/button.d.ts +1 -1
- package/dist/components/conversation-panel.d.ts +30 -2
- package/dist/components/conversation-panel.js +71 -11
- package/dist/components/conversation-panel.js.map +1 -1
- package/dist/components/email-display-helpers.d.ts +3 -1
- package/dist/components/email-display-helpers.js +63 -14
- package/dist/components/email-display-helpers.js.map +1 -1
- package/dist/components/timeline-activity.js +18 -6
- package/dist/components/timeline-activity.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/conversation-panel.test.tsx +58 -0
- package/src/components/__tests__/email-display-helpers.test.ts +45 -1
- package/src/components/__tests__/timeline-activity.test.tsx +7 -1
- package/src/components/conversation-panel.tsx +95 -1
- package/src/components/email-display-helpers.ts +63 -15
- package/src/components/timeline-activity.tsx +28 -6
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __spreadValues = (a, b) => {
|
|
7
|
+
for (var prop in b || (b = {}))
|
|
8
|
+
if (__hasOwnProp.call(b, prop))
|
|
9
|
+
__defNormalProp(a, prop, b[prop]);
|
|
10
|
+
if (__getOwnPropSymbols)
|
|
11
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
+
if (__propIsEnum.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
};
|
|
1
17
|
import { htmlToTextSnippet, sanitizeHtml } from "../internal/safe-html.js";
|
|
2
18
|
const HTML_ENTITY_RE = /&(?:#x[0-9a-f]+|#\d+|[a-z][a-z0-9]+);?/i;
|
|
3
19
|
const EMAIL_RE = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i;
|
|
@@ -145,18 +161,17 @@ function formatAddressList(input) {
|
|
|
145
161
|
const rawItems = Array.isArray(input) ? input : splitAddressList(input);
|
|
146
162
|
return rawItems.map((item) => formatSingleAddress(item)).filter(Boolean).join(", ");
|
|
147
163
|
}
|
|
148
|
-
function formatEmailTimestamp(value) {
|
|
164
|
+
function formatEmailTimestamp(value, opts) {
|
|
149
165
|
if (!value) return null;
|
|
150
166
|
const date = value instanceof Date ? value : new Date(value);
|
|
151
167
|
if (Number.isNaN(date.getTime())) return null;
|
|
152
|
-
return new Intl.DateTimeFormat("en-US", {
|
|
168
|
+
return new Intl.DateTimeFormat("en-US", __spreadValues({
|
|
153
169
|
month: "short",
|
|
154
170
|
day: "numeric",
|
|
155
171
|
year: "numeric",
|
|
156
172
|
hour: "numeric",
|
|
157
|
-
minute: "2-digit"
|
|
158
|
-
|
|
159
|
-
}).format(date);
|
|
173
|
+
minute: "2-digit"
|
|
174
|
+
}, (opts == null ? void 0 : opts.timeZone) ? { timeZone: opts.timeZone } : {})).format(date);
|
|
160
175
|
}
|
|
161
176
|
function escapeHtmlText(value) {
|
|
162
177
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -212,6 +227,12 @@ function makeHtmlSegment(html) {
|
|
|
212
227
|
if (!html.trim() || !visibleText && !/<(?:img|hr)\b/i.test(html)) return null;
|
|
213
228
|
return { html, visibleText };
|
|
214
229
|
}
|
|
230
|
+
function makeBlankLineSegment(html) {
|
|
231
|
+
return { html, visibleText: "" };
|
|
232
|
+
}
|
|
233
|
+
function isBlankLineHtml(html) {
|
|
234
|
+
return Boolean(html.trim()) && !htmlToVisibleText(html) && !/<(?:img|hr)\b/i.test(html);
|
|
235
|
+
}
|
|
215
236
|
function splitInlineNodes(nodes, wrapper) {
|
|
216
237
|
const containsBr = hasDirectBr(nodes);
|
|
217
238
|
const chunks = [[]];
|
|
@@ -223,10 +244,20 @@ function splitInlineNodes(nodes, wrapper) {
|
|
|
223
244
|
}
|
|
224
245
|
(_a = chunks[chunks.length - 1]) == null ? void 0 : _a.push(serializeNode(node));
|
|
225
246
|
});
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
247
|
+
const rendered = chunks.map((chunk) => chunk.join(""));
|
|
248
|
+
const segments = [];
|
|
249
|
+
rendered.forEach((innerHtml, index) => {
|
|
250
|
+
const html = wrapper ? wrapHtmlLike(wrapper, innerHtml) : containsBr ? `<div>${innerHtml}</div>` : innerHtml;
|
|
251
|
+
const segment = makeHtmlSegment(html);
|
|
252
|
+
if (segment) {
|
|
253
|
+
segments.push(segment);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (containsBr && index > 0 && index < rendered.length - 1) {
|
|
257
|
+
segments.push(makeBlankLineSegment(wrapper ? wrapHtmlLike(wrapper, "<br>") : "<div><br></div>"));
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
return segments;
|
|
230
261
|
}
|
|
231
262
|
function splitElementSegment(element) {
|
|
232
263
|
const tagName = element.tagName.toLowerCase();
|
|
@@ -234,6 +265,9 @@ function splitElementSegment(element) {
|
|
|
234
265
|
const segment2 = makeHtmlSegment(element.outerHTML);
|
|
235
266
|
return segment2 ? [segment2] : [];
|
|
236
267
|
}
|
|
268
|
+
if (isBlankLineHtml(element.outerHTML)) {
|
|
269
|
+
return [makeBlankLineSegment(element.outerHTML)];
|
|
270
|
+
}
|
|
237
271
|
if (tagName === "div" && hasDirectBlockChild(element)) {
|
|
238
272
|
const childSegments = splitHtmlNodes(Array.from(element.childNodes));
|
|
239
273
|
return childSegments.length ? childSegments : [makeHtmlSegment(element.outerHTML)].filter(Boolean);
|
|
@@ -287,9 +321,15 @@ function splitHtmlSegmentsFallback(html) {
|
|
|
287
321
|
const pushInline = (inlineHtml) => {
|
|
288
322
|
const chunks = inlineHtml.split(BR_TAG_RE);
|
|
289
323
|
const hadBr = chunks.length > 1;
|
|
290
|
-
chunks.forEach((chunk) => {
|
|
324
|
+
chunks.forEach((chunk, index) => {
|
|
291
325
|
const segment = makeHtmlSegment(hadBr ? `<div>${chunk}</div>` : chunk);
|
|
292
|
-
if (segment)
|
|
326
|
+
if (segment) {
|
|
327
|
+
segments.push(segment);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (hadBr && index > 0 && index < chunks.length - 1) {
|
|
331
|
+
segments.push(makeBlankLineSegment("<div><br /></div>"));
|
|
332
|
+
}
|
|
293
333
|
});
|
|
294
334
|
};
|
|
295
335
|
while (cursor < html.length) {
|
|
@@ -306,13 +346,22 @@ function splitHtmlSegmentsFallback(html) {
|
|
|
306
346
|
const openTagEnd = tagStart + rawOpen.length - 1;
|
|
307
347
|
const segmentEnd = findMatchingCloseTag(html, tagName, openTagEnd);
|
|
308
348
|
const blockHtml = html.slice(tagStart, segmentEnd);
|
|
309
|
-
if (
|
|
349
|
+
if (isBlankLineHtml(blockHtml)) {
|
|
350
|
+
segments.push(makeBlankLineSegment(blockHtml));
|
|
351
|
+
} else if (SPLITTABLE_BLOCK_TAGS.has(tagName) && BR_TAG_RE.test(blockHtml)) {
|
|
310
352
|
const openTag = rawOpen;
|
|
311
353
|
const closeTag = `</${tagName}>`;
|
|
312
354
|
const inner = blockHtml.replace(new RegExp(`^${rawOpen.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i"), "").replace(new RegExp(`${closeTag}$`, "i"), "");
|
|
313
|
-
inner.split(BR_TAG_RE)
|
|
355
|
+
const innerChunks = inner.split(BR_TAG_RE);
|
|
356
|
+
innerChunks.forEach((chunk, index) => {
|
|
314
357
|
const segment = makeHtmlSegment(`${openTag}${chunk}${closeTag}`);
|
|
315
|
-
if (segment)
|
|
358
|
+
if (segment) {
|
|
359
|
+
segments.push(segment);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (index > 0 && index < innerChunks.length - 1) {
|
|
363
|
+
segments.push(makeBlankLineSegment(`${openTag}<br />${closeTag}`));
|
|
364
|
+
}
|
|
316
365
|
});
|
|
317
366
|
} else {
|
|
318
367
|
const segment = makeHtmlSegment(blockHtml);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/email-display-helpers.ts"],"sourcesContent":["import { htmlToTextSnippet, sanitizeHtml } from \"../internal/safe-html\"\n\nexport interface NormalizedEmailSender {\n name: string\n email: string | null\n}\n\nexport interface EmailDisplaySenderInput {\n name?: string | null\n email?: string | null\n fallbackName?: string\n}\n\nexport interface SplitEmailHtmlResult {\n bodyHtml: string\n detailsHtml: string\n}\n\nexport interface SplitEmailTextResult {\n bodyText: string\n detailsText: string\n}\n\nconst HTML_ENTITY_RE = /&(?:#x[0-9a-f]+|#\\d+|[a-z][a-z0-9]+);?/i\nconst EMAIL_RE = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/i\nconst ANGLE_ADDRESS_RE = /^\\s*(.*?)\\s*<\\s*([^<>\\s]+@[^<>\\s]+)\\s*>\\s*$/\nconst BR_TAG_RE = /<br\\s*\\/?>/gi\n\nconst SPLITTABLE_BLOCK_TAGS = new Set([\"p\", \"div\"])\nconst WHOLE_BLOCK_TAGS = new Set([\"blockquote\", \"table\", \"ul\", \"ol\", \"hr\"])\nconst BLOCK_TAGS = new Set([...SPLITTABLE_BLOCK_TAGS, ...WHOLE_BLOCK_TAGS])\nconst HTML_BLOCK_START_RE = /<(p|div|blockquote|table|ul|ol|hr)\\b[^>]*>/i\n\nconst SIGNATURE_DELIMITER_RE = /^--\\s*$/\nconst GMAIL_SIGNATURE_RE = /\\b(?:gmail_signature|gmail_signature_prefix|gmail_extra)\\b/i\nconst GMAIL_QUOTE_RE = /<blockquote\\b[^>]*\\bclass=[\"'][^\"']*\\bgmail_quote\\b/i\nconst ON_WROTE_RE = /^On\\s.+wrote:\\s*$/i\nconst SIGNOFF_RE = /^(?:thanks,|thank you,|best,|regards,|sincerely,)$/i\nconst DETAILS_START_RE = /^(?:confidentiality notice\\b|this message and any attachments\\b|this email and any attachments\\b|the information contained in this message\\b|this communication may contain\\b|unsubscribe\\b|manage your preferences\\b)/i\nconst CONTACT_DETAIL_RE = /(?:@|https?:\\/\\/|www\\.|\\+?\\d[\\d\\s().-]{6,}\\d|\\b(?:ceo|cfo|cto|coo|founder|co-founder|director|manager|vp|vice president|president|head of|sales|marketing|operations|account|customer success|success|support|engineer|consultant|partner|principal|advisor|associate)\\b|\\b(?:inc|llc|ltd|corp|corporation|company|co\\.)\\b)/i\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 nbsp: \" \",\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) => safeCodePoint(Number.parseInt(hex, 16)))\n .replace(/&#(\\d+);?/g, (_match, decimal: string) => safeCodePoint(Number.parseInt(decimal, 10)))\n .replace(/&([a-z][a-z0-9]+);?/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 decodeJsonEscapes(value: string): string {\n return value\n .replace(/\\\\u\\{([0-9a-f]{1,6})\\}/gi, (_match, hex: string) => safeCodePoint(Number.parseInt(hex, 16)))\n .replace(/\\\\u([0-9a-f]{4})/gi, (_match, hex: string) => safeCodePoint(Number.parseInt(hex, 16)))\n .replace(/\\\\r\\\\n|\\\\n|\\\\r/g, \"\\n\")\n .replace(/\\\\t/g, \"\\t\")\n .replace(/\\\\([\"'\\\\/])/g, \"$1\")\n}\n\nfunction maybeParseJsonString(value: string): string {\n const trimmed = value.trim()\n if (!trimmed.startsWith('\"') || !trimmed.endsWith('\"')) return value\n if (!/[\\\\&]/.test(trimmed)) return value\n\n try {\n const parsed = JSON.parse(trimmed) as unknown\n return typeof parsed === \"string\" ? parsed : value\n } catch {\n return value\n }\n}\n\n/**\n * Decodes display-only email text so UI labels, body fallbacks, collapsed rows,\n * and snippets do not show HTML entities or JSON escape artifacts. It does not\n * sanitize HTML; sanitize at the HTML render boundary before using markup.\n */\nexport function decodeEmailDisplayText(value: string): string {\n let decoded = maybeParseJsonString(value).replace(/\\r\\n?/g, \"\\n\")\n\n for (let i = 0; i < 4; i += 1) {\n const next = decodeHtmlEntities(decodeJsonEscapes(decoded))\n if (next === decoded) break\n decoded = next\n }\n\n return decoded.replace(/\\u00a0/g, \" \")\n}\n\nfunction stripWrappingQuotes(value: string): string {\n const trimmed = value.trim()\n if ((trimmed.startsWith('\"') && trimmed.endsWith('\"')) || (trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\"))) {\n return trimmed.slice(1, -1).trim()\n }\n return trimmed\n}\n\nfunction extractEmail(value: string): string | null {\n const decoded = decodeEmailDisplayText(value)\n const angleMatch = decoded.match(ANGLE_ADDRESS_RE)\n const email = angleMatch?.[2] ?? decoded.match(EMAIL_RE)?.[0]\n return email ? email.trim() : null\n}\n\nfunction extractNameFromAddress(value: string): string {\n const decoded = decodeEmailDisplayText(value)\n const angleMatch = decoded.match(ANGLE_ADDRESS_RE)\n if (angleMatch) return stripWrappingQuotes(angleMatch[1] ?? \"\")\n return stripWrappingQuotes(decoded.replace(EMAIL_RE, \"\").replace(/[<>]/g, \"\").trim())\n}\n\nfunction cleanDisplayName(value: string, email: string | null): string {\n let name = stripWrappingQuotes(decodeEmailDisplayText(value))\n if (email) {\n name = name\n .replace(new RegExp(`<\\\\s*${email.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\s*>`, \"i\"), \"\")\n .replace(new RegExp(`^${email.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}$`, \"i\"), \"\")\n .trim()\n }\n return stripWrappingQuotes(name)\n}\n\nexport function normalizeEmailSender(input: EmailDisplaySenderInput): NormalizedEmailSender {\n const fallbackName = decodeEmailDisplayText(input.fallbackName ?? \"Unknown sender\").trim() || \"Unknown sender\"\n const rawName = input.name ? decodeEmailDisplayText(input.name) : \"\"\n const rawEmail = input.email ? decodeEmailDisplayText(input.email) : \"\"\n const email = extractEmail(rawEmail) ?? extractEmail(rawName)\n\n const nameFromName = rawName ? cleanDisplayName(extractNameFromAddress(rawName) || rawName, email) : \"\"\n const nameFromEmail = rawEmail ? cleanDisplayName(extractNameFromAddress(rawEmail), email) : \"\"\n const name = nameFromName || nameFromEmail || email || fallbackName\n\n return { name, email }\n}\n\nfunction splitAddressList(value: string): string[] {\n const parts: string[] = []\n let current = \"\"\n let quote: '\"' | \"'\" | null = null\n let angleDepth = 0\n\n for (let index = 0; index < value.length; index += 1) {\n const char = value[index]\n\n if (quote) {\n current += char\n if (char === quote && value[index - 1] !== \"\\\\\") quote = null\n continue\n }\n\n if (char === '\"' || char === \"'\") {\n quote = char\n current += char\n continue\n }\n\n if (char === \"<\") angleDepth += 1\n if (char === \">\" && angleDepth > 0) angleDepth -= 1\n\n if (char === \",\" && angleDepth === 0) {\n if (current.trim()) parts.push(current.trim())\n current = \"\"\n continue\n }\n\n current += char\n }\n\n if (current.trim()) parts.push(current.trim())\n return parts\n}\n\nfunction formatSingleAddress(value: string): string {\n const decoded = decodeEmailDisplayText(value).trim()\n if (!decoded) return \"\"\n\n const email = extractEmail(decoded)\n const name = cleanDisplayName(extractNameFromAddress(decoded), email)\n\n if (email && name) return `${name} <${email}>`\n if (email) return email\n return stripWrappingQuotes(decoded)\n}\n\nexport function formatAddressList(input?: string | string[] | null): string {\n if (!input) return \"\"\n\n const rawItems = Array.isArray(input) ? input : splitAddressList(input)\n return rawItems\n .map((item) => formatSingleAddress(item))\n .filter(Boolean)\n .join(\", \")\n}\n\nexport function formatEmailTimestamp(value?: string | Date | null): string | null {\n if (!value) return null\n const date = value instanceof Date ? value : new Date(value)\n if (Number.isNaN(date.getTime())) return null\n\n return new Intl.DateTimeFormat(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n timeZone: \"UTC\",\n }).format(date)\n}\n\ntype MessageSegment = { html?: string; text?: string; visibleText: string }\n\nfunction escapeHtmlText(value: string): string {\n return value.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\")\n}\n\nfunction decodeHtmlTextNodes(html: string): string {\n if (!HTML_ENTITY_RE.test(html) && !/\\\\[nrt\"'\\\\/]|\\\\u/i.test(html)) return html\n\n if (typeof document !== \"undefined\") {\n const template = document.createElement(\"template\")\n template.innerHTML = html\n const walker = document.createTreeWalker(template.content, NodeFilter.SHOW_TEXT)\n const textNodes: Text[] = []\n let node = walker.nextNode()\n while (node) {\n textNodes.push(node as Text)\n node = walker.nextNode()\n }\n textNodes.forEach((textNode) => {\n textNode.nodeValue = decodeEmailDisplayText(textNode.nodeValue ?? \"\")\n })\n return template.innerHTML\n }\n\n return html\n .split(/(<[^>]+>)/g)\n .map((part) => (part.startsWith(\"<\") && part.endsWith(\">\") ? part : escapeHtmlText(decodeEmailDisplayText(part))))\n .join(\"\")\n}\n\nfunction decodeHtmlText(value: string): string {\n const withoutTags = value\n .replace(BR_TAG_RE, \"\\n\")\n .replace(/<\\/(p|div|blockquote|li|tr|table|ul|ol)>/gi, \"\\n\")\n .replace(/<[^>]*>/g, \"\")\n\n return decodeEmailDisplayText(withoutTags)\n}\n\nfunction htmlToVisibleText(html: string): string {\n return decodeHtmlText(html).replace(/\\u00a0/g, \" \").replace(/[ \\t]+/g, \" \").trim()\n}\n\nfunction serializeNode(node: Node): string {\n const host = document.createElement(\"div\")\n host.appendChild(node.cloneNode(true))\n return host.innerHTML\n}\n\nfunction wrapHtmlLike(element: Element, innerHtml: string): string {\n const clone = element.cloneNode(false) as HTMLElement\n clone.innerHTML = innerHtml\n return clone.outerHTML\n}\n\nfunction hasDirectBr(nodes: readonly Node[]): boolean {\n return nodes.some((node) => node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName.toLowerCase() === \"br\")\n}\n\nfunction hasDirectBlockChild(element: Element): boolean {\n return Array.from(element.children).some((child) => {\n const tagName = child.tagName.toLowerCase()\n return tagName !== \"br\" && BLOCK_TAGS.has(tagName)\n })\n}\n\nfunction makeHtmlSegment(html: string): MessageSegment | null {\n const visibleText = htmlToVisibleText(html)\n if (!html.trim() || (!visibleText && !/<(?:img|hr)\\b/i.test(html))) return null\n return { html, visibleText }\n}\n\nfunction splitInlineNodes(nodes: readonly Node[], wrapper?: Element): MessageSegment[] {\n const containsBr = hasDirectBr(nodes)\n const chunks: string[][] = [[]]\n\n nodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName.toLowerCase() === \"br\") {\n chunks.push([])\n return\n }\n\n chunks[chunks.length - 1]?.push(serializeNode(node))\n })\n\n return chunks\n .map((chunk) => chunk.join(\"\"))\n .map((innerHtml) => {\n if (wrapper) return wrapHtmlLike(wrapper, innerHtml)\n return containsBr ? `<div>${innerHtml}</div>` : innerHtml\n })\n .map(makeHtmlSegment)\n .filter((segment): segment is MessageSegment => Boolean(segment))\n}\n\nfunction splitElementSegment(element: Element): MessageSegment[] {\n const tagName = element.tagName.toLowerCase()\n\n if (GMAIL_SIGNATURE_RE.test(element.outerHTML) || GMAIL_QUOTE_RE.test(element.outerHTML)) {\n const segment = makeHtmlSegment(element.outerHTML)\n return segment ? [segment] : []\n }\n\n if (tagName === \"div\" && hasDirectBlockChild(element)) {\n const childSegments = splitHtmlNodes(Array.from(element.childNodes))\n return childSegments.length ? childSegments : ([makeHtmlSegment(element.outerHTML)].filter(Boolean) as MessageSegment[])\n }\n\n if (SPLITTABLE_BLOCK_TAGS.has(tagName) && hasDirectBr(Array.from(element.childNodes)) && !hasDirectBlockChild(element)) {\n return splitInlineNodes(Array.from(element.childNodes), element)\n }\n\n const segment = makeHtmlSegment(element.outerHTML)\n return segment ? [segment] : []\n}\n\nfunction splitHtmlNodes(nodes: readonly Node[]): MessageSegment[] {\n const segments: MessageSegment[] = []\n let inlineNodes: Node[] = []\n\n const flushInline = () => {\n if (!inlineNodes.length) return\n segments.push(...splitInlineNodes(inlineNodes))\n inlineNodes = []\n }\n\n nodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element\n const tagName = element.tagName.toLowerCase()\n if (BLOCK_TAGS.has(tagName)) {\n flushInline()\n segments.push(...splitElementSegment(element))\n return\n }\n }\n\n inlineNodes.push(node)\n })\n\n flushInline()\n return segments\n}\n\nfunction findMatchingCloseTag(html: string, tagName: string, openTagEnd: number): number {\n if (tagName === \"hr\") return openTagEnd + 1\n\n const tagPattern = new RegExp(`</?${tagName}\\\\b[^>]*>`, \"gi\")\n tagPattern.lastIndex = openTagEnd + 1\n let depth = 1\n let match: RegExpExecArray | null\n\n while ((match = tagPattern.exec(html)) !== null) {\n const rawTag = match[0]\n if (/^<\\//.test(rawTag)) depth -= 1\n else if (!/\\/\\s*>$/.test(rawTag)) depth += 1\n if (depth === 0) return tagPattern.lastIndex\n }\n\n return html.length\n}\n\nfunction splitHtmlSegmentsFallback(html: string): MessageSegment[] {\n const segments: MessageSegment[] = []\n let cursor = 0\n\n const pushInline = (inlineHtml: string) => {\n const chunks = inlineHtml.split(BR_TAG_RE)\n const hadBr = chunks.length > 1\n chunks.forEach((chunk) => {\n const segment = makeHtmlSegment(hadBr ? `<div>${chunk}</div>` : chunk)\n if (segment) segments.push(segment)\n })\n }\n\n while (cursor < html.length) {\n const rest = html.slice(cursor)\n const match = HTML_BLOCK_START_RE.exec(rest)\n if (!match || match.index === undefined) {\n pushInline(rest)\n break\n }\n\n if (match.index > 0) pushInline(rest.slice(0, match.index))\n\n const tagStart = cursor + match.index\n const rawOpen = match[0]\n const tagName = match[1].toLowerCase()\n const openTagEnd = tagStart + rawOpen.length - 1\n const segmentEnd = findMatchingCloseTag(html, tagName, openTagEnd)\n const blockHtml = html.slice(tagStart, segmentEnd)\n\n if (SPLITTABLE_BLOCK_TAGS.has(tagName) && BR_TAG_RE.test(blockHtml)) {\n const openTag = rawOpen\n const closeTag = `</${tagName}>`\n const inner = blockHtml.replace(new RegExp(`^${rawOpen.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}`, \"i\"), \"\").replace(new RegExp(`${closeTag}$`, \"i\"), \"\")\n inner.split(BR_TAG_RE).forEach((chunk) => {\n const segment = makeHtmlSegment(`${openTag}${chunk}${closeTag}`)\n if (segment) segments.push(segment)\n })\n } else {\n const segment = makeHtmlSegment(blockHtml)\n if (segment) segments.push(segment)\n }\n\n cursor = segmentEnd\n }\n\n return segments\n}\n\nfunction segmentHtmlMessage(html: string): MessageSegment[] {\n if (typeof document === \"undefined\" || typeof Node === \"undefined\") {\n return splitHtmlSegmentsFallback(html)\n }\n\n const template = document.createElement(\"template\")\n template.innerHTML = html\n return splitHtmlNodes(Array.from(template.content.childNodes))\n}\n\nfunction segmentTextMessage(text: string): MessageSegment[] {\n const normalized = decodeEmailDisplayText(text)\n const lines: string[] = normalized.match(/[^\\n]*(?:\\n|$)/g) ?? []\n return lines\n .filter((line, index) => line.length > 0 && !(index === lines.length - 1 && line === \"\"))\n .map((line) => ({ text: line, visibleText: line.replace(/\\u00a0/g, \" \").trim() }))\n}\n\nfunction firstVisibleLine(text: string): string {\n return text.replace(/\\u00a0/g, \" \").trimStart().split(/\\r?\\n/).find((line) => line.trim())?.trim() ?? \"\"\n}\n\nfunction isLikelySenderNameLine(line: string): boolean {\n if (!line || line.length > 60) return false\n if (/[,@:;!?]|https?:\\/\\/|www\\.|\\d/.test(line)) return false\n\n const words = line.split(/\\s+/).filter(Boolean)\n if (words.length < 1 || words.length > 4) return false\n\n return words.every((word) => /^[A-Z][A-Za-z'.-]*$/.test(word))\n}\n\nfunction nextVisibleSegmentText(segments: MessageSegment[], fromIndex: number): string {\n for (let index = fromIndex + 1; index < segments.length; index += 1) {\n const text = firstVisibleLine(segments[index]?.visibleText ?? \"\")\n if (text) return text\n }\n return \"\"\n}\n\nfunction isGmailDetailsSegment(segment: MessageSegment): boolean {\n return Boolean(segment.html && (GMAIL_SIGNATURE_RE.test(segment.html) || GMAIL_QUOTE_RE.test(segment.html)))\n}\n\nfunction isFooterBoundary(segments: MessageSegment[], index: number): boolean {\n const segment = segments[index]\n if (!segment) return false\n if (isGmailDetailsSegment(segment)) return true\n\n const line = firstVisibleLine(segment.visibleText)\n if (!line) return false\n if (SIGNATURE_DELIMITER_RE.test(line) || DETAILS_START_RE.test(line) || ON_WROTE_RE.test(line)) return true\n\n const nextText = nextVisibleSegmentText(segments, index)\n if (SIGNOFF_RE.test(line)) return Boolean(nextText && (isLikelySenderNameLine(nextText) || CONTACT_DETAIL_RE.test(nextText)))\n\n return isLikelySenderNameLine(line) && CONTACT_DETAIL_RE.test(nextText)\n}\n\nfunction splitFooterSegments(segments: MessageSegment[]): { bodySegments: MessageSegment[]; detailsSegments: MessageSegment[] } {\n const visibleIndexes = segments\n .map((segment, index) => (segment.visibleText || isGmailDetailsSegment(segment) ? index : -1))\n .filter((index) => index >= 0)\n const visibleCount = visibleIndexes.length\n if (visibleCount < 2) return { bodySegments: segments, detailsSegments: [] }\n\n const trailingCount = Math.max(Math.ceil(visibleCount * 0.4), 8)\n const firstTrailingOrdinal = Math.max(1, visibleCount - trailingCount)\n\n for (let ordinal = firstTrailingOrdinal; ordinal < visibleCount; ordinal += 1) {\n const index = visibleIndexes[ordinal]\n if (ordinal > 0 && isFooterBoundary(segments, index)) {\n return { bodySegments: segments.slice(0, index), detailsSegments: segments.slice(index) }\n }\n }\n\n return { bodySegments: segments, detailsSegments: [] }\n}\n\nexport function splitEmailHtmlForDisplay(html: string): SplitEmailHtmlResult {\n const sanitizedHtml = sanitizeHtml(decodeHtmlTextNodes(html))\n const { bodySegments, detailsSegments } = splitFooterSegments(segmentHtmlMessage(sanitizedHtml))\n\n if (!detailsSegments.length) return { bodyHtml: sanitizedHtml, detailsHtml: \"\" }\n\n return {\n bodyHtml: bodySegments.map((segment) => segment.html ?? \"\").join(\"\"),\n detailsHtml: detailsSegments.map((segment) => segment.html ?? \"\").join(\"\"),\n }\n}\n\nexport function splitEmailTextForDisplay(text: string): SplitEmailTextResult {\n const decodedText = decodeEmailDisplayText(text)\n const { bodySegments, detailsSegments } = splitFooterSegments(segmentTextMessage(decodedText))\n\n if (!detailsSegments.length) return { bodyText: decodedText, detailsText: \"\" }\n\n return {\n bodyText: bodySegments.map((segment) => segment.text ?? \"\").join(\"\"),\n detailsText: detailsSegments.map((segment) => segment.text ?? \"\").join(\"\"),\n }\n}\n\nexport function emailBodySnippet(input: { bodyHtml?: string | null; body?: string | null }, maxLength = 140): string {\n const html = input.bodyHtml?.trim()\n if (html) {\n return decodeEmailDisplayText(htmlToTextSnippet(splitEmailHtmlForDisplay(html).bodyHtml, maxLength)).trim()\n }\n\n const text = input.body ?? \"\"\n const bodyText = splitEmailTextForDisplay(text).bodyText\n const firstLine = bodyText.split(\"\\n\").find((line) => line.trim())?.trim() ?? \"\"\n return decodeEmailDisplayText(firstLine).replace(/\\s+/g, \" \").trim().slice(0, maxLength)\n}\n"],"mappings":"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","names":["segment"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/email-display-helpers.ts"],"sourcesContent":["import { htmlToTextSnippet, sanitizeHtml } from \"../internal/safe-html\"\n\nexport interface NormalizedEmailSender {\n name: string\n email: string | null\n}\n\nexport interface EmailDisplaySenderInput {\n name?: string | null\n email?: string | null\n fallbackName?: string\n}\n\nexport interface SplitEmailHtmlResult {\n bodyHtml: string\n detailsHtml: string\n}\n\nexport interface SplitEmailTextResult {\n bodyText: string\n detailsText: string\n}\n\nconst HTML_ENTITY_RE = /&(?:#x[0-9a-f]+|#\\d+|[a-z][a-z0-9]+);?/i\nconst EMAIL_RE = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/i\nconst ANGLE_ADDRESS_RE = /^\\s*(.*?)\\s*<\\s*([^<>\\s]+@[^<>\\s]+)\\s*>\\s*$/\nconst BR_TAG_RE = /<br\\s*\\/?>/gi\n\nconst SPLITTABLE_BLOCK_TAGS = new Set([\"p\", \"div\"])\nconst WHOLE_BLOCK_TAGS = new Set([\"blockquote\", \"table\", \"ul\", \"ol\", \"hr\"])\nconst BLOCK_TAGS = new Set([...SPLITTABLE_BLOCK_TAGS, ...WHOLE_BLOCK_TAGS])\nconst HTML_BLOCK_START_RE = /<(p|div|blockquote|table|ul|ol|hr)\\b[^>]*>/i\n\nconst SIGNATURE_DELIMITER_RE = /^--\\s*$/\nconst GMAIL_SIGNATURE_RE = /\\b(?:gmail_signature|gmail_signature_prefix|gmail_extra)\\b/i\nconst GMAIL_QUOTE_RE = /<blockquote\\b[^>]*\\bclass=[\"'][^\"']*\\bgmail_quote\\b/i\nconst ON_WROTE_RE = /^On\\s.+wrote:\\s*$/i\nconst SIGNOFF_RE = /^(?:thanks,|thank you,|best,|regards,|sincerely,)$/i\nconst DETAILS_START_RE = /^(?:confidentiality notice\\b|this message and any attachments\\b|this email and any attachments\\b|the information contained in this message\\b|this communication may contain\\b|unsubscribe\\b|manage your preferences\\b)/i\nconst CONTACT_DETAIL_RE = /(?:@|https?:\\/\\/|www\\.|\\+?\\d[\\d\\s().-]{6,}\\d|\\b(?:ceo|cfo|cto|coo|founder|co-founder|director|manager|vp|vice president|president|head of|sales|marketing|operations|account|customer success|success|support|engineer|consultant|partner|principal|advisor|associate)\\b|\\b(?:inc|llc|ltd|corp|corporation|company|co\\.)\\b)/i\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 nbsp: \" \",\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) => safeCodePoint(Number.parseInt(hex, 16)))\n .replace(/&#(\\d+);?/g, (_match, decimal: string) => safeCodePoint(Number.parseInt(decimal, 10)))\n .replace(/&([a-z][a-z0-9]+);?/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 decodeJsonEscapes(value: string): string {\n return value\n .replace(/\\\\u\\{([0-9a-f]{1,6})\\}/gi, (_match, hex: string) => safeCodePoint(Number.parseInt(hex, 16)))\n .replace(/\\\\u([0-9a-f]{4})/gi, (_match, hex: string) => safeCodePoint(Number.parseInt(hex, 16)))\n .replace(/\\\\r\\\\n|\\\\n|\\\\r/g, \"\\n\")\n .replace(/\\\\t/g, \"\\t\")\n .replace(/\\\\([\"'\\\\/])/g, \"$1\")\n}\n\nfunction maybeParseJsonString(value: string): string {\n const trimmed = value.trim()\n if (!trimmed.startsWith('\"') || !trimmed.endsWith('\"')) return value\n if (!/[\\\\&]/.test(trimmed)) return value\n\n try {\n const parsed = JSON.parse(trimmed) as unknown\n return typeof parsed === \"string\" ? parsed : value\n } catch {\n return value\n }\n}\n\n/**\n * Decodes display-only email text so UI labels, body fallbacks, collapsed rows,\n * and snippets do not show HTML entities or JSON escape artifacts. It does not\n * sanitize HTML; sanitize at the HTML render boundary before using markup.\n */\nexport function decodeEmailDisplayText(value: string): string {\n let decoded = maybeParseJsonString(value).replace(/\\r\\n?/g, \"\\n\")\n\n for (let i = 0; i < 4; i += 1) {\n const next = decodeHtmlEntities(decodeJsonEscapes(decoded))\n if (next === decoded) break\n decoded = next\n }\n\n return decoded.replace(/\\u00a0/g, \" \")\n}\n\nfunction stripWrappingQuotes(value: string): string {\n const trimmed = value.trim()\n if ((trimmed.startsWith('\"') && trimmed.endsWith('\"')) || (trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\"))) {\n return trimmed.slice(1, -1).trim()\n }\n return trimmed\n}\n\nfunction extractEmail(value: string): string | null {\n const decoded = decodeEmailDisplayText(value)\n const angleMatch = decoded.match(ANGLE_ADDRESS_RE)\n const email = angleMatch?.[2] ?? decoded.match(EMAIL_RE)?.[0]\n return email ? email.trim() : null\n}\n\nfunction extractNameFromAddress(value: string): string {\n const decoded = decodeEmailDisplayText(value)\n const angleMatch = decoded.match(ANGLE_ADDRESS_RE)\n if (angleMatch) return stripWrappingQuotes(angleMatch[1] ?? \"\")\n return stripWrappingQuotes(decoded.replace(EMAIL_RE, \"\").replace(/[<>]/g, \"\").trim())\n}\n\nfunction cleanDisplayName(value: string, email: string | null): string {\n let name = stripWrappingQuotes(decodeEmailDisplayText(value))\n if (email) {\n name = name\n .replace(new RegExp(`<\\\\s*${email.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\s*>`, \"i\"), \"\")\n .replace(new RegExp(`^${email.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}$`, \"i\"), \"\")\n .trim()\n }\n return stripWrappingQuotes(name)\n}\n\nexport function normalizeEmailSender(input: EmailDisplaySenderInput): NormalizedEmailSender {\n const fallbackName = decodeEmailDisplayText(input.fallbackName ?? \"Unknown sender\").trim() || \"Unknown sender\"\n const rawName = input.name ? decodeEmailDisplayText(input.name) : \"\"\n const rawEmail = input.email ? decodeEmailDisplayText(input.email) : \"\"\n const email = extractEmail(rawEmail) ?? extractEmail(rawName)\n\n const nameFromName = rawName ? cleanDisplayName(extractNameFromAddress(rawName) || rawName, email) : \"\"\n const nameFromEmail = rawEmail ? cleanDisplayName(extractNameFromAddress(rawEmail), email) : \"\"\n const name = nameFromName || nameFromEmail || email || fallbackName\n\n return { name, email }\n}\n\nfunction splitAddressList(value: string): string[] {\n const parts: string[] = []\n let current = \"\"\n let quote: '\"' | \"'\" | null = null\n let angleDepth = 0\n\n for (let index = 0; index < value.length; index += 1) {\n const char = value[index]\n\n if (quote) {\n current += char\n if (char === quote && value[index - 1] !== \"\\\\\") quote = null\n continue\n }\n\n if (char === '\"' || char === \"'\") {\n quote = char\n current += char\n continue\n }\n\n if (char === \"<\") angleDepth += 1\n if (char === \">\" && angleDepth > 0) angleDepth -= 1\n\n if (char === \",\" && angleDepth === 0) {\n if (current.trim()) parts.push(current.trim())\n current = \"\"\n continue\n }\n\n current += char\n }\n\n if (current.trim()) parts.push(current.trim())\n return parts\n}\n\nfunction formatSingleAddress(value: string): string {\n const decoded = decodeEmailDisplayText(value).trim()\n if (!decoded) return \"\"\n\n const email = extractEmail(decoded)\n const name = cleanDisplayName(extractNameFromAddress(decoded), email)\n\n if (email && name) return `${name} <${email}>`\n if (email) return email\n return stripWrappingQuotes(decoded)\n}\n\nexport function formatAddressList(input?: string | string[] | null): string {\n if (!input) return \"\"\n\n const rawItems = Array.isArray(input) ? input : splitAddressList(input)\n return rawItems\n .map((item) => formatSingleAddress(item))\n .filter(Boolean)\n .join(\", \")\n}\n\nexport function formatEmailTimestamp(\n value?: string | Date | null,\n opts?: { timeZone?: string },\n): string | null {\n if (!value) return null\n const date = value instanceof Date ? value : new Date(value)\n if (Number.isNaN(date.getTime())) return null\n\n // Default = the runtime's local timezone (the viewer's, in the browser).\n // Callers that render during SSR/hydration pass timeZone \"UTC\" for the\n // first paint so server and client HTML match, then re-render local after\n // mount (see useHydrated in timeline-activity).\n return new Intl.DateTimeFormat(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n ...(opts?.timeZone ? { timeZone: opts.timeZone } : {}),\n }).format(date)\n}\n\ntype MessageSegment = { html?: string; text?: string; visibleText: string }\n\nfunction escapeHtmlText(value: string): string {\n return value.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\")\n}\n\nfunction decodeHtmlTextNodes(html: string): string {\n if (!HTML_ENTITY_RE.test(html) && !/\\\\[nrt\"'\\\\/]|\\\\u/i.test(html)) return html\n\n if (typeof document !== \"undefined\") {\n const template = document.createElement(\"template\")\n template.innerHTML = html\n const walker = document.createTreeWalker(template.content, NodeFilter.SHOW_TEXT)\n const textNodes: Text[] = []\n let node = walker.nextNode()\n while (node) {\n textNodes.push(node as Text)\n node = walker.nextNode()\n }\n textNodes.forEach((textNode) => {\n textNode.nodeValue = decodeEmailDisplayText(textNode.nodeValue ?? \"\")\n })\n return template.innerHTML\n }\n\n return html\n .split(/(<[^>]+>)/g)\n .map((part) => (part.startsWith(\"<\") && part.endsWith(\">\") ? part : escapeHtmlText(decodeEmailDisplayText(part))))\n .join(\"\")\n}\n\nfunction decodeHtmlText(value: string): string {\n const withoutTags = value\n .replace(BR_TAG_RE, \"\\n\")\n .replace(/<\\/(p|div|blockquote|li|tr|table|ul|ol)>/gi, \"\\n\")\n .replace(/<[^>]*>/g, \"\")\n\n return decodeEmailDisplayText(withoutTags)\n}\n\nfunction htmlToVisibleText(html: string): string {\n return decodeHtmlText(html).replace(/\\u00a0/g, \" \").replace(/[ \\t]+/g, \" \").trim()\n}\n\nfunction serializeNode(node: Node): string {\n const host = document.createElement(\"div\")\n host.appendChild(node.cloneNode(true))\n return host.innerHTML\n}\n\nfunction wrapHtmlLike(element: Element, innerHtml: string): string {\n const clone = element.cloneNode(false) as HTMLElement\n clone.innerHTML = innerHtml\n return clone.outerHTML\n}\n\nfunction hasDirectBr(nodes: readonly Node[]): boolean {\n return nodes.some((node) => node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName.toLowerCase() === \"br\")\n}\n\nfunction hasDirectBlockChild(element: Element): boolean {\n return Array.from(element.children).some((child) => {\n const tagName = child.tagName.toLowerCase()\n return tagName !== \"br\" && BLOCK_TAGS.has(tagName)\n })\n}\n\nfunction makeHtmlSegment(html: string): MessageSegment | null {\n const visibleText = htmlToVisibleText(html)\n if (!html.trim() || (!visibleText && !/<(?:img|hr)\\b/i.test(html))) return null\n return { html, visibleText }\n}\n\n// Blank-line segments carry no visible text, so they never participate in\n// footer-boundary detection — but their html must survive the rebuild, or a\n// split body loses every intentional blank line between paragraphs (the\n// Gmail wire format marks them as <div><br></div>).\nfunction makeBlankLineSegment(html: string): MessageSegment {\n return { html, visibleText: \"\" }\n}\n\nfunction isBlankLineHtml(html: string): boolean {\n return Boolean(html.trim()) && !htmlToVisibleText(html) && !/<(?:img|hr)\\b/i.test(html)\n}\n\nfunction splitInlineNodes(nodes: readonly Node[], wrapper?: Element): MessageSegment[] {\n const containsBr = hasDirectBr(nodes)\n const chunks: string[][] = [[]]\n\n nodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE && (node as Element).tagName.toLowerCase() === \"br\") {\n chunks.push([])\n return\n }\n\n chunks[chunks.length - 1]?.push(serializeNode(node))\n })\n\n const rendered = chunks.map((chunk) => chunk.join(\"\"))\n const segments: MessageSegment[] = []\n rendered.forEach((innerHtml, index) => {\n const html = wrapper ? wrapHtmlLike(wrapper, innerHtml) : containsBr ? `<div>${innerHtml}</div>` : innerHtml\n const segment = makeHtmlSegment(html)\n if (segment) {\n segments.push(segment)\n return\n }\n // An interior empty chunk sits between two <br>s: an intentional blank\n // line. Leading/trailing empties stay dropped, as before.\n if (containsBr && index > 0 && index < rendered.length - 1) {\n segments.push(makeBlankLineSegment(wrapper ? wrapHtmlLike(wrapper, \"<br>\") : \"<div><br></div>\"))\n }\n })\n return segments\n}\n\nfunction splitElementSegment(element: Element): MessageSegment[] {\n const tagName = element.tagName.toLowerCase()\n\n if (GMAIL_SIGNATURE_RE.test(element.outerHTML) || GMAIL_QUOTE_RE.test(element.outerHTML)) {\n const segment = makeHtmlSegment(element.outerHTML)\n return segment ? [segment] : []\n }\n\n // A block with no visible content (e.g. <div><br></div>, <p></p>) is a\n // blank-line marker — keep it whole instead of splitting/dropping it.\n if (isBlankLineHtml(element.outerHTML)) {\n return [makeBlankLineSegment(element.outerHTML)]\n }\n\n if (tagName === \"div\" && hasDirectBlockChild(element)) {\n const childSegments = splitHtmlNodes(Array.from(element.childNodes))\n return childSegments.length ? childSegments : ([makeHtmlSegment(element.outerHTML)].filter(Boolean) as MessageSegment[])\n }\n\n if (SPLITTABLE_BLOCK_TAGS.has(tagName) && hasDirectBr(Array.from(element.childNodes)) && !hasDirectBlockChild(element)) {\n return splitInlineNodes(Array.from(element.childNodes), element)\n }\n\n const segment = makeHtmlSegment(element.outerHTML)\n return segment ? [segment] : []\n}\n\nfunction splitHtmlNodes(nodes: readonly Node[]): MessageSegment[] {\n const segments: MessageSegment[] = []\n let inlineNodes: Node[] = []\n\n const flushInline = () => {\n if (!inlineNodes.length) return\n segments.push(...splitInlineNodes(inlineNodes))\n inlineNodes = []\n }\n\n nodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element\n const tagName = element.tagName.toLowerCase()\n if (BLOCK_TAGS.has(tagName)) {\n flushInline()\n segments.push(...splitElementSegment(element))\n return\n }\n }\n\n inlineNodes.push(node)\n })\n\n flushInline()\n return segments\n}\n\nfunction findMatchingCloseTag(html: string, tagName: string, openTagEnd: number): number {\n if (tagName === \"hr\") return openTagEnd + 1\n\n const tagPattern = new RegExp(`</?${tagName}\\\\b[^>]*>`, \"gi\")\n tagPattern.lastIndex = openTagEnd + 1\n let depth = 1\n let match: RegExpExecArray | null\n\n while ((match = tagPattern.exec(html)) !== null) {\n const rawTag = match[0]\n if (/^<\\//.test(rawTag)) depth -= 1\n else if (!/\\/\\s*>$/.test(rawTag)) depth += 1\n if (depth === 0) return tagPattern.lastIndex\n }\n\n return html.length\n}\n\nfunction splitHtmlSegmentsFallback(html: string): MessageSegment[] {\n const segments: MessageSegment[] = []\n let cursor = 0\n\n const pushInline = (inlineHtml: string) => {\n const chunks = inlineHtml.split(BR_TAG_RE)\n const hadBr = chunks.length > 1\n chunks.forEach((chunk, index) => {\n const segment = makeHtmlSegment(hadBr ? `<div>${chunk}</div>` : chunk)\n if (segment) {\n segments.push(segment)\n return\n }\n if (hadBr && index > 0 && index < chunks.length - 1) {\n segments.push(makeBlankLineSegment(\"<div><br /></div>\"))\n }\n })\n }\n\n while (cursor < html.length) {\n const rest = html.slice(cursor)\n const match = HTML_BLOCK_START_RE.exec(rest)\n if (!match || match.index === undefined) {\n pushInline(rest)\n break\n }\n\n if (match.index > 0) pushInline(rest.slice(0, match.index))\n\n const tagStart = cursor + match.index\n const rawOpen = match[0]\n const tagName = match[1].toLowerCase()\n const openTagEnd = tagStart + rawOpen.length - 1\n const segmentEnd = findMatchingCloseTag(html, tagName, openTagEnd)\n const blockHtml = html.slice(tagStart, segmentEnd)\n\n if (isBlankLineHtml(blockHtml)) {\n segments.push(makeBlankLineSegment(blockHtml))\n } else if (SPLITTABLE_BLOCK_TAGS.has(tagName) && BR_TAG_RE.test(blockHtml)) {\n const openTag = rawOpen\n const closeTag = `</${tagName}>`\n const inner = blockHtml.replace(new RegExp(`^${rawOpen.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}`, \"i\"), \"\").replace(new RegExp(`${closeTag}$`, \"i\"), \"\")\n const innerChunks = inner.split(BR_TAG_RE)\n innerChunks.forEach((chunk, index) => {\n const segment = makeHtmlSegment(`${openTag}${chunk}${closeTag}`)\n if (segment) {\n segments.push(segment)\n return\n }\n if (index > 0 && index < innerChunks.length - 1) {\n segments.push(makeBlankLineSegment(`${openTag}<br />${closeTag}`))\n }\n })\n } else {\n const segment = makeHtmlSegment(blockHtml)\n if (segment) segments.push(segment)\n }\n\n cursor = segmentEnd\n }\n\n return segments\n}\n\nfunction segmentHtmlMessage(html: string): MessageSegment[] {\n if (typeof document === \"undefined\" || typeof Node === \"undefined\") {\n return splitHtmlSegmentsFallback(html)\n }\n\n const template = document.createElement(\"template\")\n template.innerHTML = html\n return splitHtmlNodes(Array.from(template.content.childNodes))\n}\n\nfunction segmentTextMessage(text: string): MessageSegment[] {\n const normalized = decodeEmailDisplayText(text)\n const lines: string[] = normalized.match(/[^\\n]*(?:\\n|$)/g) ?? []\n return lines\n .filter((line, index) => line.length > 0 && !(index === lines.length - 1 && line === \"\"))\n .map((line) => ({ text: line, visibleText: line.replace(/\\u00a0/g, \" \").trim() }))\n}\n\nfunction firstVisibleLine(text: string): string {\n return text.replace(/\\u00a0/g, \" \").trimStart().split(/\\r?\\n/).find((line) => line.trim())?.trim() ?? \"\"\n}\n\nfunction isLikelySenderNameLine(line: string): boolean {\n if (!line || line.length > 60) return false\n if (/[,@:;!?]|https?:\\/\\/|www\\.|\\d/.test(line)) return false\n\n const words = line.split(/\\s+/).filter(Boolean)\n if (words.length < 1 || words.length > 4) return false\n\n return words.every((word) => /^[A-Z][A-Za-z'.-]*$/.test(word))\n}\n\nfunction nextVisibleSegmentText(segments: MessageSegment[], fromIndex: number): string {\n for (let index = fromIndex + 1; index < segments.length; index += 1) {\n const text = firstVisibleLine(segments[index]?.visibleText ?? \"\")\n if (text) return text\n }\n return \"\"\n}\n\nfunction isGmailDetailsSegment(segment: MessageSegment): boolean {\n return Boolean(segment.html && (GMAIL_SIGNATURE_RE.test(segment.html) || GMAIL_QUOTE_RE.test(segment.html)))\n}\n\nfunction isFooterBoundary(segments: MessageSegment[], index: number): boolean {\n const segment = segments[index]\n if (!segment) return false\n if (isGmailDetailsSegment(segment)) return true\n\n const line = firstVisibleLine(segment.visibleText)\n if (!line) return false\n if (SIGNATURE_DELIMITER_RE.test(line) || DETAILS_START_RE.test(line) || ON_WROTE_RE.test(line)) return true\n\n const nextText = nextVisibleSegmentText(segments, index)\n if (SIGNOFF_RE.test(line)) return Boolean(nextText && (isLikelySenderNameLine(nextText) || CONTACT_DETAIL_RE.test(nextText)))\n\n return isLikelySenderNameLine(line) && CONTACT_DETAIL_RE.test(nextText)\n}\n\nfunction splitFooterSegments(segments: MessageSegment[]): { bodySegments: MessageSegment[]; detailsSegments: MessageSegment[] } {\n const visibleIndexes = segments\n .map((segment, index) => (segment.visibleText || isGmailDetailsSegment(segment) ? index : -1))\n .filter((index) => index >= 0)\n const visibleCount = visibleIndexes.length\n if (visibleCount < 2) return { bodySegments: segments, detailsSegments: [] }\n\n const trailingCount = Math.max(Math.ceil(visibleCount * 0.4), 8)\n const firstTrailingOrdinal = Math.max(1, visibleCount - trailingCount)\n\n for (let ordinal = firstTrailingOrdinal; ordinal < visibleCount; ordinal += 1) {\n const index = visibleIndexes[ordinal]\n if (ordinal > 0 && isFooterBoundary(segments, index)) {\n return { bodySegments: segments.slice(0, index), detailsSegments: segments.slice(index) }\n }\n }\n\n return { bodySegments: segments, detailsSegments: [] }\n}\n\nexport function splitEmailHtmlForDisplay(html: string): SplitEmailHtmlResult {\n const sanitizedHtml = sanitizeHtml(decodeHtmlTextNodes(html))\n const { bodySegments, detailsSegments } = splitFooterSegments(segmentHtmlMessage(sanitizedHtml))\n\n if (!detailsSegments.length) return { bodyHtml: sanitizedHtml, detailsHtml: \"\" }\n\n return {\n bodyHtml: bodySegments.map((segment) => segment.html ?? \"\").join(\"\"),\n detailsHtml: detailsSegments.map((segment) => segment.html ?? \"\").join(\"\"),\n }\n}\n\nexport function splitEmailTextForDisplay(text: string): SplitEmailTextResult {\n const decodedText = decodeEmailDisplayText(text)\n const { bodySegments, detailsSegments } = splitFooterSegments(segmentTextMessage(decodedText))\n\n if (!detailsSegments.length) return { bodyText: decodedText, detailsText: \"\" }\n\n return {\n bodyText: bodySegments.map((segment) => segment.text ?? \"\").join(\"\"),\n detailsText: detailsSegments.map((segment) => segment.text ?? \"\").join(\"\"),\n }\n}\n\nexport function emailBodySnippet(input: { bodyHtml?: string | null; body?: string | null }, maxLength = 140): string {\n const html = input.bodyHtml?.trim()\n if (html) {\n return decodeEmailDisplayText(htmlToTextSnippet(splitEmailHtmlForDisplay(html).bodyHtml, maxLength)).trim()\n }\n\n const text = input.body ?? \"\"\n const bodyText = splitEmailTextForDisplay(text).bodyText\n const firstLine = bodyText.split(\"\\n\").find((line) => line.trim())?.trim() ?? \"\"\n return decodeEmailDisplayText(firstLine).replace(/\\s+/g, \" \").trim().slice(0, maxLength)\n}\n"],"mappings":"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","names":["segment"]}
|
|
@@ -199,10 +199,20 @@ function cleanGongText(value) {
|
|
|
199
199
|
if (!value) return "";
|
|
200
200
|
return decodeEmailDisplayText(value).trim();
|
|
201
201
|
}
|
|
202
|
-
function
|
|
202
|
+
function useHydrated() {
|
|
203
|
+
const [hydrated, setHydrated] = React.useState(false);
|
|
204
|
+
React.useEffect(() => {
|
|
205
|
+
setHydrated(true);
|
|
206
|
+
}, []);
|
|
207
|
+
return hydrated;
|
|
208
|
+
}
|
|
209
|
+
function timestampTimeZone(opts) {
|
|
210
|
+
return (opts == null ? void 0 : opts.utcTimestamps) ? { timeZone: "UTC" } : {};
|
|
211
|
+
}
|
|
212
|
+
function getTimelineGongCallDisplay(gongCall, opts) {
|
|
203
213
|
var _a, _b, _c, _d;
|
|
204
214
|
const title = cleanGongText(gongCall.title);
|
|
205
|
-
const startTime = (_a = formatEmailTimestamp(gongCall.startTime)) != null ? _a : "";
|
|
215
|
+
const startTime = (_a = formatEmailTimestamp(gongCall.startTime, timestampTimeZone(opts))) != null ? _a : "";
|
|
206
216
|
const duration = formatCallDuration(gongCall.durationSeconds);
|
|
207
217
|
const direction = cleanGongText(gongCall.direction);
|
|
208
218
|
const outcome = cleanGongText(gongCall.outcome);
|
|
@@ -213,13 +223,13 @@ function getTimelineGongCallDisplay(gongCall) {
|
|
|
213
223
|
const snippet = (_d = (_c = brief.split("\n").find((line) => line.trim())) == null ? void 0 : _c.trim()) != null ? _d : "";
|
|
214
224
|
return { title, startTime, duration, direction, outcome, brief, keyPoints, nextSteps, url, snippet };
|
|
215
225
|
}
|
|
216
|
-
function getTimelineEmailDisplay(email) {
|
|
226
|
+
function getTimelineEmailDisplay(email, opts) {
|
|
217
227
|
var _a, _b, _c, _d, _e;
|
|
218
228
|
const sender = normalizeEmailSender({ name: email.from, email: email.fromEmail });
|
|
219
229
|
const to = formatAddressList(email.to) || decodeEmailDisplayText((_a = email.to) != null ? _a : "");
|
|
220
230
|
const cc = formatAddressList(email.cc) || decodeEmailDisplayText((_b = email.cc) != null ? _b : "");
|
|
221
231
|
const bcc = formatAddressList(email.bcc) || decodeEmailDisplayText((_c = email.bcc) != null ? _c : "");
|
|
222
|
-
const date = (_e = formatEmailTimestamp(email.date)) != null ? _e : decodeEmailDisplayText((_d = email.date) != null ? _d : "");
|
|
232
|
+
const date = (_e = formatEmailTimestamp(email.date, timestampTimeZone(opts))) != null ? _e : decodeEmailDisplayText((_d = email.date) != null ? _d : "");
|
|
223
233
|
const subject = email.subject ? decodeEmailDisplayText(email.subject) : "";
|
|
224
234
|
const bodyText = reactNodeToDisplayText(email.body);
|
|
225
235
|
const snippet = emailBodySnippet({ bodyHtml: email.bodyHtml, body: bodyText }, 140);
|
|
@@ -231,7 +241,8 @@ function EmailMetadata({
|
|
|
231
241
|
showAllRecipients,
|
|
232
242
|
setShowAllRecipients
|
|
233
243
|
}) {
|
|
234
|
-
const
|
|
244
|
+
const hydrated = useHydrated();
|
|
245
|
+
const display = getTimelineEmailDisplay(email, { utcTimestamps: !hydrated });
|
|
235
246
|
const hasExpandableRecipients = Boolean(display.cc || display.bcc);
|
|
236
247
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
237
248
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
@@ -542,7 +553,8 @@ function GongCallCard({
|
|
|
542
553
|
classes
|
|
543
554
|
}) {
|
|
544
555
|
const gongCall = event.gongCall;
|
|
545
|
-
const
|
|
556
|
+
const hydrated = useHydrated();
|
|
557
|
+
const display = getTimelineGongCallDisplay(gongCall, { utcTimestamps: !hydrated });
|
|
546
558
|
if (variant === "default") {
|
|
547
559
|
return /* @__PURE__ */ jsx("div", { className: classes.cardContainer, "data-variant": variant, "data-slot": "timeline-gong-card", children: /* @__PURE__ */ jsx(
|
|
548
560
|
"div",
|