svelte-asciiart 0.0.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/README.md +68 -0
- package/dist/AsciiArt.svelte +127 -0
- package/dist/AsciiArt.svelte.d.ts +17 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# svelte-asciiart
|
|
2
|
+
|
|
3
|
+
[](https://alexey.work?ref=ascii-md)
|
|
4
|
+
|
|
5
|
+
A Svelte 5 component for rendering ASCII art as scalable SVG with optional grid overlay and frame.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install svelte-asciiart
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```svelte
|
|
16
|
+
<script>
|
|
17
|
+
import { AsciiArt } from 'svelte-asciiart';
|
|
18
|
+
|
|
19
|
+
const text = `+----------+
|
|
20
|
+
| Hello |
|
|
21
|
+
| World! |
|
|
22
|
+
+----------+`;
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<AsciiArt {text} />
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Props
|
|
29
|
+
|
|
30
|
+
| Prop | Type | Default | Description |
|
|
31
|
+
| ------------------- | ---------------------------------------------------------------- | -------------------- | ------------------------------------------------ |
|
|
32
|
+
| `text` | `string` | required | The ASCII art text to render |
|
|
33
|
+
| `rows` | `number` | auto | Number of rows (derived from text if not set) |
|
|
34
|
+
| `cols` | `number` | auto | Number of columns (derived from text if not set) |
|
|
35
|
+
| `grid` | `boolean` | `false` | Enable grid mode with cell-based layout |
|
|
36
|
+
| `cellAspect` | `number` | `0.6` | Width/height ratio of grid cells |
|
|
37
|
+
| `measureCellAspect` | `boolean` | `false` | Measure actual font aspect ratio |
|
|
38
|
+
| `gridClass` | `string` | `''` | CSS class for grid lines |
|
|
39
|
+
| `gridStyle` | `string` | `''` | Inline styles for grid lines |
|
|
40
|
+
| `fontFamily` | `string` | `'Courier New', ...` | Font family for text |
|
|
41
|
+
| `frame` | `boolean` | `false` | Show frame around content (grid mode only) |
|
|
42
|
+
| `frameMargin` | `number \| [number, number] \| [number, number, number, number]` | `0` | Margin around frame in grid cells |
|
|
43
|
+
| `frameClass` | `string` | `''` | CSS class for frame |
|
|
44
|
+
| `frameStyle` | `string` | `''` | Inline styles for frame |
|
|
45
|
+
|
|
46
|
+
## Grid Mode
|
|
47
|
+
|
|
48
|
+
Grid mode renders text character-by-character in a precise grid, useful for ASCII art that needs exact alignment:
|
|
49
|
+
|
|
50
|
+
```svelte
|
|
51
|
+
<AsciiArt {text} grid frame frameMargin={[1, 2]} gridClass="ascii-grid" frameClass="ascii-frame" />
|
|
52
|
+
|
|
53
|
+
<style>
|
|
54
|
+
.ascii-grid {
|
|
55
|
+
stroke: #90ee90;
|
|
56
|
+
stroke-width: 0.03;
|
|
57
|
+
opacity: 0.5;
|
|
58
|
+
}
|
|
59
|
+
.ascii-frame {
|
|
60
|
+
stroke: #ffb366;
|
|
61
|
+
stroke-width: 0.05;
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
MIT
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SVGAttributes } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
type Margin = number | [number, number] | [number, number, number, number];
|
|
5
|
+
|
|
6
|
+
// Props
|
|
7
|
+
interface Props extends SVGAttributes<SVGSVGElement> {
|
|
8
|
+
text: string;
|
|
9
|
+
rows?: number;
|
|
10
|
+
cols?: number;
|
|
11
|
+
grid?: boolean;
|
|
12
|
+
cellAspect?: number;
|
|
13
|
+
gridClass?: string;
|
|
14
|
+
frame?: boolean;
|
|
15
|
+
margin?: Margin;
|
|
16
|
+
frameClass?: string;
|
|
17
|
+
svg?: SVGSVGElement | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
text,
|
|
22
|
+
rows,
|
|
23
|
+
cols,
|
|
24
|
+
grid = false,
|
|
25
|
+
cellAspect = 0.6,
|
|
26
|
+
gridClass = '',
|
|
27
|
+
frame = false,
|
|
28
|
+
margin = 0,
|
|
29
|
+
frameClass = '',
|
|
30
|
+
svg = $bindable(),
|
|
31
|
+
...rest
|
|
32
|
+
}: Props = $props();
|
|
33
|
+
|
|
34
|
+
function parseMargin(m: Margin): { top: number; right: number; bottom: number; left: number } {
|
|
35
|
+
if (typeof m === 'number') return { top: m, right: m, bottom: m, left: m };
|
|
36
|
+
if (m.length === 2) return { top: m[0], right: m[1], bottom: m[0], left: m[1] };
|
|
37
|
+
return { top: m[0], right: m[1], bottom: m[2], left: m[3] };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function fmt(n: number, digits = 3): string {
|
|
41
|
+
if (!Number.isFinite(n)) return String(n);
|
|
42
|
+
if (Math.abs(n) < 1e-12) return '0';
|
|
43
|
+
const s = n.toFixed(digits);
|
|
44
|
+
return s.replace(/\.0+$/, '').replace(/(\.\d*?)0+$/, '$1');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const parsedMargin = $derived(parseMargin(margin));
|
|
48
|
+
|
|
49
|
+
// Derive rows/cols from text if not provided
|
|
50
|
+
const lines = $derived(text.split('\n'));
|
|
51
|
+
const contentRows = $derived(lines.length);
|
|
52
|
+
const contentCols = $derived(Math.max(0, ...lines.map((l) => l.length)));
|
|
53
|
+
const frameRows = $derived(rows ?? contentRows);
|
|
54
|
+
const frameCols = $derived(cols ?? contentCols);
|
|
55
|
+
const renderRows = $derived(Math.max(frameRows, contentRows));
|
|
56
|
+
const renderCols = $derived(Math.max(frameCols, contentCols));
|
|
57
|
+
|
|
58
|
+
const paddedLines = $derived(lines.map((l) => l.padEnd(renderCols, ' ')).slice(0, renderRows));
|
|
59
|
+
const gridRows = $derived(Array.from({ length: renderRows }, (_, r) => r));
|
|
60
|
+
const gridCols = $derived(Array.from({ length: renderCols }, (_, c) => c));
|
|
61
|
+
const charAt = (r: number, c: number) => (paddedLines[r] ?? '').charAt(c) || ' ';
|
|
62
|
+
|
|
63
|
+
// Character dimensions for monospace font (approximate ratio)
|
|
64
|
+
// Width:Height ratio for monospace is typically ~0.6
|
|
65
|
+
const cellHeight = 1;
|
|
66
|
+
const defaultFontStack =
|
|
67
|
+
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
|
|
68
|
+
|
|
69
|
+
// Calculate viewBox dimensions (frame + margin). Content may overflow the frame into the margin.
|
|
70
|
+
const totalCols = $derived(frameCols + parsedMargin.left + parsedMargin.right);
|
|
71
|
+
const totalRows = $derived(frameRows + parsedMargin.top + parsedMargin.bottom);
|
|
72
|
+
const viewBoxWidth = $derived(totalCols * cellAspect);
|
|
73
|
+
const viewBoxHeight = $derived(totalRows * cellHeight);
|
|
74
|
+
const totalGridCols = $derived(Array.from({ length: totalCols + 1 }, (_, c) => c));
|
|
75
|
+
const totalGridRows = $derived(Array.from({ length: totalRows + 1 }, (_, r) => r));
|
|
76
|
+
|
|
77
|
+
// Offsets for content
|
|
78
|
+
const offsetX = $derived(parsedMargin.left * cellAspect);
|
|
79
|
+
const offsetY = $derived(parsedMargin.top * cellHeight);
|
|
80
|
+
|
|
81
|
+
const viewBox = $derived(`0 0 ${fmt(viewBoxWidth)} ${fmt(viewBoxHeight)}`);
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<svg
|
|
85
|
+
bind:this={svg}
|
|
86
|
+
{...rest}
|
|
87
|
+
{viewBox}
|
|
88
|
+
overflow="hidden"
|
|
89
|
+
preserveAspectRatio="xMinYMin meet"
|
|
90
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
91
|
+
style="width: 100%; height: 100%; font-family: var(--ascii-font-family, {defaultFontStack});"
|
|
92
|
+
>
|
|
93
|
+
{#if grid}
|
|
94
|
+
<path
|
|
95
|
+
class={gridClass}
|
|
96
|
+
d={[
|
|
97
|
+
...totalGridCols.map((c) => `M ${fmt(c * cellAspect)} 0 V ${fmt(totalRows * cellHeight)}`),
|
|
98
|
+
...totalGridRows.map((r) => `M 0 ${fmt(r * cellHeight)} H ${fmt(totalCols * cellAspect)}`)
|
|
99
|
+
].join(' ')}
|
|
100
|
+
fill="none"
|
|
101
|
+
/>
|
|
102
|
+
{/if}
|
|
103
|
+
|
|
104
|
+
{#if frame}
|
|
105
|
+
<rect
|
|
106
|
+
class={frameClass}
|
|
107
|
+
x={fmt(offsetX)}
|
|
108
|
+
y={fmt(offsetY)}
|
|
109
|
+
width={fmt(frameCols * cellAspect)}
|
|
110
|
+
height={fmt(frameRows * cellHeight)}
|
|
111
|
+
fill="none"
|
|
112
|
+
/>
|
|
113
|
+
{/if}
|
|
114
|
+
|
|
115
|
+
{#each gridRows as r}
|
|
116
|
+
<text
|
|
117
|
+
y={fmt(offsetY + r * cellHeight + 0.8 * cellHeight)}
|
|
118
|
+
font-size={fmt(0.9 * cellHeight)}
|
|
119
|
+
fill="currentColor"
|
|
120
|
+
xml:space="preserve"
|
|
121
|
+
>
|
|
122
|
+
{#each gridCols as c}
|
|
123
|
+
<tspan x={fmt(offsetX + c * cellAspect + 0.1 * cellAspect)}>{charAt(r, c)}</tspan>
|
|
124
|
+
{/each}
|
|
125
|
+
</text>
|
|
126
|
+
{/each}
|
|
127
|
+
</svg>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SVGAttributes } from 'svelte/elements';
|
|
2
|
+
type Margin = number | [number, number] | [number, number, number, number];
|
|
3
|
+
interface Props extends SVGAttributes<SVGSVGElement> {
|
|
4
|
+
text: string;
|
|
5
|
+
rows?: number;
|
|
6
|
+
cols?: number;
|
|
7
|
+
grid?: boolean;
|
|
8
|
+
cellAspect?: number;
|
|
9
|
+
gridClass?: string;
|
|
10
|
+
frame?: boolean;
|
|
11
|
+
margin?: Margin;
|
|
12
|
+
frameClass?: string;
|
|
13
|
+
svg?: SVGSVGElement | null;
|
|
14
|
+
}
|
|
15
|
+
declare const AsciiArt: import("svelte").Component<Props, {}, "svg">;
|
|
16
|
+
type AsciiArt = ReturnType<typeof AsciiArt>;
|
|
17
|
+
export default AsciiArt;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AsciiArt } from './AsciiArt.svelte';
|
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "svelte-asciiart",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"dev": "vite dev",
|
|
6
|
+
"build": "vite build && npm run prepack",
|
|
7
|
+
"preview": "vite preview",
|
|
8
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
9
|
+
"prepack": "svelte-kit sync && svelte-package && cp ../../README.md README.md && publint",
|
|
10
|
+
"pub": "npm run prepack && npm publish --access public",
|
|
11
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
12
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
13
|
+
"test:unit": "vitest",
|
|
14
|
+
"test": "npm run test:unit -- --run",
|
|
15
|
+
"format": "prettier --write .",
|
|
16
|
+
"lint": "prettier --check ."
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"!dist/**/*.test.*",
|
|
21
|
+
"!dist/**/*.spec.*"
|
|
22
|
+
],
|
|
23
|
+
"sideEffects": [
|
|
24
|
+
"**/*.css"
|
|
25
|
+
],
|
|
26
|
+
"svelte": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"type": "module",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"svelte": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"svelte": "^5.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@sveltejs/adapter-auto": "^7.0.0",
|
|
40
|
+
"@sveltejs/kit": "^2.49.1",
|
|
41
|
+
"@sveltejs/package": "^2.5.7",
|
|
42
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
|
43
|
+
"@vitest/browser-playwright": "^4.0.15",
|
|
44
|
+
"playwright": "^1.57.0",
|
|
45
|
+
"prettier": "^3.7.4",
|
|
46
|
+
"prettier-plugin-svelte": "^3.4.0",
|
|
47
|
+
"publint": "^0.3.15",
|
|
48
|
+
"svelte": "^5.45.6",
|
|
49
|
+
"svelte-check": "^4.3.4",
|
|
50
|
+
"typescript": "^5.9.3",
|
|
51
|
+
"vite": "^7.2.6",
|
|
52
|
+
"vitest": "^4.0.15",
|
|
53
|
+
"vitest-browser-svelte": "^2.0.1"
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"svelte"
|
|
57
|
+
]
|
|
58
|
+
}
|