618 lines
23 KiB
HTML
618 lines
23 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ro">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Red Valley — NUI Simulator</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
:root {
|
||
--bg-primary: #0a0b0d;
|
||
--bg-secondary: #111318;
|
||
--bg-tertiary: #181a1f;
|
||
--bg-card: #1a1c22;
|
||
--accent: #ff1a35;
|
||
--accent-glow: rgba(255, 26, 53, 0.15);
|
||
--text-primary: #ffffff;
|
||
--text-secondary: #8b8d94;
|
||
--text-muted: #555760;
|
||
--border: #222430;
|
||
--border-hover: #333540;
|
||
--success: #22c55e;
|
||
--warning: #f59e0b;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Inter', sans-serif;
|
||
background: var(--bg-primary);
|
||
color: var(--text-primary);
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.app { display: flex; height: 100vh; }
|
||
|
||
/* ====== SIDEBAR ====== */
|
||
.sidebar {
|
||
width: 300px;
|
||
background: var(--bg-secondary);
|
||
border-right: 1px solid var(--border);
|
||
display: flex;
|
||
flex-direction: column;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.sidebar-header {
|
||
padding: 16px 16px 12px;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
|
||
.sidebar-header h1 {
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.sidebar-header h1 .badge {
|
||
font-size: 10px; font-weight: 600;
|
||
background: var(--accent); padding: 2px 7px;
|
||
border-radius: 4px; letter-spacing: 0.5px;
|
||
}
|
||
|
||
.sidebar-header .subtitle {
|
||
font-size: 11px; color: var(--text-muted); margin-top: 3px;
|
||
display: flex; align-items: center; gap: 6px;
|
||
}
|
||
|
||
.sidebar-header .count {
|
||
background: var(--bg-tertiary); padding: 1px 6px;
|
||
border-radius: 3px; font-weight: 600; color: var(--accent);
|
||
}
|
||
|
||
.search-box {
|
||
padding: 10px 16px;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
|
||
.search-box input {
|
||
width: 100%; height: 34px;
|
||
background: var(--bg-tertiary);
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
color: var(--text-primary);
|
||
font-size: 12px; font-family: 'Inter', sans-serif;
|
||
padding: 0 12px 0 34px;
|
||
outline: none; transition: border-color 0.2s;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23555760' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E");
|
||
background-repeat: no-repeat;
|
||
background-position: 10px center;
|
||
}
|
||
|
||
.search-box input:focus { border-color: var(--accent); }
|
||
.search-box input::placeholder { color: var(--text-muted); }
|
||
|
||
.filter-bar {
|
||
display: flex; gap: 4px;
|
||
padding: 8px 16px;
|
||
border-bottom: 1px solid var(--border);
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.filter-pill {
|
||
font-size: 10px; font-weight: 500;
|
||
padding: 4px 10px; border-radius: 4px;
|
||
background: var(--bg-tertiary);
|
||
border: 1px solid var(--border);
|
||
color: var(--text-muted);
|
||
cursor: pointer; transition: all 0.15s;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.filter-pill:hover { color: var(--text-primary); border-color: var(--border-hover); }
|
||
.filter-pill.active { background: var(--accent-glow); border-color: var(--accent); color: var(--accent); }
|
||
|
||
.nui-list {
|
||
flex: 1; overflow-y: auto; padding: 6px;
|
||
}
|
||
|
||
.nui-list::-webkit-scrollbar { width: 4px; }
|
||
.nui-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||
|
||
.nui-item {
|
||
display: flex; align-items: center; gap: 8px;
|
||
padding: 8px 10px; border-radius: 6px;
|
||
cursor: pointer; transition: all 0.12s;
|
||
margin-bottom: 1px; border: 1px solid transparent;
|
||
}
|
||
|
||
.nui-item:hover { background: var(--bg-tertiary); }
|
||
|
||
.nui-item.active {
|
||
background: var(--accent-glow);
|
||
border-color: rgba(255, 26, 53, 0.3);
|
||
}
|
||
|
||
.nui-item .icon {
|
||
width: 30px; height: 30px; border-radius: 5px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 13px; flex-shrink: 0;
|
||
background: var(--bg-tertiary); border: 1px solid var(--border);
|
||
}
|
||
|
||
.nui-item.active .icon { background: var(--accent); border-color: var(--accent); }
|
||
|
||
.nui-item .info { flex: 1; min-width: 0; }
|
||
|
||
.nui-item .info .name {
|
||
font-size: 12px; font-weight: 600;
|
||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||
}
|
||
|
||
.nui-item .info .path {
|
||
font-size: 10px; color: var(--text-muted);
|
||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||
}
|
||
|
||
.nui-item .tag {
|
||
font-size: 9px; font-weight: 600;
|
||
padding: 2px 5px; border-radius: 3px;
|
||
flex-shrink: 0; text-transform: uppercase;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
|
||
.nui-item.missing .name { color: var(--text-muted); text-decoration: line-through; }
|
||
|
||
/* ====== MAIN ====== */
|
||
.main {
|
||
flex: 1; display: flex; flex-direction: column; min-width: 0;
|
||
}
|
||
|
||
.toolbar {
|
||
height: 44px; background: var(--bg-secondary);
|
||
border-bottom: 1px solid var(--border);
|
||
display: flex; align-items: center;
|
||
padding: 0 12px; gap: 6px;
|
||
}
|
||
|
||
.toolbar .btn {
|
||
height: 28px; padding: 0 10px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 5px;
|
||
background: var(--bg-tertiary);
|
||
color: var(--text-secondary);
|
||
font-size: 11px; font-weight: 500;
|
||
font-family: 'Inter', sans-serif;
|
||
cursor: pointer; display: flex;
|
||
align-items: center; gap: 4px;
|
||
transition: all 0.12s;
|
||
}
|
||
|
||
.toolbar .btn:hover {
|
||
background: var(--bg-card); color: var(--text-primary);
|
||
border-color: var(--border-hover);
|
||
}
|
||
|
||
.toolbar .sep {
|
||
width: 1px; height: 20px;
|
||
background: var(--border); margin: 0 4px;
|
||
}
|
||
|
||
.toolbar .spacer { flex: 1; }
|
||
|
||
.toolbar .resolution select {
|
||
height: 28px; background: var(--bg-tertiary);
|
||
border: 1px solid var(--border); border-radius: 5px;
|
||
color: var(--text-primary); font-size: 11px;
|
||
font-family: 'Inter', sans-serif;
|
||
padding: 0 6px; outline: none; cursor: pointer;
|
||
}
|
||
|
||
.toolbar .resolution label {
|
||
font-size: 10px; color: var(--text-muted); margin-right: 4px;
|
||
}
|
||
|
||
/* ====== PREVIEW ====== */
|
||
.preview-container {
|
||
flex: 1; display: flex;
|
||
align-items: center; justify-content: center;
|
||
background:
|
||
radial-gradient(circle at 20% 50%, rgba(255, 26, 53, 0.03) 0%, transparent 50%),
|
||
repeating-linear-gradient(0deg, transparent, transparent 19px, rgba(255,255,255,0.015) 19px, rgba(255,255,255,0.015) 20px),
|
||
repeating-linear-gradient(90deg, transparent, transparent 19px, rgba(255,255,255,0.015) 19px, rgba(255,255,255,0.015) 20px),
|
||
var(--bg-primary);
|
||
padding: 16px; position: relative;
|
||
}
|
||
|
||
.preview-frame {
|
||
width: 1920px; height: 1080px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px; overflow: hidden;
|
||
background: #000; position: relative;
|
||
box-shadow: 0 0 40px rgba(0, 0, 0, 0.5);
|
||
transform-origin: center center;
|
||
}
|
||
|
||
.preview-frame iframe {
|
||
width: 100%; height: 100%; border: none;
|
||
}
|
||
|
||
.empty-state {
|
||
display: flex; flex-direction: column;
|
||
align-items: center; justify-content: center;
|
||
height: 100%; gap: 10px;
|
||
}
|
||
|
||
.empty-state .icon-large {
|
||
width: 56px; height: 56px; border-radius: 14px;
|
||
background: var(--bg-tertiary);
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 24px; border: 1px solid var(--border);
|
||
}
|
||
|
||
.empty-state h3 { font-size: 15px; font-weight: 600; }
|
||
.empty-state p { font-size: 12px; color: var(--text-muted); text-align: center; max-width: 280px; }
|
||
|
||
/* ====== STATUS BAR ====== */
|
||
.status-bar {
|
||
height: 26px; background: var(--bg-secondary);
|
||
border-top: 1px solid var(--border);
|
||
display: flex; align-items: center;
|
||
padding: 0 12px; font-size: 10px;
|
||
color: var(--text-muted); gap: 14px;
|
||
}
|
||
|
||
.status-bar .dot {
|
||
width: 6px; height: 6px; border-radius: 50%; background: var(--success);
|
||
}
|
||
|
||
.status-bar .item { display: flex; align-items: center; gap: 4px; }
|
||
|
||
/* ====== MESSAGE PANEL ====== */
|
||
.msg-panel {
|
||
width: 260px; background: var(--bg-secondary);
|
||
border-left: 1px solid var(--border);
|
||
display: flex; flex-direction: column; flex-shrink: 0;
|
||
}
|
||
|
||
.msg-panel-header {
|
||
padding: 12px 14px 10px;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
|
||
.msg-panel-header h3 {
|
||
font-size: 12px; font-weight: 700;
|
||
display: flex; align-items: center; gap: 5px;
|
||
}
|
||
|
||
.msg-panel-header p { font-size: 10px; color: var(--text-muted); margin-top: 2px; }
|
||
|
||
.msg-section {
|
||
padding: 10px 14px; border-bottom: 1px solid var(--border);
|
||
}
|
||
|
||
.msg-section h4 {
|
||
font-size: 10px; font-weight: 600; color: var(--text-muted);
|
||
text-transform: uppercase; letter-spacing: 0.4px; margin-bottom: 8px;
|
||
}
|
||
|
||
.msg-preset {
|
||
width: 100%; padding: 6px 10px; margin-bottom: 4px;
|
||
border: 1px solid var(--border); border-radius: 5px;
|
||
background: var(--bg-tertiary); color: var(--text-primary);
|
||
font-size: 11px; font-weight: 500;
|
||
font-family: 'Inter', sans-serif;
|
||
cursor: pointer; transition: all 0.12s;
|
||
text-align: left;
|
||
}
|
||
|
||
.msg-preset:hover {
|
||
background: var(--bg-card); border-color: var(--border-hover);
|
||
}
|
||
|
||
.msg-custom-area {
|
||
width: 100%; min-height: 60px; max-height: 100px;
|
||
background: var(--bg-tertiary); border: 1px solid var(--border);
|
||
border-radius: 5px; color: var(--text-primary);
|
||
font-size: 11px; font-family: 'JetBrains Mono', 'Consolas', monospace;
|
||
padding: 8px; outline: none; resize: vertical;
|
||
}
|
||
|
||
.msg-custom-area:focus { border-color: var(--accent); }
|
||
|
||
.msg-send-btn {
|
||
width: 100%; height: 28px; margin-top: 6px;
|
||
background: var(--accent); border: none;
|
||
border-radius: 5px; color: white;
|
||
font-size: 11px; font-weight: 600;
|
||
font-family: 'Inter', sans-serif;
|
||
cursor: pointer; transition: opacity 0.15s;
|
||
}
|
||
|
||
.msg-send-btn:hover { opacity: 0.85; }
|
||
|
||
.msg-log {
|
||
flex: 1; overflow-y: auto; padding: 10px 14px;
|
||
}
|
||
|
||
.msg-log::-webkit-scrollbar { width: 3px; }
|
||
.msg-log::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||
|
||
.log-entry {
|
||
font-size: 10px; padding: 3px 0;
|
||
border-bottom: 1px solid rgba(255,255,255,0.02);
|
||
line-height: 1.5; word-break: break-all;
|
||
}
|
||
|
||
.log-entry .time { color: var(--text-muted); }
|
||
.log-entry.log-out { color: #60a5fa; }
|
||
.log-entry.log-in { color: var(--success); }
|
||
.log-entry.log-err { color: var(--accent); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
<!-- Sidebar -->
|
||
<div class="sidebar">
|
||
<div class="sidebar-header">
|
||
<h1>🎮 NUI Simulator <span class="badge">RV</span></h1>
|
||
<div class="subtitle">
|
||
<span id="nuiCount" class="count">0</span> NUI resources discovered
|
||
</div>
|
||
</div>
|
||
<div class="search-box">
|
||
<input type="text" id="searchInput" placeholder="Search resources...">
|
||
</div>
|
||
<div class="filter-bar" id="filterBar"></div>
|
||
<div class="nui-list" id="nuiList"></div>
|
||
</div>
|
||
|
||
<!-- Main -->
|
||
<div class="main">
|
||
<div class="toolbar">
|
||
<button class="btn" id="btnReload" title="Reload NUI">↻ Reload</button>
|
||
<button class="btn" id="btnRescan" title="Rescan resources">🔄 Rescan</button>
|
||
<div class="sep"></div>
|
||
<button class="btn" id="btnShow">📤 Show</button>
|
||
<button class="btn" id="btnHide">📥 Hide</button>
|
||
<button class="btn" id="btnNewTab">🔗 New Tab</button>
|
||
<div class="spacer"></div>
|
||
<div class="resolution">
|
||
<label>Resolution:</label>
|
||
<select id="resolutionSelect">
|
||
<option value="1920x1080" selected>1920×1080</option>
|
||
<option value="2560x1440">2560×1440</option>
|
||
<option value="1280x720">1280×720</option>
|
||
<option value="1600x900">1600×900</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="preview-container">
|
||
<div class="preview-frame" id="previewFrame">
|
||
<div class="empty-state" id="emptyState">
|
||
<div class="icon-large">🖥️</div>
|
||
<h3>No NUI Selected</h3>
|
||
<p>Select a resource from the sidebar to preview its NUI. The server auto-discovers all ui_page resources.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="status-bar">
|
||
<div class="item"><span class="dot"></span> Connected</div>
|
||
<div class="item" id="statusResource">No resource loaded</div>
|
||
<div class="item" id="statusResolution">1920×1080</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Message Panel -->
|
||
<div class="msg-panel">
|
||
<div class="msg-panel-header">
|
||
<h3>📨 NUI Messages</h3>
|
||
<p>Send postMessage to the iframe</p>
|
||
</div>
|
||
|
||
<div class="msg-section">
|
||
<h4>Quick Actions</h4>
|
||
<button class="msg-preset" onclick="sendPreset({action:'show'})">{ action: "show" }</button>
|
||
<button class="msg-preset" onclick="sendPreset({action:'hide'})">{ action: "hide" }</button>
|
||
<button class="msg-preset" onclick="sendPreset({type:'open'})">{ type: "open" }</button>
|
||
<button class="msg-preset" onclick="sendPreset({type:'close'})">{ type: "close" }</button>
|
||
<button class="msg-preset" onclick="sendPreset({show:true})">{ show: true }</button>
|
||
<button class="msg-preset" onclick="sendPreset({show:false})">{ show: false }</button>
|
||
</div>
|
||
|
||
<div class="msg-section">
|
||
<h4>Custom Message (JSON)</h4>
|
||
<textarea class="msg-custom-area" id="customMsg" placeholder='{"action": "show", "data": {...}}'></textarea>
|
||
<button class="msg-send-btn" onclick="sendCustom()">Send Message</button>
|
||
</div>
|
||
|
||
<div class="msg-log" id="msgLog">
|
||
<h4 style="font-size:10px;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:6px;">Message Log</h4>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
let allNUIs = [];
|
||
let activeResource = null;
|
||
let activeFilter = 'all';
|
||
|
||
// ====== FETCH NUIs FROM SERVER ======
|
||
async function fetchNUIs() {
|
||
try {
|
||
const res = await fetch('/api/nuis');
|
||
allNUIs = await res.json();
|
||
document.getElementById('nuiCount').textContent = allNUIs.length;
|
||
buildFilters();
|
||
renderList();
|
||
} catch (e) {
|
||
log('err', 'Failed to fetch NUI list');
|
||
}
|
||
}
|
||
|
||
// ====== FILTERS ======
|
||
function buildFilters() {
|
||
const tags = [...new Set(allNUIs.map(n => n.tag))];
|
||
const bar = document.getElementById('filterBar');
|
||
bar.innerHTML = `<span class="filter-pill active" data-f="all" onclick="setFilter('all')">All</span>` +
|
||
tags.map(t => `<span class="filter-pill" data-f="${t}" onclick="setFilter('${t}')">${t}</span>`).join('');
|
||
}
|
||
|
||
function setFilter(f) {
|
||
activeFilter = f;
|
||
document.querySelectorAll('.filter-pill').forEach(p =>
|
||
p.classList.toggle('active', p.dataset.f === f)
|
||
);
|
||
renderList();
|
||
}
|
||
|
||
// ====== RENDER LIST ======
|
||
function renderList() {
|
||
const search = document.getElementById('searchInput').value.toLowerCase();
|
||
const list = document.getElementById('nuiList');
|
||
|
||
const filtered = allNUIs.filter(n => {
|
||
const matchSearch = n.name.toLowerCase().includes(search) || n.path.toLowerCase().includes(search);
|
||
const matchFilter = activeFilter === 'all' || n.tag === activeFilter;
|
||
return matchSearch && matchFilter;
|
||
});
|
||
|
||
list.innerHTML = filtered.map(n => `
|
||
<div class="nui-item ${activeResource === n.name ? 'active' : ''} ${!n.exists ? 'missing' : ''}"
|
||
data-name="${n.name}" data-path="${n.uiFullPath}" onclick="loadNUI('${n.name}', '${n.uiFullPath}')">
|
||
<div class="icon">${n.icon}</div>
|
||
<div class="info">
|
||
<div class="name">${n.name}</div>
|
||
<div class="path">${n.path}</div>
|
||
</div>
|
||
<span class="tag" style="background:${n.color}20;color:${n.color}">${n.tag}</span>
|
||
</div>
|
||
`).join('');
|
||
}
|
||
|
||
// ====== LOAD NUI ======
|
||
function loadNUI(name, uiPath) {
|
||
activeResource = name;
|
||
const frame = document.getElementById('previewFrame');
|
||
const empty = document.getElementById('emptyState');
|
||
if (empty) empty.remove();
|
||
|
||
const old = frame.querySelector('iframe');
|
||
if (old) old.remove();
|
||
|
||
const iframe = document.createElement('iframe');
|
||
iframe.src = '/resources/' + uiPath;
|
||
iframe.id = 'nuiIframe';
|
||
frame.appendChild(iframe);
|
||
|
||
document.getElementById('statusResource').textContent = `📦 ${name}`;
|
||
renderList();
|
||
log('out', `Loaded: ${name}`);
|
||
}
|
||
|
||
// ====== MESSAGING ======
|
||
function sendMsg(data) {
|
||
const iframe = document.getElementById('nuiIframe');
|
||
if (iframe && iframe.contentWindow) {
|
||
iframe.contentWindow.postMessage(data, '*');
|
||
log('out', `→ ${JSON.stringify(data)}`);
|
||
} else {
|
||
log('err', 'No iframe loaded');
|
||
}
|
||
}
|
||
|
||
function sendPreset(data) { sendMsg(data); }
|
||
|
||
function sendCustom() {
|
||
const raw = document.getElementById('customMsg').value.trim();
|
||
try {
|
||
const data = JSON.parse(raw);
|
||
sendMsg(data);
|
||
} catch (e) {
|
||
log('err', `Invalid JSON: ${e.message}`);
|
||
}
|
||
}
|
||
|
||
// ====== LOG ======
|
||
function log(type, msg) {
|
||
const el = document.getElementById('msgLog');
|
||
const t = new Date().toLocaleTimeString('ro-RO', { hour:'2-digit', minute:'2-digit', second:'2-digit' });
|
||
const entry = document.createElement('div');
|
||
entry.className = `log-entry log-${type}`;
|
||
entry.innerHTML = `<span class="time">[${t}]</span> ${msg}`;
|
||
el.appendChild(entry);
|
||
el.scrollTop = el.scrollHeight;
|
||
}
|
||
|
||
// Listen for messages FROM iframe
|
||
window.addEventListener('message', e => {
|
||
if (e.data && typeof e.data === 'object') {
|
||
log('in', `← ${JSON.stringify(e.data)}`);
|
||
}
|
||
});
|
||
|
||
// ====== RESOLUTION ======
|
||
function updateResolution() {
|
||
const [w, h] = document.getElementById('resolutionSelect').value.split('x').map(Number);
|
||
const frame = document.getElementById('previewFrame');
|
||
const container = document.querySelector('.preview-container');
|
||
frame.style.width = w + 'px';
|
||
frame.style.height = h + 'px';
|
||
const maxW = container.clientWidth - 32;
|
||
const maxH = container.clientHeight - 32;
|
||
const scale = Math.min(maxW / w, maxH / h, 1);
|
||
frame.style.transform = `scale(${scale})`;
|
||
document.getElementById('statusResolution').textContent = `${w}×${h} (${Math.round(scale * 100)}%)`;
|
||
}
|
||
|
||
// ====== TOOLBAR EVENTS ======
|
||
document.getElementById('btnReload').onclick = () => {
|
||
const iframe = document.getElementById('nuiIframe');
|
||
if (iframe) { iframe.src = iframe.src; log('out', 'Reloaded'); }
|
||
};
|
||
|
||
document.getElementById('btnRescan').onclick = async () => {
|
||
log('out', 'Rescanning resources...');
|
||
const res = await fetch('/api/rescan');
|
||
allNUIs = await res.json();
|
||
document.getElementById('nuiCount').textContent = allNUIs.length;
|
||
buildFilters();
|
||
renderList();
|
||
log('out', `Found ${allNUIs.length} NUIs`);
|
||
};
|
||
|
||
document.getElementById('btnShow').onclick = () => {
|
||
sendMsg({ action: 'show' });
|
||
sendMsg({ type: 'open' });
|
||
sendMsg({ show: true });
|
||
};
|
||
|
||
document.getElementById('btnHide').onclick = () => {
|
||
sendMsg({ action: 'hide' });
|
||
sendMsg({ type: 'close' });
|
||
sendMsg({ show: false });
|
||
};
|
||
|
||
document.getElementById('btnNewTab').onclick = () => {
|
||
if (activeResource) {
|
||
const n = allNUIs.find(x => x.name === activeResource);
|
||
if (n) window.open('/resources/' + n.uiFullPath, '_blank');
|
||
}
|
||
};
|
||
|
||
document.getElementById('searchInput').addEventListener('input', () => renderList());
|
||
document.getElementById('resolutionSelect').addEventListener('change', updateResolution);
|
||
window.addEventListener('resize', updateResolution);
|
||
|
||
// ====== INIT ======
|
||
fetchNUIs();
|
||
setTimeout(updateResolution, 100);
|
||
</script>
|
||
</body>
|
||
</html>
|