effective-progress 0.2.4 → 0.4.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 CHANGED
@@ -11,13 +11,13 @@
11
11
 
12
12
  <img alt="Showcase output" src="docs/images/showcase.gif" width="600" />
13
13
 
14
- `effective-progress` is an [Effect](https://effect.website/)-first terminal progress library with:
14
+ `effective-progress` is an [Effect](https://effect.website/)-first terminal progress-bar library with:
15
15
 
16
- - multiple progress bars
17
- - nested child progress bars
18
- - spinner support for indeterminate work
19
- - clean log rendering alongside progress output, allowing you to simply use Effects `Console.log` or `Effect.logInfo`.
20
- - simple to use `.all` and `.forEach` APIs similar to the ones you already know and love from `effect`. Just swap `Effect` for `Progress` and get progress bars for free!
16
+ - multiple nested tree-like progress bars
17
+ - spinner support for “we have no idea how long this takes” work
18
+ - clean log rendering alongside progress output, so you can keep using `Console.log` / `Effect.logInfo` without wrecking the UI
19
+ - familiar `.all` and `.forEach` APIs swap `Effect` for `Progress`, get progress bars basically for free
20
+ - flicker-free rendering (in theory) by drawing everything in a single terminal frame
21
21
 
22
22
  ## Install
23
23
 
@@ -94,6 +94,12 @@ Effect.runPromise(program);
94
94
 
95
95
  Configure global renderer behavior once, and a global base progress bar style:
96
96
 
97
+ Defaults:
98
+
99
+ - determinate layout: `single-line`
100
+ - max task width cap: unset (uses terminal width)
101
+ - bar width: `40`
102
+
97
103
  ```ts
98
104
  import { Effect } from "effect";
99
105
  import * as Progress from "effective-progress";
@@ -105,10 +111,6 @@ const configured = program.pipe(
105
111
  }),
106
112
  Effect.provideService(Progress.ProgressBarConfig, {
107
113
  barWidth: 36,
108
- colors: {
109
- fill: { kind: "hex", value: "#00b894" },
110
- spinner: { kind: "ansi256", value: 214 },
111
- },
112
114
  }),
113
115
  );
114
116
 
@@ -124,9 +126,6 @@ yield *
124
126
  progressbar: {
125
127
  barWidth: 20,
126
128
  spinnerFrames: [".", "o", "O", "0"],
127
- colors: {
128
- spinner: { kind: "named", value: "magentaBright" },
129
- },
130
129
  },
131
130
  });
132
131
  ```
@@ -175,40 +174,46 @@ const program = Progress.task(
175
174
  );
176
175
  ```
177
176
 
178
- ## Progress bar colors
177
+ ## Themes and render stages
179
178
 
180
- `progressbar.colors` is configured with typed color tokens that are validated by Effect Schema.
179
+ Coloring is configured through the `Theme` service.
181
180
 
182
181
  ```ts
183
- progressbar: {
184
- spinnerFrames: ["-", "\\", "|", "/"],
185
- barWidth: 30,
186
- fillChar: "━",
187
- emptyChar: "─",
188
- leftBracket: "",
189
- rightBracket: "",
190
- colors: {
191
- fill: { kind: "named", value: "cyan" },
192
- empty: { kind: "hex", value: "#9ca3af", modifiers: ["dim"] },
193
- brackets: { kind: "rgb", value: { r: 156, g: 163, b: 175 } },
194
- percent: { kind: "named", value: "whiteBright", modifiers: ["bold"] },
195
- spinner: { kind: "ansi256", value: 214 },
196
- done: { kind: "named", value: "greenBright" },
197
- failed: { kind: "named", value: "redBright", modifiers: ["bold"] },
198
- },
199
- }
200
- ```
182
+ import chalk from "chalk";
183
+ import { Effect } from "effect";
184
+ import * as Progress from "effective-progress";
201
185
 
202
- Supported color styles:
186
+ const program = Progress.task(myEffect, { description: "Work" }).pipe(
187
+ Effect.provideService(
188
+ Progress.Theme,
189
+ Progress.Theme.of({
190
+ styles: {
191
+ plain: (text) => text,
192
+ barFill: chalk.hex("#00b894"),
193
+ barEmpty: chalk.white.dim,
194
+ barBracket: chalk.rgb(180, 190, 210),
195
+ spinner: chalk.ansi256(214),
196
+ statusDone: chalk.greenBright,
197
+ statusFailed: chalk.redBright.bold,
198
+ text: chalk.white,
199
+ units: chalk.whiteBright.bold,
200
+ eta: chalk.gray,
201
+ elapsed: chalk.gray,
202
+ treeConnector: chalk.gray,
203
+ },
204
+ depthPalette: (depth, role) => (role === "text" && depth > 0 ? chalk.cyanBright : undefined),
205
+ }),
206
+ ),
207
+ );
208
+ ```
203
209
 
204
- - `named` (for example `cyan`, `greenBright`)
205
- - `hex` (for example `#00b894`)
206
- - `rgb` (for example `{ r: 0, g: 184, b: 148 }`)
207
- - `ansi256` (for example `214`)
210
+ Render internals are split into overrideable stages:
208
211
 
209
- Supported modifiers:
212
+ - `BuildStage`: logical rows/cells/segments
213
+ - `ShrinkStage`: width fitting/collapse
214
+ - `ColorStage`: role -> styled terminal strings
210
215
 
211
- - `bold`, `dim`, `italic`, `underline`, `inverse`, `hidden`, `strikethrough`
216
+ You can replace any stage with `Effect.provideService(...)` while keeping defaults for the rest.
212
217
 
213
218
  ## Dependencies & package size
214
219
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effective-progress",
3
- "version": "0.2.4",
3
+ "version": "0.4.0",
4
4
  "description": "Effect-first terminal progress bars with nested multibar support",
5
5
  "homepage": "https://github.com/stromseng/effective-progress#readme",
6
6
  "bugs": {
package/src/api.ts CHANGED
@@ -1,11 +1,20 @@
1
- import { Effect, Exit } from "effect";
1
+ import { Effect, Exit, Option } from "effect";
2
2
  import { dual } from "effect/Function";
3
3
  import type { Concurrency } from "effect/Types";
4
- import { Progress, provideProgressService } from "./runtime";
4
+ import { Progress } from "./runtime";
5
5
  import { Task } from "./types";
6
6
  import type { AddTaskOptions, TrackOptions } from "./types";
7
7
  import { inferTotal } from "./utils";
8
8
 
9
+ const provideProgress = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
10
+ Effect.gen(function* () {
11
+ const existing = yield* Effect.serviceOption(Progress);
12
+ if (Option.isSome(existing)) {
13
+ return yield* Effect.provideService(effect, Progress, existing.value);
14
+ }
15
+ return yield* Effect.scoped(effect.pipe(Effect.provide(Progress.Default)));
16
+ });
17
+
9
18
  export interface EffectExecutionOptions {
10
19
  readonly concurrency?: Concurrency;
11
20
  readonly batching?: boolean | "inherit";
@@ -19,7 +28,9 @@ export interface EffectAllExecutionOptions extends EffectExecutionOptions {
19
28
 
20
29
  export type AllOptions = Omit<TrackOptions, "total"> & EffectAllExecutionOptions;
21
30
  export type AllReturn<
22
- Arg extends ReadonlyArray<Effect.Effect<any, any, any>> | Record<string, Effect.Effect<any, any, any>>,
31
+ Arg extends
32
+ | ReadonlyArray<Effect.Effect<any, any, any>>
33
+ | Record<string, Effect.Effect<any, any, any>>,
23
34
  O extends EffectAllExecutionOptions,
24
35
  > = [
25
36
  [Arg] extends [ReadonlyArray<Effect.Effect<any, any, any>>]
@@ -48,7 +59,7 @@ export const task: {
48
59
  } = dual(
49
60
  2,
50
61
  <A, E, R>(effect: Effect.Effect<A, E, R>, options: AddTaskOptions) =>
51
- provideProgressService(
62
+ provideProgress(
52
63
  Effect.gen(function* () {
53
64
  const progress = yield* Progress;
54
65
  return yield* progress.withTask(effect, options);
@@ -56,7 +67,9 @@ export const task: {
56
67
  ) as Effect.Effect<A, E, Exclude<R, Progress | Task>>,
57
68
  );
58
69
 
59
- type AllArg = ReadonlyArray<Effect.Effect<any, any, any>> | Record<string, Effect.Effect<any, any, any>>;
70
+ type AllArg =
71
+ | ReadonlyArray<Effect.Effect<any, any, any>>
72
+ | Record<string, Effect.Effect<any, any, any>>;
60
73
 
61
74
  const wrapEffects = (
62
75
  effects: AllArg,
@@ -83,7 +96,7 @@ export const all: {
83
96
  effects: Arg,
84
97
  options: Omit<TrackOptions, "total"> & O,
85
98
  ) =>
86
- provideProgressService(
99
+ provideProgress(
87
100
  Effect.gen(function* () {
88
101
  const progress = yield* Progress;
89
102
  return yield* progress.runTask(
@@ -143,7 +156,7 @@ export const forEach: {
143
156
  f: (item: A, index: number) => Effect.Effect<B, E, R>,
144
157
  options: ForEachOptions,
145
158
  ) =>
146
- provideProgressService(
159
+ provideProgress(
147
160
  Effect.gen(function* () {
148
161
  const progress = yield* Progress;
149
162
 
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./api";
2
- export * from "./colors";
2
+ export * from "./renderer";
3
3
  export { Progress } from "./runtime";
4
4
  export * from "./terminal";
5
+ export * from "./theme";
5
6
  export * from "./types";