|
|
<!DOCTYPE html> |
|
|
<html lang="no"> |
|
|
<head> |
|
|
<meta charset="utf-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|
|
<title>Split-flap Admin – Canvas</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<script src="https://unpkg.com/feather-icons"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
|
|
<style> |
|
|
:root { |
|
|
--primary: #3b82f6; |
|
|
--secondary: #1f2937; |
|
|
--accent: #10b981; |
|
|
--danger: #ef4444; |
|
|
--warning: #f59e0b; |
|
|
--dark: #0f1115; |
|
|
--darker: #0b0d12; |
|
|
--light: #e5e7eb; |
|
|
--border: #1f2330; |
|
|
} |
|
|
|
|
|
body { |
|
|
scrollbar-width: thin; |
|
|
scrollbar-color: var(--secondary) var(--darker); |
|
|
} |
|
|
|
|
|
body::-webkit-scrollbar { |
|
|
width: 8px; |
|
|
} |
|
|
|
|
|
body::-webkit-scrollbar-track { |
|
|
background: var(--darker); |
|
|
} |
|
|
|
|
|
body::-webkit-scrollbar-thumb { |
|
|
background: var(--secondary); |
|
|
border-radius: 4px; |
|
|
} |
|
|
|
|
|
.tile-handle { |
|
|
position: absolute; |
|
|
right: -4px; |
|
|
bottom: -4px; |
|
|
width: 12px; |
|
|
height: 12px; |
|
|
background: var(--primary); |
|
|
border-radius: 2px; |
|
|
cursor: nwse-resize; |
|
|
opacity: 0; |
|
|
transition: opacity 0.2s; |
|
|
} |
|
|
|
|
|
.tile:hover .tile-handle { |
|
|
opacity: 1; |
|
|
} |
|
|
|
|
|
.grid-bg { |
|
|
background-size: calc(100%/var(--cols)) calc(100%/var(--rows)); |
|
|
background-image: |
|
|
linear-gradient(#1a2333 1px, transparent 1px), |
|
|
linear-gradient(90deg, #1a2333 1px, transparent 1px); |
|
|
} |
|
|
|
|
|
.property-section { |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
.log-entry { |
|
|
animation: fadeIn 0.3s ease; |
|
|
} |
|
|
|
|
|
@keyframes fadeIn { |
|
|
from { opacity: 0; transform: translateY(-5px); } |
|
|
to { opacity: 1; transform: translateY(0); } |
|
|
} |
|
|
|
|
|
.status-indicator { |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
input:focus, select:focus, textarea:focus { |
|
|
outline: 2px solid var(--primary); |
|
|
outline-offset: -2px; |
|
|
} |
|
|
|
|
|
.btn { |
|
|
transition: all 0.2s ease; |
|
|
} |
|
|
|
|
|
.btn:hover { |
|
|
transform: translateY(-1px); |
|
|
} |
|
|
|
|
|
.btn:active { |
|
|
transform: translateY(1px); |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-900 text-gray-100 font-sans min-h-screen"> |
|
|
<div class="flex flex-col md:flex-row h-screen overflow-hidden"> |
|
|
|
|
|
<div class="w-full md:w-80 lg:w-96 bg-gray-800 border-r border-gray-700 overflow-y-auto p-4 space-y-6"> |
|
|
|
|
|
<div class="flex items-center justify-between"> |
|
|
<h1 class="text-xl font-bold text-white flex items-center"> |
|
|
<i data-feather="layout" class="mr-2"></i> |
|
|
SplitFlap Commander |
|
|
</h1> |
|
|
<span id="status" class="px-2 py-1 bg-green-500/20 text-green-400 text-xs rounded-full border border-green-500/30">Ready</span> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700"> |
|
|
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center"> |
|
|
<i data-feather="settings" class="mr-2 w-4 h-4"></i> |
|
|
API Configuration |
|
|
</h2> |
|
|
<div class="space-y-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">API Endpoint</label> |
|
|
<input id="apiUrl" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm" value="/cb/api.php"> |
|
|
</div> |
|
|
<div class="grid grid-cols-2 gap-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Room</label> |
|
|
<input id="room" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm" value="hallA"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Token</label> |
|
|
<input id="token" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm" value="BYTT-DETTE"> |
|
|
</div> |
|
|
</div> |
|
|
<div class="grid grid-cols-3 gap-2"> |
|
|
<button id="ping" class="btn bg-blue-600 hover:bg-blue-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="wifi" class="w-4 h-4"></i> |
|
|
</button> |
|
|
<button id="clear" class="btn bg-gray-600 hover:bg-gray-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="trash-2" class="w-4 h-4"></i> |
|
|
</button> |
|
|
<button id="pushScene" class="btn bg-green-600 hover:bg-green-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="send" class="w-4 h-4"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700"> |
|
|
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center"> |
|
|
<i data-feather="grid" class="mr-2 w-4 h-4"></i> |
|
|
Grid Setup |
|
|
</h2> |
|
|
<div class="grid grid-cols-2 gap-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Columns</label> |
|
|
<input id="cols" type="number" min="4" max="80" value="20" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Rows</label> |
|
|
<input id="rows" type="number" min="4" max="40" value="10" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700"> |
|
|
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center"> |
|
|
<i data-feather="star" class="mr-2 w-4 h-4"></i> |
|
|
Icons |
|
|
</h2> |
|
|
<div class="grid grid-cols-3 gap-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Rail</label> |
|
|
<input id="icoRail" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="🚆"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Bus</label> |
|
|
<input id="icoBus" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="🚌"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Tram</label> |
|
|
<input id="icoTram" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="🚊"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Metro</label> |
|
|
<input id="icoMetro" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="🚇"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Water</label> |
|
|
<input id="icoWater" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="🛥️"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Air</label> |
|
|
<input id="icoAir" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="✈️"> |
|
|
</div> |
|
|
<div class="col-span-3"> |
|
|
<label class="block text-xs text-gray-400 mb-1">Other</label> |
|
|
<input id="icoOther" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm text-center" value="•"> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700"> |
|
|
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center"> |
|
|
<i data-feather="plus-square" class="mr-2 w-4 h-4"></i> |
|
|
Add Tiles |
|
|
</h2> |
|
|
<div class="grid grid-cols-2 gap-2"> |
|
|
<button id="addClock" class="btn bg-purple-600 hover:bg-purple-700 text-white py-2 px-3 rounded text-sm flex items-center justify-center"> |
|
|
<i data-feather="clock" class="w-4 h-4 mr-1"></i> Clock |
|
|
</button> |
|
|
<button id="addEntur" class="btn bg-indigo-600 hover:bg-indigo-700 text-white py-2 px-3 rounded text-sm flex items-center justify-center"> |
|
|
<i data-feather="list" class="w-4 h-4 mr-1"></i> Entur |
|
|
</button> |
|
|
<button id="addText" class="btn bg-blue-600 hover:bg-blue-700 text-white py-2 px-3 rounded text-sm flex items-center justify-center"> |
|
|
<i data-feather="type" class="w-4 h-4 mr-1"></i> Text |
|
|
</button> |
|
|
<div class="grid grid-cols-2 gap-2"> |
|
|
<button id="dupTile" class="btn bg-gray-600 hover:bg-gray-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="copy" class="w-4 h-4"></i> |
|
|
</button> |
|
|
<button id="delTile" class="btn bg-red-600 hover:bg-red-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="trash" class="w-4 h-4"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="propsPanel" class="bg-gray-750 rounded-lg p-4 border border-gray-700"> |
|
|
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center"> |
|
|
<i data-feather="sliders" class="mr-2 w-4 h-4"></i> |
|
|
Properties |
|
|
</h2> |
|
|
<div id="props" class="space-y-4"> |
|
|
<div class="grid grid-cols-2 gap-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">ID</label> |
|
|
<input id="p_id" readonly class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm font-mono"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Type</label> |
|
|
<input id="p_type" readonly class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm font-mono"> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="grid grid-cols-4 gap-2"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">X</label> |
|
|
<input id="p_x" type="number" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Y</label> |
|
|
<input id="p_y" type="number" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">W</label> |
|
|
<input id="p_w" type="number" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">H</label> |
|
|
<input id="p_h" type="number" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="typeClock" class="property-section hidden space-y-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Time Format</label> |
|
|
<select id="clk_fmt" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
<option value="HH:mm">HH:mm</option> |
|
|
<option value="HH:mm:ss">HH:mm:ss</option> |
|
|
<option value="dd.MM HH:mm">dd.MM HH:mm</option> |
|
|
<option value="dd.MM">dd.MM</option> |
|
|
</select> |
|
|
</div> |
|
|
<div class="grid grid-cols-2 gap-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Color</label> |
|
|
<input id="clk_color" type="color" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm h-9" value="#ffffff"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Update (ms)</label> |
|
|
<input id="clk_ms" type="number" min="200" value="1000" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="typeEntur" class="property-section hidden space-y-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Stop Place ID</label> |
|
|
<input id="en_stop" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm font-mono" value="NSR:StopPlace:58287"> |
|
|
</div> |
|
|
<div class="grid grid-cols-2 gap-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Mode</label> |
|
|
<select id="en_mode" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
<option value="departures">Departures</option> |
|
|
<option value="arrivals">Arrivals</option> |
|
|
</select> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Limit</label> |
|
|
<input id="en_limit" type="number" min="1" max="20" value="6" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
</div> |
|
|
<div class="grid grid-cols-2 gap-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Fetch (s)</label> |
|
|
<input id="en_fetch" type="number" min="5" value="30" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Lines</label> |
|
|
<input id="en_lines" type="number" min="1" max="20" value="6" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Template</label> |
|
|
<input id="en_tpl" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm font-mono" value="[{icon}] {time} {line|cut=6} {dest|cutEnd=12} Sp{quay|pad=2} {status} {delay}"> |
|
|
</div> |
|
|
|
|
|
<div class="grid grid-cols-3 gap-2"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Normal</label> |
|
|
<input id="en_c_norm" type="color" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm h-9" value="#ffffff"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Delayed</label> |
|
|
<input id="en_c_del" type="color" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm h-9" value="#ffe66d"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Cancelled</label> |
|
|
<input id="en_c_can" type="color" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm h-9" value="#ff5c5c"> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="grid grid-cols-5 gap-2 pt-2"> |
|
|
<label class="flex items-center space-x-1"> |
|
|
<input id="en_vy" type="checkbox" checked class="rounded border-gray-600 text-blue-600"> |
|
|
<span class="text-xs">Vy</span> |
|
|
</label> |
|
|
<label class="flex items-center space-x-1"> |
|
|
<input id="en_sj" type="checkbox" checked class="rounded border-gray-600 text-blue-600"> |
|
|
<span class="text-xs">SJ</span> |
|
|
</label> |
|
|
<label class="flex items-center space-x-1"> |
|
|
<input id="en_fly" type="checkbox" checked class="rounded border-gray-600 text-blue-600"> |
|
|
<span class="text-xs">Fly</span> |
|
|
</label> |
|
|
<label class="flex items-center space-x-1"> |
|
|
<input id="en_onlydel" type="checkbox" class="rounded border-gray-600 text-blue-600"> |
|
|
<span class="text-xs">Delayed</span> |
|
|
</label> |
|
|
<label class="flex items-center space-x-1"> |
|
|
<input id="en_onlycan" type="checkbox" class="rounded border-gray-600 text-blue-600"> |
|
|
<span class="text-xs">Canceled</span> |
|
|
</label> |
|
|
</div> |
|
|
|
|
|
<div class="grid grid-cols-2 gap-2 pt-2"> |
|
|
<button id="en_fetch_now" class="btn bg-blue-600 hover:bg-blue-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="refresh-cw" class="w-4 h-4 mr-1"></i> Fetch Now |
|
|
</button> |
|
|
<button id="en_preview" class="btn bg-gray-600 hover:bg-gray-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="eye" class="w-4 h-4 mr-1"></i> Preview |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="typeText" class="property-section hidden space-y-3"> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Text Content</label> |
|
|
<input id="tx_text" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm" value="Velkommen"> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-xs text-gray-400 mb-1">Text Color</label> |
|
|
<input id="tx_color" type="color" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm h-9" value="#6daaff"> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700"> |
|
|
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center"> |
|
|
<i data-feather="repeat" class="mr-2 w-4 h-4"></i> |
|
|
Auto Push |
|
|
</h2> |
|
|
<div class="grid grid-cols-3 gap-3 items-end"> |
|
|
<div class="col-span-2"> |
|
|
<label class="block text-xs text-gray-400 mb-1">Interval (seconds)</label> |
|
|
<input id="scene_push" type="number" value="10" min="3" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm"> |
|
|
</div> |
|
|
<div class="grid grid-cols-2 gap-2"> |
|
|
<button id="startAuto" class="btn bg-green-600 hover:bg-green-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="play" class="w-4 h-4"></i> |
|
|
</button> |
|
|
<button id="stopAuto" class="btn bg-red-600 hover:bg-red-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="square" class="w-4 h-4"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-750 rounded-lg p-4 border border-gray-700"> |
|
|
<h2 class="text-sm font-semibold text-gray-300 mb-3 flex items-center"> |
|
|
<i data-feather="save" class="mr-2 w-4 h-4"></i> |
|
|
Storage |
|
|
</h2> |
|
|
<div class="grid grid-cols-3 gap-2"> |
|
|
<button id="save" class="btn bg-blue-600 hover:bg-blue-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="save" class="w-4 h-4"></i> |
|
|
</button> |
|
|
<button id="load" class="btn bg-gray-600 hover:bg-gray-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="folder" class="w-4 h-4"></i> |
|
|
</button> |
|
|
<button id="export" class="btn bg-green-600 hover:bg-green-700 text-white py-2 px-3 rounded text-sm"> |
|
|
<i data-feather="download" class="w-4 h-4"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div class="mt-3"> |
|
|
<label class="block text-xs text-gray-400 mb-1">Import Configuration</label> |
|
|
<input id="importFile" type="file" accept="application/json" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm file:mr-3 file:py-1 file:px-3 file:rounded file:border-0 file:text-xs file:font-medium file:bg-gray-600 file:text-white"> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="flex-1 flex flex-col overflow-hidden"> |
|
|
|
|
|
<div class="bg-gray-800 border-b border-gray-700 p-4"> |
|
|
<div class="flex items-center justify-between"> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<h2 class="text-lg font-semibold text-white">Canvas</h2> |
|
|
<span id="dim" class="px-2 py-1 bg-gray-700 text-gray-300 text-xs rounded">20 × 10</span> |
|
|
</div> |
|
|
<div class="flex items-center space-x-2"> |
|
|
<button class="btn bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded" title="Zoom In"> |
|
|
<i data-feather="zoom-in" class="w-4 h-4"></i> |
|
|
</button> |
|
|
<button class="btn bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded" title="Zoom Out"> |
|
|
<i data-feather="zoom-out" class="w-4 h-4"></i> |
|
|
</button> |
|
|
<button class="btn bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded" title="Reset View"> |
|
|
<i data-feather="refresh-ccw" class="w-4 h-4"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="flex-1 relative overflow-auto bg-gray-900 p-4"> |
|
|
<div id="canvas" class="relative w-full h-full min-h-[400px] bg-gray-850 border border-gray-700 rounded-lg overflow-hidden"> |
|
|
<div id="grid" class="absolute inset-0 grid-bg" style="--cols:20;--rows:10"></div> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="bg-gray-800 border-t border-gray-700"> |
|
|
<div class="flex items-center justify-between p-3 border-b border-gray-700"> |
|
|
<h3 class="text-sm font-semibold text-gray-300 flex items-center"> |
|
|
<i data-feather="activity" class="mr-2 w-4 h-4"></i> |
|
|
Log |
|
|
</h3> |
|
|
<button class="text-gray-400 hover:text-gray-300 p-1 rounded" title="Clear Log"> |
|
|
<i data-feather="trash-2" class="w-4 h-4"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div id="log" class="h-32 overflow-y-auto bg-gray-900 text-xs font-mono p-3 space-y-1"></div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
|
|
|
|
|
|
const S=id=>document.getElementById(id); |
|
|
const statusEl=S('status'); const logEl=S('log'); |
|
|
|
|
|
function setStatus(s, type = 'success') { |
|
|
statusEl.textContent = s; |
|
|
const colors = { |
|
|
success: 'bg-green-500/20 text-green-400 border-green-500/30', |
|
|
error: 'bg-red-500/20 text-red-400 border-red-500/30', |
|
|
warning: 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30', |
|
|
info: 'bg-blue-500/20 text-blue-400 border-blue-500/30' |
|
|
}; |
|
|
statusEl.className = `px-2 py-1 text-xs rounded-full border ${colors[type] || colors.success}`; |
|
|
} |
|
|
|
|
|
function log(m,d){ |
|
|
const t=new Date().toLocaleTimeString(); |
|
|
const line=`[${t}] ${m}${d? " "+JSON.stringify(d):""}`; |
|
|
const entry = document.createElement('div'); |
|
|
entry.className = 'log-entry text-gray-300'; |
|
|
entry.textContent = line; |
|
|
logEl.prepend(entry); |
|
|
console.log(m,d??""); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
feather.replace(); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|