| | <!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> |
| |
|