@tuicomponents/sparkline 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ HEIGHT_BLOCKS: () => HEIGHT_BLOCKS,
24
+ SparklineComponent: () => SparklineComponent,
25
+ computeSparklineLayout: () => computeSparklineLayout,
26
+ createSparkline: () => createSparkline,
27
+ renderSparklineAnsi: () => renderSparklineAnsi,
28
+ renderSparklineMarkdown: () => renderSparklineMarkdown,
29
+ sparklineInputSchema: () => sparklineInputSchema
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/sparkline.ts
34
+ var import_core2 = require("@tuicomponents/core");
35
+
36
+ // src/schema.ts
37
+ var import_zod = require("zod");
38
+ var sparklineInputSchema = import_zod.z.object({
39
+ /** Array of numeric data points to visualize (at least 1 required) */
40
+ values: import_zod.z.array(import_zod.z.number()).min(1),
41
+ /** Compress output to this width if less than values.length */
42
+ width: import_zod.z.number().int().positive().optional(),
43
+ /** Explicit minimum value for scaling (defaults to min of values) */
44
+ min: import_zod.z.number().optional(),
45
+ /** Explicit maximum value for scaling (defaults to max of values) */
46
+ max: import_zod.z.number().optional(),
47
+ /** Optional label prefix (e.g., "CPU: ") */
48
+ label: import_zod.z.string().optional()
49
+ });
50
+
51
+ // src/layout.ts
52
+ var HEIGHT_BLOCKS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
53
+ var MIDDLE_BLOCK_INDEX = 4;
54
+ function valueToBlockIndex(value, min, max) {
55
+ if (max === min) {
56
+ return MIDDLE_BLOCK_INDEX;
57
+ }
58
+ const normalized = (value - min) / (max - min);
59
+ const index = Math.floor(normalized * 8);
60
+ return Math.max(0, Math.min(7, index));
61
+ }
62
+ function bucketValues(values, targetWidth) {
63
+ if (targetWidth >= values.length) {
64
+ return values;
65
+ }
66
+ const result = [];
67
+ const bucketSize = values.length / targetWidth;
68
+ for (let i = 0; i < targetWidth; i++) {
69
+ const startIndex = Math.floor(i * bucketSize);
70
+ const endIndex = Math.floor((i + 1) * bucketSize);
71
+ let sum = 0;
72
+ let count = 0;
73
+ for (let j = startIndex; j < endIndex && j < values.length; j++) {
74
+ const value = values[j];
75
+ if (value !== void 0) {
76
+ sum += value;
77
+ count++;
78
+ }
79
+ }
80
+ result.push(count > 0 ? sum / count : 0);
81
+ }
82
+ return result;
83
+ }
84
+ function computeSparklineLayout(input) {
85
+ const { values, width, min: explicitMin, max: explicitMax, label } = input;
86
+ const displayValues = width !== void 0 && width < values.length ? bucketValues(values, width) : values;
87
+ const actualMin = Math.min(...displayValues);
88
+ const actualMax = Math.max(...displayValues);
89
+ const min = explicitMin ?? actualMin;
90
+ const max = explicitMax ?? actualMax;
91
+ const blocks = displayValues.map((v) => {
92
+ const clampedValue = Math.max(min, Math.min(max, v));
93
+ const index = valueToBlockIndex(clampedValue, min, max);
94
+ return HEIGHT_BLOCKS[index];
95
+ }).join("");
96
+ const labelWidth = label?.length ?? 0;
97
+ const blocksWidth = displayValues.length;
98
+ const totalWidth = labelWidth + blocksWidth;
99
+ return {
100
+ blocks,
101
+ label,
102
+ totalWidth
103
+ };
104
+ }
105
+
106
+ // src/renderers.ts
107
+ var import_core = require("@tuicomponents/core");
108
+ function renderSparklineAnsi(layout, theme) {
109
+ const { blocks, label } = layout;
110
+ const styledBlocks = theme ? theme.semantic.primary(blocks) : blocks;
111
+ const styledLabel = label ? theme ? theme.semantic.header(label) : label : "";
112
+ return styledLabel + styledBlocks;
113
+ }
114
+ function renderSparklineMarkdown(layout, context) {
115
+ const { blocks, label } = layout;
116
+ const styledBlocks = context.style.secondary(blocks);
117
+ const styledLabel = label ?? "";
118
+ return (0, import_core.anchorLine)(styledLabel + styledBlocks, import_core.DEFAULT_ANCHOR);
119
+ }
120
+
121
+ // src/sparkline.ts
122
+ var SparklineComponent = class extends import_core2.BaseTuiComponent {
123
+ metadata = {
124
+ name: "sparkline",
125
+ description: "Compact inline sparkline visualization using height block characters (\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588)",
126
+ version: "0.1.0",
127
+ supportedModes: ["ansi", "markdown"],
128
+ examples: [
129
+ {
130
+ name: "basic",
131
+ description: "Simple ascending values",
132
+ input: { values: [1, 2, 3, 4, 5, 6, 7, 8] }
133
+ },
134
+ {
135
+ name: "with-label",
136
+ description: "Sparkline with a label prefix",
137
+ input: { values: [10, 25, 40, 35, 50, 45, 60], label: "Revenue: " }
138
+ },
139
+ {
140
+ name: "compressed",
141
+ description: "Data compressed to fewer columns",
142
+ input: {
143
+ values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
144
+ width: 6
145
+ }
146
+ },
147
+ {
148
+ name: "explicit-range",
149
+ description: "Values scaled to explicit min/max range",
150
+ input: {
151
+ values: [50, 60, 70, 65, 75],
152
+ min: 0,
153
+ max: 100
154
+ }
155
+ }
156
+ ]
157
+ };
158
+ schema = sparklineInputSchema;
159
+ render(input, context) {
160
+ const parsed = this.schema.parse(input);
161
+ const layout = computeSparklineLayout(parsed);
162
+ const output = context.renderMode === "markdown" ? renderSparklineMarkdown(layout, context) : renderSparklineAnsi(layout, context.theme);
163
+ const measured = (0, import_core2.measureLines)(output);
164
+ return {
165
+ output,
166
+ actualWidth: measured.maxWidth,
167
+ lineCount: measured.lineCount
168
+ };
169
+ }
170
+ };
171
+ function createSparkline() {
172
+ return new SparklineComponent();
173
+ }
174
+ import_core2.registry.register(createSparkline);
175
+ // Annotate the CommonJS export names for ESM import in node:
176
+ 0 && (module.exports = {
177
+ HEIGHT_BLOCKS,
178
+ SparklineComponent,
179
+ computeSparklineLayout,
180
+ createSparkline,
181
+ renderSparklineAnsi,
182
+ renderSparklineMarkdown,
183
+ sparklineInputSchema
184
+ });
185
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/sparkline.ts","../src/schema.ts","../src/layout.ts","../src/renderers.ts"],"sourcesContent":["// Component exports\nexport { createSparkline, SparklineComponent } from \"./sparkline.js\";\n\n// Schema exports\nexport {\n sparklineInputSchema,\n type SparklineInput,\n type SparklineInputWithDefaults,\n} from \"./schema.js\";\n\n// Layout exports (for advanced usage)\nexport {\n HEIGHT_BLOCKS,\n computeSparklineLayout,\n type SparklineLayout,\n} from \"./layout.js\";\n\n// Renderer exports (for advanced usage)\nexport { renderSparklineAnsi, renderSparklineMarkdown } from \"./renderers.js\";\n","import {\n BaseTuiComponent,\n measureLines,\n registry,\n type ComponentMetadata,\n type RenderContext,\n type RenderResult,\n} from \"@tuicomponents/core\";\nimport { sparklineInputSchema, type SparklineInput } from \"./schema.js\";\nimport { computeSparklineLayout } from \"./layout.js\";\nimport { renderSparklineAnsi, renderSparklineMarkdown } from \"./renderers.js\";\n\n/**\n * Sparkline component for compact inline data visualization.\n *\n * Renders numeric data as a row of height block characters: ▁▂▃▄▅▆▇█\n *\n * @example\n * ```ts\n * const sparkline = createSparkline();\n * const result = sparkline.render(\n * { values: [1, 3, 5, 7, 6, 4, 2], label: \"Trend: \" },\n * context\n * );\n * // Output: \"Trend: ▁▃▅▇▆▄▂\"\n * ```\n */\nexport class SparklineComponent extends BaseTuiComponent<\n SparklineInput,\n typeof sparklineInputSchema\n> {\n readonly metadata: ComponentMetadata<SparklineInput> = {\n name: \"sparkline\",\n description:\n \"Compact inline sparkline visualization using height block characters (▁▂▃▄▅▆▇█)\",\n version: \"0.1.0\",\n supportedModes: [\"ansi\", \"markdown\"],\n examples: [\n {\n name: \"basic\",\n description: \"Simple ascending values\",\n input: { values: [1, 2, 3, 4, 5, 6, 7, 8] },\n },\n {\n name: \"with-label\",\n description: \"Sparkline with a label prefix\",\n input: { values: [10, 25, 40, 35, 50, 45, 60], label: \"Revenue: \" },\n },\n {\n name: \"compressed\",\n description: \"Data compressed to fewer columns\",\n input: {\n values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],\n width: 6,\n },\n },\n {\n name: \"explicit-range\",\n description: \"Values scaled to explicit min/max range\",\n input: {\n values: [50, 60, 70, 65, 75],\n min: 0,\n max: 100,\n },\n },\n ],\n };\n\n readonly schema = sparklineInputSchema;\n\n render(input: SparklineInput, context: RenderContext): RenderResult {\n // Parse input (applies defaults)\n const parsed = this.schema.parse(input);\n\n // Compute layout\n const layout = computeSparklineLayout(parsed);\n\n // Render based on mode\n const output =\n context.renderMode === \"markdown\"\n ? renderSparklineMarkdown(layout, context)\n : renderSparklineAnsi(layout, context.theme);\n\n // Measure output\n const measured = measureLines(output);\n\n return {\n output,\n actualWidth: measured.maxWidth,\n lineCount: measured.lineCount,\n };\n }\n}\n\n/**\n * Factory function to create a SparklineComponent instance.\n */\nexport function createSparkline(): SparklineComponent {\n return new SparklineComponent();\n}\n\n// Auto-register with global registry\nregistry.register(createSparkline);\n","import { z } from \"zod\";\n\n/**\n * Input schema for the sparkline component.\n */\nexport const sparklineInputSchema = z.object({\n /** Array of numeric data points to visualize (at least 1 required) */\n values: z.array(z.number()).min(1),\n /** Compress output to this width if less than values.length */\n width: z.number().int().positive().optional(),\n /** Explicit minimum value for scaling (defaults to min of values) */\n min: z.number().optional(),\n /** Explicit maximum value for scaling (defaults to max of values) */\n max: z.number().optional(),\n /** Optional label prefix (e.g., \"CPU: \") */\n label: z.string().optional(),\n});\n\n/**\n * Input type for the sparkline component (before defaults applied).\n */\nexport type SparklineInput = z.input<typeof sparklineInputSchema>;\n\n/**\n * Input type for the sparkline component (after defaults applied).\n */\nexport type SparklineInputWithDefaults = z.output<typeof sparklineInputSchema>;\n","import type { SparklineInputWithDefaults } from \"./schema.js\";\n\n/**\n * Height block characters from lowest to highest.\n * Index 0 is the shortest bar, index 7 is the tallest.\n */\nexport const HEIGHT_BLOCKS = [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\"] as const;\n\n/**\n * Middle block index (used when all values are equal).\n */\nconst MIDDLE_BLOCK_INDEX = 4;\n\n/**\n * Layout information for a sparkline.\n */\nexport interface SparklineLayout {\n /** The computed sparkline string (blocks only) */\n blocks: string;\n /** Optional label prefix */\n label: string | undefined;\n /** Total visual width of the output */\n totalWidth: number;\n}\n\n/**\n * Map a value to a height block index (0-7).\n *\n * @param value - The value to map\n * @param min - Minimum value in the range\n * @param max - Maximum value in the range\n * @returns Block index (0-7)\n */\nfunction valueToBlockIndex(value: number, min: number, max: number): number {\n // If min === max, all values are equal, use middle block\n if (max === min) {\n return MIDDLE_BLOCK_INDEX;\n }\n\n // Normalize value to 0-1 range\n const normalized = (value - min) / (max - min);\n\n // Scale to 0-7 range and clamp\n // We use Math.floor and then clamp to ensure we get 0-7\n const index = Math.floor(normalized * 8);\n\n // Clamp to valid range (handles edge cases like value === max)\n return Math.max(0, Math.min(7, index));\n}\n\n/**\n * Bucket values into groups and return averaged values.\n *\n * @param values - Array of values to bucket\n * @param targetWidth - Target number of buckets\n * @returns Array of averaged values\n */\nfunction bucketValues(values: number[], targetWidth: number): number[] {\n if (targetWidth >= values.length) {\n return values;\n }\n\n const result: number[] = [];\n const bucketSize = values.length / targetWidth;\n\n for (let i = 0; i < targetWidth; i++) {\n const startIndex = Math.floor(i * bucketSize);\n const endIndex = Math.floor((i + 1) * bucketSize);\n\n // Calculate average for this bucket\n let sum = 0;\n let count = 0;\n for (let j = startIndex; j < endIndex && j < values.length; j++) {\n const value = values[j];\n if (value !== undefined) {\n sum += value;\n count++;\n }\n }\n\n result.push(count > 0 ? sum / count : 0);\n }\n\n return result;\n}\n\n/**\n * Compute the layout for a sparkline.\n *\n * @param input - Validated sparkline input\n * @returns Layout information\n */\nexport function computeSparklineLayout(\n input: SparklineInputWithDefaults\n): SparklineLayout {\n const { values, width, min: explicitMin, max: explicitMax, label } = input;\n\n // Apply width compression if needed\n const displayValues =\n width !== undefined && width < values.length\n ? bucketValues(values, width)\n : values;\n\n // Determine min/max for scaling\n const actualMin = Math.min(...displayValues);\n const actualMax = Math.max(...displayValues);\n const min = explicitMin ?? actualMin;\n const max = explicitMax ?? actualMax;\n\n // Convert values to block characters\n const blocks = displayValues\n .map((v) => {\n // Clamp value to explicit min/max range if provided\n const clampedValue = Math.max(min, Math.min(max, v));\n const index = valueToBlockIndex(clampedValue, min, max);\n return HEIGHT_BLOCKS[index];\n })\n .join(\"\");\n\n // Calculate total width\n const labelWidth = label?.length ?? 0;\n const blocksWidth = displayValues.length;\n const totalWidth = labelWidth + blocksWidth;\n\n return {\n blocks,\n label,\n totalWidth,\n };\n}\n","import {\n anchorLine,\n DEFAULT_ANCHOR,\n type RenderContext,\n} from \"@tuicomponents/core\";\nimport type { TuiTheme } from \"@tuicomponents/core\";\nimport type { SparklineLayout } from \"./layout.js\";\n\n/**\n * Render a sparkline in ANSI mode.\n *\n * @param layout - Computed sparkline layout\n * @param theme - Optional theme for styling\n * @returns Rendered ANSI string\n */\nexport function renderSparklineAnsi(\n layout: SparklineLayout,\n theme?: TuiTheme\n): string {\n const { blocks, label } = layout;\n\n // Apply theme if available\n const styledBlocks = theme ? theme.semantic.primary(blocks) : blocks;\n const styledLabel = label\n ? theme\n ? theme.semantic.header(label)\n : label\n : \"\";\n\n return styledLabel + styledBlocks;\n}\n\n/**\n * Render a sparkline in markdown mode.\n *\n * Uses context.style.secondary() to apply semantic styling to the blocks,\n * which wraps them in backticks for visual distinction in markdown renderers.\n * The anchor character is prepended to preserve leading whitespace.\n *\n * @param layout - Computed sparkline layout\n * @param context - Render context for styling\n * @returns Rendered markdown string\n */\nexport function renderSparklineMarkdown(\n layout: SparklineLayout,\n context: RenderContext\n): string {\n const { blocks, label } = layout;\n\n // Apply secondary styling to the data blocks\n // In markdown mode, this wraps in backticks for visual distinction\n const styledBlocks = context.style.secondary(blocks);\n const styledLabel = label ?? \"\";\n\n // Add anchor to preserve leading whitespace in markdown\n return anchorLine(styledLabel + styledBlocks, DEFAULT_ANCHOR);\n}\n"],"mappings":"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","names":["import_core"]}
@@ -0,0 +1,129 @@
1
+ import * as zod from 'zod';
2
+ import { z } from 'zod';
3
+ import { BaseTuiComponent, ComponentMetadata, RenderContext, RenderResult, TuiTheme } from '@tuicomponents/core';
4
+
5
+ /**
6
+ * Input schema for the sparkline component.
7
+ */
8
+ declare const sparklineInputSchema: z.ZodObject<{
9
+ /** Array of numeric data points to visualize (at least 1 required) */
10
+ values: z.ZodArray<z.ZodNumber, "many">;
11
+ /** Compress output to this width if less than values.length */
12
+ width: z.ZodOptional<z.ZodNumber>;
13
+ /** Explicit minimum value for scaling (defaults to min of values) */
14
+ min: z.ZodOptional<z.ZodNumber>;
15
+ /** Explicit maximum value for scaling (defaults to max of values) */
16
+ max: z.ZodOptional<z.ZodNumber>;
17
+ /** Optional label prefix (e.g., "CPU: ") */
18
+ label: z.ZodOptional<z.ZodString>;
19
+ }, "strip", z.ZodTypeAny, {
20
+ values: number[];
21
+ width?: number | undefined;
22
+ min?: number | undefined;
23
+ max?: number | undefined;
24
+ label?: string | undefined;
25
+ }, {
26
+ values: number[];
27
+ width?: number | undefined;
28
+ min?: number | undefined;
29
+ max?: number | undefined;
30
+ label?: string | undefined;
31
+ }>;
32
+ /**
33
+ * Input type for the sparkline component (before defaults applied).
34
+ */
35
+ type SparklineInput = z.input<typeof sparklineInputSchema>;
36
+ /**
37
+ * Input type for the sparkline component (after defaults applied).
38
+ */
39
+ type SparklineInputWithDefaults = z.output<typeof sparklineInputSchema>;
40
+
41
+ /**
42
+ * Sparkline component for compact inline data visualization.
43
+ *
44
+ * Renders numeric data as a row of height block characters: ▁▂▃▄▅▆▇█
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const sparkline = createSparkline();
49
+ * const result = sparkline.render(
50
+ * { values: [1, 3, 5, 7, 6, 4, 2], label: "Trend: " },
51
+ * context
52
+ * );
53
+ * // Output: "Trend: ▁▃▅▇▆▄▂"
54
+ * ```
55
+ */
56
+ declare class SparklineComponent extends BaseTuiComponent<SparklineInput, typeof sparklineInputSchema> {
57
+ readonly metadata: ComponentMetadata<SparklineInput>;
58
+ readonly schema: zod.ZodObject<{
59
+ values: zod.ZodArray<zod.ZodNumber, "many">;
60
+ width: zod.ZodOptional<zod.ZodNumber>;
61
+ min: zod.ZodOptional<zod.ZodNumber>;
62
+ max: zod.ZodOptional<zod.ZodNumber>;
63
+ label: zod.ZodOptional<zod.ZodString>;
64
+ }, "strip", zod.ZodTypeAny, {
65
+ values: number[];
66
+ width?: number | undefined;
67
+ min?: number | undefined;
68
+ max?: number | undefined;
69
+ label?: string | undefined;
70
+ }, {
71
+ values: number[];
72
+ width?: number | undefined;
73
+ min?: number | undefined;
74
+ max?: number | undefined;
75
+ label?: string | undefined;
76
+ }>;
77
+ render(input: SparklineInput, context: RenderContext): RenderResult;
78
+ }
79
+ /**
80
+ * Factory function to create a SparklineComponent instance.
81
+ */
82
+ declare function createSparkline(): SparklineComponent;
83
+
84
+ /**
85
+ * Height block characters from lowest to highest.
86
+ * Index 0 is the shortest bar, index 7 is the tallest.
87
+ */
88
+ declare const HEIGHT_BLOCKS: readonly ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
89
+ /**
90
+ * Layout information for a sparkline.
91
+ */
92
+ interface SparklineLayout {
93
+ /** The computed sparkline string (blocks only) */
94
+ blocks: string;
95
+ /** Optional label prefix */
96
+ label: string | undefined;
97
+ /** Total visual width of the output */
98
+ totalWidth: number;
99
+ }
100
+ /**
101
+ * Compute the layout for a sparkline.
102
+ *
103
+ * @param input - Validated sparkline input
104
+ * @returns Layout information
105
+ */
106
+ declare function computeSparklineLayout(input: SparklineInputWithDefaults): SparklineLayout;
107
+
108
+ /**
109
+ * Render a sparkline in ANSI mode.
110
+ *
111
+ * @param layout - Computed sparkline layout
112
+ * @param theme - Optional theme for styling
113
+ * @returns Rendered ANSI string
114
+ */
115
+ declare function renderSparklineAnsi(layout: SparklineLayout, theme?: TuiTheme): string;
116
+ /**
117
+ * Render a sparkline in markdown mode.
118
+ *
119
+ * Uses context.style.secondary() to apply semantic styling to the blocks,
120
+ * which wraps them in backticks for visual distinction in markdown renderers.
121
+ * The anchor character is prepended to preserve leading whitespace.
122
+ *
123
+ * @param layout - Computed sparkline layout
124
+ * @param context - Render context for styling
125
+ * @returns Rendered markdown string
126
+ */
127
+ declare function renderSparklineMarkdown(layout: SparklineLayout, context: RenderContext): string;
128
+
129
+ export { HEIGHT_BLOCKS, SparklineComponent, type SparklineInput, type SparklineInputWithDefaults, type SparklineLayout, computeSparklineLayout, createSparkline, renderSparklineAnsi, renderSparklineMarkdown, sparklineInputSchema };
@@ -0,0 +1,129 @@
1
+ import * as zod from 'zod';
2
+ import { z } from 'zod';
3
+ import { BaseTuiComponent, ComponentMetadata, RenderContext, RenderResult, TuiTheme } from '@tuicomponents/core';
4
+
5
+ /**
6
+ * Input schema for the sparkline component.
7
+ */
8
+ declare const sparklineInputSchema: z.ZodObject<{
9
+ /** Array of numeric data points to visualize (at least 1 required) */
10
+ values: z.ZodArray<z.ZodNumber, "many">;
11
+ /** Compress output to this width if less than values.length */
12
+ width: z.ZodOptional<z.ZodNumber>;
13
+ /** Explicit minimum value for scaling (defaults to min of values) */
14
+ min: z.ZodOptional<z.ZodNumber>;
15
+ /** Explicit maximum value for scaling (defaults to max of values) */
16
+ max: z.ZodOptional<z.ZodNumber>;
17
+ /** Optional label prefix (e.g., "CPU: ") */
18
+ label: z.ZodOptional<z.ZodString>;
19
+ }, "strip", z.ZodTypeAny, {
20
+ values: number[];
21
+ width?: number | undefined;
22
+ min?: number | undefined;
23
+ max?: number | undefined;
24
+ label?: string | undefined;
25
+ }, {
26
+ values: number[];
27
+ width?: number | undefined;
28
+ min?: number | undefined;
29
+ max?: number | undefined;
30
+ label?: string | undefined;
31
+ }>;
32
+ /**
33
+ * Input type for the sparkline component (before defaults applied).
34
+ */
35
+ type SparklineInput = z.input<typeof sparklineInputSchema>;
36
+ /**
37
+ * Input type for the sparkline component (after defaults applied).
38
+ */
39
+ type SparklineInputWithDefaults = z.output<typeof sparklineInputSchema>;
40
+
41
+ /**
42
+ * Sparkline component for compact inline data visualization.
43
+ *
44
+ * Renders numeric data as a row of height block characters: ▁▂▃▄▅▆▇█
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const sparkline = createSparkline();
49
+ * const result = sparkline.render(
50
+ * { values: [1, 3, 5, 7, 6, 4, 2], label: "Trend: " },
51
+ * context
52
+ * );
53
+ * // Output: "Trend: ▁▃▅▇▆▄▂"
54
+ * ```
55
+ */
56
+ declare class SparklineComponent extends BaseTuiComponent<SparklineInput, typeof sparklineInputSchema> {
57
+ readonly metadata: ComponentMetadata<SparklineInput>;
58
+ readonly schema: zod.ZodObject<{
59
+ values: zod.ZodArray<zod.ZodNumber, "many">;
60
+ width: zod.ZodOptional<zod.ZodNumber>;
61
+ min: zod.ZodOptional<zod.ZodNumber>;
62
+ max: zod.ZodOptional<zod.ZodNumber>;
63
+ label: zod.ZodOptional<zod.ZodString>;
64
+ }, "strip", zod.ZodTypeAny, {
65
+ values: number[];
66
+ width?: number | undefined;
67
+ min?: number | undefined;
68
+ max?: number | undefined;
69
+ label?: string | undefined;
70
+ }, {
71
+ values: number[];
72
+ width?: number | undefined;
73
+ min?: number | undefined;
74
+ max?: number | undefined;
75
+ label?: string | undefined;
76
+ }>;
77
+ render(input: SparklineInput, context: RenderContext): RenderResult;
78
+ }
79
+ /**
80
+ * Factory function to create a SparklineComponent instance.
81
+ */
82
+ declare function createSparkline(): SparklineComponent;
83
+
84
+ /**
85
+ * Height block characters from lowest to highest.
86
+ * Index 0 is the shortest bar, index 7 is the tallest.
87
+ */
88
+ declare const HEIGHT_BLOCKS: readonly ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
89
+ /**
90
+ * Layout information for a sparkline.
91
+ */
92
+ interface SparklineLayout {
93
+ /** The computed sparkline string (blocks only) */
94
+ blocks: string;
95
+ /** Optional label prefix */
96
+ label: string | undefined;
97
+ /** Total visual width of the output */
98
+ totalWidth: number;
99
+ }
100
+ /**
101
+ * Compute the layout for a sparkline.
102
+ *
103
+ * @param input - Validated sparkline input
104
+ * @returns Layout information
105
+ */
106
+ declare function computeSparklineLayout(input: SparklineInputWithDefaults): SparklineLayout;
107
+
108
+ /**
109
+ * Render a sparkline in ANSI mode.
110
+ *
111
+ * @param layout - Computed sparkline layout
112
+ * @param theme - Optional theme for styling
113
+ * @returns Rendered ANSI string
114
+ */
115
+ declare function renderSparklineAnsi(layout: SparklineLayout, theme?: TuiTheme): string;
116
+ /**
117
+ * Render a sparkline in markdown mode.
118
+ *
119
+ * Uses context.style.secondary() to apply semantic styling to the blocks,
120
+ * which wraps them in backticks for visual distinction in markdown renderers.
121
+ * The anchor character is prepended to preserve leading whitespace.
122
+ *
123
+ * @param layout - Computed sparkline layout
124
+ * @param context - Render context for styling
125
+ * @returns Rendered markdown string
126
+ */
127
+ declare function renderSparklineMarkdown(layout: SparklineLayout, context: RenderContext): string;
128
+
129
+ export { HEIGHT_BLOCKS, SparklineComponent, type SparklineInput, type SparklineInputWithDefaults, type SparklineLayout, computeSparklineLayout, createSparkline, renderSparklineAnsi, renderSparklineMarkdown, sparklineInputSchema };
package/dist/index.js ADDED
@@ -0,0 +1,159 @@
1
+ // src/sparkline.ts
2
+ import {
3
+ BaseTuiComponent,
4
+ measureLines,
5
+ registry
6
+ } from "@tuicomponents/core";
7
+
8
+ // src/schema.ts
9
+ import { z } from "zod";
10
+ var sparklineInputSchema = z.object({
11
+ /** Array of numeric data points to visualize (at least 1 required) */
12
+ values: z.array(z.number()).min(1),
13
+ /** Compress output to this width if less than values.length */
14
+ width: z.number().int().positive().optional(),
15
+ /** Explicit minimum value for scaling (defaults to min of values) */
16
+ min: z.number().optional(),
17
+ /** Explicit maximum value for scaling (defaults to max of values) */
18
+ max: z.number().optional(),
19
+ /** Optional label prefix (e.g., "CPU: ") */
20
+ label: z.string().optional()
21
+ });
22
+
23
+ // src/layout.ts
24
+ var HEIGHT_BLOCKS = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
25
+ var MIDDLE_BLOCK_INDEX = 4;
26
+ function valueToBlockIndex(value, min, max) {
27
+ if (max === min) {
28
+ return MIDDLE_BLOCK_INDEX;
29
+ }
30
+ const normalized = (value - min) / (max - min);
31
+ const index = Math.floor(normalized * 8);
32
+ return Math.max(0, Math.min(7, index));
33
+ }
34
+ function bucketValues(values, targetWidth) {
35
+ if (targetWidth >= values.length) {
36
+ return values;
37
+ }
38
+ const result = [];
39
+ const bucketSize = values.length / targetWidth;
40
+ for (let i = 0; i < targetWidth; i++) {
41
+ const startIndex = Math.floor(i * bucketSize);
42
+ const endIndex = Math.floor((i + 1) * bucketSize);
43
+ let sum = 0;
44
+ let count = 0;
45
+ for (let j = startIndex; j < endIndex && j < values.length; j++) {
46
+ const value = values[j];
47
+ if (value !== void 0) {
48
+ sum += value;
49
+ count++;
50
+ }
51
+ }
52
+ result.push(count > 0 ? sum / count : 0);
53
+ }
54
+ return result;
55
+ }
56
+ function computeSparklineLayout(input) {
57
+ const { values, width, min: explicitMin, max: explicitMax, label } = input;
58
+ const displayValues = width !== void 0 && width < values.length ? bucketValues(values, width) : values;
59
+ const actualMin = Math.min(...displayValues);
60
+ const actualMax = Math.max(...displayValues);
61
+ const min = explicitMin ?? actualMin;
62
+ const max = explicitMax ?? actualMax;
63
+ const blocks = displayValues.map((v) => {
64
+ const clampedValue = Math.max(min, Math.min(max, v));
65
+ const index = valueToBlockIndex(clampedValue, min, max);
66
+ return HEIGHT_BLOCKS[index];
67
+ }).join("");
68
+ const labelWidth = label?.length ?? 0;
69
+ const blocksWidth = displayValues.length;
70
+ const totalWidth = labelWidth + blocksWidth;
71
+ return {
72
+ blocks,
73
+ label,
74
+ totalWidth
75
+ };
76
+ }
77
+
78
+ // src/renderers.ts
79
+ import {
80
+ anchorLine,
81
+ DEFAULT_ANCHOR
82
+ } from "@tuicomponents/core";
83
+ function renderSparklineAnsi(layout, theme) {
84
+ const { blocks, label } = layout;
85
+ const styledBlocks = theme ? theme.semantic.primary(blocks) : blocks;
86
+ const styledLabel = label ? theme ? theme.semantic.header(label) : label : "";
87
+ return styledLabel + styledBlocks;
88
+ }
89
+ function renderSparklineMarkdown(layout, context) {
90
+ const { blocks, label } = layout;
91
+ const styledBlocks = context.style.secondary(blocks);
92
+ const styledLabel = label ?? "";
93
+ return anchorLine(styledLabel + styledBlocks, DEFAULT_ANCHOR);
94
+ }
95
+
96
+ // src/sparkline.ts
97
+ var SparklineComponent = class extends BaseTuiComponent {
98
+ metadata = {
99
+ name: "sparkline",
100
+ description: "Compact inline sparkline visualization using height block characters (\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588)",
101
+ version: "0.1.0",
102
+ supportedModes: ["ansi", "markdown"],
103
+ examples: [
104
+ {
105
+ name: "basic",
106
+ description: "Simple ascending values",
107
+ input: { values: [1, 2, 3, 4, 5, 6, 7, 8] }
108
+ },
109
+ {
110
+ name: "with-label",
111
+ description: "Sparkline with a label prefix",
112
+ input: { values: [10, 25, 40, 35, 50, 45, 60], label: "Revenue: " }
113
+ },
114
+ {
115
+ name: "compressed",
116
+ description: "Data compressed to fewer columns",
117
+ input: {
118
+ values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
119
+ width: 6
120
+ }
121
+ },
122
+ {
123
+ name: "explicit-range",
124
+ description: "Values scaled to explicit min/max range",
125
+ input: {
126
+ values: [50, 60, 70, 65, 75],
127
+ min: 0,
128
+ max: 100
129
+ }
130
+ }
131
+ ]
132
+ };
133
+ schema = sparklineInputSchema;
134
+ render(input, context) {
135
+ const parsed = this.schema.parse(input);
136
+ const layout = computeSparklineLayout(parsed);
137
+ const output = context.renderMode === "markdown" ? renderSparklineMarkdown(layout, context) : renderSparklineAnsi(layout, context.theme);
138
+ const measured = measureLines(output);
139
+ return {
140
+ output,
141
+ actualWidth: measured.maxWidth,
142
+ lineCount: measured.lineCount
143
+ };
144
+ }
145
+ };
146
+ function createSparkline() {
147
+ return new SparklineComponent();
148
+ }
149
+ registry.register(createSparkline);
150
+ export {
151
+ HEIGHT_BLOCKS,
152
+ SparklineComponent,
153
+ computeSparklineLayout,
154
+ createSparkline,
155
+ renderSparklineAnsi,
156
+ renderSparklineMarkdown,
157
+ sparklineInputSchema
158
+ };
159
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sparkline.ts","../src/schema.ts","../src/layout.ts","../src/renderers.ts"],"sourcesContent":["import {\n BaseTuiComponent,\n measureLines,\n registry,\n type ComponentMetadata,\n type RenderContext,\n type RenderResult,\n} from \"@tuicomponents/core\";\nimport { sparklineInputSchema, type SparklineInput } from \"./schema.js\";\nimport { computeSparklineLayout } from \"./layout.js\";\nimport { renderSparklineAnsi, renderSparklineMarkdown } from \"./renderers.js\";\n\n/**\n * Sparkline component for compact inline data visualization.\n *\n * Renders numeric data as a row of height block characters: ▁▂▃▄▅▆▇█\n *\n * @example\n * ```ts\n * const sparkline = createSparkline();\n * const result = sparkline.render(\n * { values: [1, 3, 5, 7, 6, 4, 2], label: \"Trend: \" },\n * context\n * );\n * // Output: \"Trend: ▁▃▅▇▆▄▂\"\n * ```\n */\nexport class SparklineComponent extends BaseTuiComponent<\n SparklineInput,\n typeof sparklineInputSchema\n> {\n readonly metadata: ComponentMetadata<SparklineInput> = {\n name: \"sparkline\",\n description:\n \"Compact inline sparkline visualization using height block characters (▁▂▃▄▅▆▇█)\",\n version: \"0.1.0\",\n supportedModes: [\"ansi\", \"markdown\"],\n examples: [\n {\n name: \"basic\",\n description: \"Simple ascending values\",\n input: { values: [1, 2, 3, 4, 5, 6, 7, 8] },\n },\n {\n name: \"with-label\",\n description: \"Sparkline with a label prefix\",\n input: { values: [10, 25, 40, 35, 50, 45, 60], label: \"Revenue: \" },\n },\n {\n name: \"compressed\",\n description: \"Data compressed to fewer columns\",\n input: {\n values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],\n width: 6,\n },\n },\n {\n name: \"explicit-range\",\n description: \"Values scaled to explicit min/max range\",\n input: {\n values: [50, 60, 70, 65, 75],\n min: 0,\n max: 100,\n },\n },\n ],\n };\n\n readonly schema = sparklineInputSchema;\n\n render(input: SparklineInput, context: RenderContext): RenderResult {\n // Parse input (applies defaults)\n const parsed = this.schema.parse(input);\n\n // Compute layout\n const layout = computeSparklineLayout(parsed);\n\n // Render based on mode\n const output =\n context.renderMode === \"markdown\"\n ? renderSparklineMarkdown(layout, context)\n : renderSparklineAnsi(layout, context.theme);\n\n // Measure output\n const measured = measureLines(output);\n\n return {\n output,\n actualWidth: measured.maxWidth,\n lineCount: measured.lineCount,\n };\n }\n}\n\n/**\n * Factory function to create a SparklineComponent instance.\n */\nexport function createSparkline(): SparklineComponent {\n return new SparklineComponent();\n}\n\n// Auto-register with global registry\nregistry.register(createSparkline);\n","import { z } from \"zod\";\n\n/**\n * Input schema for the sparkline component.\n */\nexport const sparklineInputSchema = z.object({\n /** Array of numeric data points to visualize (at least 1 required) */\n values: z.array(z.number()).min(1),\n /** Compress output to this width if less than values.length */\n width: z.number().int().positive().optional(),\n /** Explicit minimum value for scaling (defaults to min of values) */\n min: z.number().optional(),\n /** Explicit maximum value for scaling (defaults to max of values) */\n max: z.number().optional(),\n /** Optional label prefix (e.g., \"CPU: \") */\n label: z.string().optional(),\n});\n\n/**\n * Input type for the sparkline component (before defaults applied).\n */\nexport type SparklineInput = z.input<typeof sparklineInputSchema>;\n\n/**\n * Input type for the sparkline component (after defaults applied).\n */\nexport type SparklineInputWithDefaults = z.output<typeof sparklineInputSchema>;\n","import type { SparklineInputWithDefaults } from \"./schema.js\";\n\n/**\n * Height block characters from lowest to highest.\n * Index 0 is the shortest bar, index 7 is the tallest.\n */\nexport const HEIGHT_BLOCKS = [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\"] as const;\n\n/**\n * Middle block index (used when all values are equal).\n */\nconst MIDDLE_BLOCK_INDEX = 4;\n\n/**\n * Layout information for a sparkline.\n */\nexport interface SparklineLayout {\n /** The computed sparkline string (blocks only) */\n blocks: string;\n /** Optional label prefix */\n label: string | undefined;\n /** Total visual width of the output */\n totalWidth: number;\n}\n\n/**\n * Map a value to a height block index (0-7).\n *\n * @param value - The value to map\n * @param min - Minimum value in the range\n * @param max - Maximum value in the range\n * @returns Block index (0-7)\n */\nfunction valueToBlockIndex(value: number, min: number, max: number): number {\n // If min === max, all values are equal, use middle block\n if (max === min) {\n return MIDDLE_BLOCK_INDEX;\n }\n\n // Normalize value to 0-1 range\n const normalized = (value - min) / (max - min);\n\n // Scale to 0-7 range and clamp\n // We use Math.floor and then clamp to ensure we get 0-7\n const index = Math.floor(normalized * 8);\n\n // Clamp to valid range (handles edge cases like value === max)\n return Math.max(0, Math.min(7, index));\n}\n\n/**\n * Bucket values into groups and return averaged values.\n *\n * @param values - Array of values to bucket\n * @param targetWidth - Target number of buckets\n * @returns Array of averaged values\n */\nfunction bucketValues(values: number[], targetWidth: number): number[] {\n if (targetWidth >= values.length) {\n return values;\n }\n\n const result: number[] = [];\n const bucketSize = values.length / targetWidth;\n\n for (let i = 0; i < targetWidth; i++) {\n const startIndex = Math.floor(i * bucketSize);\n const endIndex = Math.floor((i + 1) * bucketSize);\n\n // Calculate average for this bucket\n let sum = 0;\n let count = 0;\n for (let j = startIndex; j < endIndex && j < values.length; j++) {\n const value = values[j];\n if (value !== undefined) {\n sum += value;\n count++;\n }\n }\n\n result.push(count > 0 ? sum / count : 0);\n }\n\n return result;\n}\n\n/**\n * Compute the layout for a sparkline.\n *\n * @param input - Validated sparkline input\n * @returns Layout information\n */\nexport function computeSparklineLayout(\n input: SparklineInputWithDefaults\n): SparklineLayout {\n const { values, width, min: explicitMin, max: explicitMax, label } = input;\n\n // Apply width compression if needed\n const displayValues =\n width !== undefined && width < values.length\n ? bucketValues(values, width)\n : values;\n\n // Determine min/max for scaling\n const actualMin = Math.min(...displayValues);\n const actualMax = Math.max(...displayValues);\n const min = explicitMin ?? actualMin;\n const max = explicitMax ?? actualMax;\n\n // Convert values to block characters\n const blocks = displayValues\n .map((v) => {\n // Clamp value to explicit min/max range if provided\n const clampedValue = Math.max(min, Math.min(max, v));\n const index = valueToBlockIndex(clampedValue, min, max);\n return HEIGHT_BLOCKS[index];\n })\n .join(\"\");\n\n // Calculate total width\n const labelWidth = label?.length ?? 0;\n const blocksWidth = displayValues.length;\n const totalWidth = labelWidth + blocksWidth;\n\n return {\n blocks,\n label,\n totalWidth,\n };\n}\n","import {\n anchorLine,\n DEFAULT_ANCHOR,\n type RenderContext,\n} from \"@tuicomponents/core\";\nimport type { TuiTheme } from \"@tuicomponents/core\";\nimport type { SparklineLayout } from \"./layout.js\";\n\n/**\n * Render a sparkline in ANSI mode.\n *\n * @param layout - Computed sparkline layout\n * @param theme - Optional theme for styling\n * @returns Rendered ANSI string\n */\nexport function renderSparklineAnsi(\n layout: SparklineLayout,\n theme?: TuiTheme\n): string {\n const { blocks, label } = layout;\n\n // Apply theme if available\n const styledBlocks = theme ? theme.semantic.primary(blocks) : blocks;\n const styledLabel = label\n ? theme\n ? theme.semantic.header(label)\n : label\n : \"\";\n\n return styledLabel + styledBlocks;\n}\n\n/**\n * Render a sparkline in markdown mode.\n *\n * Uses context.style.secondary() to apply semantic styling to the blocks,\n * which wraps them in backticks for visual distinction in markdown renderers.\n * The anchor character is prepended to preserve leading whitespace.\n *\n * @param layout - Computed sparkline layout\n * @param context - Render context for styling\n * @returns Rendered markdown string\n */\nexport function renderSparklineMarkdown(\n layout: SparklineLayout,\n context: RenderContext\n): string {\n const { blocks, label } = layout;\n\n // Apply secondary styling to the data blocks\n // In markdown mode, this wraps in backticks for visual distinction\n const styledBlocks = context.style.secondary(blocks);\n const styledLabel = label ?? \"\";\n\n // Add anchor to preserve leading whitespace in markdown\n return anchorLine(styledLabel + styledBlocks, DEFAULT_ANCHOR);\n}\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;;;ACPP,SAAS,SAAS;AAKX,IAAM,uBAAuB,EAAE,OAAO;AAAA;AAAA,EAE3C,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC;AAAA;AAAA,EAEjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS;AAAA;AAAA,EAE5C,KAAK,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAEzB,KAAK,EAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EAEzB,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;;;ACVM,IAAM,gBAAgB,CAAC,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,QAAG;AAKpE,IAAM,qBAAqB;AAsB3B,SAAS,kBAAkB,OAAe,KAAa,KAAqB;AAE1E,MAAI,QAAQ,KAAK;AACf,WAAO;AAAA,EACT;AAGA,QAAM,cAAc,QAAQ,QAAQ,MAAM;AAI1C,QAAM,QAAQ,KAAK,MAAM,aAAa,CAAC;AAGvC,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC;AACvC;AASA,SAAS,aAAa,QAAkB,aAA+B;AACrE,MAAI,eAAe,OAAO,QAAQ;AAChC,WAAO;AAAA,EACT;AAEA,QAAM,SAAmB,CAAC;AAC1B,QAAM,aAAa,OAAO,SAAS;AAEnC,WAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,UAAM,aAAa,KAAK,MAAM,IAAI,UAAU;AAC5C,UAAM,WAAW,KAAK,OAAO,IAAI,KAAK,UAAU;AAGhD,QAAI,MAAM;AACV,QAAI,QAAQ;AACZ,aAAS,IAAI,YAAY,IAAI,YAAY,IAAI,OAAO,QAAQ,KAAK;AAC/D,YAAM,QAAQ,OAAO,CAAC;AACtB,UAAI,UAAU,QAAW;AACvB,eAAO;AACP;AAAA,MACF;AAAA,IACF;AAEA,WAAO,KAAK,QAAQ,IAAI,MAAM,QAAQ,CAAC;AAAA,EACzC;AAEA,SAAO;AACT;AAQO,SAAS,uBACd,OACiB;AACjB,QAAM,EAAE,QAAQ,OAAO,KAAK,aAAa,KAAK,aAAa,MAAM,IAAI;AAGrE,QAAM,gBACJ,UAAU,UAAa,QAAQ,OAAO,SAClC,aAAa,QAAQ,KAAK,IAC1B;AAGN,QAAM,YAAY,KAAK,IAAI,GAAG,aAAa;AAC3C,QAAM,YAAY,KAAK,IAAI,GAAG,aAAa;AAC3C,QAAM,MAAM,eAAe;AAC3B,QAAM,MAAM,eAAe;AAG3B,QAAM,SAAS,cACZ,IAAI,CAAC,MAAM;AAEV,UAAM,eAAe,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,CAAC;AACnD,UAAM,QAAQ,kBAAkB,cAAc,KAAK,GAAG;AACtD,WAAO,cAAc,KAAK;AAAA,EAC5B,CAAC,EACA,KAAK,EAAE;AAGV,QAAM,aAAa,OAAO,UAAU;AACpC,QAAM,cAAc,cAAc;AAClC,QAAM,aAAa,aAAa;AAEhC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACjIA;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAWA,SAAS,oBACd,QACA,OACQ;AACR,QAAM,EAAE,QAAQ,MAAM,IAAI;AAG1B,QAAM,eAAe,QAAQ,MAAM,SAAS,QAAQ,MAAM,IAAI;AAC9D,QAAM,cAAc,QAChB,QACE,MAAM,SAAS,OAAO,KAAK,IAC3B,QACF;AAEJ,SAAO,cAAc;AACvB;AAaO,SAAS,wBACd,QACA,SACQ;AACR,QAAM,EAAE,QAAQ,MAAM,IAAI;AAI1B,QAAM,eAAe,QAAQ,MAAM,UAAU,MAAM;AACnD,QAAM,cAAc,SAAS;AAG7B,SAAO,WAAW,cAAc,cAAc,cAAc;AAC9D;;;AH7BO,IAAM,qBAAN,cAAiC,iBAGtC;AAAA,EACS,WAA8C;AAAA,IACrD,MAAM;AAAA,IACN,aACE;AAAA,IACF,SAAS;AAAA,IACT,gBAAgB,CAAC,QAAQ,UAAU;AAAA,IACnC,UAAU;AAAA,MACR;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,OAAO,EAAE,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,EAAE;AAAA,MAC5C;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,OAAO,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,GAAG,OAAO,YAAY;AAAA,MACpE;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,OAAO;AAAA,UACL,QAAQ,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE;AAAA,UAC9C,OAAO;AAAA,QACT;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aAAa;AAAA,QACb,OAAO;AAAA,UACL,QAAQ,CAAC,IAAI,IAAI,IAAI,IAAI,EAAE;AAAA,UAC3B,KAAK;AAAA,UACL,KAAK;AAAA,QACP;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAES,SAAS;AAAA,EAElB,OAAO,OAAuB,SAAsC;AAElE,UAAM,SAAS,KAAK,OAAO,MAAM,KAAK;AAGtC,UAAM,SAAS,uBAAuB,MAAM;AAG5C,UAAM,SACJ,QAAQ,eAAe,aACnB,wBAAwB,QAAQ,OAAO,IACvC,oBAAoB,QAAQ,QAAQ,KAAK;AAG/C,UAAM,WAAW,aAAa,MAAM;AAEpC,WAAO;AAAA,MACL;AAAA,MACA,aAAa,SAAS;AAAA,MACtB,WAAW,SAAS;AAAA,IACtB;AAAA,EACF;AACF;AAKO,SAAS,kBAAsC;AACpD,SAAO,IAAI,mBAAmB;AAChC;AAGA,SAAS,SAAS,eAAe;","names":[]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@tuicomponents/sparkline",
3
+ "version": "0.1.1",
4
+ "description": "Compact inline sparkline visualization using height blocks",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "keywords": [
25
+ "tui",
26
+ "terminal",
27
+ "sparkline",
28
+ "chart",
29
+ "visualization"
30
+ ],
31
+ "license": "UNLICENSED",
32
+ "dependencies": {
33
+ "zod": "^3.25.56",
34
+ "zod-to-json-schema": "^3.24.5",
35
+ "@tuicomponents/core": "0.1.1"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.0.0"
39
+ },
40
+ "scripts": {
41
+ "build": "tsup",
42
+ "typecheck": "tsc --noEmit",
43
+ "lint": "eslint src",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest",
46
+ "api-report": "api-extractor run",
47
+ "api-report:update": "api-extractor run --local",
48
+ "clean": "rm -rf dist"
49
+ }
50
+ }