cutsio 0.2.5 → 0.2.6
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 +213 -177
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -366,6 +366,195 @@ function resolveSelection(manifest, selection) {
|
|
|
366
366
|
return { videos: [...selected.values()], errors };
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
+
// src/ui/format.ts
|
|
370
|
+
function formatBytes(bytes) {
|
|
371
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
372
|
+
let value = bytes;
|
|
373
|
+
let unit = 0;
|
|
374
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
375
|
+
value /= 1024;
|
|
376
|
+
unit += 1;
|
|
377
|
+
}
|
|
378
|
+
return `${value.toFixed(unit === 0 ? 0 : 1)} ${units[unit]}`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/ui/theme.ts
|
|
382
|
+
var colorEnabled = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
383
|
+
function paint(code) {
|
|
384
|
+
return colorEnabled ? (text) => `\x1B[${code}m${text}\x1B[0m` : (text) => text;
|
|
385
|
+
}
|
|
386
|
+
var color = {
|
|
387
|
+
dim: paint(2),
|
|
388
|
+
bold: paint(1),
|
|
389
|
+
red: paint(31),
|
|
390
|
+
green: paint(32),
|
|
391
|
+
yellow: paint(33),
|
|
392
|
+
blue: paint(34),
|
|
393
|
+
magenta: paint(35),
|
|
394
|
+
cyan: paint(36)
|
|
395
|
+
};
|
|
396
|
+
var isInteractive = colorEnabled;
|
|
397
|
+
var animationsEnabled = colorEnabled && process.env.CUTSIO_NO_ANIM !== "1";
|
|
398
|
+
function sleep2(ms) {
|
|
399
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/ui/progress.ts
|
|
403
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
404
|
+
var BAR_WIDTH = 22;
|
|
405
|
+
var ProgressView = class {
|
|
406
|
+
active = /* @__PURE__ */ new Map();
|
|
407
|
+
interactive = isInteractive;
|
|
408
|
+
statusLine = null;
|
|
409
|
+
renderedLines = 0;
|
|
410
|
+
frame = 0;
|
|
411
|
+
timer = null;
|
|
412
|
+
dirty = false;
|
|
413
|
+
constructor() {
|
|
414
|
+
if (!this.interactive) return;
|
|
415
|
+
this.timer = setInterval(() => {
|
|
416
|
+
if (this.active.size === 0 && !this.statusLine) return;
|
|
417
|
+
this.frame += 1;
|
|
418
|
+
this.render();
|
|
419
|
+
}, 120);
|
|
420
|
+
this.timer.unref();
|
|
421
|
+
}
|
|
422
|
+
status(text) {
|
|
423
|
+
if (!this.interactive) return;
|
|
424
|
+
this.statusLine = text;
|
|
425
|
+
this.dirty = true;
|
|
426
|
+
this.render();
|
|
427
|
+
}
|
|
428
|
+
/** Print a line above the live area without tearing it. */
|
|
429
|
+
log(message) {
|
|
430
|
+
if (!this.interactive) {
|
|
431
|
+
console.log(message);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
this.clear();
|
|
435
|
+
process.stdout.write(`${message}
|
|
436
|
+
`);
|
|
437
|
+
this.render();
|
|
438
|
+
}
|
|
439
|
+
error(message) {
|
|
440
|
+
if (!this.interactive) {
|
|
441
|
+
console.error(message);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
this.clear();
|
|
445
|
+
process.stderr.write(`${message}
|
|
446
|
+
`);
|
|
447
|
+
this.render();
|
|
448
|
+
}
|
|
449
|
+
start(id, label) {
|
|
450
|
+
if (!this.interactive) {
|
|
451
|
+
console.log(` fetch ${label}`);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const now = Date.now();
|
|
455
|
+
this.active.set(id, {
|
|
456
|
+
label,
|
|
457
|
+
bytes: 0,
|
|
458
|
+
total: null,
|
|
459
|
+
lastBytes: 0,
|
|
460
|
+
lastAt: now,
|
|
461
|
+
speed: 0
|
|
462
|
+
});
|
|
463
|
+
this.dirty = true;
|
|
464
|
+
this.render();
|
|
465
|
+
}
|
|
466
|
+
progress(id, bytes, total) {
|
|
467
|
+
if (!this.interactive) return;
|
|
468
|
+
const item = this.active.get(id);
|
|
469
|
+
if (!item) return;
|
|
470
|
+
const now = Date.now();
|
|
471
|
+
const elapsed = (now - item.lastAt) / 1e3;
|
|
472
|
+
if (elapsed >= 0.3) {
|
|
473
|
+
const delta = bytes - item.lastBytes;
|
|
474
|
+
if (delta >= 0) item.speed = delta / elapsed;
|
|
475
|
+
item.lastBytes = bytes;
|
|
476
|
+
item.lastAt = now;
|
|
477
|
+
}
|
|
478
|
+
item.bytes = bytes;
|
|
479
|
+
if (total !== null) item.total = total;
|
|
480
|
+
this.dirty = true;
|
|
481
|
+
}
|
|
482
|
+
finish(id, ok, text) {
|
|
483
|
+
if (!this.interactive) {
|
|
484
|
+
if (ok) console.log(` done ${text}`);
|
|
485
|
+
else console.error(` FAILED ${text}`);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
this.clear();
|
|
489
|
+
const line = ok ? ` ${color.green("\u2713")} ${text}` : ` ${color.red("\u2717")} ${text}`;
|
|
490
|
+
(ok ? process.stdout : process.stderr).write(`${line}
|
|
491
|
+
`);
|
|
492
|
+
this.active.delete(id);
|
|
493
|
+
this.render();
|
|
494
|
+
}
|
|
495
|
+
stop() {
|
|
496
|
+
if (this.timer) clearInterval(this.timer);
|
|
497
|
+
this.timer = null;
|
|
498
|
+
if (!this.interactive) return;
|
|
499
|
+
this.clear();
|
|
500
|
+
this.active.clear();
|
|
501
|
+
this.statusLine = null;
|
|
502
|
+
}
|
|
503
|
+
/** Run `work` while a spinner status shows; cleared on exit, plain line when piped. */
|
|
504
|
+
async withStatus(text, work) {
|
|
505
|
+
if (!this.interactive) {
|
|
506
|
+
console.log(text);
|
|
507
|
+
try {
|
|
508
|
+
return await work();
|
|
509
|
+
} finally {
|
|
510
|
+
this.stop();
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
this.status(text);
|
|
514
|
+
try {
|
|
515
|
+
return await work();
|
|
516
|
+
} finally {
|
|
517
|
+
this.statusLine = null;
|
|
518
|
+
this.render();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
clear() {
|
|
522
|
+
if (this.renderedLines === 0) return;
|
|
523
|
+
process.stdout.write(`\x1B[${this.renderedLines}A\x1B[J`);
|
|
524
|
+
this.renderedLines = 0;
|
|
525
|
+
}
|
|
526
|
+
render() {
|
|
527
|
+
if (!this.interactive) return;
|
|
528
|
+
if (this.active.size === 0 && !this.statusLine) {
|
|
529
|
+
this.clear();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const lines = [];
|
|
533
|
+
if (this.statusLine) {
|
|
534
|
+
const spinner = FRAMES[this.frame % FRAMES.length] ?? FRAMES[0];
|
|
535
|
+
lines.push(` ${color.cyan(spinner ?? "")} ${color.dim(this.statusLine)}`);
|
|
536
|
+
}
|
|
537
|
+
for (const item of this.active.values()) {
|
|
538
|
+
lines.push(` ${color.bold(item.label)}`);
|
|
539
|
+
lines.push(` ${this.bar(item)}`);
|
|
540
|
+
}
|
|
541
|
+
this.clear();
|
|
542
|
+
process.stdout.write(`${lines.join("\n")}
|
|
543
|
+
`);
|
|
544
|
+
this.renderedLines = lines.length;
|
|
545
|
+
this.dirty = false;
|
|
546
|
+
}
|
|
547
|
+
bar(item) {
|
|
548
|
+
const ratio = item.total && item.total > 0 ? Math.min(1, item.bytes / item.total) : null;
|
|
549
|
+
const filled = ratio === null ? 0 : Math.round(ratio * BAR_WIDTH);
|
|
550
|
+
const track = color.cyan("\u2588".repeat(filled)) + color.dim("\u2591".repeat(BAR_WIDTH - filled));
|
|
551
|
+
const percent = ratio === null ? " ? " : `${Math.floor(ratio * 100)}%`.padStart(4);
|
|
552
|
+
const size = item.total ? `${formatBytes(item.bytes)} / ${formatBytes(item.total)}` : formatBytes(item.bytes);
|
|
553
|
+
const speed = item.speed > 0 ? `${formatBytes(item.speed)}/s` : "";
|
|
554
|
+
return `${track} ${percent} ${size} ${color.dim(speed)}`.trimEnd();
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
|
|
369
558
|
// src/commands/list.ts
|
|
370
559
|
async function listCommand(flags) {
|
|
371
560
|
const { apiKey, migrated } = await resolveApiKey();
|
|
@@ -375,12 +564,15 @@ async function listCommand(flags) {
|
|
|
375
564
|
return 1;
|
|
376
565
|
}
|
|
377
566
|
const client = new CutsioClient(resolveApiUrl(flags.url), apiKey);
|
|
567
|
+
const view = new ProgressView();
|
|
378
568
|
let manifest;
|
|
379
569
|
try {
|
|
380
|
-
manifest = await client.fetchManifest();
|
|
570
|
+
manifest = await view.withStatus("Fetching workspace\u2026", () => client.fetchManifest());
|
|
381
571
|
} catch (error) {
|
|
382
572
|
console.error(error instanceof Error ? error.message : String(error));
|
|
383
573
|
return 1;
|
|
574
|
+
} finally {
|
|
575
|
+
view.stop();
|
|
384
576
|
}
|
|
385
577
|
if (flags.json === "true") {
|
|
386
578
|
console.log(JSON.stringify(manifest, null, 2));
|
|
@@ -462,7 +654,16 @@ async function loginCommand(flags) {
|
|
|
462
654
|
continue;
|
|
463
655
|
}
|
|
464
656
|
try {
|
|
465
|
-
const
|
|
657
|
+
const view = new ProgressView();
|
|
658
|
+
let who;
|
|
659
|
+
try {
|
|
660
|
+
who = await view.withStatus(
|
|
661
|
+
"Verifying key\u2026",
|
|
662
|
+
() => new CutsioClient(apiUrl, apiKey).whoami()
|
|
663
|
+
);
|
|
664
|
+
} finally {
|
|
665
|
+
view.stop();
|
|
666
|
+
}
|
|
466
667
|
if (!who.orgId) {
|
|
467
668
|
console.error("That key is not attached to an organization. Create an organization API key.\n");
|
|
468
669
|
continue;
|
|
@@ -870,7 +1071,7 @@ async function sync(client, options) {
|
|
|
870
1071
|
}
|
|
871
1072
|
break;
|
|
872
1073
|
}
|
|
873
|
-
await
|
|
1074
|
+
await sleep3(Math.min(Math.max(serverDelayMs, prepareDelayMs), MAX_PREPARE_DELAY_MS));
|
|
874
1075
|
prepareDelayMs = Math.min(prepareDelayMs * 2, MAX_PREPARE_DELAY_MS);
|
|
875
1076
|
}
|
|
876
1077
|
return { downloaded, unchanged, skipped, failed };
|
|
@@ -890,180 +1091,10 @@ async function runPool(items, concurrency, worker) {
|
|
|
890
1091
|
})
|
|
891
1092
|
);
|
|
892
1093
|
}
|
|
893
|
-
function sleep2(ms) {
|
|
894
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// src/ui/format.ts
|
|
898
|
-
function formatBytes(bytes) {
|
|
899
|
-
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
900
|
-
let value = bytes;
|
|
901
|
-
let unit = 0;
|
|
902
|
-
while (value >= 1024 && unit < units.length - 1) {
|
|
903
|
-
value /= 1024;
|
|
904
|
-
unit += 1;
|
|
905
|
-
}
|
|
906
|
-
return `${value.toFixed(unit === 0 ? 0 : 1)} ${units[unit]}`;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
// src/ui/theme.ts
|
|
910
|
-
var colorEnabled = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
911
|
-
function paint(code) {
|
|
912
|
-
return colorEnabled ? (text) => `\x1B[${code}m${text}\x1B[0m` : (text) => text;
|
|
913
|
-
}
|
|
914
|
-
var color = {
|
|
915
|
-
dim: paint(2),
|
|
916
|
-
bold: paint(1),
|
|
917
|
-
red: paint(31),
|
|
918
|
-
green: paint(32),
|
|
919
|
-
yellow: paint(33),
|
|
920
|
-
blue: paint(34),
|
|
921
|
-
magenta: paint(35),
|
|
922
|
-
cyan: paint(36)
|
|
923
|
-
};
|
|
924
|
-
var isInteractive = colorEnabled;
|
|
925
|
-
var animationsEnabled = colorEnabled && process.env.CUTSIO_NO_ANIM !== "1";
|
|
926
1094
|
function sleep3(ms) {
|
|
927
1095
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
928
1096
|
}
|
|
929
1097
|
|
|
930
|
-
// src/ui/progress.ts
|
|
931
|
-
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
932
|
-
var BAR_WIDTH = 22;
|
|
933
|
-
var ProgressView = class {
|
|
934
|
-
active = /* @__PURE__ */ new Map();
|
|
935
|
-
interactive = isInteractive;
|
|
936
|
-
statusLine = null;
|
|
937
|
-
renderedLines = 0;
|
|
938
|
-
frame = 0;
|
|
939
|
-
timer = null;
|
|
940
|
-
dirty = false;
|
|
941
|
-
constructor() {
|
|
942
|
-
if (!this.interactive) return;
|
|
943
|
-
this.timer = setInterval(() => {
|
|
944
|
-
if (this.active.size === 0) return;
|
|
945
|
-
this.frame += 1;
|
|
946
|
-
this.render();
|
|
947
|
-
}, 120);
|
|
948
|
-
this.timer.unref();
|
|
949
|
-
}
|
|
950
|
-
status(text) {
|
|
951
|
-
if (!this.interactive) return;
|
|
952
|
-
this.statusLine = text;
|
|
953
|
-
this.dirty = true;
|
|
954
|
-
}
|
|
955
|
-
/** Print a line above the live area without tearing it. */
|
|
956
|
-
log(message) {
|
|
957
|
-
if (!this.interactive) {
|
|
958
|
-
console.log(message);
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
961
|
-
this.clear();
|
|
962
|
-
process.stdout.write(`${message}
|
|
963
|
-
`);
|
|
964
|
-
this.render();
|
|
965
|
-
}
|
|
966
|
-
error(message) {
|
|
967
|
-
if (!this.interactive) {
|
|
968
|
-
console.error(message);
|
|
969
|
-
return;
|
|
970
|
-
}
|
|
971
|
-
this.clear();
|
|
972
|
-
process.stderr.write(`${message}
|
|
973
|
-
`);
|
|
974
|
-
this.render();
|
|
975
|
-
}
|
|
976
|
-
start(id, label) {
|
|
977
|
-
if (!this.interactive) {
|
|
978
|
-
console.log(` fetch ${label}`);
|
|
979
|
-
return;
|
|
980
|
-
}
|
|
981
|
-
const now = Date.now();
|
|
982
|
-
this.active.set(id, {
|
|
983
|
-
label,
|
|
984
|
-
bytes: 0,
|
|
985
|
-
total: null,
|
|
986
|
-
lastBytes: 0,
|
|
987
|
-
lastAt: now,
|
|
988
|
-
speed: 0
|
|
989
|
-
});
|
|
990
|
-
this.dirty = true;
|
|
991
|
-
this.render();
|
|
992
|
-
}
|
|
993
|
-
progress(id, bytes, total) {
|
|
994
|
-
if (!this.interactive) return;
|
|
995
|
-
const item = this.active.get(id);
|
|
996
|
-
if (!item) return;
|
|
997
|
-
const now = Date.now();
|
|
998
|
-
const elapsed = (now - item.lastAt) / 1e3;
|
|
999
|
-
if (elapsed >= 0.3) {
|
|
1000
|
-
const delta = bytes - item.lastBytes;
|
|
1001
|
-
if (delta >= 0) item.speed = delta / elapsed;
|
|
1002
|
-
item.lastBytes = bytes;
|
|
1003
|
-
item.lastAt = now;
|
|
1004
|
-
}
|
|
1005
|
-
item.bytes = bytes;
|
|
1006
|
-
if (total !== null) item.total = total;
|
|
1007
|
-
this.dirty = true;
|
|
1008
|
-
}
|
|
1009
|
-
finish(id, ok, text) {
|
|
1010
|
-
if (!this.interactive) {
|
|
1011
|
-
if (ok) console.log(` done ${text}`);
|
|
1012
|
-
else console.error(` FAILED ${text}`);
|
|
1013
|
-
return;
|
|
1014
|
-
}
|
|
1015
|
-
this.clear();
|
|
1016
|
-
const line = ok ? ` ${color.green("\u2713")} ${text}` : ` ${color.red("\u2717")} ${text}`;
|
|
1017
|
-
(ok ? process.stdout : process.stderr).write(`${line}
|
|
1018
|
-
`);
|
|
1019
|
-
this.active.delete(id);
|
|
1020
|
-
this.render();
|
|
1021
|
-
}
|
|
1022
|
-
stop() {
|
|
1023
|
-
if (this.timer) clearInterval(this.timer);
|
|
1024
|
-
this.timer = null;
|
|
1025
|
-
if (!this.interactive) return;
|
|
1026
|
-
this.clear();
|
|
1027
|
-
this.active.clear();
|
|
1028
|
-
this.statusLine = null;
|
|
1029
|
-
}
|
|
1030
|
-
clear() {
|
|
1031
|
-
if (this.renderedLines === 0) return;
|
|
1032
|
-
process.stdout.write(`\x1B[${this.renderedLines}A\x1B[J`);
|
|
1033
|
-
this.renderedLines = 0;
|
|
1034
|
-
}
|
|
1035
|
-
render() {
|
|
1036
|
-
if (!this.interactive) return;
|
|
1037
|
-
if (this.active.size === 0 && !this.statusLine) {
|
|
1038
|
-
this.clear();
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
const lines = [];
|
|
1042
|
-
if (this.statusLine) {
|
|
1043
|
-
const spinner = FRAMES[this.frame % FRAMES.length] ?? FRAMES[0];
|
|
1044
|
-
lines.push(` ${color.cyan(spinner ?? "")} ${color.dim(this.statusLine)}`);
|
|
1045
|
-
}
|
|
1046
|
-
for (const item of this.active.values()) {
|
|
1047
|
-
lines.push(` ${color.bold(item.label)}`);
|
|
1048
|
-
lines.push(` ${this.bar(item)}`);
|
|
1049
|
-
}
|
|
1050
|
-
this.clear();
|
|
1051
|
-
process.stdout.write(`${lines.join("\n")}
|
|
1052
|
-
`);
|
|
1053
|
-
this.renderedLines = lines.length;
|
|
1054
|
-
this.dirty = false;
|
|
1055
|
-
}
|
|
1056
|
-
bar(item) {
|
|
1057
|
-
const ratio = item.total && item.total > 0 ? Math.min(1, item.bytes / item.total) : null;
|
|
1058
|
-
const filled = ratio === null ? 0 : Math.round(ratio * BAR_WIDTH);
|
|
1059
|
-
const track = color.cyan("\u2588".repeat(filled)) + color.dim("\u2591".repeat(BAR_WIDTH - filled));
|
|
1060
|
-
const percent = ratio === null ? " ? " : `${Math.floor(ratio * 100)}%`.padStart(4);
|
|
1061
|
-
const size = item.total ? `${formatBytes(item.bytes)} / ${formatBytes(item.total)}` : formatBytes(item.bytes);
|
|
1062
|
-
const speed = item.speed > 0 ? `${formatBytes(item.speed)}/s` : "";
|
|
1063
|
-
return `${track} ${percent} ${size} ${color.dim(speed)}`.trimEnd();
|
|
1064
|
-
}
|
|
1065
|
-
};
|
|
1066
|
-
|
|
1067
1098
|
// src/commands/selection-prompt.ts
|
|
1068
1099
|
import { stdin as stdin2, stdout as stdout2 } from "node:process";
|
|
1069
1100
|
import { createInterface } from "node:readline/promises";
|
|
@@ -1192,7 +1223,7 @@ async function syncCommand(flags) {
|
|
|
1192
1223
|
try {
|
|
1193
1224
|
if (flags.interactive === "true") {
|
|
1194
1225
|
view.stop();
|
|
1195
|
-
manifest = await client.fetchManifest();
|
|
1226
|
+
manifest = await view.withStatus("Fetching workspace\u2026", () => client.fetchManifest());
|
|
1196
1227
|
const prompted = await promptSelection(manifest);
|
|
1197
1228
|
if (!prompted) {
|
|
1198
1229
|
console.log("Cancelled. Nothing downloaded.");
|
|
@@ -1273,12 +1304,12 @@ async function revealBanner() {
|
|
|
1273
1304
|
for (let index = 0; index < WORDMARK.length; index += 1) {
|
|
1274
1305
|
process.stdout.write(`${shade(WORDMARK[index] ?? "", index)}
|
|
1275
1306
|
`);
|
|
1276
|
-
if (animationsEnabled) await
|
|
1307
|
+
if (animationsEnabled) await sleep2(38);
|
|
1277
1308
|
}
|
|
1278
1309
|
process.stdout.write(`${color.dim(" mezzanine sync for your Mac")}
|
|
1279
1310
|
|
|
1280
1311
|
`);
|
|
1281
|
-
if (animationsEnabled) await
|
|
1312
|
+
if (animationsEnabled) await sleep2(60);
|
|
1282
1313
|
}
|
|
1283
1314
|
|
|
1284
1315
|
// src/commands/welcome.ts
|
|
@@ -1300,12 +1331,17 @@ async function whoamiCommand(flags) {
|
|
|
1300
1331
|
return 1;
|
|
1301
1332
|
}
|
|
1302
1333
|
const client = new CutsioClient(resolveApiUrl(flags.url), apiKey);
|
|
1334
|
+
const view = new ProgressView();
|
|
1303
1335
|
try {
|
|
1304
|
-
console.log(
|
|
1336
|
+
console.log(
|
|
1337
|
+
JSON.stringify(await view.withStatus("Checking key\u2026", () => client.whoami()), null, 2)
|
|
1338
|
+
);
|
|
1305
1339
|
return 0;
|
|
1306
1340
|
} catch (error) {
|
|
1307
1341
|
console.error(error instanceof Error ? error.message : String(error));
|
|
1308
1342
|
return 1;
|
|
1343
|
+
} finally {
|
|
1344
|
+
view.stop();
|
|
1309
1345
|
}
|
|
1310
1346
|
}
|
|
1311
1347
|
|