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';
}
}