2026-03-29 21:41:17 +03:00
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
|
<html lang="ro">
|
2026-03-30 02:20:03 +03:00
|
|
|
|
|
2026-03-29 21:41:17 +03:00
|
|
|
|
<head>
|
|
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
|
<title>Red Valley — NUI Simulator</title>
|
2026-03-30 02:20:03 +03:00
|
|
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap"
|
|
|
|
|
|
rel="stylesheet">
|
2026-03-29 21:41:17 +03:00
|
|
|
|
<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);
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Layout */
|
|
|
|
|
|
.app {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
height: 100vh;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Sidebar */
|
|
|
|
|
|
.sidebar {
|
|
|
|
|
|
width: 320px;
|
|
|
|
|
|
background: var(--bg-secondary);
|
|
|
|
|
|
border-right: 1px solid var(--border);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sidebar-header {
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sidebar-header h1 {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sidebar-header h1 .badge {
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
|
padding: 2px 8px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sidebar-header p {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.search-box {
|
|
|
|
|
|
padding: 12px 20px;
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.search-box input {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 36px;
|
|
|
|
|
|
background: var(--bg-tertiary);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
color: var(--text-primary);
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-family: 'Inter', sans-serif;
|
|
|
|
|
|
padding: 0 12px 0 36px;
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
transition: border-color 0.2s;
|
|
|
|
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' 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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nui-list {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
padding: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nui-list::-webkit-scrollbar {
|
|
|
|
|
|
width: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nui-list::-webkit-scrollbar-thumb {
|
|
|
|
|
|
background: var(--border);
|
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nui-list::-webkit-scrollbar-track {
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nui-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.15s;
|
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nui-item:hover {
|
|
|
|
|
|
background: var(--bg-tertiary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nui-item.active {
|
|
|
|
|
|
background: var(--accent-glow);
|
|
|
|
|
|
border: 1px solid rgba(255, 26, 53, 0.3);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nui-item .icon {
|
|
|
|
|
|
width: 34px;
|
|
|
|
|
|
height: 34px;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
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: 13px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nui-item .info .path {
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.nui-item .tag {
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
padding: 2px 6px;
|
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-30 02:20:03 +03:00
|
|
|
|
.tag-custom {
|
|
|
|
|
|
background: rgba(34, 197, 94, 0.15);
|
|
|
|
|
|
color: #22c55e;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tag-vendor {
|
|
|
|
|
|
background: rgba(96, 165, 250, 0.15);
|
|
|
|
|
|
color: #60a5fa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tag-core {
|
|
|
|
|
|
background: rgba(192, 132, 252, 0.15);
|
|
|
|
|
|
color: #c084fc;
|
|
|
|
|
|
}
|
2026-03-29 21:41:17 +03:00
|
|
|
|
|
|
|
|
|
|
/* Main content */
|
|
|
|
|
|
.main {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar {
|
|
|
|
|
|
height: 50px;
|
|
|
|
|
|
background: var(--bg-secondary);
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 0 16px;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar .btn {
|
|
|
|
|
|
height: 32px;
|
|
|
|
|
|
padding: 0 14px;
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
background: var(--bg-tertiary);
|
|
|
|
|
|
color: var(--text-secondary);
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
font-family: 'Inter', sans-serif;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 5px;
|
|
|
|
|
|
transition: all 0.15s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar .btn:hover {
|
|
|
|
|
|
background: var(--bg-card);
|
|
|
|
|
|
color: var(--text-primary);
|
|
|
|
|
|
border-color: var(--border-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar .btn-accent {
|
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar .btn-accent:hover {
|
|
|
|
|
|
opacity: 0.85;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar .resolution {
|
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar .resolution select {
|
|
|
|
|
|
height: 32px;
|
|
|
|
|
|
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 8px;
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar .resolution label {
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Preview area */
|
|
|
|
|
|
.preview-container {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
2026-03-30 02:20:03 +03:00
|
|
|
|
background:
|
2026-03-29 21:41:17 +03:00
|
|
|
|
radial-gradient(circle at 20% 50%, rgba(255, 26, 53, 0.03) 0%, transparent 50%),
|
2026-03-30 02:20:03 +03:00
|
|
|
|
repeating-linear-gradient(0deg, transparent, transparent 19px, rgba(255, 255, 255, 0.02) 19px, rgba(255, 255, 255, 0.02) 20px),
|
|
|
|
|
|
repeating-linear-gradient(90deg, transparent, transparent 19px, rgba(255, 255, 255, 0.02) 19px, rgba(255, 255, 255, 0.02) 20px),
|
2026-03-29 21:41:17 +03:00
|
|
|
|
var(--bg-primary);
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.preview-frame {
|
|
|
|
|
|
width: 1280px;
|
|
|
|
|
|
height: 720px;
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
background: #000;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
box-shadow: 0 0 60px 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: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-state .icon-large {
|
|
|
|
|
|
width: 64px;
|
|
|
|
|
|
height: 64px;
|
|
|
|
|
|
border-radius: 16px;
|
|
|
|
|
|
background: var(--bg-tertiary);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
font-size: 28px;
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-state h3 {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.empty-state p {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
max-width: 300px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Status bar */
|
|
|
|
|
|
.status-bar {
|
|
|
|
|
|
height: 30px;
|
|
|
|
|
|
background: var(--bg-secondary);
|
|
|
|
|
|
border-top: 1px solid var(--border);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 0 16px;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-bar .dot {
|
|
|
|
|
|
width: 6px;
|
|
|
|
|
|
height: 6px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.status-bar .item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Simulation Panel */
|
|
|
|
|
|
.sim-panel {
|
|
|
|
|
|
width: 280px;
|
|
|
|
|
|
background: var(--bg-secondary);
|
|
|
|
|
|
border-left: 1px solid var(--border);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-panel-header {
|
|
|
|
|
|
padding: 16px 16px 12px;
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-panel-header h3 {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-panel-header p {
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
margin-top: 3px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-section {
|
|
|
|
|
|
padding: 14px 16px;
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-section h4 {
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-toggle {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
padding: 8px 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-toggle label {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toggle-switch {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 22px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toggle-switch input {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toggle-slider {
|
|
|
|
|
|
position: absolute;
|
2026-03-30 02:20:03 +03:00
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
2026-03-29 21:41:17 +03:00
|
|
|
|
background: #333;
|
|
|
|
|
|
border-radius: 11px;
|
|
|
|
|
|
transition: 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toggle-slider::before {
|
|
|
|
|
|
content: '';
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
width: 16px;
|
|
|
|
|
|
height: 16px;
|
|
|
|
|
|
left: 3px;
|
|
|
|
|
|
top: 3px;
|
|
|
|
|
|
background: white;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
transition: 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-30 02:20:03 +03:00
|
|
|
|
.toggle-switch input:checked+.toggle-slider {
|
2026-03-29 21:41:17 +03:00
|
|
|
|
background: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-30 02:20:03 +03:00
|
|
|
|
.toggle-switch input:checked+.toggle-slider::before {
|
2026-03-29 21:41:17 +03:00
|
|
|
|
transform: translateX(18px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-job-btn {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
background: var(--bg-tertiary);
|
|
|
|
|
|
color: var(--text-primary);
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
font-family: 'Inter', sans-serif;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.15s;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
text-align: left;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-job-btn:hover {
|
|
|
|
|
|
background: var(--bg-card);
|
|
|
|
|
|
border-color: var(--border-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-job-btn .req {
|
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-log {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-log h4 {
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-log::-webkit-scrollbar {
|
|
|
|
|
|
width: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sim-log::-webkit-scrollbar-thumb {
|
|
|
|
|
|
background: var(--border);
|
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.log-entry {
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
padding: 4px 0;
|
2026-03-30 02:20:03 +03:00
|
|
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
|
2026-03-29 21:41:17 +03:00
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.log-entry .time {
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-30 02:20:03 +03:00
|
|
|
|
.log-success {
|
|
|
|
|
|
color: var(--success);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.log-error {
|
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.log-info {
|
|
|
|
|
|
color: #60a5fa;
|
|
|
|
|
|
}
|
2026-03-29 21:41:17 +03:00
|
|
|
|
</style>
|
|
|
|
|
|
</head>
|
2026-03-30 02:20:03 +03:00
|
|
|
|
|
2026-03-29 21:41:17 +03:00
|
|
|
|
<body>
|
|
|
|
|
|
<div class="app">
|
|
|
|
|
|
<!-- Sidebar -->
|
|
|
|
|
|
<div class="sidebar">
|
|
|
|
|
|
<div class="sidebar-header">
|
|
|
|
|
|
<h1>🎮 NUI Simulator <span class="badge">RV</span></h1>
|
|
|
|
|
|
<p>Test NUI resources without launching FiveM</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="search-box">
|
|
|
|
|
|
<input type="text" id="searchInput" placeholder="Search resources...">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="nui-list" id="nuiList"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Main -->
|
|
|
|
|
|
<div class="main">
|
|
|
|
|
|
<div class="toolbar">
|
|
|
|
|
|
<button class="btn" id="btnRefresh" title="Reload NUI">↻ Reload</button>
|
|
|
|
|
|
<button class="btn" id="btnSendShow" title="Send show message">📤 Send {show}</button>
|
|
|
|
|
|
<button class="btn" id="btnSendHide" title="Send hide message">📥 Send {hide}</button>
|
|
|
|
|
|
<button class="btn" id="btnDevTools" title="Open in new tab">🔗 Open Tab</button>
|
|
|
|
|
|
<div class="resolution">
|
|
|
|
|
|
<label>Resolution:</label>
|
|
|
|
|
|
<select id="resolutionSelect">
|
|
|
|
|
|
<option value="1920x1080">1920×1080</option>
|
|
|
|
|
|
<option value="1280x720" selected>1280×720</option>
|
|
|
|
|
|
<option value="1600x900">1600×900</option>
|
|
|
|
|
|
<option value="2560x1440">2560×1440</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 interface</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="status-bar">
|
|
|
|
|
|
<div class="item">
|
|
|
|
|
|
<span class="dot"></span>
|
|
|
|
|
|
Ready
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="item" id="statusResource">No resource loaded</div>
|
|
|
|
|
|
<div class="item" id="statusResolution">1280×720</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Simulation Panel -->
|
|
|
|
|
|
<div class="sim-panel">
|
|
|
|
|
|
<div class="sim-panel-header">
|
|
|
|
|
|
<h3>🧪 Flow Simulator</h3>
|
|
|
|
|
|
<p>Simulează logica server-side</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="sim-section">
|
|
|
|
|
|
<h4>👤 Player State</h4>
|
|
|
|
|
|
<div class="sim-toggle">
|
|
|
|
|
|
<label>🪪 Permis de conducere</label>
|
|
|
|
|
|
<div class="toggle-switch">
|
|
|
|
|
|
<input type="checkbox" id="toggleLicense">
|
|
|
|
|
|
<span class="toggle-slider"></span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sim-toggle">
|
|
|
|
|
|
<label>🆔 Buletin (ID Card)</label>
|
|
|
|
|
|
<div class="toggle-switch">
|
|
|
|
|
|
<input type="checkbox" id="toggleIdCard" checked>
|
|
|
|
|
|
<span class="toggle-slider"></span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="sim-section">
|
|
|
|
|
|
<h4>💼 Job Center — Apply</h4>
|
2026-03-30 02:20:03 +03:00
|
|
|
|
<button class="sim-job-btn" data-job="garbage" onclick="simApply('garbage')">🗑️ Garbage Collector <span
|
|
|
|
|
|
class="req">🚗 permis</span></button>
|
|
|
|
|
|
<button class="sim-job-btn" data-job="deliverer" onclick="simApply('deliverer')">📦 Deliverer <span
|
|
|
|
|
|
class="req">🚗 permis</span></button>
|
|
|
|
|
|
<button class="sim-job-btn" data-job="postman" onclick="simApply('postman')">✉️ Postman <span
|
|
|
|
|
|
class="req">🚗 permis</span></button>
|
|
|
|
|
|
<button class="sim-job-btn" data-job="lumberjack" onclick="simApply('lumberjack')">🪓 Lumberjack <span
|
|
|
|
|
|
class="req">🚗 permis</span></button>
|
|
|
|
|
|
<button class="sim-job-btn" data-job="bus" onclick="simApply('bus')">🚌 Bus Driver <span class="req">🚗
|
|
|
|
|
|
permis</span></button>
|
2026-03-29 21:41:17 +03:00
|
|
|
|
<button class="sim-job-btn" data-job="miner" onclick="simApply('miner')">⛏️ Miner</button>
|
2026-03-30 02:20:03 +03:00
|
|
|
|
<button class="sim-job-btn" data-job="electrician" onclick="simApply('electrician')">⚡
|
|
|
|
|
|
Electrician</button>
|
2026-03-29 21:41:17 +03:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="sim-log" id="simLog">
|
|
|
|
|
|
<h4>📋 Server Log</h4>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
// NUI Registry - all NUI pages on the server
|
|
|
|
|
|
const nuiResources = [
|
|
|
|
|
|
{ name: "⭐ Job Center (Simulator)", path: "__mock__/jobcenter", tag: "custom", icon: "💼", mock: "../docs/mock_jobcenter.html" },
|
|
|
|
|
|
{ name: "rv-license-dialog", path: "[framework]/[addons]/rv-license-dialog/html/index.html", tag: "custom", icon: "🔒" },
|
|
|
|
|
|
{ name: "17mov_JobCenter", path: "[framework]/[base]/[jobs]/17mov_JobCenter/web/index.html", tag: "vendor", icon: "💼" },
|
|
|
|
|
|
{ name: "17mov_CharacterSystem", path: "[framework]/[base]/[auth]/17mov_CharacterSystem/web/index.html", tag: "vendor", icon: "👤" },
|
|
|
|
|
|
{ name: "17mov_Hud", path: "[framework]/[base]/[ui]/17mov_Hud/web/index.html", tag: "vendor", icon: "📊" },
|
|
|
|
|
|
{ name: "17mov_Lumberjack", path: "[framework]/[base]/[jobs]/[citizen]/17mov_Lumberjack/web/index.html", tag: "vendor", icon: "🪓" },
|
|
|
|
|
|
{ name: "17mov_Miner", path: "[framework]/[base]/[jobs]/[citizen]/17mov_Miner/web/index.html", tag: "vendor", icon: "⛏️" },
|
|
|
|
|
|
{ name: "17mov_OilRig", path: "[framework]/[base]/[jobs]/[citizen]/17mov_OilRig/web/index.html", tag: "vendor", icon: "🛢️" },
|
|
|
|
|
|
{ name: "luxu_admin", path: "luxu_admin/web/index.html", tag: "vendor", icon: "⚙️" },
|
|
|
|
|
|
{ name: "ac-carcontrol", path: "[framework]/[addons]/ac-carcontrol/html/index.html", tag: "vendor", icon: "🚗" },
|
|
|
|
|
|
{ name: "ak4y-dice", path: "[framework]/[addons]/ak4y-dice/html/index.html", tag: "vendor", icon: "🎲" },
|
|
|
|
|
|
{ name: "bit-driverschool", path: "[framework]/[addons]/bit-driverschool/html/index.html", tag: "vendor", icon: "🏫" },
|
|
|
|
|
|
{ name: "kq_animsuggest", path: "[framework]/[addons]/kq_animsuggest/nui/index.html", tag: "vendor", icon: "💃" },
|
|
|
|
|
|
{ name: "kq_dyno", path: "[framework]/[addons]/kq_dyno/html/index.html", tag: "vendor", icon: "📈" },
|
|
|
|
|
|
{ name: "qb-input", path: "[framework]/[addons]/qb-input/html/index.html", tag: "core", icon: "📝" },
|
|
|
|
|
|
{ name: "qs-smartphone-pro", path: "[framework]/[addons]/qs-smartphone-pro/html/index.html", tag: "vendor", icon: "📱" },
|
|
|
|
|
|
{ name: "svdden_banking", path: "[framework]/[addons]/svdden_banking/html/index.html", tag: "vendor", icon: "🏦" },
|
|
|
|
|
|
{ name: "rcore_casino", path: "[framework]/[addons]/[casino]/rcore_casino/client/html/index.html", tag: "vendor", icon: "🎰" },
|
|
|
|
|
|
{ name: "qs-notify", path: "[framework]/[addons]/[notify]/qs-notify/html/index.html", tag: "vendor", icon: "🔔" },
|
|
|
|
|
|
{ name: "mBossmenu", path: "[framework]/[base]/[jobs]/mBossmenu/html/index.html", tag: "vendor", icon: "👔" },
|
|
|
|
|
|
{ name: "wasabi_ambulance", path: "[framework]/[base]/[jobs]/[legal]/[ambulance]/wasabi_ambulance/nui/index.html", tag: "vendor", icon: "🚑" },
|
|
|
|
|
|
{ name: "t1ger_mechanic", path: "[framework]/[base]/[jobs]/[legal]/[mechanic]/t1ger_mechanic/web/index.html", tag: "vendor", icon: "🔧" },
|
|
|
|
|
|
{ name: "codem-dispatch", path: "[framework]/[base]/[jobs]/[legal]/[police]/codem-dispatch/nui/index.html", tag: "vendor", icon: "📻" },
|
|
|
|
|
|
{ name: "t1ger_tuningsystem", path: "[framework]/[base]/[jobs]/[legal]/[tuner]/t1ger_tuningsystem/web/index.html", tag: "vendor", icon: "🏎️" },
|
|
|
|
|
|
{ name: "qb-core", path: "[framework]/[core]/qb-core/html/index.html", tag: "core", icon: "🧩" },
|
|
|
|
|
|
{ name: "qb-menu", path: "[framework]/[core]/qb-menu/html/index.html", tag: "core", icon: "📋" },
|
|
|
|
|
|
{ name: "qb-target", path: "[framework]/[core]/qb-target/html/index.html", tag: "core", icon: "🎯" },
|
|
|
|
|
|
{ name: "interact-sound", path: "[framework]/[depends]/interact-sound/client/html/index.html", tag: "core", icon: "🔊" },
|
|
|
|
|
|
{ name: "phone-radio", path: "[framework]/[depends]/phone-radio/html/index.html", tag: "core", icon: "📻" },
|
|
|
|
|
|
{ name: "progressbar", path: "[framework]/[depends]/progressbar/html/index.html", tag: "core", icon: "⏳" },
|
|
|
|
|
|
{ name: "xsound", path: "[framework]/[depends]/xsound/html/index.html", tag: "core", icon: "🎵" },
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const BASE_PATH = "../resources/";
|
|
|
|
|
|
let activeResource = null;
|
|
|
|
|
|
|
|
|
|
|
|
// Render sidebar
|
|
|
|
|
|
function renderList(filter = "") {
|
|
|
|
|
|
const list = document.getElementById("nuiList");
|
2026-03-30 02:20:03 +03:00
|
|
|
|
const filtered = nuiResources.filter(r =>
|
2026-03-29 21:41:17 +03:00
|
|
|
|
r.name.toLowerCase().includes(filter.toLowerCase()) ||
|
|
|
|
|
|
r.path.toLowerCase().includes(filter.toLowerCase())
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
list.innerHTML = filtered.map(r => `
|
|
|
|
|
|
<div class="nui-item ${activeResource === r.name ? 'active' : ''}" data-name="${r.name}" data-path="${r.path}">
|
|
|
|
|
|
<div class="icon">${r.icon}</div>
|
|
|
|
|
|
<div class="info">
|
|
|
|
|
|
<div class="name">${r.name}</div>
|
|
|
|
|
|
<div class="path">${r.path}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="tag tag-${r.tag}">${r.tag}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
`).join("");
|
|
|
|
|
|
|
|
|
|
|
|
// Click handlers
|
|
|
|
|
|
list.querySelectorAll(".nui-item").forEach(item => {
|
|
|
|
|
|
item.addEventListener("click", () => loadNUI(item.dataset.name, item.dataset.path));
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Load NUI
|
|
|
|
|
|
function loadNUI(name, path) {
|
|
|
|
|
|
activeResource = name;
|
|
|
|
|
|
const frame = document.getElementById("previewFrame");
|
|
|
|
|
|
const empty = document.getElementById("emptyState");
|
2026-03-30 02:20:03 +03:00
|
|
|
|
|
2026-03-29 21:41:17 +03:00
|
|
|
|
if (empty) empty.remove();
|
|
|
|
|
|
|
|
|
|
|
|
// Remove existing iframe
|
|
|
|
|
|
const existingIframe = frame.querySelector("iframe");
|
|
|
|
|
|
if (existingIframe) existingIframe.remove();
|
|
|
|
|
|
|
|
|
|
|
|
// Check if this is a mock resource
|
|
|
|
|
|
const resource = nuiResources.find(r => r.name === name);
|
|
|
|
|
|
const src = resource && resource.mock ? resource.mock : BASE_PATH + path;
|
|
|
|
|
|
|
|
|
|
|
|
// Create new iframe
|
|
|
|
|
|
const iframe = document.createElement("iframe");
|
|
|
|
|
|
iframe.src = src;
|
|
|
|
|
|
iframe.id = "nuiIframe";
|
|
|
|
|
|
frame.appendChild(iframe);
|
|
|
|
|
|
|
|
|
|
|
|
// Update status
|
|
|
|
|
|
document.getElementById("statusResource").textContent = `📦 ${name}`;
|
|
|
|
|
|
renderList(document.getElementById("searchInput").value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Send NUI message to iframe
|
|
|
|
|
|
function sendMessage(data) {
|
|
|
|
|
|
const iframe = document.getElementById("nuiIframe");
|
|
|
|
|
|
if (iframe && iframe.contentWindow) {
|
|
|
|
|
|
iframe.contentWindow.postMessage(data, "*");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Resize preview
|
|
|
|
|
|
function updateResolution() {
|
|
|
|
|
|
const [w, h] = document.getElementById("resolutionSelect").value.split("x").map(Number);
|
|
|
|
|
|
const frame = document.getElementById("previewFrame");
|
|
|
|
|
|
const container = document.querySelector(".preview-container");
|
2026-03-30 02:20:03 +03:00
|
|
|
|
|
2026-03-29 21:41:17 +03:00
|
|
|
|
frame.style.width = w + "px";
|
|
|
|
|
|
frame.style.height = h + "px";
|
|
|
|
|
|
|
|
|
|
|
|
// Scale to fit
|
|
|
|
|
|
const maxW = container.clientWidth - 40;
|
|
|
|
|
|
const maxH = container.clientHeight - 40;
|
|
|
|
|
|
const scale = Math.min(maxW / w, maxH / h, 1);
|
|
|
|
|
|
frame.style.transform = `scale(${scale})`;
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById("statusResolution").textContent = `${w}×${h} (${Math.round(scale * 100)}%)`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Event listeners
|
|
|
|
|
|
document.getElementById("searchInput").addEventListener("input", e => renderList(e.target.value));
|
2026-03-30 02:20:03 +03:00
|
|
|
|
|
2026-03-29 21:41:17 +03:00
|
|
|
|
document.getElementById("btnRefresh").addEventListener("click", () => {
|
|
|
|
|
|
const iframe = document.getElementById("nuiIframe");
|
|
|
|
|
|
if (iframe) iframe.src = iframe.src;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById("btnSendShow").addEventListener("click", () => {
|
|
|
|
|
|
sendMessage({ action: "show" });
|
|
|
|
|
|
sendMessage({ type: "open" });
|
|
|
|
|
|
sendMessage({ show: true });
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById("btnSendHide").addEventListener("click", () => {
|
|
|
|
|
|
sendMessage({ action: "hide" });
|
|
|
|
|
|
sendMessage({ type: "close" });
|
|
|
|
|
|
sendMessage({ show: false });
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById("btnDevTools").addEventListener("click", () => {
|
|
|
|
|
|
if (activeResource) {
|
|
|
|
|
|
const r = nuiResources.find(n => n.name === activeResource);
|
|
|
|
|
|
if (r) window.open(BASE_PATH + r.path, "_blank");
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
document.getElementById("resolutionSelect").addEventListener("change", updateResolution);
|
|
|
|
|
|
window.addEventListener("resize", updateResolution);
|
|
|
|
|
|
|
|
|
|
|
|
// ========== FLOW SIMULATOR ==========
|
|
|
|
|
|
const licenseJobs = { garbage: true, deliverer: true, postman: true, lumberjack: true, bus: true };
|
|
|
|
|
|
|
|
|
|
|
|
function simApply(job) {
|
|
|
|
|
|
const hasLicense = document.getElementById('toggleLicense').checked;
|
|
|
|
|
|
const hasIdCard = document.getElementById('toggleIdCard').checked;
|
|
|
|
|
|
const needsLicense = licenseJobs[job] || false;
|
|
|
|
|
|
|
|
|
|
|
|
logSim('info', `Player apasă APPLY pe [${job}]`);
|
|
|
|
|
|
logSim('info', `→ Server: SetPlayerJob(src, "${job}", 0)`);
|
|
|
|
|
|
|
|
|
|
|
|
if (needsLicense) {
|
|
|
|
|
|
logSim('info', `→ Check: licenseJobs["${job}"] = true → verificare permis...`);
|
|
|
|
|
|
logSim('info', `→ metadata.licences.driver = ${hasLicense}`);
|
|
|
|
|
|
|
|
|
|
|
|
if (!hasLicense) {
|
|
|
|
|
|
logSim('error', `✖ BLOCAT: Nu are permis de conducere!`);
|
|
|
|
|
|
logSim('error', `→ TriggerClientEvent("QBCore:Notify", "error")`);
|
|
|
|
|
|
logSim('error', `→ TriggerClientEvent("rv:showLicenseDialog")`);
|
|
|
|
|
|
logSim('info', `→ return false (job NU a fost setat)`);
|
|
|
|
|
|
|
|
|
|
|
|
// Show the license dialog in preview
|
|
|
|
|
|
showLicenseDialog();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logSim('success', `✔ metadata.licences.driver = true`);
|
|
|
|
|
|
logSim('success', `✔ player.Functions.SetJob("${job}", 0) → SUCCESS`);
|
|
|
|
|
|
logSim('success', `→ Player este acum: ${job}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function showLicenseDialog() {
|
|
|
|
|
|
// Load rv-license-dialog in preview if not already loaded
|
|
|
|
|
|
const iframe = document.getElementById('nuiIframe');
|
|
|
|
|
|
const licensePath = '[framework]/[addons]/rv-license-dialog/html/index.html';
|
|
|
|
|
|
|
|
|
|
|
|
if (!iframe || activeResource !== 'rv-license-dialog') {
|
|
|
|
|
|
// Load license dialog
|
|
|
|
|
|
loadNUI('rv-license-dialog', licensePath);
|
|
|
|
|
|
// Wait for iframe to load then send show
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
sendMessage({ action: 'show' });
|
|
|
|
|
|
}, 500);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
sendMessage({ action: 'show' });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function logSim(type, msg) {
|
|
|
|
|
|
const log = document.getElementById('simLog');
|
|
|
|
|
|
const time = 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">[${time}]</span> ${msg}`;
|
|
|
|
|
|
log.appendChild(entry);
|
|
|
|
|
|
log.scrollTop = log.scrollHeight;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Init
|
|
|
|
|
|
renderList();
|
|
|
|
|
|
setTimeout(updateResolution, 100);
|
|
|
|
|
|
</script>
|
|
|
|
|
|
</body>
|
2026-03-30 02:20:03 +03:00
|
|
|
|
|
|
|
|
|
|
</html>
|