recappi 0.1.49 → 0.1.51
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 +92 -54
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -593,17 +593,20 @@ var init_LiveCaptionsScreen = __esm({
|
|
|
593
593
|
|
|
594
594
|
// src/tui/RecordingHeroScreen.tsx
|
|
595
595
|
import { useEffect as useEffect2, useRef, useState as useState3 } from "react";
|
|
596
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
596
|
+
import { Box as Box2, Text as Text2, useInput as useInput2 } from "ink";
|
|
597
597
|
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
598
|
-
function
|
|
598
|
+
function waveRowsFor(terminalRows) {
|
|
599
|
+
return terminalRows >= 30 ? 5 : 3;
|
|
600
|
+
}
|
|
601
|
+
function litCount(level, rows) {
|
|
599
602
|
const amp = Math.max(0, Math.min(1, level));
|
|
600
603
|
if (amp <= 0.028) return 0;
|
|
601
|
-
return Math.max(1, Math.min(
|
|
604
|
+
return Math.max(1, Math.min(rows, Math.ceil(Math.pow(amp, 0.58) * rows)));
|
|
602
605
|
}
|
|
603
|
-
function litCounts(samples, width) {
|
|
606
|
+
function litCounts(samples, width, rows) {
|
|
604
607
|
if (width <= 0) return [];
|
|
605
608
|
const tail = samples.slice(-width);
|
|
606
|
-
return [...Array(Math.max(0, width - tail.length)).fill(0), ...tail].map(litCount);
|
|
609
|
+
return [...Array(Math.max(0, width - tail.length)).fill(0), ...tail].map((v) => litCount(v, rows));
|
|
607
610
|
}
|
|
608
611
|
function levelDb(level) {
|
|
609
612
|
if (level <= 0.03) return "silent";
|
|
@@ -614,18 +617,19 @@ function MeterRow({
|
|
|
614
617
|
samples,
|
|
615
618
|
level,
|
|
616
619
|
paused,
|
|
617
|
-
width
|
|
620
|
+
width,
|
|
621
|
+
rows
|
|
618
622
|
}) {
|
|
619
623
|
const silent = level <= 0.03;
|
|
620
|
-
const cols = litCounts(samples, width);
|
|
624
|
+
const cols = litCounts(samples, width, rows);
|
|
621
625
|
const litColor = paused ? "gray" : "cyan";
|
|
622
626
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
623
627
|
/* @__PURE__ */ jsxs2(Box2, { width: width + 9, children: [
|
|
624
628
|
/* @__PURE__ */ jsx3(Box2, { width: 9, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: label }) }),
|
|
625
629
|
/* @__PURE__ */ jsx3(Box2, { flexGrow: 1, justifyContent: "flex-end", children: !paused && silent ? /* @__PURE__ */ jsx3(Text2, { color: "yellow", children: "silent" }) : /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: paused ? "paused" : levelDb(level) }) })
|
|
626
630
|
] }),
|
|
627
|
-
Array.from({ length:
|
|
628
|
-
const fromBottom =
|
|
631
|
+
Array.from({ length: rows }, (_, r) => {
|
|
632
|
+
const fromBottom = rows - r;
|
|
629
633
|
return /* @__PURE__ */ jsx3(Text2, { children: cols.map(
|
|
630
634
|
(c, i) => c >= fromBottom ? /* @__PURE__ */ jsx3(Text2, { color: litColor, children: c === fromBottom ? "\u2022" : "\u25CF" }, i) : /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "\xB7" }, i)
|
|
631
635
|
) }, r);
|
|
@@ -663,7 +667,13 @@ function RecordingHeroScreen({
|
|
|
663
667
|
const [tick, setTick] = useState3(() => now());
|
|
664
668
|
const [waveSys, setWaveSys] = useState3([]);
|
|
665
669
|
const [waveMic, setWaveMic] = useState3([]);
|
|
670
|
+
const [captionMode, setCaptionMode] = useState3("both");
|
|
666
671
|
const lastAppendRef = useRef(0);
|
|
672
|
+
useInput2((input) => {
|
|
673
|
+
if (input === "c") {
|
|
674
|
+
setCaptionMode((m) => m === "both" ? "source" : m === "source" ? "translation" : "both");
|
|
675
|
+
}
|
|
676
|
+
});
|
|
667
677
|
useEffect2(() => {
|
|
668
678
|
if (telemetry.level == null) return;
|
|
669
679
|
const t = now();
|
|
@@ -720,7 +730,8 @@ function RecordingHeroScreen({
|
|
|
720
730
|
const meterW = Math.max(10, Math.min(72, innerWidth - 20));
|
|
721
731
|
const sizeStr = telemetry.sizeBytes ? formatBytes2(telemetry.sizeBytes) : "";
|
|
722
732
|
const context = [telemetry.sourceLabel, telemetry.micEnabled ? "Microphone" : null, sizeStr || null].filter(Boolean).join(" \xB7 ");
|
|
723
|
-
const
|
|
733
|
+
const waveRows = waveRowsFor(size.rows);
|
|
734
|
+
const meterBlockRows = (telemetry.micEnabled ? 2 : 1) * (waveRows + 1) + (telemetry.micEnabled ? 1 : 0);
|
|
724
735
|
const fixedRows = 8 + meterBlockRows;
|
|
725
736
|
const captionRows = Math.max(2, size.rows - fixedRows);
|
|
726
737
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
@@ -739,55 +750,83 @@ function RecordingHeroScreen({
|
|
|
739
750
|
// reads as silence (the elapsed timer above proves it's live).
|
|
740
751
|
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: paused ? "Paused" : `Capturing audio${".".repeat(Math.floor(tick / 1e3) % 3 + 1)}` })
|
|
741
752
|
) : /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
742
|
-
/* @__PURE__ */ jsx3(MeterRow, { label: "System", samples: waveSys, level: telemetry.level.system ?? 0, paused, width: meterW }),
|
|
743
|
-
telemetry.micEnabled ? /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(MeterRow, { label: "Mic", samples: waveMic, level: telemetry.level.mic ?? 0, paused, width: meterW }) }) : null
|
|
753
|
+
/* @__PURE__ */ jsx3(MeterRow, { label: "System", samples: waveSys, level: telemetry.level.system ?? 0, paused, width: meterW, rows: waveRows }),
|
|
754
|
+
telemetry.micEnabled ? /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(MeterRow, { label: "Mic", samples: waveMic, level: telemetry.level.mic ?? 0, paused, width: meterW, rows: waveRows }) }) : null
|
|
744
755
|
] }) }),
|
|
745
756
|
/* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: context }) }),
|
|
746
757
|
captions ? /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
|
|
747
758
|
/* @__PURE__ */ jsx3(Text2, { bold: true, dimColor: true, children: "LIVE CAPTIONS" }),
|
|
748
|
-
/* @__PURE__ */ jsx3(HeroCaptions, { state: captions, maxRows: captionRows })
|
|
759
|
+
/* @__PURE__ */ jsx3(HeroCaptions, { state: captions, maxRows: captionRows, width: innerWidth, mode: captionMode })
|
|
749
760
|
] }) : null
|
|
750
761
|
] }),
|
|
751
762
|
/* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
752
763
|
"q stop & save",
|
|
753
|
-
canPause ? ` \xB7 p ${paused ? "resume" : "pause"}` : ""
|
|
764
|
+
canPause ? ` \xB7 p ${paused ? "resume" : "pause"}` : "",
|
|
765
|
+
captions ? " \xB7 c captions" : ""
|
|
754
766
|
] }) })
|
|
755
767
|
] });
|
|
756
768
|
}
|
|
757
|
-
function
|
|
769
|
+
function wrappedRows(text, width) {
|
|
770
|
+
return Math.max(1, Math.ceil(displayWidth(text) / Math.max(1, width)));
|
|
771
|
+
}
|
|
772
|
+
function captionColumn(items, maxRows, width, dim) {
|
|
773
|
+
const budget = Math.max(1, maxRows);
|
|
774
|
+
const chosen = [];
|
|
775
|
+
let used = 0;
|
|
776
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
777
|
+
const h = wrappedRows(items[i].text, width);
|
|
778
|
+
if (used + h > budget && chosen.length > 0) break;
|
|
779
|
+
chosen.unshift(items[i]);
|
|
780
|
+
used += h;
|
|
781
|
+
}
|
|
782
|
+
return chosen.map((it) => /* @__PURE__ */ jsx3(Text2, { dimColor: dim, wrap: "wrap", children: it.text }, it.key));
|
|
783
|
+
}
|
|
784
|
+
function HeroCaptions({
|
|
785
|
+
state,
|
|
786
|
+
maxRows,
|
|
787
|
+
width,
|
|
788
|
+
mode
|
|
789
|
+
}) {
|
|
758
790
|
const hasPartial = Boolean(state.partial && state.partial.length > 0);
|
|
759
791
|
const captionError = state.status === "error" ? `Captions unavailable: ${state.error ?? "Live captions unavailable."}` : null;
|
|
760
792
|
if (state.lines.length === 0 && !hasPartial) {
|
|
761
|
-
return /* @__PURE__ */ jsx3(Text2, { color: captionError ? "yellow" : void 0, dimColor: !captionError,
|
|
762
|
-
}
|
|
763
|
-
const rows = [];
|
|
764
|
-
for (const line of state.lines) {
|
|
765
|
-
rows.push(
|
|
766
|
-
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate-end", children: [
|
|
767
|
-
line.speaker ? `${line.speaker}: ` : "",
|
|
768
|
-
trimLead(line.text)
|
|
769
|
-
] }, `${line.id}-s`)
|
|
770
|
-
);
|
|
771
|
-
if (line.translation) {
|
|
772
|
-
rows.push(
|
|
773
|
-
/* @__PURE__ */ jsx3(Text2, { dimColor: true, wrap: "truncate-end", children: ` \u21B3 ${trimLead(line.translation)}` }, `${line.id}-t`)
|
|
774
|
-
);
|
|
775
|
-
}
|
|
793
|
+
return /* @__PURE__ */ jsx3(Text2, { color: captionError ? "yellow" : void 0, dimColor: !captionError, children: captionError ?? (state.status === "live" ? "Listening for speech\u2026" : liveCaptionStatusLabel(state.status)) });
|
|
776
794
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
795
|
+
const sourceItems = state.lines.map((l) => ({
|
|
796
|
+
key: `${l.id}-s`,
|
|
797
|
+
text: `${l.speaker ? `${l.speaker}: ` : ""}${trimLead(l.text)}`
|
|
798
|
+
}));
|
|
799
|
+
if (hasPartial) sourceItems.push({ key: "sp", text: trimLead(state.partial) });
|
|
800
|
+
const translationItems = state.lines.filter((l) => l.translation).map((l) => ({ key: `${l.id}-t`, text: trimLead(l.translation) }));
|
|
801
|
+
if (state.translationPartial) translationItems.push({ key: "tp", text: trimLead(state.translationPartial) });
|
|
802
|
+
const errLine = captionError ? /* @__PURE__ */ jsx3(Text2, { color: "yellow", wrap: "wrap", children: captionError }) : null;
|
|
803
|
+
const hasTranslation = translationItems.length > 0;
|
|
804
|
+
if (mode === "source" || !hasTranslation) {
|
|
805
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
806
|
+
captionColumn(sourceItems, maxRows, width, false),
|
|
807
|
+
errLine
|
|
808
|
+
] });
|
|
781
809
|
}
|
|
782
|
-
if (
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
810
|
+
if (mode === "translation") {
|
|
811
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
812
|
+
captionColumn(translationItems, maxRows, width, false),
|
|
813
|
+
errLine
|
|
814
|
+
] });
|
|
786
815
|
}
|
|
787
|
-
const
|
|
816
|
+
const gap = 2;
|
|
817
|
+
const colW = Math.max(12, Math.floor((width - gap) / 2));
|
|
788
818
|
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
789
|
-
|
|
790
|
-
|
|
819
|
+
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", children: [
|
|
820
|
+
/* @__PURE__ */ jsxs2(Box2, { width: colW, flexDirection: "column", marginRight: gap, children: [
|
|
821
|
+
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "ORIGINAL" }),
|
|
822
|
+
captionColumn(sourceItems, Math.max(1, maxRows - 1), colW, false)
|
|
823
|
+
] }),
|
|
824
|
+
/* @__PURE__ */ jsxs2(Box2, { width: colW, flexDirection: "column", children: [
|
|
825
|
+
/* @__PURE__ */ jsx3(Text2, { dimColor: true, children: "TRANSLATION" }),
|
|
826
|
+
captionColumn(translationItems, Math.max(1, maxRows - 1), colW, false)
|
|
827
|
+
] })
|
|
828
|
+
] }),
|
|
829
|
+
errLine
|
|
791
830
|
] });
|
|
792
831
|
}
|
|
793
832
|
function stoppedHandoffCopy(artifact, canTranscribe) {
|
|
@@ -808,14 +847,13 @@ function stoppedHandoffCopy(artifact, canTranscribe) {
|
|
|
808
847
|
}
|
|
809
848
|
return { text: "Transcribe now? \u23CE yes \xB7 n not now", tone: "normal" };
|
|
810
849
|
}
|
|
811
|
-
var
|
|
850
|
+
var WAVE_THROTTLE_MS, trimLead;
|
|
812
851
|
var init_RecordingHeroScreen = __esm({
|
|
813
852
|
"src/tui/RecordingHeroScreen.tsx"() {
|
|
814
853
|
"use strict";
|
|
815
854
|
init_format();
|
|
816
855
|
init_liveCaptions();
|
|
817
856
|
init_terminal();
|
|
818
|
-
WAVE_ROWS = 5;
|
|
819
857
|
WAVE_THROTTLE_MS = 220;
|
|
820
858
|
trimLead = (s) => s.replace(/^\s+/, "");
|
|
821
859
|
}
|
|
@@ -1382,7 +1420,7 @@ var init_JobDetailView = __esm({
|
|
|
1382
1420
|
|
|
1383
1421
|
// src/tui/RecordingDetailView.tsx
|
|
1384
1422
|
import React6, { useMemo as useMemo2, useState as useState4 } from "react";
|
|
1385
|
-
import { Box as Box12, Text as Text12, useInput as
|
|
1423
|
+
import { Box as Box12, Text as Text12, useInput as useInput4 } from "ink";
|
|
1386
1424
|
import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1387
1425
|
function RecordingDetailView({
|
|
1388
1426
|
item,
|
|
@@ -1426,7 +1464,7 @@ function RecordingDetailView({
|
|
|
1426
1464
|
setScroll(found < 0 ? Math.max(0, segments.length - 1) : found);
|
|
1427
1465
|
setTab("transcript");
|
|
1428
1466
|
};
|
|
1429
|
-
|
|
1467
|
+
useInput4((input, key) => {
|
|
1430
1468
|
if (!item.activeTranscriptId || !ready) return;
|
|
1431
1469
|
if (key.tab) {
|
|
1432
1470
|
setTab((t) => TAB_ORDER[(TAB_ORDER.indexOf(t) + (key.shift ? TAB_ORDER.length - 1 : 1)) % TAB_ORDER.length]);
|
|
@@ -1576,7 +1614,7 @@ var init_RecordingDetailView = __esm({
|
|
|
1576
1614
|
|
|
1577
1615
|
// src/tui/TranscriptView.tsx
|
|
1578
1616
|
import { useMemo as useMemo3, useState as useState5 } from "react";
|
|
1579
|
-
import { Box as Box13, Text as Text13, useInput as
|
|
1617
|
+
import { Box as Box13, Text as Text13, useInput as useInput5 } from "ink";
|
|
1580
1618
|
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1581
1619
|
function TranscriptView({ loading, data, error: error51 }) {
|
|
1582
1620
|
const size = useTerminalSize();
|
|
@@ -1593,7 +1631,7 @@ function TranscriptView({ loading, data, error: error51 }) {
|
|
|
1593
1631
|
const budget = Math.max(3, size.rows - 3);
|
|
1594
1632
|
const win = windowByHeights(heights, scroll, budget);
|
|
1595
1633
|
const page = Math.max(1, budget - 1);
|
|
1596
|
-
|
|
1634
|
+
useInput5((input, key) => {
|
|
1597
1635
|
if (key.downArrow || input === "j") setScroll((s) => Math.min(win.maxScroll, s + 1));
|
|
1598
1636
|
else if (key.upArrow || input === "k") setScroll((s) => Math.max(0, s - 1));
|
|
1599
1637
|
else if (key.pageDown || input === " ") setScroll((s) => Math.min(win.maxScroll, s + page));
|
|
@@ -1720,7 +1758,7 @@ var init_PermissionPreflightView = __esm({
|
|
|
1720
1758
|
|
|
1721
1759
|
// src/tui/RecordSetupView.tsx
|
|
1722
1760
|
import { useEffect as useEffect3, useRef as useRef2, useState as useState6 } from "react";
|
|
1723
|
-
import { Box as Box15, Text as Text15, useInput as
|
|
1761
|
+
import { Box as Box15, Text as Text15, useInput as useInput6 } from "ink";
|
|
1724
1762
|
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1725
1763
|
function levelDb2(level) {
|
|
1726
1764
|
if (level <= 0.03) return "silent";
|
|
@@ -1783,7 +1821,7 @@ function RecordSetupView({
|
|
|
1783
1821
|
const di = microphones.findIndex((device) => device.isDefault);
|
|
1784
1822
|
if (di > 0) setMicIdx(di);
|
|
1785
1823
|
}, [microphones.length]);
|
|
1786
|
-
|
|
1824
|
+
useInput6((input, key) => {
|
|
1787
1825
|
if (key.upArrow || input === "k") setSrcIdx((i) => Math.max(0, i - 1));
|
|
1788
1826
|
else if (key.downArrow || input === "j") setSrcIdx((i) => Math.min(sources.length - 1, i + 1));
|
|
1789
1827
|
else if (input === " ") setIncludeMic((m) => !m);
|
|
@@ -1862,7 +1900,7 @@ var init_RecordSetupView = __esm({
|
|
|
1862
1900
|
|
|
1863
1901
|
// src/tui/AppShell.tsx
|
|
1864
1902
|
import { useCallback, useEffect as useEffect4, useState as useState7 } from "react";
|
|
1865
|
-
import { Box as Box16, Text as Text16, useApp, useInput as
|
|
1903
|
+
import { Box as Box16, Text as Text16, useApp, useInput as useInput7 } from "ink";
|
|
1866
1904
|
import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1867
1905
|
function recordErrorCopy(code, message) {
|
|
1868
1906
|
switch (code) {
|
|
@@ -2506,7 +2544,7 @@ function AppShell({
|
|
|
2506
2544
|
setNotice(void 0);
|
|
2507
2545
|
};
|
|
2508
2546
|
const back = () => setStack((st) => st.length > 1 ? st.slice(0, -1) : st);
|
|
2509
|
-
|
|
2547
|
+
useInput7((input, key) => {
|
|
2510
2548
|
setNotice(void 0);
|
|
2511
2549
|
if (screen.kind === "recordSetup") {
|
|
2512
2550
|
if (input === "q" || key.leftArrow) back();
|
|
@@ -20480,7 +20518,7 @@ import { createRequire as createRequire2 } from "module";
|
|
|
20480
20518
|
import { homedir } from "os";
|
|
20481
20519
|
import { dirname, join as join2 } from "path";
|
|
20482
20520
|
import { fileURLToPath } from "url";
|
|
20483
|
-
import { render, useInput as
|
|
20521
|
+
import { render, useInput as useInput3 } from "ink";
|
|
20484
20522
|
init_recordingCore();
|
|
20485
20523
|
|
|
20486
20524
|
// src/sidecar.ts
|
|
@@ -21336,7 +21374,7 @@ function RecordLiveScreen({
|
|
|
21336
21374
|
onStop,
|
|
21337
21375
|
now
|
|
21338
21376
|
}) {
|
|
21339
|
-
|
|
21377
|
+
useInput3((input, key) => {
|
|
21340
21378
|
if (input === "q" || key.escape || key.leftArrow) onStop();
|
|
21341
21379
|
});
|
|
21342
21380
|
return /* @__PURE__ */ jsx4(LiveCaptionsScreen, { source, now });
|
|
@@ -21395,7 +21433,7 @@ function RecordingHeroLive({
|
|
|
21395
21433
|
if (mapped) setCaptions((prev) => liveCaptionReducer(prev, mapped));
|
|
21396
21434
|
});
|
|
21397
21435
|
}, [captionStreamEnabled, source]);
|
|
21398
|
-
|
|
21436
|
+
useInput3((input, key) => {
|
|
21399
21437
|
if (input === "q" || key.escape) onStop();
|
|
21400
21438
|
});
|
|
21401
21439
|
return /* @__PURE__ */ jsx4(
|