import * as Blockly from 'blockly'; import { getToken, onAuthChange } from '../auth/client.js'; const BROWSER_STORAGE_KEY = 'esp32block_projects'; let workspace = null; let captureOutput = null; let writeFile = null; let checkConnected = null; let browserList; let browserNameInput; let browserSaveBtn, browserLoadBtn, browserMoveCloudBtn, browserDownloadBtn, browserDeleteBtn; let browserUploadBtn, browserUploadInput; let browserSelected = null; let selectedEntry = null; let cloudProjects = []; export function initProjectsDialog(deps) { workspace = deps.workspace; captureOutput = deps.captureDeviceOutput; writeFile = deps.writeFileToDevice; checkConnected = deps.isConnected; browserList = document.getElementById('browser-list'); browserNameInput = document.getElementById('browser-save-name'); browserSaveBtn = document.getElementById('browser-save-btn'); browserLoadBtn = document.getElementById('browser-load-btn'); browserMoveCloudBtn = document.getElementById('browser-move-cloud-btn'); browserDownloadBtn = document.getElementById('browser-download-btn'); browserDeleteBtn = document.getElementById('browser-delete-btn'); browserUploadBtn = document.getElementById('browser-upload-btn'); browserUploadInput = document.getElementById('browser-upload-input'); browserSaveBtn.addEventListener('click', saveBrowser); browserLoadBtn.addEventListener('click', loadBrowser); browserMoveCloudBtn.addEventListener('click', moveBrowserToCloud); browserDownloadBtn.addEventListener('click', downloadBrowser); browserDeleteBtn.addEventListener('click', deleteBrowser); browserUploadBtn.addEventListener('click', openUploadPicker); browserUploadInput.addEventListener('change', uploadFromComputer); onAuthChange(() => { refreshBrowserList().catch(() => { /* ignore */ }); }); refreshBrowserList().catch(() => { /* ignore */ }); } export function refreshAll() { refreshBrowserList().catch(() => { /* ignore */ }); } export async function saveCurrentWorkspaceToDevice(preferredName = 'main') { if (!workspace || !writeFile || !checkConnected || !checkConnected()) return null; const filename = preferredName.endsWith('.blk') ? preferredName : preferredName + '.blk'; const state = Blockly.serialization.workspaces.save(workspace); const json = JSON.stringify(state); await writeFile(json, filename); return filename; } export async function loadWorkspaceFromDevice(preferredName = 'main') { if (!workspace || !captureOutput || !checkConnected || !checkConnected()) return null; const filename = preferredName.endsWith('.blk') ? preferredName : preferredName + '.blk'; const raw = await captureOutput( `f=open('${filename}','r')\nprint(f.read(),end='')\nf.close()` ); const state = JSON.parse(raw.trim()); Blockly.serialization.workspaces.load(state, workspace); return filename; } // ─── Browser column ────────────────────────────────────── function getBrowserProjects() { try { return JSON.parse(localStorage.getItem(BROWSER_STORAGE_KEY) || '{}'); } catch { return {}; } } function setBrowserProjects(projects) { localStorage.setItem(BROWSER_STORAGE_KEY, JSON.stringify(projects)); } async function fetchCloudProjects() { const token = getToken(); if (!token) return []; const res = await fetch(apiUrl('/api/projects'), { headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok) throw new Error(`Failed to fetch cloud projects (HTTP ${res.status})`); const data = await res.json(); return Array.isArray(data?.projects) ? data.projects : []; } function apiUrl(path) { const base = import.meta.env.BASE_URL || '/'; const p = path.startsWith('/') ? path.slice(1) : path; return (base.endsWith('/') ? base : base + '/') + p; } function displayName(entry) { const tag = entry.source === 'cloud' ? '[cloud]' : '[browser]'; return `${entry.name} ${tag}`; } async function refreshBrowserList() { const loggedIn = !!getToken(); const projects = getBrowserProjects(); const names = Object.keys(projects).sort((a, b) => a.localeCompare(b)); try { cloudProjects = await fetchCloudProjects(); } catch { cloudProjects = []; } browserList.innerHTML = ''; browserSelected = null; // legacy selection key selectedEntry = null; updateBrowserButtons(); const entries = loggedIn ? [ ...names.map((name) => ({ source: 'browser', name })), ...cloudProjects.map((p) => ({ source: 'cloud', name: p.name })), ] : names.map((name) => ({ source: 'browser', name })); entries.sort((a, b) => { const byName = a.name.localeCompare(b.name); if (byName !== 0) return byName; return a.source.localeCompare(b.source); }); if (entries.length === 0) { browserList.innerHTML = '