// ============================================================ // CLAWSTIN — Dashboard Widget for Scriptable (iPhone) // Version 3.1 // ============================================================ // CONFIG — edit this line: const WIDGET_URL = "https://clawstin.org/vital-widget.json"; // ============================================================ const C = { bg: new Color("#0d0d0d"), label: new Color("#888888"), white: new Color("#f0f0f0"), green: new Color("#34c759"), yellow: new Color("#ffd60a"), orange: new Color("#ff9f0a"), red: new Color("#ff3b30"), blue: new Color("#0a84ff"), purple: new Color("#bf5af2"), gold: new Color("#f5c842"), silver: new Color("#aaaaaa"), }; function gaugeColor(pct) { if (pct > 50) return C.green; if (pct > 20) return C.yellow; if (pct > 10) return C.orange; return C.red; } function fmt(val, decimals, prefix, suffix) { if (val == null) return "---"; return (prefix || "") + val.toFixed(decimals) + (suffix || ""); } function buildTicker(tickerStr) { if (!tickerStr || tickerStr.length === 0) return ""; const padded = tickerStr + " | "; const len = padded.length; const offset = (Math.floor(Date.now() / 60000) * 2) % len; return padded.slice(offset) + padded.slice(0, offset); } async function loadData() { try { const req = new Request(WIDGET_URL); req.timeoutInterval = 8; return await req.loadJSON(); } catch (e) { return null; } } function addRow(w, label, value, valueColor) { const r = w.addStack(); r.layoutHorizontally(); r.centerAlignContent(); const val = r.addText(value); val.textColor = valueColor || C.white; val.font = Font.boldSystemFont(13); r.addSpacer(); const lbl = r.addText(label); lbl.textColor = C.label; lbl.font = Font.systemFont(11); w.addSpacer(3); } async function buildWidget(data) { const w = new ListWidget(); w.backgroundColor = C.bg; w.setPadding(12, 14, 10, 14); w.url = "scriptable:///run/" + encodeURIComponent(Script.name()); // Title const title = w.addText("CLAWSTIN"); title.textColor = C.white; title.font = Font.boldSystemFont(14); w.addSpacer(6); if (!data) { const err = w.addText("No data -- check connection"); err.textColor = C.red; err.font = Font.systemFont(11); w.addSpacer(); return w; } const balPct = data.balance_gauge_pct ?? data.gauge_pct ?? 50; addRow(w, "Anthropic Balance", fmt(data.remaining_balance, 2, "$"), gaugeColor(balPct)); addRow(w, "Token $ Last Hour", fmt(data.last_hour_spend ?? data.cost_per_hour, 2, "$"), C.white); addRow(w, "Token $ Avg (24h)", fmt(data.avg_24h_hourly ?? data.cost_per_hour, 2, "$") + "/hr", C.label); addRow(w, "Gold /oz", fmt(data.gold_price, 2, "$"), C.gold); addRow(w, "Silver /oz", fmt(data.silver_price, 2, "$"), C.silver); addRow(w, "XMR", fmt(data.xmr_usd, 2, "$"), C.purple); w.addSpacer(4); // Separator const sep = w.addStack(); sep.backgroundColor = new Color("#333333"); sep.size = new Size(0, 1); w.addSpacer(3); // Ticker const ticker = w.addText(buildTicker(data.ticker || "")); ticker.textColor = new Color("#555555"); ticker.font = Font.systemFont(9); ticker.lineLimit = 1; return w; } const data = await loadData(); const widget = await buildWidget(data); if (config.runsInWidget) { Script.setWidget(widget); } else { await widget.presentMedium(); } Script.complete();