aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/static
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-04 23:04:00 -0400
committerhistoria <[not public]>2026-07-04 23:04:00 -0400
commit4385244d39b37d003dbb7a66a2ff3b05079b7047 (patch)
tree6498a06879cc4ff06612abed259b1403f071ecb2 /internal/admin/static
parentad22ea3526a146ea8ce305c8375c35836465cc30 (diff)
downloadthehouseoficarus-4385244d39b37d003dbb7a66a2ff3b05079b7047.tar.gz
feat: drag/drop files in gui to change directories
Diffstat (limited to 'internal/admin/static')
-rw-r--r--internal/admin/static/admin.css9
-rw-r--r--internal/admin/static/dropeditor.js3
-rw-r--r--internal/admin/static/editor.js82
-rw-r--r--internal/admin/static/hazardeditor.js3
-rw-r--r--internal/admin/static/itemeditor.js3
-rw-r--r--internal/admin/static/mobeditor.js3
-rw-r--r--internal/admin/static/moduleeditor.js3
-rw-r--r--internal/admin/static/objecteditor.js3
-rw-r--r--internal/admin/static/techeditor.js3
9 files changed, 93 insertions, 19 deletions
diff --git a/internal/admin/static/admin.css b/internal/admin/static/admin.css
index 42e64fa..33dbdc4 100644
--- a/internal/admin/static/admin.css
+++ b/internal/admin/static/admin.css
@@ -2,7 +2,7 @@
*{box-sizing:border-box;margin:0;padding:0}
input[type=number]{-moz-appearance:textfield}
input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
-body{font-family:monospace;background:var(--bg);color:var(--text);min-height:100vh}
+body{font-family:monospace;background:var(--bg);color:var(--text);display:flex;flex-direction:column;height:100vh;overflow:hidden}
.nav{background:var(--panel);border-bottom:1px solid var(--border);padding:8px 16px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.nav a{color:var(--text);text-decoration:none;padding:4px 10px;border-radius:3px;font-size:13px}
.nav a:hover,.nav a.active{background:var(--accent)}
@@ -11,7 +11,7 @@ body{font-family:monospace;background:var(--bg);color:var(--text);min-height:100
.nav .undo-bar button:disabled{opacity:.4;cursor:default}
.nav .undo-bar button:not(:disabled):hover{background:var(--accent)}
.nav .undo-bar span{color:#888;flex:1;text-align:right;margin-right:8px}
-.layout{display:flex;height:calc(100vh - 38px)}
+.layout{display:flex;flex:1;min-height:0}
.main{flex:1;overflow:auto;position:relative;user-select:none;-webkit-user-select:none}
#mapSvg{display:block}
.panel{width:300px;background:var(--panel);border-left:1px solid var(--border);overflow-y:auto;padding:16px;flex-shrink:0;position:relative}
@@ -187,7 +187,7 @@ body{font-family:monospace;background:var(--bg);color:var(--text);min-height:100
.color-picker{display:flex;flex-wrap:wrap;gap:2px;margin-top:4px;max-height:200px;overflow-y:auto}
.color-picker .swatch{width:18px;height:18px;border:1px solid transparent;cursor:pointer;border-radius:1px}
.color-picker .swatch:hover,.color-picker .swatch.selected{border-color:#fff;transform:scale(1.2);z-index:1}
-.editor-container{display:flex;height:calc(100vh - 38px)}
+.editor-container{display:flex;flex:1;min-height:0}
.editor-list{width:260px;background:var(--panel);border-right:1px solid var(--border);overflow-y:auto;padding:10px;flex-shrink:0}
.editor-main{flex:1;overflow-y:auto;padding:16px}
.editor-list .item{padding:5px 8px;font-size:12px;cursor:pointer;border-radius:3px;margin-bottom:2px}
@@ -202,6 +202,7 @@ body{font-family:monospace;background:var(--bg);color:var(--text);min-height:100
.dir-count{font-size:9px;color:#555}
.dir-items{padding-left:10px}
.dir-items .item{font-size:11px}
+.dir-drop-target{background:rgba(15,52,96,.3)!important;outline:1px dashed var(--accent);outline-offset:-2px}
.editor-main pre{background:var(--input-bg);padding:12px;border-radius:4px;font-size:12px;overflow-x:auto;border:1px solid var(--border)}
.form-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:4px 10px}
.form-grid .form-group{margin-bottom:4px}
@@ -306,7 +307,7 @@ g.ud-hover:hover text{font-weight:bold}
.obj-sub-remove{align-self:flex-end;padding:2px 6px;font-size:10px;min-width:auto;background:var(--danger);color:#fff;border:none;border-radius:3px;cursor:pointer;margin-bottom:3px}
.obj-sub-add{display:block;width:100%;margin-top:4px;padding:4px 8px;font-size:11px;background:var(--accent);color:var(--text);border:1px solid #1a5a8e;border-radius:3px;cursor:pointer;font-family:monospace}
.obj-sub-add:hover{background:var(--hover)}
-.add-section-bar{display:flex;gap:6px;align-items:center;margin-top:12px;padding:8px 0;border-top:1px solid var(--border);max-width:calc(50% - 5px)}
+.add-section-bar{display:flex;gap:6px;align-items:center;margin-top:12px;padding:8px 0;border-top:1px solid var(--border);max-width:100%;flex-wrap:wrap}
.add-section-bar select{padding:4px 8px;font-size:11px;background:var(--input-bg);color:var(--text);border:1px solid var(--input-border);border-radius:3px;font-family:monospace;flex:1}
.add-section-bar button{padding:4px 12px;font-size:11px}
.obj-kv-list{margin-top:4px}
diff --git a/internal/admin/static/dropeditor.js b/internal/admin/static/dropeditor.js
index dddcddb..cbd51c7 100644
--- a/internal/admin/static/dropeditor.js
+++ b/internal/admin/static/dropeditor.js
@@ -66,7 +66,7 @@ function renderDropEditor(id, data) {
html += renderCard(sec, data);
});
html += '</div>';
- html += renderMoveBar();
+ html += '<div class="add-section-bar">' + renderMoveBar() + '</div>';
html += '</div>';
html += '<div class="tab-content" id="tabYaml" style="display:none">';
@@ -76,6 +76,7 @@ function renderDropEditor(id, data) {
html += '<div class="btn-row">';
html += '<button class="btn btn-primary" onclick="saveDrop()">Save</button>';
html += '<button class="btn btn-danger" onclick="deleteDrop()">Delete</button>';
+ html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>';
html += '</div>';
$('#editorMain').innerHTML = html;
diff --git a/internal/admin/static/editor.js b/internal/admin/static/editor.js
index 35bdbe6..5b2d8ef 100644
--- a/internal/admin/static/editor.js
+++ b/internal/admin/static/editor.js
@@ -52,7 +52,7 @@ function renderList(filter) {
}
function renderDirGroup(name, ids, filter) {
- if (ids.length === 0 && !filter) return '';
+ if (ids.length === 0 && !filter && !name) return '';
var isFiltering = !!filter;
var displayIds = ids;
if (isFiltering) {
@@ -63,15 +63,15 @@ function renderDirGroup(name, ids, filter) {
var displayName = name || editorType.charAt(0).toUpperCase() + editorType.slice(1);
var h = '<div class="dir-group">';
if (name) {
- h += '<div class="dir-header" onclick="toggleDir(\'' + escAttr(name) + '\')">';
+ h += '<div class="dir-header" onclick="toggleDir(\'' + escAttr(name) + '\')" oncontextmenu="showDirContextMenu(event,\'' + escAttr(name) + '\')" ondragover="dirDragOver(event,\'' + escAttr(name) + '\')" ondragleave="dirDragLeave(event)" ondrop="dirDrop(event,\'' + escAttr(name) + '\')">';
h += '<span class="dir-arrow">' + (expanded ? '&#9660;' : '&#9654;') + '</span>';
h += '<span class="dir-name">' + esc(displayName) + '</span>';
h += '<span class="dir-count">' + (isFiltering ? displayIds.length + '/' + ids.length : ids.length) + '</span>';
h += '</div>';
}
- h += '<div class="dir-items"' + (name && !expanded ? ' style="display:none"' : '') + '>';
+ h += '<div class="dir-items"' + (name && !expanded ? ' style="display:none"' : '') + ' ondragover="dirDragOver(event,\'' + (name || '') + '\')" ondragleave="dirDragLeave(event)" ondrop="dirDrop(event,\'' + (name || '') + '\')">';
displayIds.forEach(function(id) {
- h += '<div class="item' + (id === currentID ? ' active' : '') + '" onclick="loadItem(\'' + esc(id) + '\')">' + esc(id) + '</div>';
+ h += '<div class="item' + (id === currentID ? ' active' : '') + '" onclick="loadItem(\'' + esc(id) + '\')" draggable="true" ondragstart="itemDragStart(event,\'' + escAttr(id) + '\')">' + esc(id) + '</div>';
});
h += '</div>';
h += '</div>';
@@ -236,8 +236,7 @@ window.refreshPage = function() { loadList(); if (currentID) loadItem(currentID)
function renderMoveBar() {
var dirs = Object.keys(treeData.dirs).sort();
- var html = '<div class="add-section-bar">';
- html += '<select id="moveDirSelect">';
+ var html = '<select id="moveDirSelect">';
html += '<option value="">Move to directory...</option>';
html += '<option value="">Root</option>';
dirs.forEach(function(dir) {
@@ -245,7 +244,6 @@ function renderMoveBar() {
});
html += '</select>';
html += '<button class="btn btn-primary btn-sm" onclick="moveToDirectory()">Move</button>';
- html += '</div>';
return html;
}
@@ -254,7 +252,6 @@ function moveToDirectory() {
var idx = sel.selectedIndex;
if (idx === 0) return;
var dir = sel.value;
- if (!confirm('Move ' + currentID + ' to ' + (dir || 'Root') + '?')) return;
API.post('/api/' + editorType + '/move', {id: currentID, dir: dir}).then(function() {
notify('Moved ' + currentID + ' to ' + (dir || 'Root'), 'success');
updateUndoBar();
@@ -262,3 +259,72 @@ function moveToDirectory() {
loadItem(currentID);
}).catch(function(e) { notify('Move failed: ' + e.message, 'error'); });
}
+
+function createNewDir() {
+ var name = prompt('New directory name:');
+ if (!name) return;
+ API.post('/api/' + editorType + '/dirs', {action: 'create', name: name}).then(function() {
+ notify('Directory ' + name + ' created', 'success');
+ updateUndoBar();
+ loadList();
+ }).catch(function(e) { notify('Failed to create directory: ' + e.message, 'error'); });
+}
+
+function showDirContextMenu(e, dir) {
+ e.preventDefault();
+ if (dir === 'Root') return;
+ var overlay = document.createElement('div');
+ overlay.className = 'cm-overlay';
+ overlay.onclick = function() { overlay.remove(); menu.remove(); };
+
+ var menu = document.createElement('div');
+ menu.className = 'cm-menu';
+ menu.style.left = e.clientX + 'px';
+ menu.style.top = e.clientY + 'px';
+
+ var delBtn = document.createElement('button');
+ delBtn.textContent = 'Delete Directory';
+ delBtn.className = 'danger';
+ delBtn.onclick = function() {
+ overlay.remove(); menu.remove();
+ API.post('/api/' + editorType + '/dirs', {action: 'delete', dir: dir}).then(function(r) {
+ notify('Deleted directory ' + dir + (r.moved ? ' (' + r.moved + ' files moved to root)' : ''), 'success');
+ updateUndoBar();
+ loadList();
+ }).catch(function(e) { notify('Delete failed: ' + e.message, 'error'); });
+ };
+ menu.appendChild(delBtn);
+
+ document.body.appendChild(overlay);
+ document.body.appendChild(menu);
+}
+
+function dirDragOver(e, dir) {
+ e.preventDefault();
+ e.stopPropagation();
+ e.currentTarget.classList.add('dir-drop-target');
+}
+
+function dirDragLeave(e) {
+ e.currentTarget.classList.remove('dir-drop-target');
+}
+
+function dirDrop(e, dir) {
+ e.preventDefault();
+ e.stopPropagation();
+ e.currentTarget.classList.remove('dir-drop-target');
+ if (dir === 'Root') dir = '';
+ var id = e.dataTransfer.getData('text/plain');
+ if (!id) return;
+ API.post('/api/' + editorType + '/move', {id: id, dir: dir}).then(function() {
+ notify('Moved ' + id + ' to ' + (dir || 'Root'), 'success');
+ updateUndoBar();
+ loadList();
+ if (currentID === id) loadItem(id);
+ }).catch(function(e) { notify('Move failed: ' + e.message, 'error'); });
+}
+
+function itemDragStart(e, id) {
+ e.dataTransfer.setData('text/plain', id);
+ e.dataTransfer.effectAllowed = 'move';
+}
diff --git a/internal/admin/static/hazardeditor.js b/internal/admin/static/hazardeditor.js
index ad317cc..ac35e78 100644
--- a/internal/admin/static/hazardeditor.js
+++ b/internal/admin/static/hazardeditor.js
@@ -89,9 +89,9 @@ function renderHazardEditor(id, data) {
});
html += '</select>';
html += '<button class="btn btn-primary btn-sm" onclick="addSection()">Add</button>';
- html += '</div>';
html += renderMoveBar();
html += '</div>';
+ html += '</div>';
html += '<div class="tab-content" id="tabYaml" style="display:none">';
html += '<pre>' + esc(data._raw || JSON.stringify(data, null, 2)) + '</pre>';
@@ -100,6 +100,7 @@ function renderHazardEditor(id, data) {
html += '<div class="btn-row">';
html += '<button class="btn btn-primary" onclick="saveHazard()">Save</button>';
html += '<button class="btn btn-danger" onclick="deleteHazard()">Delete</button>';
+ html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>';
html += '</div>';
$('#editorMain').innerHTML = html;
diff --git a/internal/admin/static/itemeditor.js b/internal/admin/static/itemeditor.js
index ce28581..84090a1 100644
--- a/internal/admin/static/itemeditor.js
+++ b/internal/admin/static/itemeditor.js
@@ -199,9 +199,9 @@ function renderItemEditor(id, data) {
});
html += '</select>';
html += '<button class="btn btn-primary btn-sm" onclick="addSection()">Add</button>';
- html += '</div>';
html += renderMoveBar();
html += '</div>';
+ html += '</div>';
html += '<div class="tab-content" id="tabYaml" style="display:none">';
html += '<pre>' + esc(data._raw || JSON.stringify(data, null, 2)) + '</pre>';
@@ -211,6 +211,7 @@ function renderItemEditor(id, data) {
html += '<button class="btn btn-primary" onclick="saveItem()">Save</button>';
html += '<button class="btn" onclick="duplicateItem()">Duplicate</button>';
html += '<button class="btn btn-danger" onclick="deleteItem()">Delete Item</button>';
+ html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>';
html += '</div>';
$('#editorMain').innerHTML = html;
diff --git a/internal/admin/static/mobeditor.js b/internal/admin/static/mobeditor.js
index 802b48a..1758673 100644
--- a/internal/admin/static/mobeditor.js
+++ b/internal/admin/static/mobeditor.js
@@ -173,9 +173,9 @@ function renderMobEditor(id, data) {
});
html += '</select>';
html += '<button class="btn btn-primary btn-sm" onclick="mobAddSection()">Add</button>';
+ html += renderMoveBar();
html += '</div>';
- html += renderMoveBar();
html += '</div>';
html += '<div class="tab-content" id="tabYaml" style="display:none">';
@@ -186,6 +186,7 @@ function renderMobEditor(id, data) {
html += '<button class="btn btn-primary" onclick="saveMob()">Save</button>';
html += '<button class="btn" onclick="duplicateMob()">Duplicate</button>';
html += '<button class="btn btn-danger" onclick="deleteMob()">Delete Mob</button>';
+ html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>';
html += '</div>';
$('#editorMain').innerHTML = html;
diff --git a/internal/admin/static/moduleeditor.js b/internal/admin/static/moduleeditor.js
index 1e7de30..6d6a3e0 100644
--- a/internal/admin/static/moduleeditor.js
+++ b/internal/admin/static/moduleeditor.js
@@ -90,9 +90,9 @@ function renderModuleEditor(id, data) {
});
html += '</select>';
html += '<button class="btn btn-primary btn-sm" onclick="addSection()">Add</button>';
- html += '</div>';
html += renderMoveBar();
html += '</div>';
+ html += '</div>';
html += '<div class="tab-content" id="tabYaml" style="display:none">';
html += '<pre>' + esc(data._raw || JSON.stringify(data, null, 2)) + '</pre>';
@@ -101,6 +101,7 @@ function renderModuleEditor(id, data) {
html += '<div class="btn-row">';
html += '<button class="btn btn-primary" onclick="saveModule()">Save</button>';
html += '<button class="btn btn-danger" onclick="deleteModule()">Delete</button>';
+ html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>';
html += '</div>';
$('#editorMain').innerHTML = html;
diff --git a/internal/admin/static/objecteditor.js b/internal/admin/static/objecteditor.js
index c51628f..3f2a875 100644
--- a/internal/admin/static/objecteditor.js
+++ b/internal/admin/static/objecteditor.js
@@ -175,9 +175,9 @@ function renderObjectEditor(id, data) {
});
html += '</select>';
html += '<button class="btn btn-primary btn-sm" onclick="addSection()">Add</button>';
+ html += renderMoveBar();
html += '</div>';
- html += renderMoveBar();
html += '</div>';
html += '<div class="tab-content" id="tabYaml" style="display:none">';
@@ -188,6 +188,7 @@ function renderObjectEditor(id, data) {
html += '<button class="btn btn-primary" onclick="saveObject()">Save</button>';
html += '<button class="btn" onclick="duplicateObject()">Duplicate</button>';
html += '<button class="btn btn-danger" onclick="deleteObject()">Delete Object</button>';
+ html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>';
html += '</div>';
$('#editorMain').innerHTML = html;
diff --git a/internal/admin/static/techeditor.js b/internal/admin/static/techeditor.js
index 4849fb1..cd4f50f 100644
--- a/internal/admin/static/techeditor.js
+++ b/internal/admin/static/techeditor.js
@@ -80,7 +80,7 @@ function renderTechEditor(id, data) {
html += renderCard(sec, data);
});
html += '</div>';
- html += renderMoveBar();
+ html += '<div class="add-section-bar">' + renderMoveBar() + '</div>';
html += '</div>';
html += '<div class="tab-content" id="tabYaml" style="display:none">';
@@ -90,6 +90,7 @@ function renderTechEditor(id, data) {
html += '<div class="btn-row">';
html += '<button class="btn btn-primary" onclick="saveTech()">Save</button>';
html += '<button class="btn btn-danger" onclick="deleteTech()">Delete</button>';
+ html += '<button class="btn btn-sm" style="margin-left:auto" onclick="createNewDir()">New Directory</button>';
html += '</div>';
$('#editorMain').innerHTML = html;