From ad22ea3526a146ea8ce305c8375c35836465cc30 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Sat, 4 Jul 2026 22:44:42 -0400 Subject: feat: admin gui move objects between directories and better toolbar wrapping --- internal/admin/server.go | 43 ++++++++++++++++++++++++++++++++-- internal/admin/static/admin.css | 2 +- internal/admin/static/dropeditor.js | 3 ++- internal/admin/static/editor.js | 29 +++++++++++++++++++++++ internal/admin/static/hazardeditor.js | 3 ++- internal/admin/static/itemeditor.js | 3 ++- internal/admin/static/mobeditor.js | 3 ++- internal/admin/static/moduleeditor.js | 3 ++- internal/admin/static/objecteditor.js | 3 ++- internal/admin/static/techeditor.js | 3 ++- internal/admin/yaml_util.go | 44 +++++++++++++++++++++++++++++++++++ 11 files changed, 129 insertions(+), 10 deletions(-) (limited to 'internal/admin') diff --git a/internal/admin/server.go b/internal/admin/server.go index ef95e73..b1f641a 100644 --- a/internal/admin/server.go +++ b/internal/admin/server.go @@ -117,20 +117,25 @@ func NewServer(cfg *config.Config, useTLS bool, accountStore *player.AccountStor apiMux.HandleFunc("/api/rooms", s.handleRooms) apiMux.HandleFunc("/api/rooms/new-empty", s.handleCreateEmptyRoom) apiMux.HandleFunc("/api/rooms/", s.handleRoomByID) + apiMux.HandleFunc("/api/objects/move", s.handleEntityMove("objects")) apiMux.HandleFunc("/api/objects", s.handleObjects) apiMux.HandleFunc("/api/objects/", s.handleObjectByID) + apiMux.HandleFunc("/api/items/move", s.handleEntityMove("items")) apiMux.HandleFunc("/api/items", s.handleItems) apiMux.HandleFunc("/api/items/", s.handleItemByID) + apiMux.HandleFunc("/api/mobs/move", s.handleEntityMove("mobs")) apiMux.HandleFunc("/api/mobs", s.handleMobs) apiMux.HandleFunc("/api/mobs/", s.handleMobByID) + apiMux.HandleFunc("/api/drops/move", s.handleEntityMove("drops")) apiMux.HandleFunc("/api/drops", s.handleDrops) apiMux.HandleFunc("/api/drops/", s.handleDropByID) + apiMux.HandleFunc("/api/hazards/move", s.handleEntityMove("hazards")) apiMux.HandleFunc("/api/hazards", s.handleHazards) apiMux.HandleFunc("/api/hazards/", s.handleHazardByID) + apiMux.HandleFunc("/api/techs/move", s.handleEntityMove("techs")) apiMux.HandleFunc("/api/techs", s.handleTechs) apiMux.HandleFunc("/api/techs/", s.handleTechByID) - apiMux.HandleFunc("/api/courses", s.handleCourses) - apiMux.HandleFunc("/api/courses/", s.handleCourseByID) + apiMux.HandleFunc("/api/modules/move", s.handleEntityMove("modules")) apiMux.HandleFunc("/api/modules", s.handleModules) apiMux.HandleFunc("/api/modules/", s.handleModuleByID) apiMux.HandleFunc("/api/players", s.handlePlayers) @@ -461,6 +466,40 @@ func (s *AdminServer) doRedo(w http.ResponseWriter, r *http.Request) { writeJSON(w, resp) } +func (s *AdminServer) handleEntityMove(entityType string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + var body struct { + ID string `json:"id"` + Dir string `json:"dir"` + } + if err := readJSON(r, &body); err != nil || body.ID == "" { + writeJSON(w, map[string]any{"error": "invalid"}) + return + } + oldPath, newPath, data, err := moveEntityFile(s.dataDir, entityType, body.ID, body.Dir) + if err != nil { + writeJSON(w, map[string]any{"error": err.Error()}) + return + } + desc := fmt.Sprintf("move %s %s to %s/", entityType, body.ID, body.Dir) + if body.Dir == "" { + desc = fmt.Sprintf("move %s %s to root", entityType, body.ID) + } + s.undoStack.Push(ChangeDesc{ + Description: desc, + FilePath: oldPath, + NewFilePath: newPath, + OldContent: data, + NewContent: data, + }) + writeJSON(w, map[string]any{"ok": true}) + } +} + func (s *AdminServer) rebuildAfterUndo(change *ChangeDesc) { s.world.RebuildRoomIndex(s.dataDir) s.world.ClearHazardCache() diff --git a/internal/admin/static/admin.css b/internal/admin/static/admin.css index f068207..42e64fa 100644 --- a/internal/admin/static/admin.css +++ b/internal/admin/static/admin.css @@ -42,7 +42,7 @@ body{font-family:monospace;background:var(--bg);color:var(--text);min-height:100 .login{max-width:360px;margin:80px auto;background:var(--panel);padding:30px;border:1px solid var(--border);border-radius:6px} .login h1{font-size:18px;margin-bottom:20px;text-align:center} .z-controls-wrapper{position:absolute;top:12px;left:12px;z-index:10} -.z-controls{position:relative;display:flex;gap:6px;align-items:center;background:var(--panel);padding:6px 10px;border-radius:4px;border:1px solid var(--border)} +.z-controls{position:relative;display:flex;gap:6px;align-items:center;flex-wrap:wrap;background:var(--panel);padding:6px 10px;border-radius:4px;border:1px solid var(--border)} .z-controls button{width:28px;height:28px;font-size:16px;line-height:1;background:var(--input-bg);color:var(--text);border:1px solid var(--border);border-radius:3px;cursor:pointer} .z-controls button:hover{background:var(--accent)} .z-controls .z-label{font-size:13px;min-width:40px;text-align:center;font-weight:bold} diff --git a/internal/admin/static/dropeditor.js b/internal/admin/static/dropeditor.js index 1569a04..dddcddb 100644 --- a/internal/admin/static/dropeditor.js +++ b/internal/admin/static/dropeditor.js @@ -66,6 +66,7 @@ function renderDropEditor(id, data) { html += renderCard(sec, data); }); html += ''; + html += renderMoveBar(); html += ''; html += '
'; html += '