recappi 0.1.15 → 0.1.16
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 -163
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,6 +9,127 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/recordingCore.ts
|
|
13
|
+
function recordingCaptureMappingFromSelection(selection = DEFAULT_RECORDING_SELECTION, sources = DEFAULT_RECORDING_SOURCES) {
|
|
14
|
+
const source = sources.find((candidate) => candidate.id === selection.sourceId) ?? sources[0];
|
|
15
|
+
if (!source) {
|
|
16
|
+
throw new Error("No recording sources are available.");
|
|
17
|
+
}
|
|
18
|
+
if (source.kind === "microphone") {
|
|
19
|
+
return {
|
|
20
|
+
source,
|
|
21
|
+
includeSystemAudio: false,
|
|
22
|
+
includeMicrophone: true,
|
|
23
|
+
sourceLabel: source.label,
|
|
24
|
+
micEnabled: true
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
source,
|
|
29
|
+
// Current helper support is system-wide capture. App-specific targeting is
|
|
30
|
+
// represented in the core model, then falls back to system capture until the
|
|
31
|
+
// Darwin adapter exposes a target app option.
|
|
32
|
+
includeSystemAudio: true,
|
|
33
|
+
includeMicrophone: selection.includeMicrophone,
|
|
34
|
+
sourceLabel: source.label,
|
|
35
|
+
micEnabled: selection.includeMicrophone
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function recordingStatusFromSidecarState(state) {
|
|
39
|
+
switch (state) {
|
|
40
|
+
case "recording":
|
|
41
|
+
return "recording";
|
|
42
|
+
case "stopping":
|
|
43
|
+
case "finalizing":
|
|
44
|
+
case "uploading":
|
|
45
|
+
return "stopping";
|
|
46
|
+
case "completed":
|
|
47
|
+
case "cancelled":
|
|
48
|
+
return "stopped";
|
|
49
|
+
case "failed":
|
|
50
|
+
return "error";
|
|
51
|
+
case "idle":
|
|
52
|
+
case "starting":
|
|
53
|
+
default:
|
|
54
|
+
return "starting";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function levelFromRmsDb(rmsDb) {
|
|
58
|
+
if (typeof rmsDb !== "number" || Number.isNaN(rmsDb)) return 0;
|
|
59
|
+
return Math.max(0, Math.min(1, (rmsDb + 60) / 60));
|
|
60
|
+
}
|
|
61
|
+
function applyRecordingEventToTelemetry(telemetry, event) {
|
|
62
|
+
if (event.type === "recording.state") {
|
|
63
|
+
return {
|
|
64
|
+
...telemetry,
|
|
65
|
+
status: recordingStatusFromSidecarState(event.state),
|
|
66
|
+
...event.message && event.state === "failed" ? { error: event.message } : {}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (event.type === "audio.level") {
|
|
70
|
+
const level = levelFromRmsDb(event.rmsDb);
|
|
71
|
+
if (event.input === "microphone") {
|
|
72
|
+
return { ...telemetry, level: { ...telemetry.level, mic: level } };
|
|
73
|
+
}
|
|
74
|
+
return { ...telemetry, level: { ...telemetry.level, system: level } };
|
|
75
|
+
}
|
|
76
|
+
if (event.type === "error") {
|
|
77
|
+
return { ...telemetry, status: "error", error: event.message };
|
|
78
|
+
}
|
|
79
|
+
return telemetry;
|
|
80
|
+
}
|
|
81
|
+
function recordingArtifactFromRecordData(data) {
|
|
82
|
+
const artifact = data.artifacts.find((item) => item.kind === "recording_session") ?? data.artifacts[0];
|
|
83
|
+
const metadata = isRecord5(artifact?.metadata) ? artifact.metadata : {};
|
|
84
|
+
const audioPath = typeof metadata.audioPath === "string" ? metadata.audioPath : typeof artifact?.localPath === "string" ? artifact.localPath : void 0;
|
|
85
|
+
const durationMs = typeof metadata.durationMs === "number" && Number.isFinite(metadata.durationMs) ? metadata.durationMs : void 0;
|
|
86
|
+
const sizeBytes = typeof metadata.sizeBytes === "number" && Number.isFinite(metadata.sizeBytes) ? metadata.sizeBytes : void 0;
|
|
87
|
+
return {
|
|
88
|
+
sessionId: data.sessionId,
|
|
89
|
+
...audioPath ? { audioPath } : {},
|
|
90
|
+
...durationMs != null ? { durationMs } : {},
|
|
91
|
+
...sizeBytes != null ? { sizeBytes } : {},
|
|
92
|
+
uploadStatus: "local_only",
|
|
93
|
+
transcriptionStatus: "not_started"
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function artifactTelemetryPatch(artifact) {
|
|
97
|
+
return {
|
|
98
|
+
savedPath: artifact.audioPath,
|
|
99
|
+
durationMs: artifact.durationMs,
|
|
100
|
+
sizeBytes: artifact.sizeBytes
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function isRecord5(value) {
|
|
104
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
105
|
+
}
|
|
106
|
+
var DEFAULT_RECORDING_SOURCES, DEFAULT_RECORDING_SCENES, DEFAULT_RECORDING_SELECTION;
|
|
107
|
+
var init_recordingCore = __esm({
|
|
108
|
+
"src/recordingCore.ts"() {
|
|
109
|
+
"use strict";
|
|
110
|
+
DEFAULT_RECORDING_SOURCES = [
|
|
111
|
+
{
|
|
112
|
+
id: "system",
|
|
113
|
+
kind: "system",
|
|
114
|
+
label: "System audio",
|
|
115
|
+
canIncludeMicrophone: true
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "microphone",
|
|
119
|
+
kind: "microphone",
|
|
120
|
+
label: "Microphone only",
|
|
121
|
+
canIncludeMicrophone: false
|
|
122
|
+
}
|
|
123
|
+
];
|
|
124
|
+
DEFAULT_RECORDING_SCENES = [{ id: "default", label: "Default" }];
|
|
125
|
+
DEFAULT_RECORDING_SELECTION = {
|
|
126
|
+
sourceId: DEFAULT_RECORDING_SOURCES[0].id,
|
|
127
|
+
includeMicrophone: true,
|
|
128
|
+
sceneId: DEFAULT_RECORDING_SCENES[0].id
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
12
133
|
// src/tui/format.ts
|
|
13
134
|
function formatClockMs(ms) {
|
|
14
135
|
if (ms == null || !Number.isFinite(ms) || ms < 0) return "--:--";
|
|
@@ -577,8 +698,7 @@ function Header({ active }) {
|
|
|
577
698
|
] }),
|
|
578
699
|
/* @__PURE__ */ jsx5(Tab, { num: "1", label: "Overview", active: active === "overview" }),
|
|
579
700
|
/* @__PURE__ */ jsx5(Tab, { num: "2", label: "Jobs", active: active === "jobs" }),
|
|
580
|
-
/* @__PURE__ */ jsx5(Tab, { num: "3", label: "Account", active: active === "account" })
|
|
581
|
-
/* @__PURE__ */ jsx5(Tab, { num: "4", label: "Record", active: active === "record" })
|
|
701
|
+
/* @__PURE__ */ jsx5(Tab, { num: "3", label: "Account", active: active === "account" })
|
|
582
702
|
] });
|
|
583
703
|
}
|
|
584
704
|
function Tab({
|
|
@@ -1339,71 +1459,175 @@ var init_PermissionPreflightView = __esm({
|
|
|
1339
1459
|
}
|
|
1340
1460
|
});
|
|
1341
1461
|
|
|
1342
|
-
// src/tui/
|
|
1343
|
-
import {
|
|
1344
|
-
import { Box as Box14, Text as Text14 } from "ink";
|
|
1345
|
-
import {
|
|
1346
|
-
function
|
|
1347
|
-
|
|
1348
|
-
|
|
1462
|
+
// src/tui/RecordSetupView.tsx
|
|
1463
|
+
import { useState as useState5 } from "react";
|
|
1464
|
+
import { Box as Box14, Text as Text14, useInput as useInput5 } from "ink";
|
|
1465
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1466
|
+
function levelBar(level, width = 8) {
|
|
1467
|
+
const v = Math.max(0, Math.min(1, level ?? 0));
|
|
1468
|
+
const n = Math.round(v * width);
|
|
1469
|
+
return "\u2587".repeat(n) + "\u2581".repeat(width - n);
|
|
1470
|
+
}
|
|
1471
|
+
function RecordSetupView({
|
|
1472
|
+
model,
|
|
1473
|
+
onStart,
|
|
1474
|
+
onCancel
|
|
1475
|
+
}) {
|
|
1476
|
+
const size = useTerminalSize();
|
|
1477
|
+
const [srcIdx, setSrcIdx] = useState5(0);
|
|
1478
|
+
const [includeMic, setIncludeMic] = useState5(true);
|
|
1479
|
+
const [sceneIdx, setSceneIdx] = useState5(0);
|
|
1480
|
+
const sources = model.sources;
|
|
1481
|
+
const selected = sources[Math.min(srcIdx, Math.max(0, sources.length - 1))];
|
|
1482
|
+
const micApplicable = selected?.canIncludeMicrophone ?? selected?.kind !== "microphone";
|
|
1483
|
+
const wide = size.columns >= 100;
|
|
1484
|
+
useInput5((input, key) => {
|
|
1485
|
+
if (key.upArrow || input === "k") setSrcIdx((i) => Math.max(0, i - 1));
|
|
1486
|
+
else if (key.downArrow || input === "j") setSrcIdx((i) => Math.min(sources.length - 1, i + 1));
|
|
1487
|
+
else if (input === " " && micApplicable) setIncludeMic((m) => !m);
|
|
1488
|
+
else if (input === "s" && model.scenes.length > 1) setSceneIdx((i) => (i + 1) % model.scenes.length);
|
|
1489
|
+
else if (key.return && selected) {
|
|
1490
|
+
onStart({
|
|
1491
|
+
sourceId: selected.id,
|
|
1492
|
+
includeMicrophone: micApplicable ? includeMic : false,
|
|
1493
|
+
sceneId: model.scenes[sceneIdx]?.id
|
|
1494
|
+
});
|
|
1495
|
+
} else if (key.escape) onCancel();
|
|
1496
|
+
});
|
|
1497
|
+
const sourceList = /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
|
|
1498
|
+
/* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "SOURCE" }),
|
|
1499
|
+
sources.map((s, i) => {
|
|
1500
|
+
const on = i === srcIdx;
|
|
1501
|
+
return /* @__PURE__ */ jsxs13(Text14, { color: on ? "cyan" : void 0, wrap: "truncate-end", children: [
|
|
1502
|
+
on ? "\u25B8 \u25CF " : " \u25CB ",
|
|
1503
|
+
s.label
|
|
1504
|
+
] }, s.id);
|
|
1505
|
+
})
|
|
1506
|
+
] });
|
|
1507
|
+
const preview = /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
|
|
1508
|
+
/* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "INPUT PREVIEW" }),
|
|
1509
|
+
/* @__PURE__ */ jsx16(Text14, { color: "green", children: levelBar(model.previewLevel, 10) }),
|
|
1510
|
+
/* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "live level of selection" })
|
|
1511
|
+
] });
|
|
1512
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, children: [
|
|
1513
|
+
/* @__PURE__ */ jsx16(Text14, { bold: true, color: "magenta", children: "New recording" }),
|
|
1514
|
+
/* @__PURE__ */ jsxs13(Box14, { marginTop: 1, flexDirection: wide ? "row" : "column", children: [
|
|
1515
|
+
/* @__PURE__ */ jsx16(Box14, { flexGrow: 1, flexDirection: "column", children: sourceList }),
|
|
1516
|
+
wide ? /* @__PURE__ */ jsx16(Box14, { marginLeft: 4, children: preview }) : null
|
|
1517
|
+
] }),
|
|
1518
|
+
/* @__PURE__ */ jsxs13(Box14, { marginTop: 1, flexDirection: "column", children: [
|
|
1519
|
+
micApplicable ? /* @__PURE__ */ jsxs13(Text14, { children: [
|
|
1520
|
+
/* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Microphone " }),
|
|
1521
|
+
/* @__PURE__ */ jsx16(Text14, { color: includeMic ? "green" : "gray", children: includeMic ? "[x] include mic" : "[ ] include mic" }),
|
|
1522
|
+
/* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " (space)" })
|
|
1523
|
+
] }) : /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Microphone is the source" }),
|
|
1524
|
+
model.scenes.length > 0 ? /* @__PURE__ */ jsxs13(Text14, { children: [
|
|
1525
|
+
/* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Scene " }),
|
|
1526
|
+
/* @__PURE__ */ jsx16(Text14, { children: model.scenes[sceneIdx]?.label ?? "Default" }),
|
|
1527
|
+
model.scenes.length > 1 ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: " (s to change)" }) : null
|
|
1528
|
+
] }) : null
|
|
1529
|
+
] }),
|
|
1530
|
+
/* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "\u2191\u2193 source \xB7 space mic \xB7 \u23CE start recording \xB7 esc cancel" }) })
|
|
1531
|
+
] });
|
|
1532
|
+
}
|
|
1533
|
+
var init_RecordSetupView = __esm({
|
|
1534
|
+
"src/tui/RecordSetupView.tsx"() {
|
|
1535
|
+
"use strict";
|
|
1536
|
+
init_terminal();
|
|
1537
|
+
}
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1540
|
+
// src/tui/RecordingHeroScreen.tsx
|
|
1541
|
+
import { useEffect as useEffect2, useState as useState6 } from "react";
|
|
1542
|
+
import { Box as Box15, Text as Text15 } from "ink";
|
|
1543
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1544
|
+
function waveform(samples, width) {
|
|
1545
|
+
if (width <= 0) return "";
|
|
1546
|
+
const tail = samples.slice(-width);
|
|
1547
|
+
const pad = width - tail.length;
|
|
1548
|
+
const cells = tail.map((v) => {
|
|
1549
|
+
const i = Math.max(0, Math.min(BLOCKS.length - 1, Math.round(Math.max(0, Math.min(1, v)) * (BLOCKS.length - 1))));
|
|
1550
|
+
return BLOCKS[i];
|
|
1551
|
+
});
|
|
1552
|
+
return "\u2581".repeat(Math.max(0, pad)) + cells.join("");
|
|
1553
|
+
}
|
|
1554
|
+
function RecordingHeroScreen({
|
|
1555
|
+
telemetry,
|
|
1556
|
+
canPause = false,
|
|
1349
1557
|
now = () => Date.now()
|
|
1350
1558
|
}) {
|
|
1351
|
-
const
|
|
1352
|
-
const [tick, setTick] =
|
|
1559
|
+
const size = useTerminalSize();
|
|
1560
|
+
const [tick, setTick] = useState6(() => now());
|
|
1561
|
+
const [wave, setWave] = useState6([]);
|
|
1353
1562
|
useEffect2(() => {
|
|
1354
|
-
const
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
});
|
|
1358
|
-
return unsubscribe;
|
|
1359
|
-
}, [source]);
|
|
1563
|
+
const lvl = Math.max(telemetry.level?.system ?? 0, telemetry.level?.mic ?? 0);
|
|
1564
|
+
setWave((w) => [...w.slice(-512), lvl]);
|
|
1565
|
+
}, [telemetry.level]);
|
|
1360
1566
|
useEffect2(() => {
|
|
1361
1567
|
const id = setInterval(() => setTick(now()), 1e3);
|
|
1362
1568
|
return () => clearInterval(id);
|
|
1363
|
-
}, [
|
|
1364
|
-
const elapsed =
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
] });
|
|
1381
|
-
break;
|
|
1382
|
-
case "error":
|
|
1383
|
-
body = /* @__PURE__ */ jsx16(Text14, { color: "red", children: state.error ? `Recording error: ${state.error}` : "Recording error" });
|
|
1384
|
-
break;
|
|
1385
|
-
default:
|
|
1386
|
-
body = /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Starting recording\u2026" });
|
|
1569
|
+
}, []);
|
|
1570
|
+
const elapsed = telemetry.startedAtMs != null ? formatClockMs(Math.max(0, tick - telemetry.startedAtMs)) : "00:00";
|
|
1571
|
+
const innerWidth = Math.max(10, size.columns - 4);
|
|
1572
|
+
if (telemetry.status === "stopped") {
|
|
1573
|
+
const meta3 = [
|
|
1574
|
+
telemetry.durationMs != null ? formatClockMs(telemetry.durationMs) : null,
|
|
1575
|
+
formatBytes2(telemetry.sizeBytes) || null
|
|
1576
|
+
].filter(Boolean).join(" \xB7 ");
|
|
1577
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, children: [
|
|
1578
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "recappi \xB7 Recording" }),
|
|
1579
|
+
/* @__PURE__ */ jsxs14(Box15, { marginTop: 1, flexDirection: "column", children: [
|
|
1580
|
+
/* @__PURE__ */ jsx17(Text15, { color: "green", children: "\u2713 Saved to your Mac" }),
|
|
1581
|
+
meta3 ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: meta3 }) : null,
|
|
1582
|
+
telemetry.savedPath ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, wrap: "truncate-middle", children: telemetry.savedPath }) : null
|
|
1583
|
+
] }),
|
|
1584
|
+
/* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "Transcribe now? \u23CE yes \xB7 n not now \xB7 esc back" }) })
|
|
1585
|
+
] });
|
|
1387
1586
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1587
|
+
if (telemetry.status === "error") {
|
|
1588
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, children: [
|
|
1589
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "recappi \xB7 Recording" }),
|
|
1590
|
+
/* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text15, { color: "red", children: telemetry.error ? `Recording error: ${telemetry.error}` : "Recording error" }) }),
|
|
1591
|
+
/* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: "esc back" }) })
|
|
1592
|
+
] });
|
|
1593
|
+
}
|
|
1594
|
+
const paused = telemetry.status === "paused";
|
|
1595
|
+
const starting = telemetry.status === "starting" || telemetry.status === "stopping";
|
|
1596
|
+
const badge = paused ? "\u23F8 PAUSED" : starting ? "\u2026" : "\u23FA REC";
|
|
1597
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, height: size.rows, children: [
|
|
1598
|
+
/* @__PURE__ */ jsxs14(Text15, { children: [
|
|
1599
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, color: "magenta", children: "recappi" }),
|
|
1600
|
+
/* @__PURE__ */ jsx17(Text15, { dimColor: true, children: " \xB7 Recording" })
|
|
1601
|
+
] }),
|
|
1602
|
+
/* @__PURE__ */ jsxs14(Box15, { flexGrow: 1, flexDirection: "column", justifyContent: "center", alignItems: "center", children: [
|
|
1603
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, color: paused ? "yellow" : "red", children: badge }),
|
|
1604
|
+
/* @__PURE__ */ jsx17(Text15, { bold: true, children: elapsed }),
|
|
1605
|
+
/* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx17(Text15, { color: paused ? "gray" : "red", children: waveform(wave, innerWidth) }) }),
|
|
1606
|
+
/* @__PURE__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
|
|
1607
|
+
telemetry.sourceLabel,
|
|
1608
|
+
telemetry.micEnabled ? " + Microphone" : ""
|
|
1609
|
+
] }) })
|
|
1610
|
+
] }),
|
|
1611
|
+
/* @__PURE__ */ jsx17(Box15, { children: /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
|
|
1612
|
+
"q stop & save",
|
|
1613
|
+
canPause ? ` \xB7 p ${paused ? "resume" : "pause"}` : ""
|
|
1614
|
+
] }) })
|
|
1393
1615
|
] });
|
|
1394
1616
|
}
|
|
1395
|
-
var
|
|
1396
|
-
|
|
1617
|
+
var BLOCKS;
|
|
1618
|
+
var init_RecordingHeroScreen = __esm({
|
|
1619
|
+
"src/tui/RecordingHeroScreen.tsx"() {
|
|
1397
1620
|
"use strict";
|
|
1398
1621
|
init_format();
|
|
1399
|
-
|
|
1622
|
+
init_terminal();
|
|
1623
|
+
BLOCKS = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
1400
1624
|
}
|
|
1401
1625
|
});
|
|
1402
1626
|
|
|
1403
1627
|
// src/tui/AppShell.tsx
|
|
1404
|
-
import { useCallback, useEffect as useEffect3, useState as
|
|
1405
|
-
import { Box as
|
|
1406
|
-
import { Fragment as
|
|
1628
|
+
import { useCallback, useEffect as useEffect3, useState as useState7 } from "react";
|
|
1629
|
+
import { Box as Box16, Text as Text16, useApp, useInput as useInput6 } from "ink";
|
|
1630
|
+
import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1407
1631
|
function recordErrorCopy(code, message) {
|
|
1408
1632
|
switch (code) {
|
|
1409
1633
|
case "record.helper_unavailable":
|
|
@@ -1440,36 +1664,37 @@ function recordErrorCopy(code, message) {
|
|
|
1440
1664
|
return { title: "Couldn't start recording.", detail: message, tone: "red" };
|
|
1441
1665
|
}
|
|
1442
1666
|
}
|
|
1443
|
-
function recordErrorState(error51) {
|
|
1667
|
+
function recordErrorState(error51, selection) {
|
|
1444
1668
|
if (error51 instanceof Error) {
|
|
1445
|
-
const descriptor =
|
|
1669
|
+
const descriptor = isRecord7(error51) && isRecord7(error51.descriptor) ? error51.descriptor : void 0;
|
|
1446
1670
|
return {
|
|
1447
1671
|
kind: "error",
|
|
1448
1672
|
message: error51.message,
|
|
1449
1673
|
code: typeof descriptor?.code === "string" ? descriptor.code : "code" in error51 && typeof error51.code === "string" ? error51.code : void 0,
|
|
1450
|
-
data:
|
|
1674
|
+
data: isRecord7(error51) ? error51.data : void 0,
|
|
1675
|
+
...selection ? { selection } : {}
|
|
1451
1676
|
};
|
|
1452
1677
|
}
|
|
1453
|
-
return { kind: "error", message: String(error51) };
|
|
1678
|
+
return { kind: "error", message: String(error51), ...selection ? { selection } : {} };
|
|
1454
1679
|
}
|
|
1455
1680
|
function permissionItemsFromRecordError(data) {
|
|
1456
|
-
const sidecarError =
|
|
1457
|
-
const sidecarData =
|
|
1681
|
+
const sidecarError = isRecord7(data) ? data : void 0;
|
|
1682
|
+
const sidecarData = isRecord7(sidecarError?.data) ? sidecarError.data : void 0;
|
|
1458
1683
|
const permission = typeof sidecarData?.permission === "string" ? sidecarData.permission : "";
|
|
1459
1684
|
const hint = typeof sidecarData?.recovery === "string" ? sidecarData.recovery : void 0;
|
|
1460
1685
|
const item = permission === "microphone" ? "Microphone" : permission === "screen_recording" ? "Screen Recording" : "Recording";
|
|
1461
1686
|
return [{ name: item, status: "denied", ...hint ? { hint } : {} }];
|
|
1462
1687
|
}
|
|
1463
1688
|
function settingsUrlFromRecordError(data) {
|
|
1464
|
-
const sidecarError =
|
|
1465
|
-
const sidecarData =
|
|
1689
|
+
const sidecarError = isRecord7(data) ? data : void 0;
|
|
1690
|
+
const sidecarData = isRecord7(sidecarError?.data) ? sidecarError.data : void 0;
|
|
1466
1691
|
const permission = typeof sidecarData?.permission === "string" ? sidecarData.permission : "";
|
|
1467
1692
|
if (permission === "microphone") {
|
|
1468
1693
|
return "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone";
|
|
1469
1694
|
}
|
|
1470
1695
|
return "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
|
|
1471
1696
|
}
|
|
1472
|
-
function
|
|
1697
|
+
function isRecord7(value) {
|
|
1473
1698
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1474
1699
|
}
|
|
1475
1700
|
function AppShell({
|
|
@@ -1490,27 +1715,30 @@ function AppShell({
|
|
|
1490
1715
|
}) {
|
|
1491
1716
|
const { exit } = useApp();
|
|
1492
1717
|
const size = useTerminalSize();
|
|
1493
|
-
const [jobs, setJobs] =
|
|
1494
|
-
const [recordings, setRecordings] =
|
|
1495
|
-
const [recordingsNextCursor, setRecordingsNextCursor] =
|
|
1496
|
-
const [recordingsTotalCount, setRecordingsTotalCount] =
|
|
1497
|
-
const [stats, setStats] =
|
|
1498
|
-
const [accountStatus, setAccountStatus] =
|
|
1499
|
-
const [origin, setOrigin] =
|
|
1500
|
-
const [stack, setStack] =
|
|
1501
|
-
const [selected, setSelected] =
|
|
1502
|
-
const [spinnerFrame, setSpinnerFrame] =
|
|
1503
|
-
const [loadingMoreRecordings, setLoadingMoreRecordings] =
|
|
1504
|
-
const [loadError, setLoadError] =
|
|
1505
|
-
const [notice, setNotice] =
|
|
1506
|
-
const [summaryCache, setSummaryCache] =
|
|
1507
|
-
const [transcriptCache, setTranscriptCache] =
|
|
1718
|
+
const [jobs, setJobs] = useState7([]);
|
|
1719
|
+
const [recordings, setRecordings] = useState7([]);
|
|
1720
|
+
const [recordingsNextCursor, setRecordingsNextCursor] = useState7(null);
|
|
1721
|
+
const [recordingsTotalCount, setRecordingsTotalCount] = useState7(void 0);
|
|
1722
|
+
const [stats, setStats] = useState7(void 0);
|
|
1723
|
+
const [accountStatus, setAccountStatus] = useState7("loading");
|
|
1724
|
+
const [origin, setOrigin] = useState7("");
|
|
1725
|
+
const [stack, setStack] = useState7([{ kind: initialView }]);
|
|
1726
|
+
const [selected, setSelected] = useState7(0);
|
|
1727
|
+
const [spinnerFrame, setSpinnerFrame] = useState7(0);
|
|
1728
|
+
const [loadingMoreRecordings, setLoadingMoreRecordings] = useState7(false);
|
|
1729
|
+
const [loadError, setLoadError] = useState7(void 0);
|
|
1730
|
+
const [notice, setNotice] = useState7(void 0);
|
|
1731
|
+
const [summaryCache, setSummaryCache] = useState7(() => /* @__PURE__ */ new Map());
|
|
1732
|
+
const [transcriptCache, setTranscriptCache] = useState7(
|
|
1508
1733
|
() => /* @__PURE__ */ new Map()
|
|
1509
1734
|
);
|
|
1510
|
-
const [audioCache, setAudioCache] =
|
|
1511
|
-
const [downloadedIds, setDownloadedIds] =
|
|
1512
|
-
const [liveRecord, setLiveRecord] =
|
|
1513
|
-
const
|
|
1735
|
+
const [audioCache, setAudioCache] = useState7(() => /* @__PURE__ */ new Map());
|
|
1736
|
+
const [downloadedIds, setDownloadedIds] = useState7(() => /* @__PURE__ */ new Set());
|
|
1737
|
+
const [liveRecord, setLiveRecord] = useState7(void 0);
|
|
1738
|
+
const recordSetupModel = {
|
|
1739
|
+
sources: DEFAULT_RECORDING_SOURCES,
|
|
1740
|
+
scenes: DEFAULT_RECORDING_SCENES
|
|
1741
|
+
};
|
|
1514
1742
|
const refreshDownloadedIds = useCallback(async () => {
|
|
1515
1743
|
if (!listDownloadedRecordingIds) return;
|
|
1516
1744
|
try {
|
|
@@ -1522,46 +1750,97 @@ function AppShell({
|
|
|
1522
1750
|
void refreshDownloadedIds();
|
|
1523
1751
|
}, [refreshDownloadedIds]);
|
|
1524
1752
|
const screen = stack[stack.length - 1];
|
|
1753
|
+
const beginLiveRecord = useCallback(
|
|
1754
|
+
(selection = DEFAULT_RECORDING_SELECTION) => {
|
|
1755
|
+
const capture = recordingCaptureMappingFromSelection(selection, DEFAULT_RECORDING_SOURCES);
|
|
1756
|
+
const telemetry = {
|
|
1757
|
+
status: "starting",
|
|
1758
|
+
startedAtMs: now(),
|
|
1759
|
+
sourceLabel: capture.sourceLabel,
|
|
1760
|
+
micEnabled: capture.micEnabled
|
|
1761
|
+
};
|
|
1762
|
+
setStack((current) => {
|
|
1763
|
+
const withoutSetup = current[current.length - 1]?.kind === "recordSetup" ? current.slice(0, -1) : current;
|
|
1764
|
+
return [...withoutSetup, { kind: "record" }];
|
|
1765
|
+
});
|
|
1766
|
+
if (!startLiveRecord) {
|
|
1767
|
+
setLiveRecord({
|
|
1768
|
+
kind: "error",
|
|
1769
|
+
code: "record.helper_unavailable",
|
|
1770
|
+
message: "Live recording is not available",
|
|
1771
|
+
selection
|
|
1772
|
+
});
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
setLiveRecord({ kind: "starting", selection, telemetry });
|
|
1776
|
+
startLiveRecord(selection).then((session) => {
|
|
1777
|
+
setLiveRecord((current) => {
|
|
1778
|
+
if (current?.kind !== "starting") return current;
|
|
1779
|
+
return {
|
|
1780
|
+
kind: "live",
|
|
1781
|
+
session,
|
|
1782
|
+
selection,
|
|
1783
|
+
telemetry: { ...current.telemetry, status: "recording" }
|
|
1784
|
+
};
|
|
1785
|
+
});
|
|
1786
|
+
}).catch((error51) => {
|
|
1787
|
+
setLiveRecord(recordErrorState(error51, selection));
|
|
1788
|
+
});
|
|
1789
|
+
},
|
|
1790
|
+
[now, startLiveRecord]
|
|
1791
|
+
);
|
|
1525
1792
|
const stopLiveRecord = useCallback(async () => {
|
|
1526
1793
|
const current = liveRecord;
|
|
1527
1794
|
if (current?.kind === "live") {
|
|
1528
|
-
|
|
1795
|
+
const stoppingTelemetry = { ...current.telemetry, status: "stopping" };
|
|
1796
|
+
setLiveRecord({
|
|
1797
|
+
kind: "stopping",
|
|
1798
|
+
session: current.session,
|
|
1799
|
+
selection: current.selection,
|
|
1800
|
+
telemetry: stoppingTelemetry
|
|
1801
|
+
});
|
|
1529
1802
|
try {
|
|
1530
|
-
await current.session.stop();
|
|
1803
|
+
const data = await current.session.stop();
|
|
1804
|
+
const artifact = recordingArtifactFromRecordData(data);
|
|
1805
|
+
const fallbackDuration = current.telemetry.startedAtMs != null ? Math.max(0, now() - current.telemetry.startedAtMs) : void 0;
|
|
1806
|
+
setLiveRecord({
|
|
1807
|
+
kind: "stopped",
|
|
1808
|
+
selection: current.selection,
|
|
1809
|
+
artifact,
|
|
1810
|
+
telemetry: {
|
|
1811
|
+
...stoppingTelemetry,
|
|
1812
|
+
...artifactTelemetryPatch(artifact),
|
|
1813
|
+
...artifact.durationMs == null && fallbackDuration != null ? { durationMs: fallbackDuration } : {},
|
|
1814
|
+
status: "stopped"
|
|
1815
|
+
}
|
|
1816
|
+
});
|
|
1817
|
+
void refreshDownloadedIds();
|
|
1531
1818
|
} catch (error51) {
|
|
1532
|
-
|
|
1819
|
+
setLiveRecord({
|
|
1820
|
+
kind: "error",
|
|
1821
|
+
message: error51 instanceof Error ? error51.message : String(error51)
|
|
1822
|
+
});
|
|
1533
1823
|
}
|
|
1824
|
+
return;
|
|
1534
1825
|
}
|
|
1535
|
-
setLiveRecord(void 0);
|
|
1826
|
+
if (current?.kind === "stopped" || current?.kind === "error") setLiveRecord(void 0);
|
|
1536
1827
|
setStack([{ kind: "overview" }]);
|
|
1537
|
-
}, [liveRecord]);
|
|
1828
|
+
}, [liveRecord, now, refreshDownloadedIds]);
|
|
1829
|
+
const liveSession = liveRecord?.kind === "live" ? liveRecord.session : void 0;
|
|
1538
1830
|
useEffect3(() => {
|
|
1539
|
-
if (
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1831
|
+
if (!liveSession) return;
|
|
1832
|
+
const session = liveSession;
|
|
1833
|
+
const unsubscribe = session.source.onEvent((event) => {
|
|
1834
|
+
setLiveRecord((current) => {
|
|
1835
|
+
if (current?.kind !== "live" || current.session !== session) return current;
|
|
1836
|
+
return {
|
|
1837
|
+
...current,
|
|
1838
|
+
telemetry: applyRecordingEventToTelemetry(current.telemetry, event)
|
|
1839
|
+
};
|
|
1545
1840
|
});
|
|
1546
|
-
return;
|
|
1547
|
-
}
|
|
1548
|
-
let cancelled = false;
|
|
1549
|
-
setLiveRecord({ kind: "starting" });
|
|
1550
|
-
startLiveRecord().then((session) => {
|
|
1551
|
-
if (cancelled) {
|
|
1552
|
-
void session.stop();
|
|
1553
|
-
return;
|
|
1554
|
-
}
|
|
1555
|
-
setLiveRecord({ kind: "live", session });
|
|
1556
|
-
}).catch((error51) => {
|
|
1557
|
-
if (!cancelled) {
|
|
1558
|
-
setLiveRecord(recordErrorState(error51));
|
|
1559
|
-
}
|
|
1560
1841
|
});
|
|
1561
|
-
return
|
|
1562
|
-
|
|
1563
|
-
};
|
|
1564
|
-
}, [screen.kind, startLiveRecord, recordRetryNonce]);
|
|
1842
|
+
return unsubscribe;
|
|
1843
|
+
}, [liveSession]);
|
|
1565
1844
|
const selectedRecording = screen.kind === "overview" ? recordings[selected] : void 0;
|
|
1566
1845
|
const peekTranscriptId = selectedRecording?.activeTranscriptId ?? void 0;
|
|
1567
1846
|
useEffect3(() => {
|
|
@@ -1736,18 +2015,26 @@ function AppShell({
|
|
|
1736
2015
|
setNotice(void 0);
|
|
1737
2016
|
};
|
|
1738
2017
|
const back = () => setStack((st) => st.length > 1 ? st.slice(0, -1) : st);
|
|
1739
|
-
|
|
2018
|
+
useInput6((input, key) => {
|
|
1740
2019
|
setNotice(void 0);
|
|
2020
|
+
if (screen.kind === "recordSetup") {
|
|
2021
|
+
if (input === "q" || key.leftArrow) back();
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
1741
2024
|
if (screen.kind === "record") {
|
|
1742
2025
|
if (liveRecord?.kind === "error" && input === "r") {
|
|
1743
|
-
|
|
2026
|
+
beginLiveRecord(liveRecord.selection ?? DEFAULT_RECORDING_SELECTION);
|
|
1744
2027
|
return;
|
|
1745
2028
|
}
|
|
1746
2029
|
if (liveRecord?.kind === "error" && input === "o") {
|
|
1747
2030
|
openUrl2?.(settingsUrlFromRecordError(liveRecord.data));
|
|
1748
2031
|
return;
|
|
1749
2032
|
}
|
|
1750
|
-
if (
|
|
2033
|
+
if (liveRecord?.kind === "stopped" && key.return) {
|
|
2034
|
+
setNotice("Transcribe handoff is coming next.");
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
if (input === "q" || key.escape || key.leftArrow || input === "n") void stopLiveRecord();
|
|
1751
2038
|
return;
|
|
1752
2039
|
}
|
|
1753
2040
|
if (input === "q") return exit();
|
|
@@ -1755,7 +2042,10 @@ function AppShell({
|
|
|
1755
2042
|
if (input === "1") return goTab("overview");
|
|
1756
2043
|
if (input === "2") return goTab("jobs");
|
|
1757
2044
|
if (input === "3") return goTab("account");
|
|
1758
|
-
if (input === "
|
|
2045
|
+
if (input === "n") {
|
|
2046
|
+
setStack((st) => [...st, { kind: "recordSetup" }]);
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
1759
2049
|
if (input === "r") return void refresh({ resetRecordings: true });
|
|
1760
2050
|
if (screen.kind === "overview") {
|
|
1761
2051
|
if (key.upArrow || input === "k") setSelected((i) => Math.max(0, i - 1));
|
|
@@ -1802,18 +2092,18 @@ function AppShell({
|
|
|
1802
2092
|
}
|
|
1803
2093
|
});
|
|
1804
2094
|
if (screen.kind === "transcript") {
|
|
1805
|
-
return /* @__PURE__ */
|
|
2095
|
+
return /* @__PURE__ */ jsx18(TranscriptView, { loading: screen.loading, data: screen.data, error: screen.error });
|
|
1806
2096
|
}
|
|
1807
2097
|
if (screen.kind === "jobDetail") {
|
|
1808
2098
|
const job = jobs.find((j) => j.jobId === screen.jobId);
|
|
1809
|
-
if (!job) return /* @__PURE__ */
|
|
1810
|
-
return /* @__PURE__ */
|
|
2099
|
+
if (!job) return /* @__PURE__ */ jsx18(Missing, { label: "Job" });
|
|
2100
|
+
return /* @__PURE__ */ jsx18(Detail, { notice, children: /* @__PURE__ */ jsx18(JobDetailView, { item: job, origin, spinnerFrame, nowMs: now() }) });
|
|
1811
2101
|
}
|
|
1812
2102
|
if (screen.kind === "recordingDetail") {
|
|
1813
2103
|
const rec = recordings.find((r) => r.recordingId === screen.recordingId);
|
|
1814
|
-
if (!rec) return /* @__PURE__ */
|
|
2104
|
+
if (!rec) return /* @__PURE__ */ jsx18(Missing, { label: "Recording" });
|
|
1815
2105
|
const detailTranscript = rec.activeTranscriptId ? transcriptCache.get(rec.activeTranscriptId) : void 0;
|
|
1816
|
-
return /* @__PURE__ */
|
|
2106
|
+
return /* @__PURE__ */ jsx18(Detail, { notice, children: /* @__PURE__ */ jsx18(
|
|
1817
2107
|
RecordingDetailView,
|
|
1818
2108
|
{
|
|
1819
2109
|
item: rec,
|
|
@@ -1823,23 +2113,35 @@ function AppShell({
|
|
|
1823
2113
|
}
|
|
1824
2114
|
) });
|
|
1825
2115
|
}
|
|
2116
|
+
if (screen.kind === "recordSetup") {
|
|
2117
|
+
return /* @__PURE__ */ jsx18(Box16, { flexDirection: "column", height: size.rows, paddingX: 1, children: /* @__PURE__ */ jsx18(
|
|
2118
|
+
RecordSetupView,
|
|
2119
|
+
{
|
|
2120
|
+
model: recordSetupModel,
|
|
2121
|
+
onStart: beginLiveRecord,
|
|
2122
|
+
onCancel: () => setStack((st) => st.length > 1 ? st.slice(0, -1) : [{ kind: "overview" }])
|
|
2123
|
+
}
|
|
2124
|
+
) });
|
|
2125
|
+
}
|
|
1826
2126
|
if (screen.kind === "record") {
|
|
1827
2127
|
if (liveRecord?.kind === "live" && liveRecord.session.mode === "live_captions") {
|
|
1828
|
-
return /* @__PURE__ */
|
|
2128
|
+
return /* @__PURE__ */ jsx18(LiveCaptionsScreen, { source: liveRecord.session.source, now });
|
|
2129
|
+
}
|
|
2130
|
+
if (liveRecord?.kind === "live" || liveRecord?.kind === "starting" || liveRecord?.kind === "stopping" || liveRecord?.kind === "stopped") {
|
|
2131
|
+
return /* @__PURE__ */ jsx18(Detail, { notice, children: /* @__PURE__ */ jsx18(RecordingHeroScreen, { telemetry: liveRecord.telemetry, now }) });
|
|
1829
2132
|
}
|
|
1830
|
-
return /* @__PURE__ */
|
|
1831
|
-
/* @__PURE__ */
|
|
1832
|
-
/* @__PURE__ */ jsx17(Box15, { flexGrow: 1, flexDirection: "column", paddingX: 1, paddingTop: 1, children: liveRecord?.kind === "live" ? /* @__PURE__ */ jsx17(RecordingScreen, { source: liveRecord.session.source, now }) : liveRecord?.kind === "error" ? (() => {
|
|
2133
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", height: size.rows, paddingX: 1, children: [
|
|
2134
|
+
/* @__PURE__ */ jsx18(Box16, { flexGrow: 1, flexDirection: "column", paddingX: 1, paddingTop: 1, children: liveRecord?.kind === "error" ? (() => {
|
|
1833
2135
|
if (liveRecord.code === "record.permission_required") {
|
|
1834
|
-
return /* @__PURE__ */
|
|
2136
|
+
return /* @__PURE__ */ jsx18(PermissionPreflightView, { items: permissionItemsFromRecordError(liveRecord.data) });
|
|
1835
2137
|
}
|
|
1836
2138
|
const copy = recordErrorCopy(liveRecord.code, liveRecord.message);
|
|
1837
|
-
return /* @__PURE__ */
|
|
1838
|
-
/* @__PURE__ */
|
|
1839
|
-
copy.detail ? /* @__PURE__ */
|
|
2139
|
+
return /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
2140
|
+
/* @__PURE__ */ jsx18(Text16, { color: copy.tone, children: copy.title }),
|
|
2141
|
+
copy.detail ? /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: copy.detail }) : null
|
|
1840
2142
|
] });
|
|
1841
|
-
})() : /* @__PURE__ */
|
|
1842
|
-
/* @__PURE__ */
|
|
2143
|
+
})() : /* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "Starting recording\u2026" }) }),
|
|
2144
|
+
/* @__PURE__ */ jsx18(Footer, { keys: "r retry \xB7 o settings \xB7 q / esc / \u2190 back" })
|
|
1843
2145
|
] });
|
|
1844
2146
|
}
|
|
1845
2147
|
const tab = screen.kind === "jobs" ? "jobs" : screen.kind === "account" ? "account" : "overview";
|
|
@@ -1857,7 +2159,7 @@ function AppShell({
|
|
|
1857
2159
|
const showPeek = size.columns >= 100;
|
|
1858
2160
|
const peekWidth = showPeek ? 34 : 0;
|
|
1859
2161
|
const listColumns = showPeek ? Math.max(30, size.columns - peekWidth - 3) : size.columns;
|
|
1860
|
-
body = /* @__PURE__ */
|
|
2162
|
+
body = /* @__PURE__ */ jsx18(
|
|
1861
2163
|
OverviewView,
|
|
1862
2164
|
{
|
|
1863
2165
|
recordings: recordings.slice(win.start, win.end),
|
|
@@ -1877,11 +2179,11 @@ function AppShell({
|
|
|
1877
2179
|
);
|
|
1878
2180
|
} else if (screen.kind === "account") {
|
|
1879
2181
|
position = "";
|
|
1880
|
-
body = /* @__PURE__ */
|
|
2182
|
+
body = /* @__PURE__ */ jsx18(AccountView, { status: accountStatus });
|
|
1881
2183
|
} else {
|
|
1882
2184
|
const win = listWindow(selected, jobs.length, Math.max(3, size.rows - 4));
|
|
1883
2185
|
position = jobs.length ? `${selected + 1} / ${jobs.length}` : "0";
|
|
1884
|
-
body = /* @__PURE__ */
|
|
2186
|
+
body = /* @__PURE__ */ jsx18(
|
|
1885
2187
|
JobsView,
|
|
1886
2188
|
{
|
|
1887
2189
|
items: jobs.slice(win.start, win.end),
|
|
@@ -1890,35 +2192,35 @@ function AppShell({
|
|
|
1890
2192
|
}
|
|
1891
2193
|
);
|
|
1892
2194
|
}
|
|
1893
|
-
const footerKeys = screen.kind === "jobs" ? `${position} \xB7 \u2191\u2193 select \xB7 \u23CE job \xB7 t transcript \xB7
|
|
1894
|
-
return /* @__PURE__ */
|
|
1895
|
-
/* @__PURE__ */
|
|
1896
|
-
/* @__PURE__ */
|
|
2195
|
+
const footerKeys = screen.kind === "jobs" ? `${position} \xB7 \u2191\u2193 select \xB7 \u23CE job \xB7 t transcript \xB7 n record \xB7 1 overview \xB7 3 account \xB7 r refresh \xB7 q quit` : screen.kind === "account" ? "3 account \xB7 n record \xB7 1 overview \xB7 2 jobs \xB7 r refresh \xB7 q quit" : `${position} \xB7 \u2191\u2193 scroll \xB7 \u23CE open \xB7 t transcript \xB7 n record \xB7 2 jobs \xB7 3 account \xB7 r refresh \xB7 q quit`;
|
|
2196
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", height: size.rows, paddingX: 1, children: [
|
|
2197
|
+
/* @__PURE__ */ jsx18(Header, { active: tab }),
|
|
2198
|
+
/* @__PURE__ */ jsxs15(Box16, { flexGrow: 1, flexDirection: "column", children: [
|
|
1897
2199
|
body,
|
|
1898
|
-
loadError && jobs.length === 0 && recordings.length === 0 ? /* @__PURE__ */
|
|
2200
|
+
loadError && jobs.length === 0 && recordings.length === 0 ? /* @__PURE__ */ jsx18(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text16, { color: "red", children: [
|
|
1899
2201
|
"! ",
|
|
1900
2202
|
loadError
|
|
1901
2203
|
] }) }) : null
|
|
1902
2204
|
] }),
|
|
1903
|
-
/* @__PURE__ */
|
|
2205
|
+
/* @__PURE__ */ jsx18(Footer, { keys: footerKeys })
|
|
1904
2206
|
] });
|
|
1905
2207
|
}
|
|
1906
2208
|
function Detail({
|
|
1907
2209
|
notice,
|
|
1908
2210
|
children
|
|
1909
2211
|
}) {
|
|
1910
|
-
return /* @__PURE__ */
|
|
2212
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
1911
2213
|
children,
|
|
1912
|
-
notice ? /* @__PURE__ */
|
|
2214
|
+
notice ? /* @__PURE__ */ jsx18(Box16, { paddingX: 1, children: /* @__PURE__ */ jsx18(Text16, { color: "green", children: notice }) }) : null
|
|
1913
2215
|
] });
|
|
1914
2216
|
}
|
|
1915
2217
|
function Missing({ label }) {
|
|
1916
|
-
return /* @__PURE__ */
|
|
1917
|
-
/* @__PURE__ */
|
|
2218
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", paddingX: 1, children: [
|
|
2219
|
+
/* @__PURE__ */ jsxs15(Text16, { dimColor: true, children: [
|
|
1918
2220
|
label,
|
|
1919
2221
|
" no longer in the list."
|
|
1920
2222
|
] }),
|
|
1921
|
-
/* @__PURE__ */
|
|
2223
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "esc back \xB7 q quit" })
|
|
1922
2224
|
] });
|
|
1923
2225
|
}
|
|
1924
2226
|
var RECORDINGS_PAGE_SIZE, RECORDINGS_PREFETCH_REMAINING;
|
|
@@ -1934,7 +2236,9 @@ var init_AppShell = __esm({
|
|
|
1934
2236
|
init_TranscriptView();
|
|
1935
2237
|
init_LiveCaptionsScreen();
|
|
1936
2238
|
init_PermissionPreflightView();
|
|
1937
|
-
|
|
2239
|
+
init_RecordSetupView();
|
|
2240
|
+
init_RecordingHeroScreen();
|
|
2241
|
+
init_recordingCore();
|
|
1938
2242
|
init_format();
|
|
1939
2243
|
init_terminal();
|
|
1940
2244
|
RECORDINGS_PAGE_SIZE = 50;
|
|
@@ -1953,7 +2257,7 @@ __export(tui_exports, {
|
|
|
1953
2257
|
runDashboard: () => runDashboard,
|
|
1954
2258
|
useTerminalSize: () => useTerminalSize
|
|
1955
2259
|
});
|
|
1956
|
-
import
|
|
2260
|
+
import React9 from "react";
|
|
1957
2261
|
import { render as render2 } from "ink";
|
|
1958
2262
|
import { spawn as spawn3 } from "child_process";
|
|
1959
2263
|
function openUrl(url2) {
|
|
@@ -1974,7 +2278,7 @@ function copyText(text) {
|
|
|
1974
2278
|
async function runDashboard(deps) {
|
|
1975
2279
|
const renderApp = deps.renderApp ?? render2;
|
|
1976
2280
|
const app = renderApp(
|
|
1977
|
-
|
|
2281
|
+
React9.createElement(AppShell, {
|
|
1978
2282
|
fetchJobs: deps.fetchJobs,
|
|
1979
2283
|
fetchTranscript: deps.fetchTranscript,
|
|
1980
2284
|
fetchRecordings: deps.fetchRecordings,
|
|
@@ -19564,6 +19868,7 @@ import { createRequire as createRequire2 } from "module";
|
|
|
19564
19868
|
import { dirname, join } from "path";
|
|
19565
19869
|
import { fileURLToPath } from "url";
|
|
19566
19870
|
import { render, useInput as useInput2 } from "ink";
|
|
19871
|
+
init_recordingCore();
|
|
19567
19872
|
|
|
19568
19873
|
// src/sidecar.ts
|
|
19569
19874
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -19724,7 +20029,7 @@ var MiniSidecarClient = class {
|
|
|
19724
20029
|
}
|
|
19725
20030
|
};
|
|
19726
20031
|
function sidecarErrorToCliError(error51) {
|
|
19727
|
-
const data =
|
|
20032
|
+
const data = isRecord6(error51.data) ? error51.data : void 0;
|
|
19728
20033
|
const maybeCode = typeof data?.cliCode === "string" ? cliErrorCodeSchema.safeParse(data.cliCode) : void 0;
|
|
19729
20034
|
const hint = typeof data?.recovery === "string" ? data.recovery : void 0;
|
|
19730
20035
|
const retryable = typeof data?.retryable === "boolean" ? data.retryable : error51.code >= -32099 && error51.code <= -32e3;
|
|
@@ -19741,7 +20046,7 @@ function sidecarErrorToCliError(error51) {
|
|
|
19741
20046
|
retryable
|
|
19742
20047
|
});
|
|
19743
20048
|
}
|
|
19744
|
-
function
|
|
20049
|
+
function isRecord6(value) {
|
|
19745
20050
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19746
20051
|
}
|
|
19747
20052
|
function spawnMiniSidecar(opts) {
|
|
@@ -19807,14 +20112,21 @@ async function recordViaSidecar(opts) {
|
|
|
19807
20112
|
session?.close();
|
|
19808
20113
|
}
|
|
19809
20114
|
}
|
|
19810
|
-
async function startLiveRecordSession(opts
|
|
19811
|
-
|
|
20115
|
+
async function startLiveRecordSession(opts, selection = {
|
|
20116
|
+
sourceId: DEFAULT_RECORDING_SOURCES[0].id,
|
|
20117
|
+
includeMicrophone: true
|
|
20118
|
+
}, sources = DEFAULT_RECORDING_SOURCES) {
|
|
20119
|
+
const capture = recordingCaptureMappingFromSelection(selection, sources);
|
|
20120
|
+
const session = await startRecordSession({
|
|
20121
|
+
...opts,
|
|
20122
|
+
includeSystemAudio: capture.includeSystemAudio,
|
|
20123
|
+
includeMicrophone: capture.includeMicrophone,
|
|
20124
|
+
live: false
|
|
20125
|
+
});
|
|
19812
20126
|
return {
|
|
19813
20127
|
mode: "local",
|
|
19814
20128
|
source: session.source,
|
|
19815
|
-
stop:
|
|
19816
|
-
await session.stop();
|
|
19817
|
-
}
|
|
20129
|
+
stop: session.stop
|
|
19818
20130
|
};
|
|
19819
20131
|
}
|
|
19820
20132
|
async function startRecordSession(opts) {
|
|
@@ -20139,24 +20451,27 @@ async function runCli(deps = {}) {
|
|
|
20139
20451
|
recordingAudio,
|
|
20140
20452
|
listDownloadedRecordingIds: () => recordingAudio.listDownloadedRecordingIds(),
|
|
20141
20453
|
listDownloads: () => recordingAudio.listDownloads(),
|
|
20142
|
-
startLiveRecord: async () => {
|
|
20454
|
+
startLiveRecord: async (selection) => {
|
|
20143
20455
|
const liveStatus = await client.authStatus();
|
|
20144
20456
|
if (!liveStatus.loggedIn || !liveStatus.userId) {
|
|
20145
20457
|
throw cliError("auth.not_logged_in", "Sign in before starting a sidecar recording.", {
|
|
20146
20458
|
hint: "Run recappi auth login, or import the Recappi Mini session with recappi auth import-macos."
|
|
20147
20459
|
});
|
|
20148
20460
|
}
|
|
20149
|
-
return startLiveRecordSession(
|
|
20150
|
-
|
|
20151
|
-
|
|
20152
|
-
|
|
20153
|
-
|
|
20461
|
+
return startLiveRecordSession(
|
|
20462
|
+
{
|
|
20463
|
+
account: {
|
|
20464
|
+
backendOrigin: auth.origin,
|
|
20465
|
+
userId: liveStatus.userId,
|
|
20466
|
+
...liveStatus.email ? { email: liveStatus.email } : {}
|
|
20467
|
+
},
|
|
20468
|
+
cliVersion: CLI_VERSION,
|
|
20469
|
+
env: deps.env,
|
|
20470
|
+
homeDir: deps.homeDir,
|
|
20471
|
+
runtime: deps.recordRuntime
|
|
20154
20472
|
},
|
|
20155
|
-
|
|
20156
|
-
|
|
20157
|
-
homeDir: deps.homeDir,
|
|
20158
|
-
runtime: deps.recordRuntime
|
|
20159
|
-
});
|
|
20473
|
+
selection
|
|
20474
|
+
);
|
|
20160
20475
|
},
|
|
20161
20476
|
initialView: parsed.initialView
|
|
20162
20477
|
});
|