markform 0.1.21 → 0.1.23
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/README.md +66 -18
- package/dist/ai-sdk.d.mts +1 -1
- package/dist/ai-sdk.mjs +1 -1
- package/dist/{apply-CD-t7ovb.mjs → apply-KzQztrDV.mjs} +100 -74
- package/dist/apply-KzQztrDV.mjs.map +1 -0
- package/dist/bin.mjs +1 -1
- package/dist/{cli-ChdIy1a7.mjs → cli-ZcOC47KK.mjs} +24 -1213
- package/dist/cli-ZcOC47KK.mjs.map +1 -0
- package/dist/cli.mjs +1 -1
- package/dist/{coreTypes-BQrWf_Wt.d.mts → coreTypes-BlsJkU1w.d.mts} +1 -1
- package/dist/fillRecord-DTl5lnK0.d.mts +345 -0
- package/dist/fillRecordRenderer-VBQ2vwPV.mjs +1253 -0
- package/dist/fillRecordRenderer-VBQ2vwPV.mjs.map +1 -0
- package/dist/index.d.mts +53 -343
- package/dist/index.mjs +4 -4
- package/dist/render.d.mts +74 -0
- package/dist/render.mjs +4 -0
- package/dist/{session-ZgegwtkT.mjs → session-BCcltrLA.mjs} +1 -1
- package/dist/{session-ZgegwtkT.mjs.map → session-BCcltrLA.mjs.map} +1 -1
- package/dist/{session-BPuQ-ok0.mjs → session-VeSkVrck.mjs} +1 -1
- package/dist/{shared-DwdyWmvE.mjs → shared-CsdT2T7k.mjs} +1 -1
- package/dist/{shared-DwdyWmvE.mjs.map → shared-CsdT2T7k.mjs.map} +1 -1
- package/dist/{shared-BTR35aMz.mjs → shared-fb0nkzQi.mjs} +1 -1
- package/dist/{src-DOPe4tmu.mjs → src-B2uFvGli.mjs} +103 -21
- package/dist/{src-DOPe4tmu.mjs.map → src-B2uFvGli.mjs.map} +1 -1
- package/dist/urlFormat-lls7CsEP.mjs +71 -0
- package/dist/urlFormat-lls7CsEP.mjs.map +1 -0
- package/docs/markform-apis.md +53 -0
- package/examples/simple/simple-skipped-filled.report.md +8 -8
- package/examples/twitter-thread/twitter-thread.form.md +373 -0
- package/package.json +5 -1
- package/dist/apply-CD-t7ovb.mjs.map +0 -1
- package/dist/cli-ChdIy1a7.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fillRecordRenderer-VBQ2vwPV.mjs","names":[],"sources":["../src/render/renderUtils.ts","../src/render/contentRenderers.ts","../src/render/fillRecordRenderer.ts"],"sourcesContent":["/**\n * Rendering utility functions for HTML generation.\n *\n * Pure utility functions used across all renderers. No CLI or server dependencies.\n */\n\n/**\n * Escape HTML special characters.\n */\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Format milliseconds as human-readable duration.\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${ms.toFixed(0)}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n const minutes = Math.floor(ms / 60000);\n const seconds = ((ms % 60000) / 1000).toFixed(0);\n return `${minutes}m ${seconds}s`;\n}\n\n/**\n * Format token count with K suffix for large numbers.\n */\nexport function formatTokens(count: number): string {\n if (count >= 10000) return `${(count / 1000).toFixed(1)}k`;\n if (count >= 1000) return `${(count / 1000).toFixed(1)}k`;\n return count.toLocaleString();\n}\n","/**\n * Content-only HTML renderers for form views and syntax-highlighted content.\n *\n * These produce HTML fragments (no page shell) suitable for embedding.\n * No CLI or server dependencies.\n */\n\nimport type { Field, FieldValue, ParsedForm } from '../engine/coreTypes.js';\nimport { friendlyUrlAbbrev, formatBareUrlsAsHtmlLinks } from '../utils/urlFormat.js';\nimport { escapeHtml } from './renderUtils.js';\n\n// =============================================================================\n// View Content Renderer\n// =============================================================================\n\n/**\n * Format a checkbox state for display.\n */\nfunction formatCheckboxState(state: string): string {\n switch (state) {\n case 'done':\n return '<span class=\"checkbox checked\">☑</span>';\n case 'todo':\n return '<span class=\"checkbox unchecked\">☐</span>';\n case 'active':\n return '<span class=\"state-badge state-active\">●</span>';\n case 'incomplete':\n return '<span class=\"state-badge state-incomplete\">○</span>';\n case 'na':\n return '<span class=\"state-badge state-na\">—</span>';\n case 'yes':\n return '<span class=\"checkbox checked\">☑</span>';\n case 'no':\n return '<span class=\"checkbox unchecked\">☐</span>';\n case 'unfilled':\n return '<span class=\"state-badge state-unfilled\">?</span>';\n default:\n return `<span class=\"state-badge\">${escapeHtml(state)}</span>`;\n }\n}\n\n/**\n * Render a field value for the View tab.\n */\nfunction renderViewFieldValue(\n field: Field,\n value: FieldValue | undefined,\n isSkipped: boolean,\n skipReason?: string,\n): string {\n if (isSkipped) {\n const reasonText = skipReason ? `(skipped: ${escapeHtml(skipReason)})` : '(skipped)';\n return `<div class=\"view-field-empty\">${reasonText}</div>`;\n }\n\n if (value === undefined) {\n return '<div class=\"view-field-empty\">(not filled)</div>';\n }\n\n switch (field.kind) {\n case 'string': {\n const v = value.kind === 'string' ? value.value : null;\n if (v === null || v === '') {\n return '<div class=\"view-field-empty\">(not filled)</div>';\n }\n // Auto-link bare URLs in string content for consistency with URL fields\n const formatted = formatBareUrlsAsHtmlLinks(v, escapeHtml);\n return `<div class=\"view-field-value\">${formatted}</div>`;\n }\n case 'number': {\n const v = value.kind === 'number' ? value.value : null;\n if (v === null) {\n return '<div class=\"view-field-empty\">(not filled)</div>';\n }\n return `<div class=\"view-field-value\">${v}</div>`;\n }\n case 'string_list': {\n const items = value.kind === 'string_list' ? value.items : [];\n if (items.length === 0) {\n return '<div class=\"view-field-empty\">(not filled)</div>';\n }\n // Auto-link bare URLs in string list items\n return `<div class=\"view-field-value\"><ul>${items.map((i) => `<li>${formatBareUrlsAsHtmlLinks(i, escapeHtml)}</li>`).join('')}</ul></div>`;\n }\n case 'single_select': {\n const selected = value.kind === 'single_select' ? value.selected : null;\n if (selected === null) {\n return '<div class=\"view-field-empty\">(not filled)</div>';\n }\n const opt = field.options.find((o) => o.id === selected);\n return `<div class=\"view-field-value\">${escapeHtml(opt?.label ?? selected)}</div>`;\n }\n case 'multi_select': {\n const selected = value.kind === 'multi_select' ? value.selected : [];\n // Show all options with selection state\n const items = field.options.map((opt) => {\n const isSelected = selected.includes(opt.id);\n const checkbox = isSelected\n ? '<span class=\"checkbox checked\">☑</span>'\n : '<span class=\"checkbox unchecked\">☐</span>';\n return `<li class=\"checkbox-item\">${checkbox} ${escapeHtml(opt.label)}</li>`;\n });\n return `<div class=\"view-field-value\"><ul class=\"checkbox-list\">${items.join('')}</ul></div>`;\n }\n case 'checkboxes': {\n const values = value.kind === 'checkboxes' ? value.values : {};\n const mode = field.checkboxMode ?? 'multi';\n // Show all options with their state\n const items = field.options.map((opt) => {\n const state = values[opt.id] ?? (mode === 'explicit' ? 'unfilled' : 'todo');\n // For simple mode, use checkbox symbols\n if (mode === 'simple') {\n const checkbox =\n state === 'done'\n ? '<span class=\"checkbox checked\">☑</span>'\n : '<span class=\"checkbox unchecked\">☐</span>';\n return `<li class=\"checkbox-item\">${checkbox} ${escapeHtml(opt.label)}</li>`;\n }\n // For multi/explicit modes, show state text since there are multiple states\n const stateDisplay = formatCheckboxState(state);\n return `<li class=\"checkbox-item\">${stateDisplay} ${escapeHtml(opt.label)}</li>`;\n });\n return `<div class=\"view-field-value\"><ul class=\"checkbox-list\">${items.join('')}</ul></div>`;\n }\n case 'url': {\n const v = value.kind === 'url' ? value.value : null;\n if (v === null || v === '') {\n return '<div class=\"view-field-empty\">(not filled)</div>';\n }\n const domain = friendlyUrlAbbrev(v);\n return `<div class=\"view-field-value\"><a href=\"${escapeHtml(v)}\" target=\"_blank\" class=\"url-link\" data-url=\"${escapeHtml(v)}\">${escapeHtml(domain)}</a></div>`;\n }\n case 'url_list': {\n const items = value.kind === 'url_list' ? value.items : [];\n if (items.length === 0) {\n return '<div class=\"view-field-empty\">(not filled)</div>';\n }\n return `<div class=\"view-field-value\"><ul>${items\n .map((u) => {\n const domain = friendlyUrlAbbrev(u);\n return `<li><a href=\"${escapeHtml(u)}\" target=\"_blank\" class=\"url-link\" data-url=\"${escapeHtml(u)}\">${escapeHtml(domain)}</a></li>`;\n })\n .join('')}</ul></div>`;\n }\n case 'date': {\n const v = value.kind === 'date' ? value.value : null;\n if (v === null || v === '') {\n return '<div class=\"view-field-empty\">(not filled)</div>';\n }\n return `<div class=\"view-field-value\">${escapeHtml(v)}</div>`;\n }\n case 'year': {\n const v = value.kind === 'year' ? value.value : null;\n if (v === null) {\n return '<div class=\"view-field-empty\">(not filled)</div>';\n }\n return `<div class=\"view-field-value\">${v}</div>`;\n }\n case 'table': {\n const rows = value.kind === 'table' ? value.rows : [];\n if (rows.length === 0) {\n return '<div class=\"view-field-empty\">(no data)</div>';\n }\n let tableHtml = '<div class=\"table-container\"><table class=\"data-table\">';\n tableHtml += '<thead><tr>';\n for (const col of field.columns) {\n tableHtml += `<th>${escapeHtml(col.label)}</th>`;\n }\n tableHtml += '</tr></thead><tbody>';\n for (const row of rows) {\n tableHtml += '<tr>';\n for (const col of field.columns) {\n const cell = row[col.id];\n let cellValue = '';\n let cellHtml = '';\n if (cell?.state === 'answered' && cell.value !== undefined && cell.value !== null) {\n cellValue = String(cell.value);\n // Format URL columns as domain links\n if (col.type === 'url' && cellValue) {\n const domain = friendlyUrlAbbrev(cellValue);\n cellHtml = `<a href=\"${escapeHtml(cellValue)}\" target=\"_blank\" class=\"url-link\" data-url=\"${escapeHtml(cellValue)}\">${escapeHtml(domain)}</a>`;\n } else {\n // Auto-link bare URLs in non-URL columns for consistency\n cellHtml = formatBareUrlsAsHtmlLinks(cellValue, escapeHtml);\n }\n }\n tableHtml += `<td>${cellHtml}</td>`;\n }\n tableHtml += '</tr>';\n }\n tableHtml += '</tbody></table></div>';\n return tableHtml;\n }\n default: {\n const _exhaustive: never = field;\n throw new Error(`Unhandled field kind: ${(_exhaustive as { kind: string }).kind}`);\n }\n }\n}\n\n/**\n * Render form view content (read-only display of form fields).\n * Used for View tab content.\n */\nexport function renderViewContent(form: ParsedForm): string {\n const { schema, responsesByFieldId } = form;\n let html = '<div class=\"view-content\">';\n\n for (const group of schema.groups) {\n const groupTitle = group.title ?? group.id;\n html += `<div class=\"view-group\"><h2>${escapeHtml(groupTitle)}</h2>`;\n\n for (const field of group.children) {\n const response = responsesByFieldId[field.id];\n const value = response?.state === 'answered' ? response.value : undefined;\n const isSkipped = response?.state === 'skipped';\n const skipReason = isSkipped ? response?.reason : undefined;\n\n html += '<div class=\"view-field\">';\n html += `<div class=\"view-field-label\">${escapeHtml(field.label)}`;\n html += ` <span class=\"type-badge\">${field.kind}</span>`;\n if (field.required) {\n html += ' <span class=\"required\">*</span>';\n }\n if (isSkipped) {\n html += ` <span class=\"skipped-badge\">Skipped</span>`;\n }\n html += '</div>';\n\n // Render value based on field type\n html += renderViewFieldValue(field, value, isSkipped, skipReason);\n html += '</div>';\n }\n\n html += '</div>';\n }\n\n html += '</div>';\n return html;\n}\n\n// =============================================================================\n// Source Content Renderer\n// =============================================================================\n\n/**\n * Highlight a single line of source code (Markdown + Jinja).\n */\nfunction highlightSourceLine(line: string): string {\n // First escape HTML\n let result = escapeHtml(line);\n\n // Highlight Jinja tags: {% tag %}, {% /tag %}, {# comment #}\n // Match {% ... %} patterns\n result = result.replace(\n /(\\{%\\s*)([a-zA-Z_/]+)(\\s+[^%]*)?(%\\})/g,\n (_: string, open: string, keyword: string, attrs: string | undefined, close: string) => {\n let attrHtml = '';\n if (attrs) {\n // Highlight attributes within the tag\n attrHtml = attrs.replace(\n /([a-zA-Z_]+)(=)(\"[^\"]*\"|'[^&#]*'|[^\\s%]+)?/g,\n (_m: string, attrName: string, eq: string, attrValue: string) => {\n const valueHtml = attrValue ? `<span class=\"syn-jinja-value\">${attrValue}</span>` : '';\n return `<span class=\"syn-jinja-attr\">${attrName}</span>${eq}${valueHtml}`;\n },\n );\n }\n return `<span class=\"syn-jinja-tag\">${open}</span><span class=\"syn-jinja-keyword\">${keyword}</span>${attrHtml}<span class=\"syn-jinja-tag\">${close}</span>`;\n },\n );\n\n // Highlight Jinja comments: {# ... #}\n result = result.replace(/(\\{#)(.*?)(#\\})/g, `<span class=\"syn-comment\">$1$2$3</span>`);\n\n // Highlight Markdown headers\n result = result.replace(/^(#{1,6}\\s.*)$/gm, '<span class=\"syn-md-header\">$1</span>');\n\n // Highlight YAML frontmatter markers\n if (result === '---') {\n result = '<span class=\"syn-comment\">---</span>';\n }\n\n return result;\n}\n\n/**\n * Render source content with Markdown and Jinja syntax highlighting.\n * Used for Source tab content.\n */\nexport function renderSourceContent(content: string): string {\n const lines = content.split('\\n');\n const highlighted = lines.map((line) => highlightSourceLine(line)).join('\\n');\n return `<pre>${highlighted}</pre>`;\n}\n\n// =============================================================================\n// Markdown Content Renderer\n// =============================================================================\n\n/**\n * Format inline markdown (bold, italic, code, links, checkboxes).\n * Also auto-links bare URLs for consistency.\n */\nfunction formatInlineMarkdown(text: string): string {\n let result = escapeHtml(text);\n // Checkboxes - render before other formatting to avoid conflicts\n // Checked checkbox [x] or [X]\n result = result.replace(/\\[x\\]/gi, '<span class=\"checkbox checked\">☑</span>');\n // Unchecked checkbox [ ]\n result = result.replace(/\\[ \\]/g, '<span class=\"checkbox unchecked\">☐</span>');\n // Inline code\n result = result.replace(/`([^`]+)`/g, '<code>$1</code>');\n // Bold\n result = result.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>');\n // Italic\n result = result.replace(/\\*([^*]+)\\*/g, '<em>$1</em>');\n // Links - need to unescape the URL first\n // Add url-link class and data-url for copy tooltip support\n result = result.replace(\n /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n (_: string, linkText: string, url: string) => {\n const cleanUrl = url.replace(/&/g, '&');\n return `<a href=\"${cleanUrl}\" target=\"_blank\" class=\"url-link\" data-url=\"${cleanUrl}\">${linkText}</a>`;\n },\n );\n // Auto-link bare URLs (not already in <a> tags or markdown links)\n // Uses negative lookbehind to skip URLs that are:\n // - Inside href=\"\" or data-url=\"\" attributes\n // - Inside anchor tag content (preceded by \">)\n // - Part of markdown link syntax ](\n result = result.replace(\n /(?<!href=\"|data-url=\"|\">|\\]\\()(?:https?:\\/\\/|www\\.)[^\\s<>\"]+(?<![.,;:!?'\")])/g,\n (url: string) => {\n // Unescape & back to & for the URL\n const cleanUrl = url.replace(/&/g, '&');\n const fullUrl = cleanUrl.startsWith('www.') ? `https://${cleanUrl}` : cleanUrl;\n const display = friendlyUrlAbbrev(fullUrl);\n return `<a href=\"${escapeHtml(fullUrl)}\" target=\"_blank\" class=\"url-link\" data-url=\"${escapeHtml(fullUrl)}\">${escapeHtml(display)}</a>`;\n },\n );\n return result;\n}\n\n/**\n * Render markdown content (content only, no page wrapper).\n * Used for tab content.\n */\nexport function renderMarkdownContent(content: string): string {\n const lines = content.split('\\n');\n let html = '<div class=\"markdown-content\">';\n let inParagraph = false;\n let inCodeBlock = false;\n let codeBlockContent = '';\n let inUnorderedList = false;\n let inOrderedList = false;\n let inTable = false;\n let tableHeaderDone = false;\n\n // Helper to close any open list\n const closeList = () => {\n if (inUnorderedList) {\n html += '</ul>';\n inUnorderedList = false;\n }\n if (inOrderedList) {\n html += '</ol>';\n inOrderedList = false;\n }\n };\n\n // Helper to close table\n const closeTable = () => {\n if (inTable) {\n html += '</tbody></table></div>';\n inTable = false;\n tableHeaderDone = false;\n }\n };\n\n // Helper to detect if a line is a table row\n const isTableRow = (line: string): boolean => {\n const trimmed = line.trim();\n return trimmed.startsWith('|') && trimmed.endsWith('|') && trimmed.includes('|');\n };\n\n // Helper to detect table separator line (| --- | --- |)\n const isTableSeparator = (line: string): boolean => {\n const trimmed = line.trim();\n return /^\\|[\\s-:|]+\\|$/.test(trimmed);\n };\n\n // Helper to parse table cells\n const parseTableCells = (line: string): string[] => {\n const trimmed = line.trim();\n // Remove leading and trailing pipes, then split by pipes\n const cellContent = trimmed.slice(1, -1);\n return cellContent.split('|').map((cell) => cell.trim());\n };\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Handle fenced code blocks\n if (trimmed.startsWith('```')) {\n if (inCodeBlock) {\n // End code block\n html += `<pre><code>${escapeHtml(codeBlockContent.trim())}</code></pre>`;\n codeBlockContent = '';\n inCodeBlock = false;\n } else {\n // Start code block\n if (inParagraph) {\n html += '</p>';\n inParagraph = false;\n }\n closeList();\n closeTable();\n inCodeBlock = true;\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent += line + '\\n';\n continue;\n }\n\n // Handle table rows\n if (isTableRow(trimmed)) {\n if (inParagraph) {\n html += '</p>';\n inParagraph = false;\n }\n closeList();\n\n // Skip separator line but mark header as done\n if (isTableSeparator(trimmed)) {\n tableHeaderDone = true;\n continue;\n }\n\n const cells = parseTableCells(trimmed);\n\n if (!inTable) {\n // Start new table with header\n html += '<div class=\"table-container\"><table class=\"data-table\"><thead><tr>';\n for (const cell of cells) {\n html += `<th>${formatInlineMarkdown(cell)}</th>`;\n }\n html += '</tr></thead><tbody>';\n inTable = true;\n } else if (tableHeaderDone) {\n // Regular table row\n html += '<tr>';\n for (const cell of cells) {\n html += `<td>${formatInlineMarkdown(cell)}</td>`;\n }\n html += '</tr>';\n }\n continue;\n }\n\n // Close table if we hit a non-table line\n if (inTable && !isTableRow(trimmed)) {\n closeTable();\n }\n\n // Handle headers\n if (trimmed.startsWith('# ')) {\n if (inParagraph) {\n html += '</p>';\n inParagraph = false;\n }\n closeList();\n html += `<h2>${formatInlineMarkdown(trimmed.slice(2))}</h2>`;\n } else if (trimmed.startsWith('## ')) {\n if (inParagraph) {\n html += '</p>';\n inParagraph = false;\n }\n closeList();\n html += `<h3>${formatInlineMarkdown(trimmed.slice(3))}</h3>`;\n } else if (trimmed.startsWith('### ')) {\n if (inParagraph) {\n html += '</p>';\n inParagraph = false;\n }\n closeList();\n html += `<h4>${formatInlineMarkdown(trimmed.slice(4))}</h4>`;\n } else if (trimmed.startsWith('#### ')) {\n if (inParagraph) {\n html += '</p>';\n inParagraph = false;\n }\n closeList();\n html += `<h5>${formatInlineMarkdown(trimmed.slice(5))}</h5>`;\n } else if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) {\n // Unordered list item\n if (inParagraph) {\n html += '</p>';\n inParagraph = false;\n }\n if (inOrderedList) {\n html += '</ol>';\n inOrderedList = false;\n }\n if (!inUnorderedList) {\n html += '<ul>';\n inUnorderedList = true;\n }\n const itemContent = trimmed.slice(2);\n // If item starts with checkbox, use no-bullet class\n const hasCheckbox = /^\\[[ xX]\\]/.test(itemContent);\n const liClass = hasCheckbox ? ' class=\"checkbox-item\"' : '';\n html += `<li${liClass}>${formatInlineMarkdown(itemContent)}</li>`;\n } else if (/^\\d+\\.\\s/.test(trimmed)) {\n // Ordered list item\n if (inParagraph) {\n html += '</p>';\n inParagraph = false;\n }\n if (inUnorderedList) {\n html += '</ul>';\n inUnorderedList = false;\n }\n if (!inOrderedList) {\n html += '<ol>';\n inOrderedList = true;\n }\n const text = trimmed.replace(/^\\d+\\.\\s/, '');\n html += `<li>${formatInlineMarkdown(text)}</li>`;\n } else if (trimmed === '') {\n if (inParagraph) {\n html += '</p>';\n inParagraph = false;\n }\n closeList();\n } else {\n closeList();\n if (!inParagraph) {\n html += '<p>';\n inParagraph = true;\n } else {\n html += '<br>';\n }\n html += formatInlineMarkdown(trimmed);\n }\n }\n\n if (inParagraph) {\n html += '</p>';\n }\n closeList();\n closeTable();\n\n html += '</div>';\n return html;\n}\n\n// =============================================================================\n// Syntax-Highlighted Content Renderers\n// =============================================================================\n\n/**\n * Highlight a YAML value with appropriate syntax class.\n */\nexport function highlightYamlValue(value: string): string {\n const trimmed = value.trim();\n // Booleans\n if (trimmed === 'true' || trimmed === 'false') {\n return `<span class=\"syn-bool\">${escapeHtml(value)}</span>`;\n }\n // Null\n if (trimmed === 'null' || trimmed === '~') {\n return `<span class=\"syn-null\">${escapeHtml(value)}</span>`;\n }\n // Numbers\n if (/^-?\\d+\\.?\\d*$/.test(trimmed)) {\n return `<span class=\"syn-number\">${escapeHtml(value)}</span>`;\n }\n // Quoted strings\n if (\n (trimmed.startsWith('\"') && trimmed.endsWith('\"')) ||\n (trimmed.startsWith(\"'\") && trimmed.endsWith(\"'\"))\n ) {\n return `<span class=\"syn-string\">${escapeHtml(value)}</span>`;\n }\n // Unquoted strings (treat as string)\n return `<span class=\"syn-string\">${escapeHtml(value)}</span>`;\n}\n\n/**\n * Render YAML content with syntax highlighting (content only, no page wrapper).\n * Used for tab content.\n */\nexport function renderYamlContent(content: string): string {\n const highlighted = content\n .split('\\n')\n .map((line) => {\n if (line.trim().startsWith('#')) {\n return `<span class=\"syn-comment\">${escapeHtml(line)}</span>`;\n }\n const colonIndex = line.indexOf(':');\n if (colonIndex > 0 && !line.trim().startsWith('-')) {\n const key = escapeHtml(line.slice(0, colonIndex));\n const afterColon = line.slice(colonIndex + 1).trim();\n const colonAndSpace = escapeHtml(line.slice(colonIndex, colonIndex + 1));\n if (afterColon === '') {\n return `<span class=\"syn-key\">${key}</span>${colonAndSpace}`;\n }\n const valueStart = line.indexOf(afterColon, colonIndex);\n const beforeValue = escapeHtml(line.slice(colonIndex, valueStart));\n const value = highlightYamlValue(afterColon);\n return `<span class=\"syn-key\">${key}</span>${beforeValue}${value}`;\n }\n if (line.trim().startsWith('-')) {\n const dashIndex = line.indexOf('-');\n const beforeDash = escapeHtml(line.slice(0, dashIndex));\n const afterDash = line.slice(dashIndex + 1).trim();\n if (afterDash === '') {\n return `${beforeDash}-`;\n }\n return `${beforeDash}- ${highlightYamlValue(afterDash)}`;\n }\n return escapeHtml(line);\n })\n .join('\\n');\n\n return `<pre>${highlighted}</pre>`;\n}\n\n/**\n * Render JSON content with syntax highlighting (content only, no page wrapper).\n * Used for tab content.\n */\nexport function renderJsonContent(content: string): string {\n let formatted: string;\n try {\n const parsed = JSON.parse(content) as unknown;\n formatted = JSON.stringify(parsed, null, 2);\n } catch {\n formatted = content;\n }\n\n const highlighted = formatted\n .replace(/\"([^\"]+)\":/g, '<span class=\"syn-key\">\"$1\"</span>:')\n .replace(/: \"([^\"]*)\"/g, ': <span class=\"syn-string\">\"$1\"</span>')\n .replace(/: (-?\\d+\\.?\\d*)/g, ': <span class=\"syn-number\">$1</span>')\n .replace(/: (true|false)/g, ': <span class=\"syn-bool\">$1</span>')\n .replace(/: (null)/g, ': <span class=\"syn-null\">$1</span>');\n\n return `<pre>${highlighted}</pre>`;\n}\n","/**\n * Fill record HTML renderer and associated styles/scripts.\n *\n * Renders FillRecord data as an interactive dashboard with Gantt timeline,\n * progress bars, tool summaries, and turn details. No CLI or server dependencies.\n */\n\nimport YAML from 'yaml';\n\nimport type { FillRecord } from '../harness/fillRecord.js';\nimport { escapeHtml, formatDuration, formatTokens } from './renderUtils.js';\nimport { renderYamlContent } from './contentRenderers.js';\n\n// =============================================================================\n// Fill Record Interactive Scripts\n// =============================================================================\n\n/**\n * JavaScript for fill record interactive features.\n * Consumers should include this in a <script> tag on their page.\n * Provides: frShowTip(el), frHideTip(), frCopyYaml(btn)\n */\nexport const FILL_RECORD_SCRIPTS = `\n// Copy YAML content handler for Fill Record tab (must be global for dynamically loaded content)\nfunction frCopyYaml(btn) {\n const pre = btn.parentElement.querySelector('pre');\n navigator.clipboard.writeText(pre.textContent).then(() => {\n const orig = btn.textContent;\n btn.textContent = 'Copied!';\n setTimeout(() => btn.textContent = orig, 1500);\n });\n}\n\n// Tooltip handlers for Fill Record visualizations (must be global for dynamically loaded content)\nfunction frShowTip(el) {\n var tip = document.getElementById('fr-tooltip');\n if (tip && el.dataset.tooltip) {\n tip.textContent = el.dataset.tooltip;\n // Position tooltip centered above the element\n var rect = el.getBoundingClientRect();\n tip.style.left = (rect.left + rect.width / 2) + 'px';\n tip.style.top = (rect.top - 8) + 'px';\n tip.style.transform = 'translate(-50%, -100%)';\n tip.classList.add('visible');\n }\n}\nfunction frHideTip() {\n var tip = document.getElementById('fr-tooltip');\n if (tip) tip.classList.remove('visible');\n}\n`;\n\n// =============================================================================\n// Private Helpers\n// =============================================================================\n\n/**\n * Format a patch value for display.\n * Shows full content - the container has max-height with scroll for long values.\n */\nfunction formatPatchValue(value: unknown): string {\n if (value === null || value === undefined) {\n return '<em class=\"fr-turn__patch-value--clear\">(cleared)</em>';\n }\n if (typeof value === 'string') {\n return escapeHtml(value);\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n // Arrays and objects - show full JSON\n return escapeHtml(JSON.stringify(value, null, 2));\n}\n\n/**\n * Render patches from a fill_form tool call input.\n * Returns HTML for the patch details section.\n */\nfunction renderPatchDetails(input: Record<string, unknown>): string {\n const patches = input.patches;\n if (!Array.isArray(patches) || patches.length === 0) {\n return '';\n }\n\n const patchHtml = patches\n .map((patch: unknown) => {\n if (!patch || typeof patch !== 'object') return '';\n const p = patch as Record<string, unknown>;\n const op = typeof p.op === 'string' ? p.op : 'unknown';\n const fieldId =\n typeof p.fieldId === 'string' ? p.fieldId : typeof p.noteId === 'string' ? p.noteId : '';\n\n // Determine the display based on operation type\n const opLabel = op.replace(/_/g, ' ');\n let valueHtml = '';\n\n if (op === 'skip_field') {\n valueHtml = '<em class=\"fr-turn__patch-value--skip\">(skipped)</em>';\n } else if (op === 'abort_field') {\n valueHtml = '<em class=\"fr-turn__patch-value--skip\">(aborted)</em>';\n } else if (op === 'clear_field') {\n valueHtml = '<em class=\"fr-turn__patch-value--clear\">(cleared)</em>';\n } else if ('value' in p) {\n valueHtml = formatPatchValue(p.value);\n } else if ('values' in p) {\n valueHtml = formatPatchValue(p.values);\n } else if ('rows' in p) {\n valueHtml = formatPatchValue(p.rows);\n }\n\n return `\n <div class=\"fr-turn__patch\">\n <span class=\"fr-turn__patch-field\">${escapeHtml(fieldId)}</span>\n <span class=\"fr-turn__patch-op\">${escapeHtml(opLabel)}</span>\n <span class=\"fr-turn__patch-value\">${valueHtml}</span>\n </div>\n `;\n })\n .filter(Boolean)\n .join('');\n\n return `<div class=\"fr-turn__patches\">${patchHtml}</div>`;\n}\n\n/**\n * Render a single tool call with enhanced details.\n * Shows query for web_search, patch details for fill_form.\n */\nfunction renderToolCall(tc: {\n tool: string;\n success: boolean;\n durationMs: number;\n input: Record<string, unknown>;\n result?: { error?: string; resultCount?: number };\n}): string {\n const hasError = !!tc.result?.error;\n const icon = tc.success ? '✓' : '✕';\n const errorClass = hasError ? ' fr-turn__tool--error' : '';\n\n // Build result summary\n let resultSummary = '';\n if (hasError) {\n resultSummary = `Error: ${escapeHtml(tc.result?.error ?? '')}`;\n } else if (tc.result?.resultCount !== undefined) {\n resultSummary = `${tc.result.resultCount} results`;\n } else {\n resultSummary = 'OK';\n }\n\n // Build tool-specific details\n let detailHtml = '';\n if (tc.tool === 'web_search' && typeof tc.input.query === 'string') {\n const query = escapeHtml(tc.input.query);\n detailHtml = ` <span class=\"fr-turn__query\">\"${query}\"</span>`;\n }\n\n // Base tool call line\n const toolLine = `<li class=\"fr-turn__tool${errorClass}\">${icon} <strong>${escapeHtml(tc.tool)}</strong>${detailHtml}: ${resultSummary} (${formatDuration(tc.durationMs)})</li>`;\n\n // For fill_form, add patch details\n if (tc.tool === 'fill_form' && tc.input.patches) {\n const patchDetails = renderPatchDetails(tc.input);\n if (patchDetails) {\n return toolLine + patchDetails;\n }\n }\n\n return toolLine;\n}\n\n/**\n * CSS styles for fill record visualization.\n * Uses CSS custom properties for theming (supports dark mode via prefers-color-scheme).\n * Designed to be lightweight, reusable, and embeddable.\n */\nexport const FILL_RECORD_STYLES = `\n<style>\n .fr-dashboard {\n --fr-bg: #ffffff;\n --fr-bg-muted: #f9fafb;\n --fr-bg-subtle: #f3f4f6;\n --fr-border: #e5e7eb;\n --fr-text: #111827;\n --fr-text-muted: #6b7280;\n --fr-primary: #3b82f6;\n --fr-success: #22c55e;\n --fr-warning: #f59e0b;\n --fr-error: #ef4444;\n --fr-info: #6b7280;\n\n /* Typography - consolidated to fewer sizes */\n --fr-font-sm: 13px;\n --fr-font-base: 14px;\n --fr-font-lg: 20px;\n\n font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n padding: 20px;\n max-width: 900px;\n margin: 0 auto;\n color: var(--fr-text);\n line-height: 1.5;\n }\n\n @media (prefers-color-scheme: dark) {\n .fr-dashboard {\n --fr-bg: #1f2937;\n --fr-bg-muted: #374151;\n --fr-bg-subtle: #4b5563;\n --fr-border: #4b5563;\n --fr-text: #f9fafb;\n --fr-text-muted: #9ca3af;\n }\n }\n\n .fr-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding-bottom: 12px;\n border-bottom: 1px solid var(--fr-border);\n }\n .fr-header__model {\n font-weight: 600;\n font-size: var(--fr-font-base);\n color: var(--fr-text);\n }\n .fr-header__time {\n font-weight: 600;\n font-size: var(--fr-font-base);\n color: var(--fr-text);\n }\n\n .fr-banner {\n border-radius: 8px;\n padding: 12px 16px;\n margin-bottom: 20px;\n font-size: var(--fr-font-base);\n }\n .fr-banner--error {\n background: color-mix(in srgb, var(--fr-error) 10%, var(--fr-bg));\n border: 1px solid var(--fr-error);\n }\n .fr-banner--warning {\n background: color-mix(in srgb, var(--fr-warning) 10%, var(--fr-bg));\n border: 1px solid var(--fr-warning);\n }\n\n .fr-cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n .fr-card {\n padding: 16px;\n background: var(--fr-bg-muted);\n border-radius: 8px;\n text-align: center;\n }\n .fr-card__label {\n font-size: var(--fr-font-sm);\n color: var(--fr-text-muted);\n margin-bottom: 4px;\n }\n .fr-card__value {\n font-size: var(--fr-font-lg);\n font-weight: 600;\n }\n .fr-card__sub {\n font-size: var(--fr-font-sm);\n color: var(--fr-text-muted);\n margin-top: 2px;\n }\n\n .fr-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border-radius: 4px;\n font-weight: 600;\n font-size: var(--fr-font-sm);\n }\n .fr-badge--completed { background: color-mix(in srgb, var(--fr-success) 15%, transparent); color: var(--fr-success); }\n .fr-badge--partial { background: color-mix(in srgb, var(--fr-warning) 15%, transparent); color: var(--fr-warning); }\n .fr-badge--cancelled { background: color-mix(in srgb, var(--fr-info) 15%, transparent); color: var(--fr-info); }\n .fr-badge--failed { background: color-mix(in srgb, var(--fr-error) 15%, transparent); color: var(--fr-error); }\n\n .fr-section {\n margin-bottom: 24px;\n }\n .fr-section__title {\n font-size: var(--fr-font-base);\n font-weight: 500;\n color: var(--fr-text);\n margin-bottom: 8px;\n }\n\n .fr-progress {\n background: var(--fr-border);\n border-radius: 4px;\n height: 20px;\n overflow: hidden;\n }\n .fr-progress__bar {\n background: var(--fr-primary);\n height: 100%;\n transition: width 0.3s ease;\n }\n .fr-progress__text {\n font-size: var(--fr-font-sm);\n color: var(--fr-text-muted);\n margin-top: 4px;\n }\n\n .fr-progress__segments {\n display: flex;\n height: 100%;\n width: 100%;\n }\n .fr-progress-segment {\n height: 100%;\n min-width: 2px;\n border-right: 2px solid var(--fr-bg);\n cursor: pointer;\n }\n .fr-progress-segment:last-child {\n border-right: none;\n }\n .fr-progress-segment--filled {\n background: var(--fr-primary);\n }\n .fr-progress-segment--filled:hover {\n background: color-mix(in srgb, var(--fr-primary) 70%, white);\n }\n .fr-progress-segment--prefilled {\n background: #8b5cf6;\n }\n .fr-progress-segment--prefilled:hover {\n background: color-mix(in srgb, #8b5cf6 70%, white);\n }\n .fr-progress-segment--skipped {\n background: var(--fr-warning);\n }\n .fr-progress-segment--skipped:hover {\n background: color-mix(in srgb, var(--fr-warning) 70%, white);\n }\n .fr-progress-segment--empty {\n background: var(--fr-border);\n }\n\n /* Gantt chart - each call on its own row */\n .fr-gantt {\n margin-bottom: 8px;\n }\n .fr-gantt__row {\n display: flex;\n align-items: center;\n height: 20px;\n margin-bottom: 3px;\n }\n .fr-gantt__label {\n width: 90px;\n flex-shrink: 0;\n font-size: 11px;\n color: var(--fr-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding-right: 8px;\n text-align: right;\n }\n .fr-gantt__track {\n flex: 1;\n background: var(--fr-bg-subtle);\n border-radius: 3px;\n height: 14px;\n position: relative;\n }\n .fr-gantt__bar {\n position: absolute;\n top: 2px;\n height: calc(100% - 4px);\n min-width: 6px;\n border-radius: 2px;\n cursor: pointer;\n }\n .fr-gantt__bar:hover {\n filter: brightness(1.15);\n }\n .fr-gantt__bar--llm {\n background: var(--fr-primary);\n }\n .fr-gantt__bar--tool {\n background: var(--fr-success);\n }\n .fr-gantt__legend {\n display: flex;\n gap: 16px;\n font-size: var(--fr-font-sm);\n color: var(--fr-text-muted);\n margin-top: 12px;\n padding-top: 8px;\n border-top: 1px solid var(--fr-border);\n }\n .fr-gantt__legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .fr-gantt__legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 2px;\n }\n .fr-gantt__legend-dot--llm { background: var(--fr-primary); }\n .fr-gantt__legend-dot--tool { background: var(--fr-success); }\n\n /* Tooltip container */\n .fr-tooltip {\n position: fixed;\n background: #1f2937;\n color: #f9fafb;\n padding: 8px 12px;\n border-radius: 4px;\n font-size: var(--fr-font-sm);\n white-space: pre-line;\n pointer-events: none;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.05s ease-out, visibility 0.05s ease-out;\n }\n .fr-tooltip.visible {\n opacity: 1;\n visibility: visible;\n transition: opacity 0.2s ease-in, visibility 0.2s ease-in;\n }\n\n .fr-table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--fr-font-sm);\n }\n .fr-table th {\n padding: 8px 12px;\n text-align: left;\n font-weight: 600;\n background: var(--fr-bg-subtle);\n }\n .fr-table th:not(:first-child) { text-align: center; }\n .fr-table td {\n padding: 8px 12px;\n border-bottom: 1px solid var(--fr-border);\n }\n .fr-table td:not(:first-child) { text-align: center; }\n\n .fr-details {\n border: none;\n background: none;\n }\n .fr-details > summary {\n cursor: pointer;\n font-size: var(--fr-font-base);\n font-weight: 500;\n color: var(--fr-text);\n padding: 8px 0;\n list-style: none;\n }\n .fr-details > summary::-webkit-details-marker { display: none; }\n .fr-details > summary::before {\n content: '▶';\n display: inline-block;\n margin-right: 8px;\n transition: transform 0.2s;\n font-size: 11px;\n }\n .fr-details[open] > summary::before {\n transform: rotate(90deg);\n }\n .fr-details__content {\n background: var(--fr-bg-muted);\n border-radius: 8px;\n padding: 16px;\n margin-top: 8px;\n }\n\n .fr-turn {\n margin-bottom: 8px;\n background: var(--fr-bg-muted);\n border-radius: 4px;\n }\n .fr-turn summary {\n cursor: pointer;\n padding: 12px;\n font-size: var(--fr-font-sm);\n list-style: none;\n }\n .fr-turn summary::-webkit-details-marker { display: none; }\n .fr-turn summary::before {\n content: '▶';\n display: inline-block;\n margin-right: 8px;\n transition: transform 0.2s;\n font-size: 11px;\n }\n .fr-turn[open] summary::before {\n transform: rotate(90deg);\n }\n .fr-turn__content {\n padding: 0 12px 12px;\n }\n .fr-turn__tools {\n margin: 0;\n padding-left: 20px;\n list-style: none;\n }\n .fr-turn__tool {\n margin: 4px 0;\n font-size: var(--fr-font-sm);\n color: var(--fr-text-muted);\n }\n .fr-turn__tool--error { color: var(--fr-error); }\n\n .fr-turn__query {\n color: var(--fr-primary);\n font-style: italic;\n }\n\n .fr-turn__patches {\n margin: 4px 0 8px 20px;\n padding: 8px 12px;\n background: var(--fr-bg-subtle);\n border-radius: 4px;\n font-size: var(--fr-font-sm);\n }\n .fr-turn__patch {\n margin: 4px 0;\n padding: 4px 0;\n border-bottom: 1px solid var(--fr-border);\n }\n .fr-turn__patch:last-child {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n }\n .fr-turn__patch-field {\n font-weight: 600;\n color: var(--fr-text);\n }\n .fr-turn__patch-op {\n font-size: 11px;\n padding: 1px 4px;\n border-radius: 2px;\n background: var(--fr-bg-muted);\n color: var(--fr-text-muted);\n margin-left: 6px;\n }\n .fr-turn__patch-value {\n display: block;\n margin-top: 2px;\n color: var(--fr-text-muted);\n font-family: ui-monospace, 'SF Mono', Menlo, monospace;\n word-break: break-word;\n white-space: pre-wrap;\n max-height: 200px;\n overflow: auto;\n }\n .fr-turn__patch-value--skip {\n color: var(--fr-warning);\n font-style: italic;\n }\n .fr-turn__patch-value--clear {\n color: var(--fr-info);\n font-style: italic;\n }\n\n .fr-raw {\n position: relative;\n }\n .fr-copy-btn {\n position: absolute;\n top: 8px;\n right: 8px;\n padding: 4px 8px;\n font-size: var(--fr-font-sm);\n background: var(--fr-bg-subtle);\n border: 1px solid var(--fr-border);\n border-radius: 4px;\n cursor: pointer;\n color: var(--fr-text-muted);\n transition: all 0.15s;\n }\n .fr-copy-btn:hover {\n background: var(--fr-border);\n color: var(--fr-text);\n }\n .fr-copy-btn:active {\n transform: scale(0.95);\n }\n\n /* Scoped pre styles to override parent .tab-content pre */\n .fr-dashboard pre {\n background: var(--fr-bg-muted);\n color: var(--fr-text);\n padding: 1rem;\n border-radius: 6px;\n border: 1px solid var(--fr-border);\n overflow-x: auto;\n font-family: ui-monospace, 'SF Mono', Menlo, monospace;\n font-size: 0.85rem;\n line-height: 1.5;\n margin: 0;\n }\n\n /* Override syntax highlighting colors for dark mode compatibility */\n .fr-dashboard .syn-key { color: var(--fr-primary); }\n .fr-dashboard .syn-string { color: var(--fr-success); }\n .fr-dashboard .syn-number { color: var(--fr-primary); }\n .fr-dashboard .syn-bool { color: var(--fr-warning); }\n .fr-dashboard .syn-null { color: var(--fr-error); }\n\n @media (max-width: 600px) {\n .fr-dashboard { padding: 12px; }\n .fr-cards { grid-template-columns: repeat(2, 1fr); gap: 12px; }\n .fr-card { padding: 12px; }\n .fr-card__value { font-size: 18px; }\n .fr-table { font-size: var(--fr-font-sm); }\n .fr-table th, .fr-table td { padding: 6px 8px; }\n }\n</style>\n`;\n\n/**\n * Render fill record content (dashboard-style visualization).\n * Uses CSS custom properties for theming with automatic dark mode support.\n * Mobile responsive with grid-based layout.\n *\n * @public Exported for testing and reuse.\n */\nexport function renderFillRecordContent(record: FillRecord): string {\n const { status, statusDetail, startedAt, durationMs, llm, formProgress, toolSummary, timeline } =\n record;\n\n // Format start time for display\n const startDate = new Date(startedAt);\n const formattedDate = startDate.toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n const formattedTime = startDate.toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n });\n\n // Header with model and timestamp\n const headerInfo = `\n <div class=\"fr-header\">\n <div class=\"fr-header__model\">${escapeHtml(llm.model)}</div>\n <div class=\"fr-header__time\">${formattedDate} at ${formattedTime}</div>\n </div>\n `;\n\n // Status banner for non-completed fills\n let statusBanner = '';\n if (status !== 'completed') {\n const bannerClass = status === 'failed' ? 'fr-banner--error' : 'fr-banner--warning';\n const icon = status === 'failed' ? '✕' : '⚠';\n const title = status === 'failed' ? 'FAILED' : status === 'cancelled' ? 'CANCELLED' : 'PARTIAL';\n const msg = statusDetail ?? (status === 'partial' ? 'Did not complete all fields' : '');\n statusBanner = `<div class=\"fr-banner ${bannerClass}\"><strong>${icon} ${title}${msg ? ':' : ''}</strong>${msg ? ` ${escapeHtml(msg)}` : ''}</div>`;\n }\n\n // Summary cards\n const totalTokens = llm.inputTokens + llm.outputTokens;\n const badgeClass = `fr-badge fr-badge--${status}`;\n const badgeIcon = { completed: '✓', partial: '⚠', cancelled: '⊘', failed: '✕' }[status] ?? '?';\n const badgeLabel = status.charAt(0).toUpperCase() + status.slice(1);\n\n const summaryCards = `\n <div class=\"fr-cards\">\n <div class=\"fr-card\">\n <div class=\"fr-card__label\">Status</div>\n <div><span class=\"${badgeClass}\">${badgeIcon} ${badgeLabel}</span></div>\n </div>\n <div class=\"fr-card\">\n <div class=\"fr-card__label\">Duration</div>\n <div class=\"fr-card__value\">${formatDuration(durationMs)}</div>\n </div>\n <div class=\"fr-card\">\n <div class=\"fr-card__label\">Turns</div>\n <div class=\"fr-card__value\">${timeline.length}</div>\n </div>\n <div class=\"fr-card\">\n <div class=\"fr-card__label\">Tokens</div>\n <div class=\"fr-card__value\">${formatTokens(totalTokens)}</div>\n <div class=\"fr-card__sub\">${formatTokens(llm.inputTokens)} in / ${formatTokens(llm.outputTokens)} out</div>\n </div>\n </div>\n `;\n\n // Progress bar\n // Extract filled fields from timeline to show individual segments\n // Use Map to deduplicate by fieldId, keeping only the last (final) state for each field\n const fieldsMap = new Map<string, { fieldId: string; op: string; turnNumber: number }>();\n for (const turn of timeline) {\n for (const tc of turn.toolCalls) {\n if (tc.tool === 'fill_form' && tc.input.patches) {\n const patches = tc.input.patches as { op?: string; fieldId?: string }[];\n for (const patch of patches) {\n if (patch.fieldId && patch.op) {\n fieldsMap.set(patch.fieldId, {\n fieldId: patch.fieldId,\n op: patch.op,\n turnNumber: turn.turnNumber,\n });\n }\n }\n }\n }\n }\n const fieldsFilled = Array.from(fieldsMap.values());\n\n const totalFields = formProgress.totalFields;\n const filledFields = formProgress.filledFields;\n const skippedFields = formProgress.skippedFields;\n const abortedFields = formProgress.abortedFields ?? 0;\n const progressPercent = totalFields > 0 ? Math.round((filledFields / totalFields) * 100) : 0;\n\n // Build progress segments\n const segmentWidth = totalFields > 0 ? 100 / totalFields : 0;\n\n // AI-filled fields (from timeline patches, excluding skip/abort)\n const aiFilledFields = fieldsFilled.filter(\n (f) => f.op !== 'skip_field' && f.op !== 'abort_field',\n );\n const aiFilledSegmentsHtml = aiFilledFields\n .map((f) => {\n const opLabel = f.op.replace(/_/g, ' ');\n const tooltip = `${f.fieldId}\\n${opLabel}\\nTurn ${f.turnNumber}`;\n return `<div class=\"fr-progress-segment fr-progress-segment--filled\" style=\"width: ${segmentWidth}%\" data-tooltip=\"${escapeHtml(tooltip)}\" onmouseenter=\"frShowTip(this)\" onmouseleave=\"frHideTip()\"></div>`;\n })\n .join('');\n\n // Pre-filled fields (filled before AI started, not in timeline)\n const prefilledCount = Math.max(0, filledFields - aiFilledFields.length);\n const prefilledSegmentsHtml =\n prefilledCount > 0\n ? `<div class=\"fr-progress-segment fr-progress-segment--prefilled\" style=\"width: ${segmentWidth * prefilledCount}%\" data-tooltip=\"Pre-filled (${prefilledCount} field${prefilledCount !== 1 ? 's' : ''})\" onmouseenter=\"frShowTip(this)\" onmouseleave=\"frHideTip()\"></div>`\n : '';\n\n // Skipped/aborted fields\n const skippedSegmentsHtml = fieldsFilled\n .filter((f) => f.op === 'skip_field' || f.op === 'abort_field')\n .map((f) => {\n const opLabel = f.op === 'skip_field' ? 'skipped' : 'aborted';\n const tooltip = `${f.fieldId}\\n${opLabel}\\nTurn ${f.turnNumber}`;\n return `<div class=\"fr-progress-segment fr-progress-segment--skipped\" style=\"width: ${segmentWidth}%\" data-tooltip=\"${escapeHtml(tooltip)}\" onmouseenter=\"frShowTip(this)\" onmouseleave=\"frHideTip()\"></div>`;\n })\n .join('');\n\n // Empty segments for unfilled fields\n const unfilledCount = totalFields - filledFields - skippedFields - abortedFields;\n const unfilledSegmentsHtml =\n unfilledCount > 0\n ? `<div class=\"fr-progress-segment fr-progress-segment--empty\" style=\"width: ${segmentWidth * unfilledCount}%\"></div>`\n : '';\n\n // Build progress text with details\n const progressDetails: string[] = [];\n if (prefilledCount > 0) progressDetails.push(`${prefilledCount} pre-filled`);\n if (skippedFields > 0) progressDetails.push(`${skippedFields} skipped`);\n const progressDetailsText = progressDetails.length > 0 ? ` • ${progressDetails.join(' • ')}` : '';\n\n const progressBar = `\n <div class=\"fr-section\">\n <div class=\"fr-section__title\">Progress</div>\n <div class=\"fr-progress\">\n <div class=\"fr-progress__segments\">\n ${prefilledSegmentsHtml}${aiFilledSegmentsHtml}${skippedSegmentsHtml}${unfilledSegmentsHtml}\n </div>\n </div>\n <div class=\"fr-progress__text\">\n ${filledFields}/${totalFields} fields filled (${progressPercent}%)${progressDetailsText}\n </div>\n </div>\n `;\n\n // Gantt-style timeline visualization\n // Calculate actual start/end times for each call\n const totalMs = durationMs;\n const llmCallCount = llm.totalCalls;\n const toolCallCount = toolSummary.totalCalls;\n\n // Build timeline events with actual positions\n // For each turn: LLM call happens first, then tool calls sequentially\n interface TimelineEvent {\n type: 'llm' | 'tool';\n startMs: number;\n durationMs: number;\n turnNumber: number;\n label: string;\n tokens?: { input: number; output: number; total: number };\n }\n\n const timelineEvents: TimelineEvent[] = [];\n\n for (const turn of timeline) {\n const toolTimeInTurn = turn.toolCalls.reduce((sum, tc) => sum + tc.durationMs, 0);\n const llmTimeInTurn = Math.max(0, turn.durationMs - toolTimeInTurn);\n\n // LLM call for this turn - starts at turn.startMs\n if (llmTimeInTurn > 0) {\n timelineEvents.push({\n type: 'llm',\n startMs: turn.startMs,\n durationMs: llmTimeInTurn,\n turnNumber: turn.turnNumber,\n label: `Turn ${turn.turnNumber}`,\n tokens: {\n input: turn.tokens.input,\n output: turn.tokens.output,\n total: turn.tokens.input + turn.tokens.output,\n },\n });\n }\n\n // Tool calls for this turn - use pre-computed startMs\n for (const tc of turn.toolCalls) {\n timelineEvents.push({\n type: 'tool',\n startMs: tc.startMs,\n durationMs: tc.durationMs,\n turnNumber: turn.turnNumber,\n label: tc.tool,\n });\n }\n }\n\n // Render Gantt chart rows - each event gets its own row\n const ganttRowsHtml = timelineEvents\n .map((e) => {\n const leftPct = totalMs > 0 ? (e.startMs / totalMs) * 100 : 0;\n const widthPct = totalMs > 0 ? (e.durationMs / totalMs) * 100 : 0;\n const barClass = e.type === 'llm' ? 'fr-gantt__bar--llm' : 'fr-gantt__bar--tool';\n const startTime = `Start: ${formatDuration(e.startMs)}`;\n const tooltip =\n e.type === 'llm'\n ? `${e.label} ${startTime} Duration: ${formatDuration(e.durationMs)} ${formatTokens(e.tokens?.total ?? 0)} tokens (${formatTokens(e.tokens?.input ?? 0)} in / ${formatTokens(e.tokens?.output ?? 0)} out)`\n : `${e.label} ${startTime} Duration: ${formatDuration(e.durationMs)} Turn ${e.turnNumber}`;\n\n return `\n <div class=\"fr-gantt__row\">\n <div class=\"fr-gantt__label\">${escapeHtml(e.label)}</div>\n <div class=\"fr-gantt__track\">\n <div class=\"fr-gantt__bar ${barClass}\" style=\"left: ${leftPct}%; width: ${widthPct}%\" data-tooltip=\"${tooltip}\" onmouseenter=\"frShowTip(this)\" onmouseleave=\"frHideTip()\"></div>\n </div>\n </div>`;\n })\n .join('');\n\n const llmTotalMs = timelineEvents\n .filter((e) => e.type === 'llm')\n .reduce((sum, e) => sum + e.durationMs, 0);\n const toolTotalMs = timelineEvents\n .filter((e) => e.type === 'tool')\n .reduce((sum, e) => sum + e.durationMs, 0);\n\n const timingSection = `\n <details class=\"fr-details fr-section\" open>\n <summary>Timeline (${formatDuration(totalMs)} total)</summary>\n <div class=\"fr-details__content\">\n <div class=\"fr-gantt\">\n ${ganttRowsHtml}\n <div class=\"fr-gantt__legend\">\n <div class=\"fr-gantt__legend-item\">\n <div class=\"fr-gantt__legend-dot fr-gantt__legend-dot--llm\"></div>\n <span>LLM (${llmCallCount} call${llmCallCount !== 1 ? 's' : ''}, ${formatDuration(llmTotalMs)})</span>\n </div>\n <div class=\"fr-gantt__legend-item\">\n <div class=\"fr-gantt__legend-dot fr-gantt__legend-dot--tool\"></div>\n <span>Tools (${toolCallCount} call${toolCallCount !== 1 ? 's' : ''}, ${formatDuration(toolTotalMs)})</span>\n </div>\n </div>\n </div>\n </div>\n </details>\n `;\n\n // Tool summary table\n let toolSection = '';\n if (toolSummary.byTool.length > 0) {\n const toolRows = toolSummary.byTool\n .map(\n (t) => `\n <tr>\n <td>${escapeHtml(t.toolName)}</td>\n <td>${t.callCount}</td>\n <td>${t.successCount === t.callCount ? '100%' : `${Math.round((t.successCount / t.callCount) * 100)}%`}</td>\n <td>${formatDuration(t.timing.avgMs)}</td>\n <td>${formatDuration(t.timing.p95Ms)}</td>\n </tr>\n `,\n )\n .join('');\n\n toolSection = `\n <details class=\"fr-details fr-section\" open>\n <summary>Tool Summary</summary>\n <div style=\"overflow-x: auto; margin-top: 8px;\">\n <table class=\"fr-table\">\n <thead><tr><th>Tool</th><th>Calls</th><th>Success</th><th>Avg</th><th>p95</th></tr></thead>\n <tbody>${toolRows}</tbody>\n </table>\n </div>\n </details>\n `;\n }\n\n // Turn Details accordion\n let timelineSection = '';\n if (timeline.length > 0) {\n const timelineItems = timeline\n .map((turn) => {\n const turnTokens = turn.tokens.input + turn.tokens.output;\n const toolCallsList = turn.toolCalls.map((tc) => renderToolCall(tc)).join('');\n\n const patchInfo = turn.patchesApplied > 0 ? ` • ${turn.patchesApplied} patches` : '';\n const rejectedInfo =\n turn.patchesRejected > 0\n ? ` <span style=\"color: var(--fr-error)\">(${turn.patchesRejected} rejected)</span>`\n : '';\n\n return `\n <details class=\"fr-turn\">\n <summary><strong>Turn ${turn.turnNumber}</strong> • Order ${turn.order} • ${formatDuration(turn.durationMs)} • ${formatTokens(turnTokens)} tokens${patchInfo}${rejectedInfo}</summary>\n <div class=\"fr-turn__content\">\n ${turn.toolCalls.length > 0 ? `<ul class=\"fr-turn__tools\">${toolCallsList}</ul>` : '<span class=\"fr-turn__tool\">No tool calls</span>'}\n </div>\n </details>\n `;\n })\n .join('');\n\n timelineSection = `\n <details class=\"fr-details fr-section\">\n <summary>Turn Details (${timeline.length} turns)</summary>\n <div style=\"margin-top: 8px;\">${timelineItems}</div>\n </details>\n `;\n }\n\n // Raw YAML section with copy functionality (handler defined in main page script)\n const yamlContent = YAML.stringify(record, { lineWidth: 0 });\n\n const rawSection = `\n <details class=\"fr-details fr-section\">\n <summary>Raw YAML</summary>\n <div class=\"fr-raw\" style=\"margin-top: 8px;\">\n <button class=\"fr-copy-btn\" onclick=\"frCopyYaml(this)\">Copy</button>\n ${renderYamlContent(yamlContent)}\n </div>\n </details>\n `;\n\n // Tooltip element - functions are defined in main page script\n const tooltipHtml = `<div id=\"fr-tooltip\" class=\"fr-tooltip\"></div>`;\n\n return `\n ${FILL_RECORD_STYLES}\n ${tooltipHtml}\n <div class=\"fr-dashboard\">\n ${headerInfo}\n ${statusBanner}\n ${summaryCards}\n ${progressBar}\n ${timingSection}\n ${toolSection}\n ${timelineSection}\n ${rawSection}\n </div>\n `;\n}\n"],"mappings":"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"}
|