diff options
Diffstat (limited to 'internal/admin/static')
| -rw-r--r-- | internal/admin/static/admin.css | 9 | ||||
| -rw-r--r-- | internal/admin/static/dropeditor.js | 3 | ||||
| -rw-r--r-- | internal/admin/static/editor.js | 82 | ||||
| -rw-r--r-- | internal/admin/static/hazardeditor.js | 3 | ||||
| -rw-r--r-- | internal/admin/static/itemeditor.js | 3 | ||||
| -rw-r--r-- | internal/admin/static/mobeditor.js | 3 | ||||
| -rw-r--r-- | internal/admin/static/moduleeditor.js | 3 | ||||
| -rw-r--r-- | internal/admin/static/objecteditor.js | 3 | ||||
| -rw-r--r-- | internal/admin/static/techeditor.js | 3 |
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 ? '▼' : '▶') + '</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; |
