@monowind/ascii 0.2.9 → 0.2.11
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 +3 -0
- package/dist/cdn.js +3 -3
- package/dist/cdn.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +69 -51
- package/dist/index.js.map +1 -1
- package/dist/render.d.ts +4 -0
- package/dist/render.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ import "./fonts/mono9.ts";
|
|
|
12
12
|
import type { AsciiFont } from "./font.ts";
|
|
13
13
|
export { asciiFont, registerAsciiFont } from "./registry.ts";
|
|
14
14
|
export { parseFont } from "./font.ts";
|
|
15
|
-
export { renderAscii } from "./render.ts";
|
|
15
|
+
export { renderAscii, trimAscii } from "./render.ts";
|
|
16
16
|
export type { AsciiFont, Glyph, HorizontalLayout } from "./font.ts";
|
|
17
17
|
export type { RenderedAscii } from "./render.ts";
|
|
18
18
|
export declare function onUnknownAsciiFont(handler: (name: string) => void): void;
|
|
@@ -20,8 +20,9 @@ declare const HTMLElementBase: typeof HTMLElement;
|
|
|
20
20
|
/** `<mono-ascii>`: renders its text content as ascii art. The `font`
|
|
21
21
|
* ATTRIBUTE names a registered font (default: "standard"); the `font`
|
|
22
22
|
* PROPERTY takes a parsed font object directly and wins over the
|
|
23
|
-
* attribute
|
|
24
|
-
*
|
|
23
|
+
* attribute; `trim` drops the blank rows and columns around the art.
|
|
24
|
+
* The light DOM keeps the semantic text (a11y); the shadow transcript
|
|
25
|
+
* is what select="text" selects and copies. */
|
|
25
26
|
export declare class MonoAsciiElement extends HTMLElementBase {
|
|
26
27
|
#private;
|
|
27
28
|
constructor();
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,qBAAqB,CAAC;AAC7B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,kBAAkB,CAAC;AAI1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,qBAAqB,CAAC;AAC7B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,kBAAkB,CAAC;AAI1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAQjD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAExE;AAID,QAAA,MAAM,eAAe,EAEhB,OAAO,WAAW,CAAC;AAsBxB;;;;;+CAK+C;AAC/C,qBAAa,gBAAiB,SAAQ,eAAe;;IAInD,cAKC;IAED,IAAI,IAAI,IAAI,SAAS,GAAG,IAAI,CAE3B;IAED,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,EAG9B;IAED;;iDAE6C;IAC7C,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAGhC;CACF;AA0CD;oCACoC;AACpC,wBAAgB,eAAe,IAAI,IAAI,CAUtC"}
|
package/dist/index.js
CHANGED
|
@@ -14,45 +14,62 @@ function a(e, t) {
|
|
|
14
14
|
}, i = !0;
|
|
15
15
|
for (let a of e) {
|
|
16
16
|
let e = t.glyphs.get(a === "\xA0" ? 32 : a.codePointAt(0)) ?? r;
|
|
17
|
-
|
|
17
|
+
c(n, e, i ? 0 : s(n, e, t), t), i = !1;
|
|
18
18
|
}
|
|
19
19
|
return {
|
|
20
20
|
lines: n.map((e) => e.chars.map((e) => e === t.hardblank ? " " : e).join("")),
|
|
21
|
-
runs:
|
|
21
|
+
runs: u(n)
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
-
function o(e
|
|
24
|
+
function o(e) {
|
|
25
|
+
let t = e.lines.map((e) => e.trim() !== ""), n = t.indexOf(!0);
|
|
26
|
+
if (n < 0) return {
|
|
27
|
+
lines: [],
|
|
28
|
+
runs: []
|
|
29
|
+
};
|
|
30
|
+
let r = t.lastIndexOf(!0) + 1, i = e.lines.slice(n, r), a = Math.min(...i.map((e) => e.search(/\S/)).filter((e) => e >= 0)), o = Math.max(...i.map((e) => e.trimEnd().length));
|
|
31
|
+
return {
|
|
32
|
+
lines: i.map((e) => e.slice(a, o)),
|
|
33
|
+
runs: e.runs.filter((e) => e.line >= n && e.line < r).map((e) => ({
|
|
34
|
+
...e,
|
|
35
|
+
line: e.line - n,
|
|
36
|
+
start: Math.max(e.start, a) - a,
|
|
37
|
+
end: Math.min(e.end, o) - a
|
|
38
|
+
})).filter((e) => e.end > e.start)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function s(e, t, n) {
|
|
25
42
|
if (n.layout === "full") return 0;
|
|
26
43
|
let r = Infinity;
|
|
27
44
|
for (let i = 0; i < e.length; i++) {
|
|
28
45
|
let a = e[i].chars, o = t.lines[i] ?? "", s = 0;
|
|
29
46
|
for (; s < a.length && a[a.length - 1 - s] === " ";) s++;
|
|
30
|
-
let
|
|
31
|
-
for (;
|
|
32
|
-
let u = s +
|
|
47
|
+
let c = 0;
|
|
48
|
+
for (; c < o.length && o.charAt(c) === " ";) c++;
|
|
49
|
+
let u = s + c;
|
|
33
50
|
if (n.layout !== "kerning") {
|
|
34
|
-
let e = a[a.length - 1 - s], t = o.charAt(
|
|
35
|
-
e !== void 0 && t !== "" &&
|
|
51
|
+
let e = a[a.length - 1 - s], t = o.charAt(c);
|
|
52
|
+
e !== void 0 && t !== "" && l(e, t, n) !== null && u++;
|
|
36
53
|
}
|
|
37
54
|
r = Math.min(r, u);
|
|
38
55
|
}
|
|
39
56
|
return Number.isFinite(r) ? r : 0;
|
|
40
57
|
}
|
|
41
|
-
function
|
|
58
|
+
function c(e, t, n, r) {
|
|
42
59
|
for (let i = 0; i < e.length; i++) {
|
|
43
|
-
let a = e[i], o = t.lines[i] ?? "", s = t.colors?.[i],
|
|
44
|
-
for (let e = 0; e <
|
|
45
|
-
let t = a.chars[u + e], n = o.charAt(e), i = a.paints[u + e],
|
|
46
|
-
if (t === " ") a.chars[u + e] = n, a.paints[u + e] = n === " " ? i :
|
|
60
|
+
let a = e[i], o = t.lines[i] ?? "", s = t.colors?.[i], c = Math.min(n, a.chars.length, o.length), u = a.chars.length - c;
|
|
61
|
+
for (let e = 0; e < c; e++) {
|
|
62
|
+
let t = a.chars[u + e], n = o.charAt(e), i = a.paints[u + e], c = s?.[e];
|
|
63
|
+
if (t === " ") a.chars[u + e] = n, a.paints[u + e] = n === " " ? i : c;
|
|
47
64
|
else if (n !== " ") {
|
|
48
|
-
let o =
|
|
49
|
-
a.chars[u + e] = o ?? n, a.paints[u + e] = o === t ? i :
|
|
65
|
+
let o = l(t, n, r);
|
|
66
|
+
a.chars[u + e] = o ?? n, a.paints[u + e] = o === t ? i : c;
|
|
50
67
|
}
|
|
51
68
|
}
|
|
52
|
-
for (let e =
|
|
69
|
+
for (let e = c; e < o.length; e++) a.chars.push(o.charAt(e)), a.paints.push(s?.[e]);
|
|
53
70
|
}
|
|
54
71
|
}
|
|
55
|
-
function
|
|
72
|
+
function l(e, t, n) {
|
|
56
73
|
if (e === " ") return t;
|
|
57
74
|
if (t === " ") return e;
|
|
58
75
|
let { hardblank: r } = n;
|
|
@@ -83,7 +100,7 @@ function c(e, t, n) {
|
|
|
83
100
|
}
|
|
84
101
|
return null;
|
|
85
102
|
}
|
|
86
|
-
function
|
|
103
|
+
function u(e) {
|
|
87
104
|
let t = [];
|
|
88
105
|
return e.forEach((e, n) => {
|
|
89
106
|
let r = -1, i, a = (e) => {
|
|
@@ -91,24 +108,24 @@ function l(e) {
|
|
|
91
108
|
line: n,
|
|
92
109
|
start: r,
|
|
93
110
|
end: e,
|
|
94
|
-
paint:
|
|
111
|
+
paint: d(i)
|
|
95
112
|
}), r = -1, i = void 0;
|
|
96
113
|
};
|
|
97
114
|
e.paints.forEach((e, t) => {
|
|
98
|
-
|
|
115
|
+
f(e, i) || (a(t), e && (r = t, i = e));
|
|
99
116
|
}), a(e.paints.length);
|
|
100
117
|
}), t;
|
|
101
118
|
}
|
|
102
|
-
function
|
|
119
|
+
function d(e) {
|
|
103
120
|
let t = {};
|
|
104
121
|
return e.color && (t.color = e.color), e.backgroundColor && (t.backgroundColor = e.backgroundColor), e.bold && (t.fontWeight = "bold"), t;
|
|
105
122
|
}
|
|
106
|
-
function
|
|
123
|
+
function f(e, t) {
|
|
107
124
|
return e === t ? !0 : !e || !t ? !1 : e.color === t.color && e.backgroundColor === t.backgroundColor && e.bold === t.bold;
|
|
108
125
|
}
|
|
109
126
|
//#endregion
|
|
110
127
|
//#region src/effects.ts
|
|
111
|
-
var
|
|
128
|
+
var p = {
|
|
112
129
|
rainbow: [
|
|
113
130
|
"red",
|
|
114
131
|
"yellow",
|
|
@@ -125,11 +142,11 @@ var f = {
|
|
|
125
142
|
"blue"
|
|
126
143
|
]
|
|
127
144
|
};
|
|
128
|
-
function
|
|
129
|
-
return e in
|
|
145
|
+
function m(e) {
|
|
146
|
+
return e in p;
|
|
130
147
|
}
|
|
131
|
-
function
|
|
132
|
-
let n =
|
|
148
|
+
function h(e, t) {
|
|
149
|
+
let n = p[e], r = (e) => `var(--mw-ansi-${n[e % n.length]})`, i = [];
|
|
133
150
|
return e === "metal" ? (t.forEach((e, a) => {
|
|
134
151
|
e.length > 0 && i.push({
|
|
135
152
|
line: a,
|
|
@@ -148,17 +165,17 @@ function m(e, t) {
|
|
|
148
165
|
}
|
|
149
166
|
//#endregion
|
|
150
167
|
//#region src/index.ts
|
|
151
|
-
var
|
|
152
|
-
function
|
|
153
|
-
|
|
168
|
+
var g = /* @__PURE__ */ new Set(), _ = /* @__PURE__ */ new WeakSet(), v = null;
|
|
169
|
+
function y(e) {
|
|
170
|
+
v = e;
|
|
154
171
|
}
|
|
155
|
-
var
|
|
172
|
+
var b = typeof HTMLElement > "u" ? class {} : HTMLElement, x = "<style>\n :host { display: block; }\n #mirror { margin: 0; font: inherit; line-height: inherit; letter-spacing: inherit; white-space: pre; color: transparent; }\n /* The engine paints a selection on its grid (specs/wide-characters.md);\n * the transcript's own highlight stays invisible, like the light DOM's. */\n #mirror::selection { color: transparent; text-shadow: none; background: transparent; }\n .alt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; user-select: none; -webkit-user-select: none; }\n</style><pre id=\"mirror\" aria-hidden=\"true\"></pre><span class=\"alt\"><slot></slot></span>", S = class extends b {
|
|
156
173
|
#e = null;
|
|
157
174
|
#t = null;
|
|
158
175
|
constructor() {
|
|
159
176
|
super();
|
|
160
177
|
let e = this.attachShadow({ mode: "open" });
|
|
161
|
-
e.innerHTML =
|
|
178
|
+
e.innerHTML = x, this.#t = e.getElementById("mirror");
|
|
162
179
|
}
|
|
163
180
|
get font() {
|
|
164
181
|
return this.#e;
|
|
@@ -171,35 +188,36 @@ var y = typeof HTMLElement > "u" ? class {} : HTMLElement, b = "<style>\n :host
|
|
|
171
188
|
this.#t && this.#t.textContent !== t && (this.#t.textContent = t);
|
|
172
189
|
}
|
|
173
190
|
};
|
|
174
|
-
function
|
|
175
|
-
let t =
|
|
191
|
+
function C(e) {
|
|
192
|
+
let t = w(e);
|
|
176
193
|
return e.syncMirror?.(t.lines), t;
|
|
177
194
|
}
|
|
178
|
-
function
|
|
195
|
+
function w(e) {
|
|
179
196
|
let t = (e.textContent ?? "").replace(/[ \t\r\n\f]+/g, " ").replace(/^ | $/g, "");
|
|
180
|
-
e.children.length > 0 && !
|
|
197
|
+
e.children.length > 0 && !_.has(e) && (_.add(e), console.warn("[monowind] <mono-ascii> takes text only; element children are ignored.", e));
|
|
181
198
|
let r = e.getAttribute("font") ?? "standard", i = e.font ?? n(r);
|
|
182
|
-
if (!i) return
|
|
199
|
+
if (!i) return g.has(`font:${r}`) || (g.add(`font:${r}`), v ? v(r) : console.warn(`[monowind] <mono-ascii>: no registered font "${r}"; rendering plain text.`, e)), { lines: t ? [t] : [] };
|
|
183
200
|
if (!t) return { lines: [] };
|
|
184
|
-
let
|
|
185
|
-
return
|
|
186
|
-
lines:
|
|
187
|
-
runs:
|
|
188
|
-
} :
|
|
189
|
-
lines: o.lines,
|
|
190
|
-
runs: o.runs
|
|
191
|
-
};
|
|
201
|
+
let s = a(t, i), c = e.hasAttribute("trim") ? o(s) : s, l = e.getAttribute("effect");
|
|
202
|
+
return l && m(l) ? {
|
|
203
|
+
lines: c.lines,
|
|
204
|
+
runs: h(l, c.lines)
|
|
205
|
+
} : c;
|
|
192
206
|
}
|
|
193
|
-
function
|
|
194
|
-
typeof customElements > "u" || customElements.get("mono-ascii") || (customElements.define("mono-ascii",
|
|
207
|
+
function T() {
|
|
208
|
+
typeof customElements > "u" || customElements.get("mono-ascii") || (customElements.define("mono-ascii", S), i({
|
|
195
209
|
tag: "mono-ascii",
|
|
196
|
-
observedAttributes: [
|
|
197
|
-
|
|
210
|
+
observedAttributes: [
|
|
211
|
+
"font",
|
|
212
|
+
"effect",
|
|
213
|
+
"trim"
|
|
214
|
+
],
|
|
215
|
+
render: C,
|
|
198
216
|
selectionTarget: (e) => e.shadowRoot?.getElementById("mirror") ?? null
|
|
199
217
|
}));
|
|
200
218
|
}
|
|
201
|
-
|
|
219
|
+
T();
|
|
202
220
|
//#endregion
|
|
203
|
-
export {
|
|
221
|
+
export { S as MonoAsciiElement, n as asciiFont, T as defineMonoAscii, y as onUnknownAsciiFont, t as parseFont, e as registerAsciiFont, a as renderAscii, o as trimAscii };
|
|
204
222
|
|
|
205
223
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/render.ts","../src/effects.ts","../src/index.ts"],"sourcesContent":["/**\n * Horizontal FIGlet composition: glyphs join under the font's own\n * layout mode — full width, kerning, or smushing (universal, or\n * controlled by the header's rule bits 1–6, per the FIGfont spec).\n * Vertical smushing is out of scope (single-line banners). Per-cell\n * color state from SGR-colored glyphs rides along and comes out as\n * paint runs.\n */\n\nimport type { AsciiFont, Glyph } from \"./font.ts\";\nimport type { CellColor } from \"./sgr.ts\";\nimport type { LeafPaint, LeafRun } from \"monowind\";\n\nexport interface RenderedAscii {\n lines: string[];\n runs: LeafRun[];\n}\n\ninterface Row {\n chars: string[];\n paints: (CellColor | undefined)[];\n}\n\nexport function renderAscii(text: string, font: AsciiFont): RenderedAscii {\n const rows: Row[] = Array.from({ length: font.height }, () => ({ chars: [], paints: [] }));\n const missing: Glyph = {\n lines: Array.from({ length: font.height }, () => \" \"),\n width: 1,\n };\n let first = true;\n for (const ch of text) {\n // A no-break space draws as the space glyph.\n const glyph = font.glyphs.get(ch === \"\\u00a0\" ? 0x20 : ch.codePointAt(0)!) ?? missing;\n const overlap = first ? 0 : smushAmount(rows, glyph, font);\n merge(rows, glyph, overlap, font);\n first = false;\n }\n const lines = rows.map((row) => row.chars.map((c) => (c === font.hardblank ? \" \" : c)).join(\"\"));\n return { lines, runs: collectRuns(rows) };\n}\n\n/** How far the next glyph slides left: the minimum over rows of\n * (trailing spaces + leading spaces), plus one when the touching pair\n * can smush. */\nfunction smushAmount(rows: Row[], glyph: Glyph, font: AsciiFont): number {\n if (font.layout === \"full\") return 0;\n let amount = Number.POSITIVE_INFINITY;\n for (let y = 0; y < rows.length; y++) {\n const a = rows[y]!.chars;\n const b = glyph.lines[y] ?? \"\";\n let trailing = 0;\n while (trailing < a.length && a[a.length - 1 - trailing] === \" \") trailing++;\n let leading = 0;\n while (leading < b.length && b.charAt(leading) === \" \") leading++;\n let amt = trailing + leading;\n if (font.layout !== \"kerning\") {\n const c1 = a[a.length - 1 - trailing];\n const c2 = b.charAt(leading);\n if (c1 !== undefined && c2 !== \"\" && smushem(c1, c2, font) !== null) amt++;\n }\n amount = Math.min(amount, amt);\n }\n return Number.isFinite(amount) ? amount : 0;\n}\n\nfunction merge(rows: Row[], glyph: Glyph, overlap: number, font: AsciiFont): void {\n for (let y = 0; y < rows.length; y++) {\n const row = rows[y]!;\n const line = glyph.lines[y] ?? \"\";\n const colors = glyph.colors?.[y];\n const cut = Math.min(overlap, row.chars.length, line.length);\n const base = row.chars.length - cut;\n for (let k = 0; k < cut; k++) {\n const c1 = row.chars[base + k]!;\n const c2 = line.charAt(k);\n const p1 = row.paints[base + k];\n const p2 = colors?.[k];\n if (c1 === \" \") {\n row.chars[base + k] = c2;\n row.paints[base + k] = c2 === \" \" ? p1 : p2;\n } else if (c2 !== \" \") {\n const smushed = smushem(c1, c2, font);\n row.chars[base + k] = smushed ?? c2;\n row.paints[base + k] = smushed === c1 ? p1 : p2;\n }\n }\n for (let k = cut; k < line.length; k++) {\n row.chars.push(line.charAt(k));\n row.paints.push(colors?.[k]);\n }\n }\n}\n\n/** The smushed character for a touching pair, or null when the pair\n * cannot smush (FIGfont spec, horizontal rules). */\nfunction smushem(c1: string, c2: string, font: AsciiFont): string | null {\n if (c1 === \" \") return c2;\n if (c2 === \" \") return c1;\n const { hardblank } = font;\n if (font.layout === \"smush-universal\") {\n // Universal smushing: the later character wins; hardblanks only\n // yield to other hardblanks.\n if (c1 === hardblank || c2 === hardblank) return null;\n return c2;\n }\n const rules = font.rules;\n if (c1 === hardblank || c2 === hardblank) {\n return rules & 32 && c1 === hardblank && c2 === hardblank ? hardblank : null;\n }\n if (rules & 1 && c1 === c2) return c1;\n if (rules & 2) {\n if (c1 === \"_\" && \"|/\\\\[]{}()<>\".includes(c2)) return c2;\n if (c2 === \"_\" && \"|/\\\\[]{}()<>\".includes(c1)) return c1;\n }\n if (rules & 4) {\n const classOf = (c: string) =>\n c === \"|\"\n ? 1\n : \"/\\\\\".includes(c)\n ? 2\n : \"[]\".includes(c)\n ? 3\n : \"{}\".includes(c)\n ? 4\n : \"()\".includes(c)\n ? 5\n : \"<>\".includes(c)\n ? 6\n : 0;\n const k1 = classOf(c1);\n const k2 = classOf(c2);\n if (k1 && k2 && k1 !== k2) return k1 > k2 ? c1 : c2;\n }\n if (rules & 8) {\n const pairs = [\"[]\", \"][\", \"{}\", \"}{\", \"()\", \")(\"];\n if (pairs.includes(c1 + c2)) return \"|\";\n }\n if (rules & 16) {\n if (c1 === \"/\" && c2 === \"\\\\\") return \"|\";\n if (c1 === \"\\\\\" && c2 === \"/\") return \"Y\";\n if (c1 === \">\" && c2 === \"<\") return \"X\";\n }\n return null;\n}\n\nfunction collectRuns(rows: Row[]): LeafRun[] {\n const runs: LeafRun[] = [];\n rows.forEach((row, line) => {\n let start = -1;\n let current: CellColor | undefined;\n const flush = (end: number) => {\n if (start >= 0 && current) runs.push({ line, start, end, paint: toPaint(current) });\n start = -1;\n current = undefined;\n };\n row.paints.forEach((paint, x) => {\n if (!samePaint(paint, current)) {\n flush(x);\n if (paint) {\n start = x;\n current = paint;\n }\n }\n });\n flush(row.paints.length);\n });\n return runs;\n}\n\nfunction toPaint(color: CellColor): LeafPaint {\n const paint: LeafPaint = {};\n if (color.color) paint.color = color.color;\n if (color.backgroundColor) paint.backgroundColor = color.backgroundColor;\n if (color.bold) paint.fontWeight = \"bold\";\n return paint;\n}\n\nfunction samePaint(a: CellColor | undefined, b: CellColor | undefined): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return a.color === b.color && a.backgroundColor === b.backgroundColor && a.bold === b.bold;\n}\n","/**\n * TOIlet-style render filters as the `effect` attribute — per-cell\n * color mapping defined against the theme tokens (styles.css), so\n * effects are themeable by construction, never hardcoded RGB. An\n * effect replaces any font-embedded colors (it's a whole-banner look).\n */\n\nimport type { LeafRun } from \"monowind\";\n\nconst EFFECTS = {\n // Column stripes through the six ANSI hues (toilet --gay).\n rainbow: [\"red\", \"yellow\", \"green\", \"cyan\", \"blue\", \"magenta\"],\n // Row bands from bright to dark (toilet --metal's shine).\n metal: [\"bright-white\", \"white\", \"bright-cyan\", \"bright-blue\", \"blue\"],\n} as const;\n\nexport type Effect = keyof typeof EFFECTS;\n\nexport function isEffect(name: string): name is Effect {\n return name in EFFECTS;\n}\n\nexport function effectRuns(effect: Effect, lines: string[]): LeafRun[] {\n const palette = EFFECTS[effect];\n const color = (index: number) => `var(--mw-ansi-${palette[index % palette.length]})`;\n const runs: LeafRun[] = [];\n if (effect === \"metal\") {\n lines.forEach((line, y) => {\n if (line.length > 0) {\n runs.push({\n line: y,\n start: 0,\n end: line.length,\n paint: { color: color(Math.floor((y * palette.length) / Math.max(1, lines.length))) },\n });\n }\n });\n return runs;\n }\n // Column-striped effects: one run per stripe per line.\n const stripe = 2; // two cells per hue reads better than one\n lines.forEach((line, y) => {\n for (let x = 0; x < line.length; x += stripe) {\n runs.push({\n line: y,\n start: x,\n end: Math.min(line.length, x + stripe),\n paint: { color: color(Math.floor(x / stripe)) },\n });\n }\n });\n return runs;\n}\n","/**\n * `@monowind/ascii` — FIGlet/TOIlet banner text on the monowind grid:\n * the `<mono-ascii>` element (a leaf renderer, specs/leaf-renderers.md\n * in the monowind repo) plus the font registry. The package entry\n * registers the element and the default fonts; additional fonts are\n * per-module imports (`@monowind/ascii/fonts/<name>`) or\n * `registerAsciiFont(name, data)` with data you source yourself.\n */\n\nimport { invalidateLeaves, registerLeafRenderer } from \"monowind\";\n// Default fonts (self-registering): figlet's canonical `standard`,\n// its compact sibling, and a TLF representative.\nimport \"./fonts/standard.ts\";\nimport \"./fonts/small.ts\";\nimport \"./fonts/mono9.ts\";\nimport { asciiFont } from \"./registry.ts\";\nimport { renderAscii } from \"./render.ts\";\nimport { effectRuns, isEffect } from \"./effects.ts\";\nimport type { AsciiFont } from \"./font.ts\";\nimport type { LeafContent } from \"monowind\";\n\nexport { asciiFont, registerAsciiFont } from \"./registry.ts\";\nexport { parseFont } from \"./font.ts\";\nexport { renderAscii } from \"./render.ts\";\nexport type { AsciiFont, Glyph, HorizontalLayout } from \"./font.ts\";\nexport type { RenderedAscii } from \"./render.ts\";\n\nconst warned = new Set<string>();\nconst warnedChildren = new WeakSet<Element>();\n\n/** CDN hook: called once per unknown font NAME so the bundle can\n * lazy-load it (bundler consumers import font modules instead). */\nlet unknownFontHandler: ((name: string) => void) | null = null;\nexport function onUnknownAsciiFont(handler: (name: string) => void): void {\n unknownFontHandler = handler;\n}\n\n// Import-safe outside the browser (SSR, Node scripts): same guard as\n// the core element.\nconst HTMLElementBase = (\n typeof HTMLElement === \"undefined\" ? class {} : HTMLElement\n) as typeof HTMLElement;\n\n/* The shadow pairs the two representations (grid glyphs stay the\n * visible paint): a transparent art transcript that overlays the grid\n * cell-for-cell — the host inherits the engine's typography lock, so\n * the pre aligns by construction — and the slotted semantic string,\n * visually hidden for the accessibility tree. Selection and copy read\n * the transcript natively (real text, real newlines, in every\n * sweep — drags, overshoots, select-all); the string never leaks into\n * a copy. */\n// No whitespace between top-level nodes: the host inherits the\n// engine's white-space: pre lock, so stray template newlines would\n// render as real empty lines above the transcript.\nconst SHADOW_TEMPLATE = `<style>\n :host { display: block; }\n #mirror { margin: 0; font: inherit; line-height: inherit; letter-spacing: inherit; white-space: pre; color: transparent; }\n /* The engine paints a selection on its grid (specs/wide-characters.md);\n * the transcript's own highlight stays invisible, like the light DOM's. */\n #mirror::selection { color: transparent; text-shadow: none; background: transparent; }\n .alt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; user-select: none; -webkit-user-select: none; }\n</style><pre id=\"mirror\" aria-hidden=\"true\"></pre><span class=\"alt\"><slot></slot></span>`;\n\n/** `<mono-ascii>`: renders its text content as ascii art. The `font`\n * ATTRIBUTE names a registered font (default: \"standard\"); the `font`\n * PROPERTY takes a parsed font object directly and wins over the\n * attribute. The light DOM keeps the semantic text (a11y); the shadow\n * transcript is what select=\"text\" selects and copies. */\nexport class MonoAsciiElement extends HTMLElementBase {\n #font: AsciiFont | null = null;\n #mirror: HTMLElement | null = null;\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: \"open\" });\n shadow.innerHTML = SHADOW_TEMPLATE;\n this.#mirror = shadow.getElementById(\"mirror\");\n }\n\n get font(): AsciiFont | null {\n return this.#font;\n }\n\n set font(font: AsciiFont | null) {\n this.#font = font;\n invalidateLeaves();\n }\n\n /** Keep the transcript in step with the art (called from the leaf\n * renderer each pass — the shadow is invisible to the engine, so\n * the sync never feeds back into layout). */\n syncMirror(lines: string[]): void {\n const text = lines.join(\"\\n\");\n if (this.#mirror && this.#mirror.textContent !== text) this.#mirror.textContent = text;\n }\n}\n\nfunction renderLeaf(el: Element): LeafContent {\n const content = renderContent(el);\n (el as Partial<MonoAsciiElement>).syncMirror?.(content.lines);\n return content;\n}\n\nfunction renderContent(el: Element): LeafContent {\n // The banner is the textContent with CSS collapsible white space\n // (space, tab, CR, LF, FF — newlines included) folded to one space\n // and trimmed — a one-line banner by contract. A no-break space is a\n // character: ` ` widens a gap the way it does in any text.\n const text = (el.textContent ?? \"\").replace(/[ \\t\\r\\n\\f]+/g, \" \").replace(/^ | $/g, \"\");\n if (el.children.length > 0 && !warnedChildren.has(el)) {\n warnedChildren.add(el);\n console.warn(\"[monowind] <mono-ascii> takes text only; element children are ignored.\", el);\n }\n const name = el.getAttribute(\"font\") ?? \"standard\";\n const font = (el as MonoAsciiElement).font ?? asciiFont(name);\n if (!font) {\n // Content never disappears: no (or unknown) font falls back to\n // the plain text.\n if (!warned.has(`font:${name}`)) {\n warned.add(`font:${name}`);\n if (unknownFontHandler) unknownFontHandler(name);\n else\n console.warn(\n `[monowind] <mono-ascii>: no registered font \"${name}\"; rendering plain text.`,\n el,\n );\n }\n return { lines: text ? [text] : [] };\n }\n if (!text) return { lines: [] };\n const rendered = renderAscii(text, font);\n const effect = el.getAttribute(\"effect\");\n if (effect && isEffect(effect)) {\n return { lines: rendered.lines, runs: effectRuns(effect, rendered.lines) };\n }\n return { lines: rendered.lines, runs: rendered.runs };\n}\n\n/** Idempotent registration of the element + leaf renderer (safe under\n * HMR and multiple entry loads). */\nexport function defineMonoAscii(): void {\n if (typeof customElements === \"undefined\") return;\n if (customElements.get(\"mono-ascii\")) return;\n customElements.define(\"mono-ascii\", MonoAsciiElement);\n registerLeafRenderer({\n tag: \"mono-ascii\",\n observedAttributes: [\"font\", \"effect\"],\n render: renderLeaf,\n selectionTarget: (el) => el.shadowRoot?.getElementById(\"mirror\") ?? null,\n });\n}\n\ndefineMonoAscii();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/render.ts","../src/effects.ts","../src/index.ts"],"sourcesContent":["/**\n * Horizontal FIGlet composition: glyphs join under the font's own\n * layout mode — full width, kerning, or smushing (universal, or\n * controlled by the header's rule bits 1–6, per the FIGfont spec).\n * Vertical smushing is out of scope (single-line banners). Per-cell\n * color state from SGR-colored glyphs rides along and comes out as\n * paint runs.\n */\n\nimport type { AsciiFont, Glyph } from \"./font.ts\";\nimport type { CellColor } from \"./sgr.ts\";\nimport type { LeafPaint, LeafRun } from \"monowind\";\n\nexport interface RenderedAscii {\n lines: string[];\n runs: LeafRun[];\n}\n\ninterface Row {\n chars: string[];\n paints: (CellColor | undefined)[];\n}\n\nexport function renderAscii(text: string, font: AsciiFont): RenderedAscii {\n const rows: Row[] = Array.from({ length: font.height }, () => ({ chars: [], paints: [] }));\n const missing: Glyph = {\n lines: Array.from({ length: font.height }, () => \" \"),\n width: 1,\n };\n let first = true;\n for (const ch of text) {\n // A no-break space draws as the space glyph.\n const glyph = font.glyphs.get(ch === \"\\u00a0\" ? 0x20 : ch.codePointAt(0)!) ?? missing;\n const overlap = first ? 0 : smushAmount(rows, glyph, font);\n merge(rows, glyph, overlap, font);\n first = false;\n }\n const lines = rows.map((row) => row.chars.map((c) => (c === font.hardblank ? \" \" : c)).join(\"\"));\n return { lines, runs: collectRuns(rows) };\n}\n\n/** The art without the blank rows above and below it and the blank\n * columns beside it — a FIGlet glyph keeps its font's leading space\n * column and descender row — its runs moved along. */\nexport function trimAscii(art: RenderedAscii): RenderedAscii {\n const inked = art.lines.map((line) => line.trim() !== \"\");\n const top = inked.indexOf(true);\n if (top < 0) return { lines: [], runs: [] };\n const bottom = inked.lastIndexOf(true) + 1;\n const rows = art.lines.slice(top, bottom);\n const left = Math.min(...rows.map((line) => line.search(/\\S/)).filter((at) => at >= 0));\n const right = Math.max(...rows.map((line) => line.trimEnd().length));\n return {\n lines: rows.map((line) => line.slice(left, right)),\n runs: art.runs\n .filter((run) => run.line >= top && run.line < bottom)\n .map((run) => ({\n ...run,\n line: run.line - top,\n start: Math.max(run.start, left) - left,\n end: Math.min(run.end, right) - left,\n }))\n .filter((run) => run.end > run.start),\n };\n}\n\n/** How far the next glyph slides left: the minimum over rows of\n * (trailing spaces + leading spaces), plus one when the touching pair\n * can smush. */\nfunction smushAmount(rows: Row[], glyph: Glyph, font: AsciiFont): number {\n if (font.layout === \"full\") return 0;\n let amount = Number.POSITIVE_INFINITY;\n for (let y = 0; y < rows.length; y++) {\n const a = rows[y]!.chars;\n const b = glyph.lines[y] ?? \"\";\n let trailing = 0;\n while (trailing < a.length && a[a.length - 1 - trailing] === \" \") trailing++;\n let leading = 0;\n while (leading < b.length && b.charAt(leading) === \" \") leading++;\n let amt = trailing + leading;\n if (font.layout !== \"kerning\") {\n const c1 = a[a.length - 1 - trailing];\n const c2 = b.charAt(leading);\n if (c1 !== undefined && c2 !== \"\" && smushem(c1, c2, font) !== null) amt++;\n }\n amount = Math.min(amount, amt);\n }\n return Number.isFinite(amount) ? amount : 0;\n}\n\nfunction merge(rows: Row[], glyph: Glyph, overlap: number, font: AsciiFont): void {\n for (let y = 0; y < rows.length; y++) {\n const row = rows[y]!;\n const line = glyph.lines[y] ?? \"\";\n const colors = glyph.colors?.[y];\n const cut = Math.min(overlap, row.chars.length, line.length);\n const base = row.chars.length - cut;\n for (let k = 0; k < cut; k++) {\n const c1 = row.chars[base + k]!;\n const c2 = line.charAt(k);\n const p1 = row.paints[base + k];\n const p2 = colors?.[k];\n if (c1 === \" \") {\n row.chars[base + k] = c2;\n row.paints[base + k] = c2 === \" \" ? p1 : p2;\n } else if (c2 !== \" \") {\n const smushed = smushem(c1, c2, font);\n row.chars[base + k] = smushed ?? c2;\n row.paints[base + k] = smushed === c1 ? p1 : p2;\n }\n }\n for (let k = cut; k < line.length; k++) {\n row.chars.push(line.charAt(k));\n row.paints.push(colors?.[k]);\n }\n }\n}\n\n/** The smushed character for a touching pair, or null when the pair\n * cannot smush (FIGfont spec, horizontal rules). */\nfunction smushem(c1: string, c2: string, font: AsciiFont): string | null {\n if (c1 === \" \") return c2;\n if (c2 === \" \") return c1;\n const { hardblank } = font;\n if (font.layout === \"smush-universal\") {\n // Universal smushing: the later character wins; hardblanks only\n // yield to other hardblanks.\n if (c1 === hardblank || c2 === hardblank) return null;\n return c2;\n }\n const rules = font.rules;\n if (c1 === hardblank || c2 === hardblank) {\n return rules & 32 && c1 === hardblank && c2 === hardblank ? hardblank : null;\n }\n if (rules & 1 && c1 === c2) return c1;\n if (rules & 2) {\n if (c1 === \"_\" && \"|/\\\\[]{}()<>\".includes(c2)) return c2;\n if (c2 === \"_\" && \"|/\\\\[]{}()<>\".includes(c1)) return c1;\n }\n if (rules & 4) {\n const classOf = (c: string) =>\n c === \"|\"\n ? 1\n : \"/\\\\\".includes(c)\n ? 2\n : \"[]\".includes(c)\n ? 3\n : \"{}\".includes(c)\n ? 4\n : \"()\".includes(c)\n ? 5\n : \"<>\".includes(c)\n ? 6\n : 0;\n const k1 = classOf(c1);\n const k2 = classOf(c2);\n if (k1 && k2 && k1 !== k2) return k1 > k2 ? c1 : c2;\n }\n if (rules & 8) {\n const pairs = [\"[]\", \"][\", \"{}\", \"}{\", \"()\", \")(\"];\n if (pairs.includes(c1 + c2)) return \"|\";\n }\n if (rules & 16) {\n if (c1 === \"/\" && c2 === \"\\\\\") return \"|\";\n if (c1 === \"\\\\\" && c2 === \"/\") return \"Y\";\n if (c1 === \">\" && c2 === \"<\") return \"X\";\n }\n return null;\n}\n\nfunction collectRuns(rows: Row[]): LeafRun[] {\n const runs: LeafRun[] = [];\n rows.forEach((row, line) => {\n let start = -1;\n let current: CellColor | undefined;\n const flush = (end: number) => {\n if (start >= 0 && current) runs.push({ line, start, end, paint: toPaint(current) });\n start = -1;\n current = undefined;\n };\n row.paints.forEach((paint, x) => {\n if (!samePaint(paint, current)) {\n flush(x);\n if (paint) {\n start = x;\n current = paint;\n }\n }\n });\n flush(row.paints.length);\n });\n return runs;\n}\n\nfunction toPaint(color: CellColor): LeafPaint {\n const paint: LeafPaint = {};\n if (color.color) paint.color = color.color;\n if (color.backgroundColor) paint.backgroundColor = color.backgroundColor;\n if (color.bold) paint.fontWeight = \"bold\";\n return paint;\n}\n\nfunction samePaint(a: CellColor | undefined, b: CellColor | undefined): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return a.color === b.color && a.backgroundColor === b.backgroundColor && a.bold === b.bold;\n}\n","/**\n * TOIlet-style render filters as the `effect` attribute — per-cell\n * color mapping defined against the theme tokens (styles.css), so\n * effects are themeable by construction, never hardcoded RGB. An\n * effect replaces any font-embedded colors (it's a whole-banner look).\n */\n\nimport type { LeafRun } from \"monowind\";\n\nconst EFFECTS = {\n // Column stripes through the six ANSI hues (toilet --gay).\n rainbow: [\"red\", \"yellow\", \"green\", \"cyan\", \"blue\", \"magenta\"],\n // Row bands from bright to dark (toilet --metal's shine).\n metal: [\"bright-white\", \"white\", \"bright-cyan\", \"bright-blue\", \"blue\"],\n} as const;\n\nexport type Effect = keyof typeof EFFECTS;\n\nexport function isEffect(name: string): name is Effect {\n return name in EFFECTS;\n}\n\nexport function effectRuns(effect: Effect, lines: string[]): LeafRun[] {\n const palette = EFFECTS[effect];\n const color = (index: number) => `var(--mw-ansi-${palette[index % palette.length]})`;\n const runs: LeafRun[] = [];\n if (effect === \"metal\") {\n lines.forEach((line, y) => {\n if (line.length > 0) {\n runs.push({\n line: y,\n start: 0,\n end: line.length,\n paint: { color: color(Math.floor((y * palette.length) / Math.max(1, lines.length))) },\n });\n }\n });\n return runs;\n }\n // Column-striped effects: one run per stripe per line.\n const stripe = 2; // two cells per hue reads better than one\n lines.forEach((line, y) => {\n for (let x = 0; x < line.length; x += stripe) {\n runs.push({\n line: y,\n start: x,\n end: Math.min(line.length, x + stripe),\n paint: { color: color(Math.floor(x / stripe)) },\n });\n }\n });\n return runs;\n}\n","/**\n * `@monowind/ascii` — FIGlet/TOIlet banner text on the monowind grid:\n * the `<mono-ascii>` element (a leaf renderer, specs/leaf-renderers.md\n * in the monowind repo) plus the font registry. The package entry\n * registers the element and the default fonts; additional fonts are\n * per-module imports (`@monowind/ascii/fonts/<name>`) or\n * `registerAsciiFont(name, data)` with data you source yourself.\n */\n\nimport { invalidateLeaves, registerLeafRenderer } from \"monowind\";\n// Default fonts (self-registering): figlet's canonical `standard`,\n// its compact sibling, and a TLF representative.\nimport \"./fonts/standard.ts\";\nimport \"./fonts/small.ts\";\nimport \"./fonts/mono9.ts\";\nimport { asciiFont } from \"./registry.ts\";\nimport { renderAscii, trimAscii } from \"./render.ts\";\nimport { effectRuns, isEffect } from \"./effects.ts\";\nimport type { AsciiFont } from \"./font.ts\";\nimport type { LeafContent } from \"monowind\";\n\nexport { asciiFont, registerAsciiFont } from \"./registry.ts\";\nexport { parseFont } from \"./font.ts\";\nexport { renderAscii, trimAscii } from \"./render.ts\";\nexport type { AsciiFont, Glyph, HorizontalLayout } from \"./font.ts\";\nexport type { RenderedAscii } from \"./render.ts\";\n\nconst warned = new Set<string>();\nconst warnedChildren = new WeakSet<Element>();\n\n/** CDN hook: called once per unknown font NAME so the bundle can\n * lazy-load it (bundler consumers import font modules instead). */\nlet unknownFontHandler: ((name: string) => void) | null = null;\nexport function onUnknownAsciiFont(handler: (name: string) => void): void {\n unknownFontHandler = handler;\n}\n\n// Import-safe outside the browser (SSR, Node scripts): same guard as\n// the core element.\nconst HTMLElementBase = (\n typeof HTMLElement === \"undefined\" ? class {} : HTMLElement\n) as typeof HTMLElement;\n\n/* The shadow pairs the two representations (grid glyphs stay the\n * visible paint): a transparent art transcript that overlays the grid\n * cell-for-cell — the host inherits the engine's typography lock, so\n * the pre aligns by construction — and the slotted semantic string,\n * visually hidden for the accessibility tree. Selection and copy read\n * the transcript natively (real text, real newlines, in every\n * sweep — drags, overshoots, select-all); the string never leaks into\n * a copy. */\n// No whitespace between top-level nodes: the host inherits the\n// engine's white-space: pre lock, so stray template newlines would\n// render as real empty lines above the transcript.\nconst SHADOW_TEMPLATE = `<style>\n :host { display: block; }\n #mirror { margin: 0; font: inherit; line-height: inherit; letter-spacing: inherit; white-space: pre; color: transparent; }\n /* The engine paints a selection on its grid (specs/wide-characters.md);\n * the transcript's own highlight stays invisible, like the light DOM's. */\n #mirror::selection { color: transparent; text-shadow: none; background: transparent; }\n .alt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; user-select: none; -webkit-user-select: none; }\n</style><pre id=\"mirror\" aria-hidden=\"true\"></pre><span class=\"alt\"><slot></slot></span>`;\n\n/** `<mono-ascii>`: renders its text content as ascii art. The `font`\n * ATTRIBUTE names a registered font (default: \"standard\"); the `font`\n * PROPERTY takes a parsed font object directly and wins over the\n * attribute; `trim` drops the blank rows and columns around the art.\n * The light DOM keeps the semantic text (a11y); the shadow transcript\n * is what select=\"text\" selects and copies. */\nexport class MonoAsciiElement extends HTMLElementBase {\n #font: AsciiFont | null = null;\n #mirror: HTMLElement | null = null;\n\n constructor() {\n super();\n const shadow = this.attachShadow({ mode: \"open\" });\n shadow.innerHTML = SHADOW_TEMPLATE;\n this.#mirror = shadow.getElementById(\"mirror\");\n }\n\n get font(): AsciiFont | null {\n return this.#font;\n }\n\n set font(font: AsciiFont | null) {\n this.#font = font;\n invalidateLeaves();\n }\n\n /** Keep the transcript in step with the art (called from the leaf\n * renderer each pass — the shadow is invisible to the engine, so\n * the sync never feeds back into layout). */\n syncMirror(lines: string[]): void {\n const text = lines.join(\"\\n\");\n if (this.#mirror && this.#mirror.textContent !== text) this.#mirror.textContent = text;\n }\n}\n\nfunction renderLeaf(el: Element): LeafContent {\n const content = renderContent(el);\n (el as Partial<MonoAsciiElement>).syncMirror?.(content.lines);\n return content;\n}\n\nfunction renderContent(el: Element): LeafContent {\n // The banner is the textContent with CSS collapsible white space\n // (space, tab, CR, LF, FF — newlines included) folded to one space\n // and trimmed — a one-line banner by contract. A no-break space is a\n // character: ` ` widens a gap the way it does in any text.\n const text = (el.textContent ?? \"\").replace(/[ \\t\\r\\n\\f]+/g, \" \").replace(/^ | $/g, \"\");\n if (el.children.length > 0 && !warnedChildren.has(el)) {\n warnedChildren.add(el);\n console.warn(\"[monowind] <mono-ascii> takes text only; element children are ignored.\", el);\n }\n const name = el.getAttribute(\"font\") ?? \"standard\";\n const font = (el as MonoAsciiElement).font ?? asciiFont(name);\n if (!font) {\n // Content never disappears: no (or unknown) font falls back to\n // the plain text.\n if (!warned.has(`font:${name}`)) {\n warned.add(`font:${name}`);\n if (unknownFontHandler) unknownFontHandler(name);\n else\n console.warn(\n `[monowind] <mono-ascii>: no registered font \"${name}\"; rendering plain text.`,\n el,\n );\n }\n return { lines: text ? [text] : [] };\n }\n if (!text) return { lines: [] };\n const rendered = renderAscii(text, font);\n const art = el.hasAttribute(\"trim\") ? trimAscii(rendered) : rendered;\n const effect = el.getAttribute(\"effect\");\n if (effect && isEffect(effect)) return { lines: art.lines, runs: effectRuns(effect, art.lines) };\n return art;\n}\n\n/** Idempotent registration of the element + leaf renderer (safe under\n * HMR and multiple entry loads). */\nexport function defineMonoAscii(): void {\n if (typeof customElements === \"undefined\") return;\n if (customElements.get(\"mono-ascii\")) return;\n customElements.define(\"mono-ascii\", MonoAsciiElement);\n registerLeafRenderer({\n tag: \"mono-ascii\",\n observedAttributes: [\"font\", \"effect\", \"trim\"],\n render: renderLeaf,\n selectionTarget: (el) => el.shadowRoot?.getElementById(\"mirror\") ?? null,\n });\n}\n\ndefineMonoAscii();\n"],"mappings":"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"}
|
package/dist/render.d.ts
CHANGED
|
@@ -13,4 +13,8 @@ export interface RenderedAscii {
|
|
|
13
13
|
runs: LeafRun[];
|
|
14
14
|
}
|
|
15
15
|
export declare function renderAscii(text: string, font: AsciiFont): RenderedAscii;
|
|
16
|
+
/** The art without the blank rows above and below it and the blank
|
|
17
|
+
* columns beside it — a FIGlet glyph keeps its font's leading space
|
|
18
|
+
* column and descender row — its runs moved along. */
|
|
19
|
+
export declare function trimAscii(art: RenderedAscii): RenderedAscii;
|
|
16
20
|
//# sourceMappingURL=render.d.ts.map
|
package/dist/render.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAS,MAAM,WAAW,CAAC;AAElD,OAAO,KAAK,EAAa,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB;AAOD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,aAAa,CAgBxE"}
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAS,MAAM,WAAW,CAAC;AAElD,OAAO,KAAK,EAAa,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB;AAOD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,aAAa,CAgBxE;AAED;;sDAEsD;AACtD,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,aAAa,CAoB3D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monowind/ascii",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "FIGlet/TOIlet ascii-art banner text for monowind: the <mono-ascii> element and a font library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ascii-art",
|
|
@@ -42,16 +42,16 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/figlet": "^1.7.0",
|
|
45
|
-
"@types/node": "^26.
|
|
45
|
+
"@types/node": "^26.5.0",
|
|
46
46
|
"figlet": "^1.11.4",
|
|
47
|
-
"happy-dom": "^20.14.
|
|
48
|
-
"monowind": "^0.2.
|
|
47
|
+
"happy-dom": "^20.14.3",
|
|
48
|
+
"monowind": "^0.2.11",
|
|
49
49
|
"typescript": "^7.0.2",
|
|
50
|
-
"vite": "^8.
|
|
50
|
+
"vite": "^8.3.0",
|
|
51
51
|
"vitest": "^4.1.11"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"monowind": "^0.2.
|
|
54
|
+
"monowind": "^0.2.11"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "node scripts/generate-fonts.mjs && vite build && vite build -c vite.cdn.config.ts && tsc -p tsconfig.build.json && node -e \"fs.readdirSync('dist/fonts').filter(f=>f.endsWith('.map')).forEach(f=>fs.rmSync('dist/fonts/'+f))\"",
|