@kaifronsdal/transcript-viewer
Version:
A web-based viewer for AI conversation transcripts with rollback support
1,254 lines (1,253 loc) • 65.5 kB
JavaScript
import { x as push, z as pop, J as escape_html, N as ensure_array_like, F as attr, P as attr_class, O as stringify, Q as attr_style, G as store_get, V as clsx, I as unsubscribe_stores } from './index-CeukPVPf.js';
import { c as createTranscriptLoader } from './transcript.svelte-BEaYfmNG.js';
import fastJsonPatch from 'fast-json-patch';
import { t as themeString } from './theme--C4x6dAj.js';
import markdownit from 'markdown-it';
import hljs from 'highlight.js/lib/core';
import python from 'highlight.js/lib/languages/python';
import bash from 'highlight.js/lib/languages/bash';
import plaintext from 'highlight.js/lib/languages/plaintext';
import { b as extractFirstSentence } from './transcript-utils-BY7i01oF.js';
function html(value) {
var html2 = String(value ?? "");
var open = "<!---->";
return open + html2 + "<!---->";
}
const EditOperation = {
ADD: "add",
RESET: "reset",
JSON_PATCH: "json_patch"
};
const isAddOperation = (edit) => edit.operation === EditOperation.ADD;
const isResetOperation = (edit) => edit.operation === EditOperation.RESET;
const isJsonPatchOperation = (edit) => edit.operation === EditOperation.JSON_PATCH;
const { applyPatch } = fastJsonPatch;
function getBranchTitle(branchIndex, edit) {
if (branchIndex === 0) {
return "Original";
}
if (edit && isJsonPatchOperation(edit) && edit.name) {
return edit.name;
}
return `Branch ${branchIndex}`;
}
function addColumnIfNotEmpty(columns, column) {
if (column.messages.length > 0) {
columns.push({ ...column });
}
}
function createColumn(columnIndex, edit, editNumber = 0) {
return {
id: `column-${columnIndex}`,
title: getBranchTitle(columnIndex, edit),
messages: [],
editNumber
};
}
function getMessageFromAddEdit(edit) {
if (isAddOperation(edit)) {
return edit.message;
}
return null;
}
function getNewMessagesFromResetEdit(edit) {
if (isResetOperation(edit)) {
return edit.new_messages || [];
}
return [];
}
function getPatchFromJsonEdit(edit) {
if (isJsonPatchOperation(edit)) {
return edit.patch;
}
return [];
}
function extractAvailableViews(events) {
console.log("🔍 [DEBUG] extractAvailableViews called with", events.length, "events");
const viewSet = /* @__PURE__ */ new Set();
const transcriptEvents = events.filter((event) => event.type === "transcript_event");
console.log("📋 [DEBUG] Found", transcriptEvents.length, "transcript events");
for (const event of transcriptEvents) {
if (Array.isArray(event.view)) {
event.view.forEach((view) => viewSet.add(view));
} else {
viewSet.add(event.view);
}
}
const result = Array.from(viewSet).sort();
console.log("✅ [DEBUG] extractAvailableViews returning:", result);
return result;
}
function eventAppliesToView(event, targetView) {
if (Array.isArray(event.view)) {
return event.view.includes(targetView);
}
return event.view === targetView;
}
function normalizeRawMessage(raw, viewSource, eventId) {
const baseProps = {
id: raw.id ?? void 0,
metadata: raw.metadata ?? void 0,
isShared: false,
viewSource,
eventId
};
switch (raw.role) {
case "system":
return { type: "system", content: raw.content, ...baseProps };
case "user":
return { type: "user", content: raw.content, ...baseProps };
case "assistant":
return {
type: "assistant",
content: raw.content,
tool_calls: raw.tool_calls ?? void 0,
...baseProps
};
case "tool": {
return {
type: "tool",
content: raw.content,
tool_call_id: raw.tool_call_id ?? void 0,
function: raw.function ?? void 0,
error: raw.error ?? void 0,
...baseProps
};
}
default:
return {
type: "system",
content: raw.content,
...baseProps
};
}
}
function parseTranscriptEvents(events, view, showApiFailures = false) {
console.log("🔄 [DEBUG] parseTranscriptEvents called:", { eventsLength: events?.length || 0, view, showApiFailures });
if (!events || !Array.isArray(events)) {
console.error("🚨 [ERROR] parseTranscriptEvents: events is not a valid array:", events);
return [];
}
const columns = [];
let currentMessages = [];
let editNumber = 0;
let currentColumn = {
id: "column-0",
title: "Original",
messages: [],
editNumber: 0
};
const transcriptEvents = events.filter((event) => event.type === "transcript_event");
const infoEvents = events.filter((event) => event.type === "info_event");
console.log("📋 [DEBUG] Filtered transcript events:", transcriptEvents.length, "info events:", infoEvents.length);
const allRelevantEvents = [
...transcriptEvents.filter((event) => eventAppliesToView(event, view)),
...infoEvents
// Info events don't have view filtering - they appear in all views
].sort((a, b) => {
if (a.timestamp && b.timestamp) {
return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime();
}
return 0;
});
console.log("🎯 [DEBUG] All relevant events for view", view + ":", allRelevantEvents.length);
for (let i = 0; i < allRelevantEvents.length; i++) {
const event = allRelevantEvents[i];
editNumber++;
if (event.type === "info_event") {
const infoMessage = {
type: "info",
id: event.id,
info: event.info,
timestamp: event.timestamp,
isShared: false,
viewSource: "info",
// Mark as coming from info events
eventId: event.id
};
currentMessages.push(infoMessage);
currentColumn.messages.push(infoMessage);
} else if (event.type === "transcript_event" && isAddOperation(event.edit)) {
const raw = getMessageFromAddEdit(event.edit);
if (!raw) continue;
const message = normalizeRawMessage(raw, event.view, event.id);
if (message.type === "api_failure" && !showApiFailures) {
continue;
}
currentMessages.push(message);
currentColumn.messages.push(message);
} else if (event.type === "transcript_event" && event.edit.operation === "rollback") {
const rollbackEdit = event.edit;
addColumnIfNotEmpty(columns, currentColumn);
currentColumn = createColumn(columns.length, event.edit, editNumber);
let rollbackIndex = currentMessages.length;
if (rollbackEdit.to_id) {
const targetIndex = currentMessages.findIndex((msg) => msg.id === rollbackEdit.to_id);
if (targetIndex !== -1) {
rollbackIndex = targetIndex + 1;
}
} else if (rollbackEdit.count) {
const rollbackCount = Math.min(rollbackEdit.count, currentMessages.length);
rollbackIndex = currentMessages.length - rollbackCount;
}
currentMessages = currentMessages.slice(0, rollbackIndex);
currentColumn.messages = currentMessages.map((msg) => ({
...msg,
isShared: true
}));
} else if (event.type === "transcript_event" && isResetOperation(event.edit)) {
addColumnIfNotEmpty(columns, currentColumn);
currentColumn = createColumn(columns.length, event.edit, editNumber);
const newMessages = getNewMessagesFromResetEdit(event.edit);
const messagesWithFlags = newMessages.map(
(raw) => normalizeRawMessage(raw, event.view, event.id)
);
currentMessages = messagesWithFlags;
currentColumn.messages = [...currentMessages];
} else if (event.type === "transcript_event" && isJsonPatchOperation(event.edit)) {
try {
const ops = getPatchFromJsonEdit(event.edit);
const result = applyPatch(
currentMessages,
ops,
/*validate*/
false,
/*mutateDocument*/
false
);
const nextMessagesRaw = result.newDocument;
if (!Array.isArray(nextMessagesRaw)) {
continue;
}
const nextMessages = nextMessagesRaw.map((m) => {
if (m && typeof m === "object" && "role" in m) {
return normalizeRawMessage(m, event.view, event.id);
}
return m;
});
const normalize = (msg) => {
const { isShared, viewSource, eventId, ...rest } = msg || {};
return rest;
};
const areEqual = (a, b) => JSON.stringify(normalize(a)) === JSON.stringify(normalize(b));
let prefixLen = 0;
const maxPrefix = Math.min(currentMessages.length, nextMessages.length);
while (prefixLen < maxPrefix && areEqual(currentMessages[prefixLen], nextMessages[prefixLen])) {
prefixLen++;
}
const oldIsPrefix = prefixLen === currentMessages.length && currentMessages.length <= nextMessages.length;
if (!oldIsPrefix) {
addColumnIfNotEmpty(columns, currentColumn);
currentColumn = createColumn(columns.length, event.edit, editNumber);
const messagesWithFlags = nextMessages.map((msg, index) => ({
...msg,
isShared: index < prefixLen,
viewSource: msg.viewSource ?? event.view,
eventId: msg.eventId ?? event.id
}));
currentMessages = messagesWithFlags;
currentColumn.messages = [...currentMessages];
} else {
const messagesWithFlags = nextMessages.map((msg) => ({
...msg,
isShared: msg.isShared ?? false,
viewSource: msg.viewSource ?? event.view,
eventId: msg.eventId ?? event.id
}));
currentMessages = messagesWithFlags;
currentColumn.messages = [...currentMessages];
if (currentColumn.title === "Original" && isJsonPatchOperation(event.edit) && event.edit.name) {
currentColumn.title = event.edit.name;
}
}
} catch (e) {
console.error("Failed to apply JSON patch edit", e);
}
}
}
console.log("➕ [DEBUG] Adding final column with", currentColumn.messages.length, "messages");
currentColumn.editNumber = editNumber;
addColumnIfNotEmpty(columns, currentColumn);
console.log("✅ [DEBUG] parseTranscriptEvents completed, returning", columns.length, "columns");
return columns;
}
function getJsonType(value) {
if (value === null) return "null";
if (value === void 0) return "undefined";
if (Array.isArray(value)) return "array";
const type = typeof value;
if (type === "number") {
if (isNaN(value)) return "nan";
if (value % 1 !== 0) return "float";
return "integer";
}
if (type === "object" && value instanceof Date) return "date";
return type;
}
function isExpandable(value) {
return Array.isArray(value) || typeof value === "object" && value !== null && !(value instanceof Date);
}
function getObjectSize(value) {
if (Array.isArray(value)) {
return value.length;
}
if (typeof value === "object" && value !== null) {
return Object.keys(value).length;
}
return 0;
}
function shouldCollapseString(str, collapseAfter) {
if (typeof collapseAfter !== "number") return false;
return str.length > collapseAfter;
}
function truncateString(str, length) {
if (str.length <= length) return str;
return str.substring(0, length);
}
function escapeString(str) {
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
}
function shouldRenderInline(value, inlineShortContainers, collapseStringsAfterLength) {
if (!inlineShortContainers) return false;
let maxLength;
if (typeof inlineShortContainers === "number") {
maxLength = inlineShortContainers;
} else {
maxLength = 40;
}
const inlineString = getInlineString(value, collapseStringsAfterLength);
return inlineString.length <= maxLength;
}
function getInlineString(value, collapseStringsAfterLength) {
if (Array.isArray(value)) {
const items = value.map((item) => {
if (isExpandable(item)) {
return getInlineString(item, collapseStringsAfterLength);
} else {
return formatPrimitiveValue(item, collapseStringsAfterLength);
}
});
return `[${items.join(", ")}]`;
}
if (typeof value === "object" && value !== null && !(value instanceof Date)) {
const pairs = Object.entries(value).map(([key, val]) => {
const formattedValue = isExpandable(val) ? getInlineString(val, collapseStringsAfterLength) : formatPrimitiveValue(val, collapseStringsAfterLength);
return `"${key}": ${formattedValue}`;
});
return `{${pairs.join(", ")}}`;
}
return formatPrimitiveValue(value, collapseStringsAfterLength);
}
function formatPrimitiveValue(value, collapseStringsAfterLength) {
if (value === null) return "null";
if (value === void 0) return "undefined";
if (typeof value === "string") {
let stringValue = value;
if (collapseStringsAfterLength && stringValue.length > collapseStringsAfterLength) {
stringValue = truncateString(stringValue, collapseStringsAfterLength) + "...";
}
return `"${escapeString(stringValue)}"`;
}
if (typeof value === "number") {
if (isNaN(value)) return "NaN";
if (value === Infinity) return "Infinity";
if (value === -Infinity) return "-Infinity";
return String(value);
}
if (typeof value === "boolean") return String(value);
if (value instanceof Date) return value.toISOString();
return String(value);
}
function JsonMeta($$payload, $$props) {
push();
const { value, displayObjectSize = true, displayArrayKey = true } = $$props;
const size = getObjectSize(value);
const isArray = Array.isArray(value);
const isObject = typeof value === "object" && value !== null && !Array.isArray(value);
const shouldShowMeta = displayObjectSize && (isArray || isObject) && size > 0;
const metaText = isArray ? size === 1 ? "1 item" : `${size} items` : isObject ? size === 1 ? "1 key" : `${size} keys` : "";
if (shouldShowMeta) {
$$payload.out += "<!--[-->";
$$payload.out += `<span class="json-meta svelte-7teum4">${escape_html(metaText)}</span>`;
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]-->`;
pop();
}
function JsonProperty($$payload, $$props) {
const { name, displayArrayKey = true, isArrayIndex = false } = $$props;
const shouldShow = !isArrayIndex || displayArrayKey;
const displayName = isArrayIndex ? `[${name}]` : `"${name}"`;
if (shouldShow) {
$$payload.out += "<!--[-->";
$$payload.out += `<span class="json-property svelte-1juhblb">${escape_html(displayName)}</span><span class="json-colon svelte-1juhblb">: </span>`;
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]-->`;
}
function JsonString($$payload, $$props) {
push();
const { value, collapseStringsAfterLength = 50 } = $$props;
const isCollapsible = shouldCollapseString(value, collapseStringsAfterLength);
const escapedValue = escapeString(value);
const displayValue = isCollapsible && true && collapseStringsAfterLength ? truncateString(escapedValue, collapseStringsAfterLength) : escapedValue;
const showEllipsis = isCollapsible && true;
const truncatedLength = isCollapsible && true && collapseStringsAfterLength ? collapseStringsAfterLength : null;
$$payload.out += `<span class="json-string svelte-od3rcy"><span class="json-quote svelte-od3rcy">"</span><span${attr_class("json-string-content svelte-od3rcy", void 0, { "clickable": isCollapsible })}${attr("role", isCollapsible ? "button" : void 0)}${attr("tabindex", isCollapsible ? 0 : void 0)}>${escape_html(displayValue)}`;
if (showEllipsis) {
$$payload.out += "<!--[-->";
$$payload.out += `<span class="json-ellipsis svelte-od3rcy">...</span>`;
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]--></span><span class="json-quote svelte-od3rcy">"</span>`;
if (truncatedLength) {
$$payload.out += "<!--[-->";
$$payload.out += `<span class="json-truncate-indicator svelte-od3rcy">${escape_html(value.length)} chars</span>`;
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]--></span>`;
pop();
}
function JsonValue($$payload, $$props) {
push();
const {
value,
displayDataTypes = false,
collapseStringsAfterLength = 50
} = $$props;
const type = getJsonType(value);
const displayValue = (() => {
switch (type) {
case "string":
return value;
case "integer":
case "float":
return String(value);
case "boolean":
return String(value);
case "null":
return "null";
case "undefined":
return "undefined";
case "nan":
return "NaN";
case "date":
return value.toISOString();
default:
return String(value);
}
})();
$$payload.out += `<span${attr_class(`json-value json-${stringify(type)}`, "svelte-feu0by")}>`;
if (displayDataTypes) {
$$payload.out += "<!--[-->";
$$payload.out += `<span class="json-data-type svelte-feu0by">${escape_html(type)}</span>`;
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]-->`;
if (type === "string") {
$$payload.out += "<!--[-->";
JsonString($$payload, { value, collapseStringsAfterLength });
} else if (type === "null" || type === "undefined" || type === "nan") {
$$payload.out += "<!--[1-->";
$$payload.out += `<span class="json-special-value svelte-feu0by">${escape_html(displayValue)}</span>`;
} else {
$$payload.out += "<!--[!-->";
$$payload.out += `<span class="json-primitive-value svelte-feu0by">${escape_html(displayValue)}</span>`;
}
$$payload.out += `<!--]--></span>`;
pop();
}
function ExpandIcon($$payload, $$props) {
const { expanded } = $$props;
$$payload.out += `<button${attr_class("expand-icon svelte-1f2qjvn", void 0, { "expanded": expanded })}${attr("aria-label", expanded ? "Collapse" : "Expand")}><svg width="12" height="12" viewBox="0 0 12 12" fill="none" class="svelte-1f2qjvn"><path d="M4 3L8 6L4 9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg></button>`;
}
function ContainerWrapper($$payload, $$props) {
push();
const {
hasItems,
name,
depth = 0,
collapsed = false,
displayArrayKey = true,
collapseStringsAfterLength = 50,
indentWidth = 12,
isLast = false,
isRoot = false,
inlineShortContainers = false,
inline = false,
inlineView,
collapsedView,
expandedView,
emptyView,
value
} = $$props;
const indentStyle = `margin-left: ${isRoot ? 0 : indentWidth}px`;
shouldRenderInline(value, inlineShortContainers, collapseStringsAfterLength);
let expanded = true;
let hovered = false;
const isArray = Array.isArray(value);
const openBrace = isArray ? "[" : "{";
const braceClass = isArray ? "json-bracket" : "json-brace";
if (inline) {
$$payload.out += "<!--[-->";
inlineView($$payload);
$$payload.out += `<!---->`;
} else {
$$payload.out += "<!--[!-->";
$$payload.out += `<div${attr_class("json-container svelte-yd5q3w", void 0, { "hovered": hovered, "root": isRoot })}${attr_style(indentStyle)} role="group"><div class="json-container-header svelte-yd5q3w">`;
if (hasItems) {
$$payload.out += "<!--[-->";
ExpandIcon($$payload, { expanded });
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]--> `;
if (name !== void 0) {
$$payload.out += "<!--[-->";
JsonProperty($$payload, { name, displayArrayKey, isArrayIndex: false });
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]--> `;
if (hasItems && expanded) {
$$payload.out += "<!--[-->";
$$payload.out += `<span${attr_class(clsx(braceClass), "svelte-yd5q3w")}>${escape_html(openBrace)}</span>`;
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]--> `;
if (!hasItems) {
$$payload.out += "<!--[-->";
emptyView($$payload);
$$payload.out += `<!---->`;
} else {
$$payload.out += "<!--[1-->";
}
$$payload.out += `<!--]--></div>`;
if (hasItems && expanded) {
$$payload.out += "<!--[-->";
$$payload.out += `<div class="json-expanded-content svelte-yd5q3w">`;
expandedView($$payload);
$$payload.out += `<!----></div>`;
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]--></div>`;
}
$$payload.out += `<!--]-->`;
pop();
}
function JsonArray($$payload, $$props) {
push();
const {
value,
name,
depth = 0,
collapsed = false,
displayObjectSize = true,
displayArrayKey = true,
collapseStringsAfterLength = 50,
displayDataTypes = false,
sortKeys = false,
indentWidth = 12,
isLast = false,
isRoot = false,
inlineShortContainers = false,
inline = false,
inlineRoot = false
} = $$props;
const hasItems = value.length > 0;
{
let inlineView = function($$payload2) {
const each_array = ensure_array_like(value);
$$payload2.out += `<span class="json-bracket svelte-1ctktzs">[</span><!--[-->`;
for (let index = 0, $$length = each_array.length; index < $$length; index++) {
let item = each_array[index];
if (isExpandable(item)) {
$$payload2.out += "<!--[-->";
if (Array.isArray(item)) {
$$payload2.out += "<!--[-->";
JsonArray($$payload2, { value: item, inlineShortContainers, inline: true });
} else if (item !== null && typeof item === "object") {
$$payload2.out += "<!--[1-->";
JsonObject($$payload2, { value: item, inlineShortContainers, inline: true });
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
} else {
$$payload2.out += "<!--[!-->";
JsonValue($$payload2, { value: item, displayDataTypes, collapseStringsAfterLength });
}
$$payload2.out += `<!--]-->`;
if (index < value.length - 1) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="json-comma svelte-1ctktzs">, </span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
}
$$payload2.out += `<!--]-->`;
if (!inlineRoot) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="json-bracket svelte-1ctktzs">]</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
}, collapsedView = function($$payload2) {
$$payload2.out += `<span class="json-bracket svelte-1ctktzs">[</span> `;
JsonMeta($$payload2, { value, displayObjectSize, displayArrayKey });
$$payload2.out += `<!----> <span class="json-ellipsis svelte-1ctktzs">...</span> <span class="json-bracket svelte-1ctktzs">]</span>`;
}, expandedView = function($$payload2) {
const each_array_1 = ensure_array_like(value);
$$payload2.out += `<div class="json-array-content svelte-1ctktzs"><!--[-->`;
for (let index = 0, $$length = each_array_1.length; index < $$length; index++) {
let item = each_array_1[index];
$$payload2.out += `<div class="json-array-item svelte-1ctktzs">`;
if (isExpandable(item)) {
$$payload2.out += "<!--[-->";
if (Array.isArray(item)) {
$$payload2.out += "<!--[-->";
JsonArray($$payload2, {
value: item,
depth: depth + 1,
collapsed,
displayObjectSize,
displayArrayKey,
collapseStringsAfterLength,
displayDataTypes,
sortKeys,
indentWidth,
inlineShortContainers,
isLast: index === value.length - 1
});
} else if (item !== null && typeof item === "object") {
$$payload2.out += "<!--[1-->";
JsonObject($$payload2, {
value: item,
depth: depth + 1,
collapsed,
displayObjectSize,
displayArrayKey,
collapseStringsAfterLength,
displayDataTypes,
sortKeys,
indentWidth,
inlineShortContainers,
isLast: index === value.length - 1
});
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
} else {
$$payload2.out += "<!--[!-->";
$$payload2.out += `<div class="json-array-primitive svelte-1ctktzs"${attr_style(`margin-left: ${stringify(indentWidth)}px`)}>`;
JsonValue($$payload2, { value: item, displayDataTypes, collapseStringsAfterLength });
$$payload2.out += `<!---->`;
if (index < value.length - 1) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="json-comma svelte-1ctktzs">,</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]--></div>`;
}
$$payload2.out += `<!--]--></div>`;
}
$$payload2.out += `<!--]--></div> <div class="json-array-footer svelte-1ctktzs"><span class="json-bracket svelte-1ctktzs">]</span> `;
if (!isLast && !isRoot) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="json-comma svelte-1ctktzs">,</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]--></div>`;
}, emptyView = function($$payload2) {
$$payload2.out += `<span class="json-bracket svelte-1ctktzs">[]</span> `;
if (!isLast && !isRoot) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="json-comma svelte-1ctktzs">,</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
};
ContainerWrapper($$payload, {
value,
name,
depth,
collapsed,
displayArrayKey,
collapseStringsAfterLength,
indentWidth,
isLast,
isRoot,
inlineShortContainers,
inline,
hasItems,
inlineView,
collapsedView,
expandedView,
emptyView
});
}
pop();
}
function JsonObject($$payload, $$props) {
push();
const {
value,
name,
depth = 0,
collapsed = false,
displayObjectSize = true,
displayArrayKey = true,
collapseStringsAfterLength = 50,
displayDataTypes = false,
sortKeys = false,
indentWidth = 12,
isLast = false,
isRoot = false,
inlineShortContainers = false,
inline = false,
inlineRoot = false
} = $$props;
const keys = Object.keys(value || {});
const hasItems = keys.length > 0 || Object.keys(value).length > 0;
{
let inlineView = function($$payload2) {
const each_array = ensure_array_like(keys);
$$payload2.out += `<span class="json-brace svelte-1iuouau">{</span><!--[-->`;
for (let index = 0, $$length = each_array.length; index < $$length; index++) {
let key = each_array[index];
JsonProperty($$payload2, { name: key, displayArrayKey: false, isArrayIndex: false });
$$payload2.out += `<!---->`;
if (isExpandable(value[key])) {
$$payload2.out += "<!--[-->";
if (Array.isArray(value[key])) {
$$payload2.out += "<!--[-->";
JsonArray($$payload2, { value: value[key], inlineShortContainers, inline: true });
} else {
$$payload2.out += "<!--[!-->";
JsonObject($$payload2, { value: value[key], inlineShortContainers, inline: true });
}
$$payload2.out += `<!--]-->`;
} else {
$$payload2.out += "<!--[!-->";
JsonValue($$payload2, {
value: value[key],
displayDataTypes,
collapseStringsAfterLength
});
}
$$payload2.out += `<!--]-->`;
if (index < keys.length - 1) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="json-comma svelte-1iuouau">, </span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
}
$$payload2.out += `<!--]-->`;
if (!inlineRoot) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="json-brace svelte-1iuouau">}</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
}, collapsedView = function($$payload2) {
$$payload2.out += `<span class="json-brace svelte-1iuouau">{</span> `;
JsonMeta($$payload2, { value, displayObjectSize, displayArrayKey });
$$payload2.out += `<!----> <span class="json-ellipsis svelte-1iuouau">...</span> <span class="json-brace svelte-1iuouau">}</span>`;
}, expandedView = function($$payload2) {
const each_array_1 = ensure_array_like(keys);
$$payload2.out += `<div class="json-object-content svelte-1iuouau"><!--[-->`;
for (let index = 0, $$length = each_array_1.length; index < $$length; index++) {
let key = each_array_1[index];
$$payload2.out += `<div class="json-object-item svelte-1iuouau">`;
if (isExpandable(value[key])) {
$$payload2.out += "<!--[-->";
if (Array.isArray(value[key])) {
$$payload2.out += "<!--[-->";
JsonArray($$payload2, {
value: value[key],
name: key,
depth: depth + 1,
collapsed,
displayObjectSize,
displayArrayKey,
collapseStringsAfterLength,
displayDataTypes,
sortKeys,
indentWidth,
inlineShortContainers,
isLast: index === keys.length - 1
});
} else {
$$payload2.out += "<!--[!-->";
JsonObject($$payload2, {
value: value[key],
name: key,
depth: depth + 1,
collapsed,
displayObjectSize,
displayArrayKey,
collapseStringsAfterLength,
displayDataTypes,
sortKeys,
indentWidth,
inlineShortContainers,
isLast: index === keys.length - 1
});
}
$$payload2.out += `<!--]-->`;
} else {
$$payload2.out += "<!--[!-->";
$$payload2.out += `<div class="json-object-primitive svelte-1iuouau"${attr_style(`margin-left: ${stringify(indentWidth)}px`)}>`;
JsonProperty($$payload2, { name: key, displayArrayKey, isArrayIndex: false });
$$payload2.out += `<!---->`;
JsonValue($$payload2, {
value: value[key],
displayDataTypes,
collapseStringsAfterLength
});
$$payload2.out += `<!---->`;
if (index < keys.length - 1) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="json-comma svelte-1iuouau">,</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]--></div>`;
}
$$payload2.out += `<!--]--></div>`;
}
$$payload2.out += `<!--]--></div><div class="json-object-footer svelte-1iuouau"><span class="json-brace svelte-1iuouau">}</span> `;
if (!isLast && !isRoot) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="json-comma svelte-1iuouau">,</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]--></div>`;
}, emptyView = function($$payload2) {
$$payload2.out += `<span class="json-brace svelte-1iuouau">{}</span> `;
if (!isLast && !isRoot) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="json-comma svelte-1iuouau">,</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
};
ContainerWrapper($$payload, {
value,
name,
depth,
collapsed,
displayArrayKey,
collapseStringsAfterLength,
indentWidth,
isLast,
isRoot,
inlineShortContainers,
inline,
hasItems,
inlineView,
collapsedView,
expandedView,
emptyView
});
}
pop();
}
function JsonViewer($$payload, $$props) {
push();
const {
value,
theme = "light",
collapsed = false,
collapseStringsAfterLength = 50,
indentWidth = 12,
sortKeys = false,
displayDataTypes = false,
displayObjectSize = true,
displayArrayKey = false,
inlineShortContainers = false
} = $$props;
const jsonType = getJsonType(value);
const expandableValue = isExpandable(value);
$$payload.out += `<div${attr_class("json-viewer svelte-cnbzbd", void 0, {
"light": (
// Apply theme when it changes
theme === "light"
),
"dark": theme === "dark"
})}>`;
if (expandableValue) {
$$payload.out += "<!--[-->";
if (jsonType === "array") {
$$payload.out += "<!--[-->";
JsonArray($$payload, {
value,
depth: 0,
collapsed,
displayObjectSize,
displayArrayKey,
collapseStringsAfterLength,
displayDataTypes,
sortKeys,
indentWidth,
inlineShortContainers,
isRoot: true
});
} else {
$$payload.out += "<!--[!-->";
JsonObject($$payload, {
value,
depth: 0,
collapsed,
displayObjectSize,
displayArrayKey,
collapseStringsAfterLength,
displayDataTypes,
sortKeys,
indentWidth,
inlineShortContainers,
isRoot: true
});
}
$$payload.out += `<!--]-->`;
} else {
$$payload.out += "<!--[!-->";
JsonValue($$payload, { value, displayDataTypes, collapseStringsAfterLength });
}
$$payload.out += `<!--]--></div>`;
pop();
}
function toolMessage($$payload, message) {
$$payload.out += `<div class="space-y-2">`;
if (message.error) {
$$payload.out += "<!--[-->";
$$payload.out += `<div class="whitespace-pre-wrap leading-snug text-[0.9em] text-red-600 dark:text-red-400 overflow-x-auto">${escape_html(message.error.message)}</div>`;
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]--> `;
if (message.content) {
$$payload.out += "<!--[-->";
$$payload.out += `<div class="whitespace-pre-wrap font-mono text-[0.9em] leading-snug text-sm text-gray-900 dark:text-gray-100 overflow-x-auto">${escape_html(message.content)}</div>`;
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]--></div>`;
}
function apiFailureMessage($$payload, message) {
$$payload.out += `<div class="space-y-2"><div class="flex items-center gap-2 mb-2"><span class="badge badge-error badge-sm">API FAILURE</span> <span class="text-xs text-red-600 dark:text-red-400">${escape_html(message.error_category)}</span> `;
if (message.recoverable) {
$$payload.out += "<!--[-->";
$$payload.out += `<span class="text-xs text-orange-600 dark:text-orange-400">(recoverable)</span>`;
} else {
$$payload.out += "<!--[!-->";
}
$$payload.out += `<!--]--></div> <div class="font-mono text-sm bg-red-50 dark:bg-red-900/20 p-2 rounded border border-red-200 dark:border-red-700/50 text-gray-900 dark:text-red-100">${escape_html(message.error_message)}</div></div>`;
}
function MessageCard($$payload, $$props) {
push();
var $$store_subs;
hljs.registerLanguage("python", python);
hljs.registerLanguage("bash", bash);
hljs.registerLanguage("plaintext", plaintext);
let {
message,
messageIndex,
isOpen,
isVisible = true
} = $$props;
function extractTextFromContent(content) {
if (typeof content === "string") {
return content;
}
if (Array.isArray(content)) {
const textParts = [];
for (const item of content) {
if (typeof item === "string") {
textParts.push(item);
} else if (typeof item === "object" && item !== null && item.type === "text") {
textParts.push(item.text || "");
}
}
return textParts.join("");
}
return null;
}
function hashStringToColor(str) {
if (!str) return "badge-neutral";
let hash = 42;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
const colors = [
"badge-primary",
"badge-secondary",
"badge-accent",
"badge-info",
"badge-success",
"badge-warning"
];
const colorIndex = Math.abs(hash) % colors.length;
return colors[colorIndex];
}
const md = markdownit({
html: true,
linkify: true,
breaks: true,
highlight(str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(str, { language: lang, ignoreIllegals: true }).value;
} catch {
}
}
return "";
}
});
function renderMarkdown(content) {
try {
return md.render(content || "");
} catch {
return content || "";
}
}
function leftEdgeClassForAssistant(message2) {
if (message2.type !== "assistant") return "";
const name = getMessageSourceLabel(message2) || "";
const badge = hashStringToColor(name);
const badgeToBorder = {
// DaisyUI defaults: primary≈blue, secondary≈violet, accent≈pink, info≈sky, success≈emerald/green, warning≈amber, neutral≈zinc
"badge-primary": "border-l border-blue-500",
"badge-secondary": "border-l border-violet-500",
"badge-accent": "border-l border-pink-500",
"badge-info": "border-l border-sky-500",
"badge-success": "border-l border-emerald-500",
"badge-warning": "border-l border-amber-500",
"badge-neutral": "border-l border-zinc-500"
};
return badgeToBorder[badge] || "border-l border-zinc-500";
}
function toYaml(value, indent = 0) {
const pad = (n) => " ".repeat(n);
const isPlainObject = (v) => Object.prototype.toString.call(v) === "[object Object]";
if (value === null || value === void 0) return "null";
if (typeof value === "string") {
let unescaped;
try {
unescaped = JSON.parse('"' + value.replace(/"/g, '\\"') + '"');
} catch {
unescaped = value;
}
if (unescaped.includes("\n")) {
const lines = unescaped.split("\n");
return lines.map((line) => pad(indent + 1) + line).join("\n");
}
const needsQuotes = /^[\s]*$|^[>|]|[:#\[\]{}*&!%@`]/.test(unescaped) || unescaped.startsWith("-") || unescaped.match(/^\d/) || ["true", "false", "null", "yes", "no", "on", "off"].includes(unescaped.toLowerCase());
return needsQuotes ? JSON.stringify(unescaped) : unescaped;
}
if (typeof value === "number" || typeof value === "boolean") return String(value);
if (Array.isArray(value)) {
if (value.length === 0) return "[]";
return value.map((item) => {
const rendered = toYaml(item, indent + 1);
if (rendered.includes("\n")) {
const lines = rendered.split("\n").map((l) => pad(indent + 1) + l).join("\n");
return `${pad(indent)}-
${lines}`;
}
return `${pad(indent)}- ${rendered}`;
}).join("\n");
}
if (isPlainObject(value)) {
const entries = Object.entries(value);
if (entries.length === 0) return "{}";
return entries.map(([k, v]) => {
const rendered = toYaml(v, indent + 1);
if (rendered.includes("\n")) {
const lines = rendered.split("\n").map((l) => pad(indent + 1) + l).join("\n");
return `${pad(indent)}${k}:
${lines}`;
}
return `${pad(indent)}${k}: ${rendered}`;
}).join("\n");
}
try {
return JSON.stringify(value);
} catch {
return String(value);
}
}
function getMessageSourceLabel(message2) {
const meta = message2.metadata;
if (meta && typeof meta.source === "string" && meta.source) {
return meta.source;
}
if ("name" in message2 && typeof message2.name === "string") {
return message2.name;
}
if (message2.type === "api_failure") return "API Error";
if (message2.type === "info") return "Info";
return null;
}
let collapsedToolCallById = {};
function isToolCallCollapsed(id) {
return !!collapsedToolCallById[id];
}
function messageHeader($$payload2) {
$$payload2.out += `<div class="w-full rounded-t-lg"><div role="button" tabindex="0"${attr("aria-expanded", isOpen)} class="w-full p-2 flex items-center justify-between hover:bg-black/5 dark:hover:bg-white/5 rounded-t-lg">`;
headerBadges($$payload2);
$$payload2.out += `<!----> <div class="flex items-center gap-2">`;
expandButton($$payload2);
$$payload2.out += `<!----> `;
copyMenu($$payload2);
$$payload2.out += `<!----></div></div></div>`;
}
function headerBadges($$payload2) {
const messageName = getMessageSourceLabel(message);
$$payload2.out += `<div class="flex items-center gap-2"><span class="badge badge-outline badge-xs font-mono">${escape_html(message.type.toUpperCase())}</span> <span class="badge badge-outline badge-xs">Message ${escape_html(messageIndex + 1)}</span> `;
if (messageName) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span${attr_class(`badge ${stringify(hashStringToColor(messageName))} badge-sm`)}>${escape_html(messageName.toUpperCase())}</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]--> `;
if (message.type === "tool") {
$$payload2.out += "<!--[-->";
if (message.function) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="text-xs font-mono">${escape_html(message.function)}</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]--> `;
if (message.error) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="badge badge-error badge-xs">ERROR</span>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]--></div>`;
}
function expandButton($$payload2) {
$$payload2.out += `<svg${attr_class(`w-4 h-4 transition-transform ${stringify(isOpen ? "" : "rotate-180")}`)} fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg>`;
}
function copyMenu($$payload2) {
$$payload2.out += `<div class="dropdown dropdown-end ml-2" tabindex="0" role="menu" aria-label="Message actions"><button type="button" class="btn btn-ghost btn-xs btn-square" aria-label="Message menu"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 12.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 18.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z"></path></svg></button> <ul class="dropdown-content menu bg-base-100 rounded-box z-[1] w-64 p-2 shadow-lg border border-base-300"><li><button class="text-left"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5"></path></svg> ${escape_html("Show raw JSON")}</button></li> <div class="divider my-1"></div> <li><button class="text-left"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75A1.125 1.125 0 0 1 3.75 20.625V9.375c0-.621.504-1.125 1.125-1.125H7.5a9.75 9.75 0 0 1 1.5.124m5.25 10.501v-3.375c0-.621.504-1.125 1.125-1.125h3.375c.621 0 1.125.504 1.125 1.125v3.375c0 .621-.504 1.125-1.125 1.125h-3.375A1.125 1.125 0 0 1 15.75 17.25Z"></path></svg> Copy history up to here (${escape_html(messageIndex + 1)} messages)</button></li> <li><button class="text-left"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 0 1 0 3.75H5.625a1.875 1.875 0 0 1 0-3.75Z"></path></svg> Copy events up to here</button></li> <li><button class="text-left"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 7.5V6.108c0-1.135.845-2.098 1.976-2.192.373-.03.748-.057 1.124-.08M15.75 18H18a2.25 2.25 0 0 0 2.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 0 0-1.123-.08M15.75 18.75v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5A3.375 3.375 0 0 0 6.375 7.5H5.25m11.9-3.664A2.251 2.251 0 0 0 15 2.25h-1.5a2.251 2.251 0 0 0-2.15 1.586m5.8 0c.065.21.1.433.1.664v.75h-6V4.5c0-.231.035-.454.1-.664M6.75 7.5H4.875c-.621 0-1.125.504-1.125 1.125v12c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V16.5a9 9 0 0 0-9-9Z"></path></svg> Copy this message only</button></li> <li><button class="text-left"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4"><path stroke-linecap="round" stroke-linejoin="round" d="M11.42 15.17L17.25 21A2.652 2.652 0 0 0 21 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 1 1-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 0 0 4.486-6.336l-3.276 3.277a3.004 3.004 0 0 1-2.25-2.25l3.276-3.276a4.5 4.5 0 0 0-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z"></path></svg> Copy tools created up to here</button></li></ul></div>`;
}
function messageContent($$payload2) {
$$payload2.out += `<div class="px-3 pb-3 border-t border-base-300 dark:border-white/10">`;
{
$$payload2.out += "<!--[!-->";
$$payload2.out += `<div class="text-sm leading-relaxed mt-2 message-content text-gray-900 dark:text-gray-100 overflow-x-auto">`;
if (message.type === "system") {
$$payload2.out += "<!--[-->";
systemMessage($$payload2, message);
} else if (message.type === "user") {
$$payload2.out += "<!--[1-->";
userMessage($$payload2, message);
} else if (message.type === "assistant") {
$$payload2.out += "<!--[2-->";
assistantMessage($$payload2, message);
} else if (message.type === "tool") {
$$payload2.out += "<!--[3-->";
toolMessage($$payload2, message);
} else if (message.type === "api_failure") {
$$payload2.out += "<!--[4-->";
apiFailureMessage($$payload2, message);
} else if (message.type === "info") {
$$payload2.out += "<!--[5-->";
infoMessage($$payload2, message);
} else {
$$payload2.out += "<!--[!-->";
$$payload2.out += `<div class="text-red-500">Unknown message type: ${escape_html(JSON.stringify(message))}</div>`;
}
$$payload2.out += `<!--]--></div>`;
}
$$payload2.out += `<!--]--></div>`;
}
function systemMessage($$payload2, message2) {
const textContent = extractTextFromContent(message2.content);
if (textContent !== null) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="text-gray-900 dark:text-gray-100 whitespace-pre-wrap">${escape_html(textContent)}</span>`;
} else {
$$payload2.out += "<!--[!-->";
JsonViewer($$payload2, {
value: message2.content,
theme: store_get($$store_subs ??= {}, "$themeString", themeString),
inlineShortContainers: 80
});
}
$$payload2.out += `<!--]-->`;
}
function userMessage($$payload2, message2) {
const textContent = extractTextFromContent(message2.content);
if (textContent !== null) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="text-gray-900 dark:text-gray-100 whitespace-pre-wrap">${escape_html(textContent)}</span>`;
} else {
$$payload2.out += "<!--[!-->";
JsonViewer($$payload2, {
value: message2.content,
theme: store_get($$store_subs ??= {}, "$themeString", themeString),
inlineShortContainers: 80
});
}
$$payload2.out += `<!--]-->`;
}
function assistantMessage($$payload2, message2) {
const textContent = extractTextFromContent(message2.content);
if (message2.reasoning) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<div class="mt-3 space-y-2"><i>${escape_html(message2.reasoning.trim())}</i></div><div class="my-2 border-t border-black/10 dark:border-white/10"></div>`;
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
if (textContent !== null) {
$$payload2.out += "<!--[-->";
$$payload2.out += `<span class="text-gray-900 dark:text-gray-100 whitespace-pre-wrap">${escape_html(textContent)}</span>`;
} else {
$$payload2.out += "<!--[!-->";
JsonViewer($$payload2, {
value: message2.content,
theme: store_get($$store_subs ??= {}, "$themeString", themeString),
inlineShortContainers: 80
});
}
$$payload2.out += `<!--]-->`;
if (message2.tool_calls && message2.tool_calls.length > 0) {
$$payload2.out += "<!--[-->";
toolCalls($$payload2, message2.tool_calls);
} else {
$$payload2.out += "<!--[!-->";
}
$$payload2.out += `<!--]-->`;
}
function infoMessage($$payload2, message2) {
$$payload2.out += `<div class