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 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/RecordingScreen.tsx
1343
- import { useEffect as useEffect2, useState as useState5 } from "react";
1344
- import { Box as Box14, Text as Text14 } from "ink";
1345
- import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1346
- function RecordingScreen({
1347
- source,
1348
- savedPath,
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 [state, setState] = useState5(initialLiveCaptionsState);
1352
- const [tick, setTick] = useState5(() => now());
1559
+ const size = useTerminalSize();
1560
+ const [tick, setTick] = useState6(() => now());
1561
+ const [wave, setWave] = useState6([]);
1353
1562
  useEffect2(() => {
1354
- const unsubscribe = source.onEvent((event) => {
1355
- const mapped = sidecarToLiveCaptionEvent(event);
1356
- if (mapped) setState((s) => liveCaptionReducer(s, mapped));
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
- }, [now]);
1364
- const elapsed = state.startedAtMs != null ? formatClockMs(Math.max(0, tick - state.startedAtMs)) : null;
1365
- let body;
1366
- switch (state.status) {
1367
- case "live":
1368
- body = /* @__PURE__ */ jsxs13(Fragment4, { children: [
1369
- /* @__PURE__ */ jsxs13(Text14, { children: [
1370
- /* @__PURE__ */ jsx16(Text14, { bold: true, color: "red", children: "\u25CF Recording" }),
1371
- elapsed ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: ` ${elapsed}` }) : null
1372
- ] }),
1373
- /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "Recording to your Mac. Press q to stop and save." })
1374
- ] });
1375
- break;
1376
- case "stopped":
1377
- body = /* @__PURE__ */ jsxs13(Fragment4, { children: [
1378
- /* @__PURE__ */ jsx16(Text14, { color: "green", children: "\u2713 Recording saved to your Mac." }),
1379
- savedPath ? /* @__PURE__ */ jsx16(Text14, { dimColor: true, wrap: "truncate-middle", children: savedPath }) : null
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
- const footer = state.status === "stopped" ? "esc / \u2190 back" : "q / esc / \u2190 stop & save";
1389
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", paddingX: 1, children: [
1390
- /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: "\u2039 Record" }),
1391
- /* @__PURE__ */ jsx16(Box14, { marginTop: 1, flexDirection: "column", children: body }),
1392
- /* @__PURE__ */ jsx16(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text14, { dimColor: true, children: footer }) })
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 init_RecordingScreen = __esm({
1396
- "src/tui/RecordingScreen.tsx"() {
1617
+ var BLOCKS;
1618
+ var init_RecordingHeroScreen = __esm({
1619
+ "src/tui/RecordingHeroScreen.tsx"() {
1397
1620
  "use strict";
1398
1621
  init_format();
1399
- init_liveCaptions();
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 useState6 } from "react";
1405
- import { Box as Box15, Text as Text15, useApp, useInput as useInput5 } from "ink";
1406
- import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
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 = isRecord6(error51) && isRecord6(error51.descriptor) ? error51.descriptor : void 0;
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: isRecord6(error51) ? error51.data : void 0
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 = isRecord6(data) ? data : void 0;
1457
- const sidecarData = isRecord6(sidecarError?.data) ? sidecarError.data : void 0;
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 = isRecord6(data) ? data : void 0;
1465
- const sidecarData = isRecord6(sidecarError?.data) ? sidecarError.data : void 0;
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 isRecord6(value) {
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] = useState6([]);
1494
- const [recordings, setRecordings] = useState6([]);
1495
- const [recordingsNextCursor, setRecordingsNextCursor] = useState6(null);
1496
- const [recordingsTotalCount, setRecordingsTotalCount] = useState6(void 0);
1497
- const [stats, setStats] = useState6(void 0);
1498
- const [accountStatus, setAccountStatus] = useState6("loading");
1499
- const [origin, setOrigin] = useState6("");
1500
- const [stack, setStack] = useState6([{ kind: initialView }]);
1501
- const [selected, setSelected] = useState6(0);
1502
- const [spinnerFrame, setSpinnerFrame] = useState6(0);
1503
- const [loadingMoreRecordings, setLoadingMoreRecordings] = useState6(false);
1504
- const [loadError, setLoadError] = useState6(void 0);
1505
- const [notice, setNotice] = useState6(void 0);
1506
- const [summaryCache, setSummaryCache] = useState6(() => /* @__PURE__ */ new Map());
1507
- const [transcriptCache, setTranscriptCache] = useState6(
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] = useState6(() => /* @__PURE__ */ new Map());
1511
- const [downloadedIds, setDownloadedIds] = useState6(() => /* @__PURE__ */ new Set());
1512
- const [liveRecord, setLiveRecord] = useState6(void 0);
1513
- const [recordRetryNonce, setRecordRetryNonce] = useState6(0);
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
- setLiveRecord({ kind: "starting" });
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
- setNotice(error51 instanceof Error ? error51.message : String(error51));
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 (screen.kind !== "record") return;
1540
- if (!startLiveRecord) {
1541
- setLiveRecord({
1542
- kind: "error",
1543
- code: "record.helper_unavailable",
1544
- message: "Live recording is not available"
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
- cancelled = true;
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
- useInput5((input, key) => {
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
- setRecordRetryNonce((value) => value + 1);
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 (input === "q" || key.escape || key.leftArrow) void stopLiveRecord();
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 === "4") return goTab("record");
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__ */ jsx17(TranscriptView, { loading: screen.loading, data: screen.data, error: screen.error });
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__ */ jsx17(Missing, { label: "Job" });
1810
- return /* @__PURE__ */ jsx17(Detail, { notice, children: /* @__PURE__ */ jsx17(JobDetailView, { item: job, origin, spinnerFrame, nowMs: now() }) });
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__ */ jsx17(Missing, { label: "Recording" });
2104
+ if (!rec) return /* @__PURE__ */ jsx18(Missing, { label: "Recording" });
1815
2105
  const detailTranscript = rec.activeTranscriptId ? transcriptCache.get(rec.activeTranscriptId) : void 0;
1816
- return /* @__PURE__ */ jsx17(Detail, { notice, children: /* @__PURE__ */ jsx17(
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__ */ jsx17(LiveCaptionsScreen, { source: liveRecord.session.source, now });
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__ */ jsxs14(Box15, { flexDirection: "column", height: size.rows, paddingX: 1, children: [
1831
- /* @__PURE__ */ jsx17(Header, { active: "record" }),
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__ */ jsx17(PermissionPreflightView, { items: permissionItemsFromRecordError(liveRecord.data) });
2136
+ return /* @__PURE__ */ jsx18(PermissionPreflightView, { items: permissionItemsFromRecordError(liveRecord.data) });
1835
2137
  }
1836
2138
  const copy = recordErrorCopy(liveRecord.code, liveRecord.message);
1837
- return /* @__PURE__ */ jsxs14(Fragment5, { children: [
1838
- /* @__PURE__ */ jsx17(Text15, { color: copy.tone, children: copy.title }),
1839
- copy.detail ? /* @__PURE__ */ jsx17(Text15, { dimColor: true, children: copy.detail }) : null
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__ */ jsx17(Text15, { dimColor: true, children: "Starting live recording\u2026" }) }),
1842
- /* @__PURE__ */ jsx17(Footer, { keys: "q / esc / \u2190 back" })
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__ */ jsx17(
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__ */ jsx17(AccountView, { status: accountStatus });
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__ */ jsx17(
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 1 overview \xB7 3 account \xB7 4 record \xB7 r refresh \xB7 q quit` : screen.kind === "account" ? "3 account \xB7 1 overview \xB7 2 jobs \xB7 4 record \xB7 r refresh \xB7 q quit" : `${position} \xB7 \u2191\u2193 scroll \xB7 \u23CE open \xB7 t transcript \xB7 2 jobs \xB7 3 account \xB7 4 record \xB7 r refresh \xB7 q quit`;
1894
- return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", height: size.rows, paddingX: 1, children: [
1895
- /* @__PURE__ */ jsx17(Header, { active: tab }),
1896
- /* @__PURE__ */ jsxs14(Box15, { flexGrow: 1, flexDirection: "column", children: [
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__ */ jsx17(Box15, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text15, { color: "red", children: [
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__ */ jsx17(Footer, { keys: footerKeys })
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__ */ jsxs14(Box15, { flexDirection: "column", children: [
2212
+ return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
1911
2213
  children,
1912
- notice ? /* @__PURE__ */ jsx17(Box15, { paddingX: 1, children: /* @__PURE__ */ jsx17(Text15, { color: "green", children: notice }) }) : null
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__ */ jsxs14(Box15, { flexDirection: "column", paddingX: 1, children: [
1917
- /* @__PURE__ */ jsxs14(Text15, { dimColor: true, children: [
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__ */ jsx17(Text15, { dimColor: true, children: "esc back \xB7 q quit" })
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
- init_RecordingScreen();
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 React8 from "react";
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
- React8.createElement(AppShell, {
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 = isRecord5(error51.data) ? error51.data : void 0;
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 isRecord5(value) {
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
- const session = await startRecordSession({ ...opts, live: false });
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: async () => {
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
- account: {
20151
- backendOrigin: auth.origin,
20152
- userId: liveStatus.userId,
20153
- ...liveStatus.email ? { email: liveStatus.email } : {}
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
- cliVersion: CLI_VERSION,
20156
- env: deps.env,
20157
- homeDir: deps.homeDir,
20158
- runtime: deps.recordRuntime
20159
- });
20473
+ selection
20474
+ );
20160
20475
  },
20161
20476
  initialView: parsed.initialView
20162
20477
  });