ZDDC/archive/js/directory.js
ZDDC ea385b5366 Initial commit
ZDDC — Zero Day Document Control. A file-naming convention plus five
single-file HTML tools (archive, transmittal, classifier, mdedit,
landing) and an optional Go HTTP server (zddc-server) with ACL and a
virtual archive index. Self-contained, offline-capable, dependency-free.

See README.md for an overview, AGENTS.md and ARCHITECTURE.md for the
build/release/architecture detail, bootstrap/README.md for the
two-level deployment install pattern, and zddc/README.md for the
HTTP server.
2026-04-27 11:05:47 -05:00

188 lines
6.7 KiB
JavaScript

(function() {
'use strict';
// Directory selection and scanning functionality
// Add directory
async function addDirectory() {
try {
const dirHandle = await window.showDirectoryPicker();
// Check if already added
const exists = window.app.directories.some(d => d.name === dirHandle.name);
if (exists) {
alert('This directory has already been added.');
return;
}
// Add to directories
window.app.directories.push({
handle: dirHandle,
name: dirHandle.name,
path: dirHandle.name // Root path
});
// Hide empty state if this is the first directory
if (window.app.directories.length === 1) {
window.app.modules.app.hideEmptyState();
}
// Scan the new directory
await scanDirectory(dirHandle, dirHandle.name);
window.app.modules.app.updateUI();
} catch (err) {
if (err.name !== 'AbortError') {
console.error('Error selecting directory:', err);
alert('Error selecting directory: ' + err.message);
}
}
}
// Scan directory recursively (local mode — delegates to local source in source.js)
async function scanDirectory(dirHandle, path) {
window.app.isScanning = true;
window.app.scanProgress = 'Scanning ' + path + '...';
window.app.modules.app.updateStatusBar();
const source = window.app.modules.source.createSource('local', {});
const callbacks = {
onGroupingFolder: function(folder) {
window.app.groupingFolders.push(folder);
},
onTransmittalFolder: function(folder) {
window.app.transmittalFolders.push(folder);
},
onFile: function(file) {
window.app.files.push(file);
},
onProgress: function(message) {
window.app.scanProgress = message;
window.app.modules.app.updateStatusBar();
}
};
try {
await source.scan(dirHandle, callbacks);
// Only auto-select top-level party folders (shallowest depth)
const groupingDepths = window.app.groupingFolders.map(f => f.path.split('/').length);
const minGroupingDepth = groupingDepths.length > 0 ? Math.min(...groupingDepths) : 1;
window.app.groupingFolders.forEach(folder => {
if (folder.path.split('/').length === minGroupingDepth) {
window.app.selectedGroupingFolders.add(folder.path);
}
});
window.app.transmittalFolders.forEach(folder => {
if (!window.app.modules.app.isUnderHiddenFolderType(folder.path)) {
window.app.selectedTransmittalFolders.add(folder.path);
}
});
window.app.modules.app.ensureOutstandingTransmittal();
// Auto-select Outstanding if selectAllTransmittals is active
if (window.app.selectAllTransmittals) {
window.app.selectedTransmittalFolders.add('__outstanding__');
}
window.app.modules.app.collectModifiers();
window.app.modules.app.updateUI();
window.app.modules.filtering.applyFilters();
if (window.app.modules.presets) {
window.app.modules.presets.init();
}
} catch (err) {
console.error('Error scanning directory:', err);
alert('Error scanning directory: ' + err.message);
} finally {
window.app.isScanning = false;
window.app.scanProgress = '';
window.app.modules.app.updateStatusBar();
}
}
// Refresh all directories
async function refreshDirectories() {
// Clear existing data
window.app.groupingFolders = [];
window.app.transmittalFolders = [];
window.app.files = [];
window.app.filteredFiles = [];
if (window.app.sourceMode === 'http') {
// Re-scan all HTTP sources
const dirs = window.app.directories.slice();
window.app.directories = [];
for (const dir of dirs) {
await window.app.modules.app.addHttpSource(dir.url);
}
} else {
// Re-scan all local directories
for (const dir of window.app.directories) {
await scanDirectory(dir.handle, dir.name);
}
}
window.app.modules.app.updateUI();
}
// Remove directory
function removeDirectory(dirName) {
const index = window.app.directories.findIndex(d => d.name === dirName);
if (index !== -1) {
window.app.directories.splice(index, 1);
// Remove associated folders and files
window.app.groupingFolders = window.app.groupingFolders.filter(f =>
!f.path.startsWith(dirName)
);
window.app.transmittalFolders = window.app.transmittalFolders.filter(f =>
!f.path.startsWith(dirName)
);
window.app.files = window.app.files.filter(f =>
!f.path.startsWith(dirName)
);
// Clean up the Outstanding virtual transmittal if no outstanding files remain
const hasAnyOutstanding = window.app.files.some(f => f.folderPath === '__outstanding__');
if (!hasAnyOutstanding) {
window.app.transmittalFolders = window.app.transmittalFolders.filter(f => f.path !== '__outstanding__');
window.app.selectedTransmittalFolders.delete('__outstanding__');
}
// Show empty state if no directories left
if (window.app.directories.length === 0) {
window.app.modules.app.showEmptyState();
}
window.app.modules.app.updateUI();
}
}
// Request permission for directory
async function requestPermission(dirHandle) {
const options = { mode: 'read' };
// Check current permission state
if ((await dirHandle.queryPermission(options)) === 'granted') {
return true;
}
// Request permission
if ((await dirHandle.requestPermission(options)) === 'granted') {
return true;
}
return false;
}
window.app.modules.directory = {
addDirectory,
scanDirectory,
refreshDirectories,
removeDirectory,
requestPermission
};
})();