← back to browse
goat command

up

Display system status in Windows 11 style

0
views
0
likes
0
installs

Aliases: uptime, status, sysinfo

raw source
const { createCanvas, registerFont } = require("canvas");
const os = require("os");
const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");

const fontDir = path.join(__dirname, "fonts");

function ensureFont(filename, url) {
  const fp = path.join(fontDir, filename);
  if (!fs.existsSync(fp)) {
    try {
      fs.mkdirSync(fontDir, { recursive: true });
      execSync(`curl -L -o "${fp}" "${url}"`, { stdio: "pipe" });
    } catch {}
  }
  return fp;
}

ensureFont("NotoColorEmoji.ttf", "https://github.com/googlefonts/noto-emoji/raw/main/fonts/NotoColorEmoji.ttf");

registerFont(path.join(fontDir, "CourierPrime-Regular.ttf"), { family: "UI" });
registerFont(path.join(fontDir, "CourierPrime-Bold.ttf"),    { family: "UI", weight: "bold" });
try {
  registerFont(path.join(fontDir, "NotoColorEmoji.ttf"), { family: "Emoji" });
} catch {}

const themeMap = {};

const THEMES = {
  dark: {
    bg0: "#091525", bg1: "#0d1f3c", bg2: "#0c1a35", bg3: "#07101e",
    winBody: "rgba(10,18,38,0.84)",
    sidebar: "rgba(255,255,255,0.032)",
    tbFrom: "rgba(255,255,255,0.11)", tbTo: "rgba(255,255,255,0.04)",
    tbBorder: "rgba(255,255,255,0.10)",
    tbText: "rgba(255,255,255,0.80)",
    btnBg: "rgba(255,255,255,0.06)",
    btnText: "rgba(255,255,255,0.78)",
    sidebarBorder: "rgba(255,255,255,0.055)",
    activeItem: "rgba(0,120,212,0.22)",
    activeText: "rgba(255,255,255,0.88)",
    inactiveText: "rgba(255,255,255,0.34)",
    taskFrom: "rgba(18,28,52,0.97)", taskTo: "rgba(12,20,40,0.99)",
    taskBorder: "rgba(255,255,255,0.07)",
    taskText: "rgba(255,255,255,0.80)",
    taskSub: "rgba(255,255,255,0.42)",
    taskIcons: "rgba(255,255,255,0.55)",
    pageTitle: "#ffffff",
    dateText: "rgba(255,255,255,0.28)",
    cardBg: "rgba(255,255,255,0.052)",
    cardBorder: "rgba(255,255,255,0.09)",
    cardShine: "rgba(255,255,255,0.07)",
    cardShadow: "rgba(0,0,0,0.55)",
    labelText: "rgba(255,255,255,0.40)",
    labelText2: "rgba(255,255,255,0.38)",
    labelText3: "rgba(255,255,255,0.46)",
    labelText4: "rgba(255,255,255,0.68)",
    valueText: "rgba(255,255,255,0.80)",
    barLabel: "rgba(255,255,255,0.78)",
    barPct: "rgba(255,255,255,0.50)",
    barTrack: "rgba(255,255,255,0.10)",
    ringTrack: "rgba(255,255,255,0.07)",
    ringValue: "#ffffff",
    rowAlt: "rgba(255,255,255,0.022)",
    detailValue: "rgba(255,255,255,0.80)",
    subtitleText: "rgba(255,255,255,0.28)",
    networkIP: "rgba(255,255,255,0.55)",
    networkMask: "rgba(255,255,255,0.35)",
    cmdText: "rgba(255,255,255,0.55)",
    themeToggleBg: "rgba(255,255,255,0.06)",
    themeToggleBorder: "rgba(255,255,255,0.18)",
    themeActiveBg: "#0078d4",
    themeActiveText: "#ffffff",
    themeInactiveText: "rgba(255,255,255,0.45)",
  },
  light: {
    bg0: "#f0f4f8", bg1: "#e8edf5", bg2: "#edf1f7", bg3: "#e4eaf2",
    winBody: "rgba(255,255,255,0.92)",
    sidebar: "rgba(240,244,248,0.85)",
    tbFrom: "rgba(255,255,255,0.95)", tbTo: "rgba(240,244,248,0.90)",
    tbBorder: "rgba(0,0,0,0.10)",
    tbText: "rgba(20,20,20,0.82)",
    btnBg: "rgba(0,0,0,0.05)",
    btnText: "rgba(20,20,20,0.70)",
    sidebarBorder: "rgba(0,0,0,0.07)",
    activeItem: "rgba(0,120,212,0.12)",
    activeText: "#0078d4",
    inactiveText: "rgba(40,40,40,0.45)",
    taskFrom: "rgba(255,255,255,0.98)", taskTo: "rgba(240,244,248,0.99)",
    taskBorder: "rgba(0,0,0,0.08)",
    taskText: "rgba(20,20,20,0.80)",
    taskSub: "rgba(20,20,20,0.42)",
    taskIcons: "rgba(40,40,40,0.40)",
    pageTitle: "#1a1a2e",
    dateText: "rgba(30,30,30,0.38)",
    cardBg: "rgba(0,0,0,0.03)",
    cardBorder: "rgba(0,0,0,0.08)",
    cardShine: "rgba(255,255,255,0.60)",
    cardShadow: "rgba(0,0,0,0.12)",
    labelText: "rgba(30,30,30,0.45)",
    labelText2: "rgba(30,30,30,0.42)",
    labelText3: "rgba(30,30,30,0.55)",
    labelText4: "rgba(30,30,30,0.65)",
    valueText: "rgba(20,20,20,0.80)",
    barLabel: "rgba(30,30,30,0.80)",
    barPct: "rgba(30,30,30,0.45)",
    barTrack: "rgba(0,0,0,0.08)",
    ringTrack: "rgba(0,0,0,0.08)",
    ringValue: "#1a1a2e",
    rowAlt: "rgba(0,0,0,0.03)",
    detailValue: "rgba(20,20,20,0.80)",
    subtitleText: "rgba(30,30,30,0.38)",
    networkIP: "rgba(30,30,30,0.60)",
    networkMask: "rgba(30,30,30,0.40)",
    cmdText: "rgba(30,30,30,0.60)",
    themeToggleBg: "rgba(0,0,0,0.04)",
    themeToggleBorder: "rgba(0,0,0,0.15)",
    themeActiveBg: "#0078d4",
    themeActiveText: "#ffffff",
    themeInactiveText: "rgba(30,30,30,0.45)",
  }
};

let prev = null;
const getCPU = () => {
  let idle = 0, total = 0;
  for (const c of os.cpus()) {
    for (const t in c.times) total += c.times[t];
    idle += c.times.idle;
  }
  const cur = { idle, total };
  if (!prev) { prev = cur; return 0; }
  const di = cur.idle - prev.idle, dt = cur.total - prev.total;
  prev = cur;
  return dt ? Math.round(100 - (100 * di / dt)) : 0;
};
const getDisk = () => {
  try {
    const d = execSync("df -k /").toString().split("\n")[1].split(/\s+/);
    return Math.round((parseInt(d[2]) / parseInt(d[1])) * 100);
  } catch { return Math.floor(Math.random() * 30) + 40; }
};
const getDiskTotal = () => {
  try {
    const d = execSync("df -k /").toString().split("\n")[1].split(/\s+/);
    return (parseInt(d[1]) / 1024 / 1024).toFixed(1);
  } catch { return "N/A"; }
};
const getDiskUsed = () => {
  try {
    const d = execSync("df -k /").toString().split("\n")[1].split(/\s+/);
    return (parseInt(d[2]) / 1024 / 1024).toFixed(1);
  } catch { return "N/A"; }
};
const getNetwork = () => {
  try {
    const ifaces = os.networkInterfaces();
    let t = 0;
    for (const i in ifaces) ifaces[i].forEach(a => { if (!a.internal && a.family === "IPv4") t++; });
    return t;
  } catch { return 1; }
};
const getNetworkIPs = () => {
  try {
    const ifaces = os.networkInterfaces();
    const result = [];
    for (const name in ifaces) {
      ifaces[name].forEach(a => {
        if (!a.internal && a.family === "IPv4") result.push({ name, address: a.address, netmask: a.netmask });
      });
    }
    return result;
  } catch { return []; }
};
const getTemperature = () => {
  try {
    if (os.platform() === "linux") {
      return Math.round(parseInt(execSync("cat /sys/class/thermal/thermal_zone0/temp").toString()) / 1000);
    } else if (os.platform() === "darwin") {
      const t = execSync("sudo powermetrics --samplers smc -i1 -n1 | grep -i 'CPU die temperature'").toString();
      const m = t.match(/(\d+.?\d*)/);
      return m ? Math.round(parseFloat(m[0])) : 45;
    }
  } catch { return Math.floor(Math.random() * 20) + 40; }
  return 45;
};
const getDhakaTime = () => {
  const now = new Date();
  const timeStr = now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", timeZone: "Asia/Dhaka" });
  const dateStr = now.toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: "Asia/Dhaka" });
  const fullStr = now.toLocaleString("en-US", {
    weekday: "short", day: "numeric", month: "short", year: "numeric",
    hour: "2-digit", minute: "2-digit", second: "2-digit", timeZone: "Asia/Dhaka"
  }) + " BDT";
  return { timeStr, dateStr, fullStr };
};

function roundRect(ctx, x, y, w, h, r) {
  const tl = Array.isArray(r) ? r[0] : r;
  const tr = Array.isArray(r) ? r[1] : r;
  const br = Array.isArray(r) ? r[2] : r;
  const bl = Array.isArray(r) ? r[3] : r;
  ctx.beginPath();
  ctx.moveTo(x + tl, y);
  ctx.lineTo(x + w - tr, y);
  ctx.quadraticCurveTo(x + w, y, x + w, y + tr);
  ctx.lineTo(x + w, y + h - br);
  ctx.quadraticCurveTo(x + w, y + h, x + w - br, y + h);
  ctx.lineTo(x + bl, y + h);
  ctx.quadraticCurveTo(x, y + h, x, y + h - bl);
  ctx.lineTo(x, y + tl);
  ctx.quadraticCurveTo(x, y, x + tl, y);
  ctx.closePath();
}

function acrylicCardT(ctx, x, y, w, h, r, th) {
  ctx.save();
  ctx.shadowColor   = th.cardShadow;
  ctx.shadowBlur    = 18;
  ctx.shadowOffsetY = 4;
  roundRect(ctx, x, y, w, h, r);
  ctx.fillStyle = th.cardBg;
  ctx.fill();
  ctx.restore();
  roundRect(ctx, x, y, w, h, r);
  ctx.strokeStyle = th.cardBorder;
  ctx.lineWidth   = 1;
  ctx.stroke();
  roundRect(ctx, x + 1, y + 1, w - 2, 2, 1);
  ctx.fillStyle = th.cardShine;
  ctx.fill();
}

function win11BarT(ctx, x, y, w, value, color1, color2, label, pct, th) {
  const trackH = 10;
  ctx.font      = "bold 24px 'UI'";
  ctx.fillStyle = th.barLabel;
  ctx.textAlign = "left";
  ctx.fillText(label, x, y - 10);
  ctx.fillStyle = th.barPct;
  ctx.textAlign = "right";
  ctx.fillText(`${pct}%`, x + w, y - 10);
  roundRect(ctx, x, y, w, trackH, trackH / 2);
  ctx.fillStyle = th.barTrack;
  ctx.fill();
  if (value > 0) {
    const fw = Math.max((value / 100) * w, trackH);
    const g  = ctx.createLinearGradient(x, 0, x + fw, 0);
    g.addColorStop(0, color1);
    g.addColorStop(1, color2);
    roundRect(ctx, x, y, fw, trackH, trackH / 2);
    ctx.fillStyle = g;
    ctx.fill();
    ctx.save();
    ctx.shadowColor = color1;
    ctx.shadowBlur  = 12;
    roundRect(ctx, x + fw - 4, y, 4, trackH, trackH / 2);
    ctx.fillStyle = color2;
    ctx.fill();
    ctx.restore();
  }
  ctx.textAlign = "left";
}

function clipText(ctx, text, maxW) {
  let t = text;
  while (ctx.measureText(t).width > maxW && t.length > 1) t = t.slice(0, -1);
  if (t !== text) t = t.slice(0, -1) + "..";
  return t;
}

function drawBase(c, W, H, activePage, version, th) {
  const bg = c.createLinearGradient(0, 0, W, H);
  bg.addColorStop(0,    th.bg0);
  bg.addColorStop(0.4,  th.bg1);
  bg.addColorStop(0.75, th.bg2);
  bg.addColorStop(1,    th.bg3);
  c.fillStyle = bg;
  c.fillRect(0, 0, W, H);

  const glow = (gx, gy, gr, col) => {
    const rg = c.createRadialGradient(gx, gy, 0, gx, gy, gr);
    rg.addColorStop(0, col);
    rg.addColorStop(1, "rgba(0,0,0,0)");
    c.fillStyle = rg;
    c.fillRect(gx - gr, gy - gr, gr * 2, gr * 2);
  };
  glow(280, 200, 480, "rgba(0,120,212,0.17)");
  glow(1520, 820, 440, "rgba(90,60,200,0.13)");
  glow(900, 580, 560, "rgba(0,80,170,0.08)");

  const WIN_X = 55, WIN_Y = 28, WIN_W = W - 110;
  const TB_H  = 50;

  roundRect(c, WIN_X, WIN_Y, WIN_W, TB_H, [12, 12, 0, 0]);
  const tbG = c.createLinearGradient(WIN_X, WIN_Y, WIN_X, WIN_Y + TB_H);
  tbG.addColorStop(0, th.tbFrom);
  tbG.addColorStop(1, th.tbTo);
  c.fillStyle = tbG;
  c.fill();
  c.strokeStyle = th.tbBorder;
  c.lineWidth   = 1;
  c.stroke();

  c.beginPath();
  c.arc(WIN_X + 22, WIN_Y + 25, 9, 0, Math.PI * 2);
  c.fillStyle = "#0078d4";
  c.fill();

  c.font      = "bold 23px 'UI'";
  c.fillStyle = th.tbText;
  c.textAlign = "left";
  c.fillText(`System Monitor  \u2014  v${version}`, WIN_X + 46, WIN_Y + 32);

  [
    { x: WIN_X + WIN_W - 138, label: "\u2014", bg: th.btnBg },
    { x: WIN_X + WIN_W - 92,  label: "\u25A1",  bg: th.btnBg },
    { x: WIN_X + WIN_W - 46,  label: "\u2715",  bg: "rgba(196,43,28,0.85)" },
  ].forEach(btn => {
    roundRect(c, btn.x - 8, WIN_Y + 1, 44, TB_H - 2, 0);
    c.fillStyle = btn.bg;
    c.fill();
    c.font      = "19px 'UI'";
    c.fillStyle = btn.label === "\u2715" ? "rgba(255,255,255,0.90)" : th.btnText;
    c.textAlign = "center";
    c.fillText(btn.label, btn.x + 14, WIN_Y + 30);
  });

  const BODY_Y = WIN_Y + TB_H;
  const BODY_H = H - TB_H - WIN_Y - 30 - 72;
  roundRect(c, WIN_X, BODY_Y, WIN_W, BODY_H, [0, 0, 12, 12]);
  c.fillStyle = th.winBody;
  c.fill();
  c.strokeStyle = th.tbBorder;
  c.lineWidth   = 1;
  c.stroke();

  const SB_W = 265;
  roundRect(c, WIN_X + 1, BODY_Y + 1, SB_W, BODY_H - 2, [0, 0, 0, 12]);
  c.fillStyle = th.sidebar;
  c.fill();
  c.strokeStyle = th.sidebarBorder;
  c.lineWidth   = 1;
  c.beginPath();
  c.moveTo(WIN_X + SB_W, BODY_Y + 8);
  c.lineTo(WIN_X + SB_W, BODY_Y + BODY_H - 8);
  c.stroke();

  const pages = ["Overview", "Performance", "Network", "Storage", "Settings"];
  pages.forEach((label, i) => {
    const ny     = BODY_Y + 28 + i * 66;
    const active = label.toLowerCase() === activePage;
    if (active) {
      roundRect(c, WIN_X + 10, ny - 4, SB_W - 20, 54, 8);
      c.fillStyle = th.activeItem;
      c.fill();
      c.fillStyle = "#0078d4";
      c.fillRect(WIN_X + 10, ny + 4, 4, 34);
    }
    c.font      = "bold 21px 'UI'";
    c.fillStyle = active ? th.activeText : th.inactiveText;
    c.textAlign = "left";
    c.fillText(label, WIN_X + 28, ny + 30);
  });

  const { timeStr, dateStr } = getDhakaTime();
  const TASK_Y = H - 70;
  const taskBg = c.createLinearGradient(0, TASK_Y, 0, H);
  taskBg.addColorStop(0, th.taskFrom);
  taskBg.addColorStop(1, th.taskTo);
  c.fillStyle = taskBg;
  c.fillRect(0, TASK_Y, W, 70);
  c.strokeStyle = th.taskBorder;
  c.lineWidth   = 1;
  c.beginPath();
  c.moveTo(0, TASK_Y);
  c.lineTo(W, TASK_Y);
  c.stroke();

  roundRect(c, W / 2 - 230, TASK_Y + 9, 50, 50, 8);
  c.fillStyle = "rgba(0,120,212,0.88)";
  c.fill();
  c.font      = "bold 26px 'UI'";
  c.fillStyle = "#ffffff";
  c.textAlign = "center";
  c.fillText("\u229E", W / 2 - 205, TASK_Y + 43);

  ["\u25A3", "\u25A6", "\u2740", "\u25CE", "\u2699"].forEach((icon, i) => {
    const ix = W / 2 - 120 + i * 58;
    c.font      = "26px 'UI'";
    c.textAlign = "center";
    c.fillStyle = i === 0 ? th.taskText : th.taskIcons;
    c.fillText(icon, ix, TASK_Y + 43);
    if (i === 0) {
      c.beginPath();
      c.arc(ix, TASK_Y + 62, 3, 0, Math.PI * 2);
      c.fillStyle = "#0078d4";
      c.fill();
    }
  });

  c.font      = "bold 21px 'UI'";
  c.fillStyle = th.taskText;
  c.textAlign = "right";
  c.fillText(timeStr, W - 28, TASK_Y + 32);
  c.font      = "19px 'UI'";
  c.fillStyle = th.taskSub;
  c.fillText(dateStr, W - 28, TASK_Y + 56);

  ["\u266B", "\u25D0", "\u2602"].forEach((ico, i) => {
    c.font      = "22px 'UI'";
    c.fillStyle = th.taskIcons;
    c.textAlign = "center";
    c.fillText(ico, W - 165 + i * 34, TASK_Y + 40);
  });

  return { WIN_X, WIN_Y, WIN_W, TB_H, BODY_Y, BODY_H, SB_W };
}

function drawPageHeader(c, MX, MY, WIN_X, WIN_W, title, subtitle, subtitleColor, th) {
  c.font      = "bold 46px 'UI'";
  c.fillStyle = th.pageTitle;
  c.textAlign = "left";
  c.fillText(title, MX, MY + 42);

  if (subtitle) {
    const pillW = c.measureText(subtitle).width + 44;
    roundRect(c, MX + 232, MY + 12, pillW, 34, 17);
    c.fillStyle = subtitleColor + "20";
    c.fill();
    roundRect(c, MX + 232, MY + 12, pillW, 34, 17);
    c.strokeStyle = subtitleColor + "55";
    c.lineWidth   = 1;
    c.stroke();
    c.beginPath();
    c.arc(MX + 250, MY + 29, 6, 0, Math.PI * 2);
    c.fillStyle = subtitleColor;
    c.fill();
    c.font      = "bold 19px 'UI'";
    c.fillStyle = subtitleColor;
    c.textAlign = "left";
    c.fillText(subtitle, MX + 263, MY + 34);
  }

  c.font      = "21px 'UI'";
  c.fillStyle = th.subtitleText;
  c.textAlign = "right";
  c.fillText(getDhakaTime().fullStr, WIN_X + WIN_W - 28, MY + 42);
}

async function renderPage(page, sysData, th) {
  const W = 1800, H = 1160;
  const cv = createCanvas(W, H);
  const c  = cv.getContext("2d");

  const { WIN_X, WIN_Y, WIN_W, TB_H, BODY_Y, BODY_H, SB_W } = drawBase(c, W, H, page, "3.0", th);

  const MX  = WIN_X + SB_W + 28;
  const MY  = BODY_Y + 22;
  const MCW = WIN_W - SB_W - 46;
  const gap = 18;

  const { cpu, ram, disk, network, temp, threads, platform, arch, hostname,
    load, cpuModel, ramGB, usedGB, uptime, ping, pingLabel, pingAccent,
    sysStatus, sysStatusColor, diskTotal, diskUsed, netIPs, themeName } = sysData;

  if (page === "overview") {
    drawPageHeader(c, MX, MY, WIN_X, WIN_W, "Overview", sysStatus, sysStatusColor, th);

    const R1Y = MY + 62, R1H = 120;
    const C4W = (MCW - gap * 3) / 4;
    [
      { label: "Hostname",  value: hostname.substring(0, 16), accent: "#60a5fa", large: false },
      { label: "OS / Arch", value: `${platform}  ${arch}`,   accent: "#a78bfa", large: false },
      { label: "Processor", value: cpuModel,                  accent: "#f59e0b", large: false },
      { label: "Uptime",    value: uptime,                    accent: "#34d399", large: true  },
    ].forEach((card, i) => {
      const cx = MX + i * (C4W + gap);
      acrylicCardT(c, cx, R1Y, C4W, R1H, 12, th);
      c.font      = "19px 'UI'";
      c.fillStyle = th.labelText;
      c.textAlign = "left";
      c.fillText(card.label, cx + 16, R1Y + 28);
      if (card.large) {
        c.font      = "bold 44px 'UI'";
        c.fillStyle = card.accent;
        c.textAlign = "left";
        c.fillText(card.value, cx + 16, R1Y + 88);
      } else {
        c.font = "bold 26px 'UI'";
        const clipped = clipText(c, card.value, C4W - 28);
        c.fillStyle = card.accent;
        c.textAlign = "left";
        c.fillText(clipped, cx + 16, R1Y + 78);
      }
    });

    const R2Y = R1Y + R1H + 18;
    const PERF_W = Math.floor((MCW * 0.60 - gap * 2) / 3);
    const PERF_H = 235;
    [
      { label: "CPU",    value: cpu,  sub: `${threads} Cores \u00b7 Load ${load}`, c1: "#0078d4", c2: "#60a5fa" },
      { label: "Memory", value: ram,  sub: `${usedGB} GB / ${ramGB} GB`,           c1: "#7c3aed", c2: "#a78bfa" },
      { label: "Disk",   value: disk, sub: `Usage ${disk}% \u00b7 Active`,          c1: "#db2777", c2: "#f472b6" },
    ].forEach((card, i) => {
      const px = MX + i * (PERF_W + gap);
      acrylicCardT(c, px, R2Y, PERF_W, PERF_H, 14, th);
      const ringCX = px + PERF_W / 2, ringCY = R2Y + 102, ringR = 68;
      c.beginPath(); c.arc(ringCX, ringCY, ringR, Math.PI * 0.75, Math.PI * 2.25);
      c.strokeStyle = th.ringTrack; c.lineWidth = 11; c.lineCap = "round"; c.stroke();
      const arcEnd = Math.PI * 0.75 + (card.value / 100) * Math.PI * 1.5;
      const arcG = c.createLinearGradient(px, R2Y, px + PERF_W, R2Y + PERF_H);
      arcG.addColorStop(0, card.c1); arcG.addColorStop(1, card.c2);
      c.beginPath(); c.arc(ringCX, ringCY, ringR, Math.PI * 0.75, arcEnd);
      c.strokeStyle = arcG; c.lineWidth = 11; c.lineCap = "round"; c.stroke();
      c.save(); c.shadowColor = card.c1; c.shadowBlur = 16;
      c.beginPath(); c.arc(ringCX, ringCY, ringR, arcEnd - 0.04, arcEnd);
      c.strokeStyle = card.c2; c.lineWidth = 11; c.stroke(); c.restore();
      c.font = "bold 44px 'UI'"; c.fillStyle = th.ringValue; c.textAlign = "center";
      c.fillText(`${card.value}%`, ringCX, ringCY + 15);
      c.font = "bold 26px 'UI'"; c.fillStyle = card.c2; c.fillText(card.label, ringCX, R2Y + 192);
      c.font = "19px 'UI'"; c.fillStyle = th.labelText; c.fillText(card.sub, ringCX, R2Y + 218);
    });

    const DPX = MX + PERF_W * 3 + gap * 3;
    const DPW = MCW - PERF_W * 3 - gap * 3;
    acrylicCardT(c, DPX, R2Y, DPW, PERF_H, 14, th);
    c.font = "bold 24px 'UI'"; c.fillStyle = th.labelText4; c.textAlign = "left";
    c.fillText("System Details", DPX + 18, R2Y + 34);
    [
      { k: "Node.js",  v: process.version },
      { k: "Threads",  v: `${threads}`    },
      { k: "Temp",     v: `${temp}\u00b0C` },
      { k: "Net IFs",  v: `${network}`    },
      { k: "Load Avg", v: `${load}`       },
    ].forEach((row, i) => {
      const dy = R2Y + 64 + i * 34;
      if (i % 2 === 0) {
        roundRect(c, DPX + 10, dy - 16, DPW - 20, 30, 6);
        c.fillStyle = th.rowAlt; c.fill();
      }
      c.font = "19px 'UI'"; c.fillStyle = th.labelText2; c.textAlign = "left";
      c.fillText(row.k, DPX + 20, dy + 5);
      c.font = "bold 19px 'UI'"; c.fillStyle = th.detailValue; c.textAlign = "right";
      c.fillText(row.v, DPX + DPW - 18, dy + 5);
    });

    const R3Y = R2Y + PERF_H + 18, R3H = 160;
    acrylicCardT(c, MX, R3Y, MCW, R3H, 14, th);
    c.font = "bold 22px 'UI'"; c.fillStyle = th.labelText3; c.textAlign = "left";
    c.fillText("Resource Usage", MX + 18, R3Y + 30);
    const barW = (MCW - 90) / 3;
    [
      { label: "CPU Usage",    value: cpu,  c1: "#0078d4", c2: "#60a5fa" },
      { label: "Memory Usage", value: ram,  c1: "#7c3aed", c2: "#a78bfa" },
      { label: "Disk Usage",   value: disk, c1: "#db2777", c2: "#f472b6" },
    ].forEach((bar, i) => {
      win11BarT(c, MX + 18 + i * (barW + 27), R3Y + 86, barW, bar.value, bar.c1, bar.c2, bar.label, bar.value, th);
    });

    const R4Y = R3Y + R3H + 18, R4H = 110;
    const T4W = (MCW - gap * 3) / 4;
    [
      { label: "Response", value: `${ping}ms`,       sub: pingLabel,                                          accent: pingAccent },
      { label: "Hostname", value: hostname.substring(0, 14), sub: `${platform} \u00b7 ${arch}`,              accent: "#60a5fa"  },
      { label: "CPU Temp", value: `${temp}\u00b0C`,  sub: temp > 70 ? "High" : temp > 50 ? "Warm" : "Cool",  accent: temp > 70 ? "#f87171" : temp > 50 ? "#fb923c" : "#34d399" },
      { label: "Load Avg", value: `${load}`,         sub: `${threads} threads`,                               accent: "#a78bfa"  },
    ].forEach((tile, i) => {
      const tx = MX + i * (T4W + gap);
      acrylicCardT(c, tx, R4Y, T4W, R4H, 14, th);
      c.font = "19px 'UI'"; c.fillStyle = th.labelText; c.textAlign = "left";
      c.fillText(tile.label, tx + 18, R4Y + 30);
      c.font = "bold 32px 'UI'"; c.fillStyle = tile.accent;
      c.fillText(tile.value, tx + 18, R4Y + 72);
      c.font = "18px 'UI'"; c.fillStyle = th.labelText2;
      c.fillText(tile.sub, tx + 18, R4Y + 96);
    });

  } else if (page === "performance") {
    drawPageHeader(c, MX, MY, WIN_X, WIN_W, "Performance", "Real-time stats", "#60a5fa", th);

    const cpuColor  = cpu > 80 ? "#f87171" : cpu > 50 ? "#fb923c" : "#60a5fa";
    const ramColor  = ram > 80 ? "#f87171" : ram > 50 ? "#fb923c" : "#a78bfa";
    const diskColor = disk > 80 ? "#f87171" : disk > 50 ? "#fb923c" : "#f472b6";
    const tempColor = temp > 70 ? "#f87171" : temp > 50 ? "#fb923c" : "#34d399";

    const bigRings = [
      { label: "CPU Usage",    value: cpu,  sub: `${threads} cores \u00b7 ${load} avg`, c1: "#0078d4", c2: cpuColor  },
      { label: "Memory",       value: ram,  sub: `${usedGB} / ${ramGB} GB`,              c1: "#7c3aed", c2: ramColor  },
      { label: "Disk",         value: disk, sub: `${diskUsed} / ${diskTotal} GB`,        c1: "#db2777", c2: diskColor },
      { label: "Temperature",  value: Math.min(temp, 99), sub: `${temp}\u00b0C \u00b7 ${temp > 70 ? "Hot" : temp > 50 ? "Warm" : "Cool"}`, c1: "#b45309", c2: tempColor },
    ];

    const RW = (MCW - gap * 3) / 4;
    const RH = 320;
    const RY = MY + 62;
    bigRings.forEach((card, i) => {
      const px = MX + i * (RW + gap);
      acrylicCardT(c, px, RY, RW, RH, 14, th);
      const ringCX = px + RW / 2, ringCY = RY + 130, ringR = 90;
      c.beginPath(); c.arc(ringCX, ringCY, ringR, Math.PI * 0.75, Math.PI * 2.25);
      c.strokeStyle = th.ringTrack; c.lineWidth = 14; c.lineCap = "round"; c.stroke();
      const arcEnd = Math.PI * 0.75 + (card.value / 100) * Math.PI * 1.5;
      const arcG = c.createLinearGradient(px, RY, px + RW, RY + RH);
      arcG.addColorStop(0, card.c1); arcG.addColorStop(1, card.c2);
      c.beginPath(); c.arc(ringCX, ringCY, ringR, Math.PI * 0.75, arcEnd);
      c.strokeStyle = arcG; c.lineWidth = 14; c.lineCap = "round"; c.stroke();
      c.save(); c.shadowColor = card.c1; c.shadowBlur = 20;
      c.beginPath(); c.arc(ringCX, ringCY, ringR, arcEnd - 0.04, arcEnd);
      c.strokeStyle = card.c2; c.lineWidth = 14; c.stroke(); c.restore();
      c.font = "bold 56px 'UI'"; c.fillStyle = th.ringValue; c.textAlign = "center";
      c.fillText(`${card.value}%`, ringCX, ringCY + 20);
      c.font = "bold 28px 'UI'"; c.fillStyle = card.c2; c.fillText(card.label, ringCX, RY + 262);
      c.font = "20px 'UI'"; c.fillStyle = th.labelText; c.fillText(card.sub, ringCX, RY + 294);
    });

    const R2Y = RY + RH + 22, R2H = 200;
    acrylicCardT(c, MX, R2Y, MCW, R2H, 14, th);
    c.font = "bold 24px 'UI'"; c.fillStyle = th.labelText4; c.textAlign = "left";
    c.fillText("Detailed Breakdown", MX + 20, R2Y + 36);
    const bW = (MCW - 80) / 3;
    [
      { label: "CPU Usage",    value: cpu,  c1: "#0078d4", c2: cpuColor  },
      { label: "Memory Usage", value: ram,  c1: "#7c3aed", c2: ramColor  },
      { label: "Disk Usage",   value: disk, c1: "#db2777", c2: diskColor },
    ].forEach((bar, i) => {
      win11BarT(c, MX + 18 + i * (bW + 22), R2Y + 100, bW, bar.value, bar.c1, bar.c2, bar.label, bar.value, th);
    });

    const R3Y = R2Y + R2H + 18, R3H = 130;
    acrylicCardT(c, MX, R3Y, MCW, R3H, 14, th);
    c.font = "bold 24px 'UI'"; c.fillStyle = th.labelText4; c.textAlign = "left";
    c.fillText("Process Info", MX + 20, R3Y + 36);
    const procInfo = [
      { k: "CPU Model",     v: clipText(c, cpuModel, 600)  },
      { k: "Node.js",       v: process.version             },
      { k: "Platform",      v: `${platform} ${arch}`       },
      { k: "Load Average",  v: `${os.loadavg().map(l => l.toFixed(2)).join("  ")}` },
    ];
    procInfo.forEach((row, i) => {
      const dx = MX + 20 + i * ((MCW - 40) / 4);
      acrylicCardT(c, dx, R3Y + 50, (MCW - 40) / 4 - 10, 64, 8, th);
      c.font = "18px 'UI'"; c.fillStyle = th.labelText2; c.textAlign = "left";
      c.fillText(row.k, dx + 12, R3Y + 72);
      c.font = "bold 20px 'UI'"; c.fillStyle = th.detailValue;
      c.fillText(row.v, dx + 12, R3Y + 100);
    });

  } else if (page === "network") {
    drawPageHeader(c, MX, MY, WIN_X, WIN_W, "Network", `${network} Interface${network !== 1 ? "s" : ""} Active`, "#4ade80", th);

    const R1Y = MY + 62, R1H = 130;
    const C3W = (MCW - gap * 2) / 3;
    [
      { label: "Interfaces",  value: `${network}`,          sub: "Active IPv4",    accent: "#4ade80"  },
      { label: "Hostname",    value: hostname.substring(0, 18), sub: platform,     accent: "#60a5fa"  },
      { label: "Response",    value: `${ping}ms`,            sub: pingLabel,        accent: pingAccent },
    ].forEach((card, i) => {
      const cx = MX + i * (C3W + gap);
      acrylicCardT(c, cx, R1Y, C3W, R1H, 12, th);
      c.font = "19px 'UI'"; c.fillStyle = th.labelText; c.textAlign = "left";
      c.fillText(card.label, cx + 16, R1Y + 30);
      c.font = "bold 40px 'UI'"; c.fillStyle = card.accent;
      c.fillText(card.value, cx + 16, R1Y + 84);
      c.font = "18px 'UI'"; c.fillStyle = th.labelText2;
      c.fillText(card.sub, cx + 16, R1Y + 112);
    });

    const R2Y = R1Y + R1H + 18;
    acrylicCardT(c, MX, R2Y, MCW, 380, 14, th);
    c.font = "bold 26px 'UI'"; c.fillStyle = th.labelText4; c.textAlign = "left";
    c.fillText("Network Interfaces", MX + 20, R2Y + 38);

    const IPs = netIPs.length > 0 ? netIPs : [{ name: "lo", address: "127.0.0.1", netmask: "255.0.0.0" }];
    IPs.forEach((iface, i) => {
      const iy = R2Y + 60 + i * 80;
      acrylicCardT(c, MX + 10, iy, MCW - 20, 64, 8, th);
      c.font = "bold 22px 'UI'"; c.fillStyle = "#60a5fa"; c.textAlign = "left";
      c.fillText(iface.name, MX + 28, iy + 26);
      c.font = "20px 'UI'"; c.fillStyle = th.networkIP;
      c.fillText(`IP: ${iface.address}`, MX + 200, iy + 26);
      c.font = "18px 'UI'"; c.fillStyle = th.networkMask;
      c.fillText(`Netmask: ${iface.netmask}`, MX + 560, iy + 26);
      c.beginPath(); c.arc(MX + MCW - 40, iy + 20, 7, 0, Math.PI * 2);
      c.fillStyle = "#4ade80"; c.fill();
      c.font = "17px 'UI'"; c.fillStyle = "#4ade80"; c.textAlign = "right";
      c.fillText("Connected", MX + MCW - 54, iy + 26);
    });

    const R3Y = R2Y + 398, R3H = 200;
    acrylicCardT(c, MX, R3Y, MCW, R3H, 14, th);
    c.font = "bold 24px 'UI'"; c.fillStyle = th.labelText4; c.textAlign = "left";
    c.fillText("System Network Info", MX + 20, R3Y + 36);
    const netDetails = [
      { k: "Hostname",     v: os.hostname()          },
      { k: "Platform",     v: `${platform} ${arch}`  },
      { k: "DNS Resolve",  v: "Active"               },
      { k: "Ping",         v: `${ping}ms \u00b7 ${pingLabel}` },
    ];
    netDetails.forEach((row, i) => {
      const dx = MX + 20 + i * ((MCW - 40) / 4);
      acrylicCardT(c, dx, R3Y + 52, (MCW - 40) / 4 - 10, 120, 8, th);
      c.font = "19px 'UI'"; c.fillStyle = th.labelText2; c.textAlign = "left";
      c.fillText(row.k, dx + 14, R3Y + 82);
      c.font = "bold 22px 'UI'"; c.fillStyle = "#60a5fa";
      c.fillText(clipText(c, row.v, (MCW - 40) / 4 - 30), dx + 14, R3Y + 115);
    });

  } else if (page === "storage") {
    drawPageHeader(c, MX, MY, WIN_X, WIN_W, "Storage", "Disk Overview", "#f472b6", th);

    const R1Y = MY + 62, R1H = 130;
    const C3W = (MCW - gap * 2) / 3;
    [
      { label: "Total Space",  value: `${diskTotal} GB`, sub: "Root filesystem", accent: "#f472b6" },
      { label: "Used Space",   value: `${diskUsed} GB`,  sub: `${disk}% used`,   accent: disk > 80 ? "#f87171" : "#fb923c" },
      { label: "Free Space",   value: `${(parseFloat(diskTotal) - parseFloat(diskUsed)).toFixed(1)} GB`, sub: `${100 - disk}% free`, accent: "#34d399" },
    ].forEach((card, i) => {
      const cx = MX + i * (C3W + gap);
      acrylicCardT(c, cx, R1Y, C3W, R1H, 12, th);
      c.font = "19px 'UI'"; c.fillStyle = th.labelText; c.textAlign = "left";
      c.fillText(card.label, cx + 16, R1Y + 30);
      c.font = "bold 40px 'UI'"; c.fillStyle = card.accent;
      c.fillText(card.value, cx + 16, R1Y + 84);
      c.font = "18px 'UI'"; c.fillStyle = th.labelText2;
      c.fillText(card.sub, cx + 16, R1Y + 112);
    });

    const R2Y = R1Y + R1H + 18, R2H = 160;
    acrylicCardT(c, MX, R2Y, MCW, R2H, 14, th);
    c.font = "bold 22px 'UI'"; c.fillStyle = th.labelText3; c.textAlign = "left";
    c.fillText("Disk Usage", MX + 18, R2Y + 34);
    win11BarT(c, MX + 18, R2Y + 90, MCW - 36, disk, "#db2777", "#f472b6", "Root  /", disk, th);

    const R3Y = R2Y + R2H + 18, R3H = 260;
    acrylicCardT(c, MX, R3Y, MCW, R3H, 14, th);
    c.font = "bold 24px 'UI'"; c.fillStyle = th.labelText4; c.textAlign = "left";
    c.fillText("Filesystem Details", MX + 20, R3Y + 38);

    const diskRows = [
      { k: "Filesystem",    v: "/"                     },
      { k: "Total",         v: `${diskTotal} GB`       },
      { k: "Used",          v: `${diskUsed} GB`        },
      { k: "Available",     v: `${(parseFloat(diskTotal) - parseFloat(diskUsed)).toFixed(1)} GB` },
      { k: "Use%",          v: `${disk}%`              },
      { k: "Temp",          v: `${temp}\u00b0C`        },
    ];
    diskRows.forEach((row, i) => {
      const dy = R3Y + 68 + i * 32;
      if (i % 2 === 0) {
        roundRect(c, MX + 10, dy - 14, MCW - 20, 28, 6);
        c.fillStyle = th.rowAlt; c.fill();
      }
      c.font = "20px 'UI'"; c.fillStyle = th.labelText; c.textAlign = "left";
      c.fillText(row.k, MX + 24, dy + 5);
      c.font = "bold 20px 'UI'"; c.fillStyle = "#f472b6"; c.textAlign = "right";
      c.fillText(row.v, MX + MCW - 24, dy + 5);
    });

  } else if (page === "settings") {
    drawPageHeader(c, MX, MY, WIN_X, WIN_W, "Settings", "System Configuration", "#a78bfa", th);

    const items = [
      { label: "Bot Version",       value: "3.0",                        accent: "#60a5fa" },
      { label: "Node.js Version",   value: process.version,              accent: "#4ade80" },
      { label: "Platform",          value: `${platform} ${arch}`,        accent: "#a78bfa" },
      { label: "CPU Model",         value: cpuModel,                     accent: "#f59e0b" },
      { label: "Hostname",          value: os.hostname(),                 accent: "#60a5fa" },
      { label: "Total RAM",         value: `${ramGB} GB`,                accent: "#a78bfa" },
      { label: "CPU Threads",       value: `${threads}`,                 accent: "#f472b6" },
      { label: "Timezone",          value: "Asia/Dhaka (BDT)",           accent: "#34d399" },
      { label: "Uptime",            value: uptime,                       accent: "#34d399" },
      { label: "Load Average",      value: `${os.loadavg().map(l => l.toFixed(2)).join("  ")}`, accent: "#fb923c" },
    ];

    const R1Y = MY + 62;
    const IW  = (MCW - gap) / 2;
    items.forEach((item, i) => {
      const col = i % 2, row = Math.floor(i / 2);
      const ix = MX + col * (IW + gap);
      const iy = R1Y + row * 90;
      acrylicCardT(c, ix, iy, IW, 74, 10, th);
      c.font = "19px 'UI'"; c.fillStyle = th.labelText2; c.textAlign = "left";
      c.fillText(item.label, ix + 20, iy + 28);
      c.font = "bold 26px 'UI'"; c.fillStyle = item.accent;
      c.fillText(clipText(c, item.value, IW - 40), ix + 20, iy + 58);
    });

    const R2Y = R1Y + Math.ceil(items.length / 2) * 90 + 18;
    const isDark = themeName === "dark";

    // Appearance card with theme toggle
    const TH_H = 110;
    acrylicCardT(c, MX, R2Y, MCW, TH_H, 14, th);
    c.font = "bold 22px 'UI'"; c.fillStyle = th.labelText4; c.textAlign = "left";
    c.fillText("Appearance", MX + 20, R2Y + 34);

    const BTN_W = 220, BTN_H = 48, BTN_Y = R2Y + 50;

    // Dark button
    const darkX = MX + 20;
    roundRect(c, darkX, BTN_Y, BTN_W, BTN_H, 10);
    c.fillStyle = isDark ? th.themeActiveBg : th.themeToggleBg;
    c.fill();
    roundRect(c, darkX, BTN_Y, BTN_W, BTN_H, 10);
    c.strokeStyle = isDark ? th.themeActiveBg : th.themeToggleBorder;
    c.lineWidth = 1.5; c.stroke();
    c.font = "bold 22px 'UI'";
    c.fillStyle = isDark ? th.themeActiveText : th.themeInactiveText;
    c.textAlign = "center";
    c.fillText("\uD83C\uDF19  Dark", darkX + BTN_W / 2, BTN_Y + 31);

    // Light button
    const lightX = MX + 20 + BTN_W + 16;
    roundRect(c, lightX, BTN_Y, BTN_W, BTN_H, 10);
    c.fillStyle = !isDark ? th.themeActiveBg : th.themeToggleBg;
    c.fill();
    roundRect(c, lightX, BTN_Y, BTN_W, BTN_H, 10);
    c.strokeStyle = !isDark ? th.themeActiveBg : th.themeToggleBorder;
    c.lineWidth = 1.5; c.stroke();
    c.font = "bold 22px 'UI'";
    c.fillStyle = !isDark ? th.themeActiveText : th.themeInactiveText;
    c.textAlign = "center";
    c.fillText("\u2600\uFE0F  Light", lightX + BTN_W / 2, BTN_Y + 31);

    // hint text right side
    c.font = "19px 'UI'"; c.fillStyle = th.labelText2; c.textAlign = "left";
    c.fillText(
      `Current: ${isDark ? "Dark" : "Light"}  \u2014  Reply "dark" or "light" to switch`,
      MX + 20 + BTN_W * 2 + 40, BTN_Y + 31
    );

    // Commands card
    const R3Y = R2Y + TH_H + 18;
    acrylicCardT(c, MX, R3Y, MCW, 100, 14, th);
    c.font = "bold 22px 'UI'"; c.fillStyle = th.labelText3; c.textAlign = "left";
    c.fillText("Commands", MX + 20, R3Y + 34);
    c.font = "20px 'UI'"; c.fillStyle = th.cmdText;
    c.fillText("up \u00b7 uptime \u00b7 status \u00b7 sysinfo  \u2014  Reply: performance  network  storage  settings  dark  light", MX + 20, R3Y + 70);
  }

  return cv;
}

module.exports = {
  config: {
    name: "up",
    aliases: ["uptime", "status", "sysinfo"],
    version: "3.0",
    author: "MOHAMMAD AKASH",
    role: 0,
    category: "system",
    shortDescription: "Display system status in Windows 11 style"
  },

  onStart: async function ({ message, api, event }) {
    try {
      await sendPage("overview", message, api, event);
    } catch (error) {
      console.error("WIN11 MONITOR ERROR:", error);
      message.reply("\u274C System monitor failed to generate.");
    }
  },

  onChat: async function ({ event, api }) {
    if (!event.body) return;
    const body = event.body.toLowerCase().trim();

    if (body === "hack") {
      api.sendMessage("\uD83D\uDD12 Access Denied \u2014 Insufficient Privileges.", event.threadID);
      return;
    }

    if (body === "dark" || body === "light") {
      themeMap[event.threadID] = body;
      const fakeMsg = {
        reply: (data) => api.sendMessage(data, event.threadID, null, event.messageID)
      };
      try {
        await sendPage("settings", fakeMsg, api, event);
      } catch (error) {
        console.error("WIN11 THEME ERROR:", error);
        api.sendMessage(`\u2705 Theme switched to ${body}.`, event.threadID);
      }
      return;
    }

    const pages = ["performance", "network", "storage", "settings", "overview"];
    if (pages.includes(body) && event.messageReply) {
      try {
        const fakeMsg = {
          reply: (data) => api.sendMessage(data, event.threadID, null, event.messageID)
        };
        await sendPage(body, fakeMsg, api, event);
      } catch (error) {
        console.error("WIN11 PAGE ERROR:", error);
        api.sendMessage("\u274C Failed to render page.", event.threadID);
      }
    }
  }
};

async function sendPage(page, message, api, event) {
  const start = Date.now();
  const themeName = themeMap[event.threadID] || "dark";
  const th = THEMES[themeName];

  const cpu      = Math.min(getCPU(), 99);
  const total    = os.totalmem();
  const used     = total - os.freemem();
  const ram      = Math.min(Math.round((used / total) * 100), 99);
  const disk     = Math.min(getDisk(), 99);
  const network  = Math.min(getNetwork(), 9);
  const temp     = getTemperature();
  const threads  = os.cpus().length;
  const platform = os.platform().toUpperCase();
  const arch     = os.arch();
  const hostname = os.hostname();
  const load     = Math.min(parseFloat(os.loadavg()[0].toFixed(2)), 9.99);
  const cpuModel = os.cpus()[0].model.split("@")[0].trim();
  const ramGB    = (os.totalmem()  / 1024 / 1024 / 1024).toFixed(1);
  const usedGB   = (used           / 1024 / 1024 / 1024).toFixed(1);
  const diskTotal = getDiskTotal();
  const diskUsed  = getDiskUsed();
  const netIPs    = getNetworkIPs();

  const sec = process.uptime();
  const d   = Math.floor(sec / 86400);
  const h   = Math.floor((sec % 86400) / 3600);
  const m   = Math.floor((sec % 3600) / 60);
  const s   = Math.floor(sec % 60);
  const uptime = d > 0 ? `${d}d ${h}h ${m}m` : `${h}h ${m}m ${s}s`;
  const ping   = Math.min(Date.now() - start, 9999);

  let pingLabel = "Excellent", pingAccent = "#4ade80";
  if (ping > 200)  { pingLabel = "Good";  pingAccent = "#facc15"; }
  if (ping > 500)  { pingLabel = "Slow";  pingAccent = "#fb923c"; }
  if (ping > 1000) { pingLabel = "Poor";  pingAccent = "#f87171"; }

  const sysStatus      = ping < 100 ? "All systems running" : ping < 300 ? "System stable" : "Lag detected";
  const sysStatusColor = ping < 100 ? "#4ade80" : ping < 300 ? "#facc15" : "#f87171";

  const sysData = {
    cpu, ram, disk, network, temp, threads, platform, arch, hostname,
    load, cpuModel, ramGB, usedGB, uptime, ping, pingLabel, pingAccent,
    sysStatus, sysStatusColor, diskTotal, diskUsed, netIPs, themeName
  };

  const cv = await renderPage(page, sysData, th);
  const timestamp = Date.now();
  const cacheDir = path.join(__dirname, "cache");
  const file = path.join(cacheDir, `win11_${page}_${timestamp}.png`);
  fs.mkdirSync(cacheDir, { recursive: true });
  fs.writeFileSync(file, cv.toBuffer("image/png"));

  await message.reply({ attachment: fs.createReadStream(file) });
  setTimeout(() => { if (fs.existsSync(file)) fs.unlinkSync(file); }, 15000);
}

View raw file