effective-progress 0.10.0 → 0.11.0
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/README.md +116 -11
- package/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/index.d.mts +163 -14
- package/dist/index.mjs +462 -546
- package/package.json +4 -2
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-DQk6qfdC.mjs";
|
|
1
2
|
import { Brand, Cause, Clock, Context, Effect, Exit, FiberRef, Layer, Option, Schema } from "effect";
|
|
2
3
|
import { dual } from "effect/Function";
|
|
3
|
-
import { Box, Text, render } from "ink";
|
|
4
|
-
import
|
|
4
|
+
import { Box, Text, render, useBoxMetrics } from "ink";
|
|
5
|
+
import { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
5
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
7
|
import cliSpinners from "cli-spinners";
|
|
7
|
-
import
|
|
8
|
-
import { VirtualList } from "ink-virtual-list";
|
|
8
|
+
import stringWidth from "fast-string-width";
|
|
9
9
|
|
|
10
10
|
//#region src/types.ts
|
|
11
11
|
const TaskIdSchema = Schema.Number.pipe(Schema.brand("TaskId"));
|
|
@@ -27,7 +27,8 @@ const TaskSnapshotSchema = Schema.Struct({
|
|
|
27
27
|
transient: Schema.Boolean,
|
|
28
28
|
units: TaskUnitsSchema,
|
|
29
29
|
startedAt: Schema.Number,
|
|
30
|
-
completedAt: Schema.NullOr(Schema.Number)
|
|
30
|
+
completedAt: Schema.NullOr(Schema.Number),
|
|
31
|
+
metadata: Schema.Unknown
|
|
31
32
|
});
|
|
32
33
|
const TaskSnapshot = (snapshot) => snapshot;
|
|
33
34
|
var Task = class extends Context.Tag("stromseng.dev/effective-progress/Task")() {};
|
|
@@ -101,7 +102,8 @@ const updatedSnapshot = (snapshot, options) => {
|
|
|
101
102
|
transient: options.transient ?? snapshot.transient,
|
|
102
103
|
units,
|
|
103
104
|
startedAt: snapshot.startedAt,
|
|
104
|
-
completedAt: snapshot.completedAt
|
|
105
|
+
completedAt: snapshot.completedAt,
|
|
106
|
+
metadata: snapshot.metadata
|
|
105
107
|
});
|
|
106
108
|
};
|
|
107
109
|
const withTransient = (snapshot, transient) => TaskSnapshot({
|
|
@@ -113,7 +115,8 @@ const withTransient = (snapshot, transient) => TaskSnapshot({
|
|
|
113
115
|
transient,
|
|
114
116
|
units: snapshot.units,
|
|
115
117
|
startedAt: snapshot.startedAt,
|
|
116
|
-
completedAt: snapshot.completedAt
|
|
118
|
+
completedAt: snapshot.completedAt,
|
|
119
|
+
metadata: snapshot.metadata
|
|
117
120
|
});
|
|
118
121
|
const findInsertionIndex = (renderOrder, parentId) => {
|
|
119
122
|
if (parentId === null) return {
|
|
@@ -156,7 +159,8 @@ const makeProgressRenderStore = () => {
|
|
|
156
159
|
let nextTaskId = 0;
|
|
157
160
|
let state = {
|
|
158
161
|
tasks: /* @__PURE__ */ new Map(),
|
|
159
|
-
renderOrder: []
|
|
162
|
+
renderOrder: [],
|
|
163
|
+
columns: /* @__PURE__ */ new Map()
|
|
160
164
|
};
|
|
161
165
|
let pendingEvents = [];
|
|
162
166
|
let publishedPublication = {
|
|
@@ -245,7 +249,8 @@ const makeProgressRenderStore = () => {
|
|
|
245
249
|
transient: (parentSnapshot?.transient ?? false) || (options.transient ?? false),
|
|
246
250
|
units,
|
|
247
251
|
startedAt: now,
|
|
248
|
-
completedAt: null
|
|
252
|
+
completedAt: null,
|
|
253
|
+
metadata: options.metadata
|
|
249
254
|
});
|
|
250
255
|
updateState((current) => {
|
|
251
256
|
const nextTasks = new Map(current.tasks);
|
|
@@ -259,7 +264,8 @@ const makeProgressRenderStore = () => {
|
|
|
259
264
|
return {
|
|
260
265
|
state: {
|
|
261
266
|
tasks: nextTasks,
|
|
262
|
-
renderOrder: nextRenderOrder
|
|
267
|
+
renderOrder: nextRenderOrder,
|
|
268
|
+
columns: options.columns ? new Map(current.columns).set(taskId, options.columns) : current.columns
|
|
263
269
|
},
|
|
264
270
|
events: [new TaskAddedEvent({
|
|
265
271
|
taskId,
|
|
@@ -306,7 +312,8 @@ const makeProgressRenderStore = () => {
|
|
|
306
312
|
return {
|
|
307
313
|
state: {
|
|
308
314
|
tasks: nextTasks,
|
|
309
|
-
renderOrder: current.renderOrder
|
|
315
|
+
renderOrder: current.renderOrder,
|
|
316
|
+
columns: current.columns
|
|
310
317
|
},
|
|
311
318
|
events
|
|
312
319
|
};
|
|
@@ -333,12 +340,14 @@ const makeProgressRenderStore = () => {
|
|
|
333
340
|
total: currentTask.units.total
|
|
334
341
|
}),
|
|
335
342
|
startedAt: currentTask.startedAt,
|
|
336
|
-
completedAt: currentTask.completedAt
|
|
343
|
+
completedAt: currentTask.completedAt,
|
|
344
|
+
metadata: currentTask.metadata
|
|
337
345
|
}));
|
|
338
346
|
return {
|
|
339
347
|
state: {
|
|
340
348
|
tasks: nextTasks,
|
|
341
|
-
renderOrder: current.renderOrder
|
|
349
|
+
renderOrder: current.renderOrder,
|
|
350
|
+
columns: current.columns
|
|
342
351
|
},
|
|
343
352
|
events: [new TaskAdvancedEvent({
|
|
344
353
|
taskId,
|
|
@@ -369,12 +378,14 @@ const makeProgressRenderStore = () => {
|
|
|
369
378
|
total: currentTask.units.total
|
|
370
379
|
}),
|
|
371
380
|
startedAt: currentTask.startedAt,
|
|
372
|
-
completedAt: currentTask.completedAt
|
|
381
|
+
completedAt: currentTask.completedAt,
|
|
382
|
+
metadata: currentTask.metadata
|
|
373
383
|
}));
|
|
374
384
|
return {
|
|
375
385
|
state: {
|
|
376
386
|
tasks: nextTasks,
|
|
377
|
-
renderOrder: current.renderOrder
|
|
387
|
+
renderOrder: current.renderOrder,
|
|
388
|
+
columns: current.columns
|
|
378
389
|
},
|
|
379
390
|
events: [new TaskAdvancedEvent({
|
|
380
391
|
taskId,
|
|
@@ -392,14 +403,21 @@ const makeProgressRenderStore = () => {
|
|
|
392
403
|
state: current,
|
|
393
404
|
events: []
|
|
394
405
|
};
|
|
406
|
+
if (currentTask.status !== "running") return {
|
|
407
|
+
state: current,
|
|
408
|
+
events: []
|
|
409
|
+
};
|
|
395
410
|
const nextTasks = new Map(current.tasks);
|
|
396
411
|
if (currentTask.transient) {
|
|
397
412
|
const removedTaskIds = subtreeTaskIds(current.renderOrder, taskId);
|
|
398
413
|
for (const removedTaskId of removedTaskIds) nextTasks.delete(removedTaskId);
|
|
414
|
+
const nextColumns = new Map(current.columns);
|
|
415
|
+
for (const removedTaskId of removedTaskIds) nextColumns.delete(removedTaskId);
|
|
399
416
|
return {
|
|
400
417
|
state: {
|
|
401
418
|
tasks: nextTasks,
|
|
402
|
-
renderOrder: removeFromRenderOrder(current.renderOrder, taskId)
|
|
419
|
+
renderOrder: removeFromRenderOrder(current.renderOrder, taskId),
|
|
420
|
+
columns: nextColumns
|
|
403
421
|
},
|
|
404
422
|
events: [new TaskCompletedEvent({ taskId }), ...removedTaskIds.map((removedTaskId) => new TaskRemovedEvent({ taskId: removedTaskId }))]
|
|
405
423
|
};
|
|
@@ -421,12 +439,14 @@ const makeProgressRenderStore = () => {
|
|
|
421
439
|
total: currentTask.units.processed
|
|
422
440
|
}) : currentTask.units,
|
|
423
441
|
startedAt: currentTask.startedAt,
|
|
424
|
-
completedAt: now
|
|
442
|
+
completedAt: now,
|
|
443
|
+
metadata: currentTask.metadata
|
|
425
444
|
}));
|
|
426
445
|
return {
|
|
427
446
|
state: {
|
|
428
447
|
tasks: nextTasks,
|
|
429
|
-
renderOrder: current.renderOrder
|
|
448
|
+
renderOrder: current.renderOrder,
|
|
449
|
+
columns: current.columns
|
|
430
450
|
},
|
|
431
451
|
events: [new TaskCompletedEvent({ taskId })]
|
|
432
452
|
};
|
|
@@ -440,14 +460,21 @@ const makeProgressRenderStore = () => {
|
|
|
440
460
|
state: current,
|
|
441
461
|
events: []
|
|
442
462
|
};
|
|
463
|
+
if (currentTask.status !== "running") return {
|
|
464
|
+
state: current,
|
|
465
|
+
events: []
|
|
466
|
+
};
|
|
443
467
|
const nextTasks = new Map(current.tasks);
|
|
444
468
|
if (currentTask.transient) {
|
|
445
469
|
const removedTaskIds = subtreeTaskIds(current.renderOrder, taskId);
|
|
446
470
|
for (const removedTaskId of removedTaskIds) nextTasks.delete(removedTaskId);
|
|
471
|
+
const nextColumns = new Map(current.columns);
|
|
472
|
+
for (const removedTaskId of removedTaskIds) nextColumns.delete(removedTaskId);
|
|
447
473
|
return {
|
|
448
474
|
state: {
|
|
449
475
|
tasks: nextTasks,
|
|
450
|
-
renderOrder: removeFromRenderOrder(current.renderOrder, taskId)
|
|
476
|
+
renderOrder: removeFromRenderOrder(current.renderOrder, taskId),
|
|
477
|
+
columns: nextColumns
|
|
451
478
|
},
|
|
452
479
|
events: [new TaskFailedEvent({ taskId }), ...removedTaskIds.map((removedTaskId) => new TaskRemovedEvent({ taskId: removedTaskId }))]
|
|
453
480
|
};
|
|
@@ -461,22 +488,133 @@ const makeProgressRenderStore = () => {
|
|
|
461
488
|
transient: currentTask.transient,
|
|
462
489
|
units: currentTask.units,
|
|
463
490
|
startedAt: currentTask.startedAt,
|
|
464
|
-
completedAt: now
|
|
491
|
+
completedAt: now,
|
|
492
|
+
metadata: currentTask.metadata
|
|
465
493
|
}));
|
|
466
494
|
return {
|
|
467
495
|
state: {
|
|
468
496
|
tasks: nextTasks,
|
|
469
|
-
renderOrder: current.renderOrder
|
|
497
|
+
renderOrder: current.renderOrder,
|
|
498
|
+
columns: current.columns
|
|
470
499
|
},
|
|
471
500
|
events: [new TaskFailedEvent({ taskId })]
|
|
472
501
|
};
|
|
473
502
|
});
|
|
474
503
|
}),
|
|
475
504
|
getTask: (taskId) => Effect.sync(() => Option.fromNullable(state.tasks.get(taskId))),
|
|
476
|
-
listTasks: Effect.sync(() => Array.from(state.tasks.values()))
|
|
505
|
+
listTasks: Effect.sync(() => Array.from(state.tasks.values())),
|
|
506
|
+
setMetadata: (taskId, metadata) => Effect.sync(() => {
|
|
507
|
+
updateState((current) => {
|
|
508
|
+
const currentTask = current.tasks.get(taskId);
|
|
509
|
+
if (!currentTask) return {
|
|
510
|
+
state: current,
|
|
511
|
+
events: []
|
|
512
|
+
};
|
|
513
|
+
const nextTasks = new Map(current.tasks);
|
|
514
|
+
nextTasks.set(taskId, TaskSnapshot({
|
|
515
|
+
id: currentTask.id,
|
|
516
|
+
parentId: currentTask.parentId,
|
|
517
|
+
description: currentTask.description,
|
|
518
|
+
status: currentTask.status,
|
|
519
|
+
countDisplay: currentTask.countDisplay,
|
|
520
|
+
transient: currentTask.transient,
|
|
521
|
+
units: currentTask.units,
|
|
522
|
+
startedAt: currentTask.startedAt,
|
|
523
|
+
completedAt: currentTask.completedAt,
|
|
524
|
+
metadata
|
|
525
|
+
}));
|
|
526
|
+
return {
|
|
527
|
+
state: {
|
|
528
|
+
tasks: nextTasks,
|
|
529
|
+
renderOrder: current.renderOrder,
|
|
530
|
+
columns: current.columns
|
|
531
|
+
},
|
|
532
|
+
events: []
|
|
533
|
+
};
|
|
534
|
+
});
|
|
535
|
+
}),
|
|
536
|
+
getMetadata: (taskId) => Effect.sync(() => {
|
|
537
|
+
return state.tasks.get(taskId)?.metadata;
|
|
538
|
+
})
|
|
477
539
|
};
|
|
478
540
|
};
|
|
479
541
|
|
|
542
|
+
//#endregion
|
|
543
|
+
//#region src/renderer/hooks/use-now-clock.ts
|
|
544
|
+
const useNowClock = (active, intervalMillis) => {
|
|
545
|
+
const [now, setNow] = useState(() => Date.now());
|
|
546
|
+
useEffect(() => {
|
|
547
|
+
if (!active) return;
|
|
548
|
+
setNow(Date.now());
|
|
549
|
+
const interval = setInterval(() => {
|
|
550
|
+
setNow(Date.now());
|
|
551
|
+
}, intervalMillis);
|
|
552
|
+
return () => {
|
|
553
|
+
clearInterval(interval);
|
|
554
|
+
};
|
|
555
|
+
}, [active, intervalMillis]);
|
|
556
|
+
return now;
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
//#endregion
|
|
560
|
+
//#region src/renderer/context/now-context.tsx
|
|
561
|
+
const NOW_INTERVAL_MILLIS = 1e3;
|
|
562
|
+
const NowContext = createContext(Date.now());
|
|
563
|
+
const NowProvider = ({ active, children, nowOverride }) => {
|
|
564
|
+
const liveNow = useNowClock(active, NOW_INTERVAL_MILLIS);
|
|
565
|
+
const now = nowOverride ?? liveNow;
|
|
566
|
+
return /* @__PURE__ */ jsx(NowContext.Provider, {
|
|
567
|
+
value: now,
|
|
568
|
+
children
|
|
569
|
+
});
|
|
570
|
+
};
|
|
571
|
+
const useNow = () => useContext(NowContext);
|
|
572
|
+
|
|
573
|
+
//#endregion
|
|
574
|
+
//#region src/renderer/hooks/use-spinner-clock.ts
|
|
575
|
+
const normalizeIntervalMillis = (intervalMillis) => Math.max(1, intervalMillis);
|
|
576
|
+
const getSpinnerTickAtTime = (baseTick, startedAt, now, intervalMillis) => {
|
|
577
|
+
const elapsedMillis = Math.max(0, now - startedAt);
|
|
578
|
+
return baseTick + Math.floor(elapsedMillis / normalizeIntervalMillis(intervalMillis));
|
|
579
|
+
};
|
|
580
|
+
const useSpinnerClock = (active, intervalMillis) => {
|
|
581
|
+
const [tick, setTick] = useState(0);
|
|
582
|
+
const tickRef = useRef(tick);
|
|
583
|
+
useEffect(() => {
|
|
584
|
+
tickRef.current = tick;
|
|
585
|
+
}, [tick]);
|
|
586
|
+
useEffect(() => {
|
|
587
|
+
if (!active) return;
|
|
588
|
+
const baseTick = tickRef.current;
|
|
589
|
+
const startedAt = Date.now();
|
|
590
|
+
const updateTick = () => {
|
|
591
|
+
setTick(getSpinnerTickAtTime(baseTick, startedAt, Date.now(), intervalMillis));
|
|
592
|
+
};
|
|
593
|
+
updateTick();
|
|
594
|
+
const interval = setInterval(() => {
|
|
595
|
+
updateTick();
|
|
596
|
+
}, normalizeIntervalMillis(intervalMillis));
|
|
597
|
+
return () => {
|
|
598
|
+
clearInterval(interval);
|
|
599
|
+
};
|
|
600
|
+
}, [active, intervalMillis]);
|
|
601
|
+
return tick;
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
//#endregion
|
|
605
|
+
//#region src/renderer/context/spinner-context.tsx
|
|
606
|
+
const DEFAULT_SPINNER_INTERVAL_MILLIS = cliSpinners.dots.interval;
|
|
607
|
+
const SpinnerContext = createContext(0);
|
|
608
|
+
const SpinnerProvider = ({ active, children, intervalMillis = DEFAULT_SPINNER_INTERVAL_MILLIS, tickOverride }) => {
|
|
609
|
+
const liveTick = useSpinnerClock(active, intervalMillis);
|
|
610
|
+
const tick = tickOverride ?? liveTick;
|
|
611
|
+
return /* @__PURE__ */ jsx(SpinnerContext.Provider, {
|
|
612
|
+
value: tick,
|
|
613
|
+
children
|
|
614
|
+
});
|
|
615
|
+
};
|
|
616
|
+
const useSpinnerTick = () => useContext(SpinnerContext);
|
|
617
|
+
|
|
480
618
|
//#endregion
|
|
481
619
|
//#region src/renderer/shared/determinate.ts
|
|
482
620
|
const isDeterminate$2 = (task) => task.units.total !== void 0;
|
|
@@ -606,38 +744,17 @@ const renderAmount = (task, layout) => {
|
|
|
606
744
|
/* @__PURE__ */ jsx(Text, { children: ` ${processed}/${total}` })
|
|
607
745
|
] });
|
|
608
746
|
};
|
|
609
|
-
const
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
};
|
|
613
|
-
const createAmountColumn = (config) => {
|
|
614
|
-
const resolvedConfig = {
|
|
615
|
-
...defaultAmountColumnConfig,
|
|
616
|
-
...config
|
|
617
|
-
};
|
|
618
|
-
let amountLayout = emptyAmountLayout;
|
|
619
|
-
return {
|
|
620
|
-
Component: ({ row }) => /* @__PURE__ */ jsx(Text, {
|
|
621
|
-
wrap: "truncate-end",
|
|
622
|
-
children: renderAmount(row.task, amountLayout)
|
|
623
|
-
}),
|
|
624
|
-
measure: ({ rows }) => {
|
|
625
|
-
amountLayout = measureAmountLayout(rows);
|
|
626
|
-
return {
|
|
627
|
-
minWidth: amountLayout.preferredWidth > 0 ? Math.min(resolvedConfig.minWidth, amountLayout.preferredWidth) : 0,
|
|
628
|
-
preferredWidth: amountLayout.preferredWidth,
|
|
629
|
-
maxWidth: amountLayout.preferredWidth
|
|
630
|
-
};
|
|
631
|
-
},
|
|
632
|
-
noWrap: false,
|
|
633
|
-
sticky: resolvedConfig.sticky
|
|
634
|
-
};
|
|
635
|
-
};
|
|
747
|
+
const AmountCell = ({ task, layout }) => /* @__PURE__ */ jsx(Text, {
|
|
748
|
+
wrap: "truncate-end",
|
|
749
|
+
children: renderAmount(task, layout)
|
|
750
|
+
});
|
|
636
751
|
|
|
637
752
|
//#endregion
|
|
638
753
|
//#region src/renderer/columns/bar-column.tsx
|
|
639
|
-
const DEFAULT_BAR_WIDTH = 30;
|
|
640
754
|
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
755
|
+
const prepareBar = (rows) => {
|
|
756
|
+
return { hasDeterminateRows: rows.some((row) => row.derived.isDeterminate) };
|
|
757
|
+
};
|
|
641
758
|
const renderProgressBar = (task, width) => {
|
|
642
759
|
if (!isDeterminate$2(task)) return /* @__PURE__ */ jsx(Text, { children: ` `.repeat(Math.max(0, width)) });
|
|
643
760
|
const displayTotal = Math.max(task.units.total, task.units.succeeded + task.units.failed);
|
|
@@ -664,94 +781,21 @@ const renderProgressBar = (task, width) => {
|
|
|
664
781
|
]
|
|
665
782
|
});
|
|
666
783
|
};
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
};
|
|
672
|
-
const createBarColumn = (config) => {
|
|
673
|
-
const resolvedConfig = {
|
|
674
|
-
...defaultBarColumnConfig,
|
|
675
|
-
...config
|
|
676
|
-
};
|
|
677
|
-
return {
|
|
678
|
-
Component: ({ row, width }) => /* @__PURE__ */ jsx(Text, {
|
|
679
|
-
wrap: "truncate-end",
|
|
680
|
-
children: renderProgressBar(row.task, width)
|
|
681
|
-
}),
|
|
682
|
-
measure: ({ rows }) => {
|
|
683
|
-
const hasDeterminateRows = rows.some((row) => row.derived.isDeterminate);
|
|
684
|
-
const preferredWidth = hasDeterminateRows ? resolvedConfig.barWidth : 0;
|
|
685
|
-
return {
|
|
686
|
-
minWidth: hasDeterminateRows ? Math.min(resolvedConfig.minWidth, preferredWidth) : 0,
|
|
687
|
-
preferredWidth,
|
|
688
|
-
maxWidth: preferredWidth
|
|
689
|
-
};
|
|
690
|
-
},
|
|
691
|
-
noWrap: false,
|
|
692
|
-
sticky: resolvedConfig.sticky
|
|
693
|
-
};
|
|
694
|
-
};
|
|
695
|
-
|
|
696
|
-
//#endregion
|
|
697
|
-
//#region src/renderer/hooks/use-spinner-clock.ts
|
|
698
|
-
const normalizeIntervalMillis = (intervalMillis) => Math.max(1, intervalMillis);
|
|
699
|
-
const getSpinnerTickAtTime = (baseTick, startedAt, now, intervalMillis) => {
|
|
700
|
-
const elapsedMillis = Math.max(0, now - startedAt);
|
|
701
|
-
return baseTick + Math.floor(elapsedMillis / normalizeIntervalMillis(intervalMillis));
|
|
702
|
-
};
|
|
703
|
-
const useSpinnerClock = (active, intervalMillis) => {
|
|
704
|
-
const [tick, setTick] = useState(0);
|
|
705
|
-
const tickRef = useRef(tick);
|
|
706
|
-
useEffect(() => {
|
|
707
|
-
tickRef.current = tick;
|
|
708
|
-
}, [tick]);
|
|
709
|
-
useEffect(() => {
|
|
710
|
-
if (!active) return;
|
|
711
|
-
const baseTick = tickRef.current;
|
|
712
|
-
const startedAt = Date.now();
|
|
713
|
-
const updateTick = () => {
|
|
714
|
-
setTick(getSpinnerTickAtTime(baseTick, startedAt, Date.now(), intervalMillis));
|
|
715
|
-
};
|
|
716
|
-
updateTick();
|
|
717
|
-
const interval = setInterval(() => {
|
|
718
|
-
updateTick();
|
|
719
|
-
}, normalizeIntervalMillis(intervalMillis));
|
|
720
|
-
return () => {
|
|
721
|
-
clearInterval(interval);
|
|
722
|
-
};
|
|
723
|
-
}, [active, intervalMillis]);
|
|
724
|
-
return tick;
|
|
725
|
-
};
|
|
726
|
-
|
|
727
|
-
//#endregion
|
|
728
|
-
//#region src/renderer/context/spinner-context.tsx
|
|
729
|
-
const DEFAULT_SPINNER_INTERVAL_MILLIS = cliSpinners.dots.interval;
|
|
730
|
-
const SpinnerContext = createContext(0);
|
|
731
|
-
const SpinnerProvider = ({ active, children, intervalMillis = DEFAULT_SPINNER_INTERVAL_MILLIS, tickOverride }) => {
|
|
732
|
-
const liveTick = useSpinnerClock(active, intervalMillis);
|
|
733
|
-
const tick = tickOverride ?? liveTick;
|
|
734
|
-
return /* @__PURE__ */ jsx(SpinnerContext.Provider, {
|
|
735
|
-
value: tick,
|
|
736
|
-
children
|
|
737
|
-
});
|
|
738
|
-
};
|
|
739
|
-
const useSpinnerTick = () => useContext(SpinnerContext);
|
|
784
|
+
const BarCell = ({ task, width }) => /* @__PURE__ */ jsx(Text, {
|
|
785
|
+
wrap: "truncate-end",
|
|
786
|
+
children: renderProgressBar(task, width ?? 0)
|
|
787
|
+
});
|
|
740
788
|
|
|
741
789
|
//#endregion
|
|
742
790
|
//#region src/renderer/columns/description-column.tsx
|
|
743
791
|
const MIN_TREE_DESCRIPTION_TEXT_WIDTH = 6;
|
|
744
|
-
const
|
|
745
|
-
minWidth: 1,
|
|
746
|
-
spinnerType: "dots",
|
|
747
|
-
sticky: true
|
|
748
|
-
};
|
|
792
|
+
const DEFAULT_SPINNER_TYPE = "dots";
|
|
749
793
|
const isDeterminate = (task) => task.units.total !== void 0;
|
|
750
794
|
const getSpinnerFrame = (tick, spinnerType) => {
|
|
751
795
|
const frames = cliSpinners[spinnerType].frames;
|
|
752
796
|
return frames[(tick % frames.length + frames.length) % frames.length] ?? frames[0] ?? "";
|
|
753
797
|
};
|
|
754
|
-
const getTaskIndicator = (task, tick, spinnerType =
|
|
798
|
+
const getTaskIndicator = (task, tick, spinnerType = DEFAULT_SPINNER_TYPE) => {
|
|
755
799
|
if (task.status === "running") return {
|
|
756
800
|
symbol: getSpinnerFrame(tick, spinnerType),
|
|
757
801
|
color: "yellow"
|
|
@@ -782,412 +826,246 @@ const getTaskIndicator = (task, tick, spinnerType = defaultDescriptionColumnConf
|
|
|
782
826
|
color: "green"
|
|
783
827
|
};
|
|
784
828
|
};
|
|
785
|
-
const
|
|
786
|
-
|
|
829
|
+
const prepareDescription = (rows) => ({ minTreeWidth: rows.reduce((max, row) => Math.max(max, row.derived.treePrefixWidth + 2 + MIN_TREE_DESCRIPTION_TEXT_WIDTH), MIN_TREE_DESCRIPTION_TEXT_WIDTH + 2) });
|
|
830
|
+
const TaskIndicatorGlyph = ({ task, tick, spinnerType = DEFAULT_SPINNER_TYPE }) => {
|
|
831
|
+
const indicator = getTaskIndicator(task, tick, spinnerType);
|
|
787
832
|
return /* @__PURE__ */ jsx(Text, {
|
|
788
833
|
color: indicator.color,
|
|
789
834
|
children: indicator.symbol
|
|
790
835
|
});
|
|
791
836
|
};
|
|
792
|
-
const
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
treePrefix,
|
|
817
|
-
/* @__PURE__ */ jsx(TaskIndicatorGlyph, {
|
|
818
|
-
task: row.task,
|
|
819
|
-
spinnerType: resolvedConfig.spinnerType
|
|
820
|
-
}),
|
|
821
|
-
` ${row.task.description}`
|
|
822
|
-
]
|
|
823
|
-
});
|
|
824
|
-
};
|
|
825
|
-
return {
|
|
826
|
-
Component,
|
|
827
|
-
measure: ({ rows }) => {
|
|
828
|
-
const hasNestedRows = rows.some((row) => row.tree.depth > 0);
|
|
829
|
-
minTreeWidth = rows.reduce((max, row) => Math.max(max, row.derived.treePrefixWidth + 2 + MIN_TREE_DESCRIPTION_TEXT_WIDTH), MIN_TREE_DESCRIPTION_TEXT_WIDTH + 2);
|
|
830
|
-
return {
|
|
831
|
-
minWidth: resolvedConfig.minWidth,
|
|
832
|
-
preferredWidth: Math.max(rows.reduce((max, row) => Math.max(max, row.derived.treePrefixedDescriptionWidth + 2), resolvedConfig.minWidth), hasNestedRows ? minTreeWidth : resolvedConfig.minWidth),
|
|
833
|
-
maxWidth: void 0
|
|
834
|
-
};
|
|
835
|
-
},
|
|
836
|
-
noWrap: false,
|
|
837
|
-
sticky: resolvedConfig.sticky,
|
|
838
|
-
stickyMaxWidth: resolvedConfig.stickyMaxWidth
|
|
839
|
-
};
|
|
840
|
-
};
|
|
841
|
-
|
|
842
|
-
//#endregion
|
|
843
|
-
//#region src/renderer/hooks/use-now-clock.ts
|
|
844
|
-
const useNowClock = (active, intervalMillis) => {
|
|
845
|
-
const [now, setNow] = useState(() => Date.now());
|
|
846
|
-
useEffect(() => {
|
|
847
|
-
if (!active) return;
|
|
848
|
-
setNow(Date.now());
|
|
849
|
-
const interval = setInterval(() => {
|
|
850
|
-
setNow(Date.now());
|
|
851
|
-
}, intervalMillis);
|
|
852
|
-
return () => {
|
|
853
|
-
clearInterval(interval);
|
|
854
|
-
};
|
|
855
|
-
}, [active, intervalMillis]);
|
|
856
|
-
return now;
|
|
857
|
-
};
|
|
858
|
-
|
|
859
|
-
//#endregion
|
|
860
|
-
//#region src/renderer/context/now-context.tsx
|
|
861
|
-
const NOW_INTERVAL_MILLIS = 1e3;
|
|
862
|
-
const NowContext = createContext(Date.now());
|
|
863
|
-
const NowProvider = ({ active, children, nowOverride }) => {
|
|
864
|
-
const liveNow = useNowClock(active, NOW_INTERVAL_MILLIS);
|
|
865
|
-
const now = nowOverride ?? liveNow;
|
|
866
|
-
return /* @__PURE__ */ jsx(NowContext.Provider, {
|
|
867
|
-
value: now,
|
|
868
|
-
children
|
|
837
|
+
const DescriptionCell = ({ cell, width, minTreeWidth, spinnerTick }) => {
|
|
838
|
+
const showTree = width === void 0 || width >= minTreeWidth;
|
|
839
|
+
const treePrefix = showTree ? cell.derived.treePrefix : "";
|
|
840
|
+
if (!showTree && width !== void 0 && width <= 1) return /* @__PURE__ */ jsx(TaskIndicatorGlyph, {
|
|
841
|
+
task: cell.task,
|
|
842
|
+
tick: spinnerTick
|
|
843
|
+
});
|
|
844
|
+
if (!showTree && width !== void 0 && width === 2) return /* @__PURE__ */ jsxs(Text, {
|
|
845
|
+
wrap: "truncate-end",
|
|
846
|
+
children: [/* @__PURE__ */ jsx(TaskIndicatorGlyph, {
|
|
847
|
+
task: cell.task,
|
|
848
|
+
tick: spinnerTick
|
|
849
|
+
}), "…"]
|
|
850
|
+
});
|
|
851
|
+
return /* @__PURE__ */ jsxs(Text, {
|
|
852
|
+
wrap: "truncate-end",
|
|
853
|
+
children: [
|
|
854
|
+
treePrefix,
|
|
855
|
+
/* @__PURE__ */ jsx(TaskIndicatorGlyph, {
|
|
856
|
+
task: cell.task,
|
|
857
|
+
tick: spinnerTick
|
|
858
|
+
}),
|
|
859
|
+
` ${cell.task.description}`
|
|
860
|
+
]
|
|
869
861
|
});
|
|
870
862
|
};
|
|
871
|
-
const useNow = () => useContext(NowContext);
|
|
872
863
|
|
|
873
864
|
//#endregion
|
|
874
865
|
//#region src/renderer/columns/elapsed-column.tsx
|
|
875
|
-
const
|
|
876
|
-
minWidth: 2,
|
|
877
|
-
justify: "right",
|
|
878
|
-
sticky: true
|
|
879
|
-
};
|
|
880
|
-
const ElapsedText = ({ task, width }) => {
|
|
866
|
+
const ElapsedCell = ({ task, now }) => {
|
|
881
867
|
return /* @__PURE__ */ jsx(Text, {
|
|
882
868
|
wrap: "truncate-end",
|
|
883
869
|
color: "gray",
|
|
884
|
-
children: formatElapsed(task,
|
|
870
|
+
children: formatElapsed(task, now)
|
|
885
871
|
});
|
|
886
872
|
};
|
|
887
|
-
const createElapsedColumn = (config) => {
|
|
888
|
-
const resolvedConfig = {
|
|
889
|
-
...defaultElapsedColumnConfig,
|
|
890
|
-
...config
|
|
891
|
-
};
|
|
892
|
-
const Component = ({ row, width }) => /* @__PURE__ */ jsx(ElapsedText, {
|
|
893
|
-
task: row.task,
|
|
894
|
-
width
|
|
895
|
-
});
|
|
896
|
-
return {
|
|
897
|
-
Component,
|
|
898
|
-
measure: ({ rows, now }) => {
|
|
899
|
-
const width = rows.reduce((max, row) => Math.max(max, formatElapsed(row.task, now).length), resolvedConfig.minWidth);
|
|
900
|
-
return {
|
|
901
|
-
minWidth: resolvedConfig.minWidth,
|
|
902
|
-
preferredWidth: width,
|
|
903
|
-
maxWidth: width
|
|
904
|
-
};
|
|
905
|
-
},
|
|
906
|
-
getLayoutDependency: ({ rows, now }) => rows.reduce((max, row) => Math.max(max, formatElapsed(row.task, now).length), 0),
|
|
907
|
-
justify: resolvedConfig.justify,
|
|
908
|
-
noWrap: true,
|
|
909
|
-
sticky: resolvedConfig.sticky
|
|
910
|
-
};
|
|
911
|
-
};
|
|
912
873
|
|
|
913
874
|
//#endregion
|
|
914
875
|
//#region src/renderer/columns/eta-column.tsx
|
|
915
|
-
const
|
|
916
|
-
const
|
|
917
|
-
if (
|
|
918
|
-
return
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
return eta === "" ? void 0 : eta;
|
|
923
|
-
};
|
|
924
|
-
const renderEtaText = (row, now, width) => {
|
|
925
|
-
const duration = etaDurationText(row, now);
|
|
926
|
-
if (duration === void 0) return {
|
|
927
|
-
mode: "compact",
|
|
928
|
-
text: ""
|
|
929
|
-
};
|
|
930
|
-
const prefixed = `ETA: ${duration}`;
|
|
931
|
-
if (width >= prefixed.length) return {
|
|
932
|
-
mode: "prefixed",
|
|
933
|
-
text: prefixed
|
|
934
|
-
};
|
|
935
|
-
if (width >= duration.length) return {
|
|
936
|
-
mode: "duration",
|
|
937
|
-
text: duration
|
|
938
|
-
};
|
|
939
|
-
return {
|
|
940
|
-
mode: "compact",
|
|
941
|
-
text: cropTextToWidth(primaryUnit(duration), width)
|
|
942
|
-
};
|
|
943
|
-
};
|
|
944
|
-
const defaultEtaColumnConfig = {
|
|
945
|
-
minWidth: 3,
|
|
946
|
-
justify: "right",
|
|
947
|
-
sticky: true
|
|
948
|
-
};
|
|
949
|
-
const EtaText = ({ row, width, justify }) => {
|
|
950
|
-
const rendered = renderEtaText(row, useNow(), width);
|
|
951
|
-
return /* @__PURE__ */ jsx(Box, {
|
|
952
|
-
width,
|
|
953
|
-
justifyContent: rendered.mode === "prefixed" ? justify === "right" ? "flex-end" : "flex-start" : "flex-start",
|
|
954
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
955
|
-
wrap: "truncate-end",
|
|
956
|
-
color: "gray",
|
|
957
|
-
children: rendered.text
|
|
958
|
-
})
|
|
959
|
-
});
|
|
960
|
-
};
|
|
961
|
-
const createEtaColumn = (config) => {
|
|
962
|
-
const resolvedConfig = {
|
|
963
|
-
...defaultEtaColumnConfig,
|
|
964
|
-
...config
|
|
965
|
-
};
|
|
966
|
-
const Component = ({ row, width }) => /* @__PURE__ */ jsx(EtaText, {
|
|
967
|
-
row,
|
|
968
|
-
width,
|
|
969
|
-
justify: resolvedConfig.justify
|
|
876
|
+
const EtaCell = ({ task, now }) => {
|
|
877
|
+
const eta = formatEta(task, now);
|
|
878
|
+
if (eta === "") return null;
|
|
879
|
+
return /* @__PURE__ */ jsx(Text, {
|
|
880
|
+
wrap: "truncate-end",
|
|
881
|
+
color: "gray",
|
|
882
|
+
children: `ETA: ${eta}`
|
|
970
883
|
});
|
|
971
|
-
return {
|
|
972
|
-
Component,
|
|
973
|
-
measure: ({ rows, now }) => {
|
|
974
|
-
const width = rows.reduce((max, row) => {
|
|
975
|
-
const duration = etaDurationText(row, now);
|
|
976
|
-
if (duration === void 0) return max;
|
|
977
|
-
return Math.max(max, `ETA: ${duration}`.length);
|
|
978
|
-
}, resolvedConfig.minWidth);
|
|
979
|
-
return {
|
|
980
|
-
minWidth: resolvedConfig.minWidth,
|
|
981
|
-
preferredWidth: width,
|
|
982
|
-
maxWidth: width
|
|
983
|
-
};
|
|
984
|
-
},
|
|
985
|
-
getLayoutDependency: ({ rows, now }) => rows.reduce((max, row) => {
|
|
986
|
-
const duration = etaDurationText(row, now);
|
|
987
|
-
if (duration === void 0) return max;
|
|
988
|
-
return Math.max(max, `ETA: ${duration}`.length);
|
|
989
|
-
}, 0),
|
|
990
|
-
justify: resolvedConfig.justify,
|
|
991
|
-
noWrap: true,
|
|
992
|
-
sticky: resolvedConfig.sticky
|
|
993
|
-
};
|
|
994
884
|
};
|
|
995
885
|
|
|
996
886
|
//#endregion
|
|
997
|
-
//#region src/
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
887
|
+
//#region src/columns.tsx
|
|
888
|
+
var columns_exports = /* @__PURE__ */ __exportAll({
|
|
889
|
+
amount: () => amount,
|
|
890
|
+
bar: () => bar,
|
|
891
|
+
defaults: () => defaults,
|
|
892
|
+
description: () => description,
|
|
893
|
+
elapsed: () => elapsed,
|
|
894
|
+
eta: () => eta,
|
|
895
|
+
resolveColumnSizeValue: () => resolveColumnSizeValue,
|
|
896
|
+
spacer: () => spacer
|
|
897
|
+
});
|
|
898
|
+
const spacer = ({ flexGrow, flexShrink, flexBasis, minWidth } = {}) => ({
|
|
899
|
+
render: () => null,
|
|
900
|
+
flexGrow,
|
|
901
|
+
flexShrink,
|
|
902
|
+
flexBasis,
|
|
903
|
+
minWidth
|
|
904
|
+
});
|
|
905
|
+
const description = () => ({
|
|
906
|
+
prepare: prepareDescription,
|
|
907
|
+
flexGrow: 1,
|
|
908
|
+
flexShrink: 1,
|
|
909
|
+
minWidth: 1,
|
|
910
|
+
render: (cell, ctx) => /* @__PURE__ */ jsx(DescriptionCell, {
|
|
911
|
+
cell,
|
|
912
|
+
width: ctx.width,
|
|
913
|
+
minTreeWidth: ctx.prepared.minTreeWidth,
|
|
914
|
+
spinnerTick: ctx.spinnerTick
|
|
915
|
+
})
|
|
916
|
+
});
|
|
917
|
+
const bar = () => ({
|
|
918
|
+
prepare: prepareBar,
|
|
919
|
+
flexShrink: (prepared) => prepared.hasDeterminateRows ? 1 : 0,
|
|
920
|
+
flexBasis: (prepared) => prepared.hasDeterminateRows ? 30 : 0,
|
|
921
|
+
minWidth: (prepared) => prepared.hasDeterminateRows ? 4 : 0,
|
|
922
|
+
render: ({ task }, ctx) => /* @__PURE__ */ jsx(BarCell, {
|
|
923
|
+
task,
|
|
924
|
+
width: ctx.width
|
|
925
|
+
})
|
|
926
|
+
});
|
|
927
|
+
const amount = () => ({
|
|
928
|
+
prepare: measureAmountLayout,
|
|
929
|
+
align: "right",
|
|
930
|
+
render: ({ task }, ctx) => /* @__PURE__ */ jsx(AmountCell, {
|
|
931
|
+
task,
|
|
932
|
+
layout: ctx.prepared
|
|
933
|
+
})
|
|
934
|
+
});
|
|
935
|
+
const elapsed = () => ({
|
|
936
|
+
align: "right",
|
|
937
|
+
flexShrink: 0,
|
|
938
|
+
render: ({ task }, ctx) => /* @__PURE__ */ jsx(ElapsedCell, {
|
|
939
|
+
task,
|
|
940
|
+
now: ctx.now
|
|
941
|
+
})
|
|
942
|
+
});
|
|
943
|
+
const eta = () => ({
|
|
944
|
+
align: "right",
|
|
945
|
+
flexShrink: 0,
|
|
946
|
+
render: ({ task }, ctx) => /* @__PURE__ */ jsx(EtaCell, {
|
|
947
|
+
task,
|
|
948
|
+
now: ctx.now
|
|
949
|
+
})
|
|
950
|
+
});
|
|
951
|
+
const defaults = () => [
|
|
952
|
+
description(),
|
|
953
|
+
bar(),
|
|
954
|
+
amount(),
|
|
955
|
+
elapsed(),
|
|
956
|
+
eta()
|
|
1004
957
|
];
|
|
958
|
+
const resolveColumnSizeValue = (value, prepared) => {
|
|
959
|
+
if (typeof value === "function") return value(prepared);
|
|
960
|
+
return value;
|
|
961
|
+
};
|
|
1005
962
|
|
|
1006
963
|
//#endregion
|
|
1007
|
-
//#region src/renderer/
|
|
1008
|
-
const
|
|
1009
|
-
const
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
const measured = definition.measure(context);
|
|
1023
|
-
const minWidth = Math.max(0, measured.minWidth);
|
|
1024
|
-
const maxWidth = measured.maxWidth === void 0 ? void 0 : Math.max(minWidth, measured.maxWidth);
|
|
1025
|
-
const preferredWidth = clampWidth(measured.preferredWidth, minWidth, maxWidth);
|
|
1026
|
-
const stickyLimit = definition.sticky !== true ? void 0 : definition.stickyMaxWidth !== void 0 ? Math.max(minWidth, definition.stickyMaxWidth) : maxWidth;
|
|
1027
|
-
return {
|
|
1028
|
-
definition,
|
|
1029
|
-
minWidth,
|
|
1030
|
-
maxWidth,
|
|
1031
|
-
preferredWidth,
|
|
1032
|
-
sticky: definition.sticky === true,
|
|
1033
|
-
stickyLimit
|
|
1034
|
-
};
|
|
1035
|
-
};
|
|
1036
|
-
const visibleIndices = (widths) => widths.flatMap((width, index) => width > 0 ? [index] : []);
|
|
1037
|
-
const totalVisibleWidth = (widths) => {
|
|
1038
|
-
const indices = visibleIndices(widths);
|
|
1039
|
-
if (indices.length === 0) return 0;
|
|
1040
|
-
return indices.reduce((sum, index, visibleIndex) => {
|
|
1041
|
-
const padding = visibleIndex < indices.length - 1 ? RENDERER_COLUMN_GAP : 0;
|
|
1042
|
-
return sum + widths[index] + padding;
|
|
1043
|
-
}, 0);
|
|
1044
|
-
};
|
|
1045
|
-
const nextDistinctWidth = (entries, widest) => entries.find((entry) => entry.width < widest)?.width;
|
|
1046
|
-
const shrinkWidestFirst = (widths, columns, targetWidth, canShrink) => {
|
|
1047
|
-
while (totalVisibleWidth(widths) > targetWidth) {
|
|
1048
|
-
const shrinkable = widths.map((width, index) => ({
|
|
1049
|
-
index,
|
|
1050
|
-
width,
|
|
1051
|
-
minWidth: columns[index].minWidth
|
|
1052
|
-
})).filter(({ index, width, minWidth }) => width > minWidth && canShrink(index)).sort((left, right) => right.width - left.width || right.index - left.index);
|
|
1053
|
-
if (shrinkable.length === 0) break;
|
|
1054
|
-
const overflow = totalVisibleWidth(widths) - targetWidth;
|
|
1055
|
-
const widest = shrinkable[0].width;
|
|
1056
|
-
const cohort = shrinkable.filter((entry) => entry.width === widest);
|
|
1057
|
-
const maxUniformDrop = widest - Math.max(nextDistinctWidth(shrinkable, widest) ?? 0, ...cohort.map((entry) => entry.minWidth));
|
|
1058
|
-
const uniformDrop = Math.min(maxUniformDrop, Math.floor(overflow / cohort.length));
|
|
1059
|
-
if (uniformDrop > 0) {
|
|
1060
|
-
for (const { index } of cohort) widths[index] = Math.max(columns[index].minWidth, widths[index] - uniformDrop);
|
|
1061
|
-
continue;
|
|
1062
|
-
}
|
|
1063
|
-
let changed = false;
|
|
1064
|
-
for (const { index, minWidth } of cohort) {
|
|
1065
|
-
if (totalVisibleWidth(widths) <= targetWidth) break;
|
|
1066
|
-
if (widths[index] <= minWidth) continue;
|
|
1067
|
-
widths[index] = widths[index] - 1;
|
|
1068
|
-
changed = true;
|
|
1069
|
-
}
|
|
1070
|
-
if (!changed) break;
|
|
1071
|
-
}
|
|
1072
|
-
return widths;
|
|
1073
|
-
};
|
|
1074
|
-
const forceFitFromRight = (widths, columns, targetWidth, canShrink) => {
|
|
1075
|
-
while (totalVisibleWidth(widths) > targetWidth) {
|
|
1076
|
-
const rightmostVisibleIndex = widths.findLastIndex((width, index) => width > 0 && canShrink(index));
|
|
1077
|
-
if (rightmostVisibleIndex < 0) break;
|
|
1078
|
-
widths[rightmostVisibleIndex] = widths[rightmostVisibleIndex] - 1;
|
|
1079
|
-
}
|
|
1080
|
-
return widths;
|
|
1081
|
-
};
|
|
1082
|
-
const applyStickyGrowth = (widths, columns, targetWidth, previousStickyWidths) => {
|
|
1083
|
-
const stickyTargets = columns.map((column, index) => {
|
|
1084
|
-
if (!column.sticky) return;
|
|
1085
|
-
return clampWidth(Math.max(previousStickyWidths.get(index) ?? 0, column.preferredWidth), column.minWidth, column.stickyLimit);
|
|
964
|
+
//#region src/renderer/column-resolver.ts
|
|
965
|
+
const NO_PREPARE = Symbol("no-prepare");
|
|
966
|
+
const getColumnsForRow = (row, columns) => columns.get(row.task.id) ?? defaults();
|
|
967
|
+
const toCellInfo = (row) => row;
|
|
968
|
+
const maxDefined = (values) => values.reduce((maxValue, value) => value === void 0 ? maxValue : Math.max(maxValue ?? Number.NEGATIVE_INFINITY, value), void 0);
|
|
969
|
+
const resolvePreparedGroups = (defs, cellInfos) => {
|
|
970
|
+
const groupedRows = /* @__PURE__ */ new Map();
|
|
971
|
+
defs.forEach((def, rowIndex) => {
|
|
972
|
+
if (!def?.prepare) return;
|
|
973
|
+
const key = def.prepare;
|
|
974
|
+
const rows = groupedRows.get(key);
|
|
975
|
+
const cell = cellInfos[rowIndex];
|
|
976
|
+
if (!cell) return;
|
|
977
|
+
if (rows) rows.push(cell);
|
|
978
|
+
else groupedRows.set(key, [cell]);
|
|
1086
979
|
});
|
|
1087
|
-
|
|
1088
|
-
|
|
980
|
+
const groups = [{
|
|
981
|
+
key: NO_PREPARE,
|
|
982
|
+
prepared: void 0
|
|
983
|
+
}];
|
|
984
|
+
for (const [key, rows] of groupedRows) groups.push({
|
|
985
|
+
key,
|
|
986
|
+
prepared: key(rows)
|
|
1089
987
|
});
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
988
|
+
return groups;
|
|
989
|
+
};
|
|
990
|
+
const getPreparedFor = (def, groups) => {
|
|
991
|
+
if (!def?.prepare) return;
|
|
992
|
+
return groups.find((group) => group.key === def.prepare)?.prepared;
|
|
993
|
+
};
|
|
994
|
+
const resolveColumns = (rows, columns) => {
|
|
995
|
+
const columnsByRow = rows.map((row) => getColumnsForRow(row, columns));
|
|
996
|
+
const cellInfos = rows.map(toCellInfo);
|
|
997
|
+
const maxColumnCount = columnsByRow.reduce((max, defs) => Math.max(max, defs.length), 0);
|
|
998
|
+
return Array.from({ length: maxColumnCount }, (_, index) => {
|
|
999
|
+
const defsAtIndex = columnsByRow.map((defs) => defs[index]);
|
|
1000
|
+
const preparedGroups = resolvePreparedGroups(defsAtIndex, cellInfos);
|
|
1001
|
+
const entries = defsAtIndex.map((column) => column === void 0 ? void 0 : {
|
|
1002
|
+
column,
|
|
1003
|
+
prepared: getPreparedFor(column, preparedGroups)
|
|
1098
1004
|
});
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1005
|
+
return {
|
|
1006
|
+
index,
|
|
1007
|
+
rows,
|
|
1008
|
+
entries,
|
|
1009
|
+
flexGrow: maxDefined(entries.map((entry) => entry === void 0 ? void 0 : resolveColumnSizeValue(entry.column.flexGrow, entry.prepared))),
|
|
1010
|
+
flexShrink: maxDefined(entries.map((entry) => entry === void 0 ? void 0 : resolveColumnSizeValue(entry.column.flexShrink, entry.prepared))),
|
|
1011
|
+
flexBasis: maxDefined(entries.map((entry) => entry === void 0 ? void 0 : resolveColumnSizeValue(entry.column.flexBasis, entry.prepared))),
|
|
1012
|
+
minWidth: maxDefined(entries.map((entry) => entry === void 0 ? void 0 : resolveColumnSizeValue(entry.column.minWidth, entry.prepared)))
|
|
1013
|
+
};
|
|
1104
1014
|
});
|
|
1105
|
-
return {
|
|
1106
|
-
widths,
|
|
1107
|
-
nextStickyWidths
|
|
1108
|
-
};
|
|
1109
|
-
};
|
|
1110
|
-
const planColumnLayout = (definitions, rows, now, terminalColumns, previousStickyWidths) => {
|
|
1111
|
-
const measureContext = {
|
|
1112
|
-
rows,
|
|
1113
|
-
now
|
|
1114
|
-
};
|
|
1115
|
-
const columns = definitions.map((definition) => normalizeMeasurement(definition, measureContext));
|
|
1116
|
-
let widths = columns.map((column) => column.preferredWidth);
|
|
1117
|
-
if (terminalColumns !== void 0) {
|
|
1118
|
-
widths = shrinkWidestFirst([...widths], columns, terminalColumns, (index) => columns[index].definition.fixedWidth === void 0);
|
|
1119
|
-
if (totalVisibleWidth(widths) > terminalColumns) widths = forceFitFromRight([...widths], columns, terminalColumns, (index) => columns[index].definition.fixedWidth === void 0);
|
|
1120
|
-
}
|
|
1121
|
-
const stickyResult = applyStickyGrowth(widths, columns, terminalColumns, previousStickyWidths);
|
|
1122
|
-
return {
|
|
1123
|
-
columns: stickyResult.widths.map((width, index) => ({
|
|
1124
|
-
definition: columns[index].definition,
|
|
1125
|
-
width
|
|
1126
|
-
})).filter(({ width }) => width > 0),
|
|
1127
|
-
nextStickyWidths: stickyResult.nextStickyWidths
|
|
1128
|
-
};
|
|
1129
1015
|
};
|
|
1130
1016
|
|
|
1131
1017
|
//#endregion
|
|
1132
1018
|
//#region src/renderer/public-api.tsx
|
|
1133
|
-
const
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
renderItem: ({ item: row, index: rowIndex }) => /* @__PURE__ */ jsx(ProgressRow, {
|
|
1184
|
-
row,
|
|
1185
|
-
rowIndex,
|
|
1186
|
-
terminalColumns,
|
|
1187
|
-
columns: layout.columns
|
|
1188
|
-
})
|
|
1189
|
-
});
|
|
1190
|
-
};
|
|
1019
|
+
const justifyContentForAlign = (align) => {
|
|
1020
|
+
if (align === "right") return "flex-end";
|
|
1021
|
+
if (align === "center") return "center";
|
|
1022
|
+
return "flex-start";
|
|
1023
|
+
};
|
|
1024
|
+
const renderNode = (node) => {
|
|
1025
|
+
if (typeof node === "string" || typeof node === "number") return /* @__PURE__ */ jsx(Text, {
|
|
1026
|
+
wrap: "truncate-end",
|
|
1027
|
+
children: node
|
|
1028
|
+
});
|
|
1029
|
+
return node;
|
|
1030
|
+
};
|
|
1031
|
+
const ColumnPosition = ({ position }) => {
|
|
1032
|
+
const ref = useRef(null);
|
|
1033
|
+
const { width, hasMeasured } = useBoxMetrics(ref);
|
|
1034
|
+
const now = useNow();
|
|
1035
|
+
const spinnerTick = useSpinnerTick();
|
|
1036
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
1037
|
+
ref,
|
|
1038
|
+
flexDirection: "column",
|
|
1039
|
+
flexGrow: position.flexGrow,
|
|
1040
|
+
flexShrink: position.flexShrink ?? 0,
|
|
1041
|
+
flexBasis: position.flexBasis,
|
|
1042
|
+
minWidth: position.minWidth,
|
|
1043
|
+
children: position.rows.map((row, rowIndex) => {
|
|
1044
|
+
const entry = position.entries[rowIndex];
|
|
1045
|
+
const column = entry?.column;
|
|
1046
|
+
const cell = row;
|
|
1047
|
+
const output = column?.render(cell, {
|
|
1048
|
+
width: hasMeasured ? width : void 0,
|
|
1049
|
+
now,
|
|
1050
|
+
spinnerTick,
|
|
1051
|
+
prepared: entry?.prepared
|
|
1052
|
+
}) ?? null;
|
|
1053
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
1054
|
+
height: 1,
|
|
1055
|
+
justifyContent: justifyContentForAlign(column?.align),
|
|
1056
|
+
children: renderNode(output)
|
|
1057
|
+
}, row.task.id);
|
|
1058
|
+
})
|
|
1059
|
+
});
|
|
1060
|
+
};
|
|
1061
|
+
const ProgressRenderer = ({ rows, columns }) => {
|
|
1062
|
+
if (rows.length === 0) return null;
|
|
1063
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
1064
|
+
flexDirection: "row",
|
|
1065
|
+
columnGap: 1,
|
|
1066
|
+
overflow: "hidden",
|
|
1067
|
+
children: resolveColumns(rows, columns).map((position) => /* @__PURE__ */ jsx(ColumnPosition, { position }, position.index))
|
|
1068
|
+
});
|
|
1191
1069
|
};
|
|
1192
1070
|
|
|
1193
1071
|
//#endregion
|
|
@@ -1292,31 +1170,22 @@ const useProgressRenderView = (store) => {
|
|
|
1292
1170
|
//#endregion
|
|
1293
1171
|
//#region src/renderer/renderer-service.tsx
|
|
1294
1172
|
const MAX_FPS = 24;
|
|
1295
|
-
const
|
|
1296
|
-
const
|
|
1297
|
-
return
|
|
1298
|
-
|
|
1299
|
-
|
|
1173
|
+
const ProgressRoot = ({ store }) => {
|
|
1174
|
+
const { renderSnapshot, hasRunningTasks, publication } = useProgressRenderView(store);
|
|
1175
|
+
return /* @__PURE__ */ jsx(SpinnerProvider, {
|
|
1176
|
+
active: hasRunningTasks,
|
|
1177
|
+
children: /* @__PURE__ */ jsx(NowProvider, {
|
|
1300
1178
|
active: hasRunningTasks,
|
|
1301
|
-
children: /* @__PURE__ */ jsx(
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
rows: renderSnapshot.rows,
|
|
1305
|
-
terminalColumns: getTerminalColumns(),
|
|
1306
|
-
terminalRows: getTerminalRows()
|
|
1307
|
-
})
|
|
1179
|
+
children: /* @__PURE__ */ jsx(ProgressRenderer, {
|
|
1180
|
+
rows: renderSnapshot.rows,
|
|
1181
|
+
columns: publication.snapshot.columns
|
|
1308
1182
|
})
|
|
1309
|
-
})
|
|
1310
|
-
};
|
|
1183
|
+
})
|
|
1184
|
+
});
|
|
1311
1185
|
};
|
|
1312
|
-
const makeRendererv2InkRendererService = (
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
const proot = /* @__PURE__ */ jsx(ProgressRoot, {
|
|
1316
|
-
store,
|
|
1317
|
-
getTerminalColumns: () => isTTY ? stdio.stderr.columns : void 0,
|
|
1318
|
-
getTerminalRows: () => isTTY ? stdio.stderr.rows : void 0
|
|
1319
|
-
});
|
|
1186
|
+
const makeRendererv2InkRendererService = () => {
|
|
1187
|
+
return { run: (store, stdio) => {
|
|
1188
|
+
const proot = /* @__PURE__ */ jsx(ProgressRoot, { store });
|
|
1320
1189
|
return Effect.sync(() => render(proot, {
|
|
1321
1190
|
stdout: stdio.stdout,
|
|
1322
1191
|
stderr: stdio.stderr,
|
|
@@ -1337,7 +1206,7 @@ const makeRendererv2InkRendererService = (columns) => {
|
|
|
1337
1206
|
//#endregion
|
|
1338
1207
|
//#region src/services/ink-renderer.tsx
|
|
1339
1208
|
var InkRenderer = class InkRenderer extends Context.Tag("stromseng.dev/effective-progress/InkRenderer")() {
|
|
1340
|
-
static Default = Layer.succeed(InkRenderer, InkRenderer.of(makeRendererv2InkRendererService(
|
|
1209
|
+
static Default = Layer.succeed(InkRenderer, InkRenderer.of(makeRendererv2InkRendererService()));
|
|
1341
1210
|
};
|
|
1342
1211
|
|
|
1343
1212
|
//#endregion
|
|
@@ -1352,16 +1221,16 @@ var ProgressStdio = class ProgressStdio extends Context.Tag("stromseng.dev/effec
|
|
|
1352
1221
|
|
|
1353
1222
|
//#endregion
|
|
1354
1223
|
//#region src/services/progress.ts
|
|
1224
|
+
/** Builds the scoped implementation used by `ProgressService.task(...)` without auto-providing services. */
|
|
1355
1225
|
const makeProgressService = Effect.gen(function* () {
|
|
1356
1226
|
const stdio = yield* ProgressStdio;
|
|
1357
1227
|
const inkRenderer = yield* InkRenderer;
|
|
1358
1228
|
const outerConsole = yield* Effect.console;
|
|
1359
|
-
const isTTY = Boolean(stdio.stderr.isTTY);
|
|
1360
1229
|
const store = makeProgressRenderStore();
|
|
1361
1230
|
const currentParentRef = yield* FiberRef.make(Option.none());
|
|
1362
1231
|
const scope = yield* Effect.scope;
|
|
1363
1232
|
const log = (...args) => args.length === 0 ? Effect.void : outerConsole.log(...args);
|
|
1364
|
-
yield* Effect.forkIn(inkRenderer.run(store, stdio
|
|
1233
|
+
yield* Effect.forkIn(inkRenderer.run(store, stdio), scope);
|
|
1365
1234
|
yield* Effect.sleep("0 millis");
|
|
1366
1235
|
const addTask = (options) => Effect.gen(function* () {
|
|
1367
1236
|
const resolvedParentId = options.parentId === void 0 ? yield* FiberRef.get(currentParentRef) : Option.some(options.parentId);
|
|
@@ -1377,7 +1246,27 @@ const makeProgressService = Effect.gen(function* () {
|
|
|
1377
1246
|
const failTask = store.failTask;
|
|
1378
1247
|
const getTask = store.getTask;
|
|
1379
1248
|
const listTasks = store.listTasks;
|
|
1380
|
-
const
|
|
1249
|
+
const setMetadata = store.setMetadata;
|
|
1250
|
+
const getMetadata = store.getMetadata;
|
|
1251
|
+
const makeTaskHandle = (taskId) => ({
|
|
1252
|
+
id: taskId,
|
|
1253
|
+
getMetadata: getMetadata(taskId),
|
|
1254
|
+
setMetadata: (metadata) => setMetadata(taskId, metadata),
|
|
1255
|
+
updateMetadata: (f) => Effect.flatMap(getMetadata(taskId), (current) => setMetadata(taskId, f(current))),
|
|
1256
|
+
incrementSucceeded: (amount) => incrementSucceeded(taskId, amount),
|
|
1257
|
+
incrementFailed: (amount) => incrementFailed(taskId, amount),
|
|
1258
|
+
update: (options) => updateTask(taskId, options),
|
|
1259
|
+
complete: completeTask(taskId),
|
|
1260
|
+
fail: failTask(taskId),
|
|
1261
|
+
getSnapshot: getTask(taskId).pipe(Effect.map(Option.getOrThrow))
|
|
1262
|
+
});
|
|
1263
|
+
const autoFinalizeIfRunning = (taskId, exit) => Effect.gen(function* () {
|
|
1264
|
+
const task = yield* getTask(taskId);
|
|
1265
|
+
if (Option.isNone(task) || task.value.status !== "running") return;
|
|
1266
|
+
if (Exit.isSuccess(exit)) yield* completeTask(taskId);
|
|
1267
|
+
else yield* failTask(taskId);
|
|
1268
|
+
});
|
|
1269
|
+
const scopedTask = dual(2, (effect, options) => Effect.gen(function* () {
|
|
1381
1270
|
const inheritedParentId = yield* FiberRef.get(currentParentRef);
|
|
1382
1271
|
const resolvedParentId = options.parentId === void 0 ? inheritedParentId : Option.some(options.parentId);
|
|
1383
1272
|
const taskId = yield* addTask({
|
|
@@ -1397,17 +1286,29 @@ const makeProgressService = Effect.gen(function* () {
|
|
|
1397
1286
|
log,
|
|
1398
1287
|
getTask,
|
|
1399
1288
|
listTasks,
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1289
|
+
setMetadata,
|
|
1290
|
+
getMetadata,
|
|
1291
|
+
task: dual(2, (effectOrCallback, options) => {
|
|
1292
|
+
if (typeof effectOrCallback === "function") return scopedTask(Effect.gen(function* () {
|
|
1293
|
+
const taskId = yield* Task;
|
|
1294
|
+
const handle = makeTaskHandle(taskId);
|
|
1295
|
+
const exit = yield* Effect.exit(effectOrCallback(handle));
|
|
1296
|
+
yield* autoFinalizeIfRunning(taskId, exit);
|
|
1297
|
+
return yield* Exit.match(exit, {
|
|
1298
|
+
onFailure: Effect.failCause,
|
|
1299
|
+
onSuccess: Effect.succeed
|
|
1300
|
+
});
|
|
1301
|
+
}), options);
|
|
1302
|
+
return scopedTask(Effect.gen(function* () {
|
|
1303
|
+
const taskId = yield* Task;
|
|
1304
|
+
const exit = yield* Effect.exit(effectOrCallback);
|
|
1305
|
+
yield* autoFinalizeIfRunning(taskId, exit);
|
|
1306
|
+
return yield* Exit.match(exit, {
|
|
1307
|
+
onFailure: Effect.failCause,
|
|
1308
|
+
onSuccess: Effect.succeed
|
|
1309
|
+
});
|
|
1310
|
+
}), options);
|
|
1311
|
+
})
|
|
1411
1312
|
};
|
|
1412
1313
|
return Progress.of(service);
|
|
1413
1314
|
});
|
|
@@ -1439,9 +1340,17 @@ const provideProgress = (effect) => Effect.gen(function* () {
|
|
|
1439
1340
|
if (Option.isSome(existing)) return yield* Effect.provideService(effect, Progress, existing.value);
|
|
1440
1341
|
return yield* Effect.scoped(effect.pipe(Effect.provide(Progress.Default)));
|
|
1441
1342
|
});
|
|
1442
|
-
|
|
1343
|
+
/**
|
|
1344
|
+
* Runs an effect inside a task, creating and providing a `Progress` service automatically when one
|
|
1345
|
+
* is not already present in the environment.
|
|
1346
|
+
*
|
|
1347
|
+
* The effect form tracks success and failure from the effect exit. The callback form exposes a
|
|
1348
|
+
* typed `TaskHandle` for task-local updates, typed metadata, and explicit completion or failure,
|
|
1349
|
+
* and otherwise auto-finalizes from the callback exit if the task is still `running`.
|
|
1350
|
+
*/
|
|
1351
|
+
const task = dual(2, (effectOrCallback, options) => {
|
|
1443
1352
|
return provideProgress(Effect.gen(function* () {
|
|
1444
|
-
return yield* (yield* Progress).
|
|
1353
|
+
return yield* (yield* Progress).task(effectOrCallback, options);
|
|
1445
1354
|
}));
|
|
1446
1355
|
});
|
|
1447
1356
|
const wrapEffects = (effects, tap) => Array.isArray(effects) ? effects.map(tap) : Object.fromEntries(Object.entries(effects).map(([k, effect]) => [k, tap(effect)]));
|
|
@@ -1464,10 +1373,14 @@ const isTaskFullyProcessed = (progress, taskId) => Effect.gen(function* () {
|
|
|
1464
1373
|
const { processed, total } = taskOption.value.units;
|
|
1465
1374
|
return total !== void 0 && processed >= total;
|
|
1466
1375
|
});
|
|
1376
|
+
/**
|
|
1377
|
+
* Runs multiple effects under a single parent task and keeps the task counters in sync with the
|
|
1378
|
+
* child effect outcomes.
|
|
1379
|
+
*/
|
|
1467
1380
|
const all = dual(2, (effects, options) => provideProgress(Effect.gen(function* () {
|
|
1468
1381
|
const progress = yield* Progress;
|
|
1469
|
-
return yield* progress.
|
|
1470
|
-
const taskId =
|
|
1382
|
+
return yield* progress.task((handle) => Effect.gen(function* () {
|
|
1383
|
+
const taskId = handle.id;
|
|
1471
1384
|
const exit = yield* Effect.exit(Effect.all(wrapEffects(effects, (effect) => wrapTrackedEffect(progress, taskId, effect)), {
|
|
1472
1385
|
concurrency: options.concurrency,
|
|
1473
1386
|
batching: options.batching,
|
|
@@ -1490,10 +1403,13 @@ const all = dual(2, (effects, options) => provideProgress(Effect.gen(function* (
|
|
|
1490
1403
|
countDisplay: allCountDisplay(options.mode)
|
|
1491
1404
|
});
|
|
1492
1405
|
})));
|
|
1406
|
+
/**
|
|
1407
|
+
* Runs `Effect.forEach` under a single parent task and advances the task counters as items finish.
|
|
1408
|
+
*/
|
|
1493
1409
|
const forEach = dual(3, (iterable, f, options) => provideProgress(Effect.gen(function* () {
|
|
1494
1410
|
const progress = yield* Progress;
|
|
1495
|
-
return yield* progress.
|
|
1496
|
-
const taskId =
|
|
1411
|
+
return yield* progress.task((handle) => Effect.gen(function* () {
|
|
1412
|
+
const taskId = handle.id;
|
|
1497
1413
|
const exit = yield* Effect.exit(Effect.forEach(iterable, (item, index) => wrapTrackedEffect(progress, taskId, f(item, index)), {
|
|
1498
1414
|
concurrency: options.concurrency,
|
|
1499
1415
|
batching: options.batching,
|
|
@@ -1515,4 +1431,4 @@ const forEach = dual(3, (iterable, f, options) => provideProgress(Effect.gen(fun
|
|
|
1515
1431
|
})));
|
|
1516
1432
|
|
|
1517
1433
|
//#endregion
|
|
1518
|
-
export { Progress, ProgressStdio, ProgressTaskEventSchema, Task, TaskAddedEvent, TaskAdvancedEvent, TaskCompletedEvent, TaskCountDisplaySchema, TaskFailedEvent, TaskId, TaskRemovedEvent, TaskSnapshot, TaskSnapshotSchema, TaskStatusSchema, TaskUnitsSchema, TaskUpdatedEvent, all, decodeProgressTaskEvent, forEach, task };
|
|
1434
|
+
export { columns_exports as Columns, Progress, ProgressStdio, ProgressTaskEventSchema, Task, TaskAddedEvent, TaskAdvancedEvent, TaskCompletedEvent, TaskCountDisplaySchema, TaskFailedEvent, TaskId, TaskRemovedEvent, TaskSnapshot, TaskSnapshotSchema, TaskStatusSchema, TaskUnitsSchema, TaskUpdatedEvent, all, decodeProgressTaskEvent, forEach, task };
|