recappi 0.1.45 → 0.1.46
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 +478 -169
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -59,10 +59,19 @@ function applyRecordingEventToTelemetry(telemetry, event) {
|
|
|
59
59
|
}
|
|
60
60
|
if (event.type === "audio.level") {
|
|
61
61
|
const level = levelFromRmsDb(event.rmsDb);
|
|
62
|
+
const levelDb3 = typeof event.rmsDb === "number" && Number.isFinite(event.rmsDb) ? event.rmsDb : void 0;
|
|
62
63
|
if (event.input === "microphone") {
|
|
63
|
-
return {
|
|
64
|
+
return {
|
|
65
|
+
...telemetry,
|
|
66
|
+
level: { ...telemetry.level, mic: level },
|
|
67
|
+
levelDb: { ...telemetry.levelDb, ...levelDb3 != null ? { mic: levelDb3 } : {} }
|
|
68
|
+
};
|
|
64
69
|
}
|
|
65
|
-
return {
|
|
70
|
+
return {
|
|
71
|
+
...telemetry,
|
|
72
|
+
level: { ...telemetry.level, system: level },
|
|
73
|
+
levelDb: { ...telemetry.levelDb, ...levelDb3 != null ? { system: levelDb3 } : {} }
|
|
74
|
+
};
|
|
66
75
|
}
|
|
67
76
|
if (event.type === "error") {
|
|
68
77
|
if (isLiveCaptionSidecarError(event.code)) return telemetry;
|
|
@@ -202,10 +211,6 @@ function jobDetail(item) {
|
|
|
202
211
|
if (item.status === "failed") return "failed";
|
|
203
212
|
return "";
|
|
204
213
|
}
|
|
205
|
-
function padCell(text, width) {
|
|
206
|
-
if (text.length > width) return `${text.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
207
|
-
return text.padEnd(width);
|
|
208
|
-
}
|
|
209
214
|
function charWidth(code) {
|
|
210
215
|
if (code >= 4352 && code <= 4447 || // Hangul Jamo
|
|
211
216
|
code === 9001 || code === 9002 || code >= 11904 && code <= 12350 || // CJK radicals … Kangxi
|
|
@@ -228,23 +233,6 @@ function displayWidth(text) {
|
|
|
228
233
|
for (const ch of text) width += charWidth(ch.codePointAt(0) ?? 0);
|
|
229
234
|
return width;
|
|
230
235
|
}
|
|
231
|
-
function padDisplay(text, width) {
|
|
232
|
-
const w = displayWidth(text);
|
|
233
|
-
if (w === width) return text;
|
|
234
|
-
if (w < width) return text + " ".repeat(width - w);
|
|
235
|
-
let out = "";
|
|
236
|
-
let acc = 0;
|
|
237
|
-
for (const ch of text) {
|
|
238
|
-
const cw = charWidth(ch.codePointAt(0) ?? 0);
|
|
239
|
-
if (acc + cw > width - 1) break;
|
|
240
|
-
out += ch;
|
|
241
|
-
acc += cw;
|
|
242
|
-
}
|
|
243
|
-
out += "\u2026";
|
|
244
|
-
acc += 1;
|
|
245
|
-
if (acc < width) out += " ".repeat(width - acc);
|
|
246
|
-
return out;
|
|
247
|
-
}
|
|
248
236
|
function countJobs(items) {
|
|
249
237
|
const counts = {
|
|
250
238
|
total: items.length,
|
|
@@ -617,6 +605,43 @@ function waveform(samples, width) {
|
|
|
617
605
|
});
|
|
618
606
|
return "\u2581".repeat(Math.max(0, pad)) + cells.join("");
|
|
619
607
|
}
|
|
608
|
+
function levelDb(level) {
|
|
609
|
+
if (level <= 0.03) return "silent";
|
|
610
|
+
return `${Math.round(level * 60 - 60)} dB`;
|
|
611
|
+
}
|
|
612
|
+
function MeterRow({
|
|
613
|
+
label,
|
|
614
|
+
samples,
|
|
615
|
+
level,
|
|
616
|
+
paused,
|
|
617
|
+
width
|
|
618
|
+
}) {
|
|
619
|
+
const silent = level <= 0.03;
|
|
620
|
+
return /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
621
|
+
/* @__PURE__ */ jsx3(Box2, { width: 9, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: label }) }),
|
|
622
|
+
/* @__PURE__ */ jsx3(Box2, { width, children: /* @__PURE__ */ jsx3(Text2, { color: paused ? "gray" : silent ? "yellow" : "red", children: waveform(samples, width) }) }),
|
|
623
|
+
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: ` ${paused ? "paused" : levelDb(level)}` })
|
|
624
|
+
] });
|
|
625
|
+
}
|
|
626
|
+
function ProgressBar({ fraction, width = 12 }) {
|
|
627
|
+
const f = Math.max(0, Math.min(1, fraction));
|
|
628
|
+
const filled = Math.round(f * width);
|
|
629
|
+
return /* @__PURE__ */ jsxs2(Text2, { color: "cyan", children: [
|
|
630
|
+
"\u2593".repeat(filled),
|
|
631
|
+
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "\u2591".repeat(Math.max(0, width - filled)) })
|
|
632
|
+
] });
|
|
633
|
+
}
|
|
634
|
+
function stoppedPhase(artifact) {
|
|
635
|
+
if (!artifact) return null;
|
|
636
|
+
if (artifact.uploadStatus === "uploading") {
|
|
637
|
+
return { label: "Uploading to Recappi Cloud", fraction: artifact.uploadProgress };
|
|
638
|
+
}
|
|
639
|
+
if (artifact.uploadStatus === "queued") return { label: "Queued to upload" };
|
|
640
|
+
if (artifact.transcriptionStatus === "processing") {
|
|
641
|
+
return { label: "Transcribing", fraction: artifact.transcriptionProgress };
|
|
642
|
+
}
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
620
645
|
function RecordingHeroScreen({
|
|
621
646
|
telemetry,
|
|
622
647
|
artifact,
|
|
@@ -627,11 +652,12 @@ function RecordingHeroScreen({
|
|
|
627
652
|
}) {
|
|
628
653
|
const size = useTerminalSize();
|
|
629
654
|
const [tick, setTick] = useState3(() => now());
|
|
630
|
-
const [
|
|
655
|
+
const [waveSys, setWaveSys] = useState3([]);
|
|
656
|
+
const [waveMic, setWaveMic] = useState3([]);
|
|
631
657
|
useEffect2(() => {
|
|
632
658
|
if (telemetry.level == null) return;
|
|
633
|
-
|
|
634
|
-
|
|
659
|
+
setWaveSys((w) => [...w.slice(-256), telemetry.level.system ?? 0]);
|
|
660
|
+
setWaveMic((w) => [...w.slice(-256), telemetry.level.mic ?? 0]);
|
|
635
661
|
}, [telemetry.level]);
|
|
636
662
|
useEffect2(() => {
|
|
637
663
|
const id = setInterval(() => setTick(now()), 1e3);
|
|
@@ -641,17 +667,27 @@ function RecordingHeroScreen({
|
|
|
641
667
|
const innerWidth = Math.max(10, size.columns - 4);
|
|
642
668
|
if (telemetry.status === "stopped") {
|
|
643
669
|
const handoff = stoppedHandoffCopy(artifact, canTranscribe);
|
|
670
|
+
const phase = stoppedPhase(artifact);
|
|
644
671
|
const meta3 = [
|
|
645
672
|
telemetry.durationMs != null ? formatClockMs(telemetry.durationMs) : null,
|
|
646
673
|
formatBytes2(telemetry.sizeBytes) || null
|
|
647
674
|
].filter(Boolean).join(" \xB7 ");
|
|
675
|
+
const saved = artifact?.uploadStatus === "uploaded" ? "\u2713 Saved to Recappi Cloud" : "\u2713 Saved to your Mac";
|
|
648
676
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
649
677
|
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "recappi \xB7 Recording" }),
|
|
650
678
|
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
|
|
651
|
-
/* @__PURE__ */ jsx3(Text2, { color: "green", children:
|
|
679
|
+
/* @__PURE__ */ jsx3(Text2, { color: "green", children: saved }),
|
|
652
680
|
meta3 ? /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: meta3 }) : null,
|
|
653
681
|
telemetry.savedPath ? /* @__PURE__ */ jsx3(Text2, { dimColor: true, wrap: "truncate-middle", children: telemetry.savedPath }) : null
|
|
654
682
|
] }),
|
|
683
|
+
phase ? /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, children: [
|
|
684
|
+
/* @__PURE__ */ jsx3(Text2, { color: "cyan", children: `\u25D0 ${phase.label}` }),
|
|
685
|
+
phase.fraction != null ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
686
|
+
/* @__PURE__ */ jsx3(Text2, { children: " " }),
|
|
687
|
+
/* @__PURE__ */ jsx3(ProgressBar, { fraction: phase.fraction }),
|
|
688
|
+
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: ` ${Math.round(phase.fraction * 100)}%` })
|
|
689
|
+
] }) : /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "\u2026" })
|
|
690
|
+
] }) : null,
|
|
655
691
|
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
|
|
656
692
|
/* @__PURE__ */ jsx3(Text2, { color: handoff.tone === "red" ? "red" : handoff.tone === "green" ? "green" : void 0, dimColor: handoff.tone === "dim", children: handoff.text }),
|
|
657
693
|
artifact?.error ? /* @__PURE__ */ jsx3(Text2, { color: "red", wrap: "truncate-end", children: artifact.error }) : null
|
|
@@ -668,24 +704,33 @@ function RecordingHeroScreen({
|
|
|
668
704
|
const paused = telemetry.status === "paused";
|
|
669
705
|
const starting = telemetry.status === "starting" || telemetry.status === "stopping";
|
|
670
706
|
const badge = paused ? "\u23F8 PAUSED" : starting ? "\u2026" : "\u23FA REC";
|
|
707
|
+
const meterW = Math.max(10, Math.min(48, innerWidth - 22));
|
|
708
|
+
const sizeStr = telemetry.sizeBytes ? formatBytes2(telemetry.sizeBytes) : "";
|
|
709
|
+
const context = [telemetry.sourceLabel, telemetry.micEnabled ? "Microphone" : null, sizeStr || null].filter(Boolean).join(" \xB7 ");
|
|
671
710
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, height: size.rows, children: [
|
|
672
711
|
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
673
712
|
/* @__PURE__ */ jsx3(Text2, { bold: true, color: "green", children: "recappi" }),
|
|
674
713
|
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: " \xB7 Recording" })
|
|
675
714
|
] }),
|
|
676
|
-
/* @__PURE__ */ jsxs2(Box2, {
|
|
677
|
-
/* @__PURE__ */
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
715
|
+
/* @__PURE__ */ jsxs2(Box2, { marginTop: 1, paddingX: 1, flexGrow: 1, flexDirection: "column", children: [
|
|
716
|
+
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
717
|
+
/* @__PURE__ */ jsx3(Text2, { bold: true, color: paused ? "yellow" : "red", children: badge }),
|
|
718
|
+
/* @__PURE__ */ jsx3(Text2, { children: " " }),
|
|
719
|
+
/* @__PURE__ */ jsx3(Text2, { bold: true, children: elapsed })
|
|
720
|
+
] }),
|
|
721
|
+
/* @__PURE__ */ jsx3(Box2, { marginTop: 1, flexDirection: "column", children: telemetry.level == null ? (
|
|
722
|
+
// No level telemetry yet — honest activity, not a flat meter that
|
|
723
|
+
// reads as silence (the elapsed timer above proves it's live).
|
|
682
724
|
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: paused ? "Paused" : `Capturing audio${".".repeat(Math.floor(tick / 1e3) % 3 + 1)}` })
|
|
683
|
-
) : /* @__PURE__ */
|
|
684
|
-
|
|
685
|
-
telemetry.
|
|
686
|
-
telemetry.micEnabled ? " + Microphone" : ""
|
|
725
|
+
) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
726
|
+
/* @__PURE__ */ jsx3(MeterRow, { label: "System", samples: waveSys, level: telemetry.level.system ?? 0, paused, width: meterW }),
|
|
727
|
+
telemetry.micEnabled ? /* @__PURE__ */ jsx3(MeterRow, { label: "Mic", samples: waveMic, level: telemetry.level.mic ?? 0, paused, width: meterW }) : null
|
|
687
728
|
] }) }),
|
|
688
|
-
|
|
729
|
+
/* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: context }) }),
|
|
730
|
+
captions ? /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
|
|
731
|
+
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "LIVE CAPTIONS" }),
|
|
732
|
+
/* @__PURE__ */ jsx3(HeroCaptions, { state: captions })
|
|
733
|
+
] }) : null
|
|
689
734
|
] }),
|
|
690
735
|
/* @__PURE__ */ jsx3(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
691
736
|
"q stop & save",
|
|
@@ -702,7 +747,7 @@ function HeroCaptions({ state }) {
|
|
|
702
747
|
return /* @__PURE__ */ jsx3(Text2, { color: captionError ? "yellow" : void 0, dimColor: !captionError, wrap: "truncate-end", children: captionError ?? (state.status === "live" ? "Listening for speech\u2026" : liveCaptionStatusLabel(state.status)) });
|
|
703
748
|
}
|
|
704
749
|
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
705
|
-
recent.map((line) => /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column",
|
|
750
|
+
recent.map((line) => /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
706
751
|
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate-end", children: [
|
|
707
752
|
line.speaker ? `${line.speaker}: ` : "",
|
|
708
753
|
line.text
|
|
@@ -715,11 +760,8 @@ function HeroCaptions({ state }) {
|
|
|
715
760
|
] });
|
|
716
761
|
}
|
|
717
762
|
function stoppedHandoffCopy(artifact, canTranscribe) {
|
|
718
|
-
if (artifact?.uploadStatus === "uploading") {
|
|
719
|
-
return { text: "
|
|
720
|
-
}
|
|
721
|
-
if (artifact?.transcriptionStatus === "processing") {
|
|
722
|
-
return { text: "Transcribing\u2026", tone: "normal" };
|
|
763
|
+
if (artifact?.uploadStatus === "uploading" || artifact?.transcriptionStatus === "processing") {
|
|
764
|
+
return { text: "esc run in background", tone: "dim" };
|
|
723
765
|
}
|
|
724
766
|
if (artifact?.transcriptionStatus === "queued") {
|
|
725
767
|
return { text: "Transcription queued \xB7 \u23CE open recording \xB7 n not now", tone: "green" };
|
|
@@ -763,13 +805,16 @@ function AccountView({ status }) {
|
|
|
763
805
|
function AccountBody({ status }) {
|
|
764
806
|
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
765
807
|
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
766
|
-
/* @__PURE__ */
|
|
808
|
+
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
809
|
+
/* @__PURE__ */ jsx5(Text3, { color: "green", children: "\u25CF " }),
|
|
810
|
+
/* @__PURE__ */ jsx5(Text3, { bold: true, children: status.email ?? status.userId ?? "Signed in" })
|
|
811
|
+
] }),
|
|
767
812
|
status.email && status.userId ? /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: status.userId }) : null,
|
|
768
813
|
/* @__PURE__ */ jsx5(Text3, { dimColor: true, children: `origin ${status.origin}` })
|
|
769
814
|
] }),
|
|
770
815
|
status.billing ? /* @__PURE__ */ jsx5(Usage, { billing: status.billing }) : null,
|
|
771
816
|
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
772
|
-
/* @__PURE__ */ jsx5(Text3, {
|
|
817
|
+
/* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "LOCAL STORE" }),
|
|
773
818
|
/* @__PURE__ */ jsx5(Text3, { dimColor: true, wrap: "truncate-middle", children: status.localStore.path }),
|
|
774
819
|
/* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
775
820
|
`${status.localStore.accountScopedArtifacts} artifact${status.localStore.accountScopedArtifacts === 1 ? "" : "s"} for this account`,
|
|
@@ -783,6 +828,7 @@ function Usage({ billing }) {
|
|
|
783
828
|
const minutesUsed = billing.minutesUsed;
|
|
784
829
|
const storageCap = billing.storageCapBytes;
|
|
785
830
|
return /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
831
|
+
/* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "USAGE" }),
|
|
786
832
|
/* @__PURE__ */ jsxs3(Text3, { children: [
|
|
787
833
|
/* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "Plan " }),
|
|
788
834
|
/* @__PURE__ */ jsx5(Text3, { bold: true, children: billing.tier })
|
|
@@ -883,9 +929,10 @@ function JobRow({
|
|
|
883
929
|
const glyph = statusGlyph(item.status, spinnerFrame);
|
|
884
930
|
const title = item.recording?.title ?? item.recordingId;
|
|
885
931
|
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
886
|
-
/* @__PURE__ */ jsx7(Text5, { color: "cyan", children: selected ? "\u25B8
|
|
887
|
-
/* @__PURE__ */ jsx7(Text5, { color: style.color, children:
|
|
888
|
-
/* @__PURE__ */ jsx7(
|
|
932
|
+
/* @__PURE__ */ jsx7(Box5, { width: 3, children: /* @__PURE__ */ jsx7(Text5, { color: "cyan", children: selected ? "\u25B8" : "" }) }),
|
|
933
|
+
/* @__PURE__ */ jsx7(Box5, { width: 2, children: /* @__PURE__ */ jsx7(Text5, { color: style.color, children: glyph }) }),
|
|
934
|
+
/* @__PURE__ */ jsx7(Box5, { width: 13, children: /* @__PURE__ */ jsx7(Text5, { color: style.color, children: style.label }) }),
|
|
935
|
+
/* @__PURE__ */ jsx7(Box5, { width: 26, children: /* @__PURE__ */ jsx7(Text5, { bold: selected, wrap: "truncate-end", children: title }) }),
|
|
889
936
|
/* @__PURE__ */ jsx7(Text5, { dimColor: !selected, children: jobDetail(item) })
|
|
890
937
|
] });
|
|
891
938
|
}
|
|
@@ -946,7 +993,7 @@ function recordingLayout(columns) {
|
|
|
946
993
|
const showWhen = usable >= 54;
|
|
947
994
|
const title = Math.max(
|
|
948
995
|
10,
|
|
949
|
-
usable - MARKER_W - GLYPH_W - LENGTH_W - (showWhen ? WHEN_W : 0)
|
|
996
|
+
usable - MARKER_W - GLYPH_W - LENGTH_W - (showWhen ? WHEN_W : 0) - DL_W
|
|
950
997
|
);
|
|
951
998
|
return { title, showWhen };
|
|
952
999
|
}
|
|
@@ -963,33 +1010,25 @@ function RecordingRow({
|
|
|
963
1010
|
const { glyph, color } = recordingProcessingState(item, jobStatus, spinnerFrame);
|
|
964
1011
|
const duration3 = item.durationMs ? formatClockMs(item.durationMs) : "\u2014";
|
|
965
1012
|
return /* @__PURE__ */ jsxs6(Box7, { children: [
|
|
966
|
-
/* @__PURE__ */ jsx9(Text7, { color: "cyan", children: selected ? "\u25B8
|
|
967
|
-
/* @__PURE__ */ jsx9(Text7, { color, children:
|
|
968
|
-
/* @__PURE__ */ jsx9(Text7, { bold: selected, children:
|
|
969
|
-
/* @__PURE__ */ jsx9(Text7, { dimColor: true, children:
|
|
970
|
-
showWhen ? /* @__PURE__ */ jsx9(Text7, { dimColor: true, children:
|
|
971
|
-
/* @__PURE__ */ jsx9(Text7, { color: "green", children: downloaded ? "
|
|
1013
|
+
/* @__PURE__ */ jsx9(Box7, { width: MARKER_W, children: /* @__PURE__ */ jsx9(Text7, { color: "cyan", children: selected ? "\u25B8" : "" }) }),
|
|
1014
|
+
/* @__PURE__ */ jsx9(Box7, { width: GLYPH_W, children: /* @__PURE__ */ jsx9(Text7, { color, children: glyph }) }),
|
|
1015
|
+
/* @__PURE__ */ jsx9(Box7, { width: title, children: /* @__PURE__ */ jsx9(Text7, { bold: selected, wrap: "truncate-end", children: recordingTitle2(item) }) }),
|
|
1016
|
+
/* @__PURE__ */ jsx9(Box7, { width: LENGTH_W, justifyContent: "flex-end", children: /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: duration3 }) }),
|
|
1017
|
+
showWhen ? /* @__PURE__ */ jsx9(Box7, { width: WHEN_W, justifyContent: "flex-end", children: /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: formatAge(item.createdAt, nowMs) }) }) : null,
|
|
1018
|
+
/* @__PURE__ */ jsx9(Box7, { width: DL_W, justifyContent: "flex-end", children: /* @__PURE__ */ jsx9(Text7, { color: "green", children: downloaded ? "\u2913" : "" }) })
|
|
972
1019
|
] });
|
|
973
1020
|
}
|
|
974
|
-
|
|
975
|
-
const { title, showWhen } = recordingLayout(columns);
|
|
976
|
-
return /* @__PURE__ */ jsxs6(Box7, { children: [
|
|
977
|
-
/* @__PURE__ */ jsx9(Text7, { dimColor: true, children: padDisplay("", MARKER_W + GLYPH_W) }),
|
|
978
|
-
/* @__PURE__ */ jsx9(Text7, { dimColor: true, children: padDisplay("TITLE", title) }),
|
|
979
|
-
/* @__PURE__ */ jsx9(Text7, { dimColor: true, children: padDisplay("LENGTH", LENGTH_W) }),
|
|
980
|
-
showWhen ? /* @__PURE__ */ jsx9(Text7, { dimColor: true, children: "WHEN" }) : null
|
|
981
|
-
] });
|
|
982
|
-
}
|
|
983
|
-
var UUID_RE, MARKER_W, GLYPH_W, LENGTH_W, WHEN_W;
|
|
1021
|
+
var UUID_RE, MARKER_W, GLYPH_W, LENGTH_W, WHEN_W, DL_W;
|
|
984
1022
|
var init_RecordingRow = __esm({
|
|
985
1023
|
"src/tui/RecordingRow.tsx"() {
|
|
986
1024
|
"use strict";
|
|
987
1025
|
init_format();
|
|
988
1026
|
UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
989
|
-
MARKER_W =
|
|
1027
|
+
MARKER_W = 3;
|
|
990
1028
|
GLYPH_W = 2;
|
|
991
|
-
LENGTH_W =
|
|
1029
|
+
LENGTH_W = 8;
|
|
992
1030
|
WHEN_W = 9;
|
|
1031
|
+
DL_W = 3;
|
|
993
1032
|
}
|
|
994
1033
|
});
|
|
995
1034
|
|
|
@@ -1009,28 +1048,25 @@ function RecordingsView({
|
|
|
1009
1048
|
if (items.length === 0) {
|
|
1010
1049
|
return /* @__PURE__ */ jsx10(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text8, { dimColor: true, children: "No recordings yet \u2014 run: recappi upload <file>" }) });
|
|
1011
1050
|
}
|
|
1012
|
-
return /* @__PURE__ */
|
|
1013
|
-
|
|
1014
|
-
items.
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
] }, item.recordingId);
|
|
1032
|
-
})
|
|
1033
|
-
] });
|
|
1051
|
+
return /* @__PURE__ */ jsx10(Box8, { marginTop: 1, flexDirection: "column", children: items.map((item, index) => {
|
|
1052
|
+
const bucket = dateBucket(item.createdAt, nowMs);
|
|
1053
|
+
const showHeader = index === 0 || bucket !== dateBucket(items[index - 1].createdAt, nowMs);
|
|
1054
|
+
return /* @__PURE__ */ jsxs7(React5.Fragment, { children: [
|
|
1055
|
+
showHeader ? /* @__PURE__ */ jsx10(Box8, { marginTop: index === 0 ? 0 : 1, children: /* @__PURE__ */ jsx10(Text8, { bold: true, dimColor: true, children: bucket }) }) : null,
|
|
1056
|
+
/* @__PURE__ */ jsx10(
|
|
1057
|
+
RecordingRow,
|
|
1058
|
+
{
|
|
1059
|
+
item,
|
|
1060
|
+
selected: index === selectedIndex,
|
|
1061
|
+
nowMs,
|
|
1062
|
+
columns,
|
|
1063
|
+
jobStatus: jobStatusByRecording?.get(item.recordingId),
|
|
1064
|
+
downloaded: downloadedRecordingIds?.has(item.recordingId) ?? false,
|
|
1065
|
+
spinnerFrame
|
|
1066
|
+
}
|
|
1067
|
+
)
|
|
1068
|
+
] }, item.recordingId);
|
|
1069
|
+
}) });
|
|
1034
1070
|
}
|
|
1035
1071
|
var init_RecordingsView = __esm({
|
|
1036
1072
|
"src/tui/RecordingsView.tsx"() {
|
|
@@ -1060,13 +1096,14 @@ function PeekBody({
|
|
|
1060
1096
|
const meta3 = [
|
|
1061
1097
|
item.durationMs ? formatClockMs(item.durationMs) : null,
|
|
1062
1098
|
formatBytes2(item.sizeBytes) || null,
|
|
1063
|
-
item.
|
|
1064
|
-
].filter(Boolean).join("
|
|
1099
|
+
formatAge(item.createdAt, nowMs)
|
|
1100
|
+
].filter(Boolean).join(" \xB7 ");
|
|
1065
1101
|
return /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1066
|
-
/* @__PURE__ */ jsx11(Text9, { bold: true,
|
|
1067
|
-
/* @__PURE__ */
|
|
1068
|
-
|
|
1069
|
-
|
|
1102
|
+
/* @__PURE__ */ jsx11(Text9, { bold: true, wrap: "truncate-end", children: recordingTitle2(item) }),
|
|
1103
|
+
/* @__PURE__ */ jsxs8(Box9, { children: [
|
|
1104
|
+
/* @__PURE__ */ jsx11(Text9, { color: style.color, children: `${style.glyph} ${style.label}` }),
|
|
1105
|
+
meta3 ? /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: ` ${meta3}` }) : null
|
|
1106
|
+
] }),
|
|
1070
1107
|
/* @__PURE__ */ jsx11(Box9, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx11(SummarySection, { item, summary }) }),
|
|
1071
1108
|
/* @__PURE__ */ jsx11(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "\u23CE open \xB7 t transcript \xB7 o web" }) })
|
|
1072
1109
|
] });
|
|
@@ -1083,7 +1120,7 @@ function SummarySection({
|
|
|
1083
1120
|
}
|
|
1084
1121
|
const points = (summary.keyPoints ?? []).slice(0, 3);
|
|
1085
1122
|
return /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1086
|
-
/* @__PURE__ */ jsx11(Text9, {
|
|
1123
|
+
/* @__PURE__ */ jsx11(Text9, { dimColor: true, children: "SUMMARY" }),
|
|
1087
1124
|
/* @__PURE__ */ jsx11(Text9, { children: summary.tldr }),
|
|
1088
1125
|
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
|
|
1089
1126
|
] });
|
|
@@ -1098,7 +1135,7 @@ var init_RecordingPeek = __esm({
|
|
|
1098
1135
|
|
|
1099
1136
|
// src/tui/OverviewView.tsx
|
|
1100
1137
|
import { Box as Box10, Text as Text10 } from "ink";
|
|
1101
|
-
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1138
|
+
import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1102
1139
|
function OverviewView({
|
|
1103
1140
|
recordings,
|
|
1104
1141
|
jobs,
|
|
@@ -1119,12 +1156,21 @@ function OverviewView({
|
|
|
1119
1156
|
const queued = stats?.jobs.queued ?? jobCounts.queued;
|
|
1120
1157
|
return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", children: [
|
|
1121
1158
|
/* @__PURE__ */ jsxs9(Box10, { children: [
|
|
1122
|
-
/* @__PURE__ */ jsx12(Text10, { dimColor: true, children: "Recordings " }),
|
|
1123
1159
|
/* @__PURE__ */ jsx12(Text10, { bold: true, children: stats?.recordings.total ?? recordings.length }),
|
|
1124
|
-
|
|
1125
|
-
stats?.recordings.
|
|
1126
|
-
|
|
1127
|
-
|
|
1160
|
+
/* @__PURE__ */ jsx12(Text10, { dimColor: true, children: " recordings" }),
|
|
1161
|
+
stats?.recordings.ready != null ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
1162
|
+
/* @__PURE__ */ jsx12(Text10, { dimColor: true, children: " \xB7 " }),
|
|
1163
|
+
/* @__PURE__ */ jsx12(Text10, { color: "green", children: `${stats.recordings.ready} ready` })
|
|
1164
|
+
] }) : null,
|
|
1165
|
+
running > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
1166
|
+
/* @__PURE__ */ jsx12(Text10, { dimColor: true, children: " \xB7 " }),
|
|
1167
|
+
/* @__PURE__ */ jsx12(Text10, { color: "cyan", children: `${running} transcribing` })
|
|
1168
|
+
] }) : null,
|
|
1169
|
+
queued > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
1170
|
+
/* @__PURE__ */ jsx12(Text10, { dimColor: true, children: " \xB7 " }),
|
|
1171
|
+
/* @__PURE__ */ jsx12(Text10, { color: "yellow", children: `${queued} queued` })
|
|
1172
|
+
] }) : null,
|
|
1173
|
+
stats?.recordings.totalDurationMs != null ? /* @__PURE__ */ jsx12(Text10, { dimColor: true, children: ` \xB7 ${formatClockMs(stats.recordings.totalDurationMs)} transcribed` }) : null
|
|
1128
1174
|
] }),
|
|
1129
1175
|
/* @__PURE__ */ jsxs9(Box10, { flexDirection: "row", alignItems: "flex-start", children: [
|
|
1130
1176
|
/* @__PURE__ */ jsx12(Box10, { flexGrow: 1, flexDirection: "column", children: /* @__PURE__ */ jsx12(
|
|
@@ -1133,7 +1179,7 @@ function OverviewView({
|
|
|
1133
1179
|
items: recordings,
|
|
1134
1180
|
selectedIndex,
|
|
1135
1181
|
nowMs,
|
|
1136
|
-
columns,
|
|
1182
|
+
columns: showPeek ? Math.max(20, columns - peekWidth - 1) : columns,
|
|
1137
1183
|
jobStatusByRecording,
|
|
1138
1184
|
downloadedRecordingIds,
|
|
1139
1185
|
spinnerFrame
|
|
@@ -1295,7 +1341,7 @@ var init_JobDetailView = __esm({
|
|
|
1295
1341
|
// src/tui/RecordingDetailView.tsx
|
|
1296
1342
|
import React6, { useMemo as useMemo2, useState as useState4 } from "react";
|
|
1297
1343
|
import { Box as Box12, Text as Text12, useInput as useInput3 } from "ink";
|
|
1298
|
-
import { Fragment as
|
|
1344
|
+
import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1299
1345
|
function RecordingDetailView({
|
|
1300
1346
|
item,
|
|
1301
1347
|
nowMs,
|
|
@@ -1360,14 +1406,14 @@ function RecordingDetailView({
|
|
|
1360
1406
|
});
|
|
1361
1407
|
return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", paddingX: 1, children: [
|
|
1362
1408
|
/* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "\u2039 Recordings" }),
|
|
1363
|
-
/* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { bold: true,
|
|
1409
|
+
/* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text12, { bold: true, children: title }) }),
|
|
1364
1410
|
/* @__PURE__ */ jsxs11(Text12, { children: [
|
|
1365
1411
|
/* @__PURE__ */ jsx14(Text12, { color: style.color, children: `${style.glyph} ${style.label}` }),
|
|
1366
1412
|
/* @__PURE__ */ jsx14(Text12, { dimColor: true, children: ` ${formatAge(item.createdAt, nowMs) || "\u2014"}` })
|
|
1367
1413
|
] }),
|
|
1368
1414
|
meta3 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: meta3 }) : null,
|
|
1369
1415
|
/* @__PURE__ */ jsx14(AudioActionRow, { item, audio }),
|
|
1370
|
-
!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(
|
|
1416
|
+
!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(Fragment5, { children: [
|
|
1371
1417
|
/* @__PURE__ */ jsx14(TabBar, { active: tab }),
|
|
1372
1418
|
/* @__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 }) })
|
|
1373
1419
|
] }),
|
|
@@ -1386,7 +1432,7 @@ function RecordingDetailView({
|
|
|
1386
1432
|
function TabBar({ active }) {
|
|
1387
1433
|
return /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: TAB_ORDER.map((tab, i) => /* @__PURE__ */ jsxs11(React6.Fragment, { children: [
|
|
1388
1434
|
i > 0 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: " " }) : null,
|
|
1389
|
-
tab === active ? /* @__PURE__ */ jsx14(Text12, { inverse: true, bold: true, children: ` ${TAB_LABEL[tab]} ` }) : /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: ` ${TAB_LABEL[tab]} ` })
|
|
1435
|
+
tab === active ? /* @__PURE__ */ jsx14(Text12, { inverse: true, bold: true, color: "cyan", children: ` ${TAB_LABEL[tab]} ` }) : /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: ` ${TAB_LABEL[tab]} ` })
|
|
1390
1436
|
] }, tab)) });
|
|
1391
1437
|
}
|
|
1392
1438
|
function AudioActionRow({
|
|
@@ -1432,7 +1478,7 @@ function SummaryPane({
|
|
|
1432
1478
|
return /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `Summary ${summary?.status ?? "unavailable"}` });
|
|
1433
1479
|
}
|
|
1434
1480
|
const points = (summary.keyPoints ?? []).slice(0, Math.max(1, budget - 4));
|
|
1435
|
-
return /* @__PURE__ */ jsxs11(
|
|
1481
|
+
return /* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
1436
1482
|
/* @__PURE__ */ jsx14(Text12, { children: summary.tldr }),
|
|
1437
1483
|
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
|
|
1438
1484
|
] });
|
|
@@ -1443,13 +1489,13 @@ function ChaptersPane({
|
|
|
1443
1489
|
selectedIndex
|
|
1444
1490
|
}) {
|
|
1445
1491
|
if (chapters.length === 0) return /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "No chapters" });
|
|
1446
|
-
return /* @__PURE__ */ jsxs11(
|
|
1492
|
+
return /* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
1447
1493
|
chapters.slice(win.start, win.end).map((chapter, i) => {
|
|
1448
1494
|
const index = win.start + i;
|
|
1449
1495
|
const selected = index === selectedIndex;
|
|
1450
1496
|
return /* @__PURE__ */ jsxs11(Text12, { wrap: "truncate-end", children: [
|
|
1451
1497
|
/* @__PURE__ */ jsx14(Text12, { color: "cyan", children: selected ? "\u25B8 " : " " }),
|
|
1452
|
-
/* @__PURE__ */ jsx14(Text12, {
|
|
1498
|
+
/* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `[${formatClockMs(chapter.startMs)}] ` }),
|
|
1453
1499
|
/* @__PURE__ */ jsx14(Text12, { bold: selected, children: chapter.title })
|
|
1454
1500
|
] }, index);
|
|
1455
1501
|
}),
|
|
@@ -1461,10 +1507,10 @@ function TranscriptPane({
|
|
|
1461
1507
|
win
|
|
1462
1508
|
}) {
|
|
1463
1509
|
if (segments.length === 0) return /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "(no segments)" });
|
|
1464
|
-
return /* @__PURE__ */ jsxs11(
|
|
1510
|
+
return /* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
1465
1511
|
segments.slice(win.start, win.end).map((seg, i) => /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
1466
|
-
/* @__PURE__ */ jsx14(Text12, {
|
|
1467
|
-
seg.speaker ? /* @__PURE__ */ jsx14(Text12, {
|
|
1512
|
+
/* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `[${formatClockMs(seg.startMs)}] ` }),
|
|
1513
|
+
seg.speaker ? /* @__PURE__ */ jsx14(Text12, { color: "cyan", children: `${seg.speaker} ` }) : null,
|
|
1468
1514
|
/* @__PURE__ */ jsx14(Text12, { children: seg.text })
|
|
1469
1515
|
] }, win.start + i)),
|
|
1470
1516
|
win.maxScroll > 0 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: ` ${win.start + 1}\u2013${win.end} / ${segments.length}` }) : null
|
|
@@ -1631,15 +1677,33 @@ var init_PermissionPreflightView = __esm({
|
|
|
1631
1677
|
});
|
|
1632
1678
|
|
|
1633
1679
|
// src/tui/RecordSetupView.tsx
|
|
1634
|
-
import { useState as useState6 } from "react";
|
|
1680
|
+
import { useEffect as useEffect3, useState as useState6 } from "react";
|
|
1635
1681
|
import { Box as Box15, Text as Text15, useInput as useInput5 } from "ink";
|
|
1636
1682
|
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1683
|
+
function levelDb2(level) {
|
|
1684
|
+
if (level <= 0.03) return "silent";
|
|
1685
|
+
return `${Math.round(level * 60 - 60)} dB`;
|
|
1686
|
+
}
|
|
1687
|
+
function meterBar(level, width) {
|
|
1688
|
+
const f = Math.max(0, Math.min(1, level));
|
|
1689
|
+
const filled = Math.round(f * width);
|
|
1690
|
+
return "\u2587".repeat(filled) + "\u2591".repeat(Math.max(0, width - filled));
|
|
1691
|
+
}
|
|
1692
|
+
function InputMeter({ level }) {
|
|
1693
|
+
if (level == null) return /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "\u2014" });
|
|
1694
|
+
const silent = level <= 0.03;
|
|
1695
|
+
return /* @__PURE__ */ jsxs14(Text15, { children: [
|
|
1696
|
+
/* @__PURE__ */ jsx17(Text15, { color: silent ? "yellow" : "green", children: meterBar(level, METER_W) }),
|
|
1697
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: ` ${levelDb2(level)}` })
|
|
1698
|
+
] });
|
|
1699
|
+
}
|
|
1637
1700
|
function RecordSetupView({
|
|
1638
1701
|
model,
|
|
1702
|
+
levels,
|
|
1639
1703
|
onStart,
|
|
1640
|
-
onCancel
|
|
1704
|
+
onCancel,
|
|
1705
|
+
onSelectionChange
|
|
1641
1706
|
}) {
|
|
1642
|
-
const size = useTerminalSize();
|
|
1643
1707
|
const [srcIdx, setSrcIdx] = useState6(0);
|
|
1644
1708
|
const [includeMic, setIncludeMic] = useState6(true);
|
|
1645
1709
|
const [micIdx, setMicIdx] = useState6(
|
|
@@ -1650,10 +1714,18 @@ function RecordSetupView({
|
|
|
1650
1714
|
const microphones = model.microphones ?? [];
|
|
1651
1715
|
const selected = sources[Math.min(srcIdx, Math.max(0, sources.length - 1))];
|
|
1652
1716
|
const selectedMic = microphones[Math.min(micIdx, Math.max(0, microphones.length - 1))];
|
|
1653
|
-
const wide = size.columns >= 100;
|
|
1654
1717
|
const hasAppSource = sources.some((source) => source.kind === "app");
|
|
1655
1718
|
const hasMultipleSources = sources.length > 1;
|
|
1656
1719
|
const hasMultipleMicrophones = microphones.length > 1;
|
|
1720
|
+
const selection = {
|
|
1721
|
+
sourceId: selected?.id ?? "system",
|
|
1722
|
+
includeMicrophone: includeMic,
|
|
1723
|
+
...includeMic && selectedMic ? { microphoneDeviceId: selectedMic.id } : {},
|
|
1724
|
+
sceneId: model.scenes[sceneIdx]?.id
|
|
1725
|
+
};
|
|
1726
|
+
useEffect3(() => {
|
|
1727
|
+
onSelectionChange?.(selection);
|
|
1728
|
+
}, [srcIdx, includeMic, micIdx]);
|
|
1657
1729
|
useInput5((input, key) => {
|
|
1658
1730
|
if (key.upArrow || input === "k") setSrcIdx((i) => Math.max(0, i - 1));
|
|
1659
1731
|
else if (key.downArrow || input === "j") setSrcIdx((i) => Math.min(sources.length - 1, i + 1));
|
|
@@ -1661,30 +1733,29 @@ function RecordSetupView({
|
|
|
1661
1733
|
else if (input === "m" && includeMic && hasMultipleMicrophones)
|
|
1662
1734
|
setMicIdx((i) => (i + 1) % microphones.length);
|
|
1663
1735
|
else if (input === "s" && model.scenes.length > 1) setSceneIdx((i) => (i + 1) % model.scenes.length);
|
|
1664
|
-
else if (key.return && selected)
|
|
1665
|
-
|
|
1666
|
-
sourceId: selected.id,
|
|
1667
|
-
includeMicrophone: includeMic,
|
|
1668
|
-
...includeMic && selectedMic ? { microphoneDeviceId: selectedMic.id } : {},
|
|
1669
|
-
sceneId: model.scenes[sceneIdx]?.id
|
|
1670
|
-
});
|
|
1671
|
-
} else if (key.escape) onCancel();
|
|
1736
|
+
else if (key.return && selected) onStart(selection);
|
|
1737
|
+
else if (key.escape) onCancel();
|
|
1672
1738
|
});
|
|
1673
1739
|
const sourceList = /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
1674
|
-
/* @__PURE__ */
|
|
1740
|
+
/* @__PURE__ */ jsxs14(Box15, { children: [
|
|
1741
|
+
/* @__PURE__ */ jsx17(Box15, { width: 36, children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "SOURCE" }) }),
|
|
1742
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "INPUT" })
|
|
1743
|
+
] }),
|
|
1675
1744
|
sources.map((s, i) => {
|
|
1676
1745
|
const on = i === srcIdx;
|
|
1677
|
-
return /* @__PURE__ */ jsxs14(
|
|
1678
|
-
on ? "
|
|
1679
|
-
|
|
1746
|
+
return /* @__PURE__ */ jsxs14(Box15, { children: [
|
|
1747
|
+
/* @__PURE__ */ jsx17(Box15, { width: 36, children: /* @__PURE__ */ jsxs14(Text15, { color: on ? "cyan" : void 0, wrap: "truncate-end", children: [
|
|
1748
|
+
on ? "\u25B8 \u25CF " : " \u25CB ",
|
|
1749
|
+
s.label
|
|
1750
|
+
] }) }),
|
|
1751
|
+
/* @__PURE__ */ jsx17(InputMeter, { level: levels?.bySourceId?.[s.id] })
|
|
1680
1752
|
] }, s.id);
|
|
1681
1753
|
}),
|
|
1682
1754
|
!hasAppSource ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "No app-specific sources available right now" }) : null
|
|
1683
1755
|
] });
|
|
1684
|
-
const capturePlan = /* @__PURE__ */ jsxs14(
|
|
1685
|
-
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "
|
|
1686
|
-
/* @__PURE__ */ jsx17(Text15, { children: includeMic ? `${selected?.label ?? "System audio"} + microphone` : `${selected?.label ?? "System audio"} only` })
|
|
1687
|
-
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: includeMic ? "Mic is mixed into the recording" : "Mic stays muted" })
|
|
1756
|
+
const capturePlan = /* @__PURE__ */ jsxs14(Text15, { children: [
|
|
1757
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Capture " }),
|
|
1758
|
+
/* @__PURE__ */ jsx17(Text15, { children: includeMic ? `${selected?.label ?? "System audio"} + microphone` : `${selected?.label ?? "System audio"} only` })
|
|
1688
1759
|
] });
|
|
1689
1760
|
const shortcuts = [
|
|
1690
1761
|
hasMultipleSources ? "\u2191\u2193 source" : void 0,
|
|
@@ -1696,9 +1767,9 @@ function RecordSetupView({
|
|
|
1696
1767
|
].filter(Boolean).join(" \xB7 ");
|
|
1697
1768
|
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, children: [
|
|
1698
1769
|
/* @__PURE__ */ jsx17(Text15, { bold: true, color: "green", children: "New recording" }),
|
|
1699
|
-
/* @__PURE__ */ jsxs14(Box15, { marginTop: 1, flexDirection:
|
|
1700
|
-
|
|
1701
|
-
/* @__PURE__ */ jsx17(Box15, {
|
|
1770
|
+
/* @__PURE__ */ jsxs14(Box15, { marginTop: 1, flexDirection: "column", children: [
|
|
1771
|
+
sourceList,
|
|
1772
|
+
/* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: capturePlan })
|
|
1702
1773
|
] }),
|
|
1703
1774
|
/* @__PURE__ */ jsxs14(Box15, { marginTop: 1, flexDirection: "column", children: [
|
|
1704
1775
|
/* @__PURE__ */ jsxs14(Text15, { children: [
|
|
@@ -1706,12 +1777,14 @@ function RecordSetupView({
|
|
|
1706
1777
|
/* @__PURE__ */ jsx17(Text15, { color: includeMic ? "green" : "gray", children: includeMic ? "[x] include mic" : "[ ] include mic" }),
|
|
1707
1778
|
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " (space)" })
|
|
1708
1779
|
] }),
|
|
1709
|
-
selectedMic ? /* @__PURE__ */ jsxs14(
|
|
1710
|
-
/* @__PURE__ */ jsx17(Text15, { dimColor:
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1780
|
+
selectedMic ? /* @__PURE__ */ jsxs14(Box15, { children: [
|
|
1781
|
+
/* @__PURE__ */ jsx17(Box15, { width: 36, children: /* @__PURE__ */ jsxs14(Text15, { dimColor: !includeMic, wrap: "truncate-end", children: [
|
|
1782
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Mic device " }),
|
|
1783
|
+
selectedMic.label
|
|
1784
|
+
] }) }),
|
|
1785
|
+
includeMic ? /* @__PURE__ */ jsx17(InputMeter, { level: levels?.byMicrophoneId?.[selectedMic.id] }) : null
|
|
1714
1786
|
] }) : null,
|
|
1787
|
+
includeMic && hasMultipleMicrophones ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " (m to change device)" }) : null,
|
|
1715
1788
|
model.scenes.length > 0 ? /* @__PURE__ */ jsxs14(Text15, { children: [
|
|
1716
1789
|
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Scene " }),
|
|
1717
1790
|
/* @__PURE__ */ jsx17(Text15, { children: model.scenes[sceneIdx]?.label ?? "Default" }),
|
|
@@ -1721,17 +1794,18 @@ function RecordSetupView({
|
|
|
1721
1794
|
/* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: shortcuts }) })
|
|
1722
1795
|
] });
|
|
1723
1796
|
}
|
|
1797
|
+
var METER_W;
|
|
1724
1798
|
var init_RecordSetupView = __esm({
|
|
1725
1799
|
"src/tui/RecordSetupView.tsx"() {
|
|
1726
1800
|
"use strict";
|
|
1727
|
-
|
|
1801
|
+
METER_W = 12;
|
|
1728
1802
|
}
|
|
1729
1803
|
});
|
|
1730
1804
|
|
|
1731
1805
|
// src/tui/AppShell.tsx
|
|
1732
|
-
import { useCallback, useEffect as
|
|
1806
|
+
import { useCallback, useEffect as useEffect4, useState as useState7 } from "react";
|
|
1733
1807
|
import { Box as Box16, Text as Text16, useApp, useInput as useInput6 } from "ink";
|
|
1734
|
-
import { Fragment as
|
|
1808
|
+
import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1735
1809
|
function recordErrorCopy(code, message) {
|
|
1736
1810
|
switch (code) {
|
|
1737
1811
|
case "record.helper_unavailable":
|
|
@@ -1812,6 +1886,43 @@ function transcribeHandoffErrorCopy(error51) {
|
|
|
1812
1886
|
return "Could not start transcription. Please try again.";
|
|
1813
1887
|
}
|
|
1814
1888
|
}
|
|
1889
|
+
function artifactProgressPatchFromOperationEvent(event) {
|
|
1890
|
+
if (event.command !== "upload") return void 0;
|
|
1891
|
+
if (event.status === "uploading" && typeof event.percent === "number") {
|
|
1892
|
+
return {
|
|
1893
|
+
uploadStatus: "uploading",
|
|
1894
|
+
uploadProgress: Math.max(0, Math.min(1, event.percent / 100))
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
if (event.status === "finishing_upload") {
|
|
1898
|
+
return {
|
|
1899
|
+
uploadStatus: "uploading",
|
|
1900
|
+
uploadProgress: 1
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
if (event.status === "starting_transcription") {
|
|
1904
|
+
return {
|
|
1905
|
+
uploadStatus: "uploaded",
|
|
1906
|
+
uploadProgress: 1,
|
|
1907
|
+
transcriptionStatus: "queued"
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
return void 0;
|
|
1911
|
+
}
|
|
1912
|
+
function transcriptionStatusFromJob(status) {
|
|
1913
|
+
switch (status) {
|
|
1914
|
+
case "queued":
|
|
1915
|
+
return "queued";
|
|
1916
|
+
case "running":
|
|
1917
|
+
return "processing";
|
|
1918
|
+
case "succeeded":
|
|
1919
|
+
return "ready";
|
|
1920
|
+
case "failed":
|
|
1921
|
+
return "failed";
|
|
1922
|
+
default:
|
|
1923
|
+
return "not_started";
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1815
1926
|
function permissionItemsFromRecordError(data) {
|
|
1816
1927
|
const sidecarError = isRecord8(data) ? data : void 0;
|
|
1817
1928
|
const sidecarData = isRecord8(sidecarError?.data) ? sidecarError.data : void 0;
|
|
@@ -1850,6 +1961,7 @@ function AppShell({
|
|
|
1850
1961
|
listDownloadedRecordingIds,
|
|
1851
1962
|
fetchRecordSetup,
|
|
1852
1963
|
startLiveRecord,
|
|
1964
|
+
startRecordSetupPreview,
|
|
1853
1965
|
transcribeRecordingArtifact,
|
|
1854
1966
|
initialView = "overview",
|
|
1855
1967
|
openUrl: openUrl2,
|
|
@@ -1884,6 +1996,13 @@ function AppShell({
|
|
|
1884
1996
|
sources: DEFAULT_RECORDING_SOURCES,
|
|
1885
1997
|
microphones: []
|
|
1886
1998
|
});
|
|
1999
|
+
const [recordSetupSelection, setRecordSetupSelection] = useState7(
|
|
2000
|
+
DEFAULT_RECORDING_SELECTION
|
|
2001
|
+
);
|
|
2002
|
+
const [recordSetupLevels, setRecordSetupLevels] = useState7({
|
|
2003
|
+
bySourceId: {},
|
|
2004
|
+
byMicrophoneId: {}
|
|
2005
|
+
});
|
|
1887
2006
|
const recordSetupModel = {
|
|
1888
2007
|
sources: recordSetupInputs.sources.length > 0 ? recordSetupInputs.sources : DEFAULT_RECORDING_SOURCES,
|
|
1889
2008
|
microphones: recordSetupInputs.microphones ?? [],
|
|
@@ -1896,10 +2015,56 @@ function AppShell({
|
|
|
1896
2015
|
} catch {
|
|
1897
2016
|
}
|
|
1898
2017
|
}, [listDownloadedRecordingIds]);
|
|
1899
|
-
|
|
2018
|
+
useEffect4(() => {
|
|
1900
2019
|
void refreshDownloadedIds();
|
|
1901
2020
|
}, [refreshDownloadedIds]);
|
|
1902
2021
|
const screen = stack[stack.length - 1];
|
|
2022
|
+
useEffect4(() => {
|
|
2023
|
+
if (screen.kind !== "recordSetup" || !startRecordSetupPreview) return;
|
|
2024
|
+
let cancelled = false;
|
|
2025
|
+
let preview;
|
|
2026
|
+
let unsubscribe;
|
|
2027
|
+
const selection = recordSetupSelection;
|
|
2028
|
+
setRecordSetupLevels({ bySourceId: {}, byMicrophoneId: {} });
|
|
2029
|
+
startRecordSetupPreview(selection, recordSetupModel.sources).then((session) => {
|
|
2030
|
+
if (cancelled) {
|
|
2031
|
+
void session.stop();
|
|
2032
|
+
return;
|
|
2033
|
+
}
|
|
2034
|
+
preview = session;
|
|
2035
|
+
unsubscribe = session.source.onEvent((event) => {
|
|
2036
|
+
if (event.type !== "audio.level") return;
|
|
2037
|
+
const level = levelFromRmsDb(event.rmsDb);
|
|
2038
|
+
if (event.input === "microphone") {
|
|
2039
|
+
const microphoneId = event.microphoneDeviceId ?? selection.microphoneDeviceId;
|
|
2040
|
+
if (!microphoneId) return;
|
|
2041
|
+
setRecordSetupLevels((current) => ({
|
|
2042
|
+
...current,
|
|
2043
|
+
byMicrophoneId: { ...current.byMicrophoneId ?? {}, [microphoneId]: level }
|
|
2044
|
+
}));
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
const sourceId = event.sourceId ?? selection.sourceId;
|
|
2048
|
+
setRecordSetupLevels((current) => ({
|
|
2049
|
+
...current,
|
|
2050
|
+
bySourceId: { ...current.bySourceId, [sourceId]: level }
|
|
2051
|
+
}));
|
|
2052
|
+
});
|
|
2053
|
+
}).catch(() => {
|
|
2054
|
+
});
|
|
2055
|
+
return () => {
|
|
2056
|
+
cancelled = true;
|
|
2057
|
+
unsubscribe?.();
|
|
2058
|
+
if (preview) void preview.stop();
|
|
2059
|
+
};
|
|
2060
|
+
}, [
|
|
2061
|
+
recordSetupSelection.includeMicrophone,
|
|
2062
|
+
recordSetupSelection.microphoneDeviceId,
|
|
2063
|
+
recordSetupSelection.sourceId,
|
|
2064
|
+
recordSetupModel.sources,
|
|
2065
|
+
screen.kind,
|
|
2066
|
+
startRecordSetupPreview
|
|
2067
|
+
]);
|
|
1903
2068
|
const beginLiveRecord = useCallback(
|
|
1904
2069
|
(selection = DEFAULT_RECORDING_SELECTION) => {
|
|
1905
2070
|
const capture = recordingCaptureMappingFromSelection(selection, recordSetupModel.sources);
|
|
@@ -1979,7 +2144,7 @@ function AppShell({
|
|
|
1979
2144
|
setStack([{ kind: "overview" }]);
|
|
1980
2145
|
}, [liveRecord, now, refreshDownloadedIds]);
|
|
1981
2146
|
const liveSession = liveRecord?.kind === "live" ? liveRecord.session : void 0;
|
|
1982
|
-
|
|
2147
|
+
useEffect4(() => {
|
|
1983
2148
|
if (!liveSession) return;
|
|
1984
2149
|
const session = liveSession;
|
|
1985
2150
|
const unsubscribe = session.source.onEvent((event) => {
|
|
@@ -2002,7 +2167,7 @@ function AppShell({
|
|
|
2002
2167
|
}, [liveSession]);
|
|
2003
2168
|
const selectedRecording = screen.kind === "overview" ? recordings[selected] : void 0;
|
|
2004
2169
|
const peekTranscriptId = selectedRecording?.activeTranscriptId ?? void 0;
|
|
2005
|
-
|
|
2170
|
+
useEffect4(() => {
|
|
2006
2171
|
if (!peekTranscriptId || summaryCache.has(peekTranscriptId)) return;
|
|
2007
2172
|
let cancelled = false;
|
|
2008
2173
|
const timer = setTimeout(() => {
|
|
@@ -2067,23 +2232,46 @@ function AppShell({
|
|
|
2067
2232
|
artifact: {
|
|
2068
2233
|
...artifact,
|
|
2069
2234
|
uploadStatus: "uploading",
|
|
2235
|
+
uploadProgress: 0,
|
|
2070
2236
|
transcriptionStatus: "not_started",
|
|
2237
|
+
transcriptionProgress: void 0,
|
|
2071
2238
|
error: void 0
|
|
2072
2239
|
}
|
|
2073
2240
|
});
|
|
2074
2241
|
try {
|
|
2075
|
-
const uploaded = await transcribeRecordingArtifact(artifact)
|
|
2242
|
+
const uploaded = await transcribeRecordingArtifact(artifact, (event) => {
|
|
2243
|
+
const patch = artifactProgressPatchFromOperationEvent(event);
|
|
2244
|
+
if (!patch) return;
|
|
2245
|
+
setLiveRecord((latest) => {
|
|
2246
|
+
if (latest?.kind !== "stopped" || latest.artifact?.sessionId !== artifact.sessionId) {
|
|
2247
|
+
return latest;
|
|
2248
|
+
}
|
|
2249
|
+
return {
|
|
2250
|
+
...latest,
|
|
2251
|
+
artifact: {
|
|
2252
|
+
...latest.artifact,
|
|
2253
|
+
...patch
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
});
|
|
2257
|
+
});
|
|
2076
2258
|
const transcriptionStatus = uploaded.transcriptId != null || uploaded.status === "succeeded" || uploaded.status === "ready" ? "ready" : uploaded.status === "running" ? "processing" : uploaded.jobId ? "queued" : "not_started";
|
|
2077
|
-
setLiveRecord({
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
...
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2259
|
+
setLiveRecord((latest) => {
|
|
2260
|
+
const base = latest?.kind === "stopped" && latest.artifact?.sessionId === artifact.sessionId ? latest : current;
|
|
2261
|
+
return {
|
|
2262
|
+
...base,
|
|
2263
|
+
artifact: {
|
|
2264
|
+
...artifact,
|
|
2265
|
+
...base.artifact ?? {},
|
|
2266
|
+
recordingId: uploaded.recordingId,
|
|
2267
|
+
...uploaded.jobId ? { jobId: uploaded.jobId } : {},
|
|
2268
|
+
...uploaded.transcriptId ? { transcriptId: uploaded.transcriptId } : {},
|
|
2269
|
+
uploadStatus: "uploaded",
|
|
2270
|
+
uploadProgress: 1,
|
|
2271
|
+
transcriptionStatus,
|
|
2272
|
+
...transcriptionStatus === "ready" ? { transcriptionProgress: 1 } : {}
|
|
2273
|
+
}
|
|
2274
|
+
};
|
|
2087
2275
|
});
|
|
2088
2276
|
setNotice(
|
|
2089
2277
|
transcriptionStatus === "ready" ? "Transcription ready." : uploaded.jobId ? "Transcription queued." : "Uploaded to Recappi Cloud."
|
|
@@ -2127,13 +2315,13 @@ function AppShell({
|
|
|
2127
2315
|
setLoadingMoreRecordings(false);
|
|
2128
2316
|
}
|
|
2129
2317
|
}, [fetchRecordings, loadingMoreRecordings, recordingsNextCursor]);
|
|
2130
|
-
|
|
2318
|
+
useEffect4(() => {
|
|
2131
2319
|
void refresh({ resetRecordings: true });
|
|
2132
2320
|
const id = setInterval(() => void refresh(), pollMs);
|
|
2133
2321
|
return () => clearInterval(id);
|
|
2134
2322
|
}, [refresh, pollMs]);
|
|
2135
2323
|
const hasRunning = jobs.some((item) => item.status === "running");
|
|
2136
|
-
|
|
2324
|
+
useEffect4(() => {
|
|
2137
2325
|
if (!hasRunning) return;
|
|
2138
2326
|
const id = setInterval(() => setSpinnerFrame((f) => f + 1), spinnerMs);
|
|
2139
2327
|
return () => clearInterval(id);
|
|
@@ -2146,12 +2334,29 @@ function AppShell({
|
|
|
2146
2334
|
jobStatusByRecording.set(job.recordingId, job.status);
|
|
2147
2335
|
}
|
|
2148
2336
|
}
|
|
2337
|
+
useEffect4(() => {
|
|
2338
|
+
setLiveRecord((current) => {
|
|
2339
|
+
if (current?.kind !== "stopped" || !current.artifact?.jobId) return current;
|
|
2340
|
+
const job = jobs.find((item) => item.jobId === current.artifact?.jobId);
|
|
2341
|
+
if (!job) return current;
|
|
2342
|
+
const fraction = transcribeFraction(job);
|
|
2343
|
+
return {
|
|
2344
|
+
...current,
|
|
2345
|
+
artifact: {
|
|
2346
|
+
...current.artifact,
|
|
2347
|
+
transcriptionStatus: transcriptionStatusFromJob(job.status),
|
|
2348
|
+
...job.transcriptId ? { transcriptId: job.transcriptId } : {},
|
|
2349
|
+
...job.status === "succeeded" ? { transcriptionProgress: 1 } : fraction != null ? { transcriptionProgress: fraction } : {}
|
|
2350
|
+
}
|
|
2351
|
+
};
|
|
2352
|
+
});
|
|
2353
|
+
}, [jobs]);
|
|
2149
2354
|
const listLength = screen.kind === "jobs" ? jobs.length : screen.kind === "overview" ? recordings.length : 0;
|
|
2150
|
-
|
|
2355
|
+
useEffect4(() => {
|
|
2151
2356
|
setSelected((i) => Math.max(0, Math.min(i, Math.max(0, listLength - 1))));
|
|
2152
2357
|
}, [listLength]);
|
|
2153
2358
|
const visibleRecordingRows = Math.max(3, size.rows - 6);
|
|
2154
|
-
|
|
2359
|
+
useEffect4(() => {
|
|
2155
2360
|
if (screen.kind !== "overview" || !recordingsNextCursor) return;
|
|
2156
2361
|
const nearLoadedEnd = recordings.length - selected <= RECORDINGS_PREFETCH_REMAINING;
|
|
2157
2362
|
const underfilledViewport = recordings.length < visibleRecordingRows;
|
|
@@ -2184,7 +2389,7 @@ function AppShell({
|
|
|
2184
2389
|
[fetchTranscript]
|
|
2185
2390
|
);
|
|
2186
2391
|
const detailTranscriptId = screen.kind === "recordingDetail" ? recordings.find((r) => r.recordingId === screen.recordingId)?.activeTranscriptId : void 0;
|
|
2187
|
-
|
|
2392
|
+
useEffect4(() => {
|
|
2188
2393
|
if (!detailTranscriptId || transcriptCache.has(detailTranscriptId)) return;
|
|
2189
2394
|
let cancelled = false;
|
|
2190
2395
|
setTranscriptCache((m) => new Map(m).set(detailTranscriptId, "loading"));
|
|
@@ -2344,8 +2549,10 @@ function AppShell({
|
|
|
2344
2549
|
RecordSetupView,
|
|
2345
2550
|
{
|
|
2346
2551
|
model: recordSetupModel,
|
|
2552
|
+
levels: recordSetupLevels,
|
|
2347
2553
|
onStart: beginLiveRecord,
|
|
2348
|
-
onCancel: () => setStack((st) => st.length > 1 ? st.slice(0, -1) : [{ kind: "overview" }])
|
|
2554
|
+
onCancel: () => setStack((st) => st.length > 1 ? st.slice(0, -1) : [{ kind: "overview" }]),
|
|
2555
|
+
onSelectionChange: setRecordSetupSelection
|
|
2349
2556
|
}
|
|
2350
2557
|
) });
|
|
2351
2558
|
}
|
|
@@ -2371,7 +2578,7 @@ function AppShell({
|
|
|
2371
2578
|
return /* @__PURE__ */ jsx18(PermissionPreflightView, { items: permissionItemsFromRecordError(liveRecord.data) });
|
|
2372
2579
|
}
|
|
2373
2580
|
const copy = recordErrorCopy(liveRecord.code, liveRecord.message);
|
|
2374
|
-
return /* @__PURE__ */ jsxs15(
|
|
2581
|
+
return /* @__PURE__ */ jsxs15(Fragment6, { children: [
|
|
2375
2582
|
/* @__PURE__ */ jsx18(Text16, { color: copy.tone, children: copy.title }),
|
|
2376
2583
|
copy.detail ? /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: copy.detail }) : null
|
|
2377
2584
|
] });
|
|
@@ -2524,6 +2731,7 @@ async function runDashboard(deps) {
|
|
|
2524
2731
|
listDownloadedRecordingIds: deps.listDownloadedRecordingIds,
|
|
2525
2732
|
fetchRecordSetup: deps.fetchRecordSetup,
|
|
2526
2733
|
startLiveRecord: deps.startLiveRecord,
|
|
2734
|
+
startRecordSetupPreview: deps.startRecordSetupPreview,
|
|
2527
2735
|
transcribeRecordingArtifact: deps.transcribeRecordingArtifact,
|
|
2528
2736
|
initialView: deps.initialView ?? "overview",
|
|
2529
2737
|
openUrl,
|
|
@@ -17284,6 +17492,19 @@ var sidecarRecordingStartResultSchema = external_exports.object({
|
|
|
17284
17492
|
state: sidecarRecordingStateSchema,
|
|
17285
17493
|
localSessionRef: external_exports.string().optional()
|
|
17286
17494
|
});
|
|
17495
|
+
var sidecarLevelPreviewStartParamsSchema = external_exports.object({
|
|
17496
|
+
options: sidecarRecordingOptionsSchema
|
|
17497
|
+
});
|
|
17498
|
+
var sidecarLevelPreviewStartResultSchema = external_exports.object({
|
|
17499
|
+
previewId: external_exports.string()
|
|
17500
|
+
});
|
|
17501
|
+
var sidecarLevelPreviewStopParamsSchema = external_exports.object({
|
|
17502
|
+
previewId: external_exports.string()
|
|
17503
|
+
});
|
|
17504
|
+
var sidecarLevelPreviewStopResultSchema = external_exports.object({
|
|
17505
|
+
previewId: external_exports.string(),
|
|
17506
|
+
state: external_exports.literal("stopped")
|
|
17507
|
+
});
|
|
17287
17508
|
var sidecarSessionParamsSchema = external_exports.object({
|
|
17288
17509
|
sessionId: external_exports.string()
|
|
17289
17510
|
});
|
|
@@ -17325,6 +17546,18 @@ var sidecarRequestSchema = external_exports.discriminatedUnion("method", [
|
|
|
17325
17546
|
method: external_exports.literal("recappi.recording.start"),
|
|
17326
17547
|
params: sidecarRecordingStartParamsSchema
|
|
17327
17548
|
}),
|
|
17549
|
+
external_exports.object({
|
|
17550
|
+
jsonrpc: external_exports.literal("2.0"),
|
|
17551
|
+
id: sidecarJsonRpcIdSchema,
|
|
17552
|
+
method: external_exports.literal("recappi.recording.level_preview.start"),
|
|
17553
|
+
params: sidecarLevelPreviewStartParamsSchema
|
|
17554
|
+
}),
|
|
17555
|
+
external_exports.object({
|
|
17556
|
+
jsonrpc: external_exports.literal("2.0"),
|
|
17557
|
+
id: sidecarJsonRpcIdSchema,
|
|
17558
|
+
method: external_exports.literal("recappi.recording.level_preview.stop"),
|
|
17559
|
+
params: sidecarLevelPreviewStopParamsSchema
|
|
17560
|
+
}),
|
|
17328
17561
|
external_exports.object({
|
|
17329
17562
|
jsonrpc: external_exports.literal("2.0"),
|
|
17330
17563
|
id: sidecarJsonRpcIdSchema,
|
|
@@ -17383,8 +17616,11 @@ var sidecarEventSchema = external_exports.discriminatedUnion("type", [
|
|
|
17383
17616
|
}),
|
|
17384
17617
|
external_exports.object({
|
|
17385
17618
|
type: external_exports.literal("audio.level"),
|
|
17386
|
-
sessionId: external_exports.string(),
|
|
17619
|
+
sessionId: external_exports.string().optional(),
|
|
17620
|
+
previewId: external_exports.string().optional(),
|
|
17387
17621
|
input: external_exports.enum(["system", "microphone"]),
|
|
17622
|
+
sourceId: external_exports.string().optional(),
|
|
17623
|
+
microphoneDeviceId: external_exports.string().optional(),
|
|
17388
17624
|
rmsDb: external_exports.number().optional(),
|
|
17389
17625
|
peakDb: external_exports.number().optional(),
|
|
17390
17626
|
at: external_exports.number().int().optional(),
|
|
@@ -20224,6 +20460,20 @@ var MiniSidecarClient = class {
|
|
|
20224
20460
|
sidecarRecordingStartResultSchema
|
|
20225
20461
|
);
|
|
20226
20462
|
}
|
|
20463
|
+
startLevelPreview(params) {
|
|
20464
|
+
return this.request(
|
|
20465
|
+
"recappi.recording.level_preview.start",
|
|
20466
|
+
sidecarLevelPreviewStartParamsSchema.parse(params),
|
|
20467
|
+
sidecarLevelPreviewStartResultSchema
|
|
20468
|
+
);
|
|
20469
|
+
}
|
|
20470
|
+
stopLevelPreview(params) {
|
|
20471
|
+
return this.request(
|
|
20472
|
+
"recappi.recording.level_preview.stop",
|
|
20473
|
+
sidecarLevelPreviewStopParamsSchema.parse(params),
|
|
20474
|
+
sidecarLevelPreviewStopResultSchema
|
|
20475
|
+
);
|
|
20476
|
+
}
|
|
20227
20477
|
getPermissionStatus(params) {
|
|
20228
20478
|
return this.request(
|
|
20229
20479
|
"recappi.permissions.status",
|
|
@@ -20573,6 +20823,55 @@ async function listRecordInputs(opts) {
|
|
|
20573
20823
|
sidecar.kill();
|
|
20574
20824
|
}
|
|
20575
20825
|
}
|
|
20826
|
+
async function startRecordSetupLevelPreview(opts, selection = {
|
|
20827
|
+
sourceId: DEFAULT_RECORDING_SOURCES[0].id,
|
|
20828
|
+
includeMicrophone: true
|
|
20829
|
+
}, sources = DEFAULT_RECORDING_SOURCES) {
|
|
20830
|
+
const capture = recordingCaptureMappingFromSelection(selection, sources);
|
|
20831
|
+
const command = resolveSidecarCommand(opts);
|
|
20832
|
+
const sidecarArgs = opts.sidecarArgs ?? [];
|
|
20833
|
+
const spawnSidecar = opts.runtime?.spawnSidecar ?? spawnMiniSidecar;
|
|
20834
|
+
const sidecar = spawnSidecar({ command, args: sidecarArgs, env: opts.env });
|
|
20835
|
+
let previewId;
|
|
20836
|
+
let closed = false;
|
|
20837
|
+
const close = () => {
|
|
20838
|
+
if (closed) return;
|
|
20839
|
+
closed = true;
|
|
20840
|
+
sidecar.kill();
|
|
20841
|
+
};
|
|
20842
|
+
try {
|
|
20843
|
+
await sidecar.client.handshake(
|
|
20844
|
+
defaultSidecarHandshakeParams({
|
|
20845
|
+
client: { name: "recappi-cli", version: opts.cliVersion },
|
|
20846
|
+
capabilities: ["recording.capture"]
|
|
20847
|
+
})
|
|
20848
|
+
);
|
|
20849
|
+
const started = await sidecar.client.startLevelPreview({
|
|
20850
|
+
options: {
|
|
20851
|
+
includeSystemAudio: capture.includeSystemAudio,
|
|
20852
|
+
includeMicrophone: capture.includeMicrophone,
|
|
20853
|
+
liveCaptions: false,
|
|
20854
|
+
...capture.targetBundleId ? { targetBundleId: capture.targetBundleId } : {},
|
|
20855
|
+
...capture.microphoneDeviceId ? { microphoneDeviceId: capture.microphoneDeviceId } : {}
|
|
20856
|
+
}
|
|
20857
|
+
});
|
|
20858
|
+
previewId = started.previewId;
|
|
20859
|
+
return {
|
|
20860
|
+
source: sidecar.client,
|
|
20861
|
+
stop: async () => {
|
|
20862
|
+
try {
|
|
20863
|
+
if (previewId) await sidecar.client.stopLevelPreview({ previewId });
|
|
20864
|
+
} catch {
|
|
20865
|
+
} finally {
|
|
20866
|
+
close();
|
|
20867
|
+
}
|
|
20868
|
+
}
|
|
20869
|
+
};
|
|
20870
|
+
} catch (error51) {
|
|
20871
|
+
close();
|
|
20872
|
+
throw error51;
|
|
20873
|
+
}
|
|
20874
|
+
}
|
|
20576
20875
|
async function startRecordSession(opts) {
|
|
20577
20876
|
let retriedAfterMicrophoneGrant = false;
|
|
20578
20877
|
while (true) {
|
|
@@ -21120,14 +21419,24 @@ async function runCli(deps = {}) {
|
|
|
21120
21419
|
sources
|
|
21121
21420
|
);
|
|
21122
21421
|
},
|
|
21123
|
-
|
|
21422
|
+
startRecordSetupPreview: async (selection, sources) => startRecordSetupLevelPreview(
|
|
21423
|
+
{
|
|
21424
|
+
cliVersion: CLI_VERSION,
|
|
21425
|
+
env: deps.env,
|
|
21426
|
+
runtime: deps.recordRuntime
|
|
21427
|
+
},
|
|
21428
|
+
selection,
|
|
21429
|
+
sources
|
|
21430
|
+
),
|
|
21431
|
+
transcribeRecordingArtifact: async (artifact, onEvent) => {
|
|
21124
21432
|
if (!artifact.audioPath) {
|
|
21125
21433
|
throw cliError("input.not_found", "No local audio file is available to transcribe.");
|
|
21126
21434
|
}
|
|
21127
21435
|
const data = await client.uploadPathBatch({
|
|
21128
21436
|
inputPath: artifact.audioPath,
|
|
21129
21437
|
transcribe: true,
|
|
21130
|
-
wait: false
|
|
21438
|
+
wait: false,
|
|
21439
|
+
onEvent
|
|
21131
21440
|
});
|
|
21132
21441
|
if (data.failures.length > 0) {
|
|
21133
21442
|
const failure = data.failures[0];
|