import { e as ensure_array_like, c as attr_style } from './index2-BpEbpEAf.js';
import { o as onDestroy } from './index-server-BI-Ep7Rw.js';
import { p as page } from './index3-Cp9d5nmD.js';
import { B as Button } from './Button-qfXYs6Cv.js';
import { g as getPersistedTelescopeSourceId } from './source-selection-hOQHZ-fn.js';
import { X as escape_html } from './context-B0vutJuA.js';
import './state.svelte-DbqYHGBV.js';
import './utils-KcIDVAAe.js';
import './index-BSa6KzSq.js';
import 'zod';

function _page($$renderer, $$props) {
  $$renderer.component(($$renderer2) => {
    const TIMELINE_RULER_HEIGHT_PX = 32;
    const TIMELINE_ROW_HEIGHT_PX = 40;
    const WAVEFORM_ROW_HEIGHT_PX = 160;
    const TIMELINE_SCALE_HEIGHT_PX = 32;
    const BOSUN_COLORS = {
      blue: "#569cd6",
      green: "#93FF6D",
      greenDark: "#00B58A",
      orange: "#ce9178",
      purple: "#81147D",
      red: "#FF6666",
      gray: "#767676"
    };
    const RAW_VAD_SPAN_NAMES = /* @__PURE__ */ new Set(["vad.start_of_speech", "vad.end_of_speech"]);
    const RAW_STT_SPAN_NAMES = /* @__PURE__ */ new Set([
      "stt.start_of_speech",
      "stt.interim_transcript",
      "stt.preflight_transcript",
      "stt.final_transcript"
    ]);
    const HIDDEN_LATENCY_SPAN_NAMES = /* @__PURE__ */ new Set([
      "vad.user_speech",
      "vad.endpointing",
      "vad.trailing_silence",
      "stt.endpointing",
      "stt.transcription",
      "stt.turn_commit",
      "stt.until_turn_committed",
      "stt.transcript_to_speech_created",
      "stt.transcript_to_llm_request",
      "stt.transcript_trigger_selected",
      "stt.speech_end_to_trigger_transcript",
      "stt.speech_end_to_final_transcript",
      "stt.trigger_transcript_to_reply_trigger",
      "stt.final_transcript_to_commit"
    ]);
    const ROW_DEFS = [
      { label: "VAD", names: [...RAW_VAD_SPAN_NAMES] },
      { label: "STT", names: [...RAW_STT_SPAN_NAMES] },
      { label: "Trigger", component: "trigger" },
      { label: "LLM", names: ["llm.request.ttft", "llm.streaming"] },
      {
        label: "LLM→TTS",
        names: ["llm_to_tts.buffering", "llm_to_tts.streaming"]
      },
      { label: "TTS generation", names: ["tts.generation"] },
      {
        label: "TTS→Audio",
        names: ["tts_to_audio.buffering", "tts_to_audio.streaming"]
      },
      { label: "LLM→Audio start", names: ["llm_to_audio.latency"] },
      { label: "Other" }
    ];
    const COLOR_BY_KEY = {
      "vad.user_speech": tintHex(BOSUN_COLORS.purple, 0.35),
      "vad.endpointing": tintHex(BOSUN_COLORS.purple, -0.15),
      "llm.request": tintHex(BOSUN_COLORS.blue, -0.25),
      "llm.streaming": BOSUN_COLORS.blue,
      "llm_to_tts.buffering": tintHex(BOSUN_COLORS.orange, -0.25),
      "llm_to_tts.streaming": BOSUN_COLORS.orange,
      "tts.generation": tintHex(BOSUN_COLORS.orange, 0.25),
      "tts_to_audio.buffering": tintHex(BOSUN_COLORS.purple, -0.25),
      "tts_to_audio.streaming": tintHex(BOSUN_COLORS.purple, 0.25),
      "llm_to_audio.ok": BOSUN_COLORS.green,
      "llm_to_audio.high": BOSUN_COLORS.red,
      trigger: BOSUN_COLORS.greenDark,
      stt: tintHex(BOSUN_COLORS.purple, 0.2),
      other: BOSUN_COLORS.gray
    };
    let sourceId = getPersistedTelescopeSourceId() ?? "";
    let traces = [];
    let recording = {
      alignmentOffsetMs: 0
    };
    let uploadInFlight = false;
    let playbackPositionMs = 0;
    let playbackPlaying = false;
    let viewportStartMs = null;
    let viewportEndMs = null;
    let fileInputEl = null;
    let playbackAudioContext = null;
    let playbackSourceNode = null;
    let playbackStartedAtMs = null;
    let playbackStartedAtContextTime = null;
    const conversationId = (() => page.params.conversationId)();
    function buildRecordingApiUrlFor(cid, nextSourceId) {
      const params = new URLSearchParams();
      if (nextSourceId) params.set("source", nextSourceId);
      const search = params.toString();
      return `/traces/api/latency/${encodeURIComponent(cid)}${search ? `?${search}` : ""}`;
    }
    (() => {
      if (!conversationId) return "";
      return buildRecordingApiUrlFor(conversationId, sourceId);
    })();
    const timelineOriginMs = (() => {
      let min = Number.POSITIVE_INFINITY;
      for (const trace of traces) {
        if (typeof trace.createdAt !== "number" || !Number.isFinite(trace.createdAt)) continue;
        min = Math.min(min, trace.createdAt);
      }
      return Number.isFinite(min) ? min : 0;
    })();
    const callSegments = (() => buildCallLatencySegments(traces))();
    const latencyTraceCount = (() => traces.filter((trace) => {
      const metrics = trace?.metrics;
      return !!metrics && typeof metrics === "object";
    }).length)();
    const timelineRows = (() => {
      const grouped = /* @__PURE__ */ new Map();
      for (const segment of callSegments) {
        const items = grouped.get(segment.row);
        if (items) items.push(segment);
        else grouped.set(segment.row, [segment]);
      }
      return [...ROW_DEFS].reverse().map((row) => ({ label: row.label, segments: grouped.get(row.label) ?? [] })).filter((row) => row.segments.length > 0);
    })();
    const guideLines = (() => buildGuideLines())();
    const timelinePlotHeightPx = (() => TIMELINE_RULER_HEIGHT_PX + timelineRows.length * TIMELINE_ROW_HEIGHT_PX + WAVEFORM_ROW_HEIGHT_PX + TIMELINE_SCALE_HEIGHT_PX)();
    const playbackAbsoluteMs = (() => {
      return null;
    })();
    const playbackCursor = (() => {
      const absoluteMs = playbackAbsoluteMs;
      if (absoluteMs == null || viewportStartMs == null || viewportEndMs == null) return null;
      const durationMs = viewportEndMs - viewportStartMs;
      if (!Number.isFinite(durationMs) || durationMs <= 0) return null;
      if (absoluteMs < viewportStartMs || absoluteMs > viewportEndMs) return null;
      return {
        leftPercent: clamp((absoluteMs - viewportStartMs) / durationMs * 100, 0, 100),
        heightPx: timelinePlotHeightPx
      };
    })();
    const contentExtent = (() => {
      let min = Number.POSITIVE_INFINITY;
      let max = Number.NEGATIVE_INFINITY;
      for (const segment of callSegments) {
        min = Math.min(min, segment.start);
        max = Math.max(max, segment.end);
      }
      if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) return null;
      return { min, max };
    })();
    const viewportDurationMs = (() => {
      if (viewportStartMs == null || viewportEndMs == null) return null;
      const duration = viewportEndMs - viewportStartMs;
      return duration > 0 ? duration : null;
    })();
    const visibleTicks = (() => {
      if (viewportStartMs == null || viewportEndMs == null) return [];
      return buildTicks(viewportStartMs, viewportEndMs, timelineOriginMs);
    })();
    const visibleTickStepMs = (() => {
      if (viewportStartMs == null || viewportEndMs == null) return null;
      const durationMs = viewportEndMs - viewportStartMs;
      if (!Number.isFinite(durationMs) || durationMs <= 0) return null;
      return chooseTickStep(durationMs);
    })();
    onDestroy(() => {
      stopPlayback(false);
      void closePlaybackAudioContext();
    });
    function normalizeNonEmptyString(value) {
      if (typeof value !== "string") return null;
      const trimmed = value.trim();
      return trimmed ? trimmed : null;
    }
    function ms(value) {
      return typeof value === "number" && Number.isFinite(value) ? value : null;
    }
    function clampByte(value) {
      return Math.max(0, Math.min(255, Math.round(value)));
    }
    function clamp(value, min, max) {
      return Math.min(max, Math.max(min, value));
    }
    function tintHex(hex, amount) {
      const raw = (hex ?? "").trim().replace(/^#/, "");
      const expanded = raw.length === 3 ? raw.split("").map((part) => `${part}${part}`).join("") : raw;
      if (expanded.length !== 6 || !/^[0-9a-fA-F]+$/.test(expanded)) return hex;
      const r = parseInt(expanded.slice(0, 2), 16);
      const g = parseInt(expanded.slice(2, 4), 16);
      const b = parseInt(expanded.slice(4, 6), 16);
      const t = Math.abs(Math.max(-1, Math.min(1, amount)));
      const target = amount >= 0 ? 255 : 0;
      return `#${clampByte(r + (target - r) * t).toString(16).padStart(2, "0")}${clampByte(g + (target - g) * t).toString(16).padStart(2, "0")}${clampByte(b + (target - b) * t).toString(16).padStart(2, "0")}`;
    }
    function fmtSignedMs(value) {
      if (!Number.isFinite(value)) return "0 ms";
      const rounded = Math.round(value);
      if (rounded === 0) return "0 ms";
      return `${rounded > 0 ? "+" : ""}${rounded} ms`;
    }
    function formatRelativeTime(offsetMs) {
      if (!Number.isFinite(offsetMs)) return "—";
      const sign = offsetMs < 0 ? "-" : "";
      const abs = Math.abs(offsetMs);
      if (abs < 1e4) return `${sign}${(abs / 1e3).toFixed(1)}s`;
      if (abs < 6e4) return `${sign}${(abs / 1e3).toFixed(2)}s`;
      const totalSeconds = abs / 1e3;
      const minutes = Math.floor(totalSeconds / 60);
      const seconds = totalSeconds - minutes * 60;
      return `${sign}${minutes}:${seconds.toFixed(1).padStart(4, "0")}`;
    }
    function formatScaleStep(valueMs) {
      if (valueMs == null || !Number.isFinite(valueMs) || valueMs <= 0) return "—";
      if (valueMs < 1e3) return `${Math.round(valueMs)} ms`;
      if (valueMs < 1e4) return `${Math.round(valueMs)} ms (${(valueMs / 1e3).toFixed(1)} s)`;
      return `${Math.round(valueMs)} ms (${(valueMs / 1e3).toFixed(2)} s)`;
    }
    function spanAttribute(span, key) {
      const attributes = span?.attributes;
      if (!attributes || typeof attributes !== "object") return null;
      return attributes[`bosun.${key}`] ?? attributes[`anyvan.${key}`] ?? null;
    }
    function spanStringAttribute(span, key) {
      return normalizeNonEmptyString(spanAttribute(span, key));
    }
    function spanComponent(span) {
      return spanStringAttribute(span, "component");
    }
    function shouldIgnoreLatencySpan(span) {
      const name = normalizeNonEmptyString(span?.name);
      if (!name) return false;
      if (HIDDEN_LATENCY_SPAN_NAMES.has(name)) return true;
      if (name.startsWith("vad.") && !RAW_VAD_SPAN_NAMES.has(name)) return true;
      if (name.startsWith("stt.") && !RAW_STT_SPAN_NAMES.has(name)) return true;
      return false;
    }
    function spanLabel(span) {
      const label = spanStringAttribute(span, "label");
      if (label) return label;
      if (typeof span?.name === "string" && span.name) return span.name;
      return "span";
    }
    function describeTrace(trace) {
      const time = typeof trace.createdAt === "number" && Number.isFinite(trace.createdAt) ? new Date(trace.createdAt).toLocaleTimeString() : "unknown time";
      if (trace.traceType === "CONVERSATIONAL") {
        return `Conversational • ${trace.eventName} • ${time}`;
      }
      if (trace.traceType === "STEP") {
        return `Step • ${trace.name ?? "unknown"} • ${time}`;
      }
      return `API • ${trace.operationName} • ${time}`;
    }
    function spanColorKey(span) {
      const name = typeof span?.name === "string" ? span.name : "";
      if (name === "vad.endpointing") return "vad.endpointing";
      if (name.startsWith("vad.")) return "vad.user_speech";
      if (name === "llm.request.ttft") return "llm.request";
      if (name === "llm.streaming") return "llm.streaming";
      if (name === "llm_to_tts.buffering") return "llm_to_tts.buffering";
      if (name === "llm_to_tts.streaming") return "llm_to_tts.streaming";
      if (name === "tts.generation") return "tts.generation";
      if (name === "tts_to_audio.buffering") return "tts_to_audio.buffering";
      if (name === "tts_to_audio.streaming") return "tts_to_audio.streaming";
      if (name.startsWith("stt.")) return "stt";
      if (name.startsWith("trigger.")) return "trigger";
      const component = spanComponent(span);
      if (component === "vad") return "vad.user_speech";
      if (component === "stt") return "stt";
      if (component === "trigger") return "trigger";
      return "other";
    }
    function makePointSpan(name, atMs, attributes) {
      return {
        name,
        startTimeUnixMs: atMs,
        endTimeUnixMs: atMs,
        durationMs: 0,
        attributes
      };
    }
    function buildLatencyDebugSpans(metrics) {
      const debug = metrics?.debug ?? {};
      const ttsAudioStartAtMs = ms(metrics?.timing?.ttsAudioStartAtMs);
      const isPreResponseEvent = (atMs) => ttsAudioStartAtMs == null || atMs <= ttsAudioStartAtMs;
      const built = [];
      let hasRawVad = false;
      let hasRawStt = false;
      const vadEvents = Array.isArray(debug?.vadEvents?.events) ? debug.vadEvents.events : [];
      vadEvents.forEach((event, index) => {
        const atMs = ms(event?.atMs);
        const type = normalizeNonEmptyString(event?.type);
        if (atMs == null || !isPreResponseEvent(atMs) || type !== "start_of_speech" && type !== "end_of_speech") {
          return;
        }
        hasRawVad = true;
        built.push(makePointSpan(`vad.${type}`, atMs, {
          "anyvan.component": "vad",
          "anyvan.label": type === "start_of_speech" ? "VAD activated" : "VAD deactivated",
          "anyvan.description": type === "start_of_speech" ? "Raw VAD START_OF_SPEECH event" : "Raw VAD END_OF_SPEECH event",
          "anyvan.eventIndex": index,
          "anyvan.samplesIndex": typeof event?.samplesIndex === "number" ? event.samplesIndex : void 0,
          "anyvan.speechDurationMs": typeof event?.speechDurationMs === "number" ? event.speechDurationMs : void 0,
          "anyvan.silenceDurationMs": typeof event?.silenceDurationMs === "number" ? event.silenceDurationMs : void 0
        }));
      });
      const sttEvents = Array.isArray(debug?.sttEvents?.events) ? debug.sttEvents.events : [];
      sttEvents.forEach((event, index) => {
        const atMs = ms(event?.atMs);
        const type = normalizeNonEmptyString(event?.type);
        if (atMs == null || !isPreResponseEvent(atMs) || type !== "start_of_speech" && type !== "interim_transcript" && type !== "preflight_transcript" && type !== "final_transcript") {
          return;
        }
        hasRawStt = true;
        const label = type === "start_of_speech" ? "STT start of speech" : type === "interim_transcript" ? "STT interim transcript" : type === "preflight_transcript" ? "STT preflight transcript" : "STT final transcript";
        built.push(makePointSpan(`stt.${type}`, atMs, {
          "anyvan.component": "stt",
          "anyvan.label": label,
          "anyvan.description": `Raw STT ${type.toUpperCase()} event`,
          "anyvan.eventIndex": index,
          "anyvan.requestId": typeof event?.requestId === "string" ? event.requestId : void 0,
          "anyvan.text": normalizeNonEmptyString(event?.text),
          "anyvan.language": typeof event?.language === "string" || event?.language === null ? event.language : void 0,
          "anyvan.speakerId": typeof event?.speakerId === "string" || event?.speakerId === null ? event.speakerId : void 0
        }));
      });
      return { spans: built, hasRawVad, hasRawStt };
    }
    function buildSegmentsForMetrics(trace, metrics, prefix) {
      const debugSpans = buildLatencyDebugSpans(metrics);
      const spans = Array.isArray(metrics?.spans) ? metrics.spans.filter((span) => {
        const name = normalizeNonEmptyString(span?.name);
        if (name?.startsWith("vad.") && debugSpans.hasRawVad) return false;
        if (name?.startsWith("stt.") && debugSpans.hasRawStt) return false;
        return !shouldIgnoreLatencySpan(span);
      }) : [];
      const summary = metrics?.summary ?? {};
      const timing = metrics?.timing ?? {};
      const indexed = [...spans, ...debugSpans.spans].map((span, index) => ({ span, index }));
      const used = /* @__PURE__ */ new Set();
      const groupedRows = ROW_DEFS.filter((row) => row.label !== "LLM→Audio start" && row.label !== "Other").map((row) => {
        const matches = indexed.filter(({ span, index }) => {
          if (used.has(index)) return false;
          const name = typeof span?.name === "string" ? span.name : "";
          const component = spanComponent(span);
          const nameMatch = Array.isArray(row.names) && name !== "" && row.names.includes(name);
          const componentMatch = typeof row.component === "string" && component === row.component;
          return nameMatch || componentMatch;
        }).sort((a, b) => (ms(a.span.startTimeUnixMs) ?? 0) - (ms(b.span.startTimeUnixMs) ?? 0));
        for (const match of matches) used.add(match.index);
        return { label: row.label, spans: matches.map((match) => match.span) };
      }).filter((row) => row.spans.length > 0);
      const other = indexed.filter(({ index }) => !used.has(index)).map((entry) => entry.span).sort((a, b) => (ms(a.startTimeUnixMs) ?? 0) - (ms(b.startTimeUnixMs) ?? 0));
      if (other.length) groupedRows.push({ label: "Other", spans: other });
      const built = [];
      for (const row of groupedRows) {
        row.spans.forEach((span, index) => {
          const start = ms(span.startTimeUnixMs);
          const end = ms(span.endTimeUnixMs);
          if (start == null || end == null || end < start) return;
          built.push({
            id: `${prefix}:${row.label}:${span.name ?? "span"}:${start}:${end}:${index}`,
            row: row.label,
            start,
            end,
            durationMs: Math.max(0, end - start),
            colorKey: spanColorKey(span),
            label: spanLabel(span),
            description: spanStringAttribute(span, "description"),
            explain: spanStringAttribute(span, "explain"),
            text: spanStringAttribute(span, "text"),
            traceLabel: describeTrace(trace)
          });
        });
      }
      const llmRequestStartAtMs = ms(timing?.llmRequestStartAtMs);
      const ttsAudioStartAtMs = ms(timing?.ttsAudioStartAtMs);
      if (llmRequestStartAtMs != null && ttsAudioStartAtMs != null && ttsAudioStartAtMs >= llmRequestStartAtMs) {
        const durationMs = ttsAudioStartAtMs - llmRequestStartAtMs;
        built.push({
          id: `${prefix}:llm_to_audio:${llmRequestStartAtMs}:${ttsAudioStartAtMs}`,
          row: "LLM→Audio start",
          start: llmRequestStartAtMs,
          end: ttsAudioStartAtMs,
          durationMs,
          colorKey: durationMs > 800 || Boolean(summary?.problematic) ? "llm_to_audio.high" : "llm_to_audio.ok",
          label: "LLM→Audio start",
          description: "LLM request start → agent speaking (first audio frame)",
          explain: durationMs > 800 || Boolean(summary?.problematic) ? "High latency (>800ms) or problematic timing" : "OK",
          text: null,
          traceLabel: describeTrace(trace)
        });
      }
      return built;
    }
    function buildCallLatencySegments(input) {
      const built = [];
      input.forEach((trace, index) => {
        const metrics = trace?.metrics;
        if (!metrics || typeof metrics !== "object") return;
        const key = normalizeNonEmptyString(trace?.traceId) ?? `${trace.traceType}:${trace.createdAt}:${index}`;
        built.push(...buildSegmentsForMetrics(trace, metrics, key));
      });
      return built.sort((a, b) => a.start - b.start || a.end - b.end || a.row.localeCompare(b.row));
    }
    function segmentStyle(segment) {
      if (viewportStartMs == null || viewportEndMs == null) return null;
      const duration = viewportEndMs - viewportStartMs;
      if (!Number.isFinite(duration) || duration <= 0) return null;
      const fill = COLOR_BY_KEY[segment.colorKey] ?? BOSUN_COLORS.gray;
      const border = tintHex(fill, -0.25);
      if (segment.end === segment.start) {
        if (segment.start < viewportStartMs || segment.start > viewportEndMs) return null;
        const pointPercent = clamp((segment.start - viewportStartMs) / duration * 100, 0, 100);
        return `left:calc(${pointPercent}% - 2px);width:4px;background:${fill};border-color:${border};`;
      }
      const visibleStart = Math.max(segment.start, viewportStartMs);
      const visibleEnd = Math.min(segment.end, viewportEndMs);
      if (visibleEnd < visibleStart) return null;
      const leftPercent = (visibleStart - viewportStartMs) / duration * 100;
      const widthPercent = (visibleEnd - visibleStart) / duration * 100;
      return `left:${leftPercent}%;width:max(${widthPercent}%,2px);background:${fill};border-color:${border};`;
    }
    function guideLineStyle(guide) {
      return `left:${guide.leftPercent}%;top:${guide.topPx}px;height:${guide.heightPx}px;border-color:${guide.color};opacity:${guide.opacity};`;
    }
    function buildGuideLines(rows) {
      return [];
    }
    function chooseTickStep(durationMs) {
      const approx = durationMs / 7;
      const magnitude = 10 ** Math.floor(Math.log10(Math.max(1, approx)));
      const normalized = approx / magnitude;
      if (normalized <= 1) return 1 * magnitude;
      if (normalized <= 2) return 2 * magnitude;
      if (normalized <= 5) return 5 * magnitude;
      return 10 * magnitude;
    }
    function buildTicks(startMs, endMs, originMs) {
      const durationMs = endMs - startMs;
      if (!Number.isFinite(durationMs) || durationMs <= 0) return [];
      const stepMs = chooseTickStep(durationMs);
      const firstTick = Math.ceil((startMs - originMs) / stepMs) * stepMs + originMs;
      const ticks = [];
      for (let tick = firstTick; tick <= endMs; tick += stepMs) {
        const leftPercent = (tick - startMs) / durationMs * 100;
        ticks.push({
          time: tick,
          leftPercent,
          label: formatRelativeTime(tick - originMs)
        });
      }
      return ticks;
    }
    async function closePlaybackAudioContext() {
      const context = playbackAudioContext;
      playbackAudioContext = null;
      if (!context) return;
      await context.close().catch(() => {
      });
    }
    function getPlaybackPositionMs() {
      const durationMs = getRecordingDurationMs();
      if (playbackPlaying && playbackAudioContext && playbackStartedAtMs != null && playbackStartedAtContextTime != null) {
        const elapsedMs = (playbackAudioContext.currentTime - playbackStartedAtContextTime) * 1e3;
        const currentMs = playbackStartedAtMs + Math.max(0, elapsedMs);
        return durationMs > 0 ? clamp(currentMs, 0, durationMs) : Math.max(0, currentMs);
      }
      return durationMs > 0 ? clamp(playbackPositionMs, 0, durationMs) : Math.max(0, playbackPositionMs);
    }
    function stopPlayback(preservePosition = true) {
      if (preservePosition) {
        playbackPositionMs = getPlaybackPositionMs();
      }
      const source = playbackSourceNode;
      playbackSourceNode = null;
      playbackStartedAtMs = null;
      playbackStartedAtContextTime = null;
      playbackPlaying = false;
      if (!source) return;
      source.onended = null;
      try {
        source.stop();
      } catch {
      }
      source.disconnect();
    }
    function getRecordingDurationMs() {
      return 0;
    }
    async function togglePlayback() {
      return;
    }
    function fitViewport(markDirty = true) {
      const extent = contentExtent;
      if (!extent) return;
      const paddingMs = Math.max(500, (extent.max - extent.min) * 0.05);
      viewportStartMs = extent.min - paddingMs;
      viewportEndMs = extent.max + paddingMs;
    }
    function clampViewport(startMs, endMs) {
      const extent = contentExtent;
      if (!extent) return { startMs, endMs };
      const durationMs = endMs - startMs;
      if (durationMs <= 0) return { startMs, endMs };
      const marginMs = Math.max(1e3, durationMs * 0.25);
      const minAllowed = extent.min - marginMs;
      const maxAllowed = extent.max + marginMs;
      if (durationMs >= maxAllowed - minAllowed) {
        return { startMs: minAllowed, endMs: maxAllowed };
      }
      if (startMs < minAllowed) {
        endMs += minAllowed - startMs;
        startMs = minAllowed;
      }
      if (endMs > maxAllowed) {
        startMs -= endMs - maxAllowed;
        endMs = maxAllowed;
      }
      return { startMs, endMs };
    }
    function setViewport(startMs, endMs) {
      if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs <= startMs) return;
      const next = clampViewport(startMs, endMs);
      viewportStartMs = next.startMs;
      viewportEndMs = next.endMs;
    }
    function zoomViewport(factor, focusRatio = 0.5) {
      if (viewportStartMs == null || viewportEndMs == null) return;
      const currentDuration = viewportEndMs - viewportStartMs;
      if (!Number.isFinite(currentDuration) || currentDuration <= 0) return;
      const extent = contentExtent;
      const contentDuration = extent ? extent.max - extent.min : currentDuration;
      const minDuration = Math.min(100, currentDuration);
      const maxDuration = Math.max(contentDuration + Math.max(2e3, contentDuration * 0.1), 5e3);
      const nextDuration = clamp(currentDuration * factor, minDuration, maxDuration);
      const nextFocusRatio = clamp(focusRatio, 0, 1);
      const focusMs = viewportStartMs + currentDuration * nextFocusRatio;
      const nextStartMs = focusMs - nextDuration * nextFocusRatio;
      setViewport(nextStartMs, nextStartMs + nextDuration);
    }
    async function resetAlignment() {
      return;
    }
    $$renderer2.push(`<div class="flex h-full min-h-0 flex-col bg-rc-gray-700"><div class="flex-none border-b border-rc-gray-300/40 p-4"><div class="flex flex-wrap items-start justify-between gap-3"><div><h2 class="text-lg font-semibold text-rc-gray-100">Latency `);
    if (conversationId) {
      $$renderer2.push("<!--[-->");
      $$renderer2.push(`<span class="text-rc-gray-100">for conversation Id:</span> <span class="text-rc-green-200">${escape_html(conversationId)}</span>`);
    } else {
      $$renderer2.push("<!--[!-->");
    }
    $$renderer2.push(`<!--]--></h2> <div class="mt-2 flex flex-wrap gap-x-5 gap-y-1 text-xs text-rc-gray-300"><div><span class="text-rc-gray-400">Latency traces:</span> <span class="font-mono text-rc-gray-100">${escape_html(callSegments.length)}</span></div> <div><span class="text-rc-gray-400">Traces with metrics:</span> <span class="font-mono text-rc-gray-100">${escape_html(latencyTraceCount)}</span></div> <div><span class="text-rc-gray-400">Recording offset:</span> <span class="font-mono text-rc-gray-100">${escape_html(fmtSignedMs(recording.alignmentOffsetMs))}</span></div> `);
    {
      $$renderer2.push("<!--[!-->");
    }
    $$renderer2.push(`<!--]--> `);
    if (viewportStartMs != null && viewportEndMs != null) {
      $$renderer2.push("<!--[-->");
      $$renderer2.push(`<div><span class="text-rc-gray-400">Viewport:</span> <span class="font-mono text-rc-gray-100">${escape_html(formatRelativeTime(viewportStartMs - timelineOriginMs))} → ${escape_html(formatRelativeTime(viewportEndMs - timelineOriginMs))}</span></div>`);
    } else {
      $$renderer2.push("<!--[!-->");
    }
    $$renderer2.push(`<!--]--></div></div> <div class="flex flex-wrap items-center gap-2"><input type="file" accept=".wav,audio/wav,audio/x-wav" class="hidden"/> `);
    Button($$renderer2, {
      variant: "secondary",
      size: "small",
      disabled: uploadInFlight,
      onclick: () => fileInputEl?.click(),
      children: ($$renderer3) => {
        $$renderer3.push(`<!---->${escape_html("Upload WAV")}`);
      },
      $$slots: { default: true }
    });
    $$renderer2.push(`<!----> `);
    Button($$renderer2, {
      variant: "secondary",
      size: "small",
      disabled: true,
      onclick: () => void togglePlayback(),
      children: ($$renderer3) => {
        $$renderer3.push(`<!---->${escape_html(playbackPlaying ? "Pause" : "Play")}`);
      },
      $$slots: { default: true }
    });
    $$renderer2.push(`<!----> `);
    Button($$renderer2, {
      variant: "secondary",
      size: "small",
      disabled: viewportDurationMs == null,
      onclick: () => zoomViewport(0.8),
      children: ($$renderer3) => {
        $$renderer3.push(`<!---->Zoom in`);
      },
      $$slots: { default: true }
    });
    $$renderer2.push(`<!----> `);
    Button($$renderer2, {
      variant: "secondary",
      size: "small",
      disabled: viewportDurationMs == null,
      onclick: () => zoomViewport(1.25),
      children: ($$renderer3) => {
        $$renderer3.push(`<!---->Zoom out`);
      },
      $$slots: { default: true }
    });
    $$renderer2.push(`<!----> `);
    Button($$renderer2, {
      variant: "secondary",
      size: "small",
      disabled: !contentExtent,
      onclick: () => fitViewport(true),
      children: ($$renderer3) => {
        $$renderer3.push(`<!---->Fit call`);
      },
      $$slots: { default: true }
    });
    $$renderer2.push(`<!----> `);
    Button($$renderer2, {
      variant: "secondary",
      size: "small",
      disabled: true,
      onclick: resetAlignment,
      children: ($$renderer3) => {
        $$renderer3.push(`<!---->Reset alignment`);
      },
      $$slots: { default: true }
    });
    $$renderer2.push(`<!----></div></div> `);
    {
      $$renderer2.push("<!--[!-->");
    }
    $$renderer2.push(`<!--]--> `);
    {
      $$renderer2.push("<!--[!-->");
    }
    $$renderer2.push(`<!--]--> `);
    {
      $$renderer2.push("<!--[!-->");
    }
    $$renderer2.push(`<!--]--> `);
    {
      $$renderer2.push("<!--[!-->");
    }
    $$renderer2.push(`<!--]--> `);
    {
      $$renderer2.push("<!--[!-->");
    }
    $$renderer2.push(`<!--]--></div> <div class="rc-scroll min-h-0 flex-1 overflow-auto p-4"><div class="flex flex-col gap-4"><div class="rounded-md border border-rc-gray-650 bg-rc-gray-900/60 p-3"><div class="flex flex-wrap items-start justify-between gap-3"><div><div class="text-sm font-semibold text-rc-gray-100">Recording</div> `);
    {
      $$renderer2.push("<!--[!-->");
      $$renderer2.push(`<div class="mt-1 text-sm text-rc-gray-300">Upload a WAV recording to overlay the call waveform and persist it under this
								project's \`data/\` folder.</div>`);
    }
    $$renderer2.push(`<!--]--></div> <div class="text-xs text-rc-gray-300"><div>Drag the latency chart to pan the viewport.</div> <div>Scroll anywhere in the timeline to zoom traces and waveform together.</div> <div>Click the waveform to move the playhead.</div> <div>Press Space to play or pause.</div> <div>Drag the waveform itself to align the audio against the traces.</div> `);
    {
      $$renderer2.push("<!--[!-->");
    }
    $$renderer2.push(`<!--]--></div></div></div> <div class="rounded-md border border-rc-gray-650 bg-rc-gray-900/60 p-3"><div class="mb-3 flex items-center justify-between gap-3"><div class="text-sm font-semibold text-rc-gray-100">Call timeline</div> <div class="text-xs text-rc-gray-400">Shared x-axis for traces and waveform</div></div> `);
    {
      $$renderer2.push("<!--[!-->");
      if (!callSegments.length) {
        $$renderer2.push("<!--[-->");
        $$renderer2.push(`<div class="text-sm text-rc-gray-300">No latency metrics were found on this call.</div>`);
      } else {
        $$renderer2.push("<!--[!-->");
        if (viewportStartMs == null || viewportEndMs == null) {
          $$renderer2.push("<!--[-->");
          $$renderer2.push(`<div class="text-sm text-rc-gray-300">Preparing timeline…</div>`);
        } else {
          $$renderer2.push("<!--[!-->");
          $$renderer2.push(`<div class="grid grid-cols-[140px_minmax(0,1fr)] gap-x-3"><div class="flex flex-col"><div class="h-8"></div> <!--[-->`);
          const each_array = ensure_array_like(timelineRows);
          for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
            let row = each_array[$$index];
            $$renderer2.push(`<div class="flex h-10 items-center text-xs text-rc-gray-300">${escape_html(row.label)}</div>`);
          }
          $$renderer2.push(`<!--]--> <div class="flex h-40 items-center text-xs text-rc-gray-300">Waveform</div> <div class="flex h-8 items-center text-xs text-rc-gray-300">Scale</div></div> <div class="relative"><div class="pointer-events-none absolute inset-0 z-10 overflow-hidden"><!--[-->`);
          const each_array_1 = ensure_array_like(guideLines);
          for (let $$index_1 = 0, $$length = each_array_1.length; $$index_1 < $$length; $$index_1++) {
            let guide = each_array_1[$$index_1];
            $$renderer2.push(`<div class="absolute -translate-x-1/2 border-l border-dashed"${attr_style(guideLineStyle(guide))}></div>`);
          }
          $$renderer2.push(`<!--]--> `);
          if (playbackCursor) {
            $$renderer2.push("<!--[-->");
            $$renderer2.push(`<div class="absolute top-0 -translate-x-1/2 border-l-2 border-rc-green-200/90"${attr_style(`left:${playbackCursor.leftPercent}%;height:${playbackCursor.heightPx}px;`)}></div> <div class="absolute top-1 h-2 w-2 -translate-x-1/2 rounded-full bg-rc-green-200"${attr_style(`left:${playbackCursor.leftPercent}%;`)}></div>`);
          } else {
            $$renderer2.push("<!--[!-->");
          }
          $$renderer2.push(`<!--]--></div> <div class="relative z-0 h-8 overflow-hidden rounded-t border border-b-0 border-rc-gray-700/60 bg-rc-gray-800/30"><!--[-->`);
          const each_array_2 = ensure_array_like(visibleTicks);
          for (let $$index_2 = 0, $$length = each_array_2.length; $$index_2 < $$length; $$index_2++) {
            let tick = each_array_2[$$index_2];
            $$renderer2.push(`<div class="absolute inset-y-0 w-px bg-rc-gray-700/70"${attr_style(`left:${tick.leftPercent}%;`)}></div> <div class="absolute bottom-1 -translate-x-1/2 whitespace-nowrap text-[11px] text-rc-gray-400"${attr_style(`left:${tick.leftPercent}%;`)}>${escape_html(tick.label)}</div>`);
          }
          $$renderer2.push(`<!--]--></div> <!--[-->`);
          const each_array_3 = ensure_array_like(timelineRows);
          for (let $$index_5 = 0, $$length = each_array_3.length; $$index_5 < $$length; $$index_5++) {
            let row = each_array_3[$$index_5];
            $$renderer2.push(`<div class="relative z-0 h-10 overflow-hidden border-x border-t border-rc-gray-700/60 bg-rc-gray-800/20"><!--[-->`);
            const each_array_4 = ensure_array_like(visibleTicks);
            for (let $$index_3 = 0, $$length2 = each_array_4.length; $$index_3 < $$length2; $$index_3++) {
              let tick = each_array_4[$$index_3];
              $$renderer2.push(`<div class="absolute inset-y-0 w-px bg-rc-gray-700/60"${attr_style(`left:${tick.leftPercent}%;`)}></div>`);
            }
            $$renderer2.push(`<!--]--> <!--[-->`);
            const each_array_5 = ensure_array_like(row.segments);
            for (let $$index_4 = 0, $$length2 = each_array_5.length; $$index_4 < $$length2; $$index_4++) {
              let segment = each_array_5[$$index_4];
              const style = segmentStyle(segment);
              if (style) {
                $$renderer2.push("<!--[-->");
                $$renderer2.push(`<div class="absolute top-1/2 h-5 -translate-y-1/2 rounded border shadow-[0_0_0_1px_rgba(0,0,0,0.12)]"${attr_style(style)}></div>`);
              } else {
                $$renderer2.push("<!--[!-->");
              }
              $$renderer2.push(`<!--]-->`);
            }
            $$renderer2.push(`<!--]--></div>`);
          }
          $$renderer2.push(`<!--]--> <div class="relative z-0 h-40 overflow-hidden border-x border-t border-rc-gray-700/60 bg-rc-gray-800/30">`);
          {
            $$renderer2.push("<!--[-->");
            $$renderer2.push(`<div class="absolute inset-0 flex items-center justify-center px-4 text-sm text-rc-gray-300">Upload a WAV file to render the call waveform here.</div>`);
          }
          $$renderer2.push(`<!--]--> <!--[-->`);
          const each_array_6 = ensure_array_like(visibleTicks);
          for (let $$index_6 = 0, $$length = each_array_6.length; $$index_6 < $$length; $$index_6++) {
            let tick = each_array_6[$$index_6];
            $$renderer2.push(`<div class="absolute inset-y-0 w-px bg-rc-gray-700/60"${attr_style(`left:${tick.leftPercent}%;`)}></div>`);
          }
          $$renderer2.push(`<!--]--> `);
          {
            $$renderer2.push("<!--[!-->");
          }
          $$renderer2.push(`<!--]--></div> <div class="relative z-0 h-8 overflow-hidden rounded-b border-x border-b border-t border-rc-gray-700/60 bg-rc-gray-800/25"><div class="absolute left-3 top-1/2 -translate-y-1/2 rounded bg-rc-gray-900/80 px-2 py-0.5 text-[11px] text-rc-gray-300">Grid: ${escape_html(formatScaleStep(visibleTickStepMs))}</div> <!--[-->`);
          const each_array_7 = ensure_array_like(visibleTicks);
          for (let $$index_7 = 0, $$length = each_array_7.length; $$index_7 < $$length; $$index_7++) {
            let tick = each_array_7[$$index_7];
            $$renderer2.push(`<div class="absolute inset-y-0 w-px bg-rc-gray-700/60"${attr_style(`left:${tick.leftPercent}%;`)}></div> <div class="absolute bottom-1 -translate-x-1/2 whitespace-nowrap text-[11px] text-rc-gray-400"${attr_style(`left:${tick.leftPercent}%;`)}>${escape_html(tick.label)}</div>`);
          }
          $$renderer2.push(`<!--]--></div></div></div>`);
        }
        $$renderer2.push(`<!--]-->`);
      }
      $$renderer2.push(`<!--]-->`);
    }
    $$renderer2.push(`<!--]--></div></div></div></div> `);
    {
      $$renderer2.push("<!--[!-->");
    }
    $$renderer2.push(`<!--]-->`);
  });
}

export { _page as default };
//# sourceMappingURL=_page.svelte-VFizsKQC.js.map
