const $ = (s) => document.querySelector(s); const esc = (s) => { const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }; let selectedProject = null; function toast(msg, kind = '') { const t = $('#toast'); t.textContent = msg; t.className = 'toast show ' + kind; setTimeout(() => t.classList.remove('show'), 3500); } // --- Path bar (breadcrumbs) --- function renderPathBar(currentPath) { const bar = $('#path-bar'); const segments = currentPath.split('/').filter(Boolean); let html = ''; for (let i = 0; i < segments.length; i++) { const fullPath = '/' + segments.slice(0, i + 1).join('/'); if (i > 0) html += '/'; html += `${esc(segments[i])}`; } bar.innerHTML = html; bar.querySelectorAll('.path-segment').forEach((el) => { el.addEventListener('click', () => browse(el.dataset.path)); }); } // --- File list --- function renderFileList(entries) { const list = $('#file-list'); if (!entries.length) { list.innerHTML = '
No Xcode projects or subdirectories found here.
'; return; } list.innerHTML = entries.map((e) => { let icon, cls; if (e.type === 'xcworkspace') { icon = '\u{1F4E6}'; cls = 'xcode-project'; } else if (e.type === 'xcodeproj') { icon = '\u{1F528}'; cls = 'xcode-project'; } else { icon = '\u{1F4C1}'; cls = ''; } return `Loading...
'; const url = dirPath ? `/api/filesystem/browse?path=${encodeURIComponent(dirPath)}` : '/api/filesystem/browse'; try { const r = await fetch(url); if (r.status === 401) { location.href = '/login'; return; } const data = await r.json(); if (!r.ok) throw new Error(data.error); renderPathBar(data.path); renderFileList(data.entries); } catch (err) { list.innerHTML = `${esc(err.message)}
`; } } // --- Select a project --- async function selectProject(projectPath) { selectedProject = projectPath; const config = $('#project-config'); config.style.display = 'block'; $('#selected-project').textContent = projectPath; const select = $('#scheme-select'); select.disabled = true; select.innerHTML = ''; $('#build-btn').disabled = true; document.querySelectorAll('.file-entry').forEach((el) => { el.classList.toggle('selected', el.dataset.path === projectPath); }); try { const r = await fetch(`/api/filesystem/schemes?projectPath=${encodeURIComponent(projectPath)}`); const data = await r.json(); if (!r.ok) throw new Error(data.error); if (!data.schemes.length) { select.innerHTML = ''; return; } select.innerHTML = data.schemes.map((s) => `` ).join(''); select.disabled = false; $('#build-btn').disabled = false; } catch (err) { select.innerHTML = ``; toast(err.message, 'error'); } } // --- Build button --- $('#build-btn').addEventListener('click', async () => { if (!selectedProject) return; const scheme = $('#scheme-select').value; const btn = $('#build-btn'); btn.disabled = true; btn.textContent = 'Starting build...'; try { const r = await fetch('/api/build/local', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ projectPath: selectedProject, scheme }), }); const data = await r.json(); if (!r.ok) throw new Error(data.error || 'Build failed to start'); location.href = `/#${data.job_id}`; } catch (err) { toast(err.message, 'error'); btn.disabled = false; btn.textContent = 'Build'; } }); // Start browsing at default path browse(null);