import * as Blockly from 'blockly'; const BROWSER_STORAGE_KEY = 'esp32block_projects'; let workspace = null; let captureOutput = null; let execCode = null; let writeFile = null; let checkConnected = null; let browserList, deviceList; let browserNameInput, deviceNameInput; let browserSaveBtn, browserLoadBtn, browserDeleteBtn; let deviceSaveBtn, deviceLoadBtn, deviceDeleteBtn; let deviceStatus; let browserSelected = null; let deviceSelected = null; export function initProjectsDialog(deps) { workspace = deps.workspace; captureOutput = deps.captureDeviceOutput; execCode = deps.executeCode; writeFile = deps.writeFileToDevice; checkConnected = deps.isConnected; browserList = document.getElementById('browser-list'); deviceList = document.getElementById('device-list'); browserNameInput = document.getElementById('browser-save-name'); deviceNameInput = document.getElementById('device-save-name'); browserSaveBtn = document.getElementById('browser-save-btn'); browserLoadBtn = document.getElementById('browser-load-btn'); browserDeleteBtn = document.getElementById('browser-delete-btn'); deviceSaveBtn = document.getElementById('device-save-btn'); deviceLoadBtn = document.getElementById('device-load-btn'); deviceDeleteBtn = document.getElementById('device-delete-btn'); deviceStatus = document.getElementById('device-status'); browserSaveBtn.addEventListener('click', saveBrowser); browserLoadBtn.addEventListener('click', loadBrowser); browserDeleteBtn.addEventListener('click', deleteBrowser); deviceSaveBtn.addEventListener('click', saveDevice); deviceLoadBtn.addEventListener('click', loadDevice); deviceDeleteBtn.addEventListener('click', deleteDevice); refreshBrowserList(); refreshDeviceList(); } export function refreshAll() { refreshBrowserList(); refreshDeviceList(); } // ─── 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)); } function refreshBrowserList() { const projects = getBrowserProjects(); const names = Object.keys(projects).sort(); browserList.innerHTML = ''; browserSelected = null; updateBrowserButtons(); if (names.length === 0) { browserList.innerHTML = '
  • No saved projects
  • '; return; } for (const name of names) { const li = document.createElement('li'); li.textContent = name; li.addEventListener('click', () => selectBrowserItem(name, li)); li.addEventListener('dblclick', () => { selectBrowserItem(name, li); loadBrowser(); }); browserList.appendChild(li); } } function selectBrowserItem(name, li) { browserList.querySelectorAll('li').forEach(el => el.classList.remove('selected')); li.classList.add('selected'); browserSelected = name; browserNameInput.value = name; updateBrowserButtons(); } function updateBrowserButtons() { browserLoadBtn.disabled = !browserSelected; browserDeleteBtn.disabled = !browserSelected; } function saveBrowser() { const name = browserNameInput.value.trim(); if (!name) return; const projects = getBrowserProjects(); projects[name] = Blockly.serialization.workspaces.save(workspace); setBrowserProjects(projects); browserNameInput.value = ''; refreshBrowserList(); } function loadBrowser() { if (!browserSelected) return; const projects = getBrowserProjects(); const state = projects[browserSelected]; if (!state) return; Blockly.serialization.workspaces.load(state, workspace); } function deleteBrowser() { if (!browserSelected) return; const projects = getBrowserProjects(); delete projects[browserSelected]; setBrowserProjects(projects); refreshBrowserList(); } // ─── Device column ─────────────────────────────────────── export async function refreshDeviceList() { deviceList.innerHTML = ''; deviceSelected = null; updateDeviceButtons(); if (!checkConnected()) { deviceStatus.textContent = 'Connect a device to see its projects'; deviceSaveBtn.disabled = true; deviceList.innerHTML = '
  • Not connected
  • '; return; } deviceStatus.textContent = 'Loading...'; deviceSaveBtn.disabled = false; try { const raw = await captureOutput( "import os\n" + "for f in os.listdir('/'):\n" + " if f.endswith('.blk'): print(f)" ); const files = raw.trim().split('\n').filter(Boolean).map(f => f.trim()); deviceList.innerHTML = ''; if (files.length === 0) { deviceList.innerHTML = '
  • No saved projects
  • '; deviceStatus.textContent = ''; return; } for (const file of files) { const displayName = file.replace(/\.blk$/, ''); const li = document.createElement('li'); li.textContent = displayName; li.addEventListener('click', () => selectDeviceItem(displayName, file, li)); li.addEventListener('dblclick', () => { selectDeviceItem(displayName, file, li); loadDevice(); }); deviceList.appendChild(li); } deviceStatus.textContent = ''; } catch { deviceList.innerHTML = '
  • Error reading device
  • '; deviceStatus.textContent = 'Could not list files'; } } function selectDeviceItem(displayName, filename, li) { deviceList.querySelectorAll('li').forEach(el => el.classList.remove('selected')); li.classList.add('selected'); deviceSelected = filename; deviceNameInput.value = displayName; updateDeviceButtons(); } function updateDeviceButtons() { const connected = checkConnected(); deviceSaveBtn.disabled = !connected; deviceLoadBtn.disabled = !deviceSelected || !connected; deviceDeleteBtn.disabled = !deviceSelected || !connected; } async function saveDevice() { const name = deviceNameInput.value.trim(); if (!name || !checkConnected()) return; const filename = name.endsWith('.blk') ? name : name + '.blk'; const state = Blockly.serialization.workspaces.save(workspace); const json = JSON.stringify(state); deviceStatus.textContent = 'Saving...'; deviceSaveBtn.disabled = true; try { await writeFile(json, filename); deviceNameInput.value = ''; await refreshDeviceList(); } catch { deviceStatus.textContent = 'Save failed'; } } async function loadDevice() { if (!deviceSelected || !checkConnected()) return; deviceStatus.textContent = 'Loading...'; try { const raw = await captureOutput( `f=open('${deviceSelected}','r')\nprint(f.read(),end='')\nf.close()` ); const state = JSON.parse(raw.trim()); Blockly.serialization.workspaces.load(state, workspace); deviceStatus.textContent = ''; } catch { deviceStatus.textContent = 'Load failed'; } } async function deleteDevice() { if (!deviceSelected || !checkConnected()) return; deviceStatus.textContent = 'Deleting...'; try { await execCode(`import os\nos.remove('${deviceSelected}')`); await new Promise(r => setTimeout(r, 300)); await refreshDeviceList(); } catch { deviceStatus.textContent = 'Delete failed'; } }