diff --git a/admin-web/index.html b/admin-web/index.html index 5ffcabd..6f4834c 100644 --- a/admin-web/index.html +++ b/admin-web/index.html @@ -39,6 +39,14 @@ .runtime-const-group-title { font-size: 14px; margin: 0 0 8px; font-weight: 600; color: #cfe3ff; } .modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.5); display: flex; align-items: center; justify-content: center; } .modal { width: 420px; max-width: 95vw; background: #151b2a; border: 1px solid #2a3551; border-radius: 8px; padding: 14px; } + .live-ws-bar { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; margin-top: 8px; } + .live-ws-bar-main { flex: 1; min-width: 220px; } + .live-ws-bar-actions { flex-shrink: 0; display: flex; flex-direction: column; gap: 6px; align-items: stretch; } + .live-ws-bar-actions .btn { margin-top: 0; margin-right: 0; } + details.live-details > summary { cursor: pointer; color: #cfe3ff; font-weight: 600; list-style-position: outside; } + details.live-details > summary::-webkit-details-marker { color: #9eb0d6; } + .live-json-pre { max-height: 320px; overflow: auto; font-size: 11px; margin: 8px 0 0; padding: 10px; background: #0f1522; border: 1px solid #2f3b5a; border-radius: 6px; white-space: pre-wrap; word-break: break-word; } + .quest-world-panel { margin-top: 12px; padding-top: 12px; border-top: 1px solid #2a3551; }
@@ -86,6 +94,8 @@ _heroLiveWsStatus: "disconnected", _heroLiveWsError: "", _heroLiveWsLastAt: null, + _liveSnapshotOpen: false, + _heroQuestWorldOpen: false, }; state._confirmAction = null; @@ -562,6 +572,26 @@ const [info, engine] = await Promise.all([api("info"), api("engine/status")]); state.serverInfo = info; state.engine = engine; render(); } + function toggleLiveSnapshotOpen(ev) { + if (ev) ev.preventDefault(); + state._liveSnapshotOpen = !state._liveSnapshotOpen; + render(); + } + function toggleHeroQuestWorldOpen(ev) { + if (ev) ev.preventDefault(); + state._heroQuestWorldOpen = !state._heroQuestWorldOpen; + render(); + } + async function pauseServerTime() { + await api("time/pause", { method: "POST", body: "{}" }); + await loadServer(); + setMessage("Время на сервере приостановлено"); + } + async function resumeServerTime() { + await api("time/resume", { method: "POST", body: "{}" }); + await loadServer(); + setMessage("Время на сервере возобновлено"); + } async function searchHeroes() { const q = document.getElementById("hero-query")?.value || ""; const data = await api(`heroes?limit=50&offset=0&query=${encodeURIComponent(q)}`); @@ -710,15 +740,33 @@ const last = state._heroLiveWsLastAt ? new Date(state._heroLiveWsLastAt).toLocaleTimeString() : "—"; const heroId = state._heroLiveWsHeroId; const err = state._heroLiveWsError; + const h = state.selectedHero; + const jsonSnap = h && h.id + ? `${e(JSON.stringify(h, null, 2))}`
+ : `Нет JSON — подключите live или дождитесь первого сообщения.
`; + const openAttr = state._liveSnapshotOpen ? " open" : ""; return `Endpoint: /admin-ws/hero/{heroId} (BasicAuth from saved credentials).
+| ID | Slot | Name | Rarity | iLvl | Actions |
|---|---|---|---|---|---|
| No inventory items | |||||
| QuestID | Title | Status | Progress | Actions |
|---|---|---|---|---|
| No quests for hero | ||||
| ID | Title | Type | Rewards | Action |
|---|---|---|---|---|
| Select NPC | ||||
Полный обзор городов и NPC есть на вкладке «Towns».
+ + +| ID | Title | Type | Rewards | Action |
|---|---|---|---|---|
| Выберите NPC | ||||