window.API = { async get(url) { const r = await fetch(url,{credentials:'same-origin'}); if(!r.ok)throw new Error(await r.text()); return r.json(); }, async post(url,data) { const r = await fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data),credentials:'same-origin'}); if(!r.ok)throw new Error(await r.text()); return r.json(); }, async put(url,data) { const r = await fetch(url,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(data),credentials:'same-origin'}); if(!r.ok)throw new Error(await r.text()); return r.json(); }, async del(url) { const r = await fetch(url,{method:'DELETE',credentials:'same-origin'}); if(!r.ok)throw new Error(await r.text()); return r.json(); } }; window.$ = (sel) => document.querySelector(sel); window.$$ = (sel) => document.querySelectorAll(sel); window.notify = function(msg, type) { var el = document.createElement('div'); el.className = 'notification ' + (type || ''); el.textContent = msg; document.body.appendChild(el); setTimeout(function() { el.remove(); }, 3000); }; window.undoState = null; window.updateUndoBar = function() { API.get('/api/undo/state').then(function(s) { undoState = s; var uBtn = $('.undo-btn'); var rBtn = $('.redo-btn'); var uLabel = $('.undo-label'); if (uBtn) { uBtn.disabled = !s.can_undo; uBtn.title = s.undo_desc || ''; } if (rBtn) { rBtn.disabled = !s.can_redo; rBtn.title = s.redo_desc || ''; } if (uLabel) { uLabel.textContent = s.undo_desc || ''; } }).catch(function() {}); }; window.doUndo = function() { API.post('/api/undo/undo').then(function(r) { notify(r.message || 'Undo complete', 'success'); updateUndoBar(); if(r.has_duplicates) { window.location = '/'; return; } if(window.refreshPage)refreshPage(); }).catch(function(e) { notify('Undo failed: '+e.message, 'error'); }); }; window.doRedo = function() { API.post('/api/undo/redo').then(function(r) { notify(r.message || 'Redo complete', 'success'); updateUndoBar(); if(r.has_duplicates) { window.location = '/'; return; } if(window.refreshPage)refreshPage(); }).catch(function(e) { notify('Redo failed: '+e.message, 'error'); }); }; window.addEventListener('keydown', function(e) { if ((e.ctrlKey || e.metaKey) && e.key === 'z') { e.preventDefault(); if (e.shiftKey) { doRedo(); } else { doUndo(); } } }); window.saveForm = function(url, data, msg) { API.put(url, data).then(function() { updateUndoBar(); notify(msg || 'Saved', 'success'); }).catch(function(e) { notify('Save failed: '+e.message, 'error'); }); }; window.entityRename = function(type, oldID, newID) { return API.post('/api/' + type + '/rename', {id: oldID, new_id: newID}).then(function(r) { notify('Renamed ' + oldID + ' to ' + newID, 'success'); updateUndoBar(); return r; }); }; window.renderRenameableHeader = function(label, id) { var h = '
'; h += '

' + esc(label) + ': ' + esc(id) + '

'; h += '\u270E'; h += ''; h += '
'; return h; }; window.handleHeaderRename = function(oldID, newID) { var type = window.editorType || ''; if (!type) return; entityRename(type, oldID, newID).then(function() { window.currentID = newID; var typeSingular = type.slice(0, -1) + 'ID'; if (window[typeSingular] !== undefined) window[typeSingular] = newID; if (window.loadList) window.loadList(); if (window.loadItem) window.loadItem(newID); }).catch(function(e) { notify('Rename failed: ' + e.message, 'error'); }); }; window.startHeaderRename = function(pencilEl, oldID) { var header = pencilEl.parentElement; var textSpan = header.querySelector('.rename-header-text'); var editRow = header.querySelector('.rename-edit-row'); textSpan.style.display = 'none'; pencilEl.style.display = 'none'; editRow.style.display = 'flex'; editRow.innerHTML = '' + '' + ''; var input = editRow.querySelector('.rename-input'); var confirmBtn = editRow.querySelector('.rename-btn-confirm'); var cancelBtn = editRow.querySelector('.rename-btn-cancel'); input.focus(); input.select(); function cancel() { textSpan.style.display = ''; pencilEl.style.display = ''; editRow.style.display = 'none'; } function confirm() { var newID = input.value.trim(); if (!newID || newID === oldID) { cancel(); return; } if (window.handleHeaderRename) handleHeaderRename(oldID, newID); } cancelBtn.onclick = cancel; input.onkeydown = function(e) { if (e.key === 'Enter') confirm(); else if (e.key === 'Escape') cancel(); }; confirmBtn.onclick = confirm; }; document.addEventListener('DOMContentLoaded', function() { updateUndoBar(); });