AkurAI Build
Menu

Public repository / 3af23b0e937520000e1aa43c7e2eb9de2675abd1

AkurAI-Mail / scripts/gen-icons.ts

Latest change 3af23b0e937520000e1aa43c7e2eb9de2675abd1 - OLI-248: webmail list and reader as framework islands on a signals store by Ólafur Búi Ólafsson

// One-off generator: inline SVG icon data for the icons the mail UI uses, taken from lucide-svelte's shipped components.
const names = "Archive ArrowLeft Bell Bold Bot Check ChevronDown ChevronLeft ChevronRight CircleHelp Clock Clock3 Download FilePen FileText Flag FolderInput Forward Inbox Italic Keyboard Link2 List ListOrdered Mail MessageCircle MoreHorizontal Paperclip PenLine Plus Quote RefreshCcw RefreshCw Reply ReplyAll Search SearchCheck Send ServerCog Settings2 ShieldAlert SlidersHorizontal Star Trash2 TriangleAlert UserPlus WifiOff X".split(" ");
const kebab = (n: string) => n.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Za-z])([0-9])/g, "$1-$2").toLowerCase();
let out = `// icons.js — inline SVG icons for AkurAI Mail (no build step, no runtime dependency).
// Path data from Lucide (https://lucide.dev), ISC License, Copyright (c) Lucide Icons and Contributors;
// icons derived from Feather are MIT, Copyright (c) 2013-present Cole Bemis. Generated by scripts/gen-icons.ts.

const SVG_NS = "http://www.w3.org/2000/svg";

/** @type {Record<string, [string, Record<string, string>][]>} */
export const ICONS = {
`;
for (const n of names) {
  // Icons lucide has renamed since the UI was written keep their old names here.
  const RENAMED: Record<string, string> = { MoreHorizontal: "ellipsis", CircleHelp: "circle-question-mark" };
  const file = `node_modules/lucide-svelte/dist/icons/${RENAMED[n] ?? kebab(n)}.svelte`;
  const src = await Bun.file(file).text();
  const m = /const iconNode = (\[.*\]);/.exec(src);
  if (!m) throw new Error(`no iconNode in ${file}`);
  out += `  ${n}: ${m[1]},\n`;
}
out += `};

/**
 * An inline SVG element for icon \`name\` (a key of ICONS).
 * @param {string} name
 * @param {{ size?: number, strokeWidth?: number, fill?: string, class?: string, label?: string }} [options]
 * @returns {SVGSVGElement}
 */
export function icon(name, { size = 24, strokeWidth = 2, fill = "none", class: className = "", label = "" } = {}) {
  // Accepts PascalCase keys and lucide's kebab-case names ("message-circle").
  const key = name.includes("-") || name[0] !== name[0].toUpperCase() ? name.replace(/(^|-)([a-z0-9])/g, (_, _sep, c) => c.toUpperCase()) : name;
  const nodes = ICONS[key];
  if (!nodes) throw new Error(\`unknown icon \${name}\`);
  const svg = document.createElementNS(SVG_NS, "svg");
  const attrs = { xmlns: SVG_NS, width: String(size), height: String(size), viewBox: "0 0 24 24", fill, stroke: "currentColor", "stroke-width": String(strokeWidth), "stroke-linecap": "round", "stroke-linejoin": "round", class: \`icon icon-\${name} \${className}\`.trim() };
  for (const [key, value] of Object.entries(attrs)) svg.setAttribute(key, value);
  if (label) svg.setAttribute("aria-label", label); else svg.setAttribute("aria-hidden", "true");
  for (const [tag, props] of nodes) {
    const child = document.createElementNS(SVG_NS, tag);
    for (const [key, value] of Object.entries(props)) child.setAttribute(key, value);
    svg.appendChild(child);
  }
  return svg;
}
`;
await Bun.write("src/framework/icons.js", out);
console.log("wrote", names.length, "icons");