view design_system/src/components/icon.js @ 279:b3b547563ec7

Add Google connector service and agent wiki Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code. Co-authored-by: Copilot <[email protected]> Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 22:22:36 -0700
parents 41a49c29a28f
children
line wrap: on
line source

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

const paths = {
  add: ["M12 5v14", "M5 12h14"],
  alert: ["M12 3 2.8 20h18.4L12 3Z", "M12 9v5", "M12 17h.01"],
  archive: ["M4 7h16v13H4Z", "M3 3h18v4H3Z", "M9 11h6"],
  "arrow-down": ["M12 4v16", "M6 14l6 6 6-6"],
  "arrow-left": ["M20 12H4", "M10 6l-6 6 6 6"],
  "arrow-right": ["M4 12h16", "M14 6l6 6-6 6"],
  "arrow-up": ["M12 20V4", "M6 10l6-6 6 6"],
  check: ["m5 12 4 4L19 6"],
  "chevron-down": ["m5 9 7 7 7-7"],
  "chevron-left": ["m15 5-7 7 7 7"],
  "chevron-right": ["m9 5 7 7-7 7"],
  "chevron-up": ["m5 15 7-7 7 7"],
  close: ["M6 6l12 12", "M18 6 6 18"],
  code: ["m9 6-6 6 6 6", "m6-12 6 6-6 6"],
  copy: ["M8 8h12v12H8Z", "M4 16V4h12"],
  download: ["M12 3v12", "m7-7 5 5 5-5", "M5 21h14"],
  error: ["M5 5l14 14", "M19 5 5 19"],
  external: ["M14 5h5v5", "m19 5-8 8", "M19 13v6H5V5h6"],
  file: ["M6 3h8l4 4v14H6Z", "M14 3v5h4"],
  folder: ["M3 6h7l2 2h9v12H3Z"],
  branch: ["M7 5v10a4 4 0 0 0 4 4h6", "M7 9h7a3 3 0 0 0 3-3V5"],
  calendar: ["M5 4h14v17H5Z", "M8 2v4", "M16 2v4", "M5 9h14"],
  history: ["M4 5v5h5", "M5 9a8 8 0 1 0 2-4", "M12 7v5l3 2"],
  info: ["M12 11v6", "M12 7h.01", "M3 12a9 9 0 1 0 18 0 9 9 0 1 0-18 0"],
  menu: ["M4 6h16", "M4 12h16", "M4 18h16"],
  moon: ["M20 15a8 8 0 0 1-11-11 9 9 0 1 0 11 11Z"],
  more: ["M5 12h.01", "M12 12h.01", "M19 12h.01"],
  offline: [
    "M5 12a7 7 0 0 1 10.5-6",
    "M19 12a7 7 0 0 0-.7-3",
    "M8.5 15.5a5 5 0 0 1 7 0",
    "M11.5 19h1",
    "M3 3l18 18",
  ],
  pause: ["M8 5v14", "M16 5v14"],
  paw: [
    "M8 11c-1.7 0-3-1.5-3-3.3S6 4.5 7.7 4.5s2.5 1.5 2.5 3.3S9.7 11 8 11Z",
    "M16 11c-1.7 0-2.2-1.5-2.2-3.2s.8-3.3 2.5-3.3S19 6 19 7.7 17.7 11 16 11Z",
    "M5.3 15c-1.5 0-2.8-1.2-2.8-2.7s1.2-2.8 2.7-2.8S8 10.7 8 12.2 6.8 15 5.3 15Z",
    "M18.7 15c-1.5 0-2.7-1.2-2.7-2.8s1.2-2.7 2.8-2.7 2.7 1.2 2.7 2.8-1.3 2.7-2.8 2.7Z",
    "M7 18.5c0-3 2.2-5.5 5-5.5s5 2.5 5 5.5c0 1.5-1.2 2.5-2.7 2.5-.8 0-1.5-.4-2.3-.4s-1.5.4-2.3.4C8.2 21 7 20 7 18.5Z",
  ],
  play: ["m8 5 11 7-11 7Z"],
  repository: ["M4 4h13a3 3 0 0 1 3 3v13H7a3 3 0 0 1-3-3Z", "M8 4v16"],
  retry: ["M4 4v6h6", "M5 9a8 8 0 1 1 2 8"],
  search: ["M4 11a7 7 0 1 0 14 0 7 7 0 1 0-14 0", "m16 16 4 4"],
  settings: [
    "M9.5 4.2 10 2h4l.5 2.2 2 .8 2-1.2 2.8 2.8-1.2 2 .8 2L22 10v4l-2.2.5-.8 2 1.2 2-2.8 2.8-2-1.2-2 .8-.5 2.2h-4L9.5 20l-2-.8-2 1.2-2.8-2.8 1.2-2-.8-2L2 14v-4l2.2-.5.8-2-1.2-2 2.8-2.8 2 1.2Z",
    "M9 12a3 3 0 1 0 6 0 3 3 0 1 0-6 0",
  ],
  sun: ["M12 2v3", "M12 19v3", "M2 12h3", "M19 12h3", "m5 5 2 2", "m17 17 2 2", "m19 5-2 2", "M7 17l-2 2", "M8 12a4 4 0 1 0 8 0 4 4 0 1 0-8 0"],
  trash: ["M4 7h16", "M9 3h6l1 4H8Z", "m6 7 1 14h10l1-14", "M10 11v6", "M14 11v6"],
  upload: ["M12 21V9", "m5 14 5-5 5 5", "M5 3h14"],
  user: ["M8 8a4 4 0 1 0 8 0 4 4 0 1 0-8 0", "M4 21a8 8 0 0 1 16 0"],
};

/** @const {!ReadonlyArray<string>} Names in the first-party icon registry. */
export const ICON_NAMES = Object.freeze(Object.keys(paths).sort());

function safeSize(value) {
  if (/^\d+(?:\.\d+)?(?:px|rem|em)?$/.test(value || "")) return value;
  return "1em";
}

/**
 * Renders validated first-party SVG geometry with inherited color.
 *
 * @extends {HTMLElement}
 */
export class ZenIcon extends HTMLElement {
  static get observedAttributes() {
    return ["label", "name", "size"];
  }

  connectedCallback() {
    this.render();
  }

  attributeChangedCallback() {
    if (this.isConnected) this.render();
  }

  render() {
    const requested = this.getAttribute("name") || "info";
    const known = Object.hasOwn(paths, requested);
    const definition = known ? paths[requested] : paths.alert;
    const svg = document.createElementNS(SVG_NAMESPACE, "svg");
    svg.setAttribute("viewBox", "0 0 24 24");
    svg.setAttribute("fill", "none");
    svg.setAttribute("stroke", "currentColor");
    svg.setAttribute("stroke-width", "2");
    svg.setAttribute("stroke-linecap", "round");
    svg.setAttribute("stroke-linejoin", "round");
    svg.setAttribute("focusable", "false");

    for (const data of definition) {
      const path = document.createElementNS(SVG_NAMESPACE, "path");
      path.setAttribute("d", data);
      svg.append(path);
    }

    const label = this.getAttribute("label");
    if (label) {
      this.setAttribute("role", "img");
      this.setAttribute("aria-label", label);
      this.removeAttribute("aria-hidden");
    } else {
      this.removeAttribute("role");
      this.removeAttribute("aria-label");
      this.setAttribute("aria-hidden", "true");
    }
    this.dataset.zenIcon = known ? requested : "alert";
    this.style.setProperty(
      "--zenbu-icon-size",
      safeSize(this.getAttribute("size")),
    );
    this.replaceChildren(svg);
  }
}

if (!customElements.get("zen-icon")) {
  customElements.define("zen-icon", ZenIcon);
}