repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
pi/agent/skills/optional/ascii-boxes/index.mjs
Raw/** ASCII Box Drawing - Minimal, dependency-free implementation */
import { fileURLToPath } from "node:url";
const SPACES = " ".repeat(128);
const repeatSpace = (n) =>
n <= 0 ? "" : n < 128 ? SPACES.slice(0, n) : " ".repeat(n);
const B = {
single: {
h: "─",
v: "│",
tl: "┌",
tr: "┐",
bl: "└",
br: "┘",
te: "┬",
be: "┴",
le: "├",
re: "┤",
c: "┼",
},
double: {
h: "═",
v: "║",
tl: "╔",
tr: "╗",
bl: "╚",
br: "╝",
te: "╦",
be: "╩",
le: "╠",
re: "╣",
c: "╬",
},
bold: {
h: "━",
v: "┃",
tl: "┏",
tr: "┓",
bl: "┗",
br: "┛",
te: "┳",
be: "┻",
le: "┣",
re: "┫",
c: "╋",
},
};
function style(name) {
return name === "double" ? "double" : name === "bold" ? "bold" : "single";
}
function wrap(text, w) {
if (w < 1) return [""];
const words = text.trim().split(/\s+/);
if (!words.length || (words.length === 1 && !words[0])) return [""];
const lines = [];
let cur = "";
for (const word of words) {
const wl = word.length;
if (wl > w) {
if (cur) lines.push(cur);
for (let i = 0; i < wl; i += w) lines.push(word.slice(i, i + w));
cur = "";
} else if (!cur) {
cur = word;
} else if (cur.length + 1 + wl <= w) {
cur += " " + word;
} else {
lines.push(cur);
cur = word;
}
}
if (cur) lines.push(cur);
return lines;
}
function align(line, w, a) {
const t = line || "";
const e = w - t.length;
if (e <= 0) return t;
if (a === "left") return t + repeatSpace(e);
if (a === "right") return repeatSpace(e) + t;
const l = e >> 1;
return repeatSpace(l) + t + repeatSpace(e - l);
}
export function drawBox(content, opts = {}) {
const b = B[style(opts.style)];
const pt = opts.paddingTop ?? 0,
pb = opts.paddingBottom ?? 0;
const pl = opts.paddingLeft ?? 1,
pr = opts.paddingRight ?? 1;
const al = opts.alignment ?? "left";
const lines = typeof content === "string"
? opts.width > 0
? wrap(content, opts.width)
: [content]
: content;
let iw = opts.width ?? 0;
if (iw === 0) {
let m = 1;
for (let i = 0; i < lines.length; i++)
if (lines[i].length > m) m = lines[i].length;
iw = m;
}
const tw = iw + pl + pr;
const hf = b.h.repeat(tw);
const tl = b.tl + hf + b.tr,
bl = b.bl + hf + b.br;
const el = b.v + repeatSpace(tw) + b.v;
const out = new Array(1 + pt + lines.length + pb + 1);
out[0] = tl;
let idx = 1;
for (let i = 0; i < pt; i++, idx++) out[idx] = el;
if (al === "left") {
for (let i = 0; i < lines.length; i++, idx++) {
const cl = lines[i],
pad = cl + repeatSpace(iw - cl.length);
out[idx] = b.v + repeatSpace(pl) + pad + repeatSpace(pr) + b.v;
}
} else {
for (let i = 0; i < lines.length; i++, idx++) {
out[idx] =
b.v + repeatSpace(pl) + align(lines[i], iw, al) + repeatSpace(pr) + b.v;
}
}
for (let i = 0; i < pb; i++, idx++) out[idx] = el;
out[idx] = bl;
return out.join("\n");
}
export function createTable(headers, rows, opts = {}) {
const b = B[style(opts.style)];
const pad = opts.padding ?? 1;
const nc = Math.max(headers.length, ...rows.map((r) => r.length));
const cw = new Array(nc).fill(0);
for (let c = 0; c < nc; c++) {
if (headers[c]) cw[c] = Math.max(cw[c], headers[c].length);
for (const row of rows) if (row[c]) cw[c] = Math.max(cw[c], row[c].length);
}
const ps = repeatSpace(pad);
const fr = (cells, l, sep, r) => {
const parts = new Array(nc);
for (let c = 0; c < nc; c++)
parts[c] = align(cells[c] || "", cw[c], "left");
return l + ps + parts.join(ps + sep + ps) + ps + r;
};
const hl = (l, m, r) =>
l + cw.map((w) => b.h.repeat(w + pad * 2)).join(m) + r;
const lines = [];
lines.push(hl(b.tl, b.te || b.h.repeat(2), b.tr));
lines.push(fr(headers, b.v, b.v, b.v));
lines.push(hl(b.le || b.tl, b.c || b.te || b.h, b.re || b.tr));
for (const row of rows) lines.push(fr(row, b.v, b.v, b.v));
lines.push(hl(b.bl, b.be || b.h.repeat(2), b.br));
return lines.join("\n");
}
export function drawNested(outerText, innerText, opts = {}) {
const gap = opts.gap ?? 1;
const so = style(opts.styleOuter ?? opts.styleInner);
const si = style(opts.styleInner ?? so);
const innerBox = drawBox(innerText, {
style: si,
paddingTop: 0,
paddingBottom: 0,
paddingLeft: 1,
paddingRight: 1,
alignment: "left",
});
const innerLines = innerBox.split("\n");
const iw = Math.max(...innerLines.map((l) => l.length));
const outerLines = wrap(outerText, iw);
const centered = new Array(outerLines.length);
for (let i = 0; i < outerLines.length; i++)
centered[i] = align(outerLines[i], iw, "center");
const combined = new Array(gap + centered.length + gap + innerLines.length);
let i = 0;
for (let g = 0; g < gap; g++) combined[i++] = "";
for (let o = 0; o < centered.length; o++) combined[i++] = centered[o];
for (let g = 0; g < gap; g++) combined[i++] = "";
for (let il = 0; il < innerLines.length; il++) combined[i++] = innerLines[il];
return drawBox(combined, {
style: so,
paddingTop: 0,
paddingBottom: 0,
paddingLeft: 0,
paddingRight: 0,
alignment: "left",
});
}
function main(args) {
const cmd = args[0];
if (cmd === "draw" || cmd === "box") {
const textParts = [];
let i = 1;
while (i < args.length && !args[i].startsWith("--")) textParts.push(args[i++]);
const text = textParts.join(" ") || "";
let width;
let borderStyle = "single";
let alignment = "left";
while (i < args.length) {
const a = args[i];
if (a === "--width" && i + 1 < args.length) {
width = +args[i + 1];
i += 2;
} else if (a === "--style" && i + 1 < args.length) {
borderStyle = style(args[i + 1]);
i += 2;
} else if (a === "--align" && i + 1 < args.length) {
const s = args[i + 1];
alignment = s === "center" ? "center" : s === "right" ? "right" : "left";
i += 2;
} else {
i++;
}
}
console.log(drawBox(text, { width, style: borderStyle, alignment }));
} else if (cmd === "table") {
let headerStr = "";
const rows = [];
let borderStyle = "single";
for (let i = 1; i < args.length; i++) {
if (args[i] === "--header" && i + 1 < args.length) {
headerStr = args[++i];
} else if (args[i] === "--rows" && i + 1 < args.length) {
i++;
while (i < args.length && !args[i].startsWith("--")) rows.push(args[i++].split(",").map((s) => s.trim()));
i--;
} else if (args[i] === "--style" && i + 1 < args.length) {
borderStyle = style(args[++i]);
}
}
console.log(createTable(headerStr.split(",").map((s) => s.trim()), rows, { style: borderStyle }));
} else if (cmd === "nested") {
const outer = args[1] || "";
const inner = args[2] || "";
let gap = 1;
let borderStyle = "single";
for (let i = 3; i < args.length; i++) {
if (args[i] === "--gap" && i + 1 < args.length) {
gap = +args[++i];
} else if (args[i] === "--style" && i + 1 < args.length) {
borderStyle = style(args[++i]);
}
}
console.log(drawNested(outer, inner, { gap, styleOuter: borderStyle }));
} else {
console.log(
`ASCII Box Drawing\n\n draw <text> [--width N] [--style single|double|bold] [--align left|center|right]\n table --header H1,H2 --rows a,b c,d\n nested Outer Inner [--gap N]`,
);
}
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
try {
main(process.argv.slice(2));
} catch (e) {
console.error("Error:", e instanceof Error ? e.message : e);
process.exit(1);
}
}