recappi 0.1.64 → 0.1.65

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
@@ -139,7 +139,7 @@ function formatClockMs(ms) {
139
139
  const ss = String(secs).padStart(2, "0");
140
140
  return hours > 0 ? `${hours}:${mm}:${ss}` : `${mm}:${ss}`;
141
141
  }
142
- function progressBar(fraction, width = 10) {
142
+ function progressBar2(fraction, width = 10) {
143
143
  const clamped = Math.max(0, Math.min(1, fraction));
144
144
  const filled = Math.round(clamped * width);
145
145
  return `[${"\u2588".repeat(filled)}${"\u2591".repeat(width - filled)}]`;
@@ -200,7 +200,7 @@ function jobDetail(item) {
200
200
  const fraction = transcribeFraction(item);
201
201
  if (fraction != null) {
202
202
  const pct2 = Math.round(fraction * 100);
203
- return `${progressBar(fraction)} ${String(pct2).padStart(3)}% ${formatClockMs(
203
+ return `${progressBar2(fraction)} ${String(pct2).padStart(3)}% ${formatClockMs(
204
204
  item.processedDurationMs
205
205
  )} / ${formatClockMs(item.recording?.durationMs)}`;
206
206
  }
@@ -335,7 +335,7 @@ function groupedListWindow(buckets, selected, budget) {
335
335
  }
336
336
  return listWindow(selected, total, 1);
337
337
  }
338
- function formatBytes2(bytes) {
338
+ function formatBytes3(bytes) {
339
339
  if (bytes == null || !Number.isFinite(bytes) || bytes < 0) return "";
340
340
  const units = ["B", "KB", "MB", "GB", "TB"];
341
341
  let value = bytes;
@@ -647,16 +647,16 @@ function Usage({
647
647
  ] }),
648
648
  /* @__PURE__ */ jsxs3(Text3, { children: [
649
649
  /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "Minutes " }),
650
- minutesCap != null ? /* @__PURE__ */ jsx5(Text3, { color: billing.isOverMinutes ? "red" : "cyan", children: `${progressBar(minutesUsed / Math.max(1, minutesCap), 12)} ` }) : null,
650
+ minutesCap != null ? /* @__PURE__ */ jsx5(Text3, { color: billing.isOverMinutes ? "red" : "cyan", children: `${progressBar2(minutesUsed / Math.max(1, minutesCap), 12)} ` }) : null,
651
651
  /* @__PURE__ */ jsx5(Text3, { color: billing.isOverMinutes ? "red" : void 0, children: `${Math.round(minutesUsed)}` }),
652
652
  /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: ` / ${minutesCap != null ? Math.round(minutesCap) : "\u221E"} min` }),
653
653
  /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: ` (batch ${Math.round(billing.batchMinutesUsed)} \xB7 live ${Math.round(billing.realtimeMinutesUsed)})` })
654
654
  ] }),
655
655
  /* @__PURE__ */ jsxs3(Text3, { children: [
656
656
  /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: "Storage " }),
657
- storageCap != null ? /* @__PURE__ */ jsx5(Text3, { color: billing.isOverStorage ? "red" : "cyan", children: `${progressBar(billing.storageBytes / Math.max(1, storageCap), 12)} ` }) : null,
658
- /* @__PURE__ */ jsx5(Text3, { color: billing.isOverStorage ? "red" : void 0, children: formatBytes2(billing.storageBytes) }),
659
- /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: ` / ${storageCap != null ? formatBytes2(storageCap) : "\u221E"}` })
657
+ storageCap != null ? /* @__PURE__ */ jsx5(Text3, { color: billing.isOverStorage ? "red" : "cyan", children: `${progressBar2(billing.storageBytes / Math.max(1, storageCap), 12)} ` }) : null,
658
+ /* @__PURE__ */ jsx5(Text3, { color: billing.isOverStorage ? "red" : void 0, children: formatBytes3(billing.storageBytes) }),
659
+ /* @__PURE__ */ jsx5(Text3, { dimColor: true, children: ` / ${storageCap != null ? formatBytes3(storageCap) : "\u221E"}` })
660
660
  ] }),
661
661
  billing.isOverMinutes || billing.isOverStorage ? /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
662
662
  billing.isOverMinutes ? "Over minutes limit. " : "",
@@ -907,7 +907,7 @@ function PeekBody({
907
907
  const style = recordingStatusStyle(item.status);
908
908
  const meta3 = [
909
909
  item.durationMs ? formatClockMs(item.durationMs) : null,
910
- formatBytes2(item.sizeBytes) || null,
910
+ formatBytes3(item.sizeBytes) || null,
911
911
  formatAge(item.createdAt, nowMs)
912
912
  ].filter(Boolean).join(" \xB7 ");
913
913
  return /* @__PURE__ */ jsxs8(Fragment3, { children: [
@@ -1108,7 +1108,7 @@ function StatusLine({
1108
1108
  if (fraction != null) {
1109
1109
  const pct2 = Math.round(fraction * 100);
1110
1110
  return /* @__PURE__ */ jsxs10(Text11, { children: [
1111
- `${progressBar(fraction)} ${pct2}% ${formatClockMs(item.processedDurationMs)} / ${formatClockMs(
1111
+ `${progressBar2(fraction)} ${pct2}% ${formatClockMs(item.processedDurationMs)} / ${formatClockMs(
1112
1112
  item.recording?.durationMs
1113
1113
  )}`,
1114
1114
  /* @__PURE__ */ jsx13(Text11, { dimColor: true, children: elapsed })
@@ -1169,7 +1169,7 @@ function RecordingDetailView({
1169
1169
  const title = recordingTitle2(item);
1170
1170
  const meta3 = [
1171
1171
  item.durationMs ? formatClockMs(item.durationMs) : void 0,
1172
- formatBytes2(item.sizeBytes) || void 0,
1172
+ formatBytes3(item.sizeBytes) || void 0,
1173
1173
  item.contentType || void 0
1174
1174
  ].filter(Boolean).join(" \xB7 ");
1175
1175
  const ready = typeof transcript === "object";
@@ -1729,7 +1729,7 @@ function RecordFrame({
1729
1729
  ...captions.lines.filter((l) => l.translation).map((l) => trimLead2(l.translation)),
1730
1730
  ...captions.translationPartial ? [trimLead2(captions.translationPartial)] : []
1731
1731
  ] : [];
1732
- const status = telemetry.sizeBytes ? formatBytes2(telemetry.sizeBytes) : "";
1732
+ const status = telemetry.sizeBytes ? formatBytes3(telemetry.sizeBytes) : "";
1733
1733
  const sourceLine = [telemetry.sourceLabel, telemetry.micEnabled ? "Microphone" : null, status || null].filter(Boolean).join(" \xB7 ");
1734
1734
  return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", paddingX: 1, height: size.rows, children: [
1735
1735
  /* @__PURE__ */ jsxs15(Box16, { justifyContent: "space-between", children: [
@@ -20235,13 +20235,240 @@ ${lines.join("\n")}
20235
20235
  ` : "";
20236
20236
  }
20237
20237
 
20238
+ // src/progressStepper.ts
20239
+ var STEP_DEFS = [
20240
+ { key: "check", label: "Check" },
20241
+ { key: "upload", label: "Upload" },
20242
+ { key: "transcribe", label: "Transcribe" },
20243
+ { key: "done", label: "Done" }
20244
+ ];
20245
+ var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
20246
+ var BAR_WIDTH = 16;
20247
+ var LABEL_WIDTH = 11;
20248
+ function createStepperModel() {
20249
+ return {
20250
+ steps: STEP_DEFS.map((d) => ({ ...d, status: "pending", detail: "" })),
20251
+ finished: false
20252
+ };
20253
+ }
20254
+ function isStepperEvent(event) {
20255
+ if (event.command !== "upload") return false;
20256
+ if (event.type === "started") return true;
20257
+ return typeof event.status === "string";
20258
+ }
20259
+ function cloneModel(model) {
20260
+ return {
20261
+ steps: model.steps.map((s) => ({ ...s })),
20262
+ ...model.transcribeStartMs != null ? { transcribeStartMs: model.transcribeStartMs } : {},
20263
+ finished: model.finished
20264
+ };
20265
+ }
20266
+ function applyStepperEvent(model, event, nowMs) {
20267
+ const next = cloneModel(model);
20268
+ const step = (key) => next.steps.find((s) => s.key === key);
20269
+ const markDone = (key) => {
20270
+ const s = step(key);
20271
+ if (s.status !== "failed") {
20272
+ s.status = "done";
20273
+ s.percent = void 0;
20274
+ }
20275
+ };
20276
+ const startTranscribeClock = () => {
20277
+ if (next.transcribeStartMs == null) next.transcribeStartMs = nowMs;
20278
+ };
20279
+ switch (event.status) {
20280
+ case "checking_audio":
20281
+ step("check").status = "active";
20282
+ break;
20283
+ case "starting_upload": {
20284
+ markDone("check");
20285
+ const file2 = fileFromEvent(event);
20286
+ if (file2) step("check").detail = file2;
20287
+ step("upload").status = "active";
20288
+ step("upload").detail = "Starting\u2026";
20289
+ break;
20290
+ }
20291
+ case "uploading":
20292
+ step("upload").status = "active";
20293
+ step("upload").detail = "";
20294
+ if (typeof event.percent === "number") step("upload").percent = event.percent;
20295
+ break;
20296
+ case "finishing_upload":
20297
+ step("upload").status = "active";
20298
+ step("upload").detail = "Finalizing";
20299
+ step("upload").percent = 100;
20300
+ break;
20301
+ case "uploaded":
20302
+ markDone("upload");
20303
+ step("upload").detail = recordingUrl(event) ?? "Uploaded";
20304
+ break;
20305
+ case "starting_transcription":
20306
+ step("transcribe").status = "active";
20307
+ step("transcribe").detail = "Starting\u2026";
20308
+ startTranscribeClock();
20309
+ break;
20310
+ case "queued":
20311
+ step("transcribe").status = "active";
20312
+ step("transcribe").detail = "Queued";
20313
+ startTranscribeClock();
20314
+ break;
20315
+ case "running":
20316
+ step("transcribe").status = "active";
20317
+ startTranscribeClock();
20318
+ if (typeof event.percent === "number") {
20319
+ step("transcribe").percent = event.percent;
20320
+ step("transcribe").detail = "";
20321
+ } else {
20322
+ step("transcribe").percent = void 0;
20323
+ step("transcribe").detail = "Transcribing\u2026";
20324
+ }
20325
+ break;
20326
+ case "succeeded":
20327
+ markDone("transcribe");
20328
+ markDone("done");
20329
+ if (event.transcriptId) {
20330
+ step("done").detail = `\u2192 recappi transcript get ${event.transcriptId}`;
20331
+ }
20332
+ next.finished = true;
20333
+ break;
20334
+ case "failed":
20335
+ markActiveFailed(next, event.message);
20336
+ break;
20337
+ default:
20338
+ if (event.type === "started") step("check").status = "active";
20339
+ break;
20340
+ }
20341
+ return next;
20342
+ }
20343
+ function markStepperFailed(model, message) {
20344
+ const next = cloneModel(model);
20345
+ markActiveFailed(next, message);
20346
+ return next;
20347
+ }
20348
+ function completeStepperModel(model, next) {
20349
+ const m = cloneModel(model);
20350
+ const transcribe = m.steps.find((s) => s.key === "transcribe");
20351
+ if (transcribe.status === "pending") {
20352
+ transcribe.status = "skipped";
20353
+ transcribe.detail = "no --transcribe";
20354
+ }
20355
+ for (const s of m.steps) {
20356
+ if (s.status === "active" || s.status === "pending") s.status = "done";
20357
+ s.percent = void 0;
20358
+ }
20359
+ const done = m.steps.find((s) => s.key === "done");
20360
+ if (next?.transcriptId) done.detail = `\u2192 recappi transcript get ${next.transcriptId}`;
20361
+ else if (next?.recordingId) done.detail = `\u2192 recappi recordings get ${next.recordingId}`;
20362
+ m.finished = true;
20363
+ return m;
20364
+ }
20365
+ function markActiveFailed(model, message) {
20366
+ const active = model.steps.find((s) => s.status === "active");
20367
+ const target = active ?? model.steps.find((s) => s.status === "pending");
20368
+ if (target) {
20369
+ target.status = "failed";
20370
+ if (message) target.detail = message;
20371
+ }
20372
+ model.finished = true;
20373
+ }
20374
+ function formatStepperLines(model, nowMs, spinnerFrame, color) {
20375
+ return model.steps.map((s) => {
20376
+ const marker = markerFor(s, spinnerFrame);
20377
+ const label = s.label.padEnd(LABEL_WIDTH);
20378
+ let detail = s.detail;
20379
+ if (s.status === "active" && typeof s.percent === "number") {
20380
+ detail = `${progressBar(s.percent)} ${clampPercent2(s.percent)}%`;
20381
+ }
20382
+ if (s.key === "transcribe" && s.status === "active" && model.transcribeStartMs != null) {
20383
+ const elapsed = formatElapsed(Math.max(0, nowMs - model.transcribeStartMs));
20384
+ detail = detail ? `${detail} \xB7 ${elapsed}` : elapsed;
20385
+ }
20386
+ const body = ` ${marker} ${label}${detail}`.trimEnd();
20387
+ return color ? colorize(s.status, marker, body) : body;
20388
+ });
20389
+ }
20390
+ function markerFor(step, spinnerFrame) {
20391
+ switch (step.status) {
20392
+ case "done":
20393
+ return "\u2713";
20394
+ case "failed":
20395
+ return "\u2717";
20396
+ case "active":
20397
+ return SPINNER[spinnerFrame % SPINNER.length];
20398
+ case "skipped":
20399
+ return "\xB7";
20400
+ default:
20401
+ return "\u25CB";
20402
+ }
20403
+ }
20404
+ function colorize(status, marker, line) {
20405
+ const code = status === "done" ? "32" : status === "failed" ? "31" : status === "active" ? "36" : "90";
20406
+ const colored = `\x1B[${code}m${marker}\x1B[0m`;
20407
+ return line.replace(marker, colored);
20408
+ }
20409
+ function progressBar(percent) {
20410
+ const p = clampPercent2(percent);
20411
+ const filled = Math.round(p / 100 * BAR_WIDTH);
20412
+ return "\u2588".repeat(filled) + "\u2591".repeat(BAR_WIDTH - filled);
20413
+ }
20414
+ function clampPercent2(percent) {
20415
+ return Math.max(0, Math.min(100, Math.round(percent)));
20416
+ }
20417
+ function fileFromEvent(event) {
20418
+ const data = event.data;
20419
+ if (!data || typeof data !== "object") return void 0;
20420
+ const file2 = data.file;
20421
+ if (!file2 || typeof file2 !== "object") return void 0;
20422
+ const f = file2;
20423
+ const parts = [];
20424
+ if (typeof f.title === "string" && f.title) parts.push(f.title);
20425
+ if (typeof f.sizeBytes === "number") parts.push(formatBytes(f.sizeBytes));
20426
+ if (typeof f.durationMs === "number") parts.push(formatMediaDuration(f.durationMs));
20427
+ return parts.length ? parts.join(" \xB7 ") : void 0;
20428
+ }
20429
+ function recordingUrl(event) {
20430
+ if (typeof event.message === "string" && event.message.includes("/recordings/")) {
20431
+ const idx = event.message.indexOf("http");
20432
+ if (idx >= 0) return event.message.slice(idx);
20433
+ }
20434
+ if (event.origin && event.recordingId) {
20435
+ return `${event.origin.replace(/\/+$/, "")}/recordings/${event.recordingId}`;
20436
+ }
20437
+ return void 0;
20438
+ }
20439
+ function formatBytes(bytes) {
20440
+ if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
20441
+ if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
20442
+ if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
20443
+ return `${bytes} B`;
20444
+ }
20445
+ function formatMediaDuration(ms) {
20446
+ const totalSec = Math.round(ms / 1e3);
20447
+ const h = Math.floor(totalSec / 3600);
20448
+ const m = Math.floor(totalSec % 3600 / 60);
20449
+ const s = totalSec % 60;
20450
+ if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
20451
+ return `${m}:${String(s).padStart(2, "0")}`;
20452
+ }
20453
+ function formatElapsed(ms) {
20454
+ const totalSec = Math.floor(ms / 1e3);
20455
+ const h = Math.floor(totalSec / 3600);
20456
+ const m = Math.floor(totalSec % 3600 / 60);
20457
+ const s = totalSec % 60;
20458
+ const clock = h > 0 ? `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}` : `${m}:${String(s).padStart(2, "0")}`;
20459
+ return `${clock} elapsed`;
20460
+ }
20461
+
20238
20462
  // src/render.ts
20239
20463
  function createHumanProgressState(interactive) {
20240
20464
  return {
20241
20465
  interactive,
20242
20466
  activeLineLength: 0,
20243
20467
  lastLineByScope: /* @__PURE__ */ new Map(),
20244
- lastUploadBucketByScope: /* @__PURE__ */ new Map()
20468
+ lastUploadBucketByScope: /* @__PURE__ */ new Map(),
20469
+ stepperLinesDrawn: 0,
20470
+ spinnerFrame: 0,
20471
+ stepperShown: false
20245
20472
  };
20246
20473
  }
20247
20474
  function renderSuccess(command, data, opts) {
@@ -20263,6 +20490,7 @@ function renderSuccess(command, data, opts) {
20263
20490
  renderEnvelope(envelope, opts);
20264
20491
  return;
20265
20492
  }
20493
+ finalizeStepperSuccess(command, filtered, opts);
20266
20494
  finishHumanProgress(opts);
20267
20495
  renderHumanSuccess(command, filtered, opts);
20268
20496
  }
@@ -20291,6 +20519,7 @@ function renderFailure(command, error51, opts, data) {
20291
20519
  renderEnvelope(envelope, opts);
20292
20520
  return;
20293
20521
  }
20522
+ if (finalizeStepperFailure(command, error51, opts, data)) return;
20294
20523
  finishHumanProgress(opts);
20295
20524
  opts.stderr(`recappi: ${error51.message}
20296
20525
  `);
@@ -20316,6 +20545,10 @@ function renderEvent(event, opts) {
20316
20545
  return;
20317
20546
  }
20318
20547
  if ((event.type === "started" || event.type === "progress") && opts.mode === "human") {
20548
+ if (opts.progress?.interactive && isStepperEvent(event)) {
20549
+ renderStepperEvent(event, opts);
20550
+ return;
20551
+ }
20319
20552
  const line = formatHumanProgress(event, opts);
20320
20553
  if (line) {
20321
20554
  if (isPersistentProgressEvent(event)) writePersistentHumanProgress(line, opts);
@@ -20323,6 +20556,56 @@ function renderEvent(event, opts) {
20323
20556
  }
20324
20557
  }
20325
20558
  }
20559
+ function renderStepperEvent(event, opts) {
20560
+ const state = opts.progress;
20561
+ if (!state) return;
20562
+ if (!state.stepper) state.stepper = createStepperModel();
20563
+ state.stepper = applyStepperEvent(state.stepper, event, Date.now());
20564
+ state.spinnerFrame += 1;
20565
+ drawStepperBlock(state, opts);
20566
+ }
20567
+ function drawStepperBlock(state, opts) {
20568
+ if (!state.stepper) return;
20569
+ const lines = formatStepperLines(state.stepper, Date.now(), state.spinnerFrame, true);
20570
+ let out = "";
20571
+ if (state.stepperLinesDrawn > 0) out += `\x1B[${state.stepperLinesDrawn}A`;
20572
+ for (const line of lines) out += `\x1B[2K${line}
20573
+ `;
20574
+ opts.stderr(out);
20575
+ state.stepperLinesDrawn = lines.length;
20576
+ state.stepperShown = true;
20577
+ }
20578
+ function finalizeStepperSuccess(command, data, opts) {
20579
+ const state = opts.progress;
20580
+ if (!state?.stepper || state.stepperLinesDrawn === 0 || command !== "upload") return;
20581
+ const success2 = firstUploadSuccess(data);
20582
+ state.stepper = completeStepperModel(state.stepper, success2);
20583
+ drawStepperBlock(state, opts);
20584
+ }
20585
+ function firstUploadSuccess(data) {
20586
+ if (!isUploadBatch(data) || data.successes.length === 0) return void 0;
20587
+ const s = data.successes[0];
20588
+ return {
20589
+ ...s.transcriptId ? { transcriptId: s.transcriptId } : {},
20590
+ ...s.recordingId ? { recordingId: s.recordingId } : {}
20591
+ };
20592
+ }
20593
+ function finalizeStepperFailure(command, error51, opts, data) {
20594
+ const state = opts.progress;
20595
+ if (!state?.stepper || state.stepperLinesDrawn === 0) return false;
20596
+ state.stepper = markStepperFailed(state.stepper, stepperFailureDetail(command, error51, data));
20597
+ drawStepperBlock(state, opts);
20598
+ state.stepperLinesDrawn = 0;
20599
+ state.stepper = void 0;
20600
+ return true;
20601
+ }
20602
+ function stepperFailureDetail(command, error51, data) {
20603
+ if (command === "upload" && isUploadBatch(data) && data.failures.length > 0) {
20604
+ const first = data.failures[0].error;
20605
+ return `${first.message} (${first.code})`;
20606
+ }
20607
+ return `${error51.message} (${error51.code})`;
20608
+ }
20326
20609
  function renderEnvelope(envelope, opts) {
20327
20610
  const parsed = cliEnvelopeSchema.parse(envelope);
20328
20611
  opts.stdout(`${stableStringify(parsed, opts.compact === true)}
@@ -20372,7 +20655,7 @@ function renderHumanSuccess(command, data, opts) {
20372
20655
  }
20373
20656
  if (typeof billing.storageBytes === "number") {
20374
20657
  const cap = formatNullableCap(billing.storageCapBytes, "bytes");
20375
- opts.stdout(` storage: ${formatBytes(billing.storageBytes)} / ${cap}
20658
+ opts.stdout(` storage: ${formatBytes2(billing.storageBytes)} / ${cap}
20376
20659
  `);
20377
20660
  }
20378
20661
  const localStore = isRecord4(data.localStore) ? data.localStore : {};
@@ -20434,7 +20717,7 @@ Next cursor: ${data.nextCursor}
20434
20717
  if (typeof data.durationMs === "number")
20435
20718
  opts.stdout(` duration: ${formatDurationMs(data.durationMs)}
20436
20719
  `);
20437
- if (typeof data.sizeBytes === "number") opts.stdout(` size: ${formatBytes(data.sizeBytes)}
20720
+ if (typeof data.sizeBytes === "number") opts.stdout(` size: ${formatBytes2(data.sizeBytes)}
20438
20721
  `);
20439
20722
  if (typeof data.activeTranscriptId === "string") {
20440
20723
  opts.stdout(` activeTranscriptId: ${data.activeTranscriptId}
@@ -20483,6 +20766,7 @@ Next:
20483
20766
  return;
20484
20767
  }
20485
20768
  if (command === "upload" && isUploadBatch(data)) {
20769
+ if (opts.progress?.stepperShown && data.failures.length === 0) return;
20486
20770
  if (data.successes.length > 0) {
20487
20771
  opts.stdout(uploadSuccessHeading(data.successes));
20488
20772
  }
@@ -20708,7 +20992,13 @@ function writePersistentHumanProgress(line, opts) {
20708
20992
  }
20709
20993
  function finishHumanProgress(opts) {
20710
20994
  const state = opts.progress;
20711
- if (!state?.interactive || state.activeLineLength === 0) return;
20995
+ if (!state) return;
20996
+ if (state.stepperLinesDrawn > 0) {
20997
+ state.stepperLinesDrawn = 0;
20998
+ state.stepper = void 0;
20999
+ return;
21000
+ }
21001
+ if (!state.interactive || state.activeLineLength === 0) return;
20712
21002
  opts.stderr("\n");
20713
21003
  state.activeLineLength = 0;
20714
21004
  }
@@ -20801,7 +21091,7 @@ function numberText(value) {
20801
21091
  function formatDurationMs(ms) {
20802
21092
  return formatClock(ms / 1e3);
20803
21093
  }
20804
- function formatBytes(bytes) {
21094
+ function formatBytes2(bytes) {
20805
21095
  if (bytes < 1024) return `${bytes} B`;
20806
21096
  const units = ["KB", "MB", "GB", "TB"];
20807
21097
  let value = bytes / 1024;
@@ -20816,7 +21106,7 @@ function formatBytes(bytes) {
20816
21106
  function formatNullableCap(value, unit) {
20817
21107
  if (value === null || value === void 0) return "Unlimited";
20818
21108
  if (typeof value !== "number" || !Number.isFinite(value)) return "Unlimited";
20819
- return unit === "bytes" ? formatBytes(value) : String(value);
21109
+ return unit === "bytes" ? formatBytes2(value) : String(value);
20820
21110
  }
20821
21111
  function formatClock(seconds) {
20822
21112
  const total = Math.max(0, Math.floor(seconds));
@@ -21475,7 +21765,7 @@ function RecordingHeroScreen({
21475
21765
  const phase = stoppedPhase(artifact);
21476
21766
  const meta3 = [
21477
21767
  telemetry.durationMs != null ? formatClockMs(telemetry.durationMs) : null,
21478
- formatBytes2(telemetry.sizeBytes) || null
21768
+ formatBytes3(telemetry.sizeBytes) || null
21479
21769
  ].filter(Boolean).join(" \xB7 ");
21480
21770
  const saved = artifact?.uploadStatus === "uploaded" ? "\u2713 Saved to Recappi Cloud" : "\u2713 Saved to your Mac";
21481
21771
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
@@ -21510,7 +21800,7 @@ function RecordingHeroScreen({
21510
21800
  const starting = telemetry.status === "starting" || telemetry.status === "stopping";
21511
21801
  const badge = paused ? "\u23F8 PAUSED" : starting ? "\u2026" : "\u23FA REC";
21512
21802
  const meterW = Math.max(10, Math.min(72, innerWidth - 20));
21513
- const sizeStr = telemetry.sizeBytes ? formatBytes2(telemetry.sizeBytes) : "";
21803
+ const sizeStr = telemetry.sizeBytes ? formatBytes3(telemetry.sizeBytes) : "";
21514
21804
  const context = [telemetry.sourceLabel, telemetry.micEnabled ? "Microphone" : null, sizeStr || null].filter(Boolean).join(" \xB7 ");
21515
21805
  const waveRows = waveRowsFor(size.rows);
21516
21806
  const meterBlockRows = (telemetry.micEnabled ? 2 : 1) * (waveRows + 1) + (telemetry.micEnabled ? 1 : 0);