blessed-components 1.0.0 → 1.1.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 +16 -3
- package/dist/index.cjs +75 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +74 -1
- package/dist/index.js.map +1 -1
- package/dist/sparkline/blessed.cjs +86 -0
- package/dist/sparkline/blessed.cjs.map +1 -0
- package/dist/sparkline/blessed.d.cts +90 -0
- package/dist/sparkline/blessed.d.ts +90 -0
- package/dist/sparkline/blessed.js +80 -0
- package/dist/sparkline/blessed.js.map +1 -0
- package/dist/sparkline/index.cjs +59 -0
- package/dist/sparkline/index.cjs.map +1 -0
- package/dist/sparkline/index.d.cts +123 -0
- package/dist/sparkline/index.d.ts +123 -0
- package/dist/sparkline/index.js +57 -0
- package/dist/sparkline/index.js.map +1 -0
- package/package.json +13 -1
- package/src/components/sparkline/README.md +171 -0
package/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
Composable, typed terminal UI components for
|
|
4
4
|
[Blessed](https://github.com/chjj/blessed).
|
|
5
5
|
|
|
6
|
-
> **Project status:** early development. `ProgressBar`
|
|
7
|
-
> components are coming soon.
|
|
6
|
+
> **Project status:** early development. `ProgressBar` and `Sparkline` are
|
|
7
|
+
> available; more components are coming soon.
|
|
8
8
|
|
|
9
9
|
## Goals
|
|
10
10
|
|
|
@@ -30,7 +30,7 @@ npm install blessed blessed-components
|
|
|
30
30
|
| Component | Purpose | Priority |
|
|
31
31
|
| ------------- | ---------------------------------------------------- | -------- |
|
|
32
32
|
| [`ProgressBar`](./src/components/progress-bar/README.md) | Render one bounded horizontal progress bar. | Available |
|
|
33
|
-
| `Sparkline`
|
|
33
|
+
| [`Sparkline`](./src/components/sparkline/README.md) | Render compact time-series data with Unicode blocks. | Available |
|
|
34
34
|
| `MetricBars` | Render labeled metrics as aligned progress bars. | P0 |
|
|
35
35
|
| `Stat` | Display a label and highlighted value. | P0 |
|
|
36
36
|
|
|
@@ -115,6 +115,7 @@ npm run test:watch
|
|
|
115
115
|
npm run lint
|
|
116
116
|
npm run lint:fix
|
|
117
117
|
npm run biome:check
|
|
118
|
+
npm run preview
|
|
118
119
|
npm run typecheck
|
|
119
120
|
npm run format
|
|
120
121
|
npm run format:check
|
|
@@ -123,6 +124,18 @@ npm run format:check
|
|
|
123
124
|
ESLint and Biome extend the shared `super-configs` presets. Biome owns
|
|
124
125
|
formatting and import organization; Prettier is not used.
|
|
125
126
|
|
|
127
|
+
### Terminal component preview
|
|
128
|
+
|
|
129
|
+
Run the prototype component workbench:
|
|
130
|
+
|
|
131
|
+
```sh
|
|
132
|
+
npm run preview
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Use arrow keys and Enter to select stories, `r` to reload, `Tab` to move
|
|
136
|
+
focus, and `q` to quit. The prototype currently previews ProgressBar and
|
|
137
|
+
Sparkline stories through their public Blessed adapters.
|
|
138
|
+
|
|
126
139
|
## Testing
|
|
127
140
|
|
|
128
141
|
Development follows vertical TDD slices:
|
package/dist/index.cjs
CHANGED
|
@@ -55,7 +55,82 @@ function progressBar({ box, data, parent }) {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// src/components/sparkline/index.ts
|
|
59
|
+
var DEFAULT_CHARACTERS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
60
|
+
function renderSparkline({
|
|
61
|
+
characters = DEFAULT_CHARACTERS,
|
|
62
|
+
emptyText = "No data",
|
|
63
|
+
label,
|
|
64
|
+
max: configuredMax,
|
|
65
|
+
min: configuredMin,
|
|
66
|
+
summary,
|
|
67
|
+
value: displayValue,
|
|
68
|
+
values,
|
|
69
|
+
width
|
|
70
|
+
}) {
|
|
71
|
+
if (!Number.isInteger(width) || width < 1) {
|
|
72
|
+
throw new RangeError("Sparkline width must be a positive integer.");
|
|
73
|
+
}
|
|
74
|
+
if (characters.length < 2 || characters.some((character) => character.length === 0)) {
|
|
75
|
+
throw new RangeError("Sparkline characters must contain at least two non-empty strings.");
|
|
76
|
+
}
|
|
77
|
+
if (values.some((value) => !Number.isFinite(value))) {
|
|
78
|
+
throw new RangeError("Sparkline values must be finite.");
|
|
79
|
+
}
|
|
80
|
+
if (values.length === 0) {
|
|
81
|
+
return emptyText;
|
|
82
|
+
}
|
|
83
|
+
const min = configuredMin ?? Math.min(...values);
|
|
84
|
+
const max = configuredMax ?? Math.max(...values);
|
|
85
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || max < min) {
|
|
86
|
+
throw new RangeError("Sparkline max must be greater than or equal to min.");
|
|
87
|
+
}
|
|
88
|
+
if ((configuredMin !== void 0 || configuredMax !== void 0) && max === min) {
|
|
89
|
+
throw new RangeError("Sparkline explicit max must be greater than min.");
|
|
90
|
+
}
|
|
91
|
+
const constantIndex = Math.floor((characters.length - 1) / 2);
|
|
92
|
+
const samples = values.length <= width ? values : Array.from({ length: width }, (_, index) => {
|
|
93
|
+
const start = Math.floor(index * values.length / width);
|
|
94
|
+
const end = Math.floor((index + 1) * values.length / width);
|
|
95
|
+
return Math.max(...values.slice(start, end));
|
|
96
|
+
});
|
|
97
|
+
const sparkline2 = samples.map((value) => {
|
|
98
|
+
if (min === max) {
|
|
99
|
+
return characters[constantIndex];
|
|
100
|
+
}
|
|
101
|
+
const clampedValue = Math.min(max, Math.max(min, value));
|
|
102
|
+
const ratio = (clampedValue - min) / (max - min);
|
|
103
|
+
const index = clampedValue >= max ? characters.length - 1 : Math.floor(ratio * (characters.length - 1));
|
|
104
|
+
return characters[index];
|
|
105
|
+
}).join("");
|
|
106
|
+
const heading = [label, displayValue].filter((part) => part !== void 0).join(" ");
|
|
107
|
+
const chartLine = summary === void 0 ? sparkline2 : `${sparkline2} ${summary}`;
|
|
108
|
+
return heading.length === 0 ? chartLine : `${heading}
|
|
109
|
+
${chartLine}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/adapters/blessed/sparkline.ts
|
|
113
|
+
function sparkline({ box, data, parent }) {
|
|
114
|
+
const element = blessed__default.default.box({
|
|
115
|
+
...box,
|
|
116
|
+
content: renderSparkline(data),
|
|
117
|
+
parent,
|
|
118
|
+
tags: false
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
element,
|
|
122
|
+
destroy() {
|
|
123
|
+
element.destroy();
|
|
124
|
+
},
|
|
125
|
+
setData(nextData) {
|
|
126
|
+
element.setContent(renderSparkline(nextData));
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
58
131
|
exports.progressBar = progressBar;
|
|
59
132
|
exports.renderProgressBar = renderProgressBar;
|
|
133
|
+
exports.renderSparkline = renderSparkline;
|
|
134
|
+
exports.sparkline = sparkline;
|
|
60
135
|
//# sourceMappingURL=index.cjs.map
|
|
61
136
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/progress-bar/index.ts","../src/adapters/blessed/progress-bar.ts"],"names":["blessed"],"mappings":";;;;;;;;;;;AA6LO,SAAS,iBAAA,CAAkB;AAAA,EAChC,UAAA,GAAa,EAAE,KAAA,EAAO,QAAA,EAAK,QAAQ,QAAA,EAAI;AAAA,EACvC,cAAc,CAAC,EAAE,UAAA,EAAW,KAAM,GAAG,UAAU,CAAA,CAAA,CAAA;AAAA,EAC/C,KAAA;AAAA,EACA,GAAA,GAAM,GAAA;AAAA,EACN,GAAA,GAAM,CAAA;AAAA,EACN,KAAA;AAAA,EACA;AACF,CAAA,EAAqC;AACnC,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,KAAK,CAAA,IAAK,QAAQ,CAAA,EAAG;AACzC,IAAA,MAAM,IAAI,WAAW,+CAA+C,CAAA;AAAA,EACtE;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA,IAAK,CAAC,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA,IAAK,GAAA,IAAO,GAAA,EAAK;AAChE,IAAA,MAAM,IAAI,WAAW,2CAA2C,CAAA;AAAA,EAClE;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAI,WAAW,mCAAmC,CAAA;AAAA,EAC1D;AAEA,EAAA,MAAM,YAAA,GAAe,KAAK,GAAA,CAAI,GAAA,EAAK,KAAK,GAAA,CAAI,GAAA,EAAK,KAAK,CAAC,CAAA;AACvD,EAAA,MAAM,aAAa,IAAA,CAAK,KAAA,CAAA,CAAQ,eAAe,GAAA,KAAQ,GAAA,GAAM,OAAQ,GAAG,CAAA;AACxE,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,KAAA,CAAO,UAAA,GAAa,MAAO,KAAK,CAAA;AACzD,EAAA,MAAM,KAAA,GACJ,UAAA,CAAW,MAAA,CAAO,MAAA,CAAO,WAAW,IAAI,UAAA,CAAW,KAAA,CAAM,MAAA,CAAO,KAAA,GAAQ,WAAW,CAAA;AACrF,EAAA,MAAM,MAAA,GAAS,KAAA,KAAU,MAAA,GAAY,EAAA,GAAK,GAAG,KAAK,CAAA,CAAA,CAAA;AAClD,EAAA,MAAM,YAAY,WAAA,CAAY,EAAE,UAAA,EAAY,KAAA,EAAO,cAAc,CAAA;AAEjE,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,KAAK,IAAI,SAAS,CAAA,CAAA;AACvC;;;AClFO,SAAS,WAAA,CAAY,EAAE,GAAA,EAAK,IAAA,EAAM,QAAO,EAA0C;AACxF,EAAA,MAAM,OAAA,GAAUA,yBAAQ,GAAA,CAAI;AAAA,IAC1B,GAAG,GAAA;AAAA,IACH,OAAA,EAAS,kBAAkB,IAAI,CAAA;AAAA,IAC/B,MAAA;AAAA,IACA,IAAA,EAAM;AAAA,GACP,CAAA;AAED,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,OAAA,GAAU;AACR,MAAA,OAAA,CAAQ,OAAA,EAAQ;AAAA,IAClB,CAAA;AAAA,IACA,QAAQ,QAAA,EAAU;AAChB,MAAA,OAAA,CAAQ,UAAA,CAAW,iBAAA,CAAkB,QAAQ,CAAC,CAAA;AAAA,IAChD;AAAA,GACF;AACF","file":"index.cjs","sourcesContent":["/**\n * Options accepted by {@link renderProgressBar}.\n *\n * The renderer maps `value` from the inclusive numeric range defined by\n * `min` and `max` into a fixed-width terminal track. Values outside the range\n * are clamped before rendering.\n *\n * @example Basic percentage bar\n *\n * ```ts\n * renderProgressBar({\n * value: 50,\n * width: 10,\n * });\n * // \"█████░░░░░ 50%\"\n * ```\n *\n * @example Custom range and label\n *\n * ```ts\n * renderProgressBar({\n * label: 'Workers',\n * min: 0,\n * max: 8,\n * value: 6,\n * width: 8,\n * });\n * // \"Workers ██████░░ 75%\"\n * ```\n */\nexport interface RenderProgressBarOptions {\n /**\n * Characters used to render filled and empty cells.\n *\n * Each character should occupy one terminal cell. Multi-cell or combining\n * characters can make the visible track wider than `width`.\n *\n * @defaultValue `{ filled: '█', empty: '░' }`\n */\n characters?: ProgressBarCharacters;\n\n /**\n * Formats the value text appended after the track.\n *\n * The callback receives the clamped numeric value and its rounded\n * percentage. Returning an empty string leaves the trailing separator in\n * place; callers that need a track-only representation should account for\n * that when composing output.\n *\n * @defaultValue `({ percentage }) => `${percentage}%``\n */\n formatValue?: (context: ProgressBarValueContext) => string;\n\n /**\n * Optional text rendered before the track.\n *\n * Dynamic values must be escaped by the caller if the returned string will\n * later be inserted into a Blessed element with tags enabled. The bundled\n * Blessed adapter disables tags.\n */\n label?: string;\n\n /**\n * Upper bound of the numeric range.\n *\n * Must be finite and greater than `min`.\n *\n * @defaultValue `100`\n */\n max?: number;\n\n /**\n * Lower bound of the numeric range.\n *\n * Must be finite and lower than `max`.\n *\n * @defaultValue `0`\n */\n min?: number;\n\n /**\n * Current numeric value.\n *\n * The value must be finite. Values below `min` or above `max` are clamped\n * before the percentage and formatted value are calculated.\n */\n value: number;\n\n /**\n * Number of characters reserved for the progress track.\n *\n * This excludes the optional label, spaces, and formatted value. It must be\n * a positive integer.\n */\n width: number;\n}\n\n/**\n * Character pair used to draw a progress track.\n *\n * Use ASCII characters when the target terminal cannot display Unicode block\n * glyphs reliably.\n *\n * @example\n *\n * ```ts\n * const asciiCharacters: ProgressBarCharacters = {\n * filled: '#',\n * empty: '-',\n * };\n * ```\n */\nexport interface ProgressBarCharacters {\n /** Character repeated for unfilled track cells. */\n empty: string;\n\n /** Character repeated for filled track cells. */\n filled: string;\n}\n\n/**\n * Normalized value information passed to a progress value formatter.\n */\nexport interface ProgressBarValueContext {\n /**\n * Rounded progress in the inclusive range from `0` to `100`.\n */\n percentage: number;\n\n /**\n * Numeric value after it has been clamped to the configured range.\n */\n value: number;\n}\n\n/**\n * Renders a deterministic, single-line progress bar.\n *\n * This function is framework-independent and does not import Blessed. Import\n * it from `blessed-components/progress-bar` when only string rendering is\n * needed; that subpath keeps the Blessed adapter out of the module graph.\n *\n * The result has this structure:\n *\n * ```text\n * [label + space][fixed-width track][space][formatted value]\n * ```\n *\n * @param options - Numeric range, track width, characters, and text formatting.\n * @returns A plain terminal string. No ANSI sequences or Blessed tags are added.\n *\n * @throws `RangeError`\n * Thrown when `width` is not a positive integer.\n *\n * @throws `RangeError`\n * Thrown when `min` or `max` is not finite, or when `max` is not greater than\n * `min`.\n *\n * @throws `RangeError`\n * Thrown when `value` is not finite.\n *\n * @example Unicode output\n *\n * ```ts\n * import { renderProgressBar } from 'blessed-components/progress-bar';\n *\n * const output = renderProgressBar({\n * label: 'Quality',\n * value: 78,\n * width: 16,\n * });\n *\n * // \"Quality ████████████░░░░ 78%\"\n * ```\n *\n * @example ASCII output with a custom value\n *\n * ```ts\n * const output = renderProgressBar({\n * characters: { filled: '#', empty: '-' },\n * formatValue: ({ value }) => `${value} files`,\n * label: 'Uploaded',\n * value: 25,\n * width: 8,\n * });\n *\n * // \"Uploaded ##------ 25 files\"\n * ```\n */\nexport function renderProgressBar({\n characters = { empty: '░', filled: '█' },\n formatValue = ({ percentage }) => `${percentage}%`,\n label,\n max = 100,\n min = 0,\n value,\n width,\n}: RenderProgressBarOptions): string {\n if (!Number.isInteger(width) || width < 1) {\n throw new RangeError('ProgressBar width must be a positive integer.');\n }\n\n if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) {\n throw new RangeError('ProgressBar max must be greater than min.');\n }\n\n if (!Number.isFinite(value)) {\n throw new RangeError('ProgressBar value must be finite.');\n }\n\n const clampedValue = Math.min(max, Math.max(min, value));\n const percentage = Math.round(((clampedValue - min) / (max - min)) * 100);\n const filledWidth = Math.round((percentage / 100) * width);\n const track =\n characters.filled.repeat(filledWidth) + characters.empty.repeat(width - filledWidth);\n const prefix = label === undefined ? '' : `${label} `;\n const valueText = formatValue({ percentage, value: clampedValue });\n\n return `${prefix}${track} ${valueText}`;\n}\n","import blessed from 'blessed';\n\nimport {\n type RenderProgressBarOptions,\n renderProgressBar,\n} from '../../components/progress-bar/index.js';\n\n/**\n * Blessed box options supported by the ProgressBar adapter.\n *\n * `parent`, `content`, and `tags` are managed by {@link progressBar}:\n *\n * - `parent` comes from {@link ProgressBarOptions.parent}.\n * - `content` comes from {@link renderProgressBar}.\n * - `tags` is always disabled so labels and formatter output remain literal.\n */\nexport type ProgressBarBoxOptions = Omit<blessed.Widgets.BoxOptions, 'content' | 'parent' | 'tags'>;\n\n/**\n * Options accepted by the Blessed {@link progressBar} adapter.\n */\nexport interface ProgressBarOptions {\n /**\n * Optional Blessed box configuration.\n *\n * Position, dimensions, style, visibility, and other regular box options\n * can be supplied here.\n */\n box?: ProgressBarBoxOptions;\n\n /**\n * Data passed to the pure {@link renderProgressBar} renderer.\n */\n data: RenderProgressBarOptions;\n\n /**\n * Blessed node that receives the created box as a child.\n *\n * Usually this is a `Screen` or another `BoxElement`.\n */\n parent: blessed.Widgets.Node;\n}\n\n/**\n * Imperative handle returned by {@link progressBar}.\n *\n * The handle owns one Blessed `BoxElement`. It does not own the parent screen\n * and never calls `screen.render()`, allowing callers to batch several updates\n * into one terminal render.\n */\nexport interface ProgressBarHandle {\n /**\n * Underlying Blessed box.\n *\n * Use this for standard Blessed operations such as positioning, showing,\n * hiding, or applying styles.\n */\n readonly element: blessed.Widgets.BoxElement;\n\n /**\n * Destroys and detaches the owned box from its parent.\n *\n * This does not destroy the parent or screen. Do not use the handle after\n * calling this method.\n */\n destroy(): void;\n\n /**\n * Re-renders component content using new data.\n *\n * The underlying element is preserved. This method does not call\n * `screen.render()`; call it explicitly when updates should become visible.\n *\n * @param data - Complete renderer data replacing the previous data.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderProgressBar}.\n */\n setData(data: RenderProgressBarOptions): void;\n}\n\n/**\n * Creates a display-only ProgressBar backed by a Blessed `BoxElement`.\n *\n * Import this adapter from `blessed-components/progress-bar/blessed`. Use\n * {@link renderProgressBar} from `blessed-components/progress-bar` when a\n * Blessed element is unnecessary.\n *\n * The adapter:\n *\n * - renders initial content through {@link renderProgressBar};\n * - disables Blessed tags to keep dynamic text literal;\n * - updates the existing element through {@link ProgressBarHandle.setData};\n * - leaves screen rendering under caller control;\n * - detaches its element through {@link ProgressBarHandle.destroy}.\n *\n * @param options - Parent node, renderer data, and optional box settings.\n * @returns A handle for the created element, updates, and cleanup.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderProgressBar}.\n *\n * @example\n *\n * ```ts\n * import blessed from 'blessed';\n * import { progressBar } from 'blessed-components/progress-bar/blessed';\n *\n * const screen = blessed.screen({ smartCSR: true });\n * const upload = progressBar({\n * parent: screen,\n * box: {\n * top: 0,\n * left: 0,\n * width: 40,\n * height: 1,\n * },\n * data: {\n * label: 'Uploaded',\n * value: 25,\n * width: 20,\n * },\n * });\n *\n * screen.render();\n *\n * upload.setData({\n * label: 'Uploaded',\n * value: 75,\n * width: 20,\n * });\n * screen.render();\n *\n * upload.destroy();\n * screen.destroy();\n * ```\n */\nexport function progressBar({ box, data, parent }: ProgressBarOptions): ProgressBarHandle {\n const element = blessed.box({\n ...box,\n content: renderProgressBar(data),\n parent,\n tags: false,\n });\n\n return {\n element,\n destroy() {\n element.destroy();\n },\n setData(nextData) {\n element.setContent(renderProgressBar(nextData));\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/progress-bar/index.ts","../src/adapters/blessed/progress-bar.ts","../src/components/sparkline/index.ts","../src/adapters/blessed/sparkline.ts"],"names":["blessed","sparkline"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Options accepted by {@link renderProgressBar}.\n *\n * The renderer maps `value` from the inclusive numeric range defined by\n * `min` and `max` into a fixed-width terminal track. Values outside the range\n * are clamped before rendering.\n *\n * @example Basic percentage bar\n *\n * ```ts\n * renderProgressBar({\n * value: 50,\n * width: 10,\n * });\n * // \"█████░░░░░ 50%\"\n * ```\n *\n * @example Custom range and label\n *\n * ```ts\n * renderProgressBar({\n * label: 'Workers',\n * min: 0,\n * max: 8,\n * value: 6,\n * width: 8,\n * });\n * // \"Workers ██████░░ 75%\"\n * ```\n */\nexport interface RenderProgressBarOptions {\n /**\n * Characters used to render filled and empty cells.\n *\n * Each character should occupy one terminal cell. Multi-cell or combining\n * characters can make the visible track wider than `width`.\n *\n * @defaultValue `{ filled: '█', empty: '░' }`\n */\n characters?: ProgressBarCharacters;\n\n /**\n * Formats the value text appended after the track.\n *\n * The callback receives the clamped numeric value and its rounded\n * percentage. Returning an empty string leaves the trailing separator in\n * place; callers that need a track-only representation should account for\n * that when composing output.\n *\n * @defaultValue `({ percentage }) => `${percentage}%``\n */\n formatValue?: (context: ProgressBarValueContext) => string;\n\n /**\n * Optional text rendered before the track.\n *\n * Dynamic values must be escaped by the caller if the returned string will\n * later be inserted into a Blessed element with tags enabled. The bundled\n * Blessed adapter disables tags.\n */\n label?: string;\n\n /**\n * Upper bound of the numeric range.\n *\n * Must be finite and greater than `min`.\n *\n * @defaultValue `100`\n */\n max?: number;\n\n /**\n * Lower bound of the numeric range.\n *\n * Must be finite and lower than `max`.\n *\n * @defaultValue `0`\n */\n min?: number;\n\n /**\n * Current numeric value.\n *\n * The value must be finite. Values below `min` or above `max` are clamped\n * before the percentage and formatted value are calculated.\n */\n value: number;\n\n /**\n * Number of characters reserved for the progress track.\n *\n * This excludes the optional label, spaces, and formatted value. It must be\n * a positive integer.\n */\n width: number;\n}\n\n/**\n * Character pair used to draw a progress track.\n *\n * Use ASCII characters when the target terminal cannot display Unicode block\n * glyphs reliably.\n *\n * @example\n *\n * ```ts\n * const asciiCharacters: ProgressBarCharacters = {\n * filled: '#',\n * empty: '-',\n * };\n * ```\n */\nexport interface ProgressBarCharacters {\n /** Character repeated for unfilled track cells. */\n empty: string;\n\n /** Character repeated for filled track cells. */\n filled: string;\n}\n\n/**\n * Normalized value information passed to a progress value formatter.\n */\nexport interface ProgressBarValueContext {\n /**\n * Rounded progress in the inclusive range from `0` to `100`.\n */\n percentage: number;\n\n /**\n * Numeric value after it has been clamped to the configured range.\n */\n value: number;\n}\n\n/**\n * Renders a deterministic, single-line progress bar.\n *\n * This function is framework-independent and does not import Blessed. Import\n * it from `blessed-components/progress-bar` when only string rendering is\n * needed; that subpath keeps the Blessed adapter out of the module graph.\n *\n * The result has this structure:\n *\n * ```text\n * [label + space][fixed-width track][space][formatted value]\n * ```\n *\n * @param options - Numeric range, track width, characters, and text formatting.\n * @returns A plain terminal string. No ANSI sequences or Blessed tags are added.\n *\n * @throws `RangeError`\n * Thrown when `width` is not a positive integer.\n *\n * @throws `RangeError`\n * Thrown when `min` or `max` is not finite, or when `max` is not greater than\n * `min`.\n *\n * @throws `RangeError`\n * Thrown when `value` is not finite.\n *\n * @example Unicode output\n *\n * ```ts\n * import { renderProgressBar } from 'blessed-components/progress-bar';\n *\n * const output = renderProgressBar({\n * label: 'Quality',\n * value: 78,\n * width: 16,\n * });\n *\n * // \"Quality ████████████░░░░ 78%\"\n * ```\n *\n * @example ASCII output with a custom value\n *\n * ```ts\n * const output = renderProgressBar({\n * characters: { filled: '#', empty: '-' },\n * formatValue: ({ value }) => `${value} files`,\n * label: 'Uploaded',\n * value: 25,\n * width: 8,\n * });\n *\n * // \"Uploaded ##------ 25 files\"\n * ```\n */\nexport function renderProgressBar({\n characters = { empty: '░', filled: '█' },\n formatValue = ({ percentage }) => `${percentage}%`,\n label,\n max = 100,\n min = 0,\n value,\n width,\n}: RenderProgressBarOptions): string {\n if (!Number.isInteger(width) || width < 1) {\n throw new RangeError('ProgressBar width must be a positive integer.');\n }\n\n if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) {\n throw new RangeError('ProgressBar max must be greater than min.');\n }\n\n if (!Number.isFinite(value)) {\n throw new RangeError('ProgressBar value must be finite.');\n }\n\n const clampedValue = Math.min(max, Math.max(min, value));\n const percentage = Math.round(((clampedValue - min) / (max - min)) * 100);\n const filledWidth = Math.round((percentage / 100) * width);\n const track =\n characters.filled.repeat(filledWidth) + characters.empty.repeat(width - filledWidth);\n const prefix = label === undefined ? '' : `${label} `;\n const valueText = formatValue({ percentage, value: clampedValue });\n\n return `${prefix}${track} ${valueText}`;\n}\n","import blessed from 'blessed';\n\nimport {\n type RenderProgressBarOptions,\n renderProgressBar,\n} from '../../components/progress-bar/index.js';\n\n/**\n * Blessed box options supported by the ProgressBar adapter.\n *\n * `parent`, `content`, and `tags` are managed by {@link progressBar}:\n *\n * - `parent` comes from {@link ProgressBarOptions.parent}.\n * - `content` comes from {@link renderProgressBar}.\n * - `tags` is always disabled so labels and formatter output remain literal.\n */\nexport type ProgressBarBoxOptions = Omit<blessed.Widgets.BoxOptions, 'content' | 'parent' | 'tags'>;\n\n/**\n * Options accepted by the Blessed {@link progressBar} adapter.\n */\nexport interface ProgressBarOptions {\n /**\n * Optional Blessed box configuration.\n *\n * Position, dimensions, style, visibility, and other regular box options\n * can be supplied here.\n */\n box?: ProgressBarBoxOptions;\n\n /**\n * Data passed to the pure {@link renderProgressBar} renderer.\n */\n data: RenderProgressBarOptions;\n\n /**\n * Blessed node that receives the created box as a child.\n *\n * Usually this is a `Screen` or another `BoxElement`.\n */\n parent: blessed.Widgets.Node;\n}\n\n/**\n * Imperative handle returned by {@link progressBar}.\n *\n * The handle owns one Blessed `BoxElement`. It does not own the parent screen\n * and never calls `screen.render()`, allowing callers to batch several updates\n * into one terminal render.\n */\nexport interface ProgressBarHandle {\n /**\n * Underlying Blessed box.\n *\n * Use this for standard Blessed operations such as positioning, showing,\n * hiding, or applying styles.\n */\n readonly element: blessed.Widgets.BoxElement;\n\n /**\n * Destroys and detaches the owned box from its parent.\n *\n * This does not destroy the parent or screen. Do not use the handle after\n * calling this method.\n */\n destroy(): void;\n\n /**\n * Re-renders component content using new data.\n *\n * The underlying element is preserved. This method does not call\n * `screen.render()`; call it explicitly when updates should become visible.\n *\n * @param data - Complete renderer data replacing the previous data.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderProgressBar}.\n */\n setData(data: RenderProgressBarOptions): void;\n}\n\n/**\n * Creates a display-only ProgressBar backed by a Blessed `BoxElement`.\n *\n * Import this adapter from `blessed-components/progress-bar/blessed`. Use\n * {@link renderProgressBar} from `blessed-components/progress-bar` when a\n * Blessed element is unnecessary.\n *\n * The adapter:\n *\n * - renders initial content through {@link renderProgressBar};\n * - disables Blessed tags to keep dynamic text literal;\n * - updates the existing element through {@link ProgressBarHandle.setData};\n * - leaves screen rendering under caller control;\n * - detaches its element through {@link ProgressBarHandle.destroy}.\n *\n * @param options - Parent node, renderer data, and optional box settings.\n * @returns A handle for the created element, updates, and cleanup.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderProgressBar}.\n *\n * @example\n *\n * ```ts\n * import blessed from 'blessed';\n * import { progressBar } from 'blessed-components/progress-bar/blessed';\n *\n * const screen = blessed.screen({ smartCSR: true });\n * const upload = progressBar({\n * parent: screen,\n * box: {\n * top: 0,\n * left: 0,\n * width: 40,\n * height: 1,\n * },\n * data: {\n * label: 'Uploaded',\n * value: 25,\n * width: 20,\n * },\n * });\n *\n * screen.render();\n *\n * upload.setData({\n * label: 'Uploaded',\n * value: 75,\n * width: 20,\n * });\n * screen.render();\n *\n * upload.destroy();\n * screen.destroy();\n * ```\n */\nexport function progressBar({ box, data, parent }: ProgressBarOptions): ProgressBarHandle {\n const element = blessed.box({\n ...box,\n content: renderProgressBar(data),\n parent,\n tags: false,\n });\n\n return {\n element,\n destroy() {\n element.destroy();\n },\n setData(nextData) {\n element.setContent(renderProgressBar(nextData));\n },\n };\n}\n","const DEFAULT_CHARACTERS = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'] as const;\n\n/**\n * Options accepted by {@link renderSparkline}.\n *\n * Samples are kept in input order, scaled into `characters`, and reduced to at\n * most `width` cells. Values outside an explicit domain are clamped.\n */\nexport interface RenderSparklineOptions {\n /**\n * Ordered glyph scale from lowest to highest intensity.\n *\n * At least two non-empty strings are required. Glyphs should occupy one\n * terminal cell to keep output width predictable.\n *\n * @defaultValue `['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']`\n */\n characters?: readonly string[];\n\n /**\n * Text returned when `values` is empty.\n *\n * Metadata is not rendered for an empty series.\n *\n * @defaultValue `'No data'`\n */\n emptyText?: string;\n\n /** Optional heading label rendered above the graph. */\n label?: string;\n\n /**\n * Explicit upper domain bound.\n *\n * When omitted, the largest input value is used. If either explicit bound is\n * supplied, the resolved `max` must be greater than the resolved `min`.\n */\n max?: number;\n\n /**\n * Explicit lower domain bound.\n *\n * When omitted, the smallest input value is used.\n */\n min?: number;\n\n /** Optional text appended to the graph line after one space. */\n summary?: string;\n\n /**\n * Optional primary value rendered next to `label`.\n *\n * This value is presentation metadata and does not affect graph scaling.\n */\n value?: number | string;\n\n /**\n * Ordered finite numeric samples.\n *\n * The input array is never mutated.\n */\n values: readonly number[];\n\n /**\n * Positive integer maximum number of graph cells.\n *\n * Longer series are downsampled. Shorter series are not padded.\n */\n width: number;\n}\n\n/**\n * Renders a deterministic terminal sparkline.\n *\n * This pure renderer does not import Blessed. Use the\n * `blessed-components/sparkline` subpath to keep the Blessed adapter outside\n * the module graph.\n *\n * When downsampling is required, the series is divided into ordered buckets\n * and each bucket keeps its maximum value. This preserves narrow peaks. A\n * constant series uses the middle glyph to avoid division by zero.\n *\n * Output shape:\n *\n * ```text\n * [optional label and value]\n * [graph][optional summary]\n * ```\n *\n * With no metadata, only the graph line is returned.\n *\n * @param options - Series, domain, width, glyphs, and optional metadata.\n * @returns Plain text without ANSI sequences or Blessed tags.\n *\n * @throws `RangeError`\n * Thrown when `width` is not a positive integer.\n *\n * @throws `RangeError`\n * Thrown when fewer than two non-empty characters are provided.\n *\n * @throws `RangeError`\n * Thrown when any sample or domain bound is not finite.\n *\n * @throws `RangeError`\n * Thrown when an explicit domain does not have `max > min`.\n *\n * @example Basic series\n *\n * ```ts\n * import { renderSparkline } from 'blessed-components/sparkline';\n *\n * renderSparkline({\n * values: [1, 2, 3, 4, 5, 6, 7, 8],\n * width: 8,\n * });\n * // \"▁▂▃▄▅▆▇█\"\n * ```\n *\n * @example Metadata and summary\n *\n * ```ts\n * renderSparkline({\n * label: 'Downloads',\n * value: '25.2M',\n * values: [1, 3, 2, 8],\n * width: 4,\n * summary: 'peak: 8M',\n * });\n * // \"Downloads 25.2M\\n▁▃▂█ peak: 8M\"\n * ```\n */\nexport function renderSparkline({\n characters = DEFAULT_CHARACTERS,\n emptyText = 'No data',\n label,\n max: configuredMax,\n min: configuredMin,\n summary,\n value: displayValue,\n values,\n width,\n}: RenderSparklineOptions): string {\n if (!Number.isInteger(width) || width < 1) {\n throw new RangeError('Sparkline width must be a positive integer.');\n }\n\n if (characters.length < 2 || characters.some((character) => character.length === 0)) {\n throw new RangeError('Sparkline characters must contain at least two non-empty strings.');\n }\n\n if (values.some((value) => !Number.isFinite(value))) {\n throw new RangeError('Sparkline values must be finite.');\n }\n\n if (values.length === 0) {\n return emptyText;\n }\n\n const min = configuredMin ?? Math.min(...values);\n const max = configuredMax ?? Math.max(...values);\n\n if (!Number.isFinite(min) || !Number.isFinite(max) || max < min) {\n throw new RangeError('Sparkline max must be greater than or equal to min.');\n }\n\n if ((configuredMin !== undefined || configuredMax !== undefined) && max === min) {\n throw new RangeError('Sparkline explicit max must be greater than min.');\n }\n\n const constantIndex = Math.floor((characters.length - 1) / 2);\n const samples =\n values.length <= width\n ? values\n : Array.from({ length: width }, (_, index) => {\n const start = Math.floor((index * values.length) / width);\n const end = Math.floor(((index + 1) * values.length) / width);\n\n return Math.max(...values.slice(start, end));\n });\n const sparkline = samples\n .map((value) => {\n if (min === max) {\n return characters[constantIndex];\n }\n\n const clampedValue = Math.min(max, Math.max(min, value));\n const ratio = (clampedValue - min) / (max - min);\n const index =\n clampedValue >= max ? characters.length - 1 : Math.floor(ratio * (characters.length - 1));\n\n return characters[index];\n })\n .join('');\n const heading = [label, displayValue].filter((part) => part !== undefined).join(' ');\n const chartLine = summary === undefined ? sparkline : `${sparkline} ${summary}`;\n\n return heading.length === 0 ? chartLine : `${heading}\\n${chartLine}`;\n}\n","import blessed from 'blessed';\n\nimport { type RenderSparklineOptions, renderSparkline } from '../../components/sparkline/index.js';\n\n/**\n * Blessed box options supported by the Sparkline adapter.\n *\n * `parent`, `content`, and `tags` are controlled by {@link sparkline}.\n */\nexport type SparklineBoxOptions = Omit<blessed.Widgets.BoxOptions, 'content' | 'parent' | 'tags'>;\n\n/** Options accepted by the Blessed {@link sparkline} adapter. */\nexport interface SparklineOptions {\n /** Optional position, dimensions, style, and other Blessed box settings. */\n box?: SparklineBoxOptions;\n\n /** Data passed to the pure {@link renderSparkline} renderer. */\n data: RenderSparklineOptions;\n\n /** Blessed screen or node that receives the created box. */\n parent: blessed.Widgets.Node;\n}\n\n/**\n * Imperative handle returned by {@link sparkline}.\n *\n * The handle owns one box, never owns the parent screen, and never calls\n * `screen.render()`.\n */\nexport interface SparklineHandle {\n /** Underlying Blessed box used for standard element operations. */\n readonly element: blessed.Widgets.BoxElement;\n\n /**\n * Destroys and detaches the owned box.\n *\n * The parent and screen remain alive.\n */\n destroy(): void;\n\n /**\n * Replaces renderer data while preserving the Blessed element.\n *\n * Call `screen.render()` after one or more updates to flush visible output.\n *\n * @param data - Complete renderer data replacing the previous data.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderSparkline}.\n */\n setData(data: RenderSparklineOptions): void;\n}\n\n/**\n * Creates a display-only Sparkline backed by a Blessed `BoxElement`.\n *\n * Import from `blessed-components/sparkline/blessed`. The adapter disables\n * Blessed tags, renders through {@link renderSparkline}, and leaves terminal\n * rendering under caller control.\n *\n * @param options - Parent node, renderer data, and optional box settings.\n * @returns Handle for the created box, updates, and cleanup.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderSparkline}.\n *\n * @example\n *\n * ```ts\n * import blessed from 'blessed';\n * import { sparkline } from 'blessed-components/sparkline/blessed';\n *\n * const screen = blessed.screen({ smartCSR: true });\n * const downloads = sparkline({\n * parent: screen,\n * box: { top: 0, left: 0, width: 40, height: 2 },\n * data: {\n * label: 'Downloads',\n * values: [1, 3, 2, 8],\n * width: 20,\n * },\n * });\n *\n * screen.render();\n * downloads.setData({\n * label: 'Downloads',\n * values: [2, 4, 6, 8],\n * width: 20,\n * });\n * screen.render();\n *\n * downloads.destroy();\n * screen.destroy();\n * ```\n */\nexport function sparkline({ box, data, parent }: SparklineOptions): SparklineHandle {\n const element = blessed.box({\n ...box,\n content: renderSparkline(data),\n parent,\n tags: false,\n });\n\n return {\n element,\n destroy() {\n element.destroy();\n },\n setData(nextData) {\n element.setContent(renderSparkline(nextData));\n },\n };\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { ProgressBarBoxOptions, ProgressBarHandle, ProgressBarOptions, progressBar } from './progress-bar/blessed.cjs';
|
|
2
|
+
export { SparklineBoxOptions, SparklineHandle, SparklineOptions, sparkline } from './sparkline/blessed.cjs';
|
|
2
3
|
export { ProgressBarCharacters, ProgressBarValueContext, RenderProgressBarOptions, renderProgressBar } from './progress-bar/index.cjs';
|
|
4
|
+
export { RenderSparklineOptions, renderSparkline } from './sparkline/index.cjs';
|
|
3
5
|
import 'blessed';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { ProgressBarBoxOptions, ProgressBarHandle, ProgressBarOptions, progressBar } from './progress-bar/blessed.js';
|
|
2
|
+
export { SparklineBoxOptions, SparklineHandle, SparklineOptions, sparkline } from './sparkline/blessed.js';
|
|
2
3
|
export { ProgressBarCharacters, ProgressBarValueContext, RenderProgressBarOptions, renderProgressBar } from './progress-bar/index.js';
|
|
4
|
+
export { RenderSparklineOptions, renderSparkline } from './sparkline/index.js';
|
|
3
5
|
import 'blessed';
|
package/dist/index.js
CHANGED
|
@@ -49,6 +49,79 @@ function progressBar({ box, data, parent }) {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
// src/components/sparkline/index.ts
|
|
53
|
+
var DEFAULT_CHARACTERS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
54
|
+
function renderSparkline({
|
|
55
|
+
characters = DEFAULT_CHARACTERS,
|
|
56
|
+
emptyText = "No data",
|
|
57
|
+
label,
|
|
58
|
+
max: configuredMax,
|
|
59
|
+
min: configuredMin,
|
|
60
|
+
summary,
|
|
61
|
+
value: displayValue,
|
|
62
|
+
values,
|
|
63
|
+
width
|
|
64
|
+
}) {
|
|
65
|
+
if (!Number.isInteger(width) || width < 1) {
|
|
66
|
+
throw new RangeError("Sparkline width must be a positive integer.");
|
|
67
|
+
}
|
|
68
|
+
if (characters.length < 2 || characters.some((character) => character.length === 0)) {
|
|
69
|
+
throw new RangeError("Sparkline characters must contain at least two non-empty strings.");
|
|
70
|
+
}
|
|
71
|
+
if (values.some((value) => !Number.isFinite(value))) {
|
|
72
|
+
throw new RangeError("Sparkline values must be finite.");
|
|
73
|
+
}
|
|
74
|
+
if (values.length === 0) {
|
|
75
|
+
return emptyText;
|
|
76
|
+
}
|
|
77
|
+
const min = configuredMin ?? Math.min(...values);
|
|
78
|
+
const max = configuredMax ?? Math.max(...values);
|
|
79
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || max < min) {
|
|
80
|
+
throw new RangeError("Sparkline max must be greater than or equal to min.");
|
|
81
|
+
}
|
|
82
|
+
if ((configuredMin !== void 0 || configuredMax !== void 0) && max === min) {
|
|
83
|
+
throw new RangeError("Sparkline explicit max must be greater than min.");
|
|
84
|
+
}
|
|
85
|
+
const constantIndex = Math.floor((characters.length - 1) / 2);
|
|
86
|
+
const samples = values.length <= width ? values : Array.from({ length: width }, (_, index) => {
|
|
87
|
+
const start = Math.floor(index * values.length / width);
|
|
88
|
+
const end = Math.floor((index + 1) * values.length / width);
|
|
89
|
+
return Math.max(...values.slice(start, end));
|
|
90
|
+
});
|
|
91
|
+
const sparkline2 = samples.map((value) => {
|
|
92
|
+
if (min === max) {
|
|
93
|
+
return characters[constantIndex];
|
|
94
|
+
}
|
|
95
|
+
const clampedValue = Math.min(max, Math.max(min, value));
|
|
96
|
+
const ratio = (clampedValue - min) / (max - min);
|
|
97
|
+
const index = clampedValue >= max ? characters.length - 1 : Math.floor(ratio * (characters.length - 1));
|
|
98
|
+
return characters[index];
|
|
99
|
+
}).join("");
|
|
100
|
+
const heading = [label, displayValue].filter((part) => part !== void 0).join(" ");
|
|
101
|
+
const chartLine = summary === void 0 ? sparkline2 : `${sparkline2} ${summary}`;
|
|
102
|
+
return heading.length === 0 ? chartLine : `${heading}
|
|
103
|
+
${chartLine}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/adapters/blessed/sparkline.ts
|
|
107
|
+
function sparkline({ box, data, parent }) {
|
|
108
|
+
const element = blessed.box({
|
|
109
|
+
...box,
|
|
110
|
+
content: renderSparkline(data),
|
|
111
|
+
parent,
|
|
112
|
+
tags: false
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
element,
|
|
116
|
+
destroy() {
|
|
117
|
+
element.destroy();
|
|
118
|
+
},
|
|
119
|
+
setData(nextData) {
|
|
120
|
+
element.setContent(renderSparkline(nextData));
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { progressBar, renderProgressBar, renderSparkline, sparkline };
|
|
53
126
|
//# sourceMappingURL=index.js.map
|
|
54
127
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/progress-bar/index.ts","../src/adapters/blessed/progress-bar.ts"],"names":[],"mappings":";;;;;AA6LO,SAAS,iBAAA,CAAkB;AAAA,EAChC,UAAA,GAAa,EAAE,KAAA,EAAO,QAAA,EAAK,QAAQ,QAAA,EAAI;AAAA,EACvC,cAAc,CAAC,EAAE,UAAA,EAAW,KAAM,GAAG,UAAU,CAAA,CAAA,CAAA;AAAA,EAC/C,KAAA;AAAA,EACA,GAAA,GAAM,GAAA;AAAA,EACN,GAAA,GAAM,CAAA;AAAA,EACN,KAAA;AAAA,EACA;AACF,CAAA,EAAqC;AACnC,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,KAAK,CAAA,IAAK,QAAQ,CAAA,EAAG;AACzC,IAAA,MAAM,IAAI,WAAW,+CAA+C,CAAA;AAAA,EACtE;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA,IAAK,CAAC,MAAA,CAAO,QAAA,CAAS,GAAG,CAAA,IAAK,GAAA,IAAO,GAAA,EAAK;AAChE,IAAA,MAAM,IAAI,WAAW,2CAA2C,CAAA;AAAA,EAClE;AAEA,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAI,WAAW,mCAAmC,CAAA;AAAA,EAC1D;AAEA,EAAA,MAAM,YAAA,GAAe,KAAK,GAAA,CAAI,GAAA,EAAK,KAAK,GAAA,CAAI,GAAA,EAAK,KAAK,CAAC,CAAA;AACvD,EAAA,MAAM,aAAa,IAAA,CAAK,KAAA,CAAA,CAAQ,eAAe,GAAA,KAAQ,GAAA,GAAM,OAAQ,GAAG,CAAA;AACxE,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,KAAA,CAAO,UAAA,GAAa,MAAO,KAAK,CAAA;AACzD,EAAA,MAAM,KAAA,GACJ,UAAA,CAAW,MAAA,CAAO,MAAA,CAAO,WAAW,IAAI,UAAA,CAAW,KAAA,CAAM,MAAA,CAAO,KAAA,GAAQ,WAAW,CAAA;AACrF,EAAA,MAAM,MAAA,GAAS,KAAA,KAAU,MAAA,GAAY,EAAA,GAAK,GAAG,KAAK,CAAA,CAAA,CAAA;AAClD,EAAA,MAAM,YAAY,WAAA,CAAY,EAAE,UAAA,EAAY,KAAA,EAAO,cAAc,CAAA;AAEjE,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,KAAK,IAAI,SAAS,CAAA,CAAA;AACvC;;;AClFO,SAAS,WAAA,CAAY,EAAE,GAAA,EAAK,IAAA,EAAM,QAAO,EAA0C;AACxF,EAAA,MAAM,OAAA,GAAU,QAAQ,GAAA,CAAI;AAAA,IAC1B,GAAG,GAAA;AAAA,IACH,OAAA,EAAS,kBAAkB,IAAI,CAAA;AAAA,IAC/B,MAAA;AAAA,IACA,IAAA,EAAM;AAAA,GACP,CAAA;AAED,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,OAAA,GAAU;AACR,MAAA,OAAA,CAAQ,OAAA,EAAQ;AAAA,IAClB,CAAA;AAAA,IACA,QAAQ,QAAA,EAAU;AAChB,MAAA,OAAA,CAAQ,UAAA,CAAW,iBAAA,CAAkB,QAAQ,CAAC,CAAA;AAAA,IAChD;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * Options accepted by {@link renderProgressBar}.\n *\n * The renderer maps `value` from the inclusive numeric range defined by\n * `min` and `max` into a fixed-width terminal track. Values outside the range\n * are clamped before rendering.\n *\n * @example Basic percentage bar\n *\n * ```ts\n * renderProgressBar({\n * value: 50,\n * width: 10,\n * });\n * // \"█████░░░░░ 50%\"\n * ```\n *\n * @example Custom range and label\n *\n * ```ts\n * renderProgressBar({\n * label: 'Workers',\n * min: 0,\n * max: 8,\n * value: 6,\n * width: 8,\n * });\n * // \"Workers ██████░░ 75%\"\n * ```\n */\nexport interface RenderProgressBarOptions {\n /**\n * Characters used to render filled and empty cells.\n *\n * Each character should occupy one terminal cell. Multi-cell or combining\n * characters can make the visible track wider than `width`.\n *\n * @defaultValue `{ filled: '█', empty: '░' }`\n */\n characters?: ProgressBarCharacters;\n\n /**\n * Formats the value text appended after the track.\n *\n * The callback receives the clamped numeric value and its rounded\n * percentage. Returning an empty string leaves the trailing separator in\n * place; callers that need a track-only representation should account for\n * that when composing output.\n *\n * @defaultValue `({ percentage }) => `${percentage}%``\n */\n formatValue?: (context: ProgressBarValueContext) => string;\n\n /**\n * Optional text rendered before the track.\n *\n * Dynamic values must be escaped by the caller if the returned string will\n * later be inserted into a Blessed element with tags enabled. The bundled\n * Blessed adapter disables tags.\n */\n label?: string;\n\n /**\n * Upper bound of the numeric range.\n *\n * Must be finite and greater than `min`.\n *\n * @defaultValue `100`\n */\n max?: number;\n\n /**\n * Lower bound of the numeric range.\n *\n * Must be finite and lower than `max`.\n *\n * @defaultValue `0`\n */\n min?: number;\n\n /**\n * Current numeric value.\n *\n * The value must be finite. Values below `min` or above `max` are clamped\n * before the percentage and formatted value are calculated.\n */\n value: number;\n\n /**\n * Number of characters reserved for the progress track.\n *\n * This excludes the optional label, spaces, and formatted value. It must be\n * a positive integer.\n */\n width: number;\n}\n\n/**\n * Character pair used to draw a progress track.\n *\n * Use ASCII characters when the target terminal cannot display Unicode block\n * glyphs reliably.\n *\n * @example\n *\n * ```ts\n * const asciiCharacters: ProgressBarCharacters = {\n * filled: '#',\n * empty: '-',\n * };\n * ```\n */\nexport interface ProgressBarCharacters {\n /** Character repeated for unfilled track cells. */\n empty: string;\n\n /** Character repeated for filled track cells. */\n filled: string;\n}\n\n/**\n * Normalized value information passed to a progress value formatter.\n */\nexport interface ProgressBarValueContext {\n /**\n * Rounded progress in the inclusive range from `0` to `100`.\n */\n percentage: number;\n\n /**\n * Numeric value after it has been clamped to the configured range.\n */\n value: number;\n}\n\n/**\n * Renders a deterministic, single-line progress bar.\n *\n * This function is framework-independent and does not import Blessed. Import\n * it from `blessed-components/progress-bar` when only string rendering is\n * needed; that subpath keeps the Blessed adapter out of the module graph.\n *\n * The result has this structure:\n *\n * ```text\n * [label + space][fixed-width track][space][formatted value]\n * ```\n *\n * @param options - Numeric range, track width, characters, and text formatting.\n * @returns A plain terminal string. No ANSI sequences or Blessed tags are added.\n *\n * @throws `RangeError`\n * Thrown when `width` is not a positive integer.\n *\n * @throws `RangeError`\n * Thrown when `min` or `max` is not finite, or when `max` is not greater than\n * `min`.\n *\n * @throws `RangeError`\n * Thrown when `value` is not finite.\n *\n * @example Unicode output\n *\n * ```ts\n * import { renderProgressBar } from 'blessed-components/progress-bar';\n *\n * const output = renderProgressBar({\n * label: 'Quality',\n * value: 78,\n * width: 16,\n * });\n *\n * // \"Quality ████████████░░░░ 78%\"\n * ```\n *\n * @example ASCII output with a custom value\n *\n * ```ts\n * const output = renderProgressBar({\n * characters: { filled: '#', empty: '-' },\n * formatValue: ({ value }) => `${value} files`,\n * label: 'Uploaded',\n * value: 25,\n * width: 8,\n * });\n *\n * // \"Uploaded ##------ 25 files\"\n * ```\n */\nexport function renderProgressBar({\n characters = { empty: '░', filled: '█' },\n formatValue = ({ percentage }) => `${percentage}%`,\n label,\n max = 100,\n min = 0,\n value,\n width,\n}: RenderProgressBarOptions): string {\n if (!Number.isInteger(width) || width < 1) {\n throw new RangeError('ProgressBar width must be a positive integer.');\n }\n\n if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) {\n throw new RangeError('ProgressBar max must be greater than min.');\n }\n\n if (!Number.isFinite(value)) {\n throw new RangeError('ProgressBar value must be finite.');\n }\n\n const clampedValue = Math.min(max, Math.max(min, value));\n const percentage = Math.round(((clampedValue - min) / (max - min)) * 100);\n const filledWidth = Math.round((percentage / 100) * width);\n const track =\n characters.filled.repeat(filledWidth) + characters.empty.repeat(width - filledWidth);\n const prefix = label === undefined ? '' : `${label} `;\n const valueText = formatValue({ percentage, value: clampedValue });\n\n return `${prefix}${track} ${valueText}`;\n}\n","import blessed from 'blessed';\n\nimport {\n type RenderProgressBarOptions,\n renderProgressBar,\n} from '../../components/progress-bar/index.js';\n\n/**\n * Blessed box options supported by the ProgressBar adapter.\n *\n * `parent`, `content`, and `tags` are managed by {@link progressBar}:\n *\n * - `parent` comes from {@link ProgressBarOptions.parent}.\n * - `content` comes from {@link renderProgressBar}.\n * - `tags` is always disabled so labels and formatter output remain literal.\n */\nexport type ProgressBarBoxOptions = Omit<blessed.Widgets.BoxOptions, 'content' | 'parent' | 'tags'>;\n\n/**\n * Options accepted by the Blessed {@link progressBar} adapter.\n */\nexport interface ProgressBarOptions {\n /**\n * Optional Blessed box configuration.\n *\n * Position, dimensions, style, visibility, and other regular box options\n * can be supplied here.\n */\n box?: ProgressBarBoxOptions;\n\n /**\n * Data passed to the pure {@link renderProgressBar} renderer.\n */\n data: RenderProgressBarOptions;\n\n /**\n * Blessed node that receives the created box as a child.\n *\n * Usually this is a `Screen` or another `BoxElement`.\n */\n parent: blessed.Widgets.Node;\n}\n\n/**\n * Imperative handle returned by {@link progressBar}.\n *\n * The handle owns one Blessed `BoxElement`. It does not own the parent screen\n * and never calls `screen.render()`, allowing callers to batch several updates\n * into one terminal render.\n */\nexport interface ProgressBarHandle {\n /**\n * Underlying Blessed box.\n *\n * Use this for standard Blessed operations such as positioning, showing,\n * hiding, or applying styles.\n */\n readonly element: blessed.Widgets.BoxElement;\n\n /**\n * Destroys and detaches the owned box from its parent.\n *\n * This does not destroy the parent or screen. Do not use the handle after\n * calling this method.\n */\n destroy(): void;\n\n /**\n * Re-renders component content using new data.\n *\n * The underlying element is preserved. This method does not call\n * `screen.render()`; call it explicitly when updates should become visible.\n *\n * @param data - Complete renderer data replacing the previous data.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderProgressBar}.\n */\n setData(data: RenderProgressBarOptions): void;\n}\n\n/**\n * Creates a display-only ProgressBar backed by a Blessed `BoxElement`.\n *\n * Import this adapter from `blessed-components/progress-bar/blessed`. Use\n * {@link renderProgressBar} from `blessed-components/progress-bar` when a\n * Blessed element is unnecessary.\n *\n * The adapter:\n *\n * - renders initial content through {@link renderProgressBar};\n * - disables Blessed tags to keep dynamic text literal;\n * - updates the existing element through {@link ProgressBarHandle.setData};\n * - leaves screen rendering under caller control;\n * - detaches its element through {@link ProgressBarHandle.destroy}.\n *\n * @param options - Parent node, renderer data, and optional box settings.\n * @returns A handle for the created element, updates, and cleanup.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderProgressBar}.\n *\n * @example\n *\n * ```ts\n * import blessed from 'blessed';\n * import { progressBar } from 'blessed-components/progress-bar/blessed';\n *\n * const screen = blessed.screen({ smartCSR: true });\n * const upload = progressBar({\n * parent: screen,\n * box: {\n * top: 0,\n * left: 0,\n * width: 40,\n * height: 1,\n * },\n * data: {\n * label: 'Uploaded',\n * value: 25,\n * width: 20,\n * },\n * });\n *\n * screen.render();\n *\n * upload.setData({\n * label: 'Uploaded',\n * value: 75,\n * width: 20,\n * });\n * screen.render();\n *\n * upload.destroy();\n * screen.destroy();\n * ```\n */\nexport function progressBar({ box, data, parent }: ProgressBarOptions): ProgressBarHandle {\n const element = blessed.box({\n ...box,\n content: renderProgressBar(data),\n parent,\n tags: false,\n });\n\n return {\n element,\n destroy() {\n element.destroy();\n },\n setData(nextData) {\n element.setContent(renderProgressBar(nextData));\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/progress-bar/index.ts","../src/adapters/blessed/progress-bar.ts","../src/components/sparkline/index.ts","../src/adapters/blessed/sparkline.ts"],"names":["sparkline","blessed"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Options accepted by {@link renderProgressBar}.\n *\n * The renderer maps `value` from the inclusive numeric range defined by\n * `min` and `max` into a fixed-width terminal track. Values outside the range\n * are clamped before rendering.\n *\n * @example Basic percentage bar\n *\n * ```ts\n * renderProgressBar({\n * value: 50,\n * width: 10,\n * });\n * // \"█████░░░░░ 50%\"\n * ```\n *\n * @example Custom range and label\n *\n * ```ts\n * renderProgressBar({\n * label: 'Workers',\n * min: 0,\n * max: 8,\n * value: 6,\n * width: 8,\n * });\n * // \"Workers ██████░░ 75%\"\n * ```\n */\nexport interface RenderProgressBarOptions {\n /**\n * Characters used to render filled and empty cells.\n *\n * Each character should occupy one terminal cell. Multi-cell or combining\n * characters can make the visible track wider than `width`.\n *\n * @defaultValue `{ filled: '█', empty: '░' }`\n */\n characters?: ProgressBarCharacters;\n\n /**\n * Formats the value text appended after the track.\n *\n * The callback receives the clamped numeric value and its rounded\n * percentage. Returning an empty string leaves the trailing separator in\n * place; callers that need a track-only representation should account for\n * that when composing output.\n *\n * @defaultValue `({ percentage }) => `${percentage}%``\n */\n formatValue?: (context: ProgressBarValueContext) => string;\n\n /**\n * Optional text rendered before the track.\n *\n * Dynamic values must be escaped by the caller if the returned string will\n * later be inserted into a Blessed element with tags enabled. The bundled\n * Blessed adapter disables tags.\n */\n label?: string;\n\n /**\n * Upper bound of the numeric range.\n *\n * Must be finite and greater than `min`.\n *\n * @defaultValue `100`\n */\n max?: number;\n\n /**\n * Lower bound of the numeric range.\n *\n * Must be finite and lower than `max`.\n *\n * @defaultValue `0`\n */\n min?: number;\n\n /**\n * Current numeric value.\n *\n * The value must be finite. Values below `min` or above `max` are clamped\n * before the percentage and formatted value are calculated.\n */\n value: number;\n\n /**\n * Number of characters reserved for the progress track.\n *\n * This excludes the optional label, spaces, and formatted value. It must be\n * a positive integer.\n */\n width: number;\n}\n\n/**\n * Character pair used to draw a progress track.\n *\n * Use ASCII characters when the target terminal cannot display Unicode block\n * glyphs reliably.\n *\n * @example\n *\n * ```ts\n * const asciiCharacters: ProgressBarCharacters = {\n * filled: '#',\n * empty: '-',\n * };\n * ```\n */\nexport interface ProgressBarCharacters {\n /** Character repeated for unfilled track cells. */\n empty: string;\n\n /** Character repeated for filled track cells. */\n filled: string;\n}\n\n/**\n * Normalized value information passed to a progress value formatter.\n */\nexport interface ProgressBarValueContext {\n /**\n * Rounded progress in the inclusive range from `0` to `100`.\n */\n percentage: number;\n\n /**\n * Numeric value after it has been clamped to the configured range.\n */\n value: number;\n}\n\n/**\n * Renders a deterministic, single-line progress bar.\n *\n * This function is framework-independent and does not import Blessed. Import\n * it from `blessed-components/progress-bar` when only string rendering is\n * needed; that subpath keeps the Blessed adapter out of the module graph.\n *\n * The result has this structure:\n *\n * ```text\n * [label + space][fixed-width track][space][formatted value]\n * ```\n *\n * @param options - Numeric range, track width, characters, and text formatting.\n * @returns A plain terminal string. No ANSI sequences or Blessed tags are added.\n *\n * @throws `RangeError`\n * Thrown when `width` is not a positive integer.\n *\n * @throws `RangeError`\n * Thrown when `min` or `max` is not finite, or when `max` is not greater than\n * `min`.\n *\n * @throws `RangeError`\n * Thrown when `value` is not finite.\n *\n * @example Unicode output\n *\n * ```ts\n * import { renderProgressBar } from 'blessed-components/progress-bar';\n *\n * const output = renderProgressBar({\n * label: 'Quality',\n * value: 78,\n * width: 16,\n * });\n *\n * // \"Quality ████████████░░░░ 78%\"\n * ```\n *\n * @example ASCII output with a custom value\n *\n * ```ts\n * const output = renderProgressBar({\n * characters: { filled: '#', empty: '-' },\n * formatValue: ({ value }) => `${value} files`,\n * label: 'Uploaded',\n * value: 25,\n * width: 8,\n * });\n *\n * // \"Uploaded ##------ 25 files\"\n * ```\n */\nexport function renderProgressBar({\n characters = { empty: '░', filled: '█' },\n formatValue = ({ percentage }) => `${percentage}%`,\n label,\n max = 100,\n min = 0,\n value,\n width,\n}: RenderProgressBarOptions): string {\n if (!Number.isInteger(width) || width < 1) {\n throw new RangeError('ProgressBar width must be a positive integer.');\n }\n\n if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) {\n throw new RangeError('ProgressBar max must be greater than min.');\n }\n\n if (!Number.isFinite(value)) {\n throw new RangeError('ProgressBar value must be finite.');\n }\n\n const clampedValue = Math.min(max, Math.max(min, value));\n const percentage = Math.round(((clampedValue - min) / (max - min)) * 100);\n const filledWidth = Math.round((percentage / 100) * width);\n const track =\n characters.filled.repeat(filledWidth) + characters.empty.repeat(width - filledWidth);\n const prefix = label === undefined ? '' : `${label} `;\n const valueText = formatValue({ percentage, value: clampedValue });\n\n return `${prefix}${track} ${valueText}`;\n}\n","import blessed from 'blessed';\n\nimport {\n type RenderProgressBarOptions,\n renderProgressBar,\n} from '../../components/progress-bar/index.js';\n\n/**\n * Blessed box options supported by the ProgressBar adapter.\n *\n * `parent`, `content`, and `tags` are managed by {@link progressBar}:\n *\n * - `parent` comes from {@link ProgressBarOptions.parent}.\n * - `content` comes from {@link renderProgressBar}.\n * - `tags` is always disabled so labels and formatter output remain literal.\n */\nexport type ProgressBarBoxOptions = Omit<blessed.Widgets.BoxOptions, 'content' | 'parent' | 'tags'>;\n\n/**\n * Options accepted by the Blessed {@link progressBar} adapter.\n */\nexport interface ProgressBarOptions {\n /**\n * Optional Blessed box configuration.\n *\n * Position, dimensions, style, visibility, and other regular box options\n * can be supplied here.\n */\n box?: ProgressBarBoxOptions;\n\n /**\n * Data passed to the pure {@link renderProgressBar} renderer.\n */\n data: RenderProgressBarOptions;\n\n /**\n * Blessed node that receives the created box as a child.\n *\n * Usually this is a `Screen` or another `BoxElement`.\n */\n parent: blessed.Widgets.Node;\n}\n\n/**\n * Imperative handle returned by {@link progressBar}.\n *\n * The handle owns one Blessed `BoxElement`. It does not own the parent screen\n * and never calls `screen.render()`, allowing callers to batch several updates\n * into one terminal render.\n */\nexport interface ProgressBarHandle {\n /**\n * Underlying Blessed box.\n *\n * Use this for standard Blessed operations such as positioning, showing,\n * hiding, or applying styles.\n */\n readonly element: blessed.Widgets.BoxElement;\n\n /**\n * Destroys and detaches the owned box from its parent.\n *\n * This does not destroy the parent or screen. Do not use the handle after\n * calling this method.\n */\n destroy(): void;\n\n /**\n * Re-renders component content using new data.\n *\n * The underlying element is preserved. This method does not call\n * `screen.render()`; call it explicitly when updates should become visible.\n *\n * @param data - Complete renderer data replacing the previous data.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderProgressBar}.\n */\n setData(data: RenderProgressBarOptions): void;\n}\n\n/**\n * Creates a display-only ProgressBar backed by a Blessed `BoxElement`.\n *\n * Import this adapter from `blessed-components/progress-bar/blessed`. Use\n * {@link renderProgressBar} from `blessed-components/progress-bar` when a\n * Blessed element is unnecessary.\n *\n * The adapter:\n *\n * - renders initial content through {@link renderProgressBar};\n * - disables Blessed tags to keep dynamic text literal;\n * - updates the existing element through {@link ProgressBarHandle.setData};\n * - leaves screen rendering under caller control;\n * - detaches its element through {@link ProgressBarHandle.destroy}.\n *\n * @param options - Parent node, renderer data, and optional box settings.\n * @returns A handle for the created element, updates, and cleanup.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderProgressBar}.\n *\n * @example\n *\n * ```ts\n * import blessed from 'blessed';\n * import { progressBar } from 'blessed-components/progress-bar/blessed';\n *\n * const screen = blessed.screen({ smartCSR: true });\n * const upload = progressBar({\n * parent: screen,\n * box: {\n * top: 0,\n * left: 0,\n * width: 40,\n * height: 1,\n * },\n * data: {\n * label: 'Uploaded',\n * value: 25,\n * width: 20,\n * },\n * });\n *\n * screen.render();\n *\n * upload.setData({\n * label: 'Uploaded',\n * value: 75,\n * width: 20,\n * });\n * screen.render();\n *\n * upload.destroy();\n * screen.destroy();\n * ```\n */\nexport function progressBar({ box, data, parent }: ProgressBarOptions): ProgressBarHandle {\n const element = blessed.box({\n ...box,\n content: renderProgressBar(data),\n parent,\n tags: false,\n });\n\n return {\n element,\n destroy() {\n element.destroy();\n },\n setData(nextData) {\n element.setContent(renderProgressBar(nextData));\n },\n };\n}\n","const DEFAULT_CHARACTERS = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'] as const;\n\n/**\n * Options accepted by {@link renderSparkline}.\n *\n * Samples are kept in input order, scaled into `characters`, and reduced to at\n * most `width` cells. Values outside an explicit domain are clamped.\n */\nexport interface RenderSparklineOptions {\n /**\n * Ordered glyph scale from lowest to highest intensity.\n *\n * At least two non-empty strings are required. Glyphs should occupy one\n * terminal cell to keep output width predictable.\n *\n * @defaultValue `['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']`\n */\n characters?: readonly string[];\n\n /**\n * Text returned when `values` is empty.\n *\n * Metadata is not rendered for an empty series.\n *\n * @defaultValue `'No data'`\n */\n emptyText?: string;\n\n /** Optional heading label rendered above the graph. */\n label?: string;\n\n /**\n * Explicit upper domain bound.\n *\n * When omitted, the largest input value is used. If either explicit bound is\n * supplied, the resolved `max` must be greater than the resolved `min`.\n */\n max?: number;\n\n /**\n * Explicit lower domain bound.\n *\n * When omitted, the smallest input value is used.\n */\n min?: number;\n\n /** Optional text appended to the graph line after one space. */\n summary?: string;\n\n /**\n * Optional primary value rendered next to `label`.\n *\n * This value is presentation metadata and does not affect graph scaling.\n */\n value?: number | string;\n\n /**\n * Ordered finite numeric samples.\n *\n * The input array is never mutated.\n */\n values: readonly number[];\n\n /**\n * Positive integer maximum number of graph cells.\n *\n * Longer series are downsampled. Shorter series are not padded.\n */\n width: number;\n}\n\n/**\n * Renders a deterministic terminal sparkline.\n *\n * This pure renderer does not import Blessed. Use the\n * `blessed-components/sparkline` subpath to keep the Blessed adapter outside\n * the module graph.\n *\n * When downsampling is required, the series is divided into ordered buckets\n * and each bucket keeps its maximum value. This preserves narrow peaks. A\n * constant series uses the middle glyph to avoid division by zero.\n *\n * Output shape:\n *\n * ```text\n * [optional label and value]\n * [graph][optional summary]\n * ```\n *\n * With no metadata, only the graph line is returned.\n *\n * @param options - Series, domain, width, glyphs, and optional metadata.\n * @returns Plain text without ANSI sequences or Blessed tags.\n *\n * @throws `RangeError`\n * Thrown when `width` is not a positive integer.\n *\n * @throws `RangeError`\n * Thrown when fewer than two non-empty characters are provided.\n *\n * @throws `RangeError`\n * Thrown when any sample or domain bound is not finite.\n *\n * @throws `RangeError`\n * Thrown when an explicit domain does not have `max > min`.\n *\n * @example Basic series\n *\n * ```ts\n * import { renderSparkline } from 'blessed-components/sparkline';\n *\n * renderSparkline({\n * values: [1, 2, 3, 4, 5, 6, 7, 8],\n * width: 8,\n * });\n * // \"▁▂▃▄▅▆▇█\"\n * ```\n *\n * @example Metadata and summary\n *\n * ```ts\n * renderSparkline({\n * label: 'Downloads',\n * value: '25.2M',\n * values: [1, 3, 2, 8],\n * width: 4,\n * summary: 'peak: 8M',\n * });\n * // \"Downloads 25.2M\\n▁▃▂█ peak: 8M\"\n * ```\n */\nexport function renderSparkline({\n characters = DEFAULT_CHARACTERS,\n emptyText = 'No data',\n label,\n max: configuredMax,\n min: configuredMin,\n summary,\n value: displayValue,\n values,\n width,\n}: RenderSparklineOptions): string {\n if (!Number.isInteger(width) || width < 1) {\n throw new RangeError('Sparkline width must be a positive integer.');\n }\n\n if (characters.length < 2 || characters.some((character) => character.length === 0)) {\n throw new RangeError('Sparkline characters must contain at least two non-empty strings.');\n }\n\n if (values.some((value) => !Number.isFinite(value))) {\n throw new RangeError('Sparkline values must be finite.');\n }\n\n if (values.length === 0) {\n return emptyText;\n }\n\n const min = configuredMin ?? Math.min(...values);\n const max = configuredMax ?? Math.max(...values);\n\n if (!Number.isFinite(min) || !Number.isFinite(max) || max < min) {\n throw new RangeError('Sparkline max must be greater than or equal to min.');\n }\n\n if ((configuredMin !== undefined || configuredMax !== undefined) && max === min) {\n throw new RangeError('Sparkline explicit max must be greater than min.');\n }\n\n const constantIndex = Math.floor((characters.length - 1) / 2);\n const samples =\n values.length <= width\n ? values\n : Array.from({ length: width }, (_, index) => {\n const start = Math.floor((index * values.length) / width);\n const end = Math.floor(((index + 1) * values.length) / width);\n\n return Math.max(...values.slice(start, end));\n });\n const sparkline = samples\n .map((value) => {\n if (min === max) {\n return characters[constantIndex];\n }\n\n const clampedValue = Math.min(max, Math.max(min, value));\n const ratio = (clampedValue - min) / (max - min);\n const index =\n clampedValue >= max ? characters.length - 1 : Math.floor(ratio * (characters.length - 1));\n\n return characters[index];\n })\n .join('');\n const heading = [label, displayValue].filter((part) => part !== undefined).join(' ');\n const chartLine = summary === undefined ? sparkline : `${sparkline} ${summary}`;\n\n return heading.length === 0 ? chartLine : `${heading}\\n${chartLine}`;\n}\n","import blessed from 'blessed';\n\nimport { type RenderSparklineOptions, renderSparkline } from '../../components/sparkline/index.js';\n\n/**\n * Blessed box options supported by the Sparkline adapter.\n *\n * `parent`, `content`, and `tags` are controlled by {@link sparkline}.\n */\nexport type SparklineBoxOptions = Omit<blessed.Widgets.BoxOptions, 'content' | 'parent' | 'tags'>;\n\n/** Options accepted by the Blessed {@link sparkline} adapter. */\nexport interface SparklineOptions {\n /** Optional position, dimensions, style, and other Blessed box settings. */\n box?: SparklineBoxOptions;\n\n /** Data passed to the pure {@link renderSparkline} renderer. */\n data: RenderSparklineOptions;\n\n /** Blessed screen or node that receives the created box. */\n parent: blessed.Widgets.Node;\n}\n\n/**\n * Imperative handle returned by {@link sparkline}.\n *\n * The handle owns one box, never owns the parent screen, and never calls\n * `screen.render()`.\n */\nexport interface SparklineHandle {\n /** Underlying Blessed box used for standard element operations. */\n readonly element: blessed.Widgets.BoxElement;\n\n /**\n * Destroys and detaches the owned box.\n *\n * The parent and screen remain alive.\n */\n destroy(): void;\n\n /**\n * Replaces renderer data while preserving the Blessed element.\n *\n * Call `screen.render()` after one or more updates to flush visible output.\n *\n * @param data - Complete renderer data replacing the previous data.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderSparkline}.\n */\n setData(data: RenderSparklineOptions): void;\n}\n\n/**\n * Creates a display-only Sparkline backed by a Blessed `BoxElement`.\n *\n * Import from `blessed-components/sparkline/blessed`. The adapter disables\n * Blessed tags, renders through {@link renderSparkline}, and leaves terminal\n * rendering under caller control.\n *\n * @param options - Parent node, renderer data, and optional box settings.\n * @returns Handle for the created box, updates, and cleanup.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderSparkline}.\n *\n * @example\n *\n * ```ts\n * import blessed from 'blessed';\n * import { sparkline } from 'blessed-components/sparkline/blessed';\n *\n * const screen = blessed.screen({ smartCSR: true });\n * const downloads = sparkline({\n * parent: screen,\n * box: { top: 0, left: 0, width: 40, height: 2 },\n * data: {\n * label: 'Downloads',\n * values: [1, 3, 2, 8],\n * width: 20,\n * },\n * });\n *\n * screen.render();\n * downloads.setData({\n * label: 'Downloads',\n * values: [2, 4, 6, 8],\n * width: 20,\n * });\n * screen.render();\n *\n * downloads.destroy();\n * screen.destroy();\n * ```\n */\nexport function sparkline({ box, data, parent }: SparklineOptions): SparklineHandle {\n const element = blessed.box({\n ...box,\n content: renderSparkline(data),\n parent,\n tags: false,\n });\n\n return {\n element,\n destroy() {\n element.destroy();\n },\n setData(nextData) {\n element.setContent(renderSparkline(nextData));\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var blessed = require('blessed');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var blessed__default = /*#__PURE__*/_interopDefault(blessed);
|
|
8
|
+
|
|
9
|
+
// src/adapters/blessed/sparkline.ts
|
|
10
|
+
|
|
11
|
+
// src/components/sparkline/index.ts
|
|
12
|
+
var DEFAULT_CHARACTERS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
|
|
13
|
+
function renderSparkline({
|
|
14
|
+
characters = DEFAULT_CHARACTERS,
|
|
15
|
+
emptyText = "No data",
|
|
16
|
+
label,
|
|
17
|
+
max: configuredMax,
|
|
18
|
+
min: configuredMin,
|
|
19
|
+
summary,
|
|
20
|
+
value: displayValue,
|
|
21
|
+
values,
|
|
22
|
+
width
|
|
23
|
+
}) {
|
|
24
|
+
if (!Number.isInteger(width) || width < 1) {
|
|
25
|
+
throw new RangeError("Sparkline width must be a positive integer.");
|
|
26
|
+
}
|
|
27
|
+
if (characters.length < 2 || characters.some((character) => character.length === 0)) {
|
|
28
|
+
throw new RangeError("Sparkline characters must contain at least two non-empty strings.");
|
|
29
|
+
}
|
|
30
|
+
if (values.some((value) => !Number.isFinite(value))) {
|
|
31
|
+
throw new RangeError("Sparkline values must be finite.");
|
|
32
|
+
}
|
|
33
|
+
if (values.length === 0) {
|
|
34
|
+
return emptyText;
|
|
35
|
+
}
|
|
36
|
+
const min = configuredMin ?? Math.min(...values);
|
|
37
|
+
const max = configuredMax ?? Math.max(...values);
|
|
38
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || max < min) {
|
|
39
|
+
throw new RangeError("Sparkline max must be greater than or equal to min.");
|
|
40
|
+
}
|
|
41
|
+
if ((configuredMin !== void 0 || configuredMax !== void 0) && max === min) {
|
|
42
|
+
throw new RangeError("Sparkline explicit max must be greater than min.");
|
|
43
|
+
}
|
|
44
|
+
const constantIndex = Math.floor((characters.length - 1) / 2);
|
|
45
|
+
const samples = values.length <= width ? values : Array.from({ length: width }, (_, index) => {
|
|
46
|
+
const start = Math.floor(index * values.length / width);
|
|
47
|
+
const end = Math.floor((index + 1) * values.length / width);
|
|
48
|
+
return Math.max(...values.slice(start, end));
|
|
49
|
+
});
|
|
50
|
+
const sparkline2 = samples.map((value) => {
|
|
51
|
+
if (min === max) {
|
|
52
|
+
return characters[constantIndex];
|
|
53
|
+
}
|
|
54
|
+
const clampedValue = Math.min(max, Math.max(min, value));
|
|
55
|
+
const ratio = (clampedValue - min) / (max - min);
|
|
56
|
+
const index = clampedValue >= max ? characters.length - 1 : Math.floor(ratio * (characters.length - 1));
|
|
57
|
+
return characters[index];
|
|
58
|
+
}).join("");
|
|
59
|
+
const heading = [label, displayValue].filter((part) => part !== void 0).join(" ");
|
|
60
|
+
const chartLine = summary === void 0 ? sparkline2 : `${sparkline2} ${summary}`;
|
|
61
|
+
return heading.length === 0 ? chartLine : `${heading}
|
|
62
|
+
${chartLine}`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/adapters/blessed/sparkline.ts
|
|
66
|
+
function sparkline({ box, data, parent }) {
|
|
67
|
+
const element = blessed__default.default.box({
|
|
68
|
+
...box,
|
|
69
|
+
content: renderSparkline(data),
|
|
70
|
+
parent,
|
|
71
|
+
tags: false
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
element,
|
|
75
|
+
destroy() {
|
|
76
|
+
element.destroy();
|
|
77
|
+
},
|
|
78
|
+
setData(nextData) {
|
|
79
|
+
element.setContent(renderSparkline(nextData));
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
exports.sparkline = sparkline;
|
|
85
|
+
//# sourceMappingURL=blessed.cjs.map
|
|
86
|
+
//# sourceMappingURL=blessed.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/sparkline/index.ts","../../src/adapters/blessed/sparkline.ts"],"names":["sparkline","blessed"],"mappings":"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","file":"blessed.cjs","sourcesContent":["const DEFAULT_CHARACTERS = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'] as const;\n\n/**\n * Options accepted by {@link renderSparkline}.\n *\n * Samples are kept in input order, scaled into `characters`, and reduced to at\n * most `width` cells. Values outside an explicit domain are clamped.\n */\nexport interface RenderSparklineOptions {\n /**\n * Ordered glyph scale from lowest to highest intensity.\n *\n * At least two non-empty strings are required. Glyphs should occupy one\n * terminal cell to keep output width predictable.\n *\n * @defaultValue `['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']`\n */\n characters?: readonly string[];\n\n /**\n * Text returned when `values` is empty.\n *\n * Metadata is not rendered for an empty series.\n *\n * @defaultValue `'No data'`\n */\n emptyText?: string;\n\n /** Optional heading label rendered above the graph. */\n label?: string;\n\n /**\n * Explicit upper domain bound.\n *\n * When omitted, the largest input value is used. If either explicit bound is\n * supplied, the resolved `max` must be greater than the resolved `min`.\n */\n max?: number;\n\n /**\n * Explicit lower domain bound.\n *\n * When omitted, the smallest input value is used.\n */\n min?: number;\n\n /** Optional text appended to the graph line after one space. */\n summary?: string;\n\n /**\n * Optional primary value rendered next to `label`.\n *\n * This value is presentation metadata and does not affect graph scaling.\n */\n value?: number | string;\n\n /**\n * Ordered finite numeric samples.\n *\n * The input array is never mutated.\n */\n values: readonly number[];\n\n /**\n * Positive integer maximum number of graph cells.\n *\n * Longer series are downsampled. Shorter series are not padded.\n */\n width: number;\n}\n\n/**\n * Renders a deterministic terminal sparkline.\n *\n * This pure renderer does not import Blessed. Use the\n * `blessed-components/sparkline` subpath to keep the Blessed adapter outside\n * the module graph.\n *\n * When downsampling is required, the series is divided into ordered buckets\n * and each bucket keeps its maximum value. This preserves narrow peaks. A\n * constant series uses the middle glyph to avoid division by zero.\n *\n * Output shape:\n *\n * ```text\n * [optional label and value]\n * [graph][optional summary]\n * ```\n *\n * With no metadata, only the graph line is returned.\n *\n * @param options - Series, domain, width, glyphs, and optional metadata.\n * @returns Plain text without ANSI sequences or Blessed tags.\n *\n * @throws `RangeError`\n * Thrown when `width` is not a positive integer.\n *\n * @throws `RangeError`\n * Thrown when fewer than two non-empty characters are provided.\n *\n * @throws `RangeError`\n * Thrown when any sample or domain bound is not finite.\n *\n * @throws `RangeError`\n * Thrown when an explicit domain does not have `max > min`.\n *\n * @example Basic series\n *\n * ```ts\n * import { renderSparkline } from 'blessed-components/sparkline';\n *\n * renderSparkline({\n * values: [1, 2, 3, 4, 5, 6, 7, 8],\n * width: 8,\n * });\n * // \"▁▂▃▄▅▆▇█\"\n * ```\n *\n * @example Metadata and summary\n *\n * ```ts\n * renderSparkline({\n * label: 'Downloads',\n * value: '25.2M',\n * values: [1, 3, 2, 8],\n * width: 4,\n * summary: 'peak: 8M',\n * });\n * // \"Downloads 25.2M\\n▁▃▂█ peak: 8M\"\n * ```\n */\nexport function renderSparkline({\n characters = DEFAULT_CHARACTERS,\n emptyText = 'No data',\n label,\n max: configuredMax,\n min: configuredMin,\n summary,\n value: displayValue,\n values,\n width,\n}: RenderSparklineOptions): string {\n if (!Number.isInteger(width) || width < 1) {\n throw new RangeError('Sparkline width must be a positive integer.');\n }\n\n if (characters.length < 2 || characters.some((character) => character.length === 0)) {\n throw new RangeError('Sparkline characters must contain at least two non-empty strings.');\n }\n\n if (values.some((value) => !Number.isFinite(value))) {\n throw new RangeError('Sparkline values must be finite.');\n }\n\n if (values.length === 0) {\n return emptyText;\n }\n\n const min = configuredMin ?? Math.min(...values);\n const max = configuredMax ?? Math.max(...values);\n\n if (!Number.isFinite(min) || !Number.isFinite(max) || max < min) {\n throw new RangeError('Sparkline max must be greater than or equal to min.');\n }\n\n if ((configuredMin !== undefined || configuredMax !== undefined) && max === min) {\n throw new RangeError('Sparkline explicit max must be greater than min.');\n }\n\n const constantIndex = Math.floor((characters.length - 1) / 2);\n const samples =\n values.length <= width\n ? values\n : Array.from({ length: width }, (_, index) => {\n const start = Math.floor((index * values.length) / width);\n const end = Math.floor(((index + 1) * values.length) / width);\n\n return Math.max(...values.slice(start, end));\n });\n const sparkline = samples\n .map((value) => {\n if (min === max) {\n return characters[constantIndex];\n }\n\n const clampedValue = Math.min(max, Math.max(min, value));\n const ratio = (clampedValue - min) / (max - min);\n const index =\n clampedValue >= max ? characters.length - 1 : Math.floor(ratio * (characters.length - 1));\n\n return characters[index];\n })\n .join('');\n const heading = [label, displayValue].filter((part) => part !== undefined).join(' ');\n const chartLine = summary === undefined ? sparkline : `${sparkline} ${summary}`;\n\n return heading.length === 0 ? chartLine : `${heading}\\n${chartLine}`;\n}\n","import blessed from 'blessed';\n\nimport { type RenderSparklineOptions, renderSparkline } from '../../components/sparkline/index.js';\n\n/**\n * Blessed box options supported by the Sparkline adapter.\n *\n * `parent`, `content`, and `tags` are controlled by {@link sparkline}.\n */\nexport type SparklineBoxOptions = Omit<blessed.Widgets.BoxOptions, 'content' | 'parent' | 'tags'>;\n\n/** Options accepted by the Blessed {@link sparkline} adapter. */\nexport interface SparklineOptions {\n /** Optional position, dimensions, style, and other Blessed box settings. */\n box?: SparklineBoxOptions;\n\n /** Data passed to the pure {@link renderSparkline} renderer. */\n data: RenderSparklineOptions;\n\n /** Blessed screen or node that receives the created box. */\n parent: blessed.Widgets.Node;\n}\n\n/**\n * Imperative handle returned by {@link sparkline}.\n *\n * The handle owns one box, never owns the parent screen, and never calls\n * `screen.render()`.\n */\nexport interface SparklineHandle {\n /** Underlying Blessed box used for standard element operations. */\n readonly element: blessed.Widgets.BoxElement;\n\n /**\n * Destroys and detaches the owned box.\n *\n * The parent and screen remain alive.\n */\n destroy(): void;\n\n /**\n * Replaces renderer data while preserving the Blessed element.\n *\n * Call `screen.render()` after one or more updates to flush visible output.\n *\n * @param data - Complete renderer data replacing the previous data.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderSparkline}.\n */\n setData(data: RenderSparklineOptions): void;\n}\n\n/**\n * Creates a display-only Sparkline backed by a Blessed `BoxElement`.\n *\n * Import from `blessed-components/sparkline/blessed`. The adapter disables\n * Blessed tags, renders through {@link renderSparkline}, and leaves terminal\n * rendering under caller control.\n *\n * @param options - Parent node, renderer data, and optional box settings.\n * @returns Handle for the created box, updates, and cleanup.\n *\n * @throws `RangeError`\n * Propagates validation errors from {@link renderSparkline}.\n *\n * @example\n *\n * ```ts\n * import blessed from 'blessed';\n * import { sparkline } from 'blessed-components/sparkline/blessed';\n *\n * const screen = blessed.screen({ smartCSR: true });\n * const downloads = sparkline({\n * parent: screen,\n * box: { top: 0, left: 0, width: 40, height: 2 },\n * data: {\n * label: 'Downloads',\n * values: [1, 3, 2, 8],\n * width: 20,\n * },\n * });\n *\n * screen.render();\n * downloads.setData({\n * label: 'Downloads',\n * values: [2, 4, 6, 8],\n * width: 20,\n * });\n * screen.render();\n *\n * downloads.destroy();\n * screen.destroy();\n * ```\n */\nexport function sparkline({ box, data, parent }: SparklineOptions): SparklineHandle {\n const element = blessed.box({\n ...box,\n content: renderSparkline(data),\n parent,\n tags: false,\n });\n\n return {\n element,\n destroy() {\n element.destroy();\n },\n setData(nextData) {\n element.setContent(renderSparkline(nextData));\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import blessed from 'blessed';
|
|
2
|
+
import { RenderSparklineOptions } from './index.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Blessed box options supported by the Sparkline adapter.
|
|
6
|
+
*
|
|
7
|
+
* `parent`, `content`, and `tags` are controlled by {@link sparkline}.
|
|
8
|
+
*/
|
|
9
|
+
type SparklineBoxOptions = Omit<blessed.Widgets.BoxOptions, 'content' | 'parent' | 'tags'>;
|
|
10
|
+
/** Options accepted by the Blessed {@link sparkline} adapter. */
|
|
11
|
+
interface SparklineOptions {
|
|
12
|
+
/** Optional position, dimensions, style, and other Blessed box settings. */
|
|
13
|
+
box?: SparklineBoxOptions;
|
|
14
|
+
/** Data passed to the pure {@link renderSparkline} renderer. */
|
|
15
|
+
data: RenderSparklineOptions;
|
|
16
|
+
/** Blessed screen or node that receives the created box. */
|
|
17
|
+
parent: blessed.Widgets.Node;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Imperative handle returned by {@link sparkline}.
|
|
21
|
+
*
|
|
22
|
+
* The handle owns one box, never owns the parent screen, and never calls
|
|
23
|
+
* `screen.render()`.
|
|
24
|
+
*/
|
|
25
|
+
interface SparklineHandle {
|
|
26
|
+
/** Underlying Blessed box used for standard element operations. */
|
|
27
|
+
readonly element: blessed.Widgets.BoxElement;
|
|
28
|
+
/**
|
|
29
|
+
* Destroys and detaches the owned box.
|
|
30
|
+
*
|
|
31
|
+
* The parent and screen remain alive.
|
|
32
|
+
*/
|
|
33
|
+
destroy(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Replaces renderer data while preserving the Blessed element.
|
|
36
|
+
*
|
|
37
|
+
* Call `screen.render()` after one or more updates to flush visible output.
|
|
38
|
+
*
|
|
39
|
+
* @param data - Complete renderer data replacing the previous data.
|
|
40
|
+
*
|
|
41
|
+
* @throws `RangeError`
|
|
42
|
+
* Propagates validation errors from {@link renderSparkline}.
|
|
43
|
+
*/
|
|
44
|
+
setData(data: RenderSparklineOptions): void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Creates a display-only Sparkline backed by a Blessed `BoxElement`.
|
|
48
|
+
*
|
|
49
|
+
* Import from `blessed-components/sparkline/blessed`. The adapter disables
|
|
50
|
+
* Blessed tags, renders through {@link renderSparkline}, and leaves terminal
|
|
51
|
+
* rendering under caller control.
|
|
52
|
+
*
|
|
53
|
+
* @param options - Parent node, renderer data, and optional box settings.
|
|
54
|
+
* @returns Handle for the created box, updates, and cleanup.
|
|
55
|
+
*
|
|
56
|
+
* @throws `RangeError`
|
|
57
|
+
* Propagates validation errors from {@link renderSparkline}.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
*
|
|
61
|
+
* ```ts
|
|
62
|
+
* import blessed from 'blessed';
|
|
63
|
+
* import { sparkline } from 'blessed-components/sparkline/blessed';
|
|
64
|
+
*
|
|
65
|
+
* const screen = blessed.screen({ smartCSR: true });
|
|
66
|
+
* const downloads = sparkline({
|
|
67
|
+
* parent: screen,
|
|
68
|
+
* box: { top: 0, left: 0, width: 40, height: 2 },
|
|
69
|
+
* data: {
|
|
70
|
+
* label: 'Downloads',
|
|
71
|
+
* values: [1, 3, 2, 8],
|
|
72
|
+
* width: 20,
|
|
73
|
+
* },
|
|
74
|
+
* });
|
|
75
|
+
*
|
|
76
|
+
* screen.render();
|
|
77
|
+
* downloads.setData({
|
|
78
|
+
* label: 'Downloads',
|
|
79
|
+
* values: [2, 4, 6, 8],
|
|
80
|
+
* width: 20,
|
|
81
|
+
* });
|
|
82
|
+
* screen.render();
|
|
83
|
+
*
|
|
84
|
+
* downloads.destroy();
|
|
85
|
+
* screen.destroy();
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
declare function sparkline({ box, data, parent }: SparklineOptions): SparklineHandle;
|
|
89
|
+
|
|
90
|
+
export { type SparklineBoxOptions, type SparklineHandle, type SparklineOptions, sparkline };
|