import * as vscode from "vscode"; const SD_SUPABASE_URL = "https://urcqtpklpfyvizcgcsia.supabase.co"; const SD_SUPABASE_KEY = "sb_publishable_v_dOX1JVgEm_vlT-Qr5lsw_EQHc-av-"; const CMD_OPEN_CONFIGURE = "openConfigure"; const CMD_DOWNLOAD_CSV = "downloadCsv"; const MONTHLY_FEE_USD = 9; let statusBar: vscode.StatusBarItem; let pollInterval: ReturnType | undefined; let sidebarProvider: PaymentsDashboardProvider | undefined; let activePanel: vscode.WebviewPanel | undefined; let csvDownloadPending = false; // ── i18n ────────────────────────────────────────────────────────────────────── const I18N: Record> = { en: { title: "⚡ ShinyDapps Payments", totalPayments: "Total Payments", totalSats: "Total Sats", lightningAddr: "Lightning Address", notSet: "Not configured", when: "When", endpoint: "Endpoint", sats: "Sats", noPayments: "No payments yet — share your API!", loading: "Loading…", setupTitle: "⚡ Quick Setup", setupStep1: "1. Open Command Palette (Ctrl+Shift+P)", setupStep2: "2. Run: ShinyDapps: Configure Lightning Address", setupStep3: "Or add manually to VS Code settings:", setupHint: "Get a free Lightning address at dashboard.blink.sv", chartTitle: "Sats earned", revenue: "Revenue (USD est.)", btcPrice: "BTC Price", proFeatures: "Full history · 30D/1Y/ALL · CSV export · Pay in Bitcoin", upgradeBtn: "Upgrade with Bitcoin →", emptySub: "Your first satoshi is waiting", connectionError: "Connection Error", retryBtn: "Retry", cfgBtn: "Set Lightning Address", }, pt: { title: "⚡ Pagamentos ShinyDapps", totalPayments: "Total de Pagamentos", totalSats: "Total de Sats", lightningAddr: "Endereço Lightning", notSet: "Não configurado", when: "Quando", endpoint: "Endpoint", sats: "Sats", noPayments: "Nenhum pagamento ainda — compartilhe sua API!", loading: "Carregando…", setupTitle: "⚡ Configuração Rápida", setupStep1: "1. Abra o Command Palette (Ctrl+Shift+P)", setupStep2: "2. Execute: ShinyDapps: Configure Lightning Address", setupStep3: "Ou adicione manualmente nas configurações do VS Code:", setupHint: "Obtenha um endereço Lightning gratuito em dashboard.blink.sv", chartTitle: "Sats ganhos", revenue: "Receita (USD est.)", btcPrice: "Preço BTC", proFeatures: "Histórico completo · 30D/1Y/ALL · CSV · Pagar em Bitcoin", upgradeBtn: "Assinar com Bitcoin →", emptySub: "Seu primeiro satoshi está esperando", connectionError: "Erro de conexão", retryBtn: "Tentar novamente", cfgBtn: "Definir Lightning Address", }, es: { title: "⚡ Pagos ShinyDapps", totalPayments: "Total de Pagos", totalSats: "Total de Sats", lightningAddr: "Dirección Lightning", notSet: "No configurado", when: "Cuándo", endpoint: "Endpoint", sats: "Sats", noPayments: "Sin pagos aún — ¡comparte tu API!", loading: "Cargando…", setupTitle: "⚡ Configuración Rápida", setupStep1: "1. Abre el Command Palette (Ctrl+Shift+P)", setupStep2: "2. Ejecuta: ShinyDapps: Configure Lightning Address", setupStep3: "O configura manualmente en ajustes de VS Code:", setupHint: "Obtén una dirección Lightning gratuita en dashboard.blink.sv", chartTitle: "Sats ganados", revenue: "Ingresos (USD est.)", btcPrice: "Precio BTC", proFeatures: "Historial completo · 30D/1Y/ALL · CSV · Pagar en Bitcoin", upgradeBtn: "Suscribirse con Bitcoin →", emptySub: "Tu primer satoshi te espera", connectionError: "Error de conexión", retryBtn: "Reintentar", cfgBtn: "Configurar Lightning Address", }, zh: { title: "⚡ ShinyDapps 收款", totalPayments: "总付款次数", totalSats: "总 Sats", lightningAddr: "闪电地址", notSet: "未配置", when: "时间", endpoint: "接口", sats: "Sats", noPayments: "暂无付款 — 分享您的 API!", loading: "加载中…", setupTitle: "⚡ 快速设置", setupStep1: "1. 打开命令面板 (Ctrl+Shift+P)", setupStep2: "2. 运行: ShinyDapps: Configure Lightning Address", setupStep3: "或在 VS Code 设置中手动配置:", setupHint: "在 dashboard.blink.sv 获取免费闪电地址", chartTitle: "收入 Sats", revenue: "收入 (USD 估)", btcPrice: "BTC 价格", proFeatures: "完整历史 · 30D/1Y/ALL · CSV导出 · 用Bitcoin支付", upgradeBtn: "用 Bitcoin 升级 →", emptySub: "你的第一个 satoshi 正在等待", connectionError: "连接错误", retryBtn: "重试", cfgBtn: "设置 Lightning 地址", }, ja: { title: "⚡ ShinyDapps 支払い", totalPayments: "総支払い数", totalSats: "総 Sats", lightningAddr: "Lightning アドレス", notSet: "未設定", when: "日時", endpoint: "エンドポイント", sats: "Sats", noPayments: "まだ支払いなし — APIをシェアしよう!", loading: "読み込み中…", setupTitle: "⚡ クイック設定", setupStep1: "1. コマンドパレットを開く (Ctrl+Shift+P)", setupStep2: "2. 実行: ShinyDapps: Configure Lightning Address", setupStep3: "または VS Code 設定で手動設定:", setupHint: "dashboard.blink.sv で無料の Lightning アドレスを取得", chartTitle: "獲得 Sats", revenue: "収益 (USD 推定)", btcPrice: "BTC 価格", proFeatures: "全履歴 · 30D/1Y/ALL · CSVエクスポート · Bitcoinで支払い", upgradeBtn: "Bitcoinでアップグレード →", emptySub: "最初のsatをお待ちしています", connectionError: "接続エラー", retryBtn: "再試行", cfgBtn: "Lightning Addressを設定", }, fr: { title: "⚡ Paiements ShinyDapps", totalPayments: "Total Paiements", totalSats: "Total Sats", lightningAddr: "Adresse Lightning", notSet: "Non configuré", when: "Quand", endpoint: "Endpoint", sats: "Sats", noPayments: "Aucun paiement — partagez votre API !", loading: "Chargement…", setupTitle: "⚡ Configuration Rapide", setupStep1: "1. Ouvrez la palette de commandes (Ctrl+Shift+P)", setupStep2: "2. Exécutez : ShinyDapps: Configure Lightning Address", setupStep3: "Ou configurez manuellement dans les paramètres VS Code :", setupHint: "Obtenez une adresse Lightning gratuite sur dashboard.blink.sv", chartTitle: "Sats gagnés", revenue: "Revenus (USD est.)", btcPrice: "Prix BTC", proFeatures: "Historique complet · 30D/1Y/ALL · Export CSV · Payer en Bitcoin", upgradeBtn: "Améliorer avec Bitcoin →", emptySub: "Votre premier satoshi vous attend", connectionError: "Erreur de connexion", retryBtn: "Réessayer", cfgBtn: "Définir Lightning Address", }, de: { title: "⚡ ShinyDapps Zahlungen", totalPayments: "Zahlungen gesamt", totalSats: "Sats gesamt", lightningAddr: "Lightning-Adresse", notSet: "Nicht konfiguriert", when: "Wann", endpoint: "Endpunkt", sats: "Sats", noPayments: "Noch keine Zahlungen — teilen Sie Ihre API!", loading: "Laden…", setupTitle: "⚡ Schnelleinrichtung", setupStep1: "1. Befehlspalette öffnen (Strg+Umschalt+P)", setupStep2: "2. Ausführen: ShinyDapps: Configure Lightning Address", setupStep3: "Oder manuell in den VS Code-Einstellungen:", setupHint: "Kostenlosen Lightning-Adresse auf dashboard.blink.sv holen", chartTitle: "Verdiente Sats", revenue: "Einnahmen (USD geschätzt)", btcPrice: "BTC-Preis", proFeatures: "Vollständiger Verlauf · 30D/1Y/ALL · CSV-Export · Mit Bitcoin bezahlen", upgradeBtn: "Mit Bitcoin upgraden →", emptySub: "Dein erster Satoshi wartet", connectionError: "Verbindungsfehler", retryBtn: "Erneut versuchen", cfgBtn: "Lightning-Adresse setzen", }, ru: { title: "⚡ Платежи ShinyDapps", totalPayments: "Всего платежей", totalSats: "Всего сатошей", lightningAddr: "Lightning-адрес", notSet: "Не настроено", when: "Когда", endpoint: "Эндпоинт", sats: "Сат.", noPayments: "Платежей ещё нет — поделитесь API!", loading: "Загрузка…", setupTitle: "⚡ Быстрая настройка", setupStep1: "1. Откройте палитру команд (Ctrl+Shift+P)", setupStep2: "2. Выполните: ShinyDapps: Configure Lightning Address", setupStep3: "Или настройте вручную в параметрах VS Code:", setupHint: "Получите бесплатный Lightning-адрес на dashboard.blink.sv", chartTitle: "Заработанные сатоши", revenue: "Доход (USD, оценка)", btcPrice: "Цена BTC", proFeatures: "Полная история · 30D/1Y/ALL · Экспорт CSV · Оплата Bitcoin", upgradeBtn: "Обновить через Bitcoin →", emptySub: "Ждём первый сатоши", connectionError: "Ошибка подключения", retryBtn: "Повторить", cfgBtn: "Задать Lightning-адрес", }, hi: { title: "⚡ ShinyDapps भुगतान", totalPayments: "कुल भुगतान", totalSats: "कुल Sats", lightningAddr: "Lightning पता", notSet: "कॉन्फ़िगर नहीं", when: "कब", endpoint: "एंडपॉइंट", sats: "Sats", noPayments: "अभी कोई भुगतान नहीं — अपना API शेयर करें!", loading: "लोड हो रहा है…", setupTitle: "⚡ त्वरित सेटअप", setupStep1: "1. Command Palette खोलें (Ctrl+Shift+P)", setupStep2: "2. चलाएं: ShinyDapps: Configure Lightning Address", setupStep3: "या VS Code सेटिंग्स में मैन्युअली सेट करें:", setupHint: "dashboard.blink.sv पर मुफ़्त Lightning पता प्राप्त करें", chartTitle: "अर्जित Sats", revenue: "आय (USD अनुमानित)", btcPrice: "BTC मूल्य", proFeatures: "पूरा इतिहास · 30D/1Y/ALL · CSV निर्यात · Bitcoin से भुगतान", upgradeBtn: "Bitcoin से अपग्रेड करें →", emptySub: "आपका पहला satoshi इंतजार कर रहा है", connectionError: "कनेक्शन त्रुटि", retryBtn: "पुनः प्रयास", cfgBtn: "Lightning Address सेट करें", }, ar: { title: "⚡ مدفوعات ShinyDapps", totalPayments: "إجمالي المدفوعات", totalSats: "إجمالي Sats", lightningAddr: "عنوان Lightning", notSet: "غير مُهيَّأ", when: "وقت", endpoint: "نقطة النهاية", sats: "Sats", noPayments: "لا مدفوعات بعد — شارك واجهة API الخاصة بك!", loading: "جارٍ التحميل…", setupTitle: "⚡ الإعداد السريع", setupStep1: "1. افتح لوحة الأوامر (Ctrl+Shift+P)", setupStep2: "2. شغّل: ShinyDapps: Configure Lightning Address", setupStep3: "أو اضبط يدويًا في إعدادات VS Code:", setupHint: "احصل على عنوان Lightning مجاني على dashboard.blink.sv", chartTitle: "Sats المكتسبة", revenue: "الإيرادات (USD تقريبي)", btcPrice: "سعر BTC", proFeatures: "تاريخ كامل · 30D/1Y/ALL · تصدير CSV · الدفع بـ Bitcoin", upgradeBtn: "ترقية بـ Bitcoin →", emptySub: "أول satoshi في انتظارك", connectionError: "خطأ في الاتصال", retryBtn: "إعادة المحاولة", cfgBtn: "تعيين Lightning Address", }, it: { title: "⚡ Pagamenti ShinyDapps", totalPayments: "Pagamenti Totali", totalSats: "Sats Totali", lightningAddr: "Indirizzo Lightning", notSet: "Non configurato", when: "Quando", endpoint: "Endpoint", sats: "Sats", noPayments: "Nessun pagamento ancora — condividi la tua API!", loading: "Caricamento…", setupTitle: "⚡ Configurazione Rapida", setupStep1: "1. Apri il Command Palette (Ctrl+Shift+P)", setupStep2: "2. Esegui: ShinyDapps: Configure Lightning Address", setupStep3: "Oppure configura manualmente nelle impostazioni VS Code:", setupHint: "Ottieni un indirizzo Lightning gratuito su dashboard.blink.sv", chartTitle: "Sats guadagnati", revenue: "Ricavi (USD stimati)", btcPrice: "Prezzo BTC", proFeatures: "Cronologia completa · 30D/1Y/ALL · Esporta CSV · Paga con Bitcoin", upgradeBtn: "Aggiorna con Bitcoin →", emptySub: "Il tuo primo satoshi ti aspetta", connectionError: "Errore di connessione", retryBtn: "Riprova", cfgBtn: "Imposta Lightning Address", }, }; // ── Shared webview message handler ─────────────────────────────────────────── function handleWebviewMessage(msg: { command: string; data?: string }) { if (msg.command === CMD_OPEN_CONFIGURE) { vscode.commands.executeCommand("shinydapps.configure"); } else if (msg.command === CMD_DOWNLOAD_CSV) { handleCsvDownload(msg.data as string); } } // ── Provider ────────────────────────────────────────────────────────────────── class PaymentsDashboardProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; constructor(_context: vscode.ExtensionContext) {} resolveWebviewView(webviewView: vscode.WebviewView) { this._view = webviewView; webviewView.webview.options = { enableScripts: true }; webviewView.webview.html = getDashboardHtml(); webviewView.webview.onDidReceiveMessage(handleWebviewMessage); } refresh() { if (this._view) this._view.webview.html = getDashboardHtml(); } } // ── Activation ──────────────────────────────────────────────────────────────── export function activate(context: vscode.ExtensionContext) { statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100); statusBar.command = "shinydapps.showDashboard"; statusBar.text = "⚡ 0 sats (0)"; statusBar.tooltip = "ShinyDapps — Click to open payment dashboard"; statusBar.show(); context.subscriptions.push(statusBar); sidebarProvider = new PaymentsDashboardProvider(context); context.subscriptions.push( vscode.window.registerWebviewViewProvider("shinydapps.payments", sidebarProvider) ); context.subscriptions.push( vscode.commands.registerCommand("shinydapps.showDashboard", () => { if (activePanel) { activePanel.reveal(); return; } activePanel = vscode.window.createWebviewPanel( "shinydappsDashboard", "⚡ ShinyDapps Payments", vscode.ViewColumn.One, { enableScripts: true } ); activePanel.webview.html = getDashboardHtml(); activePanel.webview.onDidReceiveMessage(handleWebviewMessage); activePanel.onDidDispose(() => { activePanel = undefined; }); }) ); context.subscriptions.push( vscode.commands.registerCommand("shinydapps.configure", async () => { const address = await vscode.window.showInputBox({ prompt: "⚡ Your Lightning Address (e.g. you@blink.sv)", placeHolder: "you@blink.sv", ignoreFocusOut: true, validateInput: v => (v && v.includes("@") ? null : "Enter a valid Lightning address (e.g. you@blink.sv)"), }); if (!address) return; const cfg = vscode.workspace.getConfiguration("shinydapps"); await cfg.update("lightningAddress", address, true); vscode.window.showInformationMessage(`⚡ ShinyDapps ready! Monitoring: ${address}`); startPolling(); sidebarProvider?.refresh(); if (activePanel) activePanel.webview.html = getDashboardHtml(); }) ); startPolling(); } function startPolling() { if (pollInterval) clearInterval(pollInterval); const addr = vscode.workspace.getConfiguration("shinydapps").get("lightningAddress"); if (!addr) { statusBar.text = "⚡ Not configured"; statusBar.tooltip = "ShinyDapps — Click to configure your Lightning Address"; return; } const poll = async () => { try { const res = await fetch( `${SD_SUPABASE_URL}/rest/v1/payments?owner_address=eq.${encodeURIComponent(addr)}&select=amount_sats`, { headers: { apikey: SD_SUPABASE_KEY, Authorization: `Bearer ${SD_SUPABASE_KEY}` } } ); const rows = (await res.json()) as { amount_sats: number }[]; const total = rows.reduce((s, r) => s + (r.amount_sats || 0), 0); statusBar.text = `⚡ ${total.toLocaleString()} sats (${rows.length})`; statusBar.tooltip = `ShinyDapps — ${rows.length} payments · ${total.toLocaleString()} sats`; statusBar.backgroundColor = rows.length > 0 ? new vscode.ThemeColor("statusBarItem.warningBackground") : undefined; } catch { statusBar.text = "⚡ — (offline)"; } }; poll(); pollInterval = setInterval(poll, 30_000); } // ── CSV download ────────────────────────────────────────────────────────────── async function handleCsvDownload(csvData: string) { if (csvDownloadPending) return; csvDownloadPending = true; try { const uri = await vscode.window.showSaveDialog({ defaultUri: vscode.Uri.file("payments.csv"), filters: { "CSV": ["csv"] }, }); if (!uri) return; await vscode.workspace.fs.writeFile(uri, new TextEncoder().encode(csvData)); vscode.window.showInformationMessage(`⚡ Payments exported to ${uri.fsPath}`); } finally { csvDownloadPending = false; } } // ── Webview HTML ────────────────────────────────────────────────────────────── function getDashboardHtml(): string { const addr = vscode.workspace.getConfiguration("shinydapps").get("lightningAddress") ?? ""; const i18n = JSON.stringify(I18N); return `
Lang
⚡ Loading…
⚡ l402kit.vercel.app · docs
`; } export function deactivate() { if (pollInterval) clearInterval(pollInterval); }