recappi 0.1.34 → 0.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -594,32 +594,151 @@ var init_LiveCaptionsScreen = __esm({
594
594
  }
595
595
  });
596
596
 
597
- // src/tui/AccountView.tsx
597
+ // src/tui/RecordingHeroScreen.tsx
598
+ import { useEffect as useEffect2, useState as useState3 } from "react";
598
599
  import { Box as Box2, Text as Text2 } from "ink";
599
- import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
600
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
601
+ function waveform(samples, width) {
602
+ if (width <= 0) return "";
603
+ const tail = samples.slice(-width);
604
+ const pad = width - tail.length;
605
+ const cells = tail.map((v) => {
606
+ const i = Math.max(0, Math.min(BLOCKS.length - 1, Math.round(Math.max(0, Math.min(1, v)) * (BLOCKS.length - 1))));
607
+ return BLOCKS[i];
608
+ });
609
+ return "\u2581".repeat(Math.max(0, pad)) + cells.join("");
610
+ }
611
+ function RecordingHeroScreen({
612
+ telemetry,
613
+ artifact,
614
+ canTranscribe = false,
615
+ canPause = false,
616
+ now = () => Date.now()
617
+ }) {
618
+ const size = useTerminalSize();
619
+ const [tick, setTick] = useState3(() => now());
620
+ const [wave, setWave] = useState3([]);
621
+ useEffect2(() => {
622
+ if (telemetry.level == null) return;
623
+ const lvl = Math.max(telemetry.level.system ?? 0, telemetry.level.mic ?? 0);
624
+ setWave((w) => [...w.slice(-512), lvl]);
625
+ }, [telemetry.level]);
626
+ useEffect2(() => {
627
+ const id = setInterval(() => setTick(now()), 1e3);
628
+ return () => clearInterval(id);
629
+ }, []);
630
+ const elapsed = telemetry.startedAtMs != null ? formatClockMs(Math.max(0, tick - telemetry.startedAtMs)) : "00:00";
631
+ const innerWidth = Math.max(10, size.columns - 4);
632
+ if (telemetry.status === "stopped") {
633
+ const handoff = stoppedHandoffCopy(artifact, canTranscribe);
634
+ const meta3 = [
635
+ telemetry.durationMs != null ? formatClockMs(telemetry.durationMs) : null,
636
+ formatBytes2(telemetry.sizeBytes) || null
637
+ ].filter(Boolean).join(" \xB7 ");
638
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
639
+ /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "recappi \xB7 Recording" }),
640
+ /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
641
+ /* @__PURE__ */ jsx3(Text2, { color: "green", children: "\u2713 Saved to your Mac" }),
642
+ meta3 ? /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: meta3 }) : null,
643
+ telemetry.savedPath ? /* @__PURE__ */ jsx3(Text2, { dimColor: true, wrap: "truncate-middle", children: telemetry.savedPath }) : null
644
+ ] }),
645
+ /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
646
+ /* @__PURE__ */ jsx3(Text2, { color: handoff.tone === "red" ? "red" : handoff.tone === "green" ? "green" : void 0, dimColor: handoff.tone === "dim", children: handoff.text }),
647
+ artifact?.error ? /* @__PURE__ */ jsx3(Text2, { color: "red", wrap: "truncate-end", children: artifact.error }) : null
648
+ ] })
649
+ ] });
650
+ }
651
+ if (telemetry.status === "error") {
652
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
653
+ /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "recappi \xB7 Recording" }),
654
+ /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { color: "red", children: telemetry.error ? `Recording error: ${telemetry.error}` : "Recording error" }) }),
655
+ /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "esc back" }) })
656
+ ] });
657
+ }
658
+ const paused = telemetry.status === "paused";
659
+ const starting = telemetry.status === "starting" || telemetry.status === "stopping";
660
+ const badge = paused ? "\u23F8 PAUSED" : starting ? "\u2026" : "\u23FA REC";
661
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, height: size.rows, children: [
662
+ /* @__PURE__ */ jsxs2(Text2, { children: [
663
+ /* @__PURE__ */ jsx3(Text2, { bold: true, color: "green", children: "recappi" }),
664
+ /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: " \xB7 Recording" })
665
+ ] }),
666
+ /* @__PURE__ */ jsxs2(Box2, { flexGrow: 1, flexDirection: "column", justifyContent: "center", alignItems: "center", children: [
667
+ /* @__PURE__ */ jsx3(Text2, { bold: true, color: paused ? "yellow" : "red", children: badge }),
668
+ /* @__PURE__ */ jsx3(Text2, { bold: true, children: elapsed }),
669
+ /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: telemetry.level == null ? (
670
+ // No level telemetry yet — show honest activity, not a flat meter that
671
+ // looks like silence (the elapsed timer above proves it's live).
672
+ /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: paused ? "Paused" : `Capturing audio${".".repeat(Math.floor(tick / 1e3) % 3 + 1)}` })
673
+ ) : /* @__PURE__ */ jsx3(Text2, { color: paused ? "gray" : "red", children: waveform(wave, innerWidth) }) }),
674
+ /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
675
+ telemetry.sourceLabel,
676
+ telemetry.micEnabled ? " + Microphone" : ""
677
+ ] }) })
678
+ ] }),
679
+ /* @__PURE__ */ jsx3(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
680
+ "q stop & save",
681
+ canPause ? ` \xB7 p ${paused ? "resume" : "pause"}` : ""
682
+ ] }) })
683
+ ] });
684
+ }
685
+ function stoppedHandoffCopy(artifact, canTranscribe) {
686
+ if (artifact?.uploadStatus === "uploading") {
687
+ return { text: "Uploading\u2026", tone: "normal" };
688
+ }
689
+ if (artifact?.transcriptionStatus === "processing") {
690
+ return { text: "Transcribing\u2026", tone: "normal" };
691
+ }
692
+ if (artifact?.transcriptionStatus === "queued") {
693
+ return { text: "Transcription queued \xB7 \u23CE open recording \xB7 n not now", tone: "green" };
694
+ }
695
+ if (artifact?.transcriptionStatus === "ready") {
696
+ return { text: "Transcription ready \xB7 \u23CE open recording \xB7 n not now", tone: "green" };
697
+ }
698
+ if (artifact?.uploadStatus === "failed" || artifact?.transcriptionStatus === "failed") {
699
+ return { text: "Transcription failed \xB7 \u23CE retry \xB7 n not now", tone: "red" };
700
+ }
701
+ if (!canTranscribe || !artifact?.audioPath) {
702
+ return { text: "Saved locally \xB7 n back", tone: "dim" };
703
+ }
704
+ return { text: "Transcribe now? \u23CE yes \xB7 n not now", tone: "normal" };
705
+ }
706
+ var BLOCKS;
707
+ var init_RecordingHeroScreen = __esm({
708
+ "src/tui/RecordingHeroScreen.tsx"() {
709
+ "use strict";
710
+ init_format();
711
+ init_terminal();
712
+ BLOCKS = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
713
+ }
714
+ });
715
+
716
+ // src/tui/AccountView.tsx
717
+ import { Box as Box3, Text as Text3 } from "ink";
718
+ import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
600
719
  function AccountView({ status }) {
601
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
602
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: "\u2039 Account" }),
603
- status === "loading" || status === void 0 ? /* @__PURE__ */ jsx4(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: "Loading account\u2026" }) }) : status === "error" ? /* @__PURE__ */ jsx4(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text2, { color: "red", children: "Couldn't load account status" }) }) : !status.loggedIn ? /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
604
- /* @__PURE__ */ jsx4(Text2, { color: "yellow", children: "Not signed in" }),
605
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: `origin ${status.origin}` }),
606
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: "Run `recappi auth login` to sign in." })
607
- ] }) : /* @__PURE__ */ jsx4(AccountBody, { status }),
608
- /* @__PURE__ */ jsx4(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: "r refresh \xB7 esc back \xB7 q quit" }) })
720
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, children: [
721
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "\u2039 Account" }),
722
+ status === "loading" || status === void 0 ? /* @__PURE__ */ jsx5(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "Loading account\u2026" }) }) : status === "error" ? /* @__PURE__ */ jsx5(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text3, { color: "red", children: "Couldn't load account status" }) }) : !status.loggedIn ? /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
723
+ /* @__PURE__ */ jsx5(Text3, { color: "yellow", children: "Not signed in" }),
724
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: `origin ${status.origin}` }),
725
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "Run `recappi auth login` to sign in." })
726
+ ] }) : /* @__PURE__ */ jsx5(AccountBody, { status }),
727
+ /* @__PURE__ */ jsx5(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "r refresh \xB7 esc back \xB7 q quit" }) })
609
728
  ] });
610
729
  }
611
730
  function AccountBody({ status }) {
612
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
613
- /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
614
- /* @__PURE__ */ jsx4(Text2, { bold: true, color: "green", children: status.email ?? status.userId ?? "Signed in" }),
615
- status.email && status.userId ? /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: status.userId }) : null,
616
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: `origin ${status.origin}` })
731
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
732
+ /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
733
+ /* @__PURE__ */ jsx5(Text3, { bold: true, color: "green", children: status.email ?? status.userId ?? "Signed in" }),
734
+ status.email && status.userId ? /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: status.userId }) : null,
735
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: `origin ${status.origin}` })
617
736
  ] }),
618
- status.billing ? /* @__PURE__ */ jsx4(Usage, { billing: status.billing }) : null,
619
- /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
620
- /* @__PURE__ */ jsx4(Text2, { bold: true, children: "Local store" }),
621
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, wrap: "truncate-middle", children: status.localStore.path }),
622
- /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
737
+ status.billing ? /* @__PURE__ */ jsx5(Usage, { billing: status.billing }) : null,
738
+ /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
739
+ /* @__PURE__ */ jsx5(Text3, { bold: true, children: "Local store" }),
740
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, wrap: "truncate-middle", children: status.localStore.path }),
741
+ /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
623
742
  `${status.localStore.accountScopedArtifacts} artifact${status.localStore.accountScopedArtifacts === 1 ? "" : "s"} for this account`,
624
743
  status.localStore.unattributedArtifacts > 0 ? ` \xB7 ${status.localStore.unattributedArtifacts} unattributed` : ""
625
744
  ] })
@@ -630,29 +749,29 @@ function Usage({ billing }) {
630
749
  const minutesCap = billing.minutesCap;
631
750
  const minutesUsed = billing.minutesUsed;
632
751
  const storageCap = billing.storageCapBytes;
633
- return /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
634
- /* @__PURE__ */ jsxs2(Text2, { children: [
635
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: "Plan " }),
636
- /* @__PURE__ */ jsx4(Text2, { bold: true, children: billing.tier })
752
+ return /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
753
+ /* @__PURE__ */ jsxs3(Text3, { children: [
754
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "Plan " }),
755
+ /* @__PURE__ */ jsx5(Text3, { bold: true, children: billing.tier })
637
756
  ] }),
638
- /* @__PURE__ */ jsxs2(Text2, { children: [
639
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: "Minutes " }),
640
- minutesCap != null ? /* @__PURE__ */ jsx4(Text2, { color: billing.isOverMinutes ? "red" : "cyan", children: `${progressBar(minutesUsed / Math.max(1, minutesCap), 12)} ` }) : null,
641
- /* @__PURE__ */ jsx4(Text2, { color: billing.isOverMinutes ? "red" : void 0, children: `${Math.round(minutesUsed)}` }),
642
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: ` / ${minutesCap != null ? Math.round(minutesCap) : "\u221E"} min` }),
643
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: ` (batch ${Math.round(billing.batchMinutesUsed)} \xB7 live ${Math.round(billing.realtimeMinutesUsed)})` })
757
+ /* @__PURE__ */ jsxs3(Text3, { children: [
758
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "Minutes " }),
759
+ minutesCap != null ? /* @__PURE__ */ jsx5(Text3, { color: billing.isOverMinutes ? "red" : "cyan", children: `${progressBar(minutesUsed / Math.max(1, minutesCap), 12)} ` }) : null,
760
+ /* @__PURE__ */ jsx5(Text3, { color: billing.isOverMinutes ? "red" : void 0, children: `${Math.round(minutesUsed)}` }),
761
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: ` / ${minutesCap != null ? Math.round(minutesCap) : "\u221E"} min` }),
762
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: ` (batch ${Math.round(billing.batchMinutesUsed)} \xB7 live ${Math.round(billing.realtimeMinutesUsed)})` })
644
763
  ] }),
645
- /* @__PURE__ */ jsxs2(Text2, { children: [
646
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: "Storage " }),
647
- storageCap != null ? /* @__PURE__ */ jsx4(Text2, { color: billing.isOverStorage ? "red" : "cyan", children: `${progressBar(billing.storageBytes / Math.max(1, storageCap), 12)} ` }) : null,
648
- /* @__PURE__ */ jsx4(Text2, { color: billing.isOverStorage ? "red" : void 0, children: formatBytes2(billing.storageBytes) }),
649
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: ` / ${storageCap != null ? formatBytes2(storageCap) : "\u221E"}` })
764
+ /* @__PURE__ */ jsxs3(Text3, { children: [
765
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "Storage " }),
766
+ storageCap != null ? /* @__PURE__ */ jsx5(Text3, { color: billing.isOverStorage ? "red" : "cyan", children: `${progressBar(billing.storageBytes / Math.max(1, storageCap), 12)} ` }) : null,
767
+ /* @__PURE__ */ jsx5(Text3, { color: billing.isOverStorage ? "red" : void 0, children: formatBytes2(billing.storageBytes) }),
768
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: ` / ${storageCap != null ? formatBytes2(storageCap) : "\u221E"}` })
650
769
  ] }),
651
- billing.isOverMinutes || billing.isOverStorage ? /* @__PURE__ */ jsxs2(Text2, { color: "red", children: [
770
+ billing.isOverMinutes || billing.isOverStorage ? /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
652
771
  billing.isOverMinutes ? "Over minutes limit. " : "",
653
772
  billing.isOverStorage ? "Over storage limit." : ""
654
773
  ] }) : null,
655
- /* @__PURE__ */ jsx4(Text2, { dimColor: true, children: `Period ${periodText(billing)}` })
774
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: `Period ${periodText(billing)}` })
656
775
  ] });
657
776
  }
658
777
  function periodText(billing) {
@@ -673,17 +792,17 @@ var init_AccountView = __esm({
673
792
  });
674
793
 
675
794
  // src/tui/chrome.tsx
676
- import { Box as Box3, Text as Text3 } from "ink";
677
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
795
+ import { Box as Box4, Text as Text4 } from "ink";
796
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
678
797
  function Header({ active }) {
679
- return /* @__PURE__ */ jsxs3(Box3, { children: [
680
- /* @__PURE__ */ jsxs3(Text3, { bold: true, color: "green", children: [
798
+ return /* @__PURE__ */ jsxs4(Box4, { children: [
799
+ /* @__PURE__ */ jsxs4(Text4, { bold: true, color: "green", children: [
681
800
  "\u25CF Recappi",
682
801
  " "
683
802
  ] }),
684
- /* @__PURE__ */ jsx5(Tab, { num: "1", label: "Overview", active: active === "overview" }),
685
- /* @__PURE__ */ jsx5(Tab, { num: "2", label: "Jobs", active: active === "jobs" }),
686
- /* @__PURE__ */ jsx5(Tab, { num: "3", label: "Account", active: active === "account" })
803
+ /* @__PURE__ */ jsx6(Tab, { num: "1", label: "Overview", active: active === "overview" }),
804
+ /* @__PURE__ */ jsx6(Tab, { num: "2", label: "Jobs", active: active === "jobs" }),
805
+ /* @__PURE__ */ jsx6(Tab, { num: "3", label: "Account", active: active === "account" })
687
806
  ] });
688
807
  }
689
808
  function Tab({
@@ -692,24 +811,24 @@ function Tab({
692
811
  active
693
812
  }) {
694
813
  if (active) {
695
- return /* @__PURE__ */ jsx5(Text3, { bold: true, inverse: true, color: "cyan", children: ` ${num} ${label} ` });
814
+ return /* @__PURE__ */ jsx6(Text4, { bold: true, inverse: true, color: "cyan", children: ` ${num} ${label} ` });
696
815
  }
697
- return /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
816
+ return /* @__PURE__ */ jsxs4(Text4, { dimColor: true, children: [
698
817
  " ",
699
- /* @__PURE__ */ jsx5(Text3, { color: "cyan", children: num }),
818
+ /* @__PURE__ */ jsx6(Text4, { color: "cyan", children: num }),
700
819
  ` ${label} `
701
820
  ] });
702
821
  }
703
822
  function Footer({ keys }) {
704
823
  const segments = keys.split(" \xB7 ");
705
- return /* @__PURE__ */ jsx5(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text3, { children: segments.map((segment, i) => {
824
+ return /* @__PURE__ */ jsx6(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text4, { children: segments.map((segment, i) => {
706
825
  const space = segment.indexOf(" ");
707
826
  const key = space === -1 ? segment : segment.slice(0, space);
708
827
  const desc = space === -1 ? "" : segment.slice(space);
709
- return /* @__PURE__ */ jsxs3(Text3, { children: [
710
- i > 0 ? /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: " \xB7 " }) : null,
711
- /* @__PURE__ */ jsx5(Text3, { color: "cyan", children: key }),
712
- /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: desc })
828
+ return /* @__PURE__ */ jsxs4(Text4, { children: [
829
+ i > 0 ? /* @__PURE__ */ jsx6(Text4, { dimColor: true, children: " \xB7 " }) : null,
830
+ /* @__PURE__ */ jsx6(Text4, { color: "cyan", children: key }),
831
+ /* @__PURE__ */ jsx6(Text4, { dimColor: true, children: desc })
713
832
  ] }, `${segment}-${i}`);
714
833
  }) }) });
715
834
  }
@@ -720,8 +839,8 @@ var init_chrome = __esm({
720
839
  });
721
840
 
722
841
  // src/tui/JobRow.tsx
723
- import { Box as Box4, Text as Text4 } from "ink";
724
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
842
+ import { Box as Box5, Text as Text5 } from "ink";
843
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
725
844
  function JobRow({
726
845
  item,
727
846
  selected,
@@ -730,11 +849,11 @@ function JobRow({
730
849
  const style = statusStyle(item.status);
731
850
  const glyph = statusGlyph(item.status, spinnerFrame);
732
851
  const title = item.recording?.title ?? item.recordingId;
733
- return /* @__PURE__ */ jsxs4(Box4, { children: [
734
- /* @__PURE__ */ jsx6(Text4, { color: "cyan", children: selected ? "\u25B8 " : " " }),
735
- /* @__PURE__ */ jsx6(Text4, { color: style.color, children: `${glyph} ${padCell(style.label, 13)}` }),
736
- /* @__PURE__ */ jsx6(Text4, { bold: selected, children: padCell(title, 24) }),
737
- /* @__PURE__ */ jsx6(Text4, { dimColor: !selected, children: jobDetail(item) })
852
+ return /* @__PURE__ */ jsxs5(Box5, { children: [
853
+ /* @__PURE__ */ jsx7(Text5, { color: "cyan", children: selected ? "\u25B8 " : " " }),
854
+ /* @__PURE__ */ jsx7(Text5, { color: style.color, children: `${glyph} ${padCell(style.label, 13)}` }),
855
+ /* @__PURE__ */ jsx7(Text5, { bold: selected, children: padCell(title, 24) }),
856
+ /* @__PURE__ */ jsx7(Text5, { dimColor: !selected, children: jobDetail(item) })
738
857
  ] });
739
858
  }
740
859
  var init_JobRow = __esm({
@@ -745,17 +864,17 @@ var init_JobRow = __esm({
745
864
  });
746
865
 
747
866
  // src/tui/JobsView.tsx
748
- import { Box as Box5, Text as Text5 } from "ink";
749
- import { jsx as jsx7 } from "react/jsx-runtime";
867
+ import { Box as Box6, Text as Text6 } from "ink";
868
+ import { jsx as jsx8 } from "react/jsx-runtime";
750
869
  function JobsView({
751
870
  items,
752
871
  selectedIndex,
753
872
  spinnerFrame
754
873
  }) {
755
874
  if (items.length === 0) {
756
- return /* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text5, { dimColor: true, children: "No transcription jobs yet \u2014 run: recappi upload <file> --transcribe" }) });
875
+ return /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text6, { dimColor: true, children: "No transcription jobs yet \u2014 run: recappi upload <file> --transcribe" }) });
757
876
  }
758
- return /* @__PURE__ */ jsx7(Box5, { marginTop: 1, flexDirection: "column", children: items.map((item, index) => /* @__PURE__ */ jsx7(
877
+ return /* @__PURE__ */ jsx8(Box6, { marginTop: 1, flexDirection: "column", children: items.map((item, index) => /* @__PURE__ */ jsx8(
759
878
  JobRow,
760
879
  {
761
880
  item,
@@ -773,8 +892,8 @@ var init_JobsView = __esm({
773
892
  });
774
893
 
775
894
  // src/tui/RecordingRow.tsx
776
- import { Box as Box6, Text as Text6 } from "ink";
777
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
895
+ import { Box as Box7, Text as Text7 } from "ink";
896
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
778
897
  function recordingTitle2(item) {
779
898
  const named = (item.title || item.summaryTitle || "").trim();
780
899
  if (named && !UUID_RE.test(named)) return named;
@@ -810,22 +929,22 @@ function RecordingRow({
810
929
  const { title, showWhen } = recordingLayout(columns);
811
930
  const { glyph, color } = recordingProcessingState(item, jobStatus, spinnerFrame);
812
931
  const duration3 = item.durationMs ? formatClockMs(item.durationMs) : "\u2014";
813
- return /* @__PURE__ */ jsxs5(Box6, { children: [
814
- /* @__PURE__ */ jsx8(Text6, { color: "cyan", children: selected ? "\u25B8 " : " " }),
815
- /* @__PURE__ */ jsx8(Text6, { color, children: `${glyph} ` }),
816
- /* @__PURE__ */ jsx8(Text6, { bold: selected, children: padDisplay(recordingTitle2(item), title) }),
817
- /* @__PURE__ */ jsx8(Text6, { dimColor: true, children: padDisplay(duration3, LENGTH_W) }),
818
- showWhen ? /* @__PURE__ */ jsx8(Text6, { dimColor: true, children: padDisplay(formatAge(item.createdAt, nowMs), WHEN_W) }) : null,
819
- /* @__PURE__ */ jsx8(Text6, { color: "green", children: downloaded ? " \u2913" : " " })
932
+ return /* @__PURE__ */ jsxs6(Box7, { children: [
933
+ /* @__PURE__ */ jsx9(Text7, { color: "cyan", children: selected ? "\u25B8 " : " " }),
934
+ /* @__PURE__ */ jsx9(Text7, { color, children: `${glyph} ` }),
935
+ /* @__PURE__ */ jsx9(Text7, { bold: selected, children: padDisplay(recordingTitle2(item), title) }),
936
+ /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: padDisplay(duration3, LENGTH_W) }),
937
+ showWhen ? /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: padDisplay(formatAge(item.createdAt, nowMs), WHEN_W) }) : null,
938
+ /* @__PURE__ */ jsx9(Text7, { color: "green", children: downloaded ? " \u2913" : " " })
820
939
  ] });
821
940
  }
822
941
  function RecordingHeader({ columns }) {
823
942
  const { title, showWhen } = recordingLayout(columns);
824
- return /* @__PURE__ */ jsxs5(Box6, { children: [
825
- /* @__PURE__ */ jsx8(Text6, { dimColor: true, children: padDisplay("", MARKER_W + GLYPH_W) }),
826
- /* @__PURE__ */ jsx8(Text6, { dimColor: true, children: padDisplay("TITLE", title) }),
827
- /* @__PURE__ */ jsx8(Text6, { dimColor: true, children: padDisplay("LENGTH", LENGTH_W) }),
828
- showWhen ? /* @__PURE__ */ jsx8(Text6, { dimColor: true, children: "WHEN" }) : null
943
+ return /* @__PURE__ */ jsxs6(Box7, { children: [
944
+ /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: padDisplay("", MARKER_W + GLYPH_W) }),
945
+ /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: padDisplay("TITLE", title) }),
946
+ /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: padDisplay("LENGTH", LENGTH_W) }),
947
+ showWhen ? /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: "WHEN" }) : null
829
948
  ] });
830
949
  }
831
950
  var UUID_RE, MARKER_W, GLYPH_W, LENGTH_W, WHEN_W;
@@ -842,9 +961,9 @@ var init_RecordingRow = __esm({
842
961
  });
843
962
 
844
963
  // src/tui/RecordingsView.tsx
845
- import React3 from "react";
846
- import { Box as Box7, Text as Text7 } from "ink";
847
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
964
+ import React5 from "react";
965
+ import { Box as Box8, Text as Text8 } from "ink";
966
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
848
967
  function RecordingsView({
849
968
  items,
850
969
  selectedIndex,
@@ -855,16 +974,16 @@ function RecordingsView({
855
974
  spinnerFrame = 0
856
975
  }) {
857
976
  if (items.length === 0) {
858
- return /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: "No recordings yet \u2014 run: recappi upload <file>" }) });
977
+ return /* @__PURE__ */ jsx10(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: "No recordings yet \u2014 run: recappi upload <file>" }) });
859
978
  }
860
- return /* @__PURE__ */ jsxs6(Box7, { marginTop: 1, flexDirection: "column", children: [
861
- /* @__PURE__ */ jsx9(RecordingHeader, { columns }),
979
+ return /* @__PURE__ */ jsxs7(Box8, { marginTop: 1, flexDirection: "column", children: [
980
+ /* @__PURE__ */ jsx10(RecordingHeader, { columns }),
862
981
  items.map((item, index) => {
863
982
  const bucket = dateBucket(item.createdAt, nowMs);
864
983
  const showHeader = index === 0 || bucket !== dateBucket(items[index - 1].createdAt, nowMs);
865
- return /* @__PURE__ */ jsxs6(React3.Fragment, { children: [
866
- showHeader ? /* @__PURE__ */ jsx9(Box7, { marginTop: index === 0 ? 0 : 1, children: /* @__PURE__ */ jsx9(Text7, { bold: true, color: "blue", children: bucket }) }) : null,
867
- /* @__PURE__ */ jsx9(
984
+ return /* @__PURE__ */ jsxs7(React5.Fragment, { children: [
985
+ showHeader ? /* @__PURE__ */ jsx10(Box8, { marginTop: index === 0 ? 0 : 1, children: /* @__PURE__ */ jsx10(Text8, { bold: true, color: "blue", children: bucket }) }) : null,
986
+ /* @__PURE__ */ jsx10(
868
987
  RecordingRow,
869
988
  {
870
989
  item,
@@ -889,15 +1008,15 @@ var init_RecordingsView = __esm({
889
1008
  });
890
1009
 
891
1010
  // src/tui/RecordingPeek.tsx
892
- import { Box as Box8, Text as Text8 } from "ink";
893
- import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1011
+ import { Box as Box9, Text as Text9 } from "ink";
1012
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
894
1013
  function RecordingPeek({
895
1014
  item,
896
1015
  summary,
897
1016
  nowMs,
898
1017
  width
899
1018
  }) {
900
- return /* @__PURE__ */ jsx10(Box8, { width, borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", children: !item ? /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: "No selection" }) : /* @__PURE__ */ jsx10(PeekBody, { item, summary, nowMs }) });
1019
+ return /* @__PURE__ */ jsx11(Box9, { width, borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", children: !item ? /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "No selection" }) : /* @__PURE__ */ jsx11(PeekBody, { item, summary, nowMs }) });
901
1020
  }
902
1021
  function PeekBody({
903
1022
  item,
@@ -910,30 +1029,30 @@ function PeekBody({
910
1029
  formatBytes2(item.sizeBytes) || null,
911
1030
  item.contentType || null
912
1031
  ].filter(Boolean).join(" \xB7 ");
913
- return /* @__PURE__ */ jsxs7(Fragment2, { children: [
914
- /* @__PURE__ */ jsx10(Text8, { bold: true, color: "green", wrap: "truncate-end", children: recordingTitle2(item) }),
915
- /* @__PURE__ */ jsx10(Text8, { color: style.color, children: `${style.glyph} ${style.label}` }),
916
- meta3 ? /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: meta3 }) : null,
917
- /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: formatAge(item.createdAt, nowMs) }),
918
- /* @__PURE__ */ jsx10(Box8, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx10(SummarySection, { item, summary }) }),
919
- /* @__PURE__ */ jsx10(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: "\u23CE open \xB7 t transcript \xB7 o web" }) })
1032
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
1033
+ /* @__PURE__ */ jsx11(Text9, { bold: true, color: "green", wrap: "truncate-end", children: recordingTitle2(item) }),
1034
+ /* @__PURE__ */ jsx11(Text9, { color: style.color, children: `${style.glyph} ${style.label}` }),
1035
+ meta3 ? /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: meta3 }) : null,
1036
+ /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: formatAge(item.createdAt, nowMs) }),
1037
+ /* @__PURE__ */ jsx11(Box9, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx11(SummarySection, { item, summary }) }),
1038
+ /* @__PURE__ */ jsx11(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "\u23CE open \xB7 t transcript \xB7 o web" }) })
920
1039
  ] });
921
1040
  }
922
1041
  function SummarySection({
923
1042
  item,
924
1043
  summary
925
1044
  }) {
926
- if (!item.activeTranscriptId) return /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: "No transcript yet" });
927
- if (summary === "loading" || summary === void 0) return /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: "Loading summary\u2026" });
928
- if (summary === "error") return /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: "(summary unavailable)" });
1045
+ if (!item.activeTranscriptId) return /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "No transcript yet" });
1046
+ if (summary === "loading" || summary === void 0) return /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "Loading summary\u2026" });
1047
+ if (summary === "error") return /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "(summary unavailable)" });
929
1048
  if (summary.status !== "succeeded" || !summary.tldr) {
930
- return /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: `Summary ${summary.status}` });
1049
+ return /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: `Summary ${summary.status}` });
931
1050
  }
932
1051
  const points = (summary.keyPoints ?? []).slice(0, 3);
933
- return /* @__PURE__ */ jsxs7(Fragment2, { children: [
934
- /* @__PURE__ */ jsx10(Text8, { bold: true, children: "Summary" }),
935
- /* @__PURE__ */ jsx10(Text8, { children: summary.tldr }),
936
- points.length > 0 ? /* @__PURE__ */ jsx10(Box8, { marginTop: 1, flexDirection: "column", children: points.map((point, i) => /* @__PURE__ */ jsx10(Text8, { dimColor: true, wrap: "truncate-end", children: `\u2022 ${point}` }, i)) }) : null
1052
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
1053
+ /* @__PURE__ */ jsx11(Text9, { bold: true, children: "Summary" }),
1054
+ /* @__PURE__ */ jsx11(Text9, { children: summary.tldr }),
1055
+ points.length > 0 ? /* @__PURE__ */ jsx11(Box9, { marginTop: 1, flexDirection: "column", children: points.map((point, i) => /* @__PURE__ */ jsx11(Text9, { dimColor: true, wrap: "truncate-end", children: `\u2022 ${point}` }, i)) }) : null
937
1056
  ] });
938
1057
  }
939
1058
  var init_RecordingPeek = __esm({
@@ -945,8 +1064,8 @@ var init_RecordingPeek = __esm({
945
1064
  });
946
1065
 
947
1066
  // src/tui/OverviewView.tsx
948
- import { Box as Box9, Text as Text9 } from "ink";
949
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
1067
+ import { Box as Box10, Text as Text10 } from "ink";
1068
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
950
1069
  function OverviewView({
951
1070
  recordings,
952
1071
  jobs,
@@ -965,17 +1084,17 @@ function OverviewView({
965
1084
  const jobCounts = countJobs(jobs);
966
1085
  const running = stats?.jobs.running ?? jobCounts.running;
967
1086
  const queued = stats?.jobs.queued ?? jobCounts.queued;
968
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
969
- /* @__PURE__ */ jsxs8(Box9, { children: [
970
- /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "Recordings " }),
971
- /* @__PURE__ */ jsx11(Text9, { bold: true, children: stats?.recordings.total ?? recordings.length }),
972
- stats?.recordings.ready != null ? /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: ` \xB7 ${stats.recordings.ready} ready` }) : null,
973
- stats?.recordings.totalDurationMs != null ? /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: ` \xB7 ${formatClockMs(stats.recordings.totalDurationMs)} transcribed` }) : null,
974
- running > 0 ? /* @__PURE__ */ jsx11(Text9, { color: "cyan", children: ` \xB7 ${running} transcribing` }) : null,
975
- queued > 0 ? /* @__PURE__ */ jsx11(Text9, { color: "yellow", children: ` \xB7 ${queued} queued` }) : null
1087
+ return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
1088
+ /* @__PURE__ */ jsxs9(Box10, { children: [
1089
+ /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "Recordings " }),
1090
+ /* @__PURE__ */ jsx12(Text10, { bold: true, children: stats?.recordings.total ?? recordings.length }),
1091
+ stats?.recordings.ready != null ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: ` \xB7 ${stats.recordings.ready} ready` }) : null,
1092
+ stats?.recordings.totalDurationMs != null ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: ` \xB7 ${formatClockMs(stats.recordings.totalDurationMs)} transcribed` }) : null,
1093
+ running > 0 ? /* @__PURE__ */ jsx12(Text10, { color: "cyan", children: ` \xB7 ${running} transcribing` }) : null,
1094
+ queued > 0 ? /* @__PURE__ */ jsx12(Text10, { color: "yellow", children: ` \xB7 ${queued} queued` }) : null
976
1095
  ] }),
977
- /* @__PURE__ */ jsxs8(Box9, { flexDirection: "row", alignItems: "flex-start", children: [
978
- /* @__PURE__ */ jsx11(Box9, { flexGrow: 1, flexDirection: "column", children: /* @__PURE__ */ jsx11(
1096
+ /* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", alignItems: "flex-start", children: [
1097
+ /* @__PURE__ */ jsx12(Box10, { flexGrow: 1, flexDirection: "column", children: /* @__PURE__ */ jsx12(
979
1098
  RecordingsView,
980
1099
  {
981
1100
  items: recordings,
@@ -987,7 +1106,7 @@ function OverviewView({
987
1106
  spinnerFrame
988
1107
  }
989
1108
  ) }),
990
- showPeek ? /* @__PURE__ */ jsx11(Box9, { marginLeft: 1, marginTop: 1, children: /* @__PURE__ */ jsx11(RecordingPeek, { item: peekItem, summary: peekSummary, nowMs, width: peekWidth }) }) : null
1109
+ showPeek ? /* @__PURE__ */ jsx12(Box10, { marginLeft: 1, marginTop: 1, children: /* @__PURE__ */ jsx12(RecordingPeek, { item: peekItem, summary: peekSummary, nowMs, width: peekWidth }) }) : null
991
1110
  ] })
992
1111
  ] });
993
1112
  }
@@ -1001,8 +1120,8 @@ var init_OverviewView = __esm({
1001
1120
  });
1002
1121
 
1003
1122
  // src/tui/JobDetailView.tsx
1004
- import { Box as Box10, Text as Text10 } from "ink";
1005
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
1123
+ import { Box as Box11, Text as Text11 } from "ink";
1124
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1006
1125
  function JobDetailView({
1007
1126
  item,
1008
1127
  origin,
@@ -1012,13 +1131,13 @@ function JobDetailView({
1012
1131
  const style = statusStyle(item.status);
1013
1132
  const links = resolveJobLinks(item, origin);
1014
1133
  const title = item.recording?.title ?? item.recordingId;
1015
- return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", paddingX: 1, children: [
1016
- /* @__PURE__ */ jsxs9(Text10, { dimColor: true, children: [
1134
+ return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingX: 1, children: [
1135
+ /* @__PURE__ */ jsxs10(Text11, { dimColor: true, children: [
1017
1136
  "\u2039 Jobs / ",
1018
1137
  title
1019
1138
  ] }),
1020
- /* @__PURE__ */ jsxs9(
1021
- Box10,
1139
+ /* @__PURE__ */ jsxs10(
1140
+ Box11,
1022
1141
  {
1023
1142
  marginTop: 1,
1024
1143
  borderStyle: "round",
@@ -1026,17 +1145,17 @@ function JobDetailView({
1026
1145
  paddingX: 1,
1027
1146
  flexDirection: "column",
1028
1147
  children: [
1029
- /* @__PURE__ */ jsxs9(Text10, { color: style.color, bold: true, children: [
1148
+ /* @__PURE__ */ jsxs10(Text11, { color: style.color, bold: true, children: [
1030
1149
  style.label,
1031
- item.provider ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: ` ${item.provider}` }) : null
1150
+ item.provider ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: ` ${item.provider}` }) : null
1032
1151
  ] }),
1033
- /* @__PURE__ */ jsx12(StatusLine, { item, spinnerFrame, nowMs })
1152
+ /* @__PURE__ */ jsx13(StatusLine, { item, spinnerFrame, nowMs })
1034
1153
  ]
1035
1154
  }
1036
1155
  ),
1037
- /* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "column", children: [
1038
- /* @__PURE__ */ jsx12(Text10, { bold: true, children: "Timeline" }),
1039
- /* @__PURE__ */ jsx12(
1156
+ /* @__PURE__ */ jsxs10(Box11, { marginTop: 1, flexDirection: "column", children: [
1157
+ /* @__PURE__ */ jsx13(Text11, { bold: true, children: "Timeline" }),
1158
+ /* @__PURE__ */ jsx13(
1040
1159
  TimelineRow,
1041
1160
  {
1042
1161
  label: "Enqueued",
@@ -1045,7 +1164,7 @@ function JobDetailView({
1045
1164
  nowMs
1046
1165
  }
1047
1166
  ),
1048
- /* @__PURE__ */ jsx12(
1167
+ /* @__PURE__ */ jsx13(
1049
1168
  TimelineRow,
1050
1169
  {
1051
1170
  label: "Started",
@@ -1054,7 +1173,7 @@ function JobDetailView({
1054
1173
  nowMs
1055
1174
  }
1056
1175
  ),
1057
- /* @__PURE__ */ jsx12(
1176
+ /* @__PURE__ */ jsx13(
1058
1177
  TimelineRow,
1059
1178
  {
1060
1179
  label: item.status === "failed" ? "Failed" : item.status === "running" ? "Transcribing" : "Finished",
@@ -1066,21 +1185,21 @@ function JobDetailView({
1066
1185
  }
1067
1186
  )
1068
1187
  ] }),
1069
- /* @__PURE__ */ jsx12(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text10, { children: [
1070
- /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "Recording " }),
1188
+ /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text11, { children: [
1189
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Recording " }),
1071
1190
  title,
1072
- item.recording?.durationMs ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: ` \xB7 ${formatClockMs(item.recording.durationMs)}` }) : null
1191
+ item.recording?.durationMs ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: ` \xB7 ${formatClockMs(item.recording.durationMs)}` }) : null
1073
1192
  ] }) }),
1074
- /* @__PURE__ */ jsx12(Box10, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs9(Text10, { children: [
1075
- /* @__PURE__ */ jsx12(Text10, { color: links.webUrl ? "cyan" : "gray", children: "o open" }),
1076
- /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: " \xB7 " }),
1077
- /* @__PURE__ */ jsx12(Text10, { color: links.webUrl ? "cyan" : "gray", children: "w web" }),
1078
- /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: " \xB7 " }),
1079
- /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "m mac app (soon)" }),
1080
- /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: " \xB7 " }),
1081
- /* @__PURE__ */ jsx12(Text10, { color: links.webUrl ? "cyan" : "gray", children: "c copy" })
1193
+ /* @__PURE__ */ jsx13(Box11, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs10(Text11, { children: [
1194
+ /* @__PURE__ */ jsx13(Text11, { color: links.webUrl ? "cyan" : "gray", children: "o open" }),
1195
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: " \xB7 " }),
1196
+ /* @__PURE__ */ jsx13(Text11, { color: links.webUrl ? "cyan" : "gray", children: "w web" }),
1197
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: " \xB7 " }),
1198
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "m mac app (soon)" }),
1199
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: " \xB7 " }),
1200
+ /* @__PURE__ */ jsx13(Text11, { color: links.webUrl ? "cyan" : "gray", children: "c copy" })
1082
1201
  ] }) }),
1083
- /* @__PURE__ */ jsx12(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text10, { dimColor: true, children: [
1202
+ /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text11, { dimColor: true, children: [
1084
1203
  "esc back \xB7 t transcript",
1085
1204
  item.transcriptId ? "" : " (when ready)",
1086
1205
  " \xB7 q quit"
@@ -1097,24 +1216,24 @@ function StatusLine({
1097
1216
  const elapsed = item.startedAt ? ` \xB7 ${formatClockMs(nowMs - item.startedAt)} elapsed` : "";
1098
1217
  if (fraction != null) {
1099
1218
  const pct = Math.round(fraction * 100);
1100
- return /* @__PURE__ */ jsxs9(Text10, { children: [
1219
+ return /* @__PURE__ */ jsxs10(Text11, { children: [
1101
1220
  `${progressBar(fraction)} ${pct}% ${formatClockMs(item.processedDurationMs)} / ${formatClockMs(
1102
1221
  item.recording?.durationMs
1103
1222
  )}`,
1104
- /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: elapsed })
1223
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: elapsed })
1105
1224
  ] });
1106
1225
  }
1107
1226
  const spinner = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"][spinnerFrame % 10];
1108
- return /* @__PURE__ */ jsxs9(Text10, { children: [
1227
+ return /* @__PURE__ */ jsxs10(Text11, { children: [
1109
1228
  `${spinner} transcribing\u2026`,
1110
- /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: elapsed })
1229
+ /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: elapsed })
1111
1230
  ] });
1112
1231
  }
1113
1232
  if (item.status === "succeeded")
1114
- return /* @__PURE__ */ jsx12(Text10, { children: item.transcriptId ? "transcript ready" : "done" });
1115
- if (item.status === "queued") return /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "waiting to start\u2026" });
1116
- if (item.status === "failed") return /* @__PURE__ */ jsx12(Text10, { color: "red", children: "transcription failed" });
1117
- return /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: item.status });
1233
+ return /* @__PURE__ */ jsx13(Text11, { children: item.transcriptId ? "transcript ready" : "done" });
1234
+ if (item.status === "queued") return /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "waiting to start\u2026" });
1235
+ if (item.status === "failed") return /* @__PURE__ */ jsx13(Text11, { color: "red", children: "transcription failed" });
1236
+ return /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: item.status });
1118
1237
  }
1119
1238
  function TimelineRow({
1120
1239
  label,
@@ -1127,10 +1246,10 @@ function TimelineRow({
1127
1246
  const glyph = failed ? "\u2717" : done ? "\u2713" : running ? "\u280B" : "\u25CB";
1128
1247
  const color = failed ? "red" : done ? "green" : running ? "cyan" : "gray";
1129
1248
  const age = at ? formatAge(at, nowMs) : running ? "now" : "";
1130
- return /* @__PURE__ */ jsxs9(Box10, { children: [
1131
- /* @__PURE__ */ jsx12(Text10, { color, children: ` ${glyph} ` }),
1132
- /* @__PURE__ */ jsx12(Text10, { dimColor: !done && !running, children: label }),
1133
- age ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: ` ${age}` }) : null
1249
+ return /* @__PURE__ */ jsxs10(Box11, { children: [
1250
+ /* @__PURE__ */ jsx13(Text11, { color, children: ` ${glyph} ` }),
1251
+ /* @__PURE__ */ jsx13(Text11, { dimColor: !done && !running, children: label }),
1252
+ age ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: ` ${age}` }) : null
1134
1253
  ] });
1135
1254
  }
1136
1255
  var init_JobDetailView = __esm({
@@ -1141,9 +1260,9 @@ var init_JobDetailView = __esm({
1141
1260
  });
1142
1261
 
1143
1262
  // src/tui/RecordingDetailView.tsx
1144
- import React4, { useMemo as useMemo2, useState as useState3 } from "react";
1145
- import { Box as Box11, Text as Text11, useInput as useInput3 } from "ink";
1146
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1263
+ import React6, { useMemo as useMemo2, useState as useState4 } from "react";
1264
+ import { Box as Box12, Text as Text12, useInput as useInput3 } from "ink";
1265
+ import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1147
1266
  function RecordingDetailView({
1148
1267
  item,
1149
1268
  nowMs,
@@ -1151,9 +1270,9 @@ function RecordingDetailView({
1151
1270
  audio
1152
1271
  }) {
1153
1272
  const size = useTerminalSize();
1154
- const [tab, setTab] = useState3("summary");
1155
- const [scroll, setScroll] = useState3(0);
1156
- const [chapterSel, setChapterSel] = useState3(0);
1273
+ const [tab, setTab] = useState4("summary");
1274
+ const [scroll, setScroll] = useState4(0);
1275
+ const [chapterSel, setChapterSel] = useState4(0);
1157
1276
  const style = recordingStatusStyle(item.status);
1158
1277
  const links = resolveRecordingLinks(item.recordingId, item.origin);
1159
1278
  const title = recordingTitle2(item);
@@ -1206,20 +1325,20 @@ function RecordingDetailView({
1206
1325
  else if (input === "g") setScroll(0);
1207
1326
  else if (input === "G") setScroll(segWin.maxScroll);
1208
1327
  });
1209
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", paddingX: 1, children: [
1210
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "\u2039 Recordings" }),
1211
- /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { bold: true, color: "green", children: title }) }),
1212
- /* @__PURE__ */ jsxs10(Text11, { children: [
1213
- /* @__PURE__ */ jsx13(Text11, { color: style.color, children: `${style.glyph} ${style.label}` }),
1214
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: ` ${formatAge(item.createdAt, nowMs) || "\u2014"}` })
1328
+ return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingX: 1, children: [
1329
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "\u2039 Recordings" }),
1330
+ /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { bold: true, color: "green", children: title }) }),
1331
+ /* @__PURE__ */ jsxs11(Text12, { children: [
1332
+ /* @__PURE__ */ jsx14(Text12, { color: style.color, children: `${style.glyph} ${style.label}` }),
1333
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: ` ${formatAge(item.createdAt, nowMs) || "\u2014"}` })
1215
1334
  ] }),
1216
- meta3 ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: meta3 }) : null,
1217
- /* @__PURE__ */ jsx13(AudioActionRow, { item, audio }),
1218
- !item.activeTranscriptId ? /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Transcript not available yet" }) }) : transcript === "loading" || transcript === void 0 ? /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Loading\u2026" }) }) : transcript === "error" ? /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "(transcript unavailable)" }) }) : /* @__PURE__ */ jsxs10(Fragment3, { children: [
1219
- /* @__PURE__ */ jsx13(TabBar, { active: tab }),
1220
- /* @__PURE__ */ jsx13(Box11, { marginTop: 1, flexDirection: "column", children: tab === "summary" ? /* @__PURE__ */ jsx13(SummaryPane, { summary, budget: paneBudget }) : tab === "chapters" ? /* @__PURE__ */ jsx13(ChaptersPane, { chapters, win: chapWin, selectedIndex: chapterSel }) : /* @__PURE__ */ jsx13(TranscriptPane, { segments, win: segWin }) })
1335
+ meta3 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: meta3 }) : null,
1336
+ /* @__PURE__ */ jsx14(AudioActionRow, { item, audio }),
1337
+ !item.activeTranscriptId ? /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Transcript not available yet" }) }) : transcript === "loading" || transcript === void 0 ? /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Loading\u2026" }) }) : transcript === "error" ? /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "(transcript unavailable)" }) }) : /* @__PURE__ */ jsxs11(Fragment3, { children: [
1338
+ /* @__PURE__ */ jsx14(TabBar, { active: tab }),
1339
+ /* @__PURE__ */ jsx14(Box12, { marginTop: 1, flexDirection: "column", children: tab === "summary" ? /* @__PURE__ */ jsx14(SummaryPane, { summary, budget: paneBudget }) : tab === "chapters" ? /* @__PURE__ */ jsx14(ChaptersPane, { chapters, win: chapWin, selectedIndex: chapterSel }) : /* @__PURE__ */ jsx14(TranscriptPane, { segments, win: segWin }) })
1221
1340
  ] }),
1222
- /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text11, { dimColor: true, children: [
1341
+ /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text12, { dimColor: true, children: [
1223
1342
  ready ? "tab switch" : "",
1224
1343
  ready && tab === "chapters" ? " \xB7 \u2191\u2193 select \xB7 \u23CE jump" : "",
1225
1344
  scrollable ? " \xB7 \u2191\u2193 scroll" : "",
@@ -1232,9 +1351,9 @@ function RecordingDetailView({
1232
1351
  ] });
1233
1352
  }
1234
1353
  function TabBar({ active }) {
1235
- return /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: TAB_ORDER.map((tab, i) => /* @__PURE__ */ jsxs10(React4.Fragment, { children: [
1236
- i > 0 ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: " " }) : null,
1237
- tab === active ? /* @__PURE__ */ jsx13(Text11, { inverse: true, bold: true, children: ` ${TAB_LABEL[tab]} ` }) : /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: ` ${TAB_LABEL[tab]} ` })
1354
+ return /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: TAB_ORDER.map((tab, i) => /* @__PURE__ */ jsxs11(React6.Fragment, { children: [
1355
+ i > 0 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: " " }) : null,
1356
+ tab === active ? /* @__PURE__ */ jsx14(Text12, { inverse: true, bold: true, children: ` ${TAB_LABEL[tab]} ` }) : /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: ` ${TAB_LABEL[tab]} ` })
1238
1357
  ] }, tab)) });
1239
1358
  }
1240
1359
  function AudioActionRow({
@@ -1245,30 +1364,30 @@ function AudioActionRow({
1245
1364
  const status = audio?.status ?? "idle";
1246
1365
  let line;
1247
1366
  if (!ready) {
1248
- line = /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "Audio available once the recording is ready" });
1367
+ line = /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Audio available once the recording is ready" });
1249
1368
  } else if (status === "downloading") {
1250
- line = /* @__PURE__ */ jsx13(Text11, { color: "cyan", children: "Downloading audio\u2026" });
1369
+ line = /* @__PURE__ */ jsx14(Text12, { color: "cyan", children: "Downloading audio\u2026" });
1251
1370
  } else if (status === "opening") {
1252
- line = /* @__PURE__ */ jsx13(Text11, { color: "cyan", children: "Opening\u2026" });
1371
+ line = /* @__PURE__ */ jsx14(Text12, { color: "cyan", children: "Opening\u2026" });
1253
1372
  } else if (status === "error") {
1254
- line = /* @__PURE__ */ jsx13(Text11, { color: "red", children: audio?.error ? `Audio failed: ${audio.error}` : "Audio failed" });
1373
+ line = /* @__PURE__ */ jsx14(Text12, { color: "red", children: audio?.error ? `Audio failed: ${audio.error}` : "Audio failed" });
1255
1374
  } else if (status === "ready" && audio?.localPath) {
1256
- line = /* @__PURE__ */ jsxs10(Text11, { children: [
1257
- /* @__PURE__ */ jsx13(Text11, { color: "green", children: "\u2713 Downloaded " }),
1258
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, wrap: "truncate-middle", children: audio.localPath })
1375
+ line = /* @__PURE__ */ jsxs11(Text12, { children: [
1376
+ /* @__PURE__ */ jsx14(Text12, { color: "green", children: "\u2713 Downloaded " }),
1377
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, wrap: "truncate-middle", children: audio.localPath })
1259
1378
  ] });
1260
1379
  } else {
1261
- line = /* @__PURE__ */ jsxs10(Text11, { children: [
1262
- /* @__PURE__ */ jsx13(Text11, { color: "cyan", children: "o" }),
1263
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: " open in player \xB7 " }),
1264
- /* @__PURE__ */ jsx13(Text11, { color: "cyan", children: "d" }),
1265
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: " download \xB7 " }),
1266
- /* @__PURE__ */ jsx13(Text11, { color: "cyan", children: "f" }),
1267
- /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: " reveal in Finder" })
1380
+ line = /* @__PURE__ */ jsxs11(Text12, { children: [
1381
+ /* @__PURE__ */ jsx14(Text12, { color: "cyan", children: "o" }),
1382
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: " open in player \xB7 " }),
1383
+ /* @__PURE__ */ jsx14(Text12, { color: "cyan", children: "d" }),
1384
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: " download \xB7 " }),
1385
+ /* @__PURE__ */ jsx14(Text12, { color: "cyan", children: "f" }),
1386
+ /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: " reveal in Finder" })
1268
1387
  ] });
1269
1388
  }
1270
- return /* @__PURE__ */ jsxs10(Box11, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
1271
- /* @__PURE__ */ jsx13(Text11, { color: ready ? "cyan" : "gray", children: "\u266A " }),
1389
+ return /* @__PURE__ */ jsxs11(Box12, { marginTop: 1, borderStyle: "round", borderColor: "gray", paddingX: 1, children: [
1390
+ /* @__PURE__ */ jsx14(Text12, { color: ready ? "cyan" : "gray", children: "\u266A " }),
1272
1391
  line
1273
1392
  ] });
1274
1393
  }
@@ -1277,12 +1396,12 @@ function SummaryPane({
1277
1396
  budget
1278
1397
  }) {
1279
1398
  if (!summary || summary.status !== "succeeded" || !summary.tldr) {
1280
- return /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `Summary ${summary?.status ?? "unavailable"}` });
1399
+ return /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `Summary ${summary?.status ?? "unavailable"}` });
1281
1400
  }
1282
1401
  const points = (summary.keyPoints ?? []).slice(0, Math.max(1, budget - 4));
1283
- return /* @__PURE__ */ jsxs10(Fragment3, { children: [
1284
- /* @__PURE__ */ jsx13(Text11, { children: summary.tldr }),
1285
- points.length > 0 ? /* @__PURE__ */ jsx13(Box11, { marginTop: 1, flexDirection: "column", children: points.map((point, i) => /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `\u2022 ${point}` }, i)) }) : null
1402
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
1403
+ /* @__PURE__ */ jsx14(Text12, { children: summary.tldr }),
1404
+ points.length > 0 ? /* @__PURE__ */ jsx14(Box12, { marginTop: 1, flexDirection: "column", children: points.map((point, i) => /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `\u2022 ${point}` }, i)) }) : null
1286
1405
  ] });
1287
1406
  }
1288
1407
  function ChaptersPane({
@@ -1290,32 +1409,32 @@ function ChaptersPane({
1290
1409
  win,
1291
1410
  selectedIndex
1292
1411
  }) {
1293
- if (chapters.length === 0) return /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "No chapters" });
1294
- return /* @__PURE__ */ jsxs10(Fragment3, { children: [
1412
+ if (chapters.length === 0) return /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "No chapters" });
1413
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
1295
1414
  chapters.slice(win.start, win.end).map((chapter, i) => {
1296
1415
  const index = win.start + i;
1297
1416
  const selected = index === selectedIndex;
1298
- return /* @__PURE__ */ jsxs10(Text11, { wrap: "truncate-end", children: [
1299
- /* @__PURE__ */ jsx13(Text11, { color: "cyan", children: selected ? "\u25B8 " : " " }),
1300
- /* @__PURE__ */ jsx13(Text11, { color: "blue", children: `[${formatClockMs(chapter.startMs)}] ` }),
1301
- /* @__PURE__ */ jsx13(Text11, { bold: selected, children: chapter.title })
1417
+ return /* @__PURE__ */ jsxs11(Text12, { wrap: "truncate-end", children: [
1418
+ /* @__PURE__ */ jsx14(Text12, { color: "cyan", children: selected ? "\u25B8 " : " " }),
1419
+ /* @__PURE__ */ jsx14(Text12, { color: "blue", children: `[${formatClockMs(chapter.startMs)}] ` }),
1420
+ /* @__PURE__ */ jsx14(Text12, { bold: selected, children: chapter.title })
1302
1421
  ] }, index);
1303
1422
  }),
1304
- win.end < chapters.length || win.start > 0 ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: ` ${selectedIndex + 1} / ${chapters.length}` }) : null
1423
+ win.end < chapters.length || win.start > 0 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: ` ${selectedIndex + 1} / ${chapters.length}` }) : null
1305
1424
  ] });
1306
1425
  }
1307
1426
  function TranscriptPane({
1308
1427
  segments,
1309
1428
  win
1310
1429
  }) {
1311
- if (segments.length === 0) return /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: "(no segments)" });
1312
- return /* @__PURE__ */ jsxs10(Fragment3, { children: [
1313
- segments.slice(win.start, win.end).map((seg, i) => /* @__PURE__ */ jsxs10(Text11, { children: [
1314
- /* @__PURE__ */ jsx13(Text11, { color: "blue", children: `[${formatClockMs(seg.startMs)}] ` }),
1315
- seg.speaker ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: `${seg.speaker} ` }) : null,
1316
- /* @__PURE__ */ jsx13(Text11, { children: seg.text })
1430
+ if (segments.length === 0) return /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "(no segments)" });
1431
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
1432
+ segments.slice(win.start, win.end).map((seg, i) => /* @__PURE__ */ jsxs11(Text12, { children: [
1433
+ /* @__PURE__ */ jsx14(Text12, { color: "blue", children: `[${formatClockMs(seg.startMs)}] ` }),
1434
+ seg.speaker ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `${seg.speaker} ` }) : null,
1435
+ /* @__PURE__ */ jsx14(Text12, { children: seg.text })
1317
1436
  ] }, win.start + i)),
1318
- win.maxScroll > 0 ? /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: ` ${win.start + 1}\u2013${win.end} / ${segments.length}` }) : null
1437
+ win.maxScroll > 0 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: ` ${win.start + 1}\u2013${win.end} / ${segments.length}` }) : null
1319
1438
  ] });
1320
1439
  }
1321
1440
  var TAB_ORDER, TAB_LABEL;
@@ -1335,12 +1454,12 @@ var init_RecordingDetailView = __esm({
1335
1454
  });
1336
1455
 
1337
1456
  // src/tui/TranscriptView.tsx
1338
- import { useMemo as useMemo3, useState as useState4 } from "react";
1339
- import { Box as Box12, Text as Text12, useInput as useInput4 } from "ink";
1340
- import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1457
+ import { useMemo as useMemo3, useState as useState5 } from "react";
1458
+ import { Box as Box13, Text as Text13, useInput as useInput4 } from "ink";
1459
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
1341
1460
  function TranscriptView({ loading, data, error: error51 }) {
1342
1461
  const size = useTerminalSize();
1343
- const [scroll, setScroll] = useState4(0);
1462
+ const [scroll, setScroll] = useState5(0);
1344
1463
  const segments = data?.segments ?? [];
1345
1464
  const innerWidth = Math.max(10, size.columns - 2);
1346
1465
  const heights = useMemo3(
@@ -1362,42 +1481,42 @@ function TranscriptView({ loading, data, error: error51 }) {
1362
1481
  else if (input === "G") setScroll(win.maxScroll);
1363
1482
  });
1364
1483
  if (loading) {
1365
- return /* @__PURE__ */ jsx14(Box12, { paddingX: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "Loading transcript\u2026" }) });
1484
+ return /* @__PURE__ */ jsx15(Box13, { paddingX: 1, children: /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Loading transcript\u2026" }) });
1366
1485
  }
1367
1486
  if (error51) {
1368
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingX: 1, children: [
1369
- /* @__PURE__ */ jsxs11(Text12, { color: "red", children: [
1487
+ return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 1, children: [
1488
+ /* @__PURE__ */ jsxs12(Text13, { color: "red", children: [
1370
1489
  "! ",
1371
1490
  error51
1372
1491
  ] }),
1373
- /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "q / esc / \u2190 back" })
1492
+ /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "q / esc / \u2190 back" })
1374
1493
  ] });
1375
1494
  }
1376
1495
  if (!data) {
1377
- return /* @__PURE__ */ jsx14(Box12, { paddingX: 1, children: /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "No transcript." }) });
1496
+ return /* @__PURE__ */ jsx15(Box13, { paddingX: 1, children: /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "No transcript." }) });
1378
1497
  }
1379
1498
  const title = data.summary?.title ?? "Transcript";
1380
1499
  const total = segments.length;
1381
1500
  const more = win.maxScroll > 0;
1382
1501
  const position = total === 0 ? "" : `${win.start + 1}\u2013${win.end} / ${total}`;
1383
- return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingX: 1, children: [
1384
- /* @__PURE__ */ jsxs11(Text12, { bold: true, color: "green", children: [
1502
+ return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 1, children: [
1503
+ /* @__PURE__ */ jsxs12(Text13, { bold: true, color: "green", children: [
1385
1504
  title,
1386
- more ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: ` ${position}` }) : null
1505
+ more ? /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: ` ${position}` }) : null
1387
1506
  ] }),
1388
- /* @__PURE__ */ jsx14(Box12, { marginTop: 1, flexDirection: "column", children: total === 0 ? /* @__PURE__ */ jsx14(Text12, { children: data.text }) : segments.slice(win.start, win.end).map((segment, index) => /* @__PURE__ */ jsxs11(Text12, { children: [
1389
- /* @__PURE__ */ jsxs11(Text12, { dimColor: true, children: [
1507
+ /* @__PURE__ */ jsx15(Box13, { marginTop: 1, flexDirection: "column", children: total === 0 ? /* @__PURE__ */ jsx15(Text13, { children: data.text }) : segments.slice(win.start, win.end).map((segment, index) => /* @__PURE__ */ jsxs12(Text13, { children: [
1508
+ /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
1390
1509
  "[",
1391
1510
  formatClockMs(segment.startMs),
1392
1511
  "] "
1393
1512
  ] }),
1394
- segment.speaker ? /* @__PURE__ */ jsxs11(Text12, { color: "cyan", children: [
1513
+ segment.speaker ? /* @__PURE__ */ jsxs12(Text13, { color: "cyan", children: [
1395
1514
  segment.speaker,
1396
1515
  ": "
1397
1516
  ] }) : null,
1398
1517
  segment.text
1399
1518
  ] }, win.start + index)) }),
1400
- /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text12, { dimColor: true, children: [
1519
+ /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { dimColor: true, children: [
1401
1520
  more ? "\u2191\u2193 scroll \xB7 PgUp/PgDn \xB7 g/G top/bottom \xB7 " : "",
1402
1521
  "q / esc / \u2190 back"
1403
1522
  ] }) })
@@ -1412,8 +1531,8 @@ var init_TranscriptView = __esm({
1412
1531
  });
1413
1532
 
1414
1533
  // src/tui/PermissionPreflightView.tsx
1415
- import { Box as Box13, Text as Text13 } from "ink";
1416
- import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
1534
+ import { Box as Box14, Text as Text14 } from "ink";
1535
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1417
1536
  function statusGlyph2(status) {
1418
1537
  switch (status) {
1419
1538
  case "granted":
@@ -1429,41 +1548,41 @@ function PermissionPreflightView({
1429
1548
  }) {
1430
1549
  const allGranted = items.length > 0 && items.every((item) => item.status === "granted" && !item.requiresProcessRestart);
1431
1550
  const hasRestartRequired = items.some((item) => item.requiresProcessRestart);
1432
- return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", paddingX: 1, children: [
1433
- /* @__PURE__ */ jsxs12(Text13, { children: [
1434
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "\u2039 " }),
1435
- /* @__PURE__ */ jsx15(Text13, { bold: true, color: "cyan", children: "Recording permissions" })
1551
+ return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, children: [
1552
+ /* @__PURE__ */ jsxs13(Text14, { children: [
1553
+ /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "\u2039 " }),
1554
+ /* @__PURE__ */ jsx16(Text14, { bold: true, color: "cyan", children: "Recording permissions" })
1436
1555
  ] }),
1437
- /* @__PURE__ */ jsx15(Box13, { marginTop: 1, flexDirection: "column", children: items.length === 0 ? /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Checking permissions\u2026" }) : items.map((item) => {
1556
+ /* @__PURE__ */ jsx16(Box14, { marginTop: 1, flexDirection: "column", children: items.length === 0 ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Checking permissions\u2026" }) : items.map((item) => {
1438
1557
  const status = statusGlyph2(item.status);
1439
1558
  const restart = item.requiresProcessRestart === true;
1440
1559
  const color = restart ? "yellow" : status.color;
1441
1560
  const hint = restart ? item.hint ?? `${item.name} enabled. Run recappi record again to start.` : item.status === "granted" ? void 0 : item.hint ?? DEFAULT_HINTS[item.name];
1442
- return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", children: [
1443
- /* @__PURE__ */ jsxs12(Text13, { children: [
1444
- /* @__PURE__ */ jsx15(Text13, { bold: true, color, children: status.glyph }),
1445
- /* @__PURE__ */ jsx15(Text13, { bold: true, children: ` ${item.name}` }),
1446
- /* @__PURE__ */ jsx15(Text13, { color, children: ` ${status.label}` })
1561
+ return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
1562
+ /* @__PURE__ */ jsxs13(Text14, { children: [
1563
+ /* @__PURE__ */ jsx16(Text14, { bold: true, color, children: status.glyph }),
1564
+ /* @__PURE__ */ jsx16(Text14, { bold: true, children: ` ${item.name}` }),
1565
+ /* @__PURE__ */ jsx16(Text14, { color, children: ` ${status.label}` })
1447
1566
  ] }),
1448
- hint ? /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: ` ${hint}` }) : null
1567
+ hint ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: ` ${hint}` }) : null
1449
1568
  ] }, item.name);
1450
1569
  }) }),
1451
- /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: allGranted ? /* @__PURE__ */ jsx15(Text13, { bold: true, color: "green", children: "\u2713 All set \u2014 ready to record." }) : hasRestartRequired ? /* @__PURE__ */ jsxs12(Text13, { children: [
1452
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Run recappi record again to start, or press " }),
1453
- /* @__PURE__ */ jsx15(Text13, { bold: true, color: "cyan", children: "r" }),
1454
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: " to retry." })
1455
- ] }) : /* @__PURE__ */ jsxs12(Text13, { children: [
1456
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: "Grant the permissions above, then press " }),
1457
- /* @__PURE__ */ jsx15(Text13, { bold: true, color: "cyan", children: "r" }),
1458
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: " to recheck." })
1570
+ /* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: allGranted ? /* @__PURE__ */ jsx16(Text14, { bold: true, color: "green", children: "\u2713 All set \u2014 ready to record." }) : hasRestartRequired ? /* @__PURE__ */ jsxs13(Text14, { children: [
1571
+ /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Run recappi record again to start, or press " }),
1572
+ /* @__PURE__ */ jsx16(Text14, { bold: true, color: "cyan", children: "r" }),
1573
+ /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " to retry." })
1574
+ ] }) : /* @__PURE__ */ jsxs13(Text14, { children: [
1575
+ /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Grant the permissions above, then press " }),
1576
+ /* @__PURE__ */ jsx16(Text14, { bold: true, color: "cyan", children: "r" }),
1577
+ /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " to recheck." })
1459
1578
  ] }) }),
1460
- /* @__PURE__ */ jsx15(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { children: [
1461
- /* @__PURE__ */ jsx15(Text13, { color: "cyan", children: "r" }),
1462
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: " recheck \xB7 " }),
1463
- /* @__PURE__ */ jsx15(Text13, { color: "cyan", children: "o" }),
1464
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: " open System Settings \xB7 " }),
1465
- /* @__PURE__ */ jsx15(Text13, { color: "cyan", children: "esc" }),
1466
- /* @__PURE__ */ jsx15(Text13, { dimColor: true, children: " back" })
1579
+ /* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { children: [
1580
+ /* @__PURE__ */ jsx16(Text14, { color: "cyan", children: "r" }),
1581
+ /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " recheck \xB7 " }),
1582
+ /* @__PURE__ */ jsx16(Text14, { color: "cyan", children: "o" }),
1583
+ /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " open System Settings \xB7 " }),
1584
+ /* @__PURE__ */ jsx16(Text14, { color: "cyan", children: "esc" }),
1585
+ /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " back" })
1467
1586
  ] }) })
1468
1587
  ] });
1469
1588
  }
@@ -1479,21 +1598,21 @@ var init_PermissionPreflightView = __esm({
1479
1598
  });
1480
1599
 
1481
1600
  // src/tui/RecordSetupView.tsx
1482
- import { useState as useState5 } from "react";
1483
- import { Box as Box14, Text as Text14, useInput as useInput5 } from "ink";
1484
- import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1601
+ import { useState as useState6 } from "react";
1602
+ import { Box as Box15, Text as Text15, useInput as useInput5 } from "ink";
1603
+ import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1485
1604
  function RecordSetupView({
1486
1605
  model,
1487
1606
  onStart,
1488
1607
  onCancel
1489
1608
  }) {
1490
1609
  const size = useTerminalSize();
1491
- const [srcIdx, setSrcIdx] = useState5(0);
1492
- const [includeMic, setIncludeMic] = useState5(true);
1493
- const [micIdx, setMicIdx] = useState5(
1610
+ const [srcIdx, setSrcIdx] = useState6(0);
1611
+ const [includeMic, setIncludeMic] = useState6(true);
1612
+ const [micIdx, setMicIdx] = useState6(
1494
1613
  () => Math.max(0, model.microphones?.findIndex((device) => device.isDefault) ?? 0)
1495
1614
  );
1496
- const [sceneIdx, setSceneIdx] = useState5(0);
1615
+ const [sceneIdx, setSceneIdx] = useState6(0);
1497
1616
  const sources = model.sources;
1498
1617
  const microphones = model.microphones ?? [];
1499
1618
  const selected = sources[Math.min(srcIdx, Math.max(0, sources.length - 1))];
@@ -1518,21 +1637,21 @@ function RecordSetupView({
1518
1637
  });
1519
1638
  } else if (key.escape) onCancel();
1520
1639
  });
1521
- const sourceList = /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
1522
- /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "SOURCE" }),
1640
+ const sourceList = /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
1641
+ /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "SOURCE" }),
1523
1642
  sources.map((s, i) => {
1524
1643
  const on = i === srcIdx;
1525
- return /* @__PURE__ */ jsxs13(Text14, { color: on ? "cyan" : void 0, wrap: "truncate-end", children: [
1644
+ return /* @__PURE__ */ jsxs14(Text15, { color: on ? "cyan" : void 0, wrap: "truncate-end", children: [
1526
1645
  on ? "\u25B8 \u25CF " : " \u25CB ",
1527
1646
  s.label
1528
1647
  ] }, s.id);
1529
1648
  }),
1530
- !hasAppSource ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "No app-specific sources available right now" }) : null
1649
+ !hasAppSource ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "No app-specific sources available right now" }) : null
1531
1650
  ] });
1532
- const capturePlan = /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
1533
- /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "CAPTURE PLAN" }),
1534
- /* @__PURE__ */ jsx16(Text14, { children: includeMic ? `${selected?.label ?? "System audio"} + microphone` : `${selected?.label ?? "System audio"} only` }),
1535
- /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: includeMic ? "Mic is mixed into the recording" : "Mic stays muted" })
1651
+ const capturePlan = /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
1652
+ /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "CAPTURE PLAN" }),
1653
+ /* @__PURE__ */ jsx17(Text15, { children: includeMic ? `${selected?.label ?? "System audio"} + microphone` : `${selected?.label ?? "System audio"} only` }),
1654
+ /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: includeMic ? "Mic is mixed into the recording" : "Mic stays muted" })
1536
1655
  ] });
1537
1656
  const shortcuts = [
1538
1657
  hasMultipleSources ? "\u2191\u2193 source" : void 0,
@@ -1542,31 +1661,31 @@ function RecordSetupView({
1542
1661
  "\u23CE start recording",
1543
1662
  "esc cancel"
1544
1663
  ].filter(Boolean).join(" \xB7 ");
1545
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, children: [
1546
- /* @__PURE__ */ jsx16(Text14, { bold: true, color: "green", children: "New recording" }),
1547
- /* @__PURE__ */ jsxs13(Box14, { marginTop: 1, flexDirection: wide ? "row" : "column", children: [
1548
- /* @__PURE__ */ jsx16(Box14, { flexGrow: 1, flexDirection: "column", children: sourceList }),
1549
- /* @__PURE__ */ jsx16(Box14, { marginLeft: wide ? 4 : 0, marginTop: wide ? 0 : 1, children: capturePlan })
1664
+ return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, children: [
1665
+ /* @__PURE__ */ jsx17(Text15, { bold: true, color: "green", children: "New recording" }),
1666
+ /* @__PURE__ */ jsxs14(Box15, { marginTop: 1, flexDirection: wide ? "row" : "column", children: [
1667
+ /* @__PURE__ */ jsx17(Box15, { flexGrow: 1, flexDirection: "column", children: sourceList }),
1668
+ /* @__PURE__ */ jsx17(Box15, { marginLeft: wide ? 4 : 0, marginTop: wide ? 0 : 1, children: capturePlan })
1550
1669
  ] }),
1551
- /* @__PURE__ */ jsxs13(Box14, { marginTop: 1, flexDirection: "column", children: [
1552
- /* @__PURE__ */ jsxs13(Text14, { children: [
1553
- /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Microphone " }),
1554
- /* @__PURE__ */ jsx16(Text14, { color: includeMic ? "green" : "gray", children: includeMic ? "[x] include mic" : "[ ] include mic" }),
1555
- /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " (space)" })
1670
+ /* @__PURE__ */ jsxs14(Box15, { marginTop: 1, flexDirection: "column", children: [
1671
+ /* @__PURE__ */ jsxs14(Text15, { children: [
1672
+ /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Microphone " }),
1673
+ /* @__PURE__ */ jsx17(Text15, { color: includeMic ? "green" : "gray", children: includeMic ? "[x] include mic" : "[ ] include mic" }),
1674
+ /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " (space)" })
1556
1675
  ] }),
1557
- selectedMic ? /* @__PURE__ */ jsxs13(Text14, { dimColor: !includeMic, children: [
1558
- /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Mic device " }),
1559
- /* @__PURE__ */ jsx16(Text14, { children: selectedMic.label }),
1560
- selectedMic.isDefault ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " \xB7 default" }) : null,
1561
- includeMic && hasMultipleMicrophones ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " (m to change)" }) : null
1676
+ selectedMic ? /* @__PURE__ */ jsxs14(Text15, { dimColor: !includeMic, children: [
1677
+ /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Mic device " }),
1678
+ /* @__PURE__ */ jsx17(Text15, { children: selectedMic.label }),
1679
+ selectedMic.isDefault ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " \xB7 default" }) : null,
1680
+ includeMic && hasMultipleMicrophones ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " (m to change)" }) : null
1562
1681
  ] }) : null,
1563
- model.scenes.length > 0 ? /* @__PURE__ */ jsxs13(Text14, { children: [
1564
- /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Scene " }),
1565
- /* @__PURE__ */ jsx16(Text14, { children: model.scenes[sceneIdx]?.label ?? "Default" }),
1566
- model.scenes.length > 1 ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " (s to change)" }) : null
1682
+ model.scenes.length > 0 ? /* @__PURE__ */ jsxs14(Text15, { children: [
1683
+ /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Scene " }),
1684
+ /* @__PURE__ */ jsx17(Text15, { children: model.scenes[sceneIdx]?.label ?? "Default" }),
1685
+ model.scenes.length > 1 ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " (s to change)" }) : null
1567
1686
  ] }) : null
1568
1687
  ] }),
1569
- /* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: shortcuts }) })
1688
+ /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: shortcuts }) })
1570
1689
  ] });
1571
1690
  }
1572
1691
  var init_RecordSetupView = __esm({
@@ -1576,125 +1695,6 @@ var init_RecordSetupView = __esm({
1576
1695
  }
1577
1696
  });
1578
1697
 
1579
- // src/tui/RecordingHeroScreen.tsx
1580
- import { useEffect as useEffect2, useState as useState6 } from "react";
1581
- import { Box as Box15, Text as Text15 } from "ink";
1582
- import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1583
- function waveform(samples, width) {
1584
- if (width <= 0) return "";
1585
- const tail = samples.slice(-width);
1586
- const pad = width - tail.length;
1587
- const cells = tail.map((v) => {
1588
- const i = Math.max(0, Math.min(BLOCKS.length - 1, Math.round(Math.max(0, Math.min(1, v)) * (BLOCKS.length - 1))));
1589
- return BLOCKS[i];
1590
- });
1591
- return "\u2581".repeat(Math.max(0, pad)) + cells.join("");
1592
- }
1593
- function RecordingHeroScreen({
1594
- telemetry,
1595
- artifact,
1596
- canTranscribe = false,
1597
- canPause = false,
1598
- now = () => Date.now()
1599
- }) {
1600
- const size = useTerminalSize();
1601
- const [tick, setTick] = useState6(() => now());
1602
- const [wave, setWave] = useState6([]);
1603
- useEffect2(() => {
1604
- if (telemetry.level == null) return;
1605
- const lvl = Math.max(telemetry.level.system ?? 0, telemetry.level.mic ?? 0);
1606
- setWave((w) => [...w.slice(-512), lvl]);
1607
- }, [telemetry.level]);
1608
- useEffect2(() => {
1609
- const id = setInterval(() => setTick(now()), 1e3);
1610
- return () => clearInterval(id);
1611
- }, []);
1612
- const elapsed = telemetry.startedAtMs != null ? formatClockMs(Math.max(0, tick - telemetry.startedAtMs)) : "00:00";
1613
- const innerWidth = Math.max(10, size.columns - 4);
1614
- if (telemetry.status === "stopped") {
1615
- const handoff = stoppedHandoffCopy(artifact, canTranscribe);
1616
- const meta3 = [
1617
- telemetry.durationMs != null ? formatClockMs(telemetry.durationMs) : null,
1618
- formatBytes2(telemetry.sizeBytes) || null
1619
- ].filter(Boolean).join(" \xB7 ");
1620
- return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, children: [
1621
- /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "recappi \xB7 Recording" }),
1622
- /* @__PURE__ */ jsxs14(Box15, { marginTop: 1, flexDirection: "column", children: [
1623
- /* @__PURE__ */ jsx17(Text15, { color: "green", children: "\u2713 Saved to your Mac" }),
1624
- meta3 ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: meta3 }) : null,
1625
- telemetry.savedPath ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, wrap: "truncate-middle", children: telemetry.savedPath }) : null
1626
- ] }),
1627
- /* @__PURE__ */ jsxs14(Box15, { marginTop: 1, flexDirection: "column", children: [
1628
- /* @__PURE__ */ jsx17(Text15, { color: handoff.tone === "red" ? "red" : handoff.tone === "green" ? "green" : void 0, dimColor: handoff.tone === "dim", children: handoff.text }),
1629
- artifact?.error ? /* @__PURE__ */ jsx17(Text15, { color: "red", wrap: "truncate-end", children: artifact.error }) : null
1630
- ] })
1631
- ] });
1632
- }
1633
- if (telemetry.status === "error") {
1634
- return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, children: [
1635
- /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "recappi \xB7 Recording" }),
1636
- /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text15, { color: "red", children: telemetry.error ? `Recording error: ${telemetry.error}` : "Recording error" }) }),
1637
- /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "esc back" }) })
1638
- ] });
1639
- }
1640
- const paused = telemetry.status === "paused";
1641
- const starting = telemetry.status === "starting" || telemetry.status === "stopping";
1642
- const badge = paused ? "\u23F8 PAUSED" : starting ? "\u2026" : "\u23FA REC";
1643
- return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, height: size.rows, children: [
1644
- /* @__PURE__ */ jsxs14(Text15, { children: [
1645
- /* @__PURE__ */ jsx17(Text15, { bold: true, color: "green", children: "recappi" }),
1646
- /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " \xB7 Recording" })
1647
- ] }),
1648
- /* @__PURE__ */ jsxs14(Box15, { flexGrow: 1, flexDirection: "column", justifyContent: "center", alignItems: "center", children: [
1649
- /* @__PURE__ */ jsx17(Text15, { bold: true, color: paused ? "yellow" : "red", children: badge }),
1650
- /* @__PURE__ */ jsx17(Text15, { bold: true, children: elapsed }),
1651
- /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: telemetry.level == null ? (
1652
- // No level telemetry yet — show honest activity, not a flat meter that
1653
- // looks like silence (the elapsed timer above proves it's live).
1654
- /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: paused ? "Paused" : `Capturing audio${".".repeat(Math.floor(tick / 1e3) % 3 + 1)}` })
1655
- ) : /* @__PURE__ */ jsx17(Text15, { color: paused ? "gray" : "red", children: waveform(wave, innerWidth) }) }),
1656
- /* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
1657
- telemetry.sourceLabel,
1658
- telemetry.micEnabled ? " + Microphone" : ""
1659
- ] }) })
1660
- ] }),
1661
- /* @__PURE__ */ jsx17(Box15, { children: /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
1662
- "q stop & save",
1663
- canPause ? ` \xB7 p ${paused ? "resume" : "pause"}` : ""
1664
- ] }) })
1665
- ] });
1666
- }
1667
- function stoppedHandoffCopy(artifact, canTranscribe) {
1668
- if (artifact?.uploadStatus === "uploading") {
1669
- return { text: "Uploading\u2026", tone: "normal" };
1670
- }
1671
- if (artifact?.transcriptionStatus === "processing") {
1672
- return { text: "Transcribing\u2026", tone: "normal" };
1673
- }
1674
- if (artifact?.transcriptionStatus === "queued") {
1675
- return { text: "Transcription queued \xB7 \u23CE open recording \xB7 n not now", tone: "green" };
1676
- }
1677
- if (artifact?.transcriptionStatus === "ready") {
1678
- return { text: "Transcription ready \xB7 \u23CE open recording \xB7 n not now", tone: "green" };
1679
- }
1680
- if (artifact?.uploadStatus === "failed" || artifact?.transcriptionStatus === "failed") {
1681
- return { text: "Transcription failed \xB7 \u23CE retry \xB7 n not now", tone: "red" };
1682
- }
1683
- if (!canTranscribe || !artifact?.audioPath) {
1684
- return { text: "Saved locally \xB7 n back", tone: "dim" };
1685
- }
1686
- return { text: "Transcribe now? \u23CE yes \xB7 n not now", tone: "normal" };
1687
- }
1688
- var BLOCKS;
1689
- var init_RecordingHeroScreen = __esm({
1690
- "src/tui/RecordingHeroScreen.tsx"() {
1691
- "use strict";
1692
- init_format();
1693
- init_terminal();
1694
- BLOCKS = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
1695
- }
1696
- });
1697
-
1698
1698
  // src/tui/AppShell.tsx
1699
1699
  import { useCallback, useEffect as useEffect3, useState as useState7 } from "react";
1700
1700
  import { Box as Box16, Text as Text16, useApp, useInput as useInput6 } from "ink";
@@ -2449,7 +2449,7 @@ __export(tui_exports, {
2449
2449
  runDashboard: () => runDashboard,
2450
2450
  useTerminalSize: () => useTerminalSize
2451
2451
  });
2452
- import React9 from "react";
2452
+ import React10 from "react";
2453
2453
  import { render as render2 } from "ink";
2454
2454
  import { spawn as spawn3 } from "child_process";
2455
2455
  function openUrl(url2) {
@@ -2470,7 +2470,7 @@ function copyText(text) {
2470
2470
  async function runDashboard(deps) {
2471
2471
  const renderApp = deps.renderApp ?? render2;
2472
2472
  const app = renderApp(
2473
- React9.createElement(AppShell, {
2473
+ React10.createElement(AppShell, {
2474
2474
  fetchJobs: deps.fetchJobs,
2475
2475
  fetchTranscript: deps.fetchTranscript,
2476
2476
  fetchRecordings: deps.fetchRecordings,
@@ -20091,6 +20091,7 @@ function readCliVersion() {
20091
20091
  var CLI_VERSION = readCliVersion();
20092
20092
 
20093
20093
  // src/record.tsx
20094
+ import React4 from "react";
20094
20095
  import {
20095
20096
  chmodSync,
20096
20097
  cpSync,
@@ -20421,7 +20422,8 @@ function createFifo(path6) {
20421
20422
 
20422
20423
  // src/record.tsx
20423
20424
  init_LiveCaptionsScreen();
20424
- import { jsx as jsx3 } from "react/jsx-runtime";
20425
+ init_RecordingHeroScreen();
20426
+ import { jsx as jsx4 } from "react/jsx-runtime";
20425
20427
  var SIDECAR_COMMAND_ENV = "RECAPPI_MINI_SIDECAR";
20426
20428
  var SIDECAR_HELPER_NAME = "RecappiMiniSidecar";
20427
20429
  var SIDECAR_APP_BUNDLE_NAME = "Recappi Recorder.app";
@@ -20437,6 +20439,14 @@ async function recordViaSidecar(opts) {
20437
20439
  renderApp: opts.runtime?.renderApp,
20438
20440
  now: opts.runtime?.now
20439
20441
  });
20442
+ } else if (opts.renderHero) {
20443
+ liveRenderer = opts.runtime?.createHeroRenderer?.(session.source) ?? createInkRecordingHeroRenderer({
20444
+ source: session.source,
20445
+ sourceLabel: opts.includeSystemAudio === false ? "Microphone" : "System audio \xB7 all apps",
20446
+ micEnabled: opts.includeMicrophone !== false,
20447
+ renderApp: opts.runtime?.renderApp,
20448
+ now: opts.runtime?.now
20449
+ });
20440
20450
  }
20441
20451
  if (liveRenderer) {
20442
20452
  await liveRenderer.waitUntilStop();
@@ -20866,7 +20876,7 @@ function createInkLiveRenderer(opts) {
20866
20876
  });
20867
20877
  const renderApp = opts.renderApp ?? render;
20868
20878
  const app = renderApp(
20869
- /* @__PURE__ */ jsx3(
20879
+ /* @__PURE__ */ jsx4(
20870
20880
  RecordLiveScreen,
20871
20881
  {
20872
20882
  source: opts.source,
@@ -20889,7 +20899,60 @@ function RecordLiveScreen({
20889
20899
  useInput2((input, key) => {
20890
20900
  if (input === "q" || key.escape || key.leftArrow) onStop();
20891
20901
  });
20892
- return /* @__PURE__ */ jsx3(LiveCaptionsScreen, { source, now });
20902
+ return /* @__PURE__ */ jsx4(LiveCaptionsScreen, { source, now });
20903
+ }
20904
+ function createInkRecordingHeroRenderer(opts) {
20905
+ let resolveStop;
20906
+ const stopped = new Promise((resolve) => {
20907
+ resolveStop = resolve;
20908
+ });
20909
+ const onStop = () => resolveStop?.();
20910
+ const onSigint = () => onStop();
20911
+ process.once("SIGINT", onSigint);
20912
+ const renderApp = opts.renderApp ?? render;
20913
+ const app = renderApp(
20914
+ /* @__PURE__ */ jsx4(
20915
+ RecordingHeroLive,
20916
+ {
20917
+ source: opts.source,
20918
+ sourceLabel: opts.sourceLabel,
20919
+ micEnabled: opts.micEnabled,
20920
+ onStop,
20921
+ now: opts.now ?? Date.now
20922
+ }
20923
+ ),
20924
+ { alternateScreen: true, interactive: true }
20925
+ );
20926
+ return {
20927
+ waitUntilStop: () => stopped,
20928
+ close: () => {
20929
+ process.removeListener("SIGINT", onSigint);
20930
+ app.unmount();
20931
+ }
20932
+ };
20933
+ }
20934
+ function RecordingHeroLive({
20935
+ source,
20936
+ sourceLabel,
20937
+ micEnabled,
20938
+ onStop,
20939
+ now
20940
+ }) {
20941
+ const [telemetry, setTelemetry] = React4.useState(() => ({
20942
+ status: "recording",
20943
+ startedAtMs: now(),
20944
+ sourceLabel,
20945
+ micEnabled
20946
+ }));
20947
+ React4.useEffect(() => {
20948
+ return source.onEvent((event) => {
20949
+ setTelemetry((prev) => applyRecordingEventToTelemetry(prev, event));
20950
+ });
20951
+ }, [source]);
20952
+ useInput2((input, key) => {
20953
+ if (input === "q" || key.escape) onStop();
20954
+ });
20955
+ return /* @__PURE__ */ jsx4(RecordingHeroScreen, { telemetry, now });
20893
20956
  }
20894
20957
 
20895
20958
  // src/cli.ts
@@ -21094,9 +21157,6 @@ async function runCli(deps = {}) {
21094
21157
  hint: "Run recappi auth login, or import the Recappi Mini session with recappi auth import-macos."
21095
21158
  });
21096
21159
  }
21097
- if (mode === "human" && isTTY && !parsed.live) {
21098
- stderr("Recording\u2026 press Ctrl-C to stop.\n");
21099
- }
21100
21160
  const data = await recordViaSidecar({
21101
21161
  account: {
21102
21162
  backendOrigin: auth.origin,
@@ -21114,6 +21174,7 @@ async function runCli(deps = {}) {
21114
21174
  transcriptionLanguage: parsed.transcriptionLanguage,
21115
21175
  sidecarCommand: parsed.sidecarCommand,
21116
21176
  renderLive: parsed.live === true && mode === "human" && isTTY,
21177
+ renderHero: parsed.live !== true && mode === "human" && isTTY,
21117
21178
  runtime: deps.recordRuntime
21118
21179
  });
21119
21180
  renderSuccess("record", data, render3);