recappi 0.1.36 → 0.1.38
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 +68 -19
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -597,7 +597,7 @@ var init_LiveCaptionsScreen = __esm({
|
|
|
597
597
|
// src/tui/RecordingHeroScreen.tsx
|
|
598
598
|
import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
599
599
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
600
|
-
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
600
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
601
601
|
function waveform(samples, width) {
|
|
602
602
|
if (width <= 0) return "";
|
|
603
603
|
const tail = samples.slice(-width);
|
|
@@ -611,6 +611,7 @@ function waveform(samples, width) {
|
|
|
611
611
|
function RecordingHeroScreen({
|
|
612
612
|
telemetry,
|
|
613
613
|
artifact,
|
|
614
|
+
captions,
|
|
614
615
|
canTranscribe = false,
|
|
615
616
|
canPause = false,
|
|
616
617
|
now = () => Date.now()
|
|
@@ -674,7 +675,8 @@ function RecordingHeroScreen({
|
|
|
674
675
|
/* @__PURE__ */ jsx3(Box2, { marginTop: 1, children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
675
676
|
telemetry.sourceLabel,
|
|
676
677
|
telemetry.micEnabled ? " + Microphone" : ""
|
|
677
|
-
] }) })
|
|
678
|
+
] }) }),
|
|
679
|
+
captions ? /* @__PURE__ */ jsx3(Box2, { marginTop: 1, flexDirection: "column", alignItems: "center", width: innerWidth, children: /* @__PURE__ */ jsx3(HeroCaptions, { state: captions }) }) : null
|
|
678
680
|
] }),
|
|
679
681
|
/* @__PURE__ */ jsx3(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { dimColor: true, children: [
|
|
680
682
|
"q stop & save",
|
|
@@ -682,6 +684,25 @@ function RecordingHeroScreen({
|
|
|
682
684
|
] }) })
|
|
683
685
|
] });
|
|
684
686
|
}
|
|
687
|
+
function HeroCaptions({ state }) {
|
|
688
|
+
const MAX_LINES = 3;
|
|
689
|
+
const recent = state.lines.slice(-MAX_LINES);
|
|
690
|
+
const hasPartial = Boolean(state.partial && state.partial.length > 0);
|
|
691
|
+
if (recent.length === 0 && !hasPartial) {
|
|
692
|
+
return /* @__PURE__ */ jsx3(Text2, { dimColor: true, children: state.status === "live" ? "Listening for speech\u2026" : liveCaptionStatusLabel(state.status) });
|
|
693
|
+
}
|
|
694
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
695
|
+
recent.map((line) => /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", alignItems: "center", children: [
|
|
696
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate-end", children: [
|
|
697
|
+
line.speaker ? `${line.speaker}: ` : "",
|
|
698
|
+
line.text
|
|
699
|
+
] }),
|
|
700
|
+
line.translation ? /* @__PURE__ */ jsx3(Text2, { dimColor: true, wrap: "truncate-end", children: `\u21B3 ${line.translation}` }) : null
|
|
701
|
+
] }, line.id)),
|
|
702
|
+
hasPartial ? /* @__PURE__ */ jsx3(Text2, { dimColor: true, wrap: "truncate-end", children: state.partial }) : null,
|
|
703
|
+
state.translationPartial ? /* @__PURE__ */ jsx3(Text2, { dimColor: true, wrap: "truncate-end", children: `\u21B3 ${state.translationPartial}` }) : null
|
|
704
|
+
] });
|
|
705
|
+
}
|
|
685
706
|
function stoppedHandoffCopy(artifact, canTranscribe) {
|
|
686
707
|
if (artifact?.uploadStatus === "uploading") {
|
|
687
708
|
return { text: "Uploading\u2026", tone: "normal" };
|
|
@@ -708,6 +729,7 @@ var init_RecordingHeroScreen = __esm({
|
|
|
708
729
|
"src/tui/RecordingHeroScreen.tsx"() {
|
|
709
730
|
"use strict";
|
|
710
731
|
init_format();
|
|
732
|
+
init_liveCaptions();
|
|
711
733
|
init_terminal();
|
|
712
734
|
BLOCKS = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
713
735
|
}
|
|
@@ -715,7 +737,7 @@ var init_RecordingHeroScreen = __esm({
|
|
|
715
737
|
|
|
716
738
|
// src/tui/AccountView.tsx
|
|
717
739
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
718
|
-
import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
740
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
719
741
|
function AccountView({ status }) {
|
|
720
742
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", paddingX: 1, children: [
|
|
721
743
|
/* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "\u2039 Account" }),
|
|
@@ -728,7 +750,7 @@ function AccountView({ status }) {
|
|
|
728
750
|
] });
|
|
729
751
|
}
|
|
730
752
|
function AccountBody({ status }) {
|
|
731
|
-
return /* @__PURE__ */ jsxs3(
|
|
753
|
+
return /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
732
754
|
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
733
755
|
/* @__PURE__ */ jsx5(Text3, { bold: true, color: "green", children: status.email ?? status.userId ?? "Signed in" }),
|
|
734
756
|
status.email && status.userId ? /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: status.userId }) : null,
|
|
@@ -1009,7 +1031,7 @@ var init_RecordingsView = __esm({
|
|
|
1009
1031
|
|
|
1010
1032
|
// src/tui/RecordingPeek.tsx
|
|
1011
1033
|
import { Box as Box9, Text as Text9 } from "ink";
|
|
1012
|
-
import { Fragment as
|
|
1034
|
+
import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1013
1035
|
function RecordingPeek({
|
|
1014
1036
|
item,
|
|
1015
1037
|
summary,
|
|
@@ -1029,7 +1051,7 @@ function PeekBody({
|
|
|
1029
1051
|
formatBytes2(item.sizeBytes) || null,
|
|
1030
1052
|
item.contentType || null
|
|
1031
1053
|
].filter(Boolean).join(" \xB7 ");
|
|
1032
|
-
return /* @__PURE__ */ jsxs8(
|
|
1054
|
+
return /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1033
1055
|
/* @__PURE__ */ jsx11(Text9, { bold: true, color: "green", wrap: "truncate-end", children: recordingTitle2(item) }),
|
|
1034
1056
|
/* @__PURE__ */ jsx11(Text9, { color: style.color, children: `${style.glyph} ${style.label}` }),
|
|
1035
1057
|
meta3 ? /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: meta3 }) : null,
|
|
@@ -1049,7 +1071,7 @@ function SummarySection({
|
|
|
1049
1071
|
return /* @__PURE__ */ jsx11(Text9, { dimColor: true, children: `Summary ${summary.status}` });
|
|
1050
1072
|
}
|
|
1051
1073
|
const points = (summary.keyPoints ?? []).slice(0, 3);
|
|
1052
|
-
return /* @__PURE__ */ jsxs8(
|
|
1074
|
+
return /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1053
1075
|
/* @__PURE__ */ jsx11(Text9, { bold: true, children: "Summary" }),
|
|
1054
1076
|
/* @__PURE__ */ jsx11(Text9, { children: summary.tldr }),
|
|
1055
1077
|
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
|
|
@@ -1262,7 +1284,7 @@ var init_JobDetailView = __esm({
|
|
|
1262
1284
|
// src/tui/RecordingDetailView.tsx
|
|
1263
1285
|
import React6, { useMemo as useMemo2, useState as useState4 } from "react";
|
|
1264
1286
|
import { Box as Box12, Text as Text12, useInput as useInput3 } from "ink";
|
|
1265
|
-
import { Fragment as
|
|
1287
|
+
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1266
1288
|
function RecordingDetailView({
|
|
1267
1289
|
item,
|
|
1268
1290
|
nowMs,
|
|
@@ -1334,7 +1356,7 @@ function RecordingDetailView({
|
|
|
1334
1356
|
] }),
|
|
1335
1357
|
meta3 ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: meta3 }) : null,
|
|
1336
1358
|
/* @__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(
|
|
1359
|
+
!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(Fragment4, { children: [
|
|
1338
1360
|
/* @__PURE__ */ jsx14(TabBar, { active: tab }),
|
|
1339
1361
|
/* @__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 }) })
|
|
1340
1362
|
] }),
|
|
@@ -1399,7 +1421,7 @@ function SummaryPane({
|
|
|
1399
1421
|
return /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `Summary ${summary?.status ?? "unavailable"}` });
|
|
1400
1422
|
}
|
|
1401
1423
|
const points = (summary.keyPoints ?? []).slice(0, Math.max(1, budget - 4));
|
|
1402
|
-
return /* @__PURE__ */ jsxs11(
|
|
1424
|
+
return /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
1403
1425
|
/* @__PURE__ */ jsx14(Text12, { children: summary.tldr }),
|
|
1404
1426
|
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
|
|
1405
1427
|
] });
|
|
@@ -1410,7 +1432,7 @@ function ChaptersPane({
|
|
|
1410
1432
|
selectedIndex
|
|
1411
1433
|
}) {
|
|
1412
1434
|
if (chapters.length === 0) return /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "No chapters" });
|
|
1413
|
-
return /* @__PURE__ */ jsxs11(
|
|
1435
|
+
return /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
1414
1436
|
chapters.slice(win.start, win.end).map((chapter, i) => {
|
|
1415
1437
|
const index = win.start + i;
|
|
1416
1438
|
const selected = index === selectedIndex;
|
|
@@ -1428,7 +1450,7 @@ function TranscriptPane({
|
|
|
1428
1450
|
win
|
|
1429
1451
|
}) {
|
|
1430
1452
|
if (segments.length === 0) return /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: "(no segments)" });
|
|
1431
|
-
return /* @__PURE__ */ jsxs11(
|
|
1453
|
+
return /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
1432
1454
|
segments.slice(win.start, win.end).map((seg, i) => /* @__PURE__ */ jsxs11(Text12, { children: [
|
|
1433
1455
|
/* @__PURE__ */ jsx14(Text12, { color: "blue", children: `[${formatClockMs(seg.startMs)}] ` }),
|
|
1434
1456
|
seg.speaker ? /* @__PURE__ */ jsx14(Text12, { dimColor: true, children: `${seg.speaker} ` }) : null,
|
|
@@ -1698,7 +1720,7 @@ var init_RecordSetupView = __esm({
|
|
|
1698
1720
|
// src/tui/AppShell.tsx
|
|
1699
1721
|
import { useCallback, useEffect as useEffect3, useState as useState7 } from "react";
|
|
1700
1722
|
import { Box as Box16, Text as Text16, useApp, useInput as useInput6 } from "ink";
|
|
1701
|
-
import { Fragment as
|
|
1723
|
+
import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1702
1724
|
function recordErrorCopy(code, message) {
|
|
1703
1725
|
switch (code) {
|
|
1704
1726
|
case "record.helper_unavailable":
|
|
@@ -1897,7 +1919,8 @@ function AppShell({
|
|
|
1897
1919
|
kind: "live",
|
|
1898
1920
|
session,
|
|
1899
1921
|
selection,
|
|
1900
|
-
telemetry: { ...current.telemetry, status: "recording" }
|
|
1922
|
+
telemetry: { ...current.telemetry, status: "recording" },
|
|
1923
|
+
captions: session.captionStreamEnabled ? initialLiveCaptionsState() : void 0
|
|
1901
1924
|
};
|
|
1902
1925
|
});
|
|
1903
1926
|
}).catch((error51) => {
|
|
@@ -1914,7 +1937,8 @@ function AppShell({
|
|
|
1914
1937
|
kind: "stopping",
|
|
1915
1938
|
session: current.session,
|
|
1916
1939
|
selection: current.selection,
|
|
1917
|
-
telemetry: stoppingTelemetry
|
|
1940
|
+
telemetry: stoppingTelemetry,
|
|
1941
|
+
captions: current.captions
|
|
1918
1942
|
});
|
|
1919
1943
|
try {
|
|
1920
1944
|
const data = await current.session.stop();
|
|
@@ -1948,11 +1972,18 @@ function AppShell({
|
|
|
1948
1972
|
if (!liveSession) return;
|
|
1949
1973
|
const session = liveSession;
|
|
1950
1974
|
const unsubscribe = session.source.onEvent((event) => {
|
|
1975
|
+
const captionEvent = session.captionStreamEnabled ? sidecarToLiveCaptionEvent(event) : null;
|
|
1951
1976
|
setLiveRecord((current) => {
|
|
1952
1977
|
if (current?.kind !== "live" || current.session !== session) return current;
|
|
1953
1978
|
return {
|
|
1954
1979
|
...current,
|
|
1955
|
-
telemetry: applyRecordingEventToTelemetry(current.telemetry, event)
|
|
1980
|
+
telemetry: applyRecordingEventToTelemetry(current.telemetry, event),
|
|
1981
|
+
...captionEvent ? {
|
|
1982
|
+
captions: liveCaptionReducer(
|
|
1983
|
+
current.captions ?? initialLiveCaptionsState(),
|
|
1984
|
+
captionEvent
|
|
1985
|
+
)
|
|
1986
|
+
} : {}
|
|
1956
1987
|
};
|
|
1957
1988
|
});
|
|
1958
1989
|
});
|
|
@@ -2316,6 +2347,7 @@ function AppShell({
|
|
|
2316
2347
|
RecordingHeroScreen,
|
|
2317
2348
|
{
|
|
2318
2349
|
telemetry: liveRecord.telemetry,
|
|
2350
|
+
captions: liveRecord.kind === "live" || liveRecord.kind === "stopping" ? liveRecord.captions : void 0,
|
|
2319
2351
|
artifact: liveRecord.kind === "stopped" ? liveRecord.artifact : void 0,
|
|
2320
2352
|
canTranscribe: Boolean(transcribeRecordingArtifact),
|
|
2321
2353
|
now
|
|
@@ -2328,7 +2360,7 @@ function AppShell({
|
|
|
2328
2360
|
return /* @__PURE__ */ jsx18(PermissionPreflightView, { items: permissionItemsFromRecordError(liveRecord.data) });
|
|
2329
2361
|
}
|
|
2330
2362
|
const copy = recordErrorCopy(liveRecord.code, liveRecord.message);
|
|
2331
|
-
return /* @__PURE__ */ jsxs15(
|
|
2363
|
+
return /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
2332
2364
|
/* @__PURE__ */ jsx18(Text16, { color: copy.tone, children: copy.title }),
|
|
2333
2365
|
copy.detail ? /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: copy.detail }) : null
|
|
2334
2366
|
] });
|
|
@@ -2430,6 +2462,7 @@ var init_AppShell = __esm({
|
|
|
2430
2462
|
init_PermissionPreflightView();
|
|
2431
2463
|
init_RecordSetupView();
|
|
2432
2464
|
init_RecordingHeroScreen();
|
|
2465
|
+
init_liveCaptions();
|
|
2433
2466
|
init_recordingCore();
|
|
2434
2467
|
init_format();
|
|
2435
2468
|
init_terminal();
|
|
@@ -20423,6 +20456,7 @@ function createFifo(path6) {
|
|
|
20423
20456
|
// src/record.tsx
|
|
20424
20457
|
init_LiveCaptionsScreen();
|
|
20425
20458
|
init_RecordingHeroScreen();
|
|
20459
|
+
init_liveCaptions();
|
|
20426
20460
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
20427
20461
|
var SIDECAR_COMMAND_ENV = "RECAPPI_MINI_SIDECAR";
|
|
20428
20462
|
var SIDECAR_HELPER_NAME = "RecappiMiniSidecar";
|
|
@@ -20444,6 +20478,7 @@ async function recordViaSidecar(opts) {
|
|
|
20444
20478
|
source: session.source,
|
|
20445
20479
|
sourceLabel: opts.includeSystemAudio === false ? "Microphone" : "System audio \xB7 all apps",
|
|
20446
20480
|
micEnabled: opts.includeMicrophone !== false,
|
|
20481
|
+
captionStreamEnabled: opts.live === true,
|
|
20447
20482
|
renderApp: opts.runtime?.renderApp,
|
|
20448
20483
|
now: opts.runtime?.now
|
|
20449
20484
|
});
|
|
@@ -20482,6 +20517,7 @@ async function startLiveRecordSession(opts, selection = {
|
|
|
20482
20517
|
});
|
|
20483
20518
|
return {
|
|
20484
20519
|
mode: "local",
|
|
20520
|
+
captionStreamEnabled: true,
|
|
20485
20521
|
source: session.source,
|
|
20486
20522
|
stop: session.stop
|
|
20487
20523
|
};
|
|
@@ -20917,6 +20953,7 @@ function createInkRecordingHeroRenderer(opts) {
|
|
|
20917
20953
|
source: opts.source,
|
|
20918
20954
|
sourceLabel: opts.sourceLabel,
|
|
20919
20955
|
micEnabled: opts.micEnabled,
|
|
20956
|
+
captionStreamEnabled: opts.captionStreamEnabled,
|
|
20920
20957
|
onStop,
|
|
20921
20958
|
now: opts.now ?? Date.now
|
|
20922
20959
|
}
|
|
@@ -20935,6 +20972,7 @@ function RecordingHeroLive({
|
|
|
20935
20972
|
source,
|
|
20936
20973
|
sourceLabel,
|
|
20937
20974
|
micEnabled,
|
|
20975
|
+
captionStreamEnabled,
|
|
20938
20976
|
onStop,
|
|
20939
20977
|
now
|
|
20940
20978
|
}) {
|
|
@@ -20944,15 +20982,26 @@ function RecordingHeroLive({
|
|
|
20944
20982
|
sourceLabel,
|
|
20945
20983
|
micEnabled
|
|
20946
20984
|
}));
|
|
20985
|
+
const [captions, setCaptions] = React4.useState(initialLiveCaptionsState);
|
|
20947
20986
|
React4.useEffect(() => {
|
|
20948
20987
|
return source.onEvent((event) => {
|
|
20949
20988
|
setTelemetry((prev) => applyRecordingEventToTelemetry(prev, event));
|
|
20989
|
+
if (!captionStreamEnabled) return;
|
|
20990
|
+
const mapped = sidecarToLiveCaptionEvent(event);
|
|
20991
|
+
if (mapped) setCaptions((prev) => liveCaptionReducer(prev, mapped));
|
|
20950
20992
|
});
|
|
20951
|
-
}, [source]);
|
|
20993
|
+
}, [captionStreamEnabled, source]);
|
|
20952
20994
|
useInput2((input, key) => {
|
|
20953
20995
|
if (input === "q" || key.escape) onStop();
|
|
20954
20996
|
});
|
|
20955
|
-
return /* @__PURE__ */ jsx4(
|
|
20997
|
+
return /* @__PURE__ */ jsx4(
|
|
20998
|
+
RecordingHeroScreen,
|
|
20999
|
+
{
|
|
21000
|
+
telemetry,
|
|
21001
|
+
captions: captionStreamEnabled ? captions : void 0,
|
|
21002
|
+
now
|
|
21003
|
+
}
|
|
21004
|
+
);
|
|
20956
21005
|
}
|
|
20957
21006
|
|
|
20958
21007
|
// src/cli.ts
|