recappi 0.1.50 → 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 CHANGED
@@ -593,7 +593,7 @@ 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
598
  function waveRowsFor(terminalRows) {
599
599
  return terminalRows >= 30 ? 5 : 3;
@@ -667,7 +667,13 @@ function RecordingHeroScreen({
667
667
  const [tick, setTick] = useState3(() => now());
668
668
  const [waveSys, setWaveSys] = useState3([]);
669
669
  const [waveMic, setWaveMic] = useState3([]);
670
+ const [captionMode, setCaptionMode] = useState3("both");
670
671
  const lastAppendRef = useRef(0);
672
+ useInput2((input) => {
673
+ if (input === "c") {
674
+ setCaptionMode((m) => m === "both" ? "source" : m === "source" ? "translation" : "both");
675
+ }
676
+ });
671
677
  useEffect2(() => {
672
678
  if (telemetry.level == null) return;
673
679
  const t = now();
@@ -750,55 +756,77 @@ function RecordingHeroScreen({
750
756
  /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: context }) }),
751
757
  captions ? /* @__PURE__ */ jsxs2(Box2, { marginTop: 1, flexDirection: "column", children: [
752
758
  /* @__PURE__ */ jsx3(Text2, { bold: true, dimColor: true, children: "LIVE CAPTIONS" }),
753
- /* @__PURE__ */ jsx3(HeroCaptions, { state: captions, maxRows: captionRows, width: innerWidth })
759
+ /* @__PURE__ */ jsx3(HeroCaptions, { state: captions, maxRows: captionRows, width: innerWidth, mode: captionMode })
754
760
  ] }) : null
755
761
  ] }),
756
762
  /* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
757
763
  "q stop & save",
758
- canPause ? ` \xB7 p ${paused ? "resume" : "pause"}` : ""
764
+ canPause ? ` \xB7 p ${paused ? "resume" : "pause"}` : "",
765
+ captions ? " \xB7 c captions" : ""
759
766
  ] }) })
760
767
  ] });
761
768
  }
762
769
  function wrappedRows(text, width) {
763
770
  return Math.max(1, Math.ceil(displayWidth(text) / Math.max(1, width)));
764
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
+ }
765
784
  function HeroCaptions({
766
785
  state,
767
786
  maxRows,
768
- width
787
+ width,
788
+ mode
769
789
  }) {
770
790
  const hasPartial = Boolean(state.partial && state.partial.length > 0);
771
791
  const captionError = state.status === "error" ? `Captions unavailable: ${state.error ?? "Live captions unavailable."}` : null;
772
792
  if (state.lines.length === 0 && !hasPartial) {
773
793
  return /* @__PURE__ */ jsx3(Text2, { color: captionError ? "yellow" : void 0, dimColor: !captionError, children: captionError ?? (state.status === "live" ? "Listening for speech\u2026" : liveCaptionStatusLabel(state.status)) });
774
794
  }
775
- const lines = [];
776
- for (const line of state.lines) {
777
- lines.push({
778
- key: `${line.id}-s`,
779
- text: `${line.speaker ? `${line.speaker}: ` : ""}${trimLead(line.text)}`,
780
- dim: false
781
- });
782
- if (line.translation) {
783
- lines.push({ key: `${line.id}-t`, text: ` \u21B3 ${trimLead(line.translation)}`, dim: true });
784
- }
785
- }
786
- if (hasPartial) lines.push({ key: "partial", text: trimLead(state.partial), dim: true });
787
- if (state.translationPartial) {
788
- lines.push({ key: "tpartial", text: ` \u21B3 ${trimLead(state.translationPartial)}`, dim: true });
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
+ ] });
789
809
  }
790
- const budget = Math.max(1, maxRows);
791
- const chosen = [];
792
- let used = 0;
793
- for (let i = lines.length - 1; i >= 0; i--) {
794
- const h = wrappedRows(lines[i].text, width);
795
- if (used + h > budget && chosen.length > 0) break;
796
- chosen.unshift(lines[i]);
797
- used += h;
810
+ if (mode === "translation") {
811
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
812
+ captionColumn(translationItems, maxRows, width, false),
813
+ errLine
814
+ ] });
798
815
  }
816
+ const gap = 2;
817
+ const colW = Math.max(12, Math.floor((width - gap) / 2));
799
818
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
800
- chosen.map((l) => /* @__PURE__ */ jsx3(Text2, { dimColor: l.dim, wrap: "wrap", children: l.text }, l.key)),
801
- captionError ? /* @__PURE__ */ jsx3(Text2, { color: "yellow", wrap: "wrap", children: captionError }) : null
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
802
830
  ] });
803
831
  }
804
832
  function stoppedHandoffCopy(artifact, canTranscribe) {
@@ -1392,7 +1420,7 @@ var init_JobDetailView = __esm({
1392
1420
 
1393
1421
  // src/tui/RecordingDetailView.tsx
1394
1422
  import React6, { useMemo as useMemo2, useState as useState4 } from "react";
1395
- import { Box as Box12, Text as Text12, useInput as useInput3 } from "ink";
1423
+ import { Box as Box12, Text as Text12, useInput as useInput4 } from "ink";
1396
1424
  import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
1397
1425
  function RecordingDetailView({
1398
1426
  item,
@@ -1436,7 +1464,7 @@ function RecordingDetailView({
1436
1464
  setScroll(found < 0 ? Math.max(0, segments.length - 1) : found);
1437
1465
  setTab("transcript");
1438
1466
  };
1439
- useInput3((input, key) => {
1467
+ useInput4((input, key) => {
1440
1468
  if (!item.activeTranscriptId || !ready) return;
1441
1469
  if (key.tab) {
1442
1470
  setTab((t) => TAB_ORDER[(TAB_ORDER.indexOf(t) + (key.shift ? TAB_ORDER.length - 1 : 1)) % TAB_ORDER.length]);
@@ -1586,7 +1614,7 @@ var init_RecordingDetailView = __esm({
1586
1614
 
1587
1615
  // src/tui/TranscriptView.tsx
1588
1616
  import { useMemo as useMemo3, useState as useState5 } from "react";
1589
- import { Box as Box13, Text as Text13, useInput as useInput4 } from "ink";
1617
+ import { Box as Box13, Text as Text13, useInput as useInput5 } from "ink";
1590
1618
  import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
1591
1619
  function TranscriptView({ loading, data, error: error51 }) {
1592
1620
  const size = useTerminalSize();
@@ -1603,7 +1631,7 @@ function TranscriptView({ loading, data, error: error51 }) {
1603
1631
  const budget = Math.max(3, size.rows - 3);
1604
1632
  const win = windowByHeights(heights, scroll, budget);
1605
1633
  const page = Math.max(1, budget - 1);
1606
- useInput4((input, key) => {
1634
+ useInput5((input, key) => {
1607
1635
  if (key.downArrow || input === "j") setScroll((s) => Math.min(win.maxScroll, s + 1));
1608
1636
  else if (key.upArrow || input === "k") setScroll((s) => Math.max(0, s - 1));
1609
1637
  else if (key.pageDown || input === " ") setScroll((s) => Math.min(win.maxScroll, s + page));
@@ -1730,7 +1758,7 @@ var init_PermissionPreflightView = __esm({
1730
1758
 
1731
1759
  // src/tui/RecordSetupView.tsx
1732
1760
  import { useEffect as useEffect3, useRef as useRef2, useState as useState6 } from "react";
1733
- import { Box as Box15, Text as Text15, useInput as useInput5 } from "ink";
1761
+ import { Box as Box15, Text as Text15, useInput as useInput6 } from "ink";
1734
1762
  import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
1735
1763
  function levelDb2(level) {
1736
1764
  if (level <= 0.03) return "silent";
@@ -1793,7 +1821,7 @@ function RecordSetupView({
1793
1821
  const di = microphones.findIndex((device) => device.isDefault);
1794
1822
  if (di > 0) setMicIdx(di);
1795
1823
  }, [microphones.length]);
1796
- useInput5((input, key) => {
1824
+ useInput6((input, key) => {
1797
1825
  if (key.upArrow || input === "k") setSrcIdx((i) => Math.max(0, i - 1));
1798
1826
  else if (key.downArrow || input === "j") setSrcIdx((i) => Math.min(sources.length - 1, i + 1));
1799
1827
  else if (input === " ") setIncludeMic((m) => !m);
@@ -1872,7 +1900,7 @@ var init_RecordSetupView = __esm({
1872
1900
 
1873
1901
  // src/tui/AppShell.tsx
1874
1902
  import { useCallback, useEffect as useEffect4, useState as useState7 } from "react";
1875
- import { Box as Box16, Text as Text16, useApp, useInput as useInput6 } from "ink";
1903
+ import { Box as Box16, Text as Text16, useApp, useInput as useInput7 } from "ink";
1876
1904
  import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
1877
1905
  function recordErrorCopy(code, message) {
1878
1906
  switch (code) {
@@ -2516,7 +2544,7 @@ function AppShell({
2516
2544
  setNotice(void 0);
2517
2545
  };
2518
2546
  const back = () => setStack((st) => st.length > 1 ? st.slice(0, -1) : st);
2519
- useInput6((input, key) => {
2547
+ useInput7((input, key) => {
2520
2548
  setNotice(void 0);
2521
2549
  if (screen.kind === "recordSetup") {
2522
2550
  if (input === "q" || key.leftArrow) back();
@@ -20490,7 +20518,7 @@ import { createRequire as createRequire2 } from "module";
20490
20518
  import { homedir } from "os";
20491
20519
  import { dirname, join as join2 } from "path";
20492
20520
  import { fileURLToPath } from "url";
20493
- import { render, useInput as useInput2 } from "ink";
20521
+ import { render, useInput as useInput3 } from "ink";
20494
20522
  init_recordingCore();
20495
20523
 
20496
20524
  // src/sidecar.ts
@@ -21346,7 +21374,7 @@ function RecordLiveScreen({
21346
21374
  onStop,
21347
21375
  now
21348
21376
  }) {
21349
- useInput2((input, key) => {
21377
+ useInput3((input, key) => {
21350
21378
  if (input === "q" || key.escape || key.leftArrow) onStop();
21351
21379
  });
21352
21380
  return /* @__PURE__ */ jsx4(LiveCaptionsScreen, { source, now });
@@ -21405,7 +21433,7 @@ function RecordingHeroLive({
21405
21433
  if (mapped) setCaptions((prev) => liveCaptionReducer(prev, mapped));
21406
21434
  });
21407
21435
  }, [captionStreamEnabled, source]);
21408
- useInput2((input, key) => {
21436
+ useInput3((input, key) => {
21409
21437
  if (input === "q" || key.escape) onStop();
21410
21438
  });
21411
21439
  return /* @__PURE__ */ jsx4(