ZDDC/archive/js/directory.js
ZDDC c95f07966d feat(tools,build): in-flight HTML-tool reworks and build-infra updates
Bundles a stretch of in-progress work across the SPA tools so the
tree returns to a coherent shippable state ahead of cutting a new
zddc-server stable image:

- landing: substantial rework of the project picker (sortable/filterable
  table, presets refactor, ?projects= filter, ?v= channel propagation,
  loading/error states)
- archive: presets cleanup, source.js refactor, filtering/url-state
  alignment with the landing page
- mdedit: file-system module split, resizer, file-tree improvements,
  base/toc styling tweaks
- transmittal/classifier: small template touch-ups for shared chrome
- shared: build-lib.sh helpers, new favicon.svg
- bootstrap, build.sh: pick up the channel-aware install/track zip
  generation
- tests: new landing.spec.js, expanded archive/mdedit/build-label specs
- docs: CLAUDE.md picks up the zddc-server section and freshens the
  alpha-build exception note
- regenerated artifacts: install.zip, track-{alpha,beta,stable}.zip,
  *_alpha.html — these are produced by `sh build.sh` and per project
  convention are committed alongside the source changes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 12:52:27 -05:00

190 lines
6.9 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).
// Selection is keyed by party NAME so the same-named third-party folder
// appearing under multiple projects is selected/deselected as a unit.
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.name);
}
});
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
};
})();