Spaces:
Sleeping
Sleeping
File size: 8,193 Bytes
41d16b3 bec7397 41d16b3 bec7397 41d16b3 4595db8 41d16b3 214c544 41d16b3 8354653 214c544 41d16b3 4595db8 41d16b3 5ae5432 41d16b3 4595db8 15e9574 214c544 4595db8 15e9574 4595db8 41d16b3 214c544 41d16b3 4595db8 8354653 4595db8 214c544 41d16b3 214c544 4595db8 8354653 214c544 8354653 214c544 4595db8 8354653 214c544 8354653 214c544 8354653 214c544 5ae5432 995884c 5ae5432 214c544 8354653 4595db8 bec7397 214c544 bec7397 214c544 41d16b3 4595db8 41d16b3 214c544 66006d5 41d16b3 4595db8 41d16b3 214c544 96454a9 214c544 41d16b3 4595db8 5ae5432 41d16b3 214c544 41d16b3 333dd2f 214c544 333dd2f bec7397 333dd2f 41d16b3 214c544 41d16b3 333dd2f 41d16b3 4595db8 41d16b3 5ae5432 41d16b3 4595db8 15e9574 4595db8 41d16b3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | const API = '';
function escapeHtml(text) {
if (text == null) return '';
return String(text)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
function formatApiError(detail) {
if (!detail) return null;
if (typeof detail === 'string') return detail;
if (Array.isArray(detail)) {
return detail.map((d) => (d && d.msg) || JSON.stringify(d)).join('; ');
}
return String(detail);
}
async function ddaApi(method, path, options = {}) {
const headers = { ...options.headers };
if (options.body && !(options.body instanceof FormData)) {
headers['Content-Type'] = 'application/json';
}
const res = await fetch(API + path, { method, headers, credentials: 'include', ...options });
const text = await res.text();
let data = null;
try { data = text ? JSON.parse(text) : null; } catch (_) {}
if (!res.ok) throw new Error(formatApiError(data?.detail) || res.statusText || 'Request failed');
return data;
}
function showDdaError(msg) {
const el = document.getElementById('dda-error');
if (!el) return;
el.textContent = msg;
el.classList.remove('hidden');
}
function hideDdaError() {
document.getElementById('dda-error')?.classList.add('hidden');
}
function showDdaSuccess(msg) {
const el = document.getElementById('dda-success');
if (!el) return;
el.textContent = msg;
el.classList.remove('hidden');
setTimeout(() => el.classList.add('hidden'), 4000);
}
function formatBytes(n) {
if (n >= 1024 ** 3) return (n / 1024 ** 3).toFixed(1) + ' GB';
if (n >= 1024 ** 2) return (n / 1024 ** 2).toFixed(1) + ' MB';
return (n / 1024).toFixed(0) + ' KB';
}
let ddaConfig = null;
const selectedNode = { id: null, path: '' };
window.ddaState = {
get config() { return ddaConfig; },
get localCfg() { return window._localCfg; },
get selectedNode() { return { ...selectedNode }; },
get userRole() { return window._ddaUserRole || 'analyst'; },
set userRole(r) { window._ddaUserRole = r; },
setNode(n) { selectedNode.id = n.id; selectedNode.path = n.path || ''; },
clearNode() { selectedNode.id = null; selectedNode.path = ''; },
refreshImages: () => loadLibraryImages(),
rescan: () => rescanLibrary(),
};
document.querySelectorAll('.dda-tab').forEach((btn) => {
btn.addEventListener('click', () => {
document.querySelectorAll('.dda-tab').forEach((b) => b.classList.remove('active'));
document.querySelectorAll('.dda-panel').forEach((p) => p.classList.remove('active'));
btn.classList.add('active');
const tab = btn.dataset.tab;
document.getElementById('tab-' + tab)?.classList.add('active');
if (tab === 'detect' && typeof loadCompareLibraryGrid === 'function') {
loadCompareLibraryGrid();
}
});
});
async function rescanLibrary() {
const data = await ddaApi('POST', '/api/dda/local/rescan');
if (typeof renderAllTrees === 'function') renderAllTrees({ tree: data.tree }, []);
else if (typeof renderTree === 'function') renderTree({ tree: data.tree }, []);
if (typeof populateManageNodeSelect === 'function') populateManageNodeSelect();
await loadLibraryImages();
if (typeof loadCompareLibraryGrid === 'function') await loadCompareLibraryGrid();
return data;
}
async function initDda() {
hideDdaError();
try {
try {
const me = await ddaApi('GET', '/api/dda/me');
window.ddaState.userRole = me.role || 'analyst';
} catch (_) {
window.ddaState.userRole = 'analyst';
}
ddaConfig = await ddaApi('GET', '/api/dda/config');
const localCfg = await ddaApi('GET', '/api/dda/local/config');
window._localCfg = localCfg;
document.getElementById('btn-manage-library')?.classList.toggle('hidden', window.ddaState.userRole !== 'admin');
const hint = document.getElementById('lib-config-hint');
if (hint) hint.textContent = localCfg.geotiffEnabled ? 'GeoTIFF ready' : 'GeoTIFF limited';
const pathEl = document.getElementById('lib-path-display');
if (pathEl) {
pathEl.textContent = [
'Storage root:',
localCfg.storageRoot || localCfg.writablePath || '',
'Layout: {zone}/{area}/…/Images/file.tif',
].filter(Boolean).join('\n');
}
const folderPath = document.getElementById('lib-folder-path');
if (folderPath) {
folderPath.textContent = localCfg.isHosted
? 'Hugging Face — tree library'
: `Storage: ${localCfg.storageRoot || ''}`;
}
const instr = document.getElementById('lib-instructions');
if (instr && localCfg.instructions) instr.textContent = localCfg.instructions;
const uploadLimit = document.getElementById('upload-limit-hint');
if (uploadLimit && localCfg.maxUploadGb) {
uploadLimit.textContent = `Select a tree node and image type. Max ${localCfg.maxUploadGb} GB per file.`;
}
const resHint = document.getElementById('dda-detect-res-hint');
if (resHint && localCfg.detectionMaxSide) {
resHint.textContent = `Detection runs at up to ${localCfg.detectionMaxSide}px per side.`;
}
const urlTab = new URLSearchParams(window.location.search).get('tab');
if (urlTab) document.querySelector(`.dda-tab[data-tab="${urlTab}"]`)?.click();
if (typeof loadTree === 'function') await loadTree();
await loadLibraryImages();
} catch (err) {
showDdaError(err.message || 'Failed to load library');
}
}
function selectionTitle() {
return selectedNode.path || 'All images';
}
async function loadLibraryImages() {
const grid = document.getElementById('lib-grid');
const title = document.getElementById('lib-grid-title');
if (!grid) return;
const q = document.getElementById('lib-filter')?.value?.trim() || '';
const params = new URLSearchParams();
if (selectedNode.id) params.set('node_id', String(selectedNode.id));
if (q) params.set('q', q);
if (title) title.textContent = `Images — ${selectionTitle()}`;
try {
const items = await ddaApi('GET', '/api/dda/local/images?' + params.toString());
window.ddaState.libraryItems = items;
if (!items.length) {
grid.innerHTML = `<p class="dim">No images in <strong>${escapeHtml(selectionTitle())}</strong>. Select a node and upload, or use Manage to create folders.</p>`;
return;
}
grid.innerHTML = items.map((img) => {
const thumb = img.thumbUrl || '';
const crumb = img.breadcrumb || img.nodePath || img.filename;
return `
<div class="dda-card-img" draggable="true" data-image-path="${img.path.replace(/"/g, '"')}" title="${escapeHtml(img.filename)}">
${thumb ? `<img src="${thumb}" alt="" loading="lazy" />` : '<div class="meta">No preview</div>'}
<div class="meta">
<span class="dim dda-crumb">${escapeHtml(crumb)}</span><br/>
<span class="dim">${img.imageType || ''} · ${formatBytes(img.fileSizeBytes)}</span>
</div>
</div>`;
}).join('');
grid.querySelectorAll('.dda-card-img').forEach((card) => {
card.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('application/x-dda-image-path', card.dataset.imagePath);
e.dataTransfer.setData('text/plain', card.dataset.imagePath);
});
});
if (typeof loadCompareLibraryGrid === 'function') loadCompareLibraryGrid();
} catch (err) {
grid.innerHTML = `<p class="dim">Could not load images: ${err.message}</p>`;
}
}
document.getElementById('lib-filter')?.addEventListener('input', () => {
clearTimeout(window._libFilterTimer);
window._libFilterTimer = setTimeout(loadLibraryImages, 300);
});
document.getElementById('btn-refresh-lib')?.addEventListener('click', async () => {
const btn = document.getElementById('btn-refresh-lib');
btn.disabled = true;
try {
const data = await rescanLibrary();
const sync = data.sync || {};
const parts = [`${data.totalImages || 0} image(s)`];
if (sync.nodesCreated) parts.push(`${sync.nodesCreated} folder(s) imported`);
if (sync.imagesIndexed) parts.push(`${sync.imagesIndexed} image(s) indexed`);
showDdaSuccess(`Library synced — ${parts.join(', ')}.`);
} catch (err) {
showDdaError(err.message);
} finally {
btn.disabled = false;
}
});
initDda();
|