recappi 0.1.63 → 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 +336 -24
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
|
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 `${
|
|
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
|
|
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: `${
|
|
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: `${
|
|
658
|
-
/* @__PURE__ */ jsx5(Text3, { color: billing.isOverStorage ? "red" : void 0, children:
|
|
659
|
-
/* @__PURE__ */ jsx5(Text3, { dimColor: true, children: ` / ${storageCap != null ?
|
|
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
|
-
|
|
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
|
-
`${
|
|
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
|
-
|
|
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 ?
|
|
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: [
|
|
@@ -19296,6 +19296,13 @@ var RecappiApiClient = class {
|
|
|
19296
19296
|
for (const filePath of files) {
|
|
19297
19297
|
attemptedCount += 1;
|
|
19298
19298
|
try {
|
|
19299
|
+
opts.onEvent?.({
|
|
19300
|
+
type: "progress",
|
|
19301
|
+
command: "upload",
|
|
19302
|
+
filePath,
|
|
19303
|
+
status: "checking_audio",
|
|
19304
|
+
message: `Checking ${filePath}`
|
|
19305
|
+
});
|
|
19299
19306
|
const plan = await planAudioFile(filePath, files.length === 1 ? opts.title : void 0);
|
|
19300
19307
|
const result = await this.uploadFile(plan, opts);
|
|
19301
19308
|
successes.push(result);
|
|
@@ -19338,10 +19345,19 @@ var RecappiApiClient = class {
|
|
|
19338
19345
|
async uploadFile(plan, opts) {
|
|
19339
19346
|
const relative = plan.filePath;
|
|
19340
19347
|
opts.onEvent?.({
|
|
19341
|
-
type: "
|
|
19348
|
+
type: "progress",
|
|
19342
19349
|
command: "upload",
|
|
19343
19350
|
filePath: relative,
|
|
19344
|
-
|
|
19351
|
+
status: "starting_upload",
|
|
19352
|
+
message: `Starting upload ${relative}`,
|
|
19353
|
+
data: {
|
|
19354
|
+
file: {
|
|
19355
|
+
title: plan.title,
|
|
19356
|
+
contentType: plan.contentType,
|
|
19357
|
+
sizeBytes: plan.sizeBytes,
|
|
19358
|
+
...plan.durationMs ? { durationMs: plan.durationMs } : {}
|
|
19359
|
+
}
|
|
19360
|
+
}
|
|
19345
19361
|
});
|
|
19346
19362
|
const init = await this.postJson("/api/recordings", {
|
|
19347
19363
|
title: plan.title,
|
|
@@ -20219,13 +20235,240 @@ ${lines.join("\n")}
|
|
|
20219
20235
|
` : "";
|
|
20220
20236
|
}
|
|
20221
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
|
+
|
|
20222
20462
|
// src/render.ts
|
|
20223
20463
|
function createHumanProgressState(interactive) {
|
|
20224
20464
|
return {
|
|
20225
20465
|
interactive,
|
|
20226
20466
|
activeLineLength: 0,
|
|
20227
20467
|
lastLineByScope: /* @__PURE__ */ new Map(),
|
|
20228
|
-
lastUploadBucketByScope: /* @__PURE__ */ new Map()
|
|
20468
|
+
lastUploadBucketByScope: /* @__PURE__ */ new Map(),
|
|
20469
|
+
stepperLinesDrawn: 0,
|
|
20470
|
+
spinnerFrame: 0,
|
|
20471
|
+
stepperShown: false
|
|
20229
20472
|
};
|
|
20230
20473
|
}
|
|
20231
20474
|
function renderSuccess(command, data, opts) {
|
|
@@ -20247,6 +20490,7 @@ function renderSuccess(command, data, opts) {
|
|
|
20247
20490
|
renderEnvelope(envelope, opts);
|
|
20248
20491
|
return;
|
|
20249
20492
|
}
|
|
20493
|
+
finalizeStepperSuccess(command, filtered, opts);
|
|
20250
20494
|
finishHumanProgress(opts);
|
|
20251
20495
|
renderHumanSuccess(command, filtered, opts);
|
|
20252
20496
|
}
|
|
@@ -20275,6 +20519,7 @@ function renderFailure(command, error51, opts, data) {
|
|
|
20275
20519
|
renderEnvelope(envelope, opts);
|
|
20276
20520
|
return;
|
|
20277
20521
|
}
|
|
20522
|
+
if (finalizeStepperFailure(command, error51, opts, data)) return;
|
|
20278
20523
|
finishHumanProgress(opts);
|
|
20279
20524
|
opts.stderr(`recappi: ${error51.message}
|
|
20280
20525
|
`);
|
|
@@ -20300,6 +20545,10 @@ function renderEvent(event, opts) {
|
|
|
20300
20545
|
return;
|
|
20301
20546
|
}
|
|
20302
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
|
+
}
|
|
20303
20552
|
const line = formatHumanProgress(event, opts);
|
|
20304
20553
|
if (line) {
|
|
20305
20554
|
if (isPersistentProgressEvent(event)) writePersistentHumanProgress(line, opts);
|
|
@@ -20307,6 +20556,56 @@ function renderEvent(event, opts) {
|
|
|
20307
20556
|
}
|
|
20308
20557
|
}
|
|
20309
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
|
+
}
|
|
20310
20609
|
function renderEnvelope(envelope, opts) {
|
|
20311
20610
|
const parsed = cliEnvelopeSchema.parse(envelope);
|
|
20312
20611
|
opts.stdout(`${stableStringify(parsed, opts.compact === true)}
|
|
@@ -20356,7 +20655,7 @@ function renderHumanSuccess(command, data, opts) {
|
|
|
20356
20655
|
}
|
|
20357
20656
|
if (typeof billing.storageBytes === "number") {
|
|
20358
20657
|
const cap = formatNullableCap(billing.storageCapBytes, "bytes");
|
|
20359
|
-
opts.stdout(` storage: ${
|
|
20658
|
+
opts.stdout(` storage: ${formatBytes2(billing.storageBytes)} / ${cap}
|
|
20360
20659
|
`);
|
|
20361
20660
|
}
|
|
20362
20661
|
const localStore = isRecord4(data.localStore) ? data.localStore : {};
|
|
@@ -20418,7 +20717,7 @@ Next cursor: ${data.nextCursor}
|
|
|
20418
20717
|
if (typeof data.durationMs === "number")
|
|
20419
20718
|
opts.stdout(` duration: ${formatDurationMs(data.durationMs)}
|
|
20420
20719
|
`);
|
|
20421
|
-
if (typeof data.sizeBytes === "number") opts.stdout(` size: ${
|
|
20720
|
+
if (typeof data.sizeBytes === "number") opts.stdout(` size: ${formatBytes2(data.sizeBytes)}
|
|
20422
20721
|
`);
|
|
20423
20722
|
if (typeof data.activeTranscriptId === "string") {
|
|
20424
20723
|
opts.stdout(` activeTranscriptId: ${data.activeTranscriptId}
|
|
@@ -20467,6 +20766,7 @@ Next:
|
|
|
20467
20766
|
return;
|
|
20468
20767
|
}
|
|
20469
20768
|
if (command === "upload" && isUploadBatch(data)) {
|
|
20769
|
+
if (opts.progress?.stepperShown && data.failures.length === 0) return;
|
|
20470
20770
|
if (data.successes.length > 0) {
|
|
20471
20771
|
opts.stdout(uploadSuccessHeading(data.successes));
|
|
20472
20772
|
}
|
|
@@ -20622,8 +20922,7 @@ function formatHumanProgress(event, opts) {
|
|
|
20622
20922
|
const scope = progressScope(event);
|
|
20623
20923
|
let line;
|
|
20624
20924
|
if (event.type === "started") {
|
|
20625
|
-
|
|
20626
|
-
line = label ? `Preparing ${label}` : "Preparing upload";
|
|
20925
|
+
line = event.message;
|
|
20627
20926
|
} else if (event.command === "upload") {
|
|
20628
20927
|
line = formatUploadProgress(event, opts, scope);
|
|
20629
20928
|
} else if (event.command === "jobs wait") {
|
|
@@ -20639,13 +20938,20 @@ function formatHumanProgress(event, opts) {
|
|
|
20639
20938
|
return line;
|
|
20640
20939
|
}
|
|
20641
20940
|
function formatUploadProgress(event, opts, scope) {
|
|
20941
|
+
const label = event.filePath ? humanFileLabel(event.filePath) : void 0;
|
|
20942
|
+
if (event.status === "checking_audio") {
|
|
20943
|
+
return label ? `Checking ${label}` : "Checking audio file";
|
|
20944
|
+
}
|
|
20945
|
+
if (event.status === "starting_upload") {
|
|
20946
|
+
return label ? `Starting upload ${label}` : "Starting upload";
|
|
20947
|
+
}
|
|
20642
20948
|
if (event.status === "uploading" && typeof event.percent === "number") {
|
|
20643
20949
|
const state = opts.progress;
|
|
20644
20950
|
const bucket = Math.min(100, Math.max(0, Math.floor(event.percent / 10) * 10));
|
|
20645
20951
|
const previous = state?.lastUploadBucketByScope.get(scope);
|
|
20646
20952
|
if (previous !== void 0 && bucket <= previous && bucket !== 100) return void 0;
|
|
20647
20953
|
state?.lastUploadBucketByScope.set(scope, bucket);
|
|
20648
|
-
return `Uploading${
|
|
20954
|
+
return `Uploading${label ? ` ${label}` : ""}: ${event.percent}%`;
|
|
20649
20955
|
}
|
|
20650
20956
|
if (event.status === "finishing_upload") return "Finalizing upload";
|
|
20651
20957
|
if (event.status === "uploaded") {
|
|
@@ -20686,7 +20992,13 @@ function writePersistentHumanProgress(line, opts) {
|
|
|
20686
20992
|
}
|
|
20687
20993
|
function finishHumanProgress(opts) {
|
|
20688
20994
|
const state = opts.progress;
|
|
20689
|
-
if (!state
|
|
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;
|
|
20690
21002
|
opts.stderr("\n");
|
|
20691
21003
|
state.activeLineLength = 0;
|
|
20692
21004
|
}
|
|
@@ -20779,7 +21091,7 @@ function numberText(value) {
|
|
|
20779
21091
|
function formatDurationMs(ms) {
|
|
20780
21092
|
return formatClock(ms / 1e3);
|
|
20781
21093
|
}
|
|
20782
|
-
function
|
|
21094
|
+
function formatBytes2(bytes) {
|
|
20783
21095
|
if (bytes < 1024) return `${bytes} B`;
|
|
20784
21096
|
const units = ["KB", "MB", "GB", "TB"];
|
|
20785
21097
|
let value = bytes / 1024;
|
|
@@ -20794,7 +21106,7 @@ function formatBytes(bytes) {
|
|
|
20794
21106
|
function formatNullableCap(value, unit) {
|
|
20795
21107
|
if (value === null || value === void 0) return "Unlimited";
|
|
20796
21108
|
if (typeof value !== "number" || !Number.isFinite(value)) return "Unlimited";
|
|
20797
|
-
return unit === "bytes" ?
|
|
21109
|
+
return unit === "bytes" ? formatBytes2(value) : String(value);
|
|
20798
21110
|
}
|
|
20799
21111
|
function formatClock(seconds) {
|
|
20800
21112
|
const total = Math.max(0, Math.floor(seconds));
|
|
@@ -21453,7 +21765,7 @@ function RecordingHeroScreen({
|
|
|
21453
21765
|
const phase = stoppedPhase(artifact);
|
|
21454
21766
|
const meta3 = [
|
|
21455
21767
|
telemetry.durationMs != null ? formatClockMs(telemetry.durationMs) : null,
|
|
21456
|
-
|
|
21768
|
+
formatBytes3(telemetry.sizeBytes) || null
|
|
21457
21769
|
].filter(Boolean).join(" \xB7 ");
|
|
21458
21770
|
const saved = artifact?.uploadStatus === "uploaded" ? "\u2713 Saved to Recappi Cloud" : "\u2713 Saved to your Mac";
|
|
21459
21771
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
@@ -21488,7 +21800,7 @@ function RecordingHeroScreen({
|
|
|
21488
21800
|
const starting = telemetry.status === "starting" || telemetry.status === "stopping";
|
|
21489
21801
|
const badge = paused ? "\u23F8 PAUSED" : starting ? "\u2026" : "\u23FA REC";
|
|
21490
21802
|
const meterW = Math.max(10, Math.min(72, innerWidth - 20));
|
|
21491
|
-
const sizeStr = telemetry.sizeBytes ?
|
|
21803
|
+
const sizeStr = telemetry.sizeBytes ? formatBytes3(telemetry.sizeBytes) : "";
|
|
21492
21804
|
const context = [telemetry.sourceLabel, telemetry.micEnabled ? "Microphone" : null, sizeStr || null].filter(Boolean).join(" \xB7 ");
|
|
21493
21805
|
const waveRows = waveRowsFor(size.rows);
|
|
21494
21806
|
const meterBlockRows = (telemetry.micEnabled ? 2 : 1) * (waveRows + 1) + (telemetry.micEnabled ? 1 : 0);
|