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,413 @@
|
|
|
1
|
+
import { n as SPINNER_INTERVALS, t as SPINNER_FRAMES } from "../spinner-DSByknyx.mjs";
|
|
2
|
+
import { a as getETA } from "../eta-DLiVPaSD.mjs";
|
|
3
|
+
import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region packages/ag-react/src/ui/react/Spinner.tsx
|
|
6
|
+
/**
|
|
7
|
+
* React Spinner component for silvery/Ink TUI apps
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Animated spinner component for React TUI apps
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* import { Spinner } from "@silvery/ag-react/ui/react";
|
|
15
|
+
*
|
|
16
|
+
* function LoadingView() {
|
|
17
|
+
* return <Spinner label="Loading..." />;
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* // With style
|
|
21
|
+
* <Spinner label="Processing..." style="arc" color="yellow" />
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
function Spinner({ label, style = "dots", color = "cyan" }) {
|
|
25
|
+
const [frameIndex, setFrameIndex] = useState(0);
|
|
26
|
+
const frames = SPINNER_FRAMES[style];
|
|
27
|
+
const interval = SPINNER_INTERVALS[style];
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const timer = setInterval(() => {
|
|
30
|
+
setFrameIndex((i) => (i + 1) % frames.length);
|
|
31
|
+
}, interval);
|
|
32
|
+
return () => clearInterval(timer);
|
|
33
|
+
}, [frames.length, interval]);
|
|
34
|
+
const frame = frames[frameIndex];
|
|
35
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
36
|
+
"data-progressx-spinner": true,
|
|
37
|
+
"data-color": color,
|
|
38
|
+
children: [frame, label && /* @__PURE__ */ jsxs("span", { children: [" ", label] })]
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Hook for using spinner state in custom components
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* function CustomSpinner() {
|
|
47
|
+
* const frame = useSpinnerFrame("dots");
|
|
48
|
+
* return <Text color="cyan">{frame}</Text>;
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
function useSpinnerFrame(style = "dots") {
|
|
53
|
+
const [frameIndex, setFrameIndex] = useState(0);
|
|
54
|
+
const frames = SPINNER_FRAMES[style];
|
|
55
|
+
const interval = SPINNER_INTERVALS[style];
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const timer = setInterval(() => {
|
|
58
|
+
setFrameIndex((i) => (i + 1) % frames.length);
|
|
59
|
+
}, interval);
|
|
60
|
+
return () => clearInterval(timer);
|
|
61
|
+
}, [frames.length, interval]);
|
|
62
|
+
return frames[frameIndex];
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region packages/ag-react/src/ui/react/ProgressBar.tsx
|
|
66
|
+
/**
|
|
67
|
+
* React ProgressBar component for silvery/Ink TUI apps
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* Progress bar component for React TUI apps
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```tsx
|
|
74
|
+
* import { ProgressBar } from "@silvery/ag-react/ui/react";
|
|
75
|
+
*
|
|
76
|
+
* function DownloadProgress({ current, total }) {
|
|
77
|
+
* return (
|
|
78
|
+
* <ProgressBar
|
|
79
|
+
* value={current}
|
|
80
|
+
* total={total}
|
|
81
|
+
* showPercentage
|
|
82
|
+
* showETA
|
|
83
|
+
* />
|
|
84
|
+
* );
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
function ProgressBar({ value, total, width = 40, showPercentage = true, showETA = false, label, color = "cyan" }) {
|
|
89
|
+
const [eta, setEta] = useState("--:--");
|
|
90
|
+
const etaBuffer = useRef([]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
etaBuffer.current.push({
|
|
94
|
+
time: now,
|
|
95
|
+
value
|
|
96
|
+
});
|
|
97
|
+
if (etaBuffer.current.length > 10) etaBuffer.current.shift();
|
|
98
|
+
setEta(getETA(etaBuffer.current, value, total).formatted);
|
|
99
|
+
}, [value, total]);
|
|
100
|
+
const percent = total > 0 ? value / total : 0;
|
|
101
|
+
const percentDisplay = `${Math.round(percent * 100)}%`;
|
|
102
|
+
const filledWidth = Math.round(width * percent);
|
|
103
|
+
const emptyWidth = width - filledWidth;
|
|
104
|
+
const bar = "█".repeat(filledWidth) + "░".repeat(emptyWidth);
|
|
105
|
+
const parts = [];
|
|
106
|
+
if (label) parts.push(label);
|
|
107
|
+
parts.push(`[${bar}]`);
|
|
108
|
+
if (showPercentage) parts.push(percentDisplay.padStart(4));
|
|
109
|
+
if (showETA) parts.push(`ETA: ${eta}`);
|
|
110
|
+
return /* @__PURE__ */ jsx("span", {
|
|
111
|
+
"data-progressx-bar": true,
|
|
112
|
+
"data-color": color,
|
|
113
|
+
"data-percent": percent,
|
|
114
|
+
children: parts.join(" ")
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Hook for progress bar state management
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```tsx
|
|
122
|
+
* function MyProgress() {
|
|
123
|
+
* const { value, total, update, increment, eta, percent } = useProgressBar(100);
|
|
124
|
+
*
|
|
125
|
+
* useEffect(() => {
|
|
126
|
+
* const timer = setInterval(() => increment(), 100);
|
|
127
|
+
* return () => clearInterval(timer);
|
|
128
|
+
* }, []);
|
|
129
|
+
*
|
|
130
|
+
* return <Text>{percent}% - ETA: {eta}</Text>;
|
|
131
|
+
* }
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
function useProgressBar(initialTotal) {
|
|
135
|
+
const [value, setValue] = useState(0);
|
|
136
|
+
const [total, setTotal] = useState(initialTotal);
|
|
137
|
+
const etaBuffer = useRef([]);
|
|
138
|
+
const [eta, setEta] = useState("--:--");
|
|
139
|
+
const update = (newValue) => {
|
|
140
|
+
setValue(newValue);
|
|
141
|
+
const now = Date.now();
|
|
142
|
+
etaBuffer.current.push({
|
|
143
|
+
time: now,
|
|
144
|
+
value: newValue
|
|
145
|
+
});
|
|
146
|
+
if (etaBuffer.current.length > 10) etaBuffer.current.shift();
|
|
147
|
+
setEta(getETA(etaBuffer.current, newValue, total).formatted);
|
|
148
|
+
};
|
|
149
|
+
const increment = (amount = 1) => update(value + amount);
|
|
150
|
+
const reset = (newTotal) => {
|
|
151
|
+
setValue(0);
|
|
152
|
+
if (newTotal !== void 0) setTotal(newTotal);
|
|
153
|
+
etaBuffer.current = [];
|
|
154
|
+
setEta("--:--");
|
|
155
|
+
};
|
|
156
|
+
return {
|
|
157
|
+
value,
|
|
158
|
+
total,
|
|
159
|
+
percent: total > 0 ? Math.round(value / total * 100) : 0,
|
|
160
|
+
eta,
|
|
161
|
+
update,
|
|
162
|
+
increment,
|
|
163
|
+
reset,
|
|
164
|
+
setTotal
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region packages/ag-react/src/ui/react/Tasks.tsx
|
|
169
|
+
/**
|
|
170
|
+
* React Tasks component - listr2-style task list for TUI apps
|
|
171
|
+
*/
|
|
172
|
+
/** Status icons for tasks */
|
|
173
|
+
const STATUS_ICONS = {
|
|
174
|
+
pending: "○",
|
|
175
|
+
running: "",
|
|
176
|
+
completed: "✔",
|
|
177
|
+
failed: "✖",
|
|
178
|
+
skipped: "⊘"
|
|
179
|
+
};
|
|
180
|
+
/** Status colors */
|
|
181
|
+
const STATUS_COLORS = {
|
|
182
|
+
pending: "gray",
|
|
183
|
+
running: "cyan",
|
|
184
|
+
completed: "green",
|
|
185
|
+
failed: "red",
|
|
186
|
+
skipped: "yellow"
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Single task component
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```tsx
|
|
193
|
+
* <Task title="Downloading files" status="running">
|
|
194
|
+
* <ProgressBar value={50} total={100} />
|
|
195
|
+
* </Task>
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
function Task({ title, status, children }) {
|
|
199
|
+
const spinnerFrame = useSpinnerFrame("dots");
|
|
200
|
+
const icon = status === "running" ? spinnerFrame : STATUS_ICONS[status];
|
|
201
|
+
const color = STATUS_COLORS[status];
|
|
202
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
203
|
+
"data-progressx-task": true,
|
|
204
|
+
"data-status": status,
|
|
205
|
+
"data-color": color,
|
|
206
|
+
children: [
|
|
207
|
+
/* @__PURE__ */ jsx("span", {
|
|
208
|
+
"data-icon": true,
|
|
209
|
+
children: icon
|
|
210
|
+
}),
|
|
211
|
+
/* @__PURE__ */ jsxs("span", {
|
|
212
|
+
"data-title": true,
|
|
213
|
+
children: [" ", title]
|
|
214
|
+
}),
|
|
215
|
+
children != null ? /* @__PURE__ */ jsx("div", {
|
|
216
|
+
"data-children": true,
|
|
217
|
+
children
|
|
218
|
+
}) : null
|
|
219
|
+
]
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Container for multiple tasks
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```tsx
|
|
227
|
+
* <Tasks>
|
|
228
|
+
* <Task title="Scanning files" status="completed" />
|
|
229
|
+
* <Task title="Processing" status="running">
|
|
230
|
+
* <ProgressBar value={current} total={total} />
|
|
231
|
+
* </Task>
|
|
232
|
+
* <Task title="Cleanup" status="pending" />
|
|
233
|
+
* </Tasks>
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
function Tasks({ children }) {
|
|
237
|
+
return /* @__PURE__ */ jsx("div", {
|
|
238
|
+
"data-progressx-tasks": true,
|
|
239
|
+
children
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Hook for managing task state
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```tsx
|
|
247
|
+
* function MyTasks() {
|
|
248
|
+
* const { tasks, start, complete, fail, updateProgress } = useTasks([
|
|
249
|
+
* { id: 'scan', title: 'Scanning' },
|
|
250
|
+
* { id: 'process', title: 'Processing' },
|
|
251
|
+
* ]);
|
|
252
|
+
*
|
|
253
|
+
* useEffect(() => {
|
|
254
|
+
* start('scan');
|
|
255
|
+
* doScan().then(() => {
|
|
256
|
+
* complete('scan');
|
|
257
|
+
* start('process');
|
|
258
|
+
* });
|
|
259
|
+
* }, []);
|
|
260
|
+
*
|
|
261
|
+
* return (
|
|
262
|
+
* <Tasks>
|
|
263
|
+
* {tasks.map(t => <Task key={t.id} title={t.title} status={t.status} />)}
|
|
264
|
+
* </Tasks>
|
|
265
|
+
* );
|
|
266
|
+
* }
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
function useTasks(initialTasks) {
|
|
270
|
+
const [tasks, setTasks] = React.useState(initialTasks.map((t) => ({
|
|
271
|
+
...t,
|
|
272
|
+
status: "pending"
|
|
273
|
+
})));
|
|
274
|
+
const updateTask = (id, updates) => {
|
|
275
|
+
setTasks((prev) => prev.map((t) => t.id === id ? {
|
|
276
|
+
...t,
|
|
277
|
+
...updates
|
|
278
|
+
} : t));
|
|
279
|
+
};
|
|
280
|
+
const start = (id) => updateTask(id, { status: "running" });
|
|
281
|
+
const complete = (id, title) => updateTask(id, {
|
|
282
|
+
status: "completed",
|
|
283
|
+
...title && { title }
|
|
284
|
+
});
|
|
285
|
+
const fail = (id, title) => updateTask(id, {
|
|
286
|
+
status: "failed",
|
|
287
|
+
...title && { title }
|
|
288
|
+
});
|
|
289
|
+
const skip = (id, title) => updateTask(id, {
|
|
290
|
+
status: "skipped",
|
|
291
|
+
...title && { title }
|
|
292
|
+
});
|
|
293
|
+
const updateProgress = (id, progress) => updateTask(id, { progress });
|
|
294
|
+
const getTask = (id) => tasks.find((t) => t.id === id);
|
|
295
|
+
return {
|
|
296
|
+
tasks,
|
|
297
|
+
start,
|
|
298
|
+
complete,
|
|
299
|
+
fail,
|
|
300
|
+
skip,
|
|
301
|
+
updateProgress,
|
|
302
|
+
updateTask,
|
|
303
|
+
getTask,
|
|
304
|
+
allCompleted: tasks.every((t) => t.status === "completed" || t.status === "skipped"),
|
|
305
|
+
hasFailed: tasks.some((t) => t.status === "failed")
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region packages/ag-react/src/ui/react/context.tsx
|
|
310
|
+
/**
|
|
311
|
+
* React context for progress state management
|
|
312
|
+
*/
|
|
313
|
+
const ProgressContext = createContext(null);
|
|
314
|
+
/**
|
|
315
|
+
* Progress context provider
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* ```tsx
|
|
319
|
+
* function App() {
|
|
320
|
+
* return (
|
|
321
|
+
* <ProgressProvider>
|
|
322
|
+
* <MyApp />
|
|
323
|
+
* </ProgressProvider>
|
|
324
|
+
* );
|
|
325
|
+
* }
|
|
326
|
+
*
|
|
327
|
+
* function DeepComponent() {
|
|
328
|
+
* const { showSpinner, hideSpinner } = useProgress();
|
|
329
|
+
*
|
|
330
|
+
* const handleLoad = async () => {
|
|
331
|
+
* showSpinner("Loading...");
|
|
332
|
+
* await loadData();
|
|
333
|
+
* hideSpinner();
|
|
334
|
+
* };
|
|
335
|
+
* }
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
function ProgressProvider({ children }) {
|
|
339
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
340
|
+
const [loadingText, setLoadingText] = useState("");
|
|
341
|
+
const [spinnerStyle, setSpinnerStyle] = useState("dots");
|
|
342
|
+
const [progress, setProgress] = useState(null);
|
|
343
|
+
const value = {
|
|
344
|
+
isLoading,
|
|
345
|
+
loadingText,
|
|
346
|
+
spinnerStyle,
|
|
347
|
+
showSpinner: useCallback((text, style = "dots") => {
|
|
348
|
+
setLoadingText(text);
|
|
349
|
+
setSpinnerStyle(style);
|
|
350
|
+
setIsLoading(true);
|
|
351
|
+
}, []),
|
|
352
|
+
hideSpinner: useCallback(() => {
|
|
353
|
+
setIsLoading(false);
|
|
354
|
+
setLoadingText("");
|
|
355
|
+
}, []),
|
|
356
|
+
progress,
|
|
357
|
+
updateProgress: useCallback((current, total) => {
|
|
358
|
+
setProgress((prev) => ({
|
|
359
|
+
current,
|
|
360
|
+
total: total ?? prev?.total ?? 100
|
|
361
|
+
}));
|
|
362
|
+
}, []),
|
|
363
|
+
clearProgress: useCallback(() => {
|
|
364
|
+
setProgress(null);
|
|
365
|
+
}, [])
|
|
366
|
+
};
|
|
367
|
+
return /* @__PURE__ */ jsx(ProgressContext.Provider, {
|
|
368
|
+
value,
|
|
369
|
+
children
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Hook to access progress context
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```tsx
|
|
377
|
+
* function LoadingOverlay() {
|
|
378
|
+
* const { isLoading, loadingText, spinnerStyle } = useProgress();
|
|
379
|
+
*
|
|
380
|
+
* if (!isLoading) return null;
|
|
381
|
+
*
|
|
382
|
+
* return <Spinner label={loadingText} style={spinnerStyle} />;
|
|
383
|
+
* }
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
function useProgress() {
|
|
387
|
+
const context = useContext(ProgressContext);
|
|
388
|
+
if (!context) throw new Error("useProgress must be used within a ProgressProvider");
|
|
389
|
+
return context;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Component that renders spinner when loading
|
|
393
|
+
*
|
|
394
|
+
* @example
|
|
395
|
+
* ```tsx
|
|
396
|
+
* <ProgressProvider>
|
|
397
|
+
* <ProgressIndicator />
|
|
398
|
+
* <MainContent />
|
|
399
|
+
* </ProgressProvider>
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
402
|
+
function ProgressIndicator() {
|
|
403
|
+
const { isLoading, loadingText, spinnerStyle } = useProgress();
|
|
404
|
+
if (!isLoading) return null;
|
|
405
|
+
return /* @__PURE__ */ jsx(Spinner, {
|
|
406
|
+
label: loadingText,
|
|
407
|
+
style: spinnerStyle
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
//#endregion
|
|
411
|
+
export { ProgressBar, ProgressIndicator, ProgressProvider, Spinner, Task, Tasks, useProgress, useProgressBar, useSpinnerFrame, useTasks };
|
|
412
|
+
|
|
413
|
+
//# sourceMappingURL=react.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.mjs","names":[],"sources":["../../packages/ag-react/src/ui/react/Spinner.tsx","../../packages/ag-react/src/ui/react/ProgressBar.tsx","../../packages/ag-react/src/ui/react/Tasks.tsx","../../packages/ag-react/src/ui/react/context.tsx"],"sourcesContent":["/**\n * React Spinner component for silvery/Ink TUI apps\n */\n\nimport React, { useState, useEffect } from \"react\"\nimport type { SpinnerProps, SpinnerStyle } from \"../types.js\"\nimport { SPINNER_FRAMES, SPINNER_INTERVALS } from \"../cli/spinner\"\n\n/**\n * Animated spinner component for React TUI apps\n *\n * @example\n * ```tsx\n * import { Spinner } from \"@silvery/ag-react/ui/react\";\n *\n * function LoadingView() {\n * return <Spinner label=\"Loading...\" />;\n * }\n *\n * // With style\n * <Spinner label=\"Processing...\" style=\"arc\" color=\"yellow\" />\n * ```\n */\nexport function Spinner({ label, style = \"dots\", color = \"cyan\" }: SpinnerProps): React.ReactElement {\n const [frameIndex, setFrameIndex] = useState(0)\n const frames = SPINNER_FRAMES[style]\n const interval = SPINNER_INTERVALS[style]\n\n useEffect(() => {\n const timer = setInterval(() => {\n setFrameIndex((i) => (i + 1) % frames.length)\n }, interval)\n\n return () => clearInterval(timer)\n }, [frames.length, interval])\n\n const frame = frames[frameIndex]\n\n // Note: In a real silvery app, you'd use <Text color={color}> etc.\n // This is a generic React component that can be styled by the consumer\n return (\n <span data-progressx-spinner data-color={color}>\n {frame}\n {label && <span> {label}</span>}\n </span>\n )\n}\n\n/**\n * Hook for using spinner state in custom components\n *\n * @example\n * ```tsx\n * function CustomSpinner() {\n * const frame = useSpinnerFrame(\"dots\");\n * return <Text color=\"cyan\">{frame}</Text>;\n * }\n * ```\n */\nexport function useSpinnerFrame(style: SpinnerStyle = \"dots\"): string {\n const [frameIndex, setFrameIndex] = useState(0)\n const frames = SPINNER_FRAMES[style]\n const interval = SPINNER_INTERVALS[style]\n\n useEffect(() => {\n const timer = setInterval(() => {\n setFrameIndex((i) => (i + 1) % frames.length)\n }, interval)\n\n return () => clearInterval(timer)\n }, [frames.length, interval])\n\n return frames[frameIndex]!\n}\n","/**\n * React ProgressBar component for silvery/Ink TUI apps\n */\n\nimport React, { useState, useEffect, useRef } from \"react\"\nimport type { ProgressBarProps } from \"../types.js\"\nimport { getETA, DEFAULT_ETA_BUFFER_SIZE, type ETASample } from \"../utils/eta\"\n\n/**\n * Progress bar component for React TUI apps\n *\n * @example\n * ```tsx\n * import { ProgressBar } from \"@silvery/ag-react/ui/react\";\n *\n * function DownloadProgress({ current, total }) {\n * return (\n * <ProgressBar\n * value={current}\n * total={total}\n * showPercentage\n * showETA\n * />\n * );\n * }\n * ```\n */\nexport function ProgressBar({\n value,\n total,\n width = 40,\n showPercentage = true,\n showETA = false,\n label,\n color = \"cyan\",\n}: ProgressBarProps): React.ReactElement {\n // ETA calculation state\n const [eta, setEta] = useState<string>(\"--:--\")\n const etaBuffer = useRef<ETASample[]>([])\n\n // Update ETA buffer when value changes\n useEffect(() => {\n const now = Date.now()\n etaBuffer.current.push({ time: now, value })\n\n if (etaBuffer.current.length > DEFAULT_ETA_BUFFER_SIZE) {\n etaBuffer.current.shift()\n }\n\n // Calculate ETA using shared utility\n const result = getETA(etaBuffer.current, value, total)\n setEta(result.formatted)\n }, [value, total])\n\n const percent = total > 0 ? value / total : 0\n const percentDisplay = `${Math.round(percent * 100)}%`\n\n const filledWidth = Math.round(width * percent)\n const emptyWidth = width - filledWidth\n\n const bar = \"█\".repeat(filledWidth) + \"░\".repeat(emptyWidth)\n\n // Build the display parts\n const parts: string[] = []\n\n if (label) {\n parts.push(label)\n }\n\n parts.push(`[${bar}]`)\n\n if (showPercentage) {\n parts.push(percentDisplay.padStart(4))\n }\n\n if (showETA) {\n parts.push(`ETA: ${eta}`)\n }\n\n return (\n <span data-progressx-bar data-color={color} data-percent={percent}>\n {parts.join(\" \")}\n </span>\n )\n}\n\n/**\n * Hook for progress bar state management\n *\n * @example\n * ```tsx\n * function MyProgress() {\n * const { value, total, update, increment, eta, percent } = useProgressBar(100);\n *\n * useEffect(() => {\n * const timer = setInterval(() => increment(), 100);\n * return () => clearInterval(timer);\n * }, []);\n *\n * return <Text>{percent}% - ETA: {eta}</Text>;\n * }\n * ```\n */\nexport function useProgressBar(initialTotal: number) {\n const [value, setValue] = useState(0)\n const [total, setTotal] = useState(initialTotal)\n const etaBuffer = useRef<ETASample[]>([])\n const [eta, setEta] = useState<string>(\"--:--\")\n\n const update = (newValue: number) => {\n setValue(newValue)\n\n // Update ETA buffer\n const now = Date.now()\n etaBuffer.current.push({ time: now, value: newValue })\n if (etaBuffer.current.length > DEFAULT_ETA_BUFFER_SIZE) {\n etaBuffer.current.shift()\n }\n\n // Calculate ETA using shared utility\n const result = getETA(etaBuffer.current, newValue, total)\n setEta(result.formatted)\n }\n\n const increment = (amount = 1) => update(value + amount)\n\n const reset = (newTotal?: number) => {\n setValue(0)\n if (newTotal !== undefined) setTotal(newTotal)\n etaBuffer.current = []\n setEta(\"--:--\")\n }\n\n const percent = total > 0 ? Math.round((value / total) * 100) : 0\n\n return {\n value,\n total,\n percent,\n eta,\n update,\n increment,\n reset,\n setTotal,\n }\n}\n","/**\n * React Tasks component - listr2-style task list for TUI apps\n */\n\nimport React from \"react\"\nimport type { TaskProps, TaskStatus } from \"../types.js\"\nimport { useSpinnerFrame } from \"./Spinner\"\n\n/** Status icons for tasks */\nconst STATUS_ICONS: Record<TaskStatus, string> = {\n pending: \"○\",\n running: \"\", // Will use spinner\n completed: \"✔\",\n failed: \"✖\",\n skipped: \"⊘\",\n}\n\n/** Status colors */\nconst STATUS_COLORS: Record<TaskStatus, string> = {\n pending: \"gray\",\n running: \"cyan\",\n completed: \"green\",\n failed: \"red\",\n skipped: \"yellow\",\n}\n\n/**\n * Single task component\n *\n * @example\n * ```tsx\n * <Task title=\"Downloading files\" status=\"running\">\n * <ProgressBar value={50} total={100} />\n * </Task>\n * ```\n */\nexport function Task({ title, status, children }: TaskProps): React.ReactElement {\n const spinnerFrame = useSpinnerFrame(\"dots\")\n const icon = status === \"running\" ? spinnerFrame : STATUS_ICONS[status]\n const color = STATUS_COLORS[status]\n\n return (\n <div data-progressx-task data-status={status} data-color={color}>\n <span data-icon>{icon}</span>\n <span data-title> {title}</span>\n {children != null ? <div data-children>{children as React.ReactNode}</div> : null}\n </div>\n )\n}\n\n/**\n * Container for multiple tasks\n *\n * @example\n * ```tsx\n * <Tasks>\n * <Task title=\"Scanning files\" status=\"completed\" />\n * <Task title=\"Processing\" status=\"running\">\n * <ProgressBar value={current} total={total} />\n * </Task>\n * <Task title=\"Cleanup\" status=\"pending\" />\n * </Tasks>\n * ```\n */\nexport function Tasks({ children }: { children: React.ReactNode }): React.ReactElement {\n return <div data-progressx-tasks>{children}</div>\n}\n\n/**\n * Hook for managing task state\n *\n * @example\n * ```tsx\n * function MyTasks() {\n * const { tasks, start, complete, fail, updateProgress } = useTasks([\n * { id: 'scan', title: 'Scanning' },\n * { id: 'process', title: 'Processing' },\n * ]);\n *\n * useEffect(() => {\n * start('scan');\n * doScan().then(() => {\n * complete('scan');\n * start('process');\n * });\n * }, []);\n *\n * return (\n * <Tasks>\n * {tasks.map(t => <Task key={t.id} title={t.title} status={t.status} />)}\n * </Tasks>\n * );\n * }\n * ```\n */\nexport function useTasks(initialTasks: Array<{ id: string; title: string }>) {\n const [tasks, setTasks] = React.useState<\n Array<{\n id: string\n title: string\n status: TaskStatus\n progress?: { current: number; total: number }\n }>\n >(\n initialTasks.map((t) => ({\n ...t,\n status: \"pending\" as TaskStatus,\n })),\n )\n\n const updateTask = (\n id: string,\n updates: Partial<{\n status: TaskStatus\n title: string\n progress: { current: number; total: number }\n }>,\n ) => {\n setTasks((prev) => prev.map((t) => (t.id === id ? { ...t, ...updates } : t)))\n }\n\n const start = (id: string) => updateTask(id, { status: \"running\" })\n const complete = (id: string, title?: string) => updateTask(id, { status: \"completed\", ...(title && { title }) })\n const fail = (id: string, title?: string) => updateTask(id, { status: \"failed\", ...(title && { title }) })\n const skip = (id: string, title?: string) => updateTask(id, { status: \"skipped\", ...(title && { title }) })\n const updateProgress = (id: string, progress: { current: number; total: number }) => updateTask(id, { progress })\n\n const getTask = (id: string) => tasks.find((t) => t.id === id)\n const allCompleted = tasks.every((t) => t.status === \"completed\" || t.status === \"skipped\")\n const hasFailed = tasks.some((t) => t.status === \"failed\")\n\n return {\n tasks,\n start,\n complete,\n fail,\n skip,\n updateProgress,\n updateTask,\n getTask,\n allCompleted,\n hasFailed,\n }\n}\n","/**\n * React context for progress state management\n */\n\nimport React, { createContext, useContext, useState, useCallback } from \"react\"\nimport type { SpinnerStyle } from \"../types.js\"\nimport { Spinner } from \"./Spinner\"\n\n/** Progress context state */\ninterface ProgressContextState {\n /** Currently showing a spinner */\n isLoading: boolean\n /** Loading message */\n loadingText: string\n /** Spinner style */\n spinnerStyle: SpinnerStyle\n\n /** Show a spinner with message */\n showSpinner: (text: string, style?: SpinnerStyle) => void\n /** Hide the spinner */\n hideSpinner: () => void\n\n /** Progress bar state */\n progress: { current: number; total: number } | null\n /** Update progress */\n updateProgress: (current: number, total?: number) => void\n /** Clear progress */\n clearProgress: () => void\n}\n\nconst ProgressContext = createContext<ProgressContextState | null>(null)\n\n/**\n * Progress context provider\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <ProgressProvider>\n * <MyApp />\n * </ProgressProvider>\n * );\n * }\n *\n * function DeepComponent() {\n * const { showSpinner, hideSpinner } = useProgress();\n *\n * const handleLoad = async () => {\n * showSpinner(\"Loading...\");\n * await loadData();\n * hideSpinner();\n * };\n * }\n * ```\n */\nexport function ProgressProvider({ children }: { children: React.ReactNode }): React.ReactElement {\n const [isLoading, setIsLoading] = useState(false)\n const [loadingText, setLoadingText] = useState(\"\")\n const [spinnerStyle, setSpinnerStyle] = useState<SpinnerStyle>(\"dots\")\n const [progress, setProgress] = useState<{\n current: number\n total: number\n } | null>(null)\n\n const showSpinner = useCallback((text: string, style: SpinnerStyle = \"dots\") => {\n setLoadingText(text)\n setSpinnerStyle(style)\n setIsLoading(true)\n }, [])\n\n const hideSpinner = useCallback(() => {\n setIsLoading(false)\n setLoadingText(\"\")\n }, [])\n\n const updateProgress = useCallback((current: number, total?: number) => {\n setProgress((prev) => ({\n current,\n total: total ?? prev?.total ?? 100,\n }))\n }, [])\n\n const clearProgress = useCallback(() => {\n setProgress(null)\n }, [])\n\n const value: ProgressContextState = {\n isLoading,\n loadingText,\n spinnerStyle,\n showSpinner,\n hideSpinner,\n progress,\n updateProgress,\n clearProgress,\n }\n\n return <ProgressContext.Provider value={value}>{children}</ProgressContext.Provider>\n}\n\n/**\n * Hook to access progress context\n *\n * @example\n * ```tsx\n * function LoadingOverlay() {\n * const { isLoading, loadingText, spinnerStyle } = useProgress();\n *\n * if (!isLoading) return null;\n *\n * return <Spinner label={loadingText} style={spinnerStyle} />;\n * }\n * ```\n */\nexport function useProgress(): ProgressContextState {\n const context = useContext(ProgressContext)\n\n if (!context) {\n throw new Error(\"useProgress must be used within a ProgressProvider\")\n }\n\n return context\n}\n\n/**\n * Component that renders spinner when loading\n *\n * @example\n * ```tsx\n * <ProgressProvider>\n * <ProgressIndicator />\n * <MainContent />\n * </ProgressProvider>\n * ```\n */\nexport function ProgressIndicator(): React.ReactElement | null {\n const { isLoading, loadingText, spinnerStyle } = useProgress()\n\n if (!isLoading) {\n return null\n }\n\n return <Spinner label={loadingText} style={spinnerStyle} />\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
//#region packages/ag-react/src/ui/utils/eta.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Shared ETA calculation utilities
|
|
4
|
+
*/
|
|
5
|
+
/** Sample point for ETA calculation */
|
|
6
|
+
interface ETASample {
|
|
7
|
+
time: number;
|
|
8
|
+
value: number;
|
|
9
|
+
}
|
|
10
|
+
/** ETA calculation result */
|
|
11
|
+
interface ETAResult {
|
|
12
|
+
/** Estimated seconds remaining, or null if insufficient data */
|
|
13
|
+
seconds: number | null;
|
|
14
|
+
/** Formatted ETA string (e.g., "1:30", "2:15:30", "--:--") */
|
|
15
|
+
formatted: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Calculate ETA from a buffer of samples
|
|
19
|
+
*
|
|
20
|
+
* @param buffer - Array of {time, value} samples
|
|
21
|
+
* @param current - Current progress value
|
|
22
|
+
* @param total - Total target value
|
|
23
|
+
* @returns ETA in seconds (null if insufficient data)
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const buffer = [
|
|
28
|
+
* { time: 1000, value: 0 },
|
|
29
|
+
* { time: 2000, value: 10 },
|
|
30
|
+
* ];
|
|
31
|
+
* const eta = calculateETA(buffer, 10, 100);
|
|
32
|
+
* // eta = 9 (9 seconds remaining at 10 items/sec)
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
declare function calculateETA(buffer: ETASample[], current: number, total: number): number | null;
|
|
36
|
+
/**
|
|
37
|
+
* Format ETA seconds as human-readable string
|
|
38
|
+
*
|
|
39
|
+
* @param eta - ETA in seconds (null for unknown)
|
|
40
|
+
* @returns Formatted string (e.g., "1:30", "2:15:30", "--:--", ">1d")
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* formatETA(90) // "1:30"
|
|
45
|
+
* formatETA(3665) // "1:01:05"
|
|
46
|
+
* formatETA(null) // "--:--"
|
|
47
|
+
* formatETA(100000) // ">1d"
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
declare function formatETA(eta: number | null): string;
|
|
51
|
+
/**
|
|
52
|
+
* Calculate and format ETA in one call
|
|
53
|
+
*
|
|
54
|
+
* @param buffer - Array of {time, value} samples
|
|
55
|
+
* @param current - Current progress value
|
|
56
|
+
* @param total - Total target value
|
|
57
|
+
* @returns Object with seconds (number|null) and formatted string
|
|
58
|
+
*/
|
|
59
|
+
declare function getETA(buffer: ETASample[], current: number, total: number): ETAResult;
|
|
60
|
+
/** Default buffer size for ETA smoothing */
|
|
61
|
+
declare const DEFAULT_ETA_BUFFER_SIZE = 10;
|
|
62
|
+
/**
|
|
63
|
+
* Create an ETA tracker with automatic buffer management
|
|
64
|
+
*
|
|
65
|
+
* @param bufferSize - Number of samples to keep (default: 10)
|
|
66
|
+
* @returns ETA tracker object
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const tracker = createETATracker();
|
|
71
|
+
* tracker.record(0);
|
|
72
|
+
* // ... later ...
|
|
73
|
+
* tracker.record(50);
|
|
74
|
+
* const eta = tracker.getETA(50, 100);
|
|
75
|
+
* console.log(eta.formatted); // "0:30"
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
declare function createETATracker(bufferSize?: number): {
|
|
79
|
+
/** Record a new sample */record(value: number): void; /** Get current ETA */
|
|
80
|
+
getETA(current: number, total: number): ETAResult; /** Reset the buffer */
|
|
81
|
+
reset(): void; /** Get buffer for external use */
|
|
82
|
+
getBuffer(): readonly ETASample[];
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
export { DEFAULT_ETA_BUFFER_SIZE, ETAResult, ETASample, calculateETA, createETATracker, formatETA, getETA };
|
|
86
|
+
//# sourceMappingURL=utils.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.mts","names":[],"sources":["../../packages/ag-react/src/ui/utils/eta.ts"],"mappings":";;AAKA;;;UAAiB,SAAA;EACf,IAAA;EACA,KAAA;AAAA;;UAIe,SAAA;EAIN;EAFT,OAAA;EAuB0B;EArB1B,SAAA;AAAA;;;;;;;AAwDF;;;;;AA6BA;;;;;;;iBAhEgB,YAAA,CAAa,MAAA,EAAQ,SAAA,IAAa,OAAA,UAAiB,KAAA;;;;AAyEnE;;;;;AAkBA;;;;;;iBAxDgB,SAAA,CAAU,GAAA;;;;;;;;;iBA6BV,MAAA,CAAO,MAAA,EAAQ,SAAA,IAAa,OAAA,UAAiB,KAAA,WAAgB,SAAA;;cAShE,uBAAA;;;;;;;;;;;;;;;;;iBAkBG,gBAAA,CAAiB,UAAA;;0BAaP,KAAA,WAAkB,SAAA;;wBAUlB,SAAA;AAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { _ as TextInputOptions, a as ProgressInfo, r as ProgressCallback } from "../types-CDgkE-Rw.mjs";
|
|
2
|
+
import { a as waitForEvent, c as wrapGenerator, d as attachSpinner, f as withSpinner, i as withSelect, l as createProgressCallback, n as withTextInput, o as wrapEmitter, r as createSelect, s as withIterableProgress, t as createTextInput, u as withProgress } from "../with-text-input-CmHf_9d6.mjs";
|
|
3
|
+
export { ProgressCallback, ProgressInfo, TextInputOptions, attachSpinner, createProgressCallback, createSelect, createTextInput, waitForEvent, withIterableProgress, withProgress, withSelect, withSpinner, withTextInput, wrapEmitter, wrapGenerator };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as waitForEvent, c as wrapGenerator, d as attachSpinner, f as withSpinner, i as withSelect, l as createProgressCallback, n as withTextInput, o as wrapEmitter, r as createSelect, s as withIterableProgress, t as createTextInput, u as withProgress } from "../wrappers-hhL8EQ_n.mjs";
|
|
2
|
+
export { attachSpinner, createProgressCallback, createSelect, createTextInput, waitForEvent, withIterableProgress, withProgress, withSelect, withSpinner, withTextInput, wrapEmitter, wrapGenerator };
|
package/dist/ui.d.mts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { _ as TextInputOptions, a as ProgressInfo, b as WithSelectOptions, c as SpinnerOptions, d as StepProgress, f as TableColumn, g as TaskStatus, h as TaskState, i as ProgressGenerator, l as SpinnerProps, m as TaskProps, n as ProgressBarProps, o as SelectOption, p as TableProps, r as ProgressCallback, s as SelectProps, t as ProgressBarOptions, u as SpinnerStyle, v as TextInputProps, x as WithSpinnerOptions, y as WithProgressOptions } from "./types-CDgkE-Rw.mjs";
|
|
2
|
+
import { i as createSpinner, n as SPINNER_FRAMES, r as Spinner, t as CallableSpinner } from "./spinner-Cgej6Vnb.mjs";
|
|
3
|
+
import { n as TaskHandle, r as ProgressBar, t as MultiProgress } from "./multi-progress-C0-rkn86.mjs";
|
|
4
|
+
import { a as CURSOR_RESTORE, c as CURSOR_TO_START, d as getTerminalWidth, f as isTTY, h as writeLine, i as CURSOR_HIDE, l as cursorDown, m as write, n as CLEAR_LINE_END, o as CURSOR_SAVE, p as withCursor, r as CLEAR_SCREEN, s as CURSOR_SHOW, t as CLEAR_LINE, u as cursorUp } from "./ansi-zmNzgkPB.mjs";
|
|
5
|
+
import { a as waitForEvent, c as wrapGenerator, d as attachSpinner, f as withSpinner, i as withSelect, l as createProgressCallback, n as withTextInput, o as wrapEmitter, r as createSelect, s as withIterableProgress, t as createTextInput, u as withProgress } from "./with-text-input-CmHf_9d6.mjs";
|
|
6
|
+
export { CLEAR_LINE, CLEAR_LINE_END, CLEAR_SCREEN, CURSOR_HIDE, CURSOR_RESTORE, CURSOR_SAVE, CURSOR_SHOW, CURSOR_TO_START, CallableSpinner, MultiProgress, ProgressBar, ProgressBarOptions, ProgressBarProps, ProgressCallback, ProgressGenerator, ProgressInfo, SPINNER_FRAMES, SelectOption, SelectProps, Spinner, SpinnerOptions, SpinnerProps, SpinnerStyle, StepProgress, TableColumn, TableProps, TaskHandle, TaskProps, TaskState, TaskStatus, TextInputOptions, TextInputProps, WithProgressOptions, WithSelectOptions, WithSpinnerOptions, attachSpinner, createProgressCallback, createSelect, createSpinner, createTextInput, cursorDown, cursorUp, getTerminalWidth, isTTY, waitForEvent, withCursor, withIterableProgress, withProgress, withSelect, withSpinner, withTextInput, wrapEmitter, wrapGenerator, write, writeLine };
|
package/dist/ui.mjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { i as createSpinner, r as Spinner, t as SPINNER_FRAMES } from "./spinner-DSByknyx.mjs";
|
|
2
|
+
import { a as CURSOR_RESTORE, c as CURSOR_TO_START, d as getTerminalWidth, f as isTTY, h as writeLine, i as CURSOR_HIDE, l as cursorDown, m as write, n as CLEAR_LINE_END, o as CURSOR_SAVE, p as withCursor, r as CLEAR_SCREEN, s as CURSOR_SHOW, t as CLEAR_LINE, u as cursorUp } from "./ansi-CXLE_pt1.mjs";
|
|
3
|
+
import { t as ProgressBar } from "./progress-bar-COPSBlT9.mjs";
|
|
4
|
+
import { t as MultiProgress } from "./multi-progress-CQVB9lES.mjs";
|
|
5
|
+
import "./cli-B-k7Bm56.mjs";
|
|
6
|
+
import { a as waitForEvent, c as wrapGenerator, d as attachSpinner, f as withSpinner, i as withSelect, l as createProgressCallback, n as withTextInput, o as wrapEmitter, r as createSelect, s as withIterableProgress, t as createTextInput, u as withProgress } from "./wrappers-hhL8EQ_n.mjs";
|
|
7
|
+
export { CLEAR_LINE, CLEAR_LINE_END, CLEAR_SCREEN, CURSOR_HIDE, CURSOR_RESTORE, CURSOR_SAVE, CURSOR_SHOW, CURSOR_TO_START, MultiProgress, ProgressBar, SPINNER_FRAMES, Spinner, attachSpinner, createProgressCallback, createSelect, createSpinner, createTextInput, cursorDown, cursorUp, getTerminalWidth, isTTY, waitForEvent, withCursor, withIterableProgress, withProgress, withSelect, withSpinner, withTextInput, wrapEmitter, wrapGenerator, write, writeLine };
|