// events.js — wires up DOM listeners. Idempotent so app.js can call // init() once on load. (function () { 'use strict'; var state = window.app.state; var tree = window.app.modules.tree; var loader = window.app.modules.loader; function status(msg, kind) { var el = document.getElementById('statusBar'); if (!el) return; el.textContent = msg || ''; el.classList.remove('status-bar--error', 'status-bar--info'); if (kind === 'error') el.classList.add('status-bar--error'); if (kind === 'info') el.classList.add('status-bar--info'); } function statusError(msg) { status(msg, 'error'); } function statusInfo(msg) { status(msg, 'info'); } function statusClear() { status('', null); } async function pickLocalDir() { if (typeof window.showDirectoryPicker !== 'function') { statusError('Your browser does not support local folder selection. Use a recent Chromium-based browser, or open this page via zddc-server.'); return; } var handle; try { handle = await window.showDirectoryPicker({ mode: 'read' }); } catch (e) { // User cancelled — silent return; } state.source = 'fs'; state.rootHandle = handle; state.currentPath = handle.name + '/'; var raw; try { raw = await loader.fetchFsChildren(handle); } catch (e) { statusError('Failed to read directory: ' + e.message); return; } tree.setRoot(raw); showBrowseRoot(); document.getElementById('currentPath').textContent = state.currentPath; tree.render(); statusInfo('Loaded ' + raw.length + ' item' + (raw.length === 1 ? '' : 's')); } function showBrowseRoot() { var empty = document.getElementById('emptyState'); var root = document.getElementById('browseRoot'); if (empty) empty.classList.add('hidden'); if (root) root.classList.remove('hidden'); } function init() { // Header buttons var btn = document.getElementById('addDirectoryBtn'); if (btn) btn.addEventListener('click', pickLocalDir); // Filter input var filter = document.getElementById('filterInput'); if (filter) { filter.addEventListener('input', function () { tree.setFilter(filter.value); }); } // Sort headers var ths = document.querySelectorAll('#browseTable thead th.sortable'); for (var i = 0; i < ths.length; i++) { (function (th) { th.addEventListener('click', function () { tree.setSort(th.dataset.sort); }); })(ths[i]); } // Tree-row clicks (event delegation on tbody). // Click semantics on a folder row: // - plain click → toggle just this folder // - shift-click → recursive expand/collapse of the whole // subtree (matches common file-explorer // convention; e.g. Finder, VSCode tree, // Windows Explorer) // - alt-click → ALSO recursive (alt is sometimes the // expand-all key on Linux DEs; bind both // so muscle memory works either way) // File rows: let the tag's natural target=_blank do its // job — don't intercept. var tbody = document.getElementById('browseTbody'); if (tbody) { tbody.addEventListener('click', function (e) { var row = e.target.closest('tr.tree-row'); if (!row) return; var isDir = row.dataset.isdir === 'true'; if (!isDir) return; e.preventDefault(); var id = parseInt(row.dataset.id, 10); if (e.shiftKey || e.altKey) { var node = state.nodes.get(id); if (node && node.expanded) { tree.collapseSubtree(id); } else { tree.expandSubtree(id); } } else { tree.toggleFolder(id); } }); } } // Public API window.app.modules.events = { init: init, statusError: statusError, statusInfo: statusInfo, statusClear: statusClear, showBrowseRoot: showBrowseRoot }; })();