From 00554b66df9310073c3b5e1aebd125ea54d9d554 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Mon, 6 Jul 2026 17:41:12 -0400 Subject: feat: shift+drag to create one-way links on admin map --- internal/admin/static/admin.css | 2 + internal/admin/static/map.js | 99 ++++++++++++++++++++++++++--------------- 2 files changed, 66 insertions(+), 35 deletions(-) (limited to 'internal/admin/static') diff --git a/internal/admin/static/admin.css b/internal/admin/static/admin.css index 376bf23..c620b46 100644 --- a/internal/admin/static/admin.css +++ b/internal/admin/static/admin.css @@ -51,6 +51,8 @@ body{font-family:monospace;background:var(--bg);color:var(--text);display:flex;f .z-controls .z-sep{width:1px;height:20px;background:var(--border);flex-shrink:0} .z-dir-form{display:none;position:absolute;left:0;top:calc(100% + 4px);background:var(--panel);border:1px solid var(--border);border-radius:4px;padding:8px 10px;z-index:11;gap:6px;align-items:center;white-space:nowrap} .room-tooltip{position:absolute;background:var(--panel);border:1px solid var(--border);padding:6px 10px;border-radius:3px;font-size:11px;pointer-events:none;z-index:20;white-space:nowrap} +.link-mode-badge{position:absolute;bottom:12px;right:12px;padding:6px 12px;border-radius:4px;font-size:12px;font-family:monospace;pointer-events:none;z-index:20;background:rgba(30,30,60,0.9);color:#ccc;border:1px solid var(--border)} +.link-mode-badge.oneway{background:rgba(80,50,20,0.92);color:#fca;border:1px solid #a64} .notification{position:fixed;bottom:16px;right:16px;background:var(--panel);border:1px solid var(--border);padding:10px 16px;border-radius:4px;font-size:12px;z-index:100;animation:fadeIn .2s} .notification.error{border-color:var(--danger)} .notification.success{border-color:var(--success)} diff --git a/internal/admin/static/map.js b/internal/admin/static/map.js index f0b4d87..f89a434 100644 --- a/internal/admin/static/map.js +++ b/internal/admin/static/map.js @@ -4,6 +4,8 @@ var panX = 0, panY = 0, scale = 1; var dragging = false, startX = 0, startY = 0, prevX = 0, prevY = 0; var dragRoom = false; var linkDrag = false, linkFromId = null, linkTargetId = null, linkGhostDir = null; +var linkDragOneWay = false; +var shiftHeld = false; var delDrag = false, delFromId = null, delTargetId = null; var roomMap = {}, occupied = {}; var dirChangedByUser = false; @@ -35,6 +37,7 @@ function onMapMouseMove(e) { if (linkFromId && moved) { linkDrag = true; + showDragBadge(linkDragOneWay ? 'One-way link' : 'Two-way link'); updateLinkDragTarget(e); } } @@ -62,7 +65,7 @@ function onMapMouseUp(e) { } if (linkDrag && linkFromId) { - if (linkTargetId) { createLink(linkFromId, linkTargetId); } + if (linkTargetId) { createLink(linkFromId, linkTargetId, linkDragOneWay); } else if (linkGhostDir) { createRoom(linkFromId, linkGhostDir); } } else if (!moved && !delDrag) { if (e.target.classList.contains('rm')) { @@ -74,8 +77,9 @@ function onMapMouseUp(e) { clearLinkHighlight(); clearDelHighlight(); + hideDragBadge(); dragging = false; dragRoom = false; - linkDrag = false; linkFromId = null; linkTargetId = null; linkGhostDir = null; + linkDrag = false; linkFromId = null; linkTargetId = null; linkGhostDir = null; linkDragOneWay = false; delDrag = false; delFromId = null; delTargetId = null; } @@ -86,11 +90,25 @@ function cancelMapDrag() { _mapMouseCleanup = false; clearLinkHighlight(); clearDelHighlight(); + hideDragBadge(); dragging = false; dragRoom = false; - linkDrag = false; linkFromId = null; linkTargetId = null; linkGhostDir = null; + linkDrag = false; linkFromId = null; linkTargetId = null; linkGhostDir = null; linkDragOneWay = false; delDrag = false; delFromId = null; delTargetId = null; } +function showDragBadge(text) { + var badge = document.getElementById('linkModeBadge'); + if (!badge) return; + badge.textContent = text; + badge.className = 'link-mode-badge' + (linkDragOneWay ? ' oneway' : ''); + badge.style.display = ''; +} + +function hideDragBadge() { + var badge = document.getElementById('linkModeBadge'); + if (badge) badge.style.display = 'none'; +} + function changeZ(dz) { if (dz === 0) currentZ = 0; else currentZ += dz; panX = 0; panY = 0; scale = 1; @@ -299,6 +317,7 @@ function renderMap(data) { return; } + e.preventDefault(); dragRoom = onRoom || onGhost; dragging = true; startX = e.clientX; startY = e.clientY; @@ -307,6 +326,7 @@ function renderMap(data) { linkDrag = false; linkTargetId = null; linkGhostDir = null; + linkDragOneWay = onRoom ? (e.shiftKey || shiftHeld) : false; delDrag = false; delFromId = null; delTargetId = null; clearLinkHighlight(); document.addEventListener('mousemove', onMapMouseMove); @@ -1160,13 +1180,16 @@ function renderExitsSection(r) { var setPlayerFlags = (typeof exit === 'object' && exit.set_player_flags) ? exit.set_player_flags : null; var hidden = (typeof exit === 'object' && exit.hidden) ? exit.hidden : false; var isConditional = !!(cond || bmsg || setFlags || setPlayerFlags || hidden); + var isBidi = mapData && mapData.links && mapData.links.some(function(l) { + return l.bidirectional && ((l.from === r.id && l.to === target) || (l.from === target && l.to === r.id)); + }); h += '