@asciirender/asciir 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vinay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,313 @@
1
+ # ASCIIR
2
+
3
+ <p align="center">
4
+ <img src="https://img.shields.io/npm/v/asciir?color=blue&label=npm" alt="npm version" />
5
+ <img src="https://img.shields.io/npm/dm/asciir?color=green" alt="npm downloads" />
6
+ <img src="https://img.shields.io/bundlephobia/minzip/asciir?color=orange" alt="bundle size" />
7
+ <img src="https://img.shields.io/github/license/YOUR_USERNAME/asciir?color=purple" alt="license" />
8
+ <img src="https://img.shields.io/npm/types/asciir?color=blue" alt="TypeScript" />
9
+ </p>
10
+
11
+ <p align="center">
12
+ <strong>A powerful React component for converting images to beautiful ASCII art</strong>
13
+ </p>
14
+
15
+ <p align="center">
16
+ <a href="#installation">Installation</a> •
17
+ <a href="#quick-start">Quick Start</a> •
18
+ <a href="#api-reference">API</a> •
19
+ <a href="#examples">Examples</a>
20
+ </p>
21
+
22
+ ---
23
+
24
+ ## ✨ Features
25
+
26
+ - 🎨 **Multiple Color Modes** - Monochrome, Original colors, or custom palettes
27
+ - 📝 **Custom Character Sets** - Use preset or custom character ramps
28
+ - 🎛️ **Image Filters** - Brightness, contrast, and saturation controls
29
+ - 🔲 **Dithering** - Floyd-Steinberg dithering for better gradients
30
+ - 📦 **Multiple Output Formats** - HTML, SVG, or Canvas
31
+ - 💾 **Export Options** - Download as PNG, SVG, or TXT
32
+ - 🎯 **TypeScript** - Full type definitions included
33
+ - ⚡ **Lightweight** - ~5KB gzipped, zero runtime dependencies
34
+
35
+ ## 📦 Installation
36
+
37
+ ```bash
38
+ npm install asciir
39
+ ```
40
+
41
+ ```bash
42
+ yarn add asciir
43
+ ```
44
+
45
+ ```bash
46
+ pnpm add asciir
47
+ ```
48
+
49
+ ## 🚀 Quick Start
50
+
51
+ ```tsx
52
+ import { ASCIIR } from 'asciir';
53
+
54
+ function App() {
55
+ return (
56
+ <ASCIIR
57
+ src="/path/to/image.jpg"
58
+ config={{
59
+ resolutionWidth: 100,
60
+ colorMode: 'original'
61
+ }}
62
+ />
63
+ );
64
+ }
65
+ ```
66
+
67
+ ## 📖 API Reference
68
+
69
+ ### `<ASCIIR />` Component
70
+
71
+ The main component for rendering ASCII art.
72
+
73
+ ```tsx
74
+ import { ASCIIR, useASCIIRender } from 'asciir';
75
+
76
+ function App() {
77
+ const asciiRef = useASCIIRender();
78
+
79
+ return (
80
+ <div>
81
+ <ASCIIR
82
+ ref={asciiRef}
83
+ src={imageSource}
84
+ config={config}
85
+ output="html"
86
+ onGenerate={(result) => console.log(result)}
87
+ onError={(error) => console.error(error)}
88
+ />
89
+
90
+ <button onClick={() => asciiRef.current?.downloadPNG()}>
91
+ Download PNG
92
+ </button>
93
+ </div>
94
+ );
95
+ }
96
+ ```
97
+
98
+ ### Props
99
+
100
+ | Prop | Type | Default | Description |
101
+ |------|------|---------|-------------|
102
+ | `src` | `string \| File \| Blob \| HTMLImageElement` | Required | Image source |
103
+ | `config` | `Partial<ASCIIRenderConfig>` | `{}` | Configuration overrides |
104
+ | `output` | `'html' \| 'svg' \| 'canvas'` | `'html'` | Output format |
105
+ | `onGenerate` | `(result: ProcessingResult) => void` | - | Callback when ASCII is generated |
106
+ | `onError` | `(error: Error) => void` | - | Error callback |
107
+ | `className` | `string` | - | Container CSS class |
108
+ | `style` | `CSSProperties` | - | Container inline styles |
109
+ | `autoFit` | `boolean` | `false` | Auto-fit to container width |
110
+
111
+ ### Configuration Options
112
+
113
+ ```typescript
114
+ interface ASCIIRenderConfig {
115
+ resolutionWidth: number; // Width in characters (default: 150)
116
+ characterSet: string; // Character ramp (default: " .:-=+*#%@")
117
+ inverted: boolean; // Invert character ramp (default: false)
118
+ contrastStretch: boolean; // Auto contrast (default: true)
119
+ fontColor: string; // Text color for mono mode (default: "#FFFFFF")
120
+ backgroundColor: string; // Background color (default: "#242424")
121
+ lineHeight: number; // Line height multiplier (default: 1.0)
122
+ scaleRatio: number; // Vertical scale ratio (default: 0.55)
123
+ fontSize: number; // Font size in px (default: 12)
124
+ transparentBackground: boolean; // Transparent bg (default: false)
125
+ dithering: boolean; // Enable dithering (default: false)
126
+ colorMode: 'mono' | 'original' | 'palette'; // Color mode (default: 'mono')
127
+ fontFamily: string; // Font family (default: "'Fira Code', monospace")
128
+ exportScale: number; // PNG export scale (default: 2)
129
+ fillTransparency: boolean; // Fill transparent areas (default: true)
130
+ brightness: number; // Brightness 0-3 (default: 1.0)
131
+ contrast: number; // Contrast 0-3 (default: 1.0)
132
+ saturation: number; // Saturation 0-3 (default: 1.0)
133
+ colorPalette: string[]; // Color palette for palette mode
134
+ }
135
+ ```
136
+
137
+ ### Ref Methods
138
+
139
+ ```typescript
140
+ interface ASCIIRenderRef {
141
+ getResult(): ProcessingResult | null;
142
+ downloadPNG(filename?: string): void;
143
+ downloadSVG(filename?: string): void;
144
+ downloadTXT(filename?: string): void;
145
+ copyToClipboard(): Promise<void>;
146
+ getCanvas(scale?: number): HTMLCanvasElement | null;
147
+ getSVGString(): string | null;
148
+ refresh(): void;
149
+ }
150
+ ```
151
+
152
+ ### Presets
153
+
154
+ ```tsx
155
+ import {
156
+ CHAR_SETS, // Character set presets
157
+ FONTS, // Font presets
158
+ PALETTE_PRESETS // Color palette presets
159
+ } from 'asciir';
160
+
161
+ // Character sets
162
+ CHAR_SETS.default // " .:-=+*#%@"
163
+ CHAR_SETS.standard_short // "@%#*+=-:. "
164
+ CHAR_SETS.blocks // "█▓▒░ "
165
+ CHAR_SETS.binary // "01 "
166
+ CHAR_SETS.matrix // Matrix/Katakana style
167
+
168
+ // Fonts
169
+ FONTS.fira // 'Fira Code', monospace
170
+ FONTS.vt323 // 'VT323', monospace (retro)
171
+ FONTS.roboto // 'Roboto Mono', monospace
172
+
173
+ // Color palettes
174
+ PALETTE_PRESETS.cga // CGA colors
175
+ PALETTE_PRESETS.gameboy // GameBoy green
176
+ PALETTE_PRESETS.vaporwave // Vaporwave aesthetic
177
+ ```
178
+
179
+ ### Utility Functions
180
+
181
+ For advanced usage, you can use the core functions directly:
182
+
183
+ ```tsx
184
+ import {
185
+ generateAscii,
186
+ generateCanvasFromAscii,
187
+ generateSVG,
188
+ generateHTML,
189
+ loadImage
190
+ } from 'asciir';
191
+
192
+ // Load and process an image
193
+ const img = await loadImage('/path/to/image.jpg');
194
+ const result = generateAscii(img, config);
195
+
196
+ // Generate outputs
197
+ const svg = generateSVG(result, config);
198
+ const html = generateHTML(result, config);
199
+ const canvas = generateCanvasFromAscii(result, config, 2);
200
+ ```
201
+
202
+ ## 📋 Examples
203
+
204
+ ### Basic Usage
205
+
206
+ ```tsx
207
+ <ASCIIR src="/image.jpg" />
208
+ ```
209
+
210
+ ### Colored ASCII Art
211
+
212
+ ```tsx
213
+ <ASCIIR
214
+ src="/image.jpg"
215
+ config={{
216
+ colorMode: 'original',
217
+ resolutionWidth: 120
218
+ }}
219
+ />
220
+ ```
221
+
222
+ ### Custom Palette
223
+
224
+ ```tsx
225
+ import { ASCIIR, PALETTE_PRESETS } from 'asciir';
226
+
227
+ <ASCIIR
228
+ src="/image.jpg"
229
+ config={{
230
+ colorMode: 'palette',
231
+ colorPalette: PALETTE_PRESETS.vaporwave
232
+ }}
233
+ />
234
+ ```
235
+
236
+ ### With Downloads
237
+
238
+ ```tsx
239
+ import { ASCIIR, useASCIIRender } from 'asciir';
240
+
241
+ function App() {
242
+ const ref = useASCIIRender();
243
+
244
+ return (
245
+ <>
246
+ <ASCIIR ref={ref} src="/image.jpg" />
247
+ <button onClick={() => ref.current?.downloadPNG('my-art.png')}>
248
+ Save as PNG
249
+ </button>
250
+ </>
251
+ );
252
+ }
253
+ ```
254
+
255
+ ### File Upload
256
+
257
+ ```tsx
258
+ import { useState } from 'react';
259
+ import { ASCIIR } from 'asciir';
260
+
261
+ function App() {
262
+ const [file, setFile] = useState<File | null>(null);
263
+
264
+ return (
265
+ <>
266
+ <input
267
+ type="file"
268
+ accept="image/*"
269
+ onChange={(e) => setFile(e.target.files?.[0] || null)}
270
+ />
271
+ {file && <ASCIIR src={file} />}
272
+ </>
273
+ );
274
+ }
275
+ ```
276
+
277
+ ## 🛠️ Development
278
+
279
+ ```bash
280
+ # Clone the repository
281
+ git clone https://github.com/YOUR_USERNAME/asciir.git
282
+ cd asciir
283
+
284
+ # Install dependencies
285
+ npm install
286
+
287
+ # Run the demo app
288
+ npm run dev
289
+
290
+ # Build the library
291
+ npm run build:lib
292
+
293
+ # Type checking
294
+ npm run typecheck
295
+ ```
296
+
297
+ ## 📦 Building for Production
298
+
299
+ ```bash
300
+ # Build library only (for npm publish)
301
+ npm run build:lib
302
+
303
+ # Build demo application
304
+ npm run build:demo
305
+ ```
306
+
307
+ ## 📝 License
308
+
309
+ MIT © Vinay
310
+
311
+ ## 🌟 Show Your Support
312
+
313
+ Give a ⭐️ if this project helped you!
package/dist/asciir.js ADDED
@@ -0,0 +1,387 @@
1
+ import { jsx as I } from "react/jsx-runtime";
2
+ import { forwardRef as j, useState as k, useRef as U, useMemo as E, useEffect as H, useCallback as B, useImperativeHandle as O } from "react";
3
+ const _ = {
4
+ /** Default character set */
5
+ default: " .:-=+*#%@",
6
+ /** Standard short character set */
7
+ standard_short: "@%#*+=-:. ",
8
+ /** Standard long character set with more detail */
9
+ standard_long: "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. ",
10
+ /** Unicode block shades */
11
+ blocks: "█▓▒░ ",
12
+ /** Binary style */
13
+ binary: "01 ",
14
+ /** Minimal contrast */
15
+ minimal: "#. ",
16
+ /** Matrix/Katakana style */
17
+ matrix: "ハミヒーウシナモニサワツオリアホテマケメエカキムユラセネスタヌヘ123457890:・.=*+-<>¦|"
18
+ }, C = {
19
+ fira: "'Fira Code', monospace",
20
+ vt323: "'VT323', monospace",
21
+ roboto: "'Roboto Mono', monospace",
22
+ source: "'Source Code Pro', monospace",
23
+ courier: "'Courier New', monospace"
24
+ }, Z = {
25
+ [C.fira]: 0.6,
26
+ [C.vt323]: 0.5,
27
+ [C.roboto]: 0.6,
28
+ [C.source]: 0.6,
29
+ [C.courier]: 0.6
30
+ }, z = {
31
+ /** Classic terminal green */
32
+ retro_term: ["#000000", "#00ff00", "#008800"],
33
+ /** Game Boy 4-color palette */
34
+ gameboy: ["#0f380f", "#306230", "#8bac0f", "#9bbc0f"],
35
+ /** CGA 16-color palette (subset) */
36
+ cga: ["#000000", "#555555", "#ffffff", "#ff5555", "#ff55ff", "#55ffff"],
37
+ /** Vaporwave aesthetic */
38
+ vaporwave: ["#ff71ce", "#01cdfe", "#05ffa1", "#b967ff", "#fffb96", "#000000"],
39
+ /** Grayscale gradient */
40
+ grayscale: ["#000000", "#333333", "#666666", "#999999", "#cccccc", "#ffffff"],
41
+ /** AMOLED high contrast */
42
+ amoled: ["#000000", "#ffffff"]
43
+ }, N = {
44
+ resolutionWidth: 150,
45
+ characterSet: _.default,
46
+ inverted: !1,
47
+ contrastStretch: !0,
48
+ fontColor: "#FFFFFF",
49
+ backgroundColor: "#242424",
50
+ lineHeight: 1,
51
+ scaleRatio: 0.55,
52
+ autoScaleHeight: !1,
53
+ fontSize: 12,
54
+ transparentBackground: !1,
55
+ dithering: !1,
56
+ colorMode: "mono",
57
+ fontFamily: C.fira,
58
+ exportScale: 2,
59
+ fillTransparency: !0,
60
+ brightness: 1,
61
+ contrast: 1,
62
+ saturation: 1,
63
+ colorPalette: [...z.cga]
64
+ }, V = (e) => new Promise((t, p) => {
65
+ const n = new FileReader();
66
+ n.onload = () => t(n.result), n.onerror = p, n.readAsDataURL(e);
67
+ }), D = (e) => new Promise((t, p) => {
68
+ const n = new Image();
69
+ n.crossOrigin = "anonymous", n.onload = () => t(n), n.onerror = p, n.src = e;
70
+ }), W = (e) => new Promise((t, p) => {
71
+ const n = URL.createObjectURL(e), a = new Image();
72
+ a.onload = () => {
73
+ URL.revokeObjectURL(n), t(a);
74
+ }, a.onerror = (f) => {
75
+ URL.revokeObjectURL(n), p(f);
76
+ }, a.src = n;
77
+ }), P = (e) => {
78
+ const t = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);
79
+ return t ? {
80
+ r: parseInt(t[1], 16),
81
+ g: parseInt(t[2], 16),
82
+ b: parseInt(t[3], 16)
83
+ } : { r: 0, g: 0, b: 0 };
84
+ }, L = (e) => Math.max(0, Math.min(255, e)), q = (e, t) => {
85
+ const { brightness: p, contrast: n, saturation: a } = t;
86
+ if (p === 1 && n === 1 && a === 1) return;
87
+ const f = 128 * (1 - n);
88
+ for (let s = 0; s < e.length; s += 4) {
89
+ let m = e[s], h = e[s + 1], l = e[s + 2];
90
+ m *= p, h *= p, l *= p, m = m * n + f, h = h * n + f, l = l * n + f;
91
+ const u = 0.299 * m + 0.587 * h + 0.114 * l;
92
+ m = u + a * (m - u), h = u + a * (h - u), l = u + a * (l - u), e[s] = L(m), e[s + 1] = L(h), e[s + 2] = L(l);
93
+ }
94
+ }, G = (e, t, p, n) => {
95
+ let a = 1 / 0, f = n[0];
96
+ for (const s of n) {
97
+ const m = (e - s.r) ** 2 + (t - s.g) ** 2 + (p - s.b) ** 2;
98
+ m < a && (a = m, f = s);
99
+ }
100
+ return f;
101
+ }, A = (e, t) => {
102
+ const p = document.createElement("canvas"), n = p.getContext("2d", { willReadFrequently: !0 });
103
+ if (!n)
104
+ throw new Error("Canvas context not supported");
105
+ const a = e.height / e.width, f = t.resolutionWidth, s = Math.floor(a * f * t.scaleRatio);
106
+ p.width = f, p.height = s, n.drawImage(e, 0, 0, f, s);
107
+ const h = n.getImageData(0, 0, f, s).data, l = P(t.backgroundColor), u = t.colorMode === "palette" ? t.colorPalette.map(P) : [];
108
+ for (let o = 0; o < h.length; o += 4) {
109
+ const d = h[o + 3];
110
+ if (t.fillTransparency && d < 255) {
111
+ const r = d / 255;
112
+ h[o] = Math.round(h[o] * r + l.r * (1 - r)), h[o + 1] = Math.round(h[o + 1] * r + l.g * (1 - r)), h[o + 2] = Math.round(h[o + 2] * r + l.b * (1 - r)), h[o + 3] = 255;
113
+ }
114
+ }
115
+ q(h, t);
116
+ const c = new Float32Array(f * s), v = new Uint8ClampedArray(h.length);
117
+ for (let o = 0; o < h.length; o += 4) {
118
+ let d = h[o], r = h[o + 1], i = h[o + 2];
119
+ if (t.colorMode === "palette" && u.length > 0) {
120
+ const g = G(d, r, i, u);
121
+ d = g.r, r = g.g, i = g.b;
122
+ }
123
+ v[o] = d, v[o + 1] = r, v[o + 2] = i, v[o + 3] = 255, c[o / 4] = d * 0.299 + r * 0.587 + i * 0.114;
124
+ }
125
+ if (t.contrastStretch) {
126
+ let o = 255, d = 0;
127
+ for (let i = 0; i < c.length; i++)
128
+ c[i] < o && (o = c[i]), c[i] > d && (d = c[i]);
129
+ const r = d - o;
130
+ if (r > 0)
131
+ for (let i = 0; i < c.length; i++)
132
+ c[i] = (c[i] - o) / r * 255;
133
+ else
134
+ c.fill(0);
135
+ }
136
+ let x = t.characterSet.split("");
137
+ t.inverted && (x = x.reverse());
138
+ const w = x.length - 1;
139
+ if (t.dithering)
140
+ for (let o = 0; o < s; o++)
141
+ for (let d = 0; d < f; d++) {
142
+ const r = o * f + d, i = c[r], g = 255 / Math.max(1, w), $ = Math.round(i / g) * g, M = i - $;
143
+ d + 1 < f && (c[o * f + (d + 1)] += M * 7 / 16), d - 1 >= 0 && o + 1 < s && (c[(o + 1) * f + (d - 1)] += M * 3 / 16), o + 1 < s && (c[(o + 1) * f + d] += M * 5 / 16), d + 1 < f && o + 1 < s && (c[(o + 1) * f + (d + 1)] += M * 1 / 16);
144
+ }
145
+ const R = [], b = w / 255;
146
+ for (let o = 0; o < c.length; o++) {
147
+ let d = c[o];
148
+ d = Math.max(0, Math.min(255, d));
149
+ const r = Math.floor(d * b + 0.5);
150
+ R.push(x[Math.max(0, Math.min(r, w))]);
151
+ }
152
+ const y = [];
153
+ for (let o = 0; o < R.length; o += f)
154
+ y.push(R.slice(o, o + f).join(""));
155
+ return {
156
+ text: y.join(`
157
+ `),
158
+ lines: y,
159
+ width: f,
160
+ height: s,
161
+ colorData: t.colorMode === "original" || t.colorMode === "palette" ? v : void 0
162
+ };
163
+ }, T = (e, t, p) => {
164
+ const n = document.createElement("canvas"), a = n.getContext("2d");
165
+ if (!a) throw new Error("Canvas context not supported");
166
+ const f = p || 1, s = t.fontSize * f, m = t.fontFamily;
167
+ a.font = `${s}px ${m}`, a.textBaseline = "top";
168
+ const l = a.measureText("M").width, c = s * t.lineHeight, v = Math.ceil(l * e.width), x = Math.ceil(c * e.height);
169
+ n.width = v, n.height = x, a.font = `${s}px ${m}`, a.textBaseline = "top", t.transparentBackground ? a.clearRect(0, 0, v, x) : (a.fillStyle = t.backgroundColor, a.fillRect(0, 0, v, x));
170
+ const { lines: w, colorData: R } = e;
171
+ if ((t.colorMode === "original" || t.colorMode === "palette") && R)
172
+ for (let b = 0; b < e.height; b++) {
173
+ const y = w[b], o = b * c;
174
+ for (let d = 0; d < e.width; d++) {
175
+ const r = y[d], i = (b * e.width + d) * 4, g = R[i], S = R[i + 1], $ = R[i + 2];
176
+ a.fillStyle = `rgb(${g},${S},${$})`, a.fillText(r, d * l, o);
177
+ }
178
+ }
179
+ else {
180
+ a.fillStyle = t.fontColor;
181
+ for (let b = 0; b < w.length; b++)
182
+ a.fillText(w[b], 0, b * c);
183
+ }
184
+ return n;
185
+ }, F = (e, t) => {
186
+ const { lines: p, colorData: n } = e, a = t.fontSize, f = a * 0.6, s = a * t.lineHeight, m = f * e.width, h = s * e.height, l = (c) => c.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
187
+ let u = `<svg xmlns="http://www.w3.org/2000/svg" width="${m}" height="${h}" viewBox="0 0 ${m} ${h}" shape-rendering="crispEdges">`;
188
+ if (t.transparentBackground || (u += `<rect width="100%" height="100%" fill="${t.backgroundColor}"/>`), u += `<style>text { font-family: ${t.fontFamily}, monospace; font-size: ${a}px; white-space: pre; text-rendering: geometricPrecision; }</style>`, (t.colorMode === "original" || t.colorMode === "palette") && n)
189
+ for (let c = 0; c < e.height; c++) {
190
+ let x = `<text x="0" y="${c * s + a}">`;
191
+ for (let w = 0; w < e.width; w++) {
192
+ const R = p[c][w], b = (c * e.width + w) * 4, y = n[b], o = n[b + 1], d = n[b + 2], r = "#" + ((1 << 24) + (y << 16) + (o << 8) + d).toString(16).slice(1);
193
+ x += `<tspan fill="${r}">${l(R)}</tspan>`;
194
+ }
195
+ x += "</text>", u += x;
196
+ }
197
+ else {
198
+ const c = t.fontColor;
199
+ p.forEach((v, x) => {
200
+ const w = x * s + a;
201
+ u += `<text x="0" y="${w}" fill="${c}">${l(v)}</text>`;
202
+ });
203
+ }
204
+ return u += "</svg>", u;
205
+ }, X = (e, t) => {
206
+ const { lines: p, colorData: n } = e, a = t.fontSize, f = t.lineHeight;
207
+ if ((t.colorMode === "original" || t.colorMode === "palette") && n) {
208
+ let s = `<pre style="font-family: ${t.fontFamily}; font-size: ${a}px; line-height: ${f}; margin: 0; ${t.transparentBackground ? "" : `background-color: ${t.backgroundColor};`} display: inline-block;">`;
209
+ for (let m = 0; m < e.height; m++) {
210
+ for (let h = 0; h < e.width; h++) {
211
+ const l = p[m][h], u = (m * e.width + h) * 4, c = n[u], v = n[u + 1], x = n[u + 2];
212
+ s += `<span style="color: rgb(${c},${v},${x})">${l === "<" ? "&lt;" : l === ">" ? "&gt;" : l === "&" ? "&amp;" : l}</span>`;
213
+ }
214
+ m < e.height - 1 && (s += `
215
+ `);
216
+ }
217
+ return s += "</pre>", s;
218
+ } else {
219
+ const s = p.join(`
220
+ `).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
221
+ return `<pre style="font-family: ${t.fontFamily}; font-size: ${a}px; line-height: ${f}; margin: 0; color: ${t.fontColor}; ${t.transparentBackground ? "" : `background-color: ${t.backgroundColor};`} display: inline-block;">${s}</pre>`;
222
+ }
223
+ }, J = j(
224
+ ({
225
+ src: e,
226
+ config: t,
227
+ output: p = "html",
228
+ onGenerate: n,
229
+ onError: a,
230
+ className: f,
231
+ style: s,
232
+ autoFit: m = !1
233
+ }, h) => {
234
+ const [l, u] = k(null), [c, v] = k(null), [x, w] = k(!1), R = U(null), b = U(null), y = E(
235
+ () => ({ ...N, ...t }),
236
+ [t]
237
+ );
238
+ H(() => {
239
+ let r = !1;
240
+ return (async () => {
241
+ w(!0);
242
+ try {
243
+ let g;
244
+ if (e instanceof HTMLImageElement)
245
+ g = e;
246
+ else if (typeof e == "object" && e !== null && "name" in e && "type" in e) {
247
+ const S = await V(e);
248
+ g = await D(S);
249
+ } else if (e instanceof Blob)
250
+ g = await W(e);
251
+ else if (typeof e == "string")
252
+ g = await D(e);
253
+ else
254
+ throw new Error("Invalid src type");
255
+ r || v(g);
256
+ } catch (g) {
257
+ if (!r) {
258
+ const S = g instanceof Error ? g : new Error(String(g));
259
+ a?.(S), v(null);
260
+ }
261
+ } finally {
262
+ r || w(!1);
263
+ }
264
+ })(), () => {
265
+ r = !0;
266
+ };
267
+ }, [e, a]), H(() => {
268
+ if (!c) {
269
+ u(null);
270
+ return;
271
+ }
272
+ const r = setTimeout(() => {
273
+ try {
274
+ const i = A(c, y);
275
+ u(i), n?.(i);
276
+ } catch (i) {
277
+ const g = i instanceof Error ? i : new Error(String(i));
278
+ a?.(g);
279
+ }
280
+ }, 200);
281
+ return () => clearTimeout(r);
282
+ }, [c, y, n, a]);
283
+ const o = B(() => {
284
+ if (c)
285
+ try {
286
+ const r = A(c, y);
287
+ u(r), n?.(r);
288
+ } catch (r) {
289
+ const i = r instanceof Error ? r : new Error(String(r));
290
+ a?.(i);
291
+ }
292
+ }, [c, y, n, a]);
293
+ O(
294
+ h,
295
+ () => ({
296
+ getResult: () => l,
297
+ downloadPNG: (r = `ascii-art-${Date.now()}.png`) => {
298
+ if (!l) return;
299
+ const i = T(l, y, y.exportScale), g = document.createElement("a");
300
+ g.download = r, g.href = i.toDataURL("image/png"), g.click();
301
+ },
302
+ downloadSVG: (r = `ascii-art-${Date.now()}.svg`) => {
303
+ if (!l) return;
304
+ const i = F(l, y), g = new Blob([i], { type: "image/svg+xml" }), S = URL.createObjectURL(g), $ = document.createElement("a");
305
+ $.download = r, $.href = S, $.click(), URL.revokeObjectURL(S);
306
+ },
307
+ downloadTXT: (r = `ascii-art-${Date.now()}.txt`) => {
308
+ if (!l) return;
309
+ const i = new Blob([l.text], { type: "text/plain" }), g = URL.createObjectURL(i), S = document.createElement("a");
310
+ S.download = r, S.href = g, S.click(), URL.revokeObjectURL(g);
311
+ },
312
+ copyToClipboard: async () => {
313
+ l && await navigator.clipboard.writeText(l.text);
314
+ },
315
+ getCanvas: (r) => l ? T(l, y, r ?? y.exportScale) : null,
316
+ getSVGString: () => l ? F(l, y) : null,
317
+ refresh: o
318
+ }),
319
+ [l, y, o]
320
+ );
321
+ const d = E(() => {
322
+ if (!l) return null;
323
+ switch (p) {
324
+ case "svg":
325
+ return /* @__PURE__ */ I(
326
+ "div",
327
+ {
328
+ dangerouslySetInnerHTML: { __html: F(l, y) },
329
+ style: { display: "inline-block" }
330
+ }
331
+ );
332
+ case "canvas":
333
+ const r = T(l, y);
334
+ return b.current = r, /* @__PURE__ */ I(
335
+ "img",
336
+ {
337
+ src: r.toDataURL(),
338
+ alt: "ASCII Art",
339
+ style: { display: "block", maxWidth: m ? "100%" : void 0 }
340
+ }
341
+ );
342
+ case "html":
343
+ default:
344
+ return /* @__PURE__ */ I(
345
+ "div",
346
+ {
347
+ dangerouslySetInnerHTML: { __html: X(l, y) },
348
+ style: { display: "inline-block" }
349
+ }
350
+ );
351
+ }
352
+ }, [l, y, p, m]);
353
+ return /* @__PURE__ */ I(
354
+ "div",
355
+ {
356
+ ref: R,
357
+ className: f,
358
+ style: {
359
+ display: "inline-block",
360
+ overflow: m ? "auto" : void 0,
361
+ ...s
362
+ },
363
+ children: x ? /* @__PURE__ */ I("div", { style: { padding: "1rem", opacity: 0.5 }, children: "Loading..." }) : l ? d : /* @__PURE__ */ I("div", { style: { padding: "1rem", opacity: 0.5 }, children: "No image loaded" })
364
+ }
365
+ );
366
+ }
367
+ );
368
+ J.displayName = "ASCIIRender";
369
+ const K = () => U(null);
370
+ export {
371
+ J as ASCIIR,
372
+ J as ASCIIRender,
373
+ _ as CHAR_SETS,
374
+ N as DEFAULT_CONFIG,
375
+ C as FONTS,
376
+ Z as FONT_RATIOS,
377
+ z as PALETTE_PRESETS,
378
+ J as default,
379
+ A as generateAscii,
380
+ T as generateCanvasFromAscii,
381
+ X as generateHTML,
382
+ F as generateSVG,
383
+ D as loadImage,
384
+ W as loadImageFromBlob,
385
+ V as readFileAsDataURL,
386
+ K as useASCIIRender
387
+ };
@@ -0,0 +1,4 @@
1
+ (function(y,C){typeof exports=="object"&&typeof module<"u"?C(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],C):(y=typeof globalThis<"u"?globalThis:y||self,C(y.ASCIIR={},y.jsxRuntime,y.React))})(this,(function(y,C,R){"use strict";const P={default:" .:-=+*#%@",standard_short:"@%#*+=-:. ",standard_long:"$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. ",blocks:"█▓▒░ ",binary:"01 ",minimal:"#. ",matrix:"ハミヒーウシナモニサワツオリアホテマケメエカキムユラセネスタヌヘ123457890:・.=*+-<>¦|"},M={fira:"'Fira Code', monospace",vt323:"'VT323', monospace",roboto:"'Roboto Mono', monospace",source:"'Source Code Pro', monospace",courier:"'Courier New', monospace"},N={[M.fira]:.6,[M.vt323]:.5,[M.roboto]:.6,[M.source]:.6,[M.courier]:.6},D={retro_term:["#000000","#00ff00","#008800"],gameboy:["#0f380f","#306230","#8bac0f","#9bbc0f"],cga:["#000000","#555555","#ffffff","#ff5555","#ff55ff","#55ffff"],vaporwave:["#ff71ce","#01cdfe","#05ffa1","#b967ff","#fffb96","#000000"],grayscale:["#000000","#333333","#666666","#999999","#cccccc","#ffffff"],amoled:["#000000","#ffffff"]},j={resolutionWidth:150,characterSet:P.default,inverted:!1,contrastStretch:!0,fontColor:"#FFFFFF",backgroundColor:"#242424",lineHeight:1,scaleRatio:.55,autoScaleHeight:!1,fontSize:12,transparentBackground:!1,dithering:!1,colorMode:"mono",fontFamily:M.fira,exportScale:2,fillTransparency:!0,brightness:1,contrast:1,saturation:1,colorPalette:[...D.cga]},O=t=>new Promise((e,u)=>{const o=new FileReader;o.onload=()=>e(o.result),o.onerror=u,o.readAsDataURL(t)}),A=t=>new Promise((e,u)=>{const o=new Image;o.crossOrigin="anonymous",o.onload=()=>e(o),o.onerror=u,o.src=t}),_=t=>new Promise((e,u)=>{const o=URL.createObjectURL(t),r=new Image;r.onload=()=>{URL.revokeObjectURL(o),e(r)},r.onerror=f=>{URL.revokeObjectURL(o),u(f)},r.src=o}),B=t=>{const e=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return e?{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)}:{r:0,g:0,b:0}},U=t=>Math.max(0,Math.min(255,t)),V=(t,e)=>{const{brightness:u,contrast:o,saturation:r}=e;if(u===1&&o===1&&r===1)return;const f=128*(1-o);for(let s=0;s<t.length;s+=4){let m=t[s],h=t[s+1],l=t[s+2];m*=u,h*=u,l*=u,m=m*o+f,h=h*o+f,l=l*o+f;const p=.299*m+.587*h+.114*l;m=p+r*(m-p),h=p+r*(h-p),l=p+r*(l-p),t[s]=U(m),t[s+1]=U(h),t[s+2]=U(l)}},q=(t,e,u,o)=>{let r=1/0,f=o[0];for(const s of o){const m=(t-s.r)**2+(e-s.g)**2+(u-s.b)**2;m<r&&(r=m,f=s)}return f},H=(t,e)=>{const u=document.createElement("canvas"),o=u.getContext("2d",{willReadFrequently:!0});if(!o)throw new Error("Canvas context not supported");const r=t.height/t.width,f=e.resolutionWidth,s=Math.floor(r*f*e.scaleRatio);u.width=f,u.height=s,o.drawImage(t,0,0,f,s);const h=o.getImageData(0,0,f,s).data,l=B(e.backgroundColor),p=e.colorMode==="palette"?e.colorPalette.map(B):[];for(let n=0;n<h.length;n+=4){const d=h[n+3];if(e.fillTransparency&&d<255){const a=d/255;h[n]=Math.round(h[n]*a+l.r*(1-a)),h[n+1]=Math.round(h[n+1]*a+l.g*(1-a)),h[n+2]=Math.round(h[n+2]*a+l.b*(1-a)),h[n+3]=255}}V(h,e);const i=new Float32Array(f*s),v=new Uint8ClampedArray(h.length);for(let n=0;n<h.length;n+=4){let d=h[n],a=h[n+1],c=h[n+2];if(e.colorMode==="palette"&&p.length>0){const g=q(d,a,c,p);d=g.r,a=g.g,c=g.b}v[n]=d,v[n+1]=a,v[n+2]=c,v[n+3]=255,i[n/4]=d*.299+a*.587+c*.114}if(e.contrastStretch){let n=255,d=0;for(let c=0;c<i.length;c++)i[c]<n&&(n=i[c]),i[c]>d&&(d=i[c]);const a=d-n;if(a>0)for(let c=0;c<i.length;c++)i[c]=(i[c]-n)/a*255;else i.fill(0)}let w=e.characterSet.split("");e.inverted&&(w=w.reverse());const S=w.length-1;if(e.dithering)for(let n=0;n<s;n++)for(let d=0;d<f;d++){const a=n*f+d,c=i[a],g=255/Math.max(1,S),T=Math.round(c/g)*g,E=c-T;d+1<f&&(i[n*f+(d+1)]+=E*7/16),d-1>=0&&n+1<s&&(i[(n+1)*f+(d-1)]+=E*3/16),n+1<s&&(i[(n+1)*f+d]+=E*5/16),d+1<f&&n+1<s&&(i[(n+1)*f+(d+1)]+=E*1/16)}const $=[],x=S/255;for(let n=0;n<i.length;n++){let d=i[n];d=Math.max(0,Math.min(255,d));const a=Math.floor(d*x+.5);$.push(w[Math.max(0,Math.min(a,S))])}const b=[];for(let n=0;n<$.length;n+=f)b.push($.slice(n,n+f).join(""));return{text:b.join(`
2
+ `),lines:b,width:f,height:s,colorData:e.colorMode==="original"||e.colorMode==="palette"?v:void 0}},L=(t,e,u)=>{const o=document.createElement("canvas"),r=o.getContext("2d");if(!r)throw new Error("Canvas context not supported");const f=u||1,s=e.fontSize*f,m=e.fontFamily;r.font=`${s}px ${m}`,r.textBaseline="top";const l=r.measureText("M").width,i=s*e.lineHeight,v=Math.ceil(l*t.width),w=Math.ceil(i*t.height);o.width=v,o.height=w,r.font=`${s}px ${m}`,r.textBaseline="top",e.transparentBackground?r.clearRect(0,0,v,w):(r.fillStyle=e.backgroundColor,r.fillRect(0,0,v,w));const{lines:S,colorData:$}=t;if((e.colorMode==="original"||e.colorMode==="palette")&&$)for(let x=0;x<t.height;x++){const b=S[x],n=x*i;for(let d=0;d<t.width;d++){const a=b[d],c=(x*t.width+d)*4,g=$[c],I=$[c+1],T=$[c+2];r.fillStyle=`rgb(${g},${I},${T})`,r.fillText(a,d*l,n)}}else{r.fillStyle=e.fontColor;for(let x=0;x<S.length;x++)r.fillText(S[x],0,x*i)}return o},k=(t,e)=>{const{lines:u,colorData:o}=t,r=e.fontSize,f=r*.6,s=r*e.lineHeight,m=f*t.width,h=s*t.height,l=i=>i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");let p=`<svg xmlns="http://www.w3.org/2000/svg" width="${m}" height="${h}" viewBox="0 0 ${m} ${h}" shape-rendering="crispEdges">`;if(e.transparentBackground||(p+=`<rect width="100%" height="100%" fill="${e.backgroundColor}"/>`),p+=`<style>text { font-family: ${e.fontFamily}, monospace; font-size: ${r}px; white-space: pre; text-rendering: geometricPrecision; }</style>`,(e.colorMode==="original"||e.colorMode==="palette")&&o)for(let i=0;i<t.height;i++){let w=`<text x="0" y="${i*s+r}">`;for(let S=0;S<t.width;S++){const $=u[i][S],x=(i*t.width+S)*4,b=o[x],n=o[x+1],d=o[x+2],a="#"+((1<<24)+(b<<16)+(n<<8)+d).toString(16).slice(1);w+=`<tspan fill="${a}">${l($)}</tspan>`}w+="</text>",p+=w}else{const i=e.fontColor;u.forEach((v,w)=>{const S=w*s+r;p+=`<text x="0" y="${S}" fill="${i}">${l(v)}</text>`})}return p+="</svg>",p},z=(t,e)=>{const{lines:u,colorData:o}=t,r=e.fontSize,f=e.lineHeight;if((e.colorMode==="original"||e.colorMode==="palette")&&o){let s=`<pre style="font-family: ${e.fontFamily}; font-size: ${r}px; line-height: ${f}; margin: 0; ${e.transparentBackground?"":`background-color: ${e.backgroundColor};`} display: inline-block;">`;for(let m=0;m<t.height;m++){for(let h=0;h<t.width;h++){const l=u[m][h],p=(m*t.width+h)*4,i=o[p],v=o[p+1],w=o[p+2];s+=`<span style="color: rgb(${i},${v},${w})">${l==="<"?"&lt;":l===">"?"&gt;":l==="&"?"&amp;":l}</span>`}m<t.height-1&&(s+=`
3
+ `)}return s+="</pre>",s}else{const s=u.join(`
4
+ `).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");return`<pre style="font-family: ${e.fontFamily}; font-size: ${r}px; line-height: ${f}; margin: 0; color: ${e.fontColor}; ${e.transparentBackground?"":`background-color: ${e.backgroundColor};`} display: inline-block;">${s}</pre>`}},F=R.forwardRef(({src:t,config:e,output:u="html",onGenerate:o,onError:r,className:f,style:s,autoFit:m=!1},h)=>{const[l,p]=R.useState(null),[i,v]=R.useState(null),[w,S]=R.useState(!1),$=R.useRef(null),x=R.useRef(null),b=R.useMemo(()=>({...j,...e}),[e]);R.useEffect(()=>{let a=!1;return(async()=>{S(!0);try{let g;if(t instanceof HTMLImageElement)g=t;else if(typeof t=="object"&&t!==null&&"name"in t&&"type"in t){const I=await O(t);g=await A(I)}else if(t instanceof Blob)g=await _(t);else if(typeof t=="string")g=await A(t);else throw new Error("Invalid src type");a||v(g)}catch(g){if(!a){const I=g instanceof Error?g:new Error(String(g));r?.(I),v(null)}}finally{a||S(!1)}})(),()=>{a=!0}},[t,r]),R.useEffect(()=>{if(!i){p(null);return}const a=setTimeout(()=>{try{const c=H(i,b);p(c),o?.(c)}catch(c){const g=c instanceof Error?c:new Error(String(c));r?.(g)}},200);return()=>clearTimeout(a)},[i,b,o,r]);const n=R.useCallback(()=>{if(i)try{const a=H(i,b);p(a),o?.(a)}catch(a){const c=a instanceof Error?a:new Error(String(a));r?.(c)}},[i,b,o,r]);R.useImperativeHandle(h,()=>({getResult:()=>l,downloadPNG:(a=`ascii-art-${Date.now()}.png`)=>{if(!l)return;const c=L(l,b,b.exportScale),g=document.createElement("a");g.download=a,g.href=c.toDataURL("image/png"),g.click()},downloadSVG:(a=`ascii-art-${Date.now()}.svg`)=>{if(!l)return;const c=k(l,b),g=new Blob([c],{type:"image/svg+xml"}),I=URL.createObjectURL(g),T=document.createElement("a");T.download=a,T.href=I,T.click(),URL.revokeObjectURL(I)},downloadTXT:(a=`ascii-art-${Date.now()}.txt`)=>{if(!l)return;const c=new Blob([l.text],{type:"text/plain"}),g=URL.createObjectURL(c),I=document.createElement("a");I.download=a,I.href=g,I.click(),URL.revokeObjectURL(g)},copyToClipboard:async()=>{l&&await navigator.clipboard.writeText(l.text)},getCanvas:a=>l?L(l,b,a??b.exportScale):null,getSVGString:()=>l?k(l,b):null,refresh:n}),[l,b,n]);const d=R.useMemo(()=>{if(!l)return null;switch(u){case"svg":return C.jsx("div",{dangerouslySetInnerHTML:{__html:k(l,b)},style:{display:"inline-block"}});case"canvas":const a=L(l,b);return x.current=a,C.jsx("img",{src:a.toDataURL(),alt:"ASCII Art",style:{display:"block",maxWidth:m?"100%":void 0}});case"html":default:return C.jsx("div",{dangerouslySetInnerHTML:{__html:z(l,b)},style:{display:"inline-block"}})}},[l,b,u,m]);return C.jsx("div",{ref:$,className:f,style:{display:"inline-block",overflow:m?"auto":void 0,...s},children:w?C.jsx("div",{style:{padding:"1rem",opacity:.5},children:"Loading..."}):l?d:C.jsx("div",{style:{padding:"1rem",opacity:.5},children:"No image loaded"})})});F.displayName="ASCIIRender";const W=()=>R.useRef(null);y.ASCIIR=F,y.ASCIIRender=F,y.CHAR_SETS=P,y.DEFAULT_CONFIG=j,y.FONTS=M,y.FONT_RATIOS=N,y.PALETTE_PRESETS=D,y.default=F,y.generateAscii=H,y.generateCanvasFromAscii=L,y.generateHTML=z,y.generateSVG=k,y.loadImage=A,y.loadImageFromBlob=_,y.readFileAsDataURL=O,y.useASCIIRender=W,Object.defineProperties(y,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
@@ -0,0 +1,240 @@
1
+ import { default as default_2 } from 'react';
2
+ import { RefObject } from 'react';
3
+
4
+ /**
5
+ * ASCIIRender - A React component for converting images to ASCII art
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { ASCIIRender } from 'asciirender';
10
+ *
11
+ * function App() {
12
+ * return (
13
+ * <ASCIIRender
14
+ * src="/path/to/image.jpg"
15
+ * config={{ resolutionWidth: 100, colorMode: 'original' }}
16
+ * />
17
+ * );
18
+ * }
19
+ * ```
20
+ */
21
+ declare const ASCIIRender: default_2.ForwardRefExoticComponent<ASCIIRenderProps & default_2.RefAttributes<ASCIIRenderRef>>;
22
+ export { ASCIIRender as ASCIIR }
23
+ export { ASCIIRender }
24
+ export default ASCIIRender;
25
+
26
+ /**
27
+ * Configuration options for ASCII art generation
28
+ */
29
+ export declare interface ASCIIRenderConfig {
30
+ /** Width of output in characters (columns). Default: 150 */
31
+ resolutionWidth: number;
32
+ /** Character set for ASCII mapping (dark to light). Default: " .:-=+*#%@" */
33
+ characterSet: string;
34
+ /** Whether to invert the character ramp. Default: false */
35
+ inverted: boolean;
36
+ /** Apply auto contrast stretching. Default: true */
37
+ contrastStretch: boolean;
38
+ /** Font/text color for mono mode. Default: "#FFFFFF" */
39
+ fontColor: string;
40
+ /** Background color. Default: "#242424" */
41
+ backgroundColor: string;
42
+ /** Line height multiplier. Default: 1.0 */
43
+ lineHeight: number;
44
+ /** Vertical scale ratio (to correct aspect ratio). Default: 0.55 */
45
+ scaleRatio: number;
46
+ /** Auto-scale ratio based on font height. Default: false */
47
+ autoScaleHeight: boolean;
48
+ /** Font size in pixels. Default: 12 */
49
+ fontSize: number;
50
+ /** Whether background should be transparent. Default: false */
51
+ transparentBackground: boolean;
52
+ /** Apply Floyd-Steinberg dithering. Default: false */
53
+ dithering: boolean;
54
+ /** Color mode: 'mono', 'original', or 'palette'. Default: 'mono' */
55
+ colorMode: 'mono' | 'original' | 'palette';
56
+ /** Font family for rendering. Default: "'Fira Code', monospace" */
57
+ fontFamily: string;
58
+ /** Scale factor for PNG export. Default: 2 */
59
+ exportScale: number;
60
+ /** Fill transparent areas with background color. Default: true */
61
+ fillTransparency: boolean;
62
+ /** Brightness adjustment (0-3). Default: 1.0 */
63
+ brightness: number;
64
+ /** Contrast adjustment (0-3). Default: 1.0 */
65
+ contrast: number;
66
+ /** Saturation adjustment (0-3). Default: 1.0 */
67
+ saturation: number;
68
+ /** Color palette for palette mode. Default: CGA colors */
69
+ colorPalette: string[];
70
+ }
71
+
72
+ /**
73
+ * Props for the ASCIIRender component
74
+ */
75
+ export declare interface ASCIIRenderProps {
76
+ /** Image source - can be URL, File, Blob, or HTMLImageElement */
77
+ src: string | File | Blob | HTMLImageElement;
78
+ /** Partial config overrides (all fields optional) */
79
+ config?: Partial<ASCIIRenderConfig>;
80
+ /** Output format: 'html' (default), 'svg', or 'canvas' */
81
+ output?: 'html' | 'svg' | 'canvas';
82
+ /** Callback when ASCII art is generated */
83
+ onGenerate?: (result: ProcessingResult) => void;
84
+ /** Callback on error */
85
+ onError?: (error: Error) => void;
86
+ /** Additional className for the container */
87
+ className?: string;
88
+ /** Additional style for the container */
89
+ style?: React.CSSProperties;
90
+ /** Whether to auto-fit to container width */
91
+ autoFit?: boolean;
92
+ }
93
+
94
+ /**
95
+ * Ref handle for imperative access to ASCIIRender
96
+ */
97
+ export declare interface ASCIIRenderRef {
98
+ /** Get the current processing result */
99
+ getResult: () => ProcessingResult | null;
100
+ /** Download as PNG */
101
+ downloadPNG: (filename?: string) => void;
102
+ /** Download as SVG */
103
+ downloadSVG: (filename?: string) => void;
104
+ /** Download as TXT */
105
+ downloadTXT: (filename?: string) => void;
106
+ /** Copy text to clipboard */
107
+ copyToClipboard: () => Promise<void>;
108
+ /** Get the generated canvas element */
109
+ getCanvas: (scale?: number) => HTMLCanvasElement | null;
110
+ /** Get the SVG string */
111
+ getSVGString: () => string | null;
112
+ /** Re-process the image with current config */
113
+ refresh: () => void;
114
+ }
115
+
116
+ /**
117
+ * Predefined character sets for ASCII art generation
118
+ */
119
+ export declare const CHAR_SETS: {
120
+ /** Default character set */
121
+ readonly default: " .:-=+*#%@";
122
+ /** Standard short character set */
123
+ readonly standard_short: "@%#*+=-:. ";
124
+ /** Standard long character set with more detail */
125
+ readonly standard_long: "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. ";
126
+ /** Unicode block shades */
127
+ readonly blocks: "█▓▒░ ";
128
+ /** Binary style */
129
+ readonly binary: "01 ";
130
+ /** Minimal contrast */
131
+ readonly minimal: "#. ";
132
+ /** Matrix/Katakana style */
133
+ readonly matrix: "ハミヒーウシナモニサワツオリアホテマケメエカキムユラセネスタヌヘ123457890:・.=*+-<>¦|";
134
+ };
135
+
136
+ /**
137
+ * Default configuration for ASCII art generation
138
+ */
139
+ export declare const DEFAULT_CONFIG: ASCIIRenderConfig;
140
+
141
+ /**
142
+ * Approximate width/height ratio for each font
143
+ */
144
+ export declare const FONT_RATIOS: Record<string, number>;
145
+
146
+ /**
147
+ * Predefined monospace font families
148
+ */
149
+ export declare const FONTS: {
150
+ readonly fira: "'Fira Code', monospace";
151
+ readonly vt323: "'VT323', monospace";
152
+ readonly roboto: "'Roboto Mono', monospace";
153
+ readonly source: "'Source Code Pro', monospace";
154
+ readonly courier: "'Courier New', monospace";
155
+ };
156
+
157
+ /**
158
+ * Generate ASCII art from an HTMLImageElement
159
+ */
160
+ export declare const generateAscii: (image: HTMLImageElement, config: ASCIIRenderConfig) => ProcessingResult;
161
+
162
+ /**
163
+ * Generate a canvas from ASCII result
164
+ */
165
+ export declare const generateCanvasFromAscii: (result: ProcessingResult, config: ASCIIRenderConfig, customScale?: number) => HTMLCanvasElement;
166
+
167
+ /**
168
+ * Generate HTML string from ASCII result
169
+ */
170
+ export declare const generateHTML: (result: ProcessingResult, config: ASCIIRenderConfig) => string;
171
+
172
+ /**
173
+ * Generate SVG string from ASCII result
174
+ */
175
+ export declare const generateSVG: (result: ProcessingResult, config: ASCIIRenderConfig) => string;
176
+
177
+ /**
178
+ * Load an image from a URL
179
+ */
180
+ export declare const loadImage: (src: string) => Promise<HTMLImageElement>;
181
+
182
+ /**
183
+ * Load an image from a Blob
184
+ */
185
+ export declare const loadImageFromBlob: (blob: Blob) => Promise<HTMLImageElement>;
186
+
187
+ /**
188
+ * Predefined color palettes for palette mode
189
+ */
190
+ export declare const PALETTE_PRESETS: {
191
+ /** Classic terminal green */
192
+ readonly retro_term: readonly ["#000000", "#00ff00", "#008800"];
193
+ /** Game Boy 4-color palette */
194
+ readonly gameboy: readonly ["#0f380f", "#306230", "#8bac0f", "#9bbc0f"];
195
+ /** CGA 16-color palette (subset) */
196
+ readonly cga: readonly ["#000000", "#555555", "#ffffff", "#ff5555", "#ff55ff", "#55ffff"];
197
+ /** Vaporwave aesthetic */
198
+ readonly vaporwave: readonly ["#ff71ce", "#01cdfe", "#05ffa1", "#b967ff", "#fffb96", "#000000"];
199
+ /** Grayscale gradient */
200
+ readonly grayscale: readonly ["#000000", "#333333", "#666666", "#999999", "#cccccc", "#ffffff"];
201
+ /** AMOLED high contrast */
202
+ readonly amoled: readonly ["#000000", "#ffffff"];
203
+ };
204
+
205
+ /**
206
+ * Result of ASCII art processing
207
+ */
208
+ export declare interface ProcessingResult {
209
+ /** Full ASCII text with newlines */
210
+ text: string;
211
+ /** Array of lines */
212
+ lines: string[];
213
+ /** Width in characters */
214
+ width: number;
215
+ /** Height in lines */
216
+ height: number;
217
+ /** Color data for colored modes (RGBA array) */
218
+ colorData?: Uint8ClampedArray;
219
+ }
220
+
221
+ /**
222
+ * Read a File as a data URL
223
+ */
224
+ export declare const readFileAsDataURL: (file: File) => Promise<string>;
225
+
226
+ /**
227
+ * Hook to get a typed ref for ASCIIR component
228
+ *
229
+ * @example
230
+ * ```tsx
231
+ * const asciiRef = useASCIIRender();
232
+ *
233
+ * return (
234
+ * <ASCIIR ref={asciiRef} src={imageSrc} />
235
+ * );
236
+ * ```
237
+ */
238
+ export declare const useASCIIRender: () => RefObject<ASCIIRenderRef>;
239
+
240
+ export { }
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@asciirender/asciir",
3
+ "version": "1.0.2",
4
+ "description": "A powerful React component for converting images to beautiful ASCII art with customizable settings",
5
+ "author": "dvinay",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/ACIIRenderer/ASCIIR.git"
10
+ },
11
+ "homepage": "https://github.com/ACIIRenderer/ASCIIR#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/ACIIRenderer/ASCIIR/issues"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public",
17
+ "registry": "https://registry.npmjs.org/"
18
+ },
19
+ "keywords": [
20
+ "ascii",
21
+ "ascii-art",
22
+ "image-processing",
23
+ "react",
24
+ "react-component",
25
+ "image-to-ascii",
26
+ "text-art",
27
+ "canvas",
28
+ "svg",
29
+ "typescript",
30
+ "image-converter"
31
+ ],
32
+ "type": "module",
33
+ "main": "./dist/asciir.umd.cjs",
34
+ "module": "./dist/asciir.js",
35
+ "types": "./dist/index.d.ts",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/asciir.js",
40
+ "require": "./dist/asciir.umd.cjs"
41
+ }
42
+ },
43
+ "files": [
44
+ "dist"
45
+ ],
46
+ "sideEffects": false,
47
+ "scripts": {
48
+ "dev": "vite --config vite.demo.config.ts",
49
+ "build": "npm run build:lib",
50
+ "build:lib": "tsc -p tsconfig.lib.json && vite build --config vite.config.ts",
51
+ "build:demo": "vite build --config vite.demo.config.ts",
52
+ "preview": "vite preview --config vite.demo.config.ts",
53
+ "typecheck": "tsc --noEmit",
54
+ "prepublishOnly": "npm run build:lib",
55
+ "size": "npm run build:lib && npx size-limit"
56
+ },
57
+ "peerDependencies": {
58
+ "react": ">=17.0.0",
59
+ "react-dom": ">=17.0.0"
60
+ },
61
+ "devDependencies": {
62
+ "@types/node": "^22.14.0",
63
+ "@types/react": "^18.2.0",
64
+ "@types/react-dom": "^18.2.0",
65
+ "@vitejs/plugin-react": "^5.0.0",
66
+ "lucide-react": "^0.562.0",
67
+ "react": "^19.2.3",
68
+ "react-dom": "^19.2.3",
69
+ "react-router-dom": "^7.12.0",
70
+ "typescript": "~5.8.2",
71
+ "vite": "^6.2.0",
72
+ "vite-plugin-dts": "^4.5.0"
73
+ },
74
+ "engines": {
75
+ "node": ">=18.0.0"
76
+ }
77
+ }