recappi 0.1.14 → 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 +502 -124
- 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,10 +1459,175 @@ var init_PermissionPreflightView = __esm({
|
|
|
1339
1459
|
}
|
|
1340
1460
|
});
|
|
1341
1461
|
|
|
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,
|
|
1557
|
+
now = () => Date.now()
|
|
1558
|
+
}) {
|
|
1559
|
+
const size = useTerminalSize();
|
|
1560
|
+
const [tick, setTick] = useState6(() => now());
|
|
1561
|
+
const [wave, setWave] = useState6([]);
|
|
1562
|
+
useEffect2(() => {
|
|
1563
|
+
const lvl = Math.max(telemetry.level?.system ?? 0, telemetry.level?.mic ?? 0);
|
|
1564
|
+
setWave((w) => [...w.slice(-512), lvl]);
|
|
1565
|
+
}, [telemetry.level]);
|
|
1566
|
+
useEffect2(() => {
|
|
1567
|
+
const id = setInterval(() => setTick(now()), 1e3);
|
|
1568
|
+
return () => clearInterval(id);
|
|
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
|
+
] });
|
|
1586
|
+
}
|
|
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
|
+
] }) })
|
|
1615
|
+
] });
|
|
1616
|
+
}
|
|
1617
|
+
var BLOCKS;
|
|
1618
|
+
var init_RecordingHeroScreen = __esm({
|
|
1619
|
+
"src/tui/RecordingHeroScreen.tsx"() {
|
|
1620
|
+
"use strict";
|
|
1621
|
+
init_format();
|
|
1622
|
+
init_terminal();
|
|
1623
|
+
BLOCKS = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
1624
|
+
}
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1342
1627
|
// src/tui/AppShell.tsx
|
|
1343
|
-
import { useCallback, useEffect as
|
|
1344
|
-
import { Box as
|
|
1345
|
-
import { Fragment as Fragment4, jsx 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";
|
|
1346
1631
|
function recordErrorCopy(code, message) {
|
|
1347
1632
|
switch (code) {
|
|
1348
1633
|
case "record.helper_unavailable":
|
|
@@ -1379,36 +1664,37 @@ function recordErrorCopy(code, message) {
|
|
|
1379
1664
|
return { title: "Couldn't start recording.", detail: message, tone: "red" };
|
|
1380
1665
|
}
|
|
1381
1666
|
}
|
|
1382
|
-
function recordErrorState(error51) {
|
|
1667
|
+
function recordErrorState(error51, selection) {
|
|
1383
1668
|
if (error51 instanceof Error) {
|
|
1384
|
-
const descriptor =
|
|
1669
|
+
const descriptor = isRecord7(error51) && isRecord7(error51.descriptor) ? error51.descriptor : void 0;
|
|
1385
1670
|
return {
|
|
1386
1671
|
kind: "error",
|
|
1387
1672
|
message: error51.message,
|
|
1388
1673
|
code: typeof descriptor?.code === "string" ? descriptor.code : "code" in error51 && typeof error51.code === "string" ? error51.code : void 0,
|
|
1389
|
-
data:
|
|
1674
|
+
data: isRecord7(error51) ? error51.data : void 0,
|
|
1675
|
+
...selection ? { selection } : {}
|
|
1390
1676
|
};
|
|
1391
1677
|
}
|
|
1392
|
-
return { kind: "error", message: String(error51) };
|
|
1678
|
+
return { kind: "error", message: String(error51), ...selection ? { selection } : {} };
|
|
1393
1679
|
}
|
|
1394
1680
|
function permissionItemsFromRecordError(data) {
|
|
1395
|
-
const sidecarError =
|
|
1396
|
-
const sidecarData =
|
|
1681
|
+
const sidecarError = isRecord7(data) ? data : void 0;
|
|
1682
|
+
const sidecarData = isRecord7(sidecarError?.data) ? sidecarError.data : void 0;
|
|
1397
1683
|
const permission = typeof sidecarData?.permission === "string" ? sidecarData.permission : "";
|
|
1398
1684
|
const hint = typeof sidecarData?.recovery === "string" ? sidecarData.recovery : void 0;
|
|
1399
1685
|
const item = permission === "microphone" ? "Microphone" : permission === "screen_recording" ? "Screen Recording" : "Recording";
|
|
1400
1686
|
return [{ name: item, status: "denied", ...hint ? { hint } : {} }];
|
|
1401
1687
|
}
|
|
1402
1688
|
function settingsUrlFromRecordError(data) {
|
|
1403
|
-
const sidecarError =
|
|
1404
|
-
const sidecarData =
|
|
1689
|
+
const sidecarError = isRecord7(data) ? data : void 0;
|
|
1690
|
+
const sidecarData = isRecord7(sidecarError?.data) ? sidecarError.data : void 0;
|
|
1405
1691
|
const permission = typeof sidecarData?.permission === "string" ? sidecarData.permission : "";
|
|
1406
1692
|
if (permission === "microphone") {
|
|
1407
1693
|
return "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone";
|
|
1408
1694
|
}
|
|
1409
1695
|
return "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
|
|
1410
1696
|
}
|
|
1411
|
-
function
|
|
1697
|
+
function isRecord7(value) {
|
|
1412
1698
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1413
1699
|
}
|
|
1414
1700
|
function AppShell({
|
|
@@ -1429,27 +1715,30 @@ function AppShell({
|
|
|
1429
1715
|
}) {
|
|
1430
1716
|
const { exit } = useApp();
|
|
1431
1717
|
const size = useTerminalSize();
|
|
1432
|
-
const [jobs, setJobs] =
|
|
1433
|
-
const [recordings, setRecordings] =
|
|
1434
|
-
const [recordingsNextCursor, setRecordingsNextCursor] =
|
|
1435
|
-
const [recordingsTotalCount, setRecordingsTotalCount] =
|
|
1436
|
-
const [stats, setStats] =
|
|
1437
|
-
const [accountStatus, setAccountStatus] =
|
|
1438
|
-
const [origin, setOrigin] =
|
|
1439
|
-
const [stack, setStack] =
|
|
1440
|
-
const [selected, setSelected] =
|
|
1441
|
-
const [spinnerFrame, setSpinnerFrame] =
|
|
1442
|
-
const [loadingMoreRecordings, setLoadingMoreRecordings] =
|
|
1443
|
-
const [loadError, setLoadError] =
|
|
1444
|
-
const [notice, setNotice] =
|
|
1445
|
-
const [summaryCache, setSummaryCache] =
|
|
1446
|
-
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(
|
|
1447
1733
|
() => /* @__PURE__ */ new Map()
|
|
1448
1734
|
);
|
|
1449
|
-
const [audioCache, setAudioCache] =
|
|
1450
|
-
const [downloadedIds, setDownloadedIds] =
|
|
1451
|
-
const [liveRecord, setLiveRecord] =
|
|
1452
|
-
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
|
+
};
|
|
1453
1742
|
const refreshDownloadedIds = useCallback(async () => {
|
|
1454
1743
|
if (!listDownloadedRecordingIds) return;
|
|
1455
1744
|
try {
|
|
@@ -1457,53 +1746,104 @@ function AppShell({
|
|
|
1457
1746
|
} catch {
|
|
1458
1747
|
}
|
|
1459
1748
|
}, [listDownloadedRecordingIds]);
|
|
1460
|
-
|
|
1749
|
+
useEffect3(() => {
|
|
1461
1750
|
void refreshDownloadedIds();
|
|
1462
1751
|
}, [refreshDownloadedIds]);
|
|
1463
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
|
+
);
|
|
1464
1792
|
const stopLiveRecord = useCallback(async () => {
|
|
1465
1793
|
const current = liveRecord;
|
|
1466
1794
|
if (current?.kind === "live") {
|
|
1467
|
-
|
|
1795
|
+
const stoppingTelemetry = { ...current.telemetry, status: "stopping" };
|
|
1796
|
+
setLiveRecord({
|
|
1797
|
+
kind: "stopping",
|
|
1798
|
+
session: current.session,
|
|
1799
|
+
selection: current.selection,
|
|
1800
|
+
telemetry: stoppingTelemetry
|
|
1801
|
+
});
|
|
1468
1802
|
try {
|
|
1469
|
-
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();
|
|
1470
1818
|
} catch (error51) {
|
|
1471
|
-
|
|
1819
|
+
setLiveRecord({
|
|
1820
|
+
kind: "error",
|
|
1821
|
+
message: error51 instanceof Error ? error51.message : String(error51)
|
|
1822
|
+
});
|
|
1472
1823
|
}
|
|
1824
|
+
return;
|
|
1473
1825
|
}
|
|
1474
|
-
setLiveRecord(void 0);
|
|
1826
|
+
if (current?.kind === "stopped" || current?.kind === "error") setLiveRecord(void 0);
|
|
1475
1827
|
setStack([{ kind: "overview" }]);
|
|
1476
|
-
}, [liveRecord]);
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
if (!
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1828
|
+
}, [liveRecord, now, refreshDownloadedIds]);
|
|
1829
|
+
const liveSession = liveRecord?.kind === "live" ? liveRecord.session : void 0;
|
|
1830
|
+
useEffect3(() => {
|
|
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
|
+
};
|
|
1484
1840
|
});
|
|
1485
|
-
return;
|
|
1486
|
-
}
|
|
1487
|
-
let cancelled = false;
|
|
1488
|
-
setLiveRecord({ kind: "starting" });
|
|
1489
|
-
startLiveRecord().then((session) => {
|
|
1490
|
-
if (cancelled) {
|
|
1491
|
-
void session.stop();
|
|
1492
|
-
return;
|
|
1493
|
-
}
|
|
1494
|
-
setLiveRecord({ kind: "live", session });
|
|
1495
|
-
}).catch((error51) => {
|
|
1496
|
-
if (!cancelled) {
|
|
1497
|
-
setLiveRecord(recordErrorState(error51));
|
|
1498
|
-
}
|
|
1499
1841
|
});
|
|
1500
|
-
return
|
|
1501
|
-
|
|
1502
|
-
};
|
|
1503
|
-
}, [screen.kind, startLiveRecord, recordRetryNonce]);
|
|
1842
|
+
return unsubscribe;
|
|
1843
|
+
}, [liveSession]);
|
|
1504
1844
|
const selectedRecording = screen.kind === "overview" ? recordings[selected] : void 0;
|
|
1505
1845
|
const peekTranscriptId = selectedRecording?.activeTranscriptId ?? void 0;
|
|
1506
|
-
|
|
1846
|
+
useEffect3(() => {
|
|
1507
1847
|
if (!peekTranscriptId || summaryCache.has(peekTranscriptId)) return;
|
|
1508
1848
|
let cancelled = false;
|
|
1509
1849
|
const timer = setTimeout(() => {
|
|
@@ -1571,13 +1911,13 @@ function AppShell({
|
|
|
1571
1911
|
setLoadingMoreRecordings(false);
|
|
1572
1912
|
}
|
|
1573
1913
|
}, [fetchRecordings, loadingMoreRecordings, recordingsNextCursor]);
|
|
1574
|
-
|
|
1914
|
+
useEffect3(() => {
|
|
1575
1915
|
void refresh({ resetRecordings: true });
|
|
1576
1916
|
const id = setInterval(() => void refresh(), pollMs);
|
|
1577
1917
|
return () => clearInterval(id);
|
|
1578
1918
|
}, [refresh, pollMs]);
|
|
1579
1919
|
const hasRunning = jobs.some((item) => item.status === "running");
|
|
1580
|
-
|
|
1920
|
+
useEffect3(() => {
|
|
1581
1921
|
if (!hasRunning) return;
|
|
1582
1922
|
const id = setInterval(() => setSpinnerFrame((f) => f + 1), spinnerMs);
|
|
1583
1923
|
return () => clearInterval(id);
|
|
@@ -1591,11 +1931,11 @@ function AppShell({
|
|
|
1591
1931
|
}
|
|
1592
1932
|
}
|
|
1593
1933
|
const listLength = screen.kind === "jobs" ? jobs.length : screen.kind === "overview" ? recordings.length : 0;
|
|
1594
|
-
|
|
1934
|
+
useEffect3(() => {
|
|
1595
1935
|
setSelected((i) => Math.max(0, Math.min(i, Math.max(0, listLength - 1))));
|
|
1596
1936
|
}, [listLength]);
|
|
1597
1937
|
const visibleRecordingRows = Math.max(3, size.rows - 6);
|
|
1598
|
-
|
|
1938
|
+
useEffect3(() => {
|
|
1599
1939
|
if (screen.kind !== "overview" || !recordingsNextCursor) return;
|
|
1600
1940
|
const nearLoadedEnd = recordings.length - selected <= RECORDINGS_PREFETCH_REMAINING;
|
|
1601
1941
|
const underfilledViewport = recordings.length < visibleRecordingRows;
|
|
@@ -1628,7 +1968,7 @@ function AppShell({
|
|
|
1628
1968
|
[fetchTranscript]
|
|
1629
1969
|
);
|
|
1630
1970
|
const detailTranscriptId = screen.kind === "recordingDetail" ? recordings.find((r) => r.recordingId === screen.recordingId)?.activeTranscriptId : void 0;
|
|
1631
|
-
|
|
1971
|
+
useEffect3(() => {
|
|
1632
1972
|
if (!detailTranscriptId || transcriptCache.has(detailTranscriptId)) return;
|
|
1633
1973
|
let cancelled = false;
|
|
1634
1974
|
setTranscriptCache((m) => new Map(m).set(detailTranscriptId, "loading"));
|
|
@@ -1675,18 +2015,26 @@ function AppShell({
|
|
|
1675
2015
|
setNotice(void 0);
|
|
1676
2016
|
};
|
|
1677
2017
|
const back = () => setStack((st) => st.length > 1 ? st.slice(0, -1) : st);
|
|
1678
|
-
|
|
2018
|
+
useInput6((input, key) => {
|
|
1679
2019
|
setNotice(void 0);
|
|
2020
|
+
if (screen.kind === "recordSetup") {
|
|
2021
|
+
if (input === "q" || key.leftArrow) back();
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
1680
2024
|
if (screen.kind === "record") {
|
|
1681
2025
|
if (liveRecord?.kind === "error" && input === "r") {
|
|
1682
|
-
|
|
2026
|
+
beginLiveRecord(liveRecord.selection ?? DEFAULT_RECORDING_SELECTION);
|
|
1683
2027
|
return;
|
|
1684
2028
|
}
|
|
1685
2029
|
if (liveRecord?.kind === "error" && input === "o") {
|
|
1686
2030
|
openUrl2?.(settingsUrlFromRecordError(liveRecord.data));
|
|
1687
2031
|
return;
|
|
1688
2032
|
}
|
|
1689
|
-
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();
|
|
1690
2038
|
return;
|
|
1691
2039
|
}
|
|
1692
2040
|
if (input === "q") return exit();
|
|
@@ -1694,7 +2042,10 @@ function AppShell({
|
|
|
1694
2042
|
if (input === "1") return goTab("overview");
|
|
1695
2043
|
if (input === "2") return goTab("jobs");
|
|
1696
2044
|
if (input === "3") return goTab("account");
|
|
1697
|
-
if (input === "
|
|
2045
|
+
if (input === "n") {
|
|
2046
|
+
setStack((st) => [...st, { kind: "recordSetup" }]);
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
1698
2049
|
if (input === "r") return void refresh({ resetRecordings: true });
|
|
1699
2050
|
if (screen.kind === "overview") {
|
|
1700
2051
|
if (key.upArrow || input === "k") setSelected((i) => Math.max(0, i - 1));
|
|
@@ -1741,18 +2092,18 @@ function AppShell({
|
|
|
1741
2092
|
}
|
|
1742
2093
|
});
|
|
1743
2094
|
if (screen.kind === "transcript") {
|
|
1744
|
-
return /* @__PURE__ */
|
|
2095
|
+
return /* @__PURE__ */ jsx18(TranscriptView, { loading: screen.loading, data: screen.data, error: screen.error });
|
|
1745
2096
|
}
|
|
1746
2097
|
if (screen.kind === "jobDetail") {
|
|
1747
2098
|
const job = jobs.find((j) => j.jobId === screen.jobId);
|
|
1748
|
-
if (!job) return /* @__PURE__ */
|
|
1749
|
-
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() }) });
|
|
1750
2101
|
}
|
|
1751
2102
|
if (screen.kind === "recordingDetail") {
|
|
1752
2103
|
const rec = recordings.find((r) => r.recordingId === screen.recordingId);
|
|
1753
|
-
if (!rec) return /* @__PURE__ */
|
|
2104
|
+
if (!rec) return /* @__PURE__ */ jsx18(Missing, { label: "Recording" });
|
|
1754
2105
|
const detailTranscript = rec.activeTranscriptId ? transcriptCache.get(rec.activeTranscriptId) : void 0;
|
|
1755
|
-
return /* @__PURE__ */
|
|
2106
|
+
return /* @__PURE__ */ jsx18(Detail, { notice, children: /* @__PURE__ */ jsx18(
|
|
1756
2107
|
RecordingDetailView,
|
|
1757
2108
|
{
|
|
1758
2109
|
item: rec,
|
|
@@ -1762,23 +2113,35 @@ function AppShell({
|
|
|
1762
2113
|
}
|
|
1763
2114
|
) });
|
|
1764
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
|
+
}
|
|
1765
2126
|
if (screen.kind === "record") {
|
|
1766
|
-
if (liveRecord?.kind === "live") {
|
|
1767
|
-
return /* @__PURE__ */
|
|
2127
|
+
if (liveRecord?.kind === "live" && liveRecord.session.mode === "live_captions") {
|
|
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 }) });
|
|
1768
2132
|
}
|
|
1769
|
-
return /* @__PURE__ */
|
|
1770
|
-
/* @__PURE__ */
|
|
1771
|
-
/* @__PURE__ */ jsx16(Box14, { flexGrow: 1, flexDirection: "column", paddingX: 1, paddingTop: 1, children: 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" ? (() => {
|
|
1772
2135
|
if (liveRecord.code === "record.permission_required") {
|
|
1773
|
-
return /* @__PURE__ */
|
|
2136
|
+
return /* @__PURE__ */ jsx18(PermissionPreflightView, { items: permissionItemsFromRecordError(liveRecord.data) });
|
|
1774
2137
|
}
|
|
1775
2138
|
const copy = recordErrorCopy(liveRecord.code, liveRecord.message);
|
|
1776
|
-
return /* @__PURE__ */
|
|
1777
|
-
/* @__PURE__ */
|
|
1778
|
-
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
|
|
1779
2142
|
] });
|
|
1780
|
-
})() : /* @__PURE__ */
|
|
1781
|
-
/* @__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" })
|
|
1782
2145
|
] });
|
|
1783
2146
|
}
|
|
1784
2147
|
const tab = screen.kind === "jobs" ? "jobs" : screen.kind === "account" ? "account" : "overview";
|
|
@@ -1796,7 +2159,7 @@ function AppShell({
|
|
|
1796
2159
|
const showPeek = size.columns >= 100;
|
|
1797
2160
|
const peekWidth = showPeek ? 34 : 0;
|
|
1798
2161
|
const listColumns = showPeek ? Math.max(30, size.columns - peekWidth - 3) : size.columns;
|
|
1799
|
-
body = /* @__PURE__ */
|
|
2162
|
+
body = /* @__PURE__ */ jsx18(
|
|
1800
2163
|
OverviewView,
|
|
1801
2164
|
{
|
|
1802
2165
|
recordings: recordings.slice(win.start, win.end),
|
|
@@ -1816,11 +2179,11 @@ function AppShell({
|
|
|
1816
2179
|
);
|
|
1817
2180
|
} else if (screen.kind === "account") {
|
|
1818
2181
|
position = "";
|
|
1819
|
-
body = /* @__PURE__ */
|
|
2182
|
+
body = /* @__PURE__ */ jsx18(AccountView, { status: accountStatus });
|
|
1820
2183
|
} else {
|
|
1821
2184
|
const win = listWindow(selected, jobs.length, Math.max(3, size.rows - 4));
|
|
1822
2185
|
position = jobs.length ? `${selected + 1} / ${jobs.length}` : "0";
|
|
1823
|
-
body = /* @__PURE__ */
|
|
2186
|
+
body = /* @__PURE__ */ jsx18(
|
|
1824
2187
|
JobsView,
|
|
1825
2188
|
{
|
|
1826
2189
|
items: jobs.slice(win.start, win.end),
|
|
@@ -1829,35 +2192,35 @@ function AppShell({
|
|
|
1829
2192
|
}
|
|
1830
2193
|
);
|
|
1831
2194
|
}
|
|
1832
|
-
const footerKeys = screen.kind === "jobs" ? `${position} \xB7 \u2191\u2193 select \xB7 \u23CE job \xB7 t transcript \xB7
|
|
1833
|
-
return /* @__PURE__ */
|
|
1834
|
-
/* @__PURE__ */
|
|
1835
|
-
/* @__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: [
|
|
1836
2199
|
body,
|
|
1837
|
-
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: [
|
|
1838
2201
|
"! ",
|
|
1839
2202
|
loadError
|
|
1840
2203
|
] }) }) : null
|
|
1841
2204
|
] }),
|
|
1842
|
-
/* @__PURE__ */
|
|
2205
|
+
/* @__PURE__ */ jsx18(Footer, { keys: footerKeys })
|
|
1843
2206
|
] });
|
|
1844
2207
|
}
|
|
1845
2208
|
function Detail({
|
|
1846
2209
|
notice,
|
|
1847
2210
|
children
|
|
1848
2211
|
}) {
|
|
1849
|
-
return /* @__PURE__ */
|
|
2212
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
1850
2213
|
children,
|
|
1851
|
-
notice ? /* @__PURE__ */
|
|
2214
|
+
notice ? /* @__PURE__ */ jsx18(Box16, { paddingX: 1, children: /* @__PURE__ */ jsx18(Text16, { color: "green", children: notice }) }) : null
|
|
1852
2215
|
] });
|
|
1853
2216
|
}
|
|
1854
2217
|
function Missing({ label }) {
|
|
1855
|
-
return /* @__PURE__ */
|
|
1856
|
-
/* @__PURE__ */
|
|
2218
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", paddingX: 1, children: [
|
|
2219
|
+
/* @__PURE__ */ jsxs15(Text16, { dimColor: true, children: [
|
|
1857
2220
|
label,
|
|
1858
2221
|
" no longer in the list."
|
|
1859
2222
|
] }),
|
|
1860
|
-
/* @__PURE__ */
|
|
2223
|
+
/* @__PURE__ */ jsx18(Text16, { dimColor: true, children: "esc back \xB7 q quit" })
|
|
1861
2224
|
] });
|
|
1862
2225
|
}
|
|
1863
2226
|
var RECORDINGS_PAGE_SIZE, RECORDINGS_PREFETCH_REMAINING;
|
|
@@ -1873,6 +2236,9 @@ var init_AppShell = __esm({
|
|
|
1873
2236
|
init_TranscriptView();
|
|
1874
2237
|
init_LiveCaptionsScreen();
|
|
1875
2238
|
init_PermissionPreflightView();
|
|
2239
|
+
init_RecordSetupView();
|
|
2240
|
+
init_RecordingHeroScreen();
|
|
2241
|
+
init_recordingCore();
|
|
1876
2242
|
init_format();
|
|
1877
2243
|
init_terminal();
|
|
1878
2244
|
RECORDINGS_PAGE_SIZE = 50;
|
|
@@ -1891,7 +2257,7 @@ __export(tui_exports, {
|
|
|
1891
2257
|
runDashboard: () => runDashboard,
|
|
1892
2258
|
useTerminalSize: () => useTerminalSize
|
|
1893
2259
|
});
|
|
1894
|
-
import
|
|
2260
|
+
import React9 from "react";
|
|
1895
2261
|
import { render as render2 } from "ink";
|
|
1896
2262
|
import { spawn as spawn3 } from "child_process";
|
|
1897
2263
|
function openUrl(url2) {
|
|
@@ -1912,7 +2278,7 @@ function copyText(text) {
|
|
|
1912
2278
|
async function runDashboard(deps) {
|
|
1913
2279
|
const renderApp = deps.renderApp ?? render2;
|
|
1914
2280
|
const app = renderApp(
|
|
1915
|
-
|
|
2281
|
+
React9.createElement(AppShell, {
|
|
1916
2282
|
fetchJobs: deps.fetchJobs,
|
|
1917
2283
|
fetchTranscript: deps.fetchTranscript,
|
|
1918
2284
|
fetchRecordings: deps.fetchRecordings,
|
|
@@ -19502,6 +19868,7 @@ import { createRequire as createRequire2 } from "module";
|
|
|
19502
19868
|
import { dirname, join } from "path";
|
|
19503
19869
|
import { fileURLToPath } from "url";
|
|
19504
19870
|
import { render, useInput as useInput2 } from "ink";
|
|
19871
|
+
init_recordingCore();
|
|
19505
19872
|
|
|
19506
19873
|
// src/sidecar.ts
|
|
19507
19874
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -19662,7 +20029,7 @@ var MiniSidecarClient = class {
|
|
|
19662
20029
|
}
|
|
19663
20030
|
};
|
|
19664
20031
|
function sidecarErrorToCliError(error51) {
|
|
19665
|
-
const data =
|
|
20032
|
+
const data = isRecord6(error51.data) ? error51.data : void 0;
|
|
19666
20033
|
const maybeCode = typeof data?.cliCode === "string" ? cliErrorCodeSchema.safeParse(data.cliCode) : void 0;
|
|
19667
20034
|
const hint = typeof data?.recovery === "string" ? data.recovery : void 0;
|
|
19668
20035
|
const retryable = typeof data?.retryable === "boolean" ? data.retryable : error51.code >= -32099 && error51.code <= -32e3;
|
|
@@ -19679,7 +20046,7 @@ function sidecarErrorToCliError(error51) {
|
|
|
19679
20046
|
retryable
|
|
19680
20047
|
});
|
|
19681
20048
|
}
|
|
19682
|
-
function
|
|
20049
|
+
function isRecord6(value) {
|
|
19683
20050
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19684
20051
|
}
|
|
19685
20052
|
function spawnMiniSidecar(opts) {
|
|
@@ -19745,13 +20112,21 @@ async function recordViaSidecar(opts) {
|
|
|
19745
20112
|
session?.close();
|
|
19746
20113
|
}
|
|
19747
20114
|
}
|
|
19748
|
-
async function startLiveRecordSession(opts
|
|
19749
|
-
|
|
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
|
+
});
|
|
19750
20126
|
return {
|
|
20127
|
+
mode: "local",
|
|
19751
20128
|
source: session.source,
|
|
19752
|
-
stop:
|
|
19753
|
-
await session.stop();
|
|
19754
|
-
}
|
|
20129
|
+
stop: session.stop
|
|
19755
20130
|
};
|
|
19756
20131
|
}
|
|
19757
20132
|
async function startRecordSession(opts) {
|
|
@@ -20076,24 +20451,27 @@ async function runCli(deps = {}) {
|
|
|
20076
20451
|
recordingAudio,
|
|
20077
20452
|
listDownloadedRecordingIds: () => recordingAudio.listDownloadedRecordingIds(),
|
|
20078
20453
|
listDownloads: () => recordingAudio.listDownloads(),
|
|
20079
|
-
startLiveRecord: async () => {
|
|
20454
|
+
startLiveRecord: async (selection) => {
|
|
20080
20455
|
const liveStatus = await client.authStatus();
|
|
20081
20456
|
if (!liveStatus.loggedIn || !liveStatus.userId) {
|
|
20082
20457
|
throw cliError("auth.not_logged_in", "Sign in before starting a sidecar recording.", {
|
|
20083
20458
|
hint: "Run recappi auth login, or import the Recappi Mini session with recappi auth import-macos."
|
|
20084
20459
|
});
|
|
20085
20460
|
}
|
|
20086
|
-
return startLiveRecordSession(
|
|
20087
|
-
|
|
20088
|
-
|
|
20089
|
-
|
|
20090
|
-
|
|
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
|
|
20091
20472
|
},
|
|
20092
|
-
|
|
20093
|
-
|
|
20094
|
-
homeDir: deps.homeDir,
|
|
20095
|
-
runtime: deps.recordRuntime
|
|
20096
|
-
});
|
|
20473
|
+
selection
|
|
20474
|
+
);
|
|
20097
20475
|
},
|
|
20098
20476
|
initialView: parsed.initialView
|
|
20099
20477
|
});
|