silvery 0.17.0 → 0.17.2
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/UPNG-AVSMjiFE.mjs +5076 -0
- package/dist/UPNG-AVSMjiFE.mjs.map +1 -0
- package/dist/__vite-browser-external-2447137e-D3GdsvS_.mjs +6 -0
- package/dist/__vite-browser-external-2447137e-D3GdsvS_.mjs.map +1 -0
- package/dist/animation-C_PTO0uH.mjs +304 -0
- package/dist/animation-C_PTO0uH.mjs.map +1 -0
- package/dist/ansi-CXLE_pt1.mjs +71 -0
- package/dist/ansi-CXLE_pt1.mjs.map +1 -0
- package/dist/ansi-zmNzgkPB.d.mts +49 -0
- package/dist/ansi-zmNzgkPB.d.mts.map +1 -0
- package/dist/apng-DCWY913R.mjs +3 -0
- package/dist/apng-ENBAJk-H.mjs +70 -0
- package/dist/apng-ENBAJk-H.mjs.map +1 -0
- package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
- package/dist/backend-CkIkIHR-.mjs +13396 -0
- package/dist/backend-CkIkIHR-.mjs.map +1 -0
- package/dist/backends-CkvbG3js.mjs +1181 -0
- package/dist/backends-CkvbG3js.mjs.map +1 -0
- package/dist/backends-CyJqNLeK.mjs +3 -0
- package/dist/chunk-BSw8zbkd.mjs +37 -0
- package/dist/cli-B-k7Bm56.mjs +4 -0
- package/dist/context-QreF3UHr.mjs +64 -0
- package/dist/context-QreF3UHr.mjs.map +1 -0
- package/dist/derive-D7bFJdfU.d.mts +28 -0
- package/dist/derive-D7bFJdfU.d.mts.map +1 -0
- package/dist/devtools-CscuKaDK.mjs +89 -0
- package/dist/devtools-CscuKaDK.mjs.map +1 -0
- package/dist/devtools-D4oGc6LY.mjs +2 -0
- package/dist/eta-DLiVPaSD.mjs +110 -0
- package/dist/eta-DLiVPaSD.mjs.map +1 -0
- package/dist/flexily-zero-adapter-DmG4Ge8t.mjs +3376 -0
- package/dist/flexily-zero-adapter-DmG4Ge8t.mjs.map +1 -0
- package/dist/flexily-zero-adapter-GHwEW11s.mjs +2 -0
- package/dist/gif-BaJNREpP.mjs +3 -0
- package/dist/gif-Bp6fIyN3.mjs +73 -0
- package/dist/gif-Bp6fIyN3.mjs.map +1 -0
- package/dist/gifenc-GiVCZ9-3.mjs +730 -0
- package/dist/gifenc-GiVCZ9-3.mjs.map +1 -0
- package/dist/image-Dx7gYjkq.mjs +346 -0
- package/dist/image-Dx7gYjkq.mjs.map +1 -0
- package/dist/index-CBcSpGSM.d.mts +3416 -0
- package/dist/index-CBcSpGSM.d.mts.map +1 -0
- package/dist/index-DCVL3jHo.d.mts +634 -0
- package/dist/index-DCVL3jHo.d.mts.map +1 -0
- package/dist/index-p-wBs_wH.d.mts +175 -0
- package/dist/index-p-wBs_wH.d.mts.map +1 -0
- package/dist/index.d.mts +7296 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +9399 -0
- package/dist/index.mjs.map +1 -0
- package/dist/key-mapping-BsUHe_nk.mjs +3 -0
- package/dist/key-mapping-DsyfLEdC.mjs +132 -0
- package/dist/key-mapping-DsyfLEdC.mjs.map +1 -0
- package/dist/layout-engine-B3dsnVLU.mjs +50 -0
- package/dist/layout-engine-B3dsnVLU.mjs.map +1 -0
- package/dist/layout-engine-D_lSR4i9.mjs +2 -0
- package/dist/multi-progress-C0-rkn86.d.mts +180 -0
- package/dist/multi-progress-C0-rkn86.d.mts.map +1 -0
- package/dist/multi-progress-CQVB9lES.mjs +219 -0
- package/dist/multi-progress-CQVB9lES.mjs.map +1 -0
- package/dist/node-Dedx-6xF.mjs +1085 -0
- package/dist/node-Dedx-6xF.mjs.map +1 -0
- package/dist/pipeline-DDOPrjuY.mjs +4387 -0
- package/dist/pipeline-DDOPrjuY.mjs.map +1 -0
- package/dist/progress-bar-COPSBlT9.mjs +155 -0
- package/dist/progress-bar-COPSBlT9.mjs.map +1 -0
- package/dist/reconciler-2lp5VXK7.mjs +16506 -0
- package/dist/reconciler-2lp5VXK7.mjs.map +1 -0
- package/dist/render-string-BXvxTg5P.mjs +201 -0
- package/dist/render-string-BXvxTg5P.mjs.map +1 -0
- package/dist/render-string-hvfpVtoP.mjs +2 -0
- package/dist/resvg-js-V6oMi8CY.mjs +203 -0
- package/dist/resvg-js-V6oMi8CY.mjs.map +1 -0
- package/dist/runtime-BjDHNTxJ.mjs +8723 -0
- package/dist/runtime-BjDHNTxJ.mjs.map +1 -0
- package/dist/runtime.d.mts +2 -0
- package/dist/runtime.mjs +3 -0
- package/dist/spinner-Cgej6Vnb.d.mts +127 -0
- package/dist/spinner-Cgej6Vnb.d.mts.map +1 -0
- package/dist/spinner-DSByknyx.mjs +298 -0
- package/dist/spinner-DSByknyx.mjs.map +1 -0
- package/dist/src-9B5k0JmY.mjs +1629 -0
- package/dist/src-9B5k0JmY.mjs.map +1 -0
- package/dist/src-C9f3hiVG.mjs +3620 -0
- package/dist/src-C9f3hiVG.mjs.map +1 -0
- package/dist/src-fJVbhdn-.mjs +816 -0
- package/dist/src-fJVbhdn-.mjs.map +1 -0
- package/dist/theme.d.mts +115 -0
- package/dist/theme.d.mts.map +1 -0
- package/dist/theme.mjs +8 -0
- package/dist/theme.mjs.map +1 -0
- package/dist/types-Bhj5QkIQ.mjs +13 -0
- package/dist/types-Bhj5QkIQ.mjs.map +1 -0
- package/dist/types-CDgkE-Rw.d.mts +241 -0
- package/dist/types-CDgkE-Rw.d.mts.map +1 -0
- package/dist/ui/animation.d.mts +2 -0
- package/dist/ui/animation.mjs +2 -0
- package/dist/ui/ansi.d.mts +2 -0
- package/dist/ui/ansi.mjs +2 -0
- package/dist/ui/cli.d.mts +5 -0
- package/dist/ui/cli.mjs +7 -0
- package/dist/ui/display.d.mts +35 -0
- package/dist/ui/display.d.mts.map +1 -0
- package/dist/ui/display.mjs +123 -0
- package/dist/ui/display.mjs.map +1 -0
- package/dist/ui/image.d.mts +2 -0
- package/dist/ui/image.mjs +2 -0
- package/dist/ui/input.d.mts +184 -0
- package/dist/ui/input.d.mts.map +1 -0
- package/dist/ui/input.mjs +285 -0
- package/dist/ui/input.mjs.map +1 -0
- package/dist/ui/progress.d.mts +249 -0
- package/dist/ui/progress.d.mts.map +1 -0
- package/dist/ui/progress.mjs +858 -0
- package/dist/ui/progress.mjs.map +1 -0
- package/dist/ui/react.d.mts +280 -0
- package/dist/ui/react.d.mts.map +1 -0
- package/dist/ui/react.mjs +413 -0
- package/dist/ui/react.mjs.map +1 -0
- package/dist/ui/utils.d.mts +86 -0
- package/dist/ui/utils.d.mts.map +1 -0
- package/dist/ui/utils.mjs +2 -0
- package/dist/ui/wrappers.d.mts +3 -0
- package/dist/ui/wrappers.mjs +2 -0
- package/dist/ui.d.mts +6 -0
- package/dist/ui.mjs +7 -0
- package/dist/useLatest-BMIYXd6e.d.mts +154 -0
- package/dist/useLatest-BMIYXd6e.d.mts.map +1 -0
- package/dist/useLayout-BG2cGl15.mjs +139 -0
- package/dist/useLayout-BG2cGl15.mjs.map +1 -0
- package/dist/with-text-input-CmHf_9d6.d.mts +284 -0
- package/dist/with-text-input-CmHf_9d6.d.mts.map +1 -0
- package/dist/wrapper-Dqh0zi2W.mjs +3527 -0
- package/dist/wrapper-Dqh0zi2W.mjs.map +1 -0
- package/dist/wrappers-hhL8EQ_n.mjs +810 -0
- package/dist/wrappers-hhL8EQ_n.mjs.map +1 -0
- package/dist/yoga-adapter-BJ9SOhTY.mjs +245 -0
- package/dist/yoga-adapter-BJ9SOhTY.mjs.map +1 -0
- package/dist/yoga-adapter-Daq6-dw1.mjs +2 -0
- package/package.json +48 -75
- package/CHANGELOG.md +0 -319
- package/dist/chalk.js +0 -4
- package/dist/index.js +0 -270
- package/dist/ink.js +0 -142
- package/dist/runtime.js +0 -135
- package/dist/theme.js +0 -7
- package/dist/ui/animation.js +0 -3
- package/dist/ui/ansi.js +0 -3
- package/dist/ui/cli.js +0 -9
- package/dist/ui/display.js +0 -4
- package/dist/ui/image.js +0 -4
- package/dist/ui/input.js +0 -3
- package/dist/ui/progress.js +0 -9
- package/dist/ui/react.js +0 -4
- package/dist/ui/utils.js +0 -3
- package/dist/ui/wrappers.js +0 -15
- package/dist/ui.js +0 -18
- package/src/index.ts +0 -73
- package/src/runtime.ts +0 -4
- package/src/theme.ts +0 -4
- package/src/ui/animation.ts +0 -2
- package/src/ui/ansi.ts +0 -2
- package/src/ui/cli.ts +0 -3
- package/src/ui/display.ts +0 -2
- package/src/ui/image.ts +0 -2
- package/src/ui/input.ts +0 -2
- package/src/ui/progress.ts +0 -2
- package/src/ui/react.ts +0 -2
- package/src/ui/utils.ts +0 -2
- package/src/ui/wrappers.ts +0 -2
- package/src/ui.ts +0 -4
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
import { i as createSpinner, r as Spinner } from "../spinner-DSByknyx.mjs";
|
|
2
|
+
import { t as ProgressBar } from "../progress-bar-COPSBlT9.mjs";
|
|
3
|
+
import { t as MultiProgress } from "../multi-progress-CQVB9lES.mjs";
|
|
4
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
+
//#region packages/ag-react/src/ui/progress/als-context.ts
|
|
6
|
+
/**
|
|
7
|
+
* AsyncLocalStorage context for step progress reporting
|
|
8
|
+
*
|
|
9
|
+
* Provides a `step()` function that work functions can call to report progress.
|
|
10
|
+
* Returns a no-op context when called outside of a steps() execution context,
|
|
11
|
+
* so functions work in tests without the progress UI.
|
|
12
|
+
*/
|
|
13
|
+
const stepContext = new AsyncLocalStorage();
|
|
14
|
+
/**
|
|
15
|
+
* Get the current step context
|
|
16
|
+
*
|
|
17
|
+
* Safe to call anywhere - returns a no-op context when called outside
|
|
18
|
+
* of a steps() execution context.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* async function processFiles(files: string[]) {
|
|
23
|
+
* for (let i = 0; i < files.length; i++) {
|
|
24
|
+
* step().progress(i + 1, files.length);
|
|
25
|
+
* await process(files[i]);
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* // In tests (no steps context)
|
|
30
|
+
* await processFiles(["a.md", "b.md"]); // step() returns no-op, no errors
|
|
31
|
+
*
|
|
32
|
+
* // In production (with steps context)
|
|
33
|
+
* await steps({ process: processFiles }).run(); // Shows progress
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
function step() {
|
|
37
|
+
return stepContext.getStore() ?? NO_OP_CONTEXT;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Run a function with step context (internal use by runner)
|
|
41
|
+
*/
|
|
42
|
+
function runWithStepContext(ctx, fn) {
|
|
43
|
+
return stepContext.run(ctx, fn);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create an internal step context for the runner
|
|
47
|
+
*/
|
|
48
|
+
function createStepContext(label, handle, onSubStep) {
|
|
49
|
+
let currentSubLabel;
|
|
50
|
+
let currentSubHandle = null;
|
|
51
|
+
let subStepStartTime = 0;
|
|
52
|
+
const declaredHandles = /* @__PURE__ */ new Map();
|
|
53
|
+
return {
|
|
54
|
+
get label() {
|
|
55
|
+
return label;
|
|
56
|
+
},
|
|
57
|
+
get handle() {
|
|
58
|
+
return handle;
|
|
59
|
+
},
|
|
60
|
+
progress(current, total) {
|
|
61
|
+
if (currentSubHandle) currentSubHandle.setTitle(`${currentSubLabel} (${current}/${total})`);
|
|
62
|
+
else handle.setTitle(`${label} (${current}/${total})`);
|
|
63
|
+
},
|
|
64
|
+
sub(subLabel) {
|
|
65
|
+
this._completeSubStep();
|
|
66
|
+
currentSubLabel = subLabel;
|
|
67
|
+
subStepStartTime = Date.now();
|
|
68
|
+
if (onSubStep) {
|
|
69
|
+
currentSubHandle = onSubStep(subLabel);
|
|
70
|
+
currentSubHandle.start();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
_addSubHandle(subLabel, subHandle) {
|
|
74
|
+
declaredHandles.set(subLabel, subHandle);
|
|
75
|
+
},
|
|
76
|
+
_getSubHandle(subLabel) {
|
|
77
|
+
return declaredHandles.get(subLabel);
|
|
78
|
+
},
|
|
79
|
+
_setCurrentSubHandle(subLabel, subHandle) {
|
|
80
|
+
currentSubLabel = subLabel;
|
|
81
|
+
currentSubHandle = subHandle;
|
|
82
|
+
subStepStartTime = Date.now();
|
|
83
|
+
},
|
|
84
|
+
_completeSubStep() {
|
|
85
|
+
if (currentSubHandle && currentSubLabel) {
|
|
86
|
+
const elapsed = Date.now() - subStepStartTime;
|
|
87
|
+
currentSubHandle.complete(elapsed);
|
|
88
|
+
currentSubHandle = null;
|
|
89
|
+
currentSubLabel = void 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* No-op context for when step() is called outside execution context
|
|
96
|
+
*/
|
|
97
|
+
const NO_OP_CONTEXT = {
|
|
98
|
+
progress: () => {},
|
|
99
|
+
sub: () => {},
|
|
100
|
+
get label() {
|
|
101
|
+
return "";
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region packages/ag-react/src/ui/progress/step-node.ts
|
|
106
|
+
/**
|
|
107
|
+
* Parse a declarative steps definition into a tree of StepNodes
|
|
108
|
+
*
|
|
109
|
+
* @param def - The declarative structure
|
|
110
|
+
* @param indent - Current indentation level (internal)
|
|
111
|
+
* @returns Array of StepNodes
|
|
112
|
+
*/
|
|
113
|
+
function parseStepsDef(def, indent = 0) {
|
|
114
|
+
const nodes = [];
|
|
115
|
+
for (const [key, value] of Object.entries(def)) if (typeof value === "function") nodes.push({
|
|
116
|
+
key,
|
|
117
|
+
label: generateLabel(key),
|
|
118
|
+
work: value,
|
|
119
|
+
indent
|
|
120
|
+
});
|
|
121
|
+
else if (Array.isArray(value) && value.length === 2) {
|
|
122
|
+
const [label, work] = value;
|
|
123
|
+
nodes.push({
|
|
124
|
+
key,
|
|
125
|
+
label,
|
|
126
|
+
work,
|
|
127
|
+
indent
|
|
128
|
+
});
|
|
129
|
+
} else if (typeof value === "object" && value !== null) {
|
|
130
|
+
const children = parseStepsDef(value, indent + 1);
|
|
131
|
+
nodes.push({
|
|
132
|
+
key,
|
|
133
|
+
label: generateLabel(key),
|
|
134
|
+
children,
|
|
135
|
+
indent
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return nodes;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Flatten the tree for sequential execution
|
|
142
|
+
*
|
|
143
|
+
* Returns nodes in depth-first order, with groups followed by their children.
|
|
144
|
+
*/
|
|
145
|
+
function flattenStepNodes(nodes) {
|
|
146
|
+
const result = [];
|
|
147
|
+
for (const node of nodes) {
|
|
148
|
+
result.push(node);
|
|
149
|
+
if (node.children) result.push(...flattenStepNodes(node.children));
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get only leaf nodes (nodes with work functions)
|
|
155
|
+
*/
|
|
156
|
+
function getLeafNodes(nodes) {
|
|
157
|
+
const result = [];
|
|
158
|
+
for (const node of nodes) {
|
|
159
|
+
if (node.work) result.push(node);
|
|
160
|
+
if (node.children) result.push(...getLeafNodes(node.children));
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Generate a display label from a camelCase function name
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* generateLabel("loadModules") // "Load modules"
|
|
169
|
+
* generateLabel("parseMarkdown") // "Parse markdown"
|
|
170
|
+
* generateLabel("initBoardStateGenerator") // "Init board state generator"
|
|
171
|
+
*/
|
|
172
|
+
function generateLabel(fnName) {
|
|
173
|
+
return fnName.replace(/([A-Z])/g, " $1").replace(/(\d+)/g, " $1").toLowerCase().trim().replace(/\s+/g, " ").replace(/^./, (s) => s.toUpperCase());
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region packages/ag-react/src/ui/progress/declarative.ts
|
|
177
|
+
/**
|
|
178
|
+
* Declarative steps implementation
|
|
179
|
+
*
|
|
180
|
+
* Provides the declarative overload for steps() that accepts an object
|
|
181
|
+
* structure and shows all steps upfront before execution.
|
|
182
|
+
*/
|
|
183
|
+
/**
|
|
184
|
+
* Create a declarative steps runner
|
|
185
|
+
*
|
|
186
|
+
* @param def - Object structure defining steps
|
|
187
|
+
* @returns StepsRunner with run(), pipe(), and done() methods
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* const loader = stepsDeclarative({
|
|
192
|
+
* loadModules, // "Load modules"
|
|
193
|
+
* loadRepo: { // "Load repo" (group)
|
|
194
|
+
* discover, // "Discover"
|
|
195
|
+
* parse, // "Parse"
|
|
196
|
+
* },
|
|
197
|
+
* });
|
|
198
|
+
*
|
|
199
|
+
* const results = await loader.run({ clear: true });
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
function stepsDeclarative(def) {
|
|
203
|
+
const rootNodes = parseStepsDef(def);
|
|
204
|
+
const allNodes = flattenStepNodes(rootNodes);
|
|
205
|
+
let multi = null;
|
|
206
|
+
const handles = /* @__PURE__ */ new Map();
|
|
207
|
+
const groupLeaves = /* @__PURE__ */ new Map();
|
|
208
|
+
const leafToGroups = /* @__PURE__ */ new Map();
|
|
209
|
+
for (const node of allNodes) if (node.children) {
|
|
210
|
+
const leaves = getLeafNodes([node]);
|
|
211
|
+
groupLeaves.set(node, leaves);
|
|
212
|
+
for (const leaf of leaves) {
|
|
213
|
+
const groups = leafToGroups.get(leaf) ?? [];
|
|
214
|
+
groups.push(node);
|
|
215
|
+
leafToGroups.set(leaf, groups);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
get _steps() {
|
|
220
|
+
return rootNodes;
|
|
221
|
+
},
|
|
222
|
+
async run(options) {
|
|
223
|
+
multi = new MultiProgress();
|
|
224
|
+
registerAllSteps(allNodes, multi, handles);
|
|
225
|
+
const groupStartTimes = /* @__PURE__ */ new Map();
|
|
226
|
+
const completedLeaves = /* @__PURE__ */ new Set();
|
|
227
|
+
multi.start();
|
|
228
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
229
|
+
const results = {};
|
|
230
|
+
try {
|
|
231
|
+
for (const node of allNodes) if (node.work) {
|
|
232
|
+
const groups = leafToGroups.get(node) ?? [];
|
|
233
|
+
for (const group of groups) if (!groupStartTimes.has(group)) {
|
|
234
|
+
groupStartTimes.set(group, Date.now());
|
|
235
|
+
handles.get(group)?.start();
|
|
236
|
+
}
|
|
237
|
+
const result = await executeStep(node, handles, multi);
|
|
238
|
+
setNestedResult(results, node.key, result);
|
|
239
|
+
completedLeaves.add(node);
|
|
240
|
+
for (const group of groups) if ((groupLeaves.get(group) ?? []).every((l) => completedLeaves.has(l))) {
|
|
241
|
+
const elapsed = Date.now() - groupStartTimes.get(group);
|
|
242
|
+
handles.get(group)?.complete(elapsed);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} finally {
|
|
246
|
+
multi.stop(options?.clear ?? false);
|
|
247
|
+
}
|
|
248
|
+
return results;
|
|
249
|
+
},
|
|
250
|
+
async pipe(options) {
|
|
251
|
+
multi = new MultiProgress();
|
|
252
|
+
registerAllSteps(allNodes, multi, handles);
|
|
253
|
+
const groupStartTimes = /* @__PURE__ */ new Map();
|
|
254
|
+
const completedLeaves = /* @__PURE__ */ new Set();
|
|
255
|
+
multi.start();
|
|
256
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
257
|
+
let previousResult = void 0;
|
|
258
|
+
try {
|
|
259
|
+
for (const node of allNodes) if (node.work) {
|
|
260
|
+
const groups = leafToGroups.get(node) ?? [];
|
|
261
|
+
for (const group of groups) if (!groupStartTimes.has(group)) {
|
|
262
|
+
groupStartTimes.set(group, Date.now());
|
|
263
|
+
handles.get(group)?.start();
|
|
264
|
+
}
|
|
265
|
+
previousResult = await executeStep(node, handles, multi, previousResult);
|
|
266
|
+
completedLeaves.add(node);
|
|
267
|
+
for (const group of groups) if ((groupLeaves.get(group) ?? []).every((l) => completedLeaves.has(l))) {
|
|
268
|
+
const elapsed = Date.now() - groupStartTimes.get(group);
|
|
269
|
+
handles.get(group)?.complete(elapsed);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
} finally {
|
|
273
|
+
multi.stop(options?.clear ?? false);
|
|
274
|
+
}
|
|
275
|
+
return previousResult;
|
|
276
|
+
},
|
|
277
|
+
done(options) {
|
|
278
|
+
if (multi) {
|
|
279
|
+
multi.stop(options?.clear ?? false);
|
|
280
|
+
multi = null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Register all steps with MultiProgress upfront
|
|
287
|
+
*/
|
|
288
|
+
function registerAllSteps(nodes, multi, handles) {
|
|
289
|
+
for (const node of nodes) {
|
|
290
|
+
const isGroup = node.children && !node.work;
|
|
291
|
+
const handle = multi.add(node.label, {
|
|
292
|
+
type: isGroup ? "group" : "spinner",
|
|
293
|
+
indent: node.indent
|
|
294
|
+
});
|
|
295
|
+
handles.set(node, handle);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Execute a single step
|
|
300
|
+
*/
|
|
301
|
+
async function executeStep(node, handles, multi, input) {
|
|
302
|
+
const handle = handles.get(node);
|
|
303
|
+
const startTime = Date.now();
|
|
304
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
305
|
+
const ctx = createStepContext(node.label, handle, (subLabel) => {
|
|
306
|
+
return multi.add(subLabel, {
|
|
307
|
+
type: "spinner",
|
|
308
|
+
indent: node.indent + 1,
|
|
309
|
+
insertAfter: handle.id
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
handle.start();
|
|
313
|
+
try {
|
|
314
|
+
const result = await runWithStepContext(ctx, () => {
|
|
315
|
+
if (input !== void 0) return node.work(input);
|
|
316
|
+
return node.work();
|
|
317
|
+
});
|
|
318
|
+
if (isGenerator$2(result)) return await runGenerator$2(result, ctx, node, multi);
|
|
319
|
+
if (isAsyncGenerator$1(result)) return await runAsyncGenerator$1(result, ctx, node, multi);
|
|
320
|
+
ctx._completeSubStep();
|
|
321
|
+
const elapsed = Date.now() - startTime;
|
|
322
|
+
handle.complete(elapsed);
|
|
323
|
+
return result;
|
|
324
|
+
} catch (error) {
|
|
325
|
+
handle.fail();
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Run a sync generator step
|
|
331
|
+
*/
|
|
332
|
+
async function runGenerator$2(gen, ctx, node, multi) {
|
|
333
|
+
const startTime = Date.now();
|
|
334
|
+
let result = gen.next();
|
|
335
|
+
let hasSubSteps = false;
|
|
336
|
+
let lastInsertedId = ctx.handle.id;
|
|
337
|
+
while (!result.done) {
|
|
338
|
+
const value = result.value;
|
|
339
|
+
if (isDeclareSteps$1(value)) {
|
|
340
|
+
if (!hasSubSteps) {
|
|
341
|
+
hasSubSteps = true;
|
|
342
|
+
ctx.handle.setType("group");
|
|
343
|
+
}
|
|
344
|
+
for (const label of value.declare) {
|
|
345
|
+
const subHandle = multi.add(label, {
|
|
346
|
+
type: "spinner",
|
|
347
|
+
indent: node.indent + 1,
|
|
348
|
+
insertAfter: lastInsertedId
|
|
349
|
+
});
|
|
350
|
+
lastInsertedId = subHandle.id;
|
|
351
|
+
ctx._addSubHandle(label, subHandle);
|
|
352
|
+
}
|
|
353
|
+
} else if (typeof value === "string") {
|
|
354
|
+
ctx._completeSubStep();
|
|
355
|
+
if (!hasSubSteps) {
|
|
356
|
+
hasSubSteps = true;
|
|
357
|
+
ctx.handle.setType("group");
|
|
358
|
+
}
|
|
359
|
+
const existingHandle = ctx._getSubHandle?.(value);
|
|
360
|
+
if (existingHandle) {
|
|
361
|
+
ctx._setCurrentSubHandle(value, existingHandle);
|
|
362
|
+
existingHandle.start();
|
|
363
|
+
} else {
|
|
364
|
+
const subHandle = multi.add(value, {
|
|
365
|
+
type: "spinner",
|
|
366
|
+
indent: node.indent + 1,
|
|
367
|
+
insertAfter: lastInsertedId
|
|
368
|
+
});
|
|
369
|
+
lastInsertedId = subHandle.id;
|
|
370
|
+
ctx._addSubHandle(value, subHandle);
|
|
371
|
+
subHandle.start();
|
|
372
|
+
}
|
|
373
|
+
} else if (isProgressUpdate(value)) ctx.progress(value.current ?? 0, value.total ?? 0);
|
|
374
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
375
|
+
result = gen.next();
|
|
376
|
+
}
|
|
377
|
+
ctx._completeSubStep();
|
|
378
|
+
const elapsed = Date.now() - startTime;
|
|
379
|
+
ctx.handle.complete(elapsed);
|
|
380
|
+
return result.value;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Run an async generator step
|
|
384
|
+
*/
|
|
385
|
+
async function runAsyncGenerator$1(gen, ctx, node, multi) {
|
|
386
|
+
const startTime = Date.now();
|
|
387
|
+
let result = await gen.next();
|
|
388
|
+
let hasSubSteps = false;
|
|
389
|
+
let lastInsertedId = ctx.handle.id;
|
|
390
|
+
while (!result.done) {
|
|
391
|
+
const value = result.value;
|
|
392
|
+
if (isDeclareSteps$1(value)) {
|
|
393
|
+
if (!hasSubSteps) {
|
|
394
|
+
hasSubSteps = true;
|
|
395
|
+
ctx.handle.setType("group");
|
|
396
|
+
}
|
|
397
|
+
for (const label of value.declare) {
|
|
398
|
+
const subHandle = multi.add(label, {
|
|
399
|
+
type: "spinner",
|
|
400
|
+
indent: node.indent + 1,
|
|
401
|
+
insertAfter: lastInsertedId
|
|
402
|
+
});
|
|
403
|
+
lastInsertedId = subHandle.id;
|
|
404
|
+
ctx._addSubHandle(label, subHandle);
|
|
405
|
+
}
|
|
406
|
+
} else if (typeof value === "string") {
|
|
407
|
+
ctx._completeSubStep();
|
|
408
|
+
if (!hasSubSteps) {
|
|
409
|
+
hasSubSteps = true;
|
|
410
|
+
ctx.handle.setType("group");
|
|
411
|
+
}
|
|
412
|
+
const existingHandle = ctx._getSubHandle(value);
|
|
413
|
+
if (existingHandle) {
|
|
414
|
+
ctx._setCurrentSubHandle(value, existingHandle);
|
|
415
|
+
existingHandle.start();
|
|
416
|
+
} else {
|
|
417
|
+
const subHandle = multi.add(value, {
|
|
418
|
+
type: "spinner",
|
|
419
|
+
indent: node.indent + 1,
|
|
420
|
+
insertAfter: lastInsertedId
|
|
421
|
+
});
|
|
422
|
+
lastInsertedId = subHandle.id;
|
|
423
|
+
ctx._addSubHandle(value, subHandle);
|
|
424
|
+
subHandle.start();
|
|
425
|
+
}
|
|
426
|
+
} else if (isProgressUpdate(value)) ctx.progress(value.current ?? 0, value.total ?? 0);
|
|
427
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
428
|
+
result = await gen.next();
|
|
429
|
+
}
|
|
430
|
+
ctx._completeSubStep();
|
|
431
|
+
const elapsed = Date.now() - startTime;
|
|
432
|
+
ctx.handle.complete(elapsed);
|
|
433
|
+
return result.value;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Set a nested result value by key path
|
|
437
|
+
*/
|
|
438
|
+
function setNestedResult(results, key, value) {
|
|
439
|
+
results[key] = value;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Type guards
|
|
443
|
+
*/
|
|
444
|
+
function isGenerator$2(value) {
|
|
445
|
+
return value !== null && typeof value === "object" && typeof value.next === "function" && typeof value[Symbol.iterator] === "function";
|
|
446
|
+
}
|
|
447
|
+
function isAsyncGenerator$1(value) {
|
|
448
|
+
return value !== null && typeof value === "object" && typeof value.next === "function" && typeof value[Symbol.asyncIterator] === "function";
|
|
449
|
+
}
|
|
450
|
+
function isProgressUpdate(value) {
|
|
451
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && ("current" in value || "total" in value);
|
|
452
|
+
}
|
|
453
|
+
function isDeclareSteps$1(value) {
|
|
454
|
+
return value !== null && typeof value === "object" && "declare" in value && Array.isArray(value.declare);
|
|
455
|
+
}
|
|
456
|
+
//#endregion
|
|
457
|
+
//#region packages/ag-react/src/ui/progress/steps.ts
|
|
458
|
+
/**
|
|
459
|
+
* Sequential step runner with progress display
|
|
460
|
+
*
|
|
461
|
+
* Supports two modes:
|
|
462
|
+
*
|
|
463
|
+
* ## Declarative Mode (recommended)
|
|
464
|
+
*
|
|
465
|
+
* Pass an object to declare all steps upfront:
|
|
466
|
+
*
|
|
467
|
+
* ```typescript
|
|
468
|
+
* import { steps, step } from "@silvery/ag-react/ui/progress";
|
|
469
|
+
*
|
|
470
|
+
* const loader = steps({
|
|
471
|
+
* loadModules, // Auto-named: "Load modules"
|
|
472
|
+
* loadRepo: { // Group: "Load repo"
|
|
473
|
+
* discover, // "Discover"
|
|
474
|
+
* parse, // "Parse"
|
|
475
|
+
* },
|
|
476
|
+
* });
|
|
477
|
+
*
|
|
478
|
+
* const results = await loader.run({ clear: true });
|
|
479
|
+
*
|
|
480
|
+
* // Inside work functions, use step() to report progress:
|
|
481
|
+
* async function discover() {
|
|
482
|
+
* const files = await glob("**\/*.md");
|
|
483
|
+
* for (let i = 0; i < files.length; i++) {
|
|
484
|
+
* step().progress(i + 1, files.length);
|
|
485
|
+
* await process(files[i]);
|
|
486
|
+
* }
|
|
487
|
+
* return files;
|
|
488
|
+
* }
|
|
489
|
+
* ```
|
|
490
|
+
*
|
|
491
|
+
* ## Fluent Mode (legacy)
|
|
492
|
+
*
|
|
493
|
+
* Chain steps with `.run()` and execute with `.execute()`:
|
|
494
|
+
*
|
|
495
|
+
* ```typescript
|
|
496
|
+
* await steps()
|
|
497
|
+
* .run("Loading modules", () => import("./app"))
|
|
498
|
+
* .run("Building view", async () => buildView())
|
|
499
|
+
* .execute();
|
|
500
|
+
* ```
|
|
501
|
+
*
|
|
502
|
+
* Generators can yield sub-step progress:
|
|
503
|
+
* - Yield a **string** to create a new sub-step
|
|
504
|
+
* - Yield an **object** `{ current, total }` to update progress on current sub-step
|
|
505
|
+
*/
|
|
506
|
+
function steps(def) {
|
|
507
|
+
if (def !== void 0) return stepsDeclarative(def);
|
|
508
|
+
return createFluentBuilder();
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Create a no-op StepController for work functions that don't use sub-steps.
|
|
512
|
+
* This satisfies the type union while being harmless if not used.
|
|
513
|
+
*/
|
|
514
|
+
function createNoopStepController() {
|
|
515
|
+
const controller = (_label) => {};
|
|
516
|
+
controller.progress = (_current, _total) => {};
|
|
517
|
+
controller.done = () => {};
|
|
518
|
+
return controller;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Create the fluent step builder (legacy mode)
|
|
522
|
+
*/
|
|
523
|
+
function createFluentBuilder() {
|
|
524
|
+
const stepList = [];
|
|
525
|
+
const builder = {
|
|
526
|
+
run(title, work) {
|
|
527
|
+
stepList.push({
|
|
528
|
+
title,
|
|
529
|
+
work
|
|
530
|
+
});
|
|
531
|
+
return builder;
|
|
532
|
+
},
|
|
533
|
+
async execute(options) {
|
|
534
|
+
const multi = new MultiProgress();
|
|
535
|
+
const handles = /* @__PURE__ */ new Map();
|
|
536
|
+
const results = {};
|
|
537
|
+
for (const step of stepList) handles.set(step.title, multi.add(step.title, { type: "spinner" }));
|
|
538
|
+
multi.start();
|
|
539
|
+
try {
|
|
540
|
+
for (const step of stepList) {
|
|
541
|
+
const handle = handles.get(step.title);
|
|
542
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
543
|
+
const result = step.work(createNoopStepController());
|
|
544
|
+
if (isAsyncGenerator(result)) results[step.title] = await runAsyncGenerator(result, handle, step.title, multi);
|
|
545
|
+
else if (isSyncGenerator(result)) results[step.title] = await runSyncGenerator(result, handle, step.title, multi);
|
|
546
|
+
else if (isPromiseLike$2(result)) {
|
|
547
|
+
handle.start();
|
|
548
|
+
results[step.title] = await result;
|
|
549
|
+
handle.complete();
|
|
550
|
+
} else {
|
|
551
|
+
handle.start();
|
|
552
|
+
results[step.title] = result;
|
|
553
|
+
handle.complete();
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
} finally {
|
|
557
|
+
multi.stop(options?.clear ?? false);
|
|
558
|
+
}
|
|
559
|
+
return results;
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
return builder;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Process a yielded value:
|
|
566
|
+
* - { declare: [...] } = declare all sub-steps upfront (show as pending)
|
|
567
|
+
* - string = start/create a sub-step with that label
|
|
568
|
+
* - { current, total } = update progress on current sub-step
|
|
569
|
+
*/
|
|
570
|
+
function processYield(value, state, multi) {
|
|
571
|
+
if (isDeclareSteps(value)) {
|
|
572
|
+
for (const label of value.declare) {
|
|
573
|
+
const handle = multi.add(label, {
|
|
574
|
+
type: "spinner",
|
|
575
|
+
indent: 1,
|
|
576
|
+
insertAfter: state.lastInsertId
|
|
577
|
+
});
|
|
578
|
+
state.lastInsertId = handle.id;
|
|
579
|
+
state.declaredSteps.set(label, handle);
|
|
580
|
+
}
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (typeof value === "string") {
|
|
584
|
+
if (state.currentHandle && state.currentLabel) {
|
|
585
|
+
const elapsed = Date.now() - state.subStepStartTime;
|
|
586
|
+
state.currentHandle.complete(elapsed);
|
|
587
|
+
}
|
|
588
|
+
state.currentLabel = value;
|
|
589
|
+
state.subStepStartTime = Date.now();
|
|
590
|
+
const declared = state.declaredSteps.get(value);
|
|
591
|
+
if (declared) {
|
|
592
|
+
state.currentHandle = declared;
|
|
593
|
+
state.currentHandle.start();
|
|
594
|
+
} else {
|
|
595
|
+
state.currentHandle = multi.add(value, {
|
|
596
|
+
type: "spinner",
|
|
597
|
+
indent: 1,
|
|
598
|
+
insertAfter: state.lastInsertId
|
|
599
|
+
});
|
|
600
|
+
state.lastInsertId = state.currentHandle.id;
|
|
601
|
+
state.currentHandle.start();
|
|
602
|
+
}
|
|
603
|
+
} else if (value && typeof value === "object") {
|
|
604
|
+
const { current, total } = value;
|
|
605
|
+
if (state.currentHandle && total && total > 0) state.currentHandle.setTitle(`${state.currentLabel} (${current ?? 0}/${total})`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
function isDeclareSteps(value) {
|
|
609
|
+
return value !== null && typeof value === "object" && "declare" in value && Array.isArray(value.declare);
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Run an async generator step
|
|
613
|
+
*/
|
|
614
|
+
async function runAsyncGenerator(gen, parentHandle, parentTitle, multi) {
|
|
615
|
+
const state = {
|
|
616
|
+
currentLabel: void 0,
|
|
617
|
+
currentHandle: null,
|
|
618
|
+
lastInsertId: parentHandle.id,
|
|
619
|
+
subStepStartTime: Date.now(),
|
|
620
|
+
startTime: Date.now(),
|
|
621
|
+
declaredSteps: /* @__PURE__ */ new Map()
|
|
622
|
+
};
|
|
623
|
+
let result = await gen.next();
|
|
624
|
+
while (!result.done) {
|
|
625
|
+
processYield(result.value, state, multi);
|
|
626
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
627
|
+
result = await gen.next();
|
|
628
|
+
}
|
|
629
|
+
if (state.currentHandle && state.currentLabel) {
|
|
630
|
+
const elapsed = Date.now() - state.subStepStartTime;
|
|
631
|
+
state.currentHandle.complete(elapsed);
|
|
632
|
+
}
|
|
633
|
+
const totalElapsed = Date.now() - state.startTime;
|
|
634
|
+
parentHandle.complete(totalElapsed);
|
|
635
|
+
return result.value;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Run a sync generator step
|
|
639
|
+
*/
|
|
640
|
+
async function runSyncGenerator(gen, parentHandle, parentTitle, multi) {
|
|
641
|
+
const state = {
|
|
642
|
+
currentLabel: void 0,
|
|
643
|
+
currentHandle: null,
|
|
644
|
+
lastInsertId: parentHandle.id,
|
|
645
|
+
subStepStartTime: Date.now(),
|
|
646
|
+
startTime: Date.now(),
|
|
647
|
+
declaredSteps: /* @__PURE__ */ new Map()
|
|
648
|
+
};
|
|
649
|
+
let result = gen.next();
|
|
650
|
+
while (!result.done) {
|
|
651
|
+
processYield(result.value, state, multi);
|
|
652
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
653
|
+
result = gen.next();
|
|
654
|
+
}
|
|
655
|
+
if (state.currentHandle && state.currentLabel) {
|
|
656
|
+
const elapsed = Date.now() - state.subStepStartTime;
|
|
657
|
+
state.currentHandle.complete(elapsed);
|
|
658
|
+
}
|
|
659
|
+
const totalElapsed = Date.now() - state.startTime;
|
|
660
|
+
parentHandle.complete(totalElapsed);
|
|
661
|
+
return result.value;
|
|
662
|
+
}
|
|
663
|
+
function isAsyncGenerator(value) {
|
|
664
|
+
return value !== null && typeof value === "object" && typeof value.next === "function" && typeof value[Symbol.asyncIterator] === "function";
|
|
665
|
+
}
|
|
666
|
+
function isSyncGenerator(value) {
|
|
667
|
+
return value !== null && typeof value === "object" && typeof value.next === "function" && typeof value[Symbol.iterator] === "function";
|
|
668
|
+
}
|
|
669
|
+
function isPromiseLike$2(value) {
|
|
670
|
+
return value !== null && typeof value === "object" && typeof value.then === "function";
|
|
671
|
+
}
|
|
672
|
+
//#endregion
|
|
673
|
+
//#region packages/ag-react/src/ui/progress/task.ts
|
|
674
|
+
/** Phase labels for common operations */
|
|
675
|
+
const PHASE_LABELS$1 = {
|
|
676
|
+
reading: "Reading events",
|
|
677
|
+
applying: "Applying events",
|
|
678
|
+
rules: "Evaluating rules",
|
|
679
|
+
scanning: "Scanning files",
|
|
680
|
+
reconciling: "Reconciling changes",
|
|
681
|
+
board: "Building view"
|
|
682
|
+
};
|
|
683
|
+
/**
|
|
684
|
+
* Create a task wrapper with spinner
|
|
685
|
+
*
|
|
686
|
+
* @param title - Display title for the task
|
|
687
|
+
* @returns TaskWrapper with wrap() method
|
|
688
|
+
*/
|
|
689
|
+
function task(title) {
|
|
690
|
+
return { async wrap(work) {
|
|
691
|
+
const spinner = createSpinner();
|
|
692
|
+
spinner(title);
|
|
693
|
+
try {
|
|
694
|
+
if (typeof work === "function") {
|
|
695
|
+
const result = work();
|
|
696
|
+
if (isGenerator$1(result)) return await runGenerator$1(result, spinner, title);
|
|
697
|
+
if (isPromiseLike$1(result)) {
|
|
698
|
+
const value = await result;
|
|
699
|
+
spinner.succeed(title);
|
|
700
|
+
return value;
|
|
701
|
+
}
|
|
702
|
+
spinner.succeed(title);
|
|
703
|
+
return result;
|
|
704
|
+
}
|
|
705
|
+
if (isPromiseLike$1(work)) {
|
|
706
|
+
const value = await work;
|
|
707
|
+
spinner.succeed(title);
|
|
708
|
+
return value;
|
|
709
|
+
}
|
|
710
|
+
spinner.succeed(title);
|
|
711
|
+
return work;
|
|
712
|
+
} catch (error) {
|
|
713
|
+
spinner.fail(title);
|
|
714
|
+
throw error;
|
|
715
|
+
}
|
|
716
|
+
} };
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Run a generator with progress updates
|
|
720
|
+
*/
|
|
721
|
+
async function runGenerator$1(gen, spinner, baseTitle) {
|
|
722
|
+
let result = gen.next();
|
|
723
|
+
while (!result.done) {
|
|
724
|
+
const info = result.value;
|
|
725
|
+
const phase = info.phase ?? "";
|
|
726
|
+
const phaseLabel = PHASE_LABELS$1[phase] ?? (phase || baseTitle);
|
|
727
|
+
if (info.total && info.total > 0) spinner(`${phaseLabel} (${info.current}/${info.total})`);
|
|
728
|
+
else spinner(phaseLabel);
|
|
729
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
730
|
+
result = gen.next();
|
|
731
|
+
}
|
|
732
|
+
spinner.succeed(baseTitle);
|
|
733
|
+
return result.value;
|
|
734
|
+
}
|
|
735
|
+
function isGenerator$1(value) {
|
|
736
|
+
return value !== null && typeof value === "object" && typeof value.next === "function" && typeof value.throw === "function";
|
|
737
|
+
}
|
|
738
|
+
function isPromiseLike$1(value) {
|
|
739
|
+
return value !== null && typeof value === "object" && typeof value.then === "function";
|
|
740
|
+
}
|
|
741
|
+
//#endregion
|
|
742
|
+
//#region packages/ag-react/src/ui/progress/tasks.ts
|
|
743
|
+
/** Phase labels for common operations */
|
|
744
|
+
const PHASE_LABELS = {
|
|
745
|
+
discover: "Discovering files",
|
|
746
|
+
parse: "Parsing markdown",
|
|
747
|
+
apply: "Applying changes",
|
|
748
|
+
resolve: "Resolving links",
|
|
749
|
+
materialize: "Evaluating rules",
|
|
750
|
+
board: "Building view",
|
|
751
|
+
reading: "Reading events",
|
|
752
|
+
applying: "Applying events",
|
|
753
|
+
rules: "Evaluating rules",
|
|
754
|
+
scanning: "Scanning files",
|
|
755
|
+
reconciling: "Reconciling changes"
|
|
756
|
+
};
|
|
757
|
+
/**
|
|
758
|
+
* Create a sequential task builder
|
|
759
|
+
*
|
|
760
|
+
* @returns TaskBuilder with add() and run() methods
|
|
761
|
+
*/
|
|
762
|
+
function tasks() {
|
|
763
|
+
const taskList = [];
|
|
764
|
+
const builder = {
|
|
765
|
+
add(title, work) {
|
|
766
|
+
taskList.push({
|
|
767
|
+
title,
|
|
768
|
+
work
|
|
769
|
+
});
|
|
770
|
+
return builder;
|
|
771
|
+
},
|
|
772
|
+
async run(options) {
|
|
773
|
+
const multi = new MultiProgress();
|
|
774
|
+
const handles = /* @__PURE__ */ new Map();
|
|
775
|
+
const results = {};
|
|
776
|
+
for (const task of taskList) handles.set(task.title, multi.add(task.title, { type: "spinner" }));
|
|
777
|
+
multi.start();
|
|
778
|
+
try {
|
|
779
|
+
for (const task of taskList) {
|
|
780
|
+
const handle = handles.get(task.title);
|
|
781
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
782
|
+
const result = task.work();
|
|
783
|
+
if (isGenerator(result)) results[task.title] = await runGenerator(result, handle, task.title, multi);
|
|
784
|
+
else if (isPromiseLike(result)) {
|
|
785
|
+
handle.start();
|
|
786
|
+
results[task.title] = await result;
|
|
787
|
+
handle.complete();
|
|
788
|
+
} else {
|
|
789
|
+
handle.start();
|
|
790
|
+
results[task.title] = result;
|
|
791
|
+
handle.complete();
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
} finally {
|
|
795
|
+
multi.stop(options?.clear ?? false);
|
|
796
|
+
}
|
|
797
|
+
return results;
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
return builder;
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Run a generator task with progress updates
|
|
804
|
+
* Parent task stays visible while sub-phases are indented below
|
|
805
|
+
*/
|
|
806
|
+
async function runGenerator(gen, parentHandle, baseTitle, multi) {
|
|
807
|
+
let result = gen.next();
|
|
808
|
+
let currentPhase;
|
|
809
|
+
let currentPhaseHandle = null;
|
|
810
|
+
let lastInsertId = parentHandle.id;
|
|
811
|
+
let phaseStartTime = Date.now();
|
|
812
|
+
const taskStartTime = Date.now();
|
|
813
|
+
while (!result.done) {
|
|
814
|
+
const info = result.value;
|
|
815
|
+
const phase = info.phase ?? "";
|
|
816
|
+
if (phase && phase !== currentPhase) {
|
|
817
|
+
if (currentPhaseHandle && currentPhase) {
|
|
818
|
+
const elapsed = Date.now() - phaseStartTime;
|
|
819
|
+
const prevLabel = PHASE_LABELS[currentPhase] ?? currentPhase;
|
|
820
|
+
currentPhaseHandle.complete(`${prevLabel} (${elapsed}ms)`);
|
|
821
|
+
}
|
|
822
|
+
currentPhase = phase;
|
|
823
|
+
phaseStartTime = Date.now();
|
|
824
|
+
const phaseLabel = PHASE_LABELS[phase] ?? phase;
|
|
825
|
+
currentPhaseHandle = multi.add(phaseLabel, {
|
|
826
|
+
type: "spinner",
|
|
827
|
+
indent: 1,
|
|
828
|
+
insertAfter: lastInsertId
|
|
829
|
+
});
|
|
830
|
+
lastInsertId = currentPhaseHandle.id;
|
|
831
|
+
currentPhaseHandle.start();
|
|
832
|
+
}
|
|
833
|
+
if (currentPhaseHandle && info.total && info.total > 0) {
|
|
834
|
+
const phaseLabel = PHASE_LABELS[phase] ?? phase;
|
|
835
|
+
currentPhaseHandle.setTitle(`${phaseLabel} (${info.current}/${info.total})`);
|
|
836
|
+
}
|
|
837
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
838
|
+
result = gen.next();
|
|
839
|
+
}
|
|
840
|
+
if (currentPhaseHandle && currentPhase) {
|
|
841
|
+
const elapsed = Date.now() - phaseStartTime;
|
|
842
|
+
const finalLabel = PHASE_LABELS[currentPhase] ?? currentPhase;
|
|
843
|
+
currentPhaseHandle.complete(`${finalLabel} (${elapsed}ms)`);
|
|
844
|
+
}
|
|
845
|
+
const totalElapsed = Date.now() - taskStartTime;
|
|
846
|
+
parentHandle.complete(`${baseTitle} (${totalElapsed}ms)`);
|
|
847
|
+
return result.value;
|
|
848
|
+
}
|
|
849
|
+
function isGenerator(value) {
|
|
850
|
+
return value !== null && typeof value === "object" && typeof value.next === "function" && typeof value.throw === "function";
|
|
851
|
+
}
|
|
852
|
+
function isPromiseLike(value) {
|
|
853
|
+
return value !== null && typeof value === "object" && typeof value.then === "function";
|
|
854
|
+
}
|
|
855
|
+
//#endregion
|
|
856
|
+
export { MultiProgress, ProgressBar, Spinner, createSpinner, step, steps, task, tasks };
|
|
857
|
+
|
|
858
|
+
//# sourceMappingURL=progress.mjs.map
|