recappi 0.1.64 → 0.1.66
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 +349 -21
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
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: [
|
|
@@ -18657,7 +18657,7 @@ import os3 from "os";
|
|
|
18657
18657
|
import path3 from "path";
|
|
18658
18658
|
import { createRequire } from "module";
|
|
18659
18659
|
var require2 = createRequire(import.meta.url);
|
|
18660
|
-
var
|
|
18660
|
+
var sqliteModule = null;
|
|
18661
18661
|
var CLI_STORE_SCHEMA_VERSION = 2;
|
|
18662
18662
|
function defaultStorePath(homeDir = os3.homedir(), env = process.env) {
|
|
18663
18663
|
const explicit = env.RECAPPI_CLI_STORE_PATH?.trim();
|
|
@@ -18689,7 +18689,7 @@ var CliLocalStore = class {
|
|
|
18689
18689
|
if (!opts.readonly && dbPath !== ":memory:") {
|
|
18690
18690
|
mkdirSync(path3.dirname(dbPath), { recursive: true, mode: 448 });
|
|
18691
18691
|
}
|
|
18692
|
-
this.db =
|
|
18692
|
+
this.db = openSqliteDatabase(dbPath, { readonly: opts.readonly === true });
|
|
18693
18693
|
this.now = opts.now ?? Date.now;
|
|
18694
18694
|
if (!opts.readonly) this.migrate();
|
|
18695
18695
|
}
|
|
@@ -18950,6 +18950,44 @@ function parseAccountPartition(input, mode) {
|
|
|
18950
18950
|
return null;
|
|
18951
18951
|
}
|
|
18952
18952
|
}
|
|
18953
|
+
function openSqliteDatabase(filename, opts) {
|
|
18954
|
+
const { DatabaseSync } = loadNodeSqlite();
|
|
18955
|
+
const db = new DatabaseSync(filename, opts.readonly ? { readOnly: true } : {});
|
|
18956
|
+
return {
|
|
18957
|
+
exec: (source) => db.exec(source),
|
|
18958
|
+
prepare: (source) => db.prepare(source),
|
|
18959
|
+
pragma: (source) => db.prepare(`PRAGMA ${source.trim().replace(/;$/, "")}`).all(),
|
|
18960
|
+
close: () => db.close()
|
|
18961
|
+
};
|
|
18962
|
+
}
|
|
18963
|
+
function loadNodeSqlite() {
|
|
18964
|
+
if (sqliteModule) return sqliteModule;
|
|
18965
|
+
sqliteModule = suppressNodeSqliteWarning(() => require2("node:sqlite"));
|
|
18966
|
+
return sqliteModule;
|
|
18967
|
+
}
|
|
18968
|
+
function suppressNodeSqliteWarning(run) {
|
|
18969
|
+
const emitWarning = process.emitWarning;
|
|
18970
|
+
process.emitWarning = function emitWarningWithoutNodeSqliteNoise(warning, ...args) {
|
|
18971
|
+
const message = typeof warning === "string" ? warning : warning.message;
|
|
18972
|
+
if (message.includes("SQLite is an experimental feature")) return;
|
|
18973
|
+
return emitWarning.call(process, warning, ...args);
|
|
18974
|
+
};
|
|
18975
|
+
try {
|
|
18976
|
+
return run();
|
|
18977
|
+
} catch (error51) {
|
|
18978
|
+
if (isMissingNodeSqlite(error51)) {
|
|
18979
|
+
throw cliError("internal.unexpected", "Local SQLite store requires Node.js 22 or newer.", {
|
|
18980
|
+
hint: "Upgrade Node.js, then retry. Cloud-only commands that do not touch local state can still run."
|
|
18981
|
+
});
|
|
18982
|
+
}
|
|
18983
|
+
throw error51;
|
|
18984
|
+
} finally {
|
|
18985
|
+
process.emitWarning = emitWarning;
|
|
18986
|
+
}
|
|
18987
|
+
}
|
|
18988
|
+
function isMissingNodeSqlite(error51) {
|
|
18989
|
+
return error51 instanceof Error && ("code" in error51 ? error51.code === "ERR_UNKNOWN_BUILTIN_MODULE" : false);
|
|
18990
|
+
}
|
|
18953
18991
|
function cleanString(value) {
|
|
18954
18992
|
const trimmed = value?.trim();
|
|
18955
18993
|
return trimmed ? trimmed : null;
|
|
@@ -20235,13 +20273,240 @@ ${lines.join("\n")}
|
|
|
20235
20273
|
` : "";
|
|
20236
20274
|
}
|
|
20237
20275
|
|
|
20276
|
+
// src/progressStepper.ts
|
|
20277
|
+
var STEP_DEFS = [
|
|
20278
|
+
{ key: "check", label: "Check" },
|
|
20279
|
+
{ key: "upload", label: "Upload" },
|
|
20280
|
+
{ key: "transcribe", label: "Transcribe" },
|
|
20281
|
+
{ key: "done", label: "Done" }
|
|
20282
|
+
];
|
|
20283
|
+
var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
20284
|
+
var BAR_WIDTH = 16;
|
|
20285
|
+
var LABEL_WIDTH = 11;
|
|
20286
|
+
function createStepperModel() {
|
|
20287
|
+
return {
|
|
20288
|
+
steps: STEP_DEFS.map((d) => ({ ...d, status: "pending", detail: "" })),
|
|
20289
|
+
finished: false
|
|
20290
|
+
};
|
|
20291
|
+
}
|
|
20292
|
+
function isStepperEvent(event) {
|
|
20293
|
+
if (event.command !== "upload") return false;
|
|
20294
|
+
if (event.type === "started") return true;
|
|
20295
|
+
return typeof event.status === "string";
|
|
20296
|
+
}
|
|
20297
|
+
function cloneModel(model) {
|
|
20298
|
+
return {
|
|
20299
|
+
steps: model.steps.map((s) => ({ ...s })),
|
|
20300
|
+
...model.transcribeStartMs != null ? { transcribeStartMs: model.transcribeStartMs } : {},
|
|
20301
|
+
finished: model.finished
|
|
20302
|
+
};
|
|
20303
|
+
}
|
|
20304
|
+
function applyStepperEvent(model, event, nowMs) {
|
|
20305
|
+
const next = cloneModel(model);
|
|
20306
|
+
const step = (key) => next.steps.find((s) => s.key === key);
|
|
20307
|
+
const markDone = (key) => {
|
|
20308
|
+
const s = step(key);
|
|
20309
|
+
if (s.status !== "failed") {
|
|
20310
|
+
s.status = "done";
|
|
20311
|
+
s.percent = void 0;
|
|
20312
|
+
}
|
|
20313
|
+
};
|
|
20314
|
+
const startTranscribeClock = () => {
|
|
20315
|
+
if (next.transcribeStartMs == null) next.transcribeStartMs = nowMs;
|
|
20316
|
+
};
|
|
20317
|
+
switch (event.status) {
|
|
20318
|
+
case "checking_audio":
|
|
20319
|
+
step("check").status = "active";
|
|
20320
|
+
break;
|
|
20321
|
+
case "starting_upload": {
|
|
20322
|
+
markDone("check");
|
|
20323
|
+
const file2 = fileFromEvent(event);
|
|
20324
|
+
if (file2) step("check").detail = file2;
|
|
20325
|
+
step("upload").status = "active";
|
|
20326
|
+
step("upload").detail = "Starting\u2026";
|
|
20327
|
+
break;
|
|
20328
|
+
}
|
|
20329
|
+
case "uploading":
|
|
20330
|
+
step("upload").status = "active";
|
|
20331
|
+
step("upload").detail = "";
|
|
20332
|
+
if (typeof event.percent === "number") step("upload").percent = event.percent;
|
|
20333
|
+
break;
|
|
20334
|
+
case "finishing_upload":
|
|
20335
|
+
step("upload").status = "active";
|
|
20336
|
+
step("upload").detail = "Finalizing";
|
|
20337
|
+
step("upload").percent = 100;
|
|
20338
|
+
break;
|
|
20339
|
+
case "uploaded":
|
|
20340
|
+
markDone("upload");
|
|
20341
|
+
step("upload").detail = recordingUrl(event) ?? "Uploaded";
|
|
20342
|
+
break;
|
|
20343
|
+
case "starting_transcription":
|
|
20344
|
+
step("transcribe").status = "active";
|
|
20345
|
+
step("transcribe").detail = "Starting\u2026";
|
|
20346
|
+
startTranscribeClock();
|
|
20347
|
+
break;
|
|
20348
|
+
case "queued":
|
|
20349
|
+
step("transcribe").status = "active";
|
|
20350
|
+
step("transcribe").detail = "Queued";
|
|
20351
|
+
startTranscribeClock();
|
|
20352
|
+
break;
|
|
20353
|
+
case "running":
|
|
20354
|
+
step("transcribe").status = "active";
|
|
20355
|
+
startTranscribeClock();
|
|
20356
|
+
if (typeof event.percent === "number") {
|
|
20357
|
+
step("transcribe").percent = event.percent;
|
|
20358
|
+
step("transcribe").detail = "";
|
|
20359
|
+
} else {
|
|
20360
|
+
step("transcribe").percent = void 0;
|
|
20361
|
+
step("transcribe").detail = "Transcribing\u2026";
|
|
20362
|
+
}
|
|
20363
|
+
break;
|
|
20364
|
+
case "succeeded":
|
|
20365
|
+
markDone("transcribe");
|
|
20366
|
+
markDone("done");
|
|
20367
|
+
if (event.transcriptId) {
|
|
20368
|
+
step("done").detail = `\u2192 recappi transcript get ${event.transcriptId}`;
|
|
20369
|
+
}
|
|
20370
|
+
next.finished = true;
|
|
20371
|
+
break;
|
|
20372
|
+
case "failed":
|
|
20373
|
+
markActiveFailed(next, event.message);
|
|
20374
|
+
break;
|
|
20375
|
+
default:
|
|
20376
|
+
if (event.type === "started") step("check").status = "active";
|
|
20377
|
+
break;
|
|
20378
|
+
}
|
|
20379
|
+
return next;
|
|
20380
|
+
}
|
|
20381
|
+
function markStepperFailed(model, message) {
|
|
20382
|
+
const next = cloneModel(model);
|
|
20383
|
+
markActiveFailed(next, message);
|
|
20384
|
+
return next;
|
|
20385
|
+
}
|
|
20386
|
+
function completeStepperModel(model, next) {
|
|
20387
|
+
const m = cloneModel(model);
|
|
20388
|
+
const transcribe = m.steps.find((s) => s.key === "transcribe");
|
|
20389
|
+
if (transcribe.status === "pending") {
|
|
20390
|
+
transcribe.status = "skipped";
|
|
20391
|
+
transcribe.detail = "no --transcribe";
|
|
20392
|
+
}
|
|
20393
|
+
for (const s of m.steps) {
|
|
20394
|
+
if (s.status === "active" || s.status === "pending") s.status = "done";
|
|
20395
|
+
s.percent = void 0;
|
|
20396
|
+
}
|
|
20397
|
+
const done = m.steps.find((s) => s.key === "done");
|
|
20398
|
+
if (next?.transcriptId) done.detail = `\u2192 recappi transcript get ${next.transcriptId}`;
|
|
20399
|
+
else if (next?.recordingId) done.detail = `\u2192 recappi recordings get ${next.recordingId}`;
|
|
20400
|
+
m.finished = true;
|
|
20401
|
+
return m;
|
|
20402
|
+
}
|
|
20403
|
+
function markActiveFailed(model, message) {
|
|
20404
|
+
const active = model.steps.find((s) => s.status === "active");
|
|
20405
|
+
const target = active ?? model.steps.find((s) => s.status === "pending");
|
|
20406
|
+
if (target) {
|
|
20407
|
+
target.status = "failed";
|
|
20408
|
+
if (message) target.detail = message;
|
|
20409
|
+
}
|
|
20410
|
+
model.finished = true;
|
|
20411
|
+
}
|
|
20412
|
+
function formatStepperLines(model, nowMs, spinnerFrame, color) {
|
|
20413
|
+
return model.steps.map((s) => {
|
|
20414
|
+
const marker = markerFor(s, spinnerFrame);
|
|
20415
|
+
const label = s.label.padEnd(LABEL_WIDTH);
|
|
20416
|
+
let detail = s.detail;
|
|
20417
|
+
if (s.status === "active" && typeof s.percent === "number") {
|
|
20418
|
+
detail = `${progressBar(s.percent)} ${clampPercent2(s.percent)}%`;
|
|
20419
|
+
}
|
|
20420
|
+
if (s.key === "transcribe" && s.status === "active" && model.transcribeStartMs != null) {
|
|
20421
|
+
const elapsed = formatElapsed(Math.max(0, nowMs - model.transcribeStartMs));
|
|
20422
|
+
detail = detail ? `${detail} \xB7 ${elapsed}` : elapsed;
|
|
20423
|
+
}
|
|
20424
|
+
const body = ` ${marker} ${label}${detail}`.trimEnd();
|
|
20425
|
+
return color ? colorize(s.status, marker, body) : body;
|
|
20426
|
+
});
|
|
20427
|
+
}
|
|
20428
|
+
function markerFor(step, spinnerFrame) {
|
|
20429
|
+
switch (step.status) {
|
|
20430
|
+
case "done":
|
|
20431
|
+
return "\u2713";
|
|
20432
|
+
case "failed":
|
|
20433
|
+
return "\u2717";
|
|
20434
|
+
case "active":
|
|
20435
|
+
return SPINNER[spinnerFrame % SPINNER.length];
|
|
20436
|
+
case "skipped":
|
|
20437
|
+
return "\xB7";
|
|
20438
|
+
default:
|
|
20439
|
+
return "\u25CB";
|
|
20440
|
+
}
|
|
20441
|
+
}
|
|
20442
|
+
function colorize(status, marker, line) {
|
|
20443
|
+
const code = status === "done" ? "32" : status === "failed" ? "31" : status === "active" ? "36" : "90";
|
|
20444
|
+
const colored = `\x1B[${code}m${marker}\x1B[0m`;
|
|
20445
|
+
return line.replace(marker, colored);
|
|
20446
|
+
}
|
|
20447
|
+
function progressBar(percent) {
|
|
20448
|
+
const p = clampPercent2(percent);
|
|
20449
|
+
const filled = Math.round(p / 100 * BAR_WIDTH);
|
|
20450
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(BAR_WIDTH - filled);
|
|
20451
|
+
}
|
|
20452
|
+
function clampPercent2(percent) {
|
|
20453
|
+
return Math.max(0, Math.min(100, Math.round(percent)));
|
|
20454
|
+
}
|
|
20455
|
+
function fileFromEvent(event) {
|
|
20456
|
+
const data = event.data;
|
|
20457
|
+
if (!data || typeof data !== "object") return void 0;
|
|
20458
|
+
const file2 = data.file;
|
|
20459
|
+
if (!file2 || typeof file2 !== "object") return void 0;
|
|
20460
|
+
const f = file2;
|
|
20461
|
+
const parts = [];
|
|
20462
|
+
if (typeof f.title === "string" && f.title) parts.push(f.title);
|
|
20463
|
+
if (typeof f.sizeBytes === "number") parts.push(formatBytes(f.sizeBytes));
|
|
20464
|
+
if (typeof f.durationMs === "number") parts.push(formatMediaDuration(f.durationMs));
|
|
20465
|
+
return parts.length ? parts.join(" \xB7 ") : void 0;
|
|
20466
|
+
}
|
|
20467
|
+
function recordingUrl(event) {
|
|
20468
|
+
if (typeof event.message === "string" && event.message.includes("/recordings/")) {
|
|
20469
|
+
const idx = event.message.indexOf("http");
|
|
20470
|
+
if (idx >= 0) return event.message.slice(idx);
|
|
20471
|
+
}
|
|
20472
|
+
if (event.origin && event.recordingId) {
|
|
20473
|
+
return `${event.origin.replace(/\/+$/, "")}/recordings/${event.recordingId}`;
|
|
20474
|
+
}
|
|
20475
|
+
return void 0;
|
|
20476
|
+
}
|
|
20477
|
+
function formatBytes(bytes) {
|
|
20478
|
+
if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
20479
|
+
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
20480
|
+
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
20481
|
+
return `${bytes} B`;
|
|
20482
|
+
}
|
|
20483
|
+
function formatMediaDuration(ms) {
|
|
20484
|
+
const totalSec = Math.round(ms / 1e3);
|
|
20485
|
+
const h = Math.floor(totalSec / 3600);
|
|
20486
|
+
const m = Math.floor(totalSec % 3600 / 60);
|
|
20487
|
+
const s = totalSec % 60;
|
|
20488
|
+
if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
20489
|
+
return `${m}:${String(s).padStart(2, "0")}`;
|
|
20490
|
+
}
|
|
20491
|
+
function formatElapsed(ms) {
|
|
20492
|
+
const totalSec = Math.floor(ms / 1e3);
|
|
20493
|
+
const h = Math.floor(totalSec / 3600);
|
|
20494
|
+
const m = Math.floor(totalSec % 3600 / 60);
|
|
20495
|
+
const s = totalSec % 60;
|
|
20496
|
+
const clock = h > 0 ? `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}` : `${m}:${String(s).padStart(2, "0")}`;
|
|
20497
|
+
return `${clock} elapsed`;
|
|
20498
|
+
}
|
|
20499
|
+
|
|
20238
20500
|
// src/render.ts
|
|
20239
20501
|
function createHumanProgressState(interactive) {
|
|
20240
20502
|
return {
|
|
20241
20503
|
interactive,
|
|
20242
20504
|
activeLineLength: 0,
|
|
20243
20505
|
lastLineByScope: /* @__PURE__ */ new Map(),
|
|
20244
|
-
lastUploadBucketByScope: /* @__PURE__ */ new Map()
|
|
20506
|
+
lastUploadBucketByScope: /* @__PURE__ */ new Map(),
|
|
20507
|
+
stepperLinesDrawn: 0,
|
|
20508
|
+
spinnerFrame: 0,
|
|
20509
|
+
stepperShown: false
|
|
20245
20510
|
};
|
|
20246
20511
|
}
|
|
20247
20512
|
function renderSuccess(command, data, opts) {
|
|
@@ -20263,6 +20528,7 @@ function renderSuccess(command, data, opts) {
|
|
|
20263
20528
|
renderEnvelope(envelope, opts);
|
|
20264
20529
|
return;
|
|
20265
20530
|
}
|
|
20531
|
+
finalizeStepperSuccess(command, filtered, opts);
|
|
20266
20532
|
finishHumanProgress(opts);
|
|
20267
20533
|
renderHumanSuccess(command, filtered, opts);
|
|
20268
20534
|
}
|
|
@@ -20291,6 +20557,7 @@ function renderFailure(command, error51, opts, data) {
|
|
|
20291
20557
|
renderEnvelope(envelope, opts);
|
|
20292
20558
|
return;
|
|
20293
20559
|
}
|
|
20560
|
+
if (finalizeStepperFailure(command, error51, opts, data)) return;
|
|
20294
20561
|
finishHumanProgress(opts);
|
|
20295
20562
|
opts.stderr(`recappi: ${error51.message}
|
|
20296
20563
|
`);
|
|
@@ -20316,6 +20583,10 @@ function renderEvent(event, opts) {
|
|
|
20316
20583
|
return;
|
|
20317
20584
|
}
|
|
20318
20585
|
if ((event.type === "started" || event.type === "progress") && opts.mode === "human") {
|
|
20586
|
+
if (opts.progress?.interactive && isStepperEvent(event)) {
|
|
20587
|
+
renderStepperEvent(event, opts);
|
|
20588
|
+
return;
|
|
20589
|
+
}
|
|
20319
20590
|
const line = formatHumanProgress(event, opts);
|
|
20320
20591
|
if (line) {
|
|
20321
20592
|
if (isPersistentProgressEvent(event)) writePersistentHumanProgress(line, opts);
|
|
@@ -20323,6 +20594,56 @@ function renderEvent(event, opts) {
|
|
|
20323
20594
|
}
|
|
20324
20595
|
}
|
|
20325
20596
|
}
|
|
20597
|
+
function renderStepperEvent(event, opts) {
|
|
20598
|
+
const state = opts.progress;
|
|
20599
|
+
if (!state) return;
|
|
20600
|
+
if (!state.stepper) state.stepper = createStepperModel();
|
|
20601
|
+
state.stepper = applyStepperEvent(state.stepper, event, Date.now());
|
|
20602
|
+
state.spinnerFrame += 1;
|
|
20603
|
+
drawStepperBlock(state, opts);
|
|
20604
|
+
}
|
|
20605
|
+
function drawStepperBlock(state, opts) {
|
|
20606
|
+
if (!state.stepper) return;
|
|
20607
|
+
const lines = formatStepperLines(state.stepper, Date.now(), state.spinnerFrame, true);
|
|
20608
|
+
let out = "";
|
|
20609
|
+
if (state.stepperLinesDrawn > 0) out += `\x1B[${state.stepperLinesDrawn}A`;
|
|
20610
|
+
for (const line of lines) out += `\x1B[2K${line}
|
|
20611
|
+
`;
|
|
20612
|
+
opts.stderr(out);
|
|
20613
|
+
state.stepperLinesDrawn = lines.length;
|
|
20614
|
+
state.stepperShown = true;
|
|
20615
|
+
}
|
|
20616
|
+
function finalizeStepperSuccess(command, data, opts) {
|
|
20617
|
+
const state = opts.progress;
|
|
20618
|
+
if (!state?.stepper || state.stepperLinesDrawn === 0 || command !== "upload") return;
|
|
20619
|
+
const success2 = firstUploadSuccess(data);
|
|
20620
|
+
state.stepper = completeStepperModel(state.stepper, success2);
|
|
20621
|
+
drawStepperBlock(state, opts);
|
|
20622
|
+
}
|
|
20623
|
+
function firstUploadSuccess(data) {
|
|
20624
|
+
if (!isUploadBatch(data) || data.successes.length === 0) return void 0;
|
|
20625
|
+
const s = data.successes[0];
|
|
20626
|
+
return {
|
|
20627
|
+
...s.transcriptId ? { transcriptId: s.transcriptId } : {},
|
|
20628
|
+
...s.recordingId ? { recordingId: s.recordingId } : {}
|
|
20629
|
+
};
|
|
20630
|
+
}
|
|
20631
|
+
function finalizeStepperFailure(command, error51, opts, data) {
|
|
20632
|
+
const state = opts.progress;
|
|
20633
|
+
if (!state?.stepper || state.stepperLinesDrawn === 0) return false;
|
|
20634
|
+
state.stepper = markStepperFailed(state.stepper, stepperFailureDetail(command, error51, data));
|
|
20635
|
+
drawStepperBlock(state, opts);
|
|
20636
|
+
state.stepperLinesDrawn = 0;
|
|
20637
|
+
state.stepper = void 0;
|
|
20638
|
+
return true;
|
|
20639
|
+
}
|
|
20640
|
+
function stepperFailureDetail(command, error51, data) {
|
|
20641
|
+
if (command === "upload" && isUploadBatch(data) && data.failures.length > 0) {
|
|
20642
|
+
const first = data.failures[0].error;
|
|
20643
|
+
return `${first.message} (${first.code})`;
|
|
20644
|
+
}
|
|
20645
|
+
return `${error51.message} (${error51.code})`;
|
|
20646
|
+
}
|
|
20326
20647
|
function renderEnvelope(envelope, opts) {
|
|
20327
20648
|
const parsed = cliEnvelopeSchema.parse(envelope);
|
|
20328
20649
|
opts.stdout(`${stableStringify(parsed, opts.compact === true)}
|
|
@@ -20372,7 +20693,7 @@ function renderHumanSuccess(command, data, opts) {
|
|
|
20372
20693
|
}
|
|
20373
20694
|
if (typeof billing.storageBytes === "number") {
|
|
20374
20695
|
const cap = formatNullableCap(billing.storageCapBytes, "bytes");
|
|
20375
|
-
opts.stdout(` storage: ${
|
|
20696
|
+
opts.stdout(` storage: ${formatBytes2(billing.storageBytes)} / ${cap}
|
|
20376
20697
|
`);
|
|
20377
20698
|
}
|
|
20378
20699
|
const localStore = isRecord4(data.localStore) ? data.localStore : {};
|
|
@@ -20434,7 +20755,7 @@ Next cursor: ${data.nextCursor}
|
|
|
20434
20755
|
if (typeof data.durationMs === "number")
|
|
20435
20756
|
opts.stdout(` duration: ${formatDurationMs(data.durationMs)}
|
|
20436
20757
|
`);
|
|
20437
|
-
if (typeof data.sizeBytes === "number") opts.stdout(` size: ${
|
|
20758
|
+
if (typeof data.sizeBytes === "number") opts.stdout(` size: ${formatBytes2(data.sizeBytes)}
|
|
20438
20759
|
`);
|
|
20439
20760
|
if (typeof data.activeTranscriptId === "string") {
|
|
20440
20761
|
opts.stdout(` activeTranscriptId: ${data.activeTranscriptId}
|
|
@@ -20483,6 +20804,7 @@ Next:
|
|
|
20483
20804
|
return;
|
|
20484
20805
|
}
|
|
20485
20806
|
if (command === "upload" && isUploadBatch(data)) {
|
|
20807
|
+
if (opts.progress?.stepperShown && data.failures.length === 0) return;
|
|
20486
20808
|
if (data.successes.length > 0) {
|
|
20487
20809
|
opts.stdout(uploadSuccessHeading(data.successes));
|
|
20488
20810
|
}
|
|
@@ -20708,7 +21030,13 @@ function writePersistentHumanProgress(line, opts) {
|
|
|
20708
21030
|
}
|
|
20709
21031
|
function finishHumanProgress(opts) {
|
|
20710
21032
|
const state = opts.progress;
|
|
20711
|
-
if (!state
|
|
21033
|
+
if (!state) return;
|
|
21034
|
+
if (state.stepperLinesDrawn > 0) {
|
|
21035
|
+
state.stepperLinesDrawn = 0;
|
|
21036
|
+
state.stepper = void 0;
|
|
21037
|
+
return;
|
|
21038
|
+
}
|
|
21039
|
+
if (!state.interactive || state.activeLineLength === 0) return;
|
|
20712
21040
|
opts.stderr("\n");
|
|
20713
21041
|
state.activeLineLength = 0;
|
|
20714
21042
|
}
|
|
@@ -20801,7 +21129,7 @@ function numberText(value) {
|
|
|
20801
21129
|
function formatDurationMs(ms) {
|
|
20802
21130
|
return formatClock(ms / 1e3);
|
|
20803
21131
|
}
|
|
20804
|
-
function
|
|
21132
|
+
function formatBytes2(bytes) {
|
|
20805
21133
|
if (bytes < 1024) return `${bytes} B`;
|
|
20806
21134
|
const units = ["KB", "MB", "GB", "TB"];
|
|
20807
21135
|
let value = bytes / 1024;
|
|
@@ -20816,7 +21144,7 @@ function formatBytes(bytes) {
|
|
|
20816
21144
|
function formatNullableCap(value, unit) {
|
|
20817
21145
|
if (value === null || value === void 0) return "Unlimited";
|
|
20818
21146
|
if (typeof value !== "number" || !Number.isFinite(value)) return "Unlimited";
|
|
20819
|
-
return unit === "bytes" ?
|
|
21147
|
+
return unit === "bytes" ? formatBytes2(value) : String(value);
|
|
20820
21148
|
}
|
|
20821
21149
|
function formatClock(seconds) {
|
|
20822
21150
|
const total = Math.max(0, Math.floor(seconds));
|
|
@@ -21475,7 +21803,7 @@ function RecordingHeroScreen({
|
|
|
21475
21803
|
const phase = stoppedPhase(artifact);
|
|
21476
21804
|
const meta3 = [
|
|
21477
21805
|
telemetry.durationMs != null ? formatClockMs(telemetry.durationMs) : null,
|
|
21478
|
-
|
|
21806
|
+
formatBytes3(telemetry.sizeBytes) || null
|
|
21479
21807
|
].filter(Boolean).join(" \xB7 ");
|
|
21480
21808
|
const saved = artifact?.uploadStatus === "uploaded" ? "\u2713 Saved to Recappi Cloud" : "\u2713 Saved to your Mac";
|
|
21481
21809
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", paddingX: 1, children: [
|
|
@@ -21510,7 +21838,7 @@ function RecordingHeroScreen({
|
|
|
21510
21838
|
const starting = telemetry.status === "starting" || telemetry.status === "stopping";
|
|
21511
21839
|
const badge = paused ? "\u23F8 PAUSED" : starting ? "\u2026" : "\u23FA REC";
|
|
21512
21840
|
const meterW = Math.max(10, Math.min(72, innerWidth - 20));
|
|
21513
|
-
const sizeStr = telemetry.sizeBytes ?
|
|
21841
|
+
const sizeStr = telemetry.sizeBytes ? formatBytes3(telemetry.sizeBytes) : "";
|
|
21514
21842
|
const context = [telemetry.sourceLabel, telemetry.micEnabled ? "Microphone" : null, sizeStr || null].filter(Boolean).join(" \xB7 ");
|
|
21515
21843
|
const waveRows = waveRowsFor(size.rows);
|
|
21516
21844
|
const meterBlockRows = (telemetry.micEnabled ? 2 : 1) * (waveRows + 1) + (telemetry.micEnabled ? 1 : 0);
|