@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/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. The light DOM keeps the semantic text (a11y); the shadow
24
- * transcript is what select="text" selects and copies. */
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();
@@ -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;AAC1C,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;;;;0DAI0D;AAC1D,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;AA2CD;oCACoC;AACpC,wBAAgB,eAAe,IAAI,IAAI,CAUtC"}
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
- s(n, e, i ? 0 : o(n, e, t), t), i = !1;
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: l(n)
21
+ runs: u(n)
22
22
  };
23
23
  }
24
- function o(e, t, n) {
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 l = 0;
31
- for (; l < o.length && o.charAt(l) === " ";) l++;
32
- let u = s + l;
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(l);
35
- e !== void 0 && t !== "" && c(e, t, n) !== null && u++;
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 s(e, t, n, r) {
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], l = Math.min(n, a.chars.length, o.length), u = a.chars.length - l;
44
- for (let e = 0; e < l; e++) {
45
- let t = a.chars[u + e], n = o.charAt(e), i = a.paints[u + e], l = s?.[e];
46
- if (t === " ") a.chars[u + e] = n, a.paints[u + e] = n === " " ? i : l;
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 = c(t, n, r);
49
- a.chars[u + e] = o ?? n, a.paints[u + e] = o === t ? i : l;
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 = l; e < o.length; e++) a.chars.push(o.charAt(e)), a.paints.push(s?.[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 c(e, t, n) {
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 l(e) {
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: u(i)
111
+ paint: d(i)
95
112
  }), r = -1, i = void 0;
96
113
  };
97
114
  e.paints.forEach((e, t) => {
98
- d(e, i) || (a(t), e && (r = t, i = e));
115
+ f(e, i) || (a(t), e && (r = t, i = e));
99
116
  }), a(e.paints.length);
100
117
  }), t;
101
118
  }
102
- function u(e) {
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 d(e, t) {
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 f = {
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 p(e) {
129
- return e in f;
145
+ function m(e) {
146
+ return e in p;
130
147
  }
131
- function m(e, t) {
132
- let n = f[e], r = (e) => `var(--mw-ansi-${n[e % n.length]})`, i = [];
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 h = /* @__PURE__ */ new Set(), g = /* @__PURE__ */ new WeakSet(), _ = null;
152
- function v(e) {
153
- _ = e;
168
+ var g = /* @__PURE__ */ new Set(), _ = /* @__PURE__ */ new WeakSet(), v = null;
169
+ function y(e) {
170
+ v = e;
154
171
  }
155
- var y = typeof HTMLElement > "u" ? class {} : HTMLElement, b = "<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>", x = class extends y {
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 = b, this.#t = e.getElementById("mirror");
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 S(e) {
175
- let t = C(e);
191
+ function C(e) {
192
+ let t = w(e);
176
193
  return e.syncMirror?.(t.lines), t;
177
194
  }
178
- function C(e) {
195
+ function w(e) {
179
196
  let t = (e.textContent ?? "").replace(/[ \t\r\n\f]+/g, " ").replace(/^ | $/g, "");
180
- e.children.length > 0 && !g.has(e) && (g.add(e), console.warn("[monowind] <mono-ascii> takes text only; element children are ignored.", e));
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 h.has(`font:${r}`) || (h.add(`font:${r}`), _ ? _(r) : console.warn(`[monowind] <mono-ascii>: no registered font "${r}"; rendering plain text.`, e)), { lines: t ? [t] : [] };
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 o = a(t, i), s = e.getAttribute("effect");
185
- return s && p(s) ? {
186
- lines: o.lines,
187
- runs: m(s, o.lines)
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 w() {
194
- typeof customElements > "u" || customElements.get("mono-ascii") || (customElements.define("mono-ascii", x), i({
207
+ function T() {
208
+ typeof customElements > "u" || customElements.get("mono-ascii") || (customElements.define("mono-ascii", S), i({
195
209
  tag: "mono-ascii",
196
- observedAttributes: ["font", "effect"],
197
- render: S,
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
- w();
219
+ T();
202
220
  //#endregion
203
- export { x as MonoAsciiElement, n as asciiFont, w as defineMonoAscii, v as onUnknownAsciiFont, t as parseFont, e as registerAsciiFont, a as renderAscii };
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: `&nbsp;` 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: `&nbsp;` 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
@@ -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.9",
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.4.1",
45
+ "@types/node": "^26.5.0",
46
46
  "figlet": "^1.11.4",
47
- "happy-dom": "^20.14.0",
48
- "monowind": "^0.2.9",
47
+ "happy-dom": "^20.14.3",
48
+ "monowind": "^0.2.11",
49
49
  "typescript": "^7.0.2",
50
- "vite": "^8.2.2",
50
+ "vite": "^8.3.0",
51
51
  "vitest": "^4.1.11"
52
52
  },
53
53
  "peerDependencies": {
54
- "monowind": "^0.2.9"
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))\"",