AkurAI Build
Menu

Public repository / 439f0dcabf5cdf2b120c7724be895cf1df30507f

AkurAI-Framework / site/frontend/ai/components/display/weather_widget.js

Latest change 88d669a6b3c5464f8a947a66be0437a90424470f - feat(ai): add Display components — citation, link_preview, stats_display, terminal, weather_widget by Ólafur Búi Ólafsson

/**
 * Weather Widget — location, temp, condition, hi/lo, optional hourly array.
 * Payload: {
 *   location: string,
 *   temp: number,
 *   unit?: "C" | "F",
 *   condition: string,
 *   hi: number,
 *   lo: number,
 *   hourly?: Array<{ time: string, temp: number, condition: string }>
 * }
 * Condition icon: native SVG. No libs.
 */

function el(tag, cls) {
  const node = document.createElement(tag);
  if (cls) node.className = cls;
  return node;
}

// Map a condition string to an emoji (no lib, no images)
function conditionEmoji(condition) {
  const c = String(condition).toLowerCase();
  if (c.includes("thunder") || c.includes("storm")) return "⛈️";
  if (c.includes("snow") || c.includes("blizzard")) return "❄️";
  if (c.includes("sleet") || c.includes("hail")) return "🌨️";
  if (c.includes("rain") || c.includes("drizzle") || c.includes("shower")) return "🌧️";
  if (c.includes("fog") || c.includes("mist") || c.includes("haze")) return "🌫️";
  if (c.includes("cloud") || c.includes("overcast")) return "☁️";
  if (c.includes("partly") || c.includes("partial")) return "⛅";
  if (c.includes("clear") || c.includes("sunny")) return "☀️";
  if (c.includes("wind") || c.includes("breezy")) return "💨";
  return "🌡️";
}

export const def = {
  name: "weather_widget",
  category: "Display",
  title: "Weather Widget",

  match(payload) {
    return (
      payload !== null &&
      typeof payload === "object" &&
      typeof payload.location === "string" &&
      typeof payload.temp === "number" &&
      typeof payload.condition === "string" &&
      typeof payload.hi === "number" &&
      typeof payload.lo === "number"
    );
  },

  render(payload) {
    const { location, temp, unit = "C", condition, hi, lo, hourly } = payload;
    const deg = (n) => String(Math.round(n)) + "°" + unit;

    const card = el("div", "card tc-display-weather");

    // Top row: location + icon
    const top = el("div", "tc-display-weather__top");

    const locEl = el("div", "tc-display-weather__location muted small");
    locEl.textContent = location;
    top.appendChild(locEl);

    const icon = el("span", "tc-display-weather__icon");
    icon.textContent = conditionEmoji(condition);
    icon.setAttribute("aria-label", condition);
    top.appendChild(icon);

    card.appendChild(top);

    // Main temp
    const tempEl = el("div", "tc-display-weather__temp");
    tempEl.textContent = deg(temp);
    card.appendChild(tempEl);

    // Condition label
    const condEl = el("div", "tc-display-weather__condition muted");
    condEl.textContent = condition;
    card.appendChild(condEl);

    // Hi / Lo row
    const range = el("div", "tc-display-weather__range");
    const hiEl = el("span", "tc-display-weather__hi");
    hiEl.textContent = "H " + deg(hi);
    const sep = el("span", "muted");
    sep.textContent = " · ";
    const loEl = el("span", "tc-display-weather__lo muted");
    loEl.textContent = "L " + deg(lo);
    range.appendChild(hiEl);
    range.appendChild(sep);
    range.appendChild(loEl);
    card.appendChild(range);

    // Optional hourly strip
    if (Array.isArray(hourly) && hourly.length > 0) {
      const strip = el("div", "tc-display-weather__hourly");
      for (const h of hourly.slice(0, 8)) {
        const slot = el("div", "tc-display-weather__slot");

        const slotTime = el("div", "tc-display-weather__slot-time muted small");
        slotTime.textContent = String(h.time ?? "");
        slot.appendChild(slotTime);

        const slotIcon = el("div", "tc-display-weather__slot-icon");
        slotIcon.textContent = conditionEmoji(h.condition ?? "");
        slotIcon.setAttribute("aria-label", h.condition ?? "");
        slot.appendChild(slotIcon);

        const slotTemp = el("div", "tc-display-weather__slot-temp");
        slotTemp.textContent = deg(h.temp);
        slot.appendChild(slotTemp);

        strip.appendChild(slot);
      }
      card.appendChild(strip);
    }

    return card;
  },

  example: {
    location: "Akureyri, Iceland",
    temp: 9,
    unit: "C",
    condition: "Partly cloudy",
    hi: 12,
    lo: 5,
    hourly: [
      { time: "Now", temp: 9, condition: "Partly cloudy" },
      { time: "13:00", temp: 10, condition: "Partly cloudy" },
      { time: "14:00", temp: 11, condition: "Sunny" },
      { time: "15:00", temp: 12, condition: "Sunny" },
      { time: "16:00", temp: 11, condition: "Cloudy" },
      { time: "17:00", temp: 10, condition: "Rain" },
      { time: "18:00", temp: 8, condition: "Rain" },
      { time: "19:00", temp: 7, condition: "Cloudy" },
    ],
  },
};