Adding Project Files
This commit is contained in:
543
index.html
Normal file
543
index.html
Normal file
@@ -0,0 +1,543 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Dreckshub</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<!-- Tighter vertical spacing for customisation options -->
|
||||
<style>
|
||||
/* predictable sizing */
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
|
||||
/* very tight vertical spacing */
|
||||
.customisation-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px; /* minimal vertical gap */
|
||||
width: 100%;
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
.customisation-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px; /* minimal gap between rows */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* compact horizontal row: label | color wheel | reset button */
|
||||
.customisation-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px; /* tight horizontal spacing */
|
||||
padding: 1px 0; /* minimal vertical padding */
|
||||
min-height: 26px; /* compact row height */
|
||||
}
|
||||
|
||||
.customisation-row label {
|
||||
flex: 0 0 auto;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* tighten spacing inside the control group so wheel is very close to name */
|
||||
.color-control {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px; /* very tight */
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
/* rectangular swatch with rounded corners (replaces circular variant) */
|
||||
input[type="color"]{
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 40px; /* slightly wider rectangle */
|
||||
height: 26px;
|
||||
padding: 0;
|
||||
border: none; /* remove default box */
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
border-radius: 6px; /* rounded rectangle (not pill) */
|
||||
}
|
||||
|
||||
/* WebKit browsers: remove wrapper padding & match rounded corners */
|
||||
input[type="color"]::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
input[type="color"]::-webkit-color-swatch {
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.12); /* subtle inset ring */
|
||||
}
|
||||
|
||||
/* Firefox swatch styling with rounded corners */
|
||||
input[type="color"]::-moz-color-swatch {
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
/* nicer focus state (matches rectangle radius) */
|
||||
input[type="color"]:focus {
|
||||
outline: 2px solid rgba(15,102,214,0.9); /* example accent */
|
||||
outline-offset: 2px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* slider accent: use CSS variable so we can update from JS */
|
||||
input[type="range"],
|
||||
.vertical-slider {
|
||||
accent-color: var(--accent-color, #0f66d6);
|
||||
}
|
||||
|
||||
/* media query variant (keep sizes in sync with tightened layout) */
|
||||
@media (max-width: 520px) {
|
||||
.customisation-grid { gap: 2px; }
|
||||
.customisation-column { gap: 2px; }
|
||||
.customisation-row { gap: 3px; min-height: 24px; padding: 1px 0; }
|
||||
input[type="color"] { width: 32px; height: 24px; border-radius:5px; }
|
||||
input[type="color"]::-webkit-color-swatch-wrapper { border-radius:5px; }
|
||||
input[type="color"]::-webkit-color-swatch { border-radius:5px; }
|
||||
input[type="color"]::-moz-color-swatch { border-radius:5px; }
|
||||
.reset-default { width: 26px; height: 26px; font-size:11px; }
|
||||
.reset-default .reset-arrow { font-size:14px; } /* increased from 13px */
|
||||
}
|
||||
|
||||
/* General settings: Device, COM Port and Auto-connect — normal weight */
|
||||
.settings-left-column .settings-row label[for="device-select"],
|
||||
.settings-left-column .settings-row label[for="com-port-select"],
|
||||
.settings-left-column .settings-row label[for="autoconnect"] {
|
||||
font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: 400; /* normal */
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
/* ensure selects and option text match (not bold) */
|
||||
.settings-left-column .settings-row .settings-select,
|
||||
#device-select,
|
||||
#com-port-select,
|
||||
.settings-left-column .settings-row .settings-select option {
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
/* keep checkbox sizing unchanged but use the same font metrics for adjacent label */
|
||||
.settings-left-column .settings-row .settings-checkbox {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* ensure potentiometer ring color can be overridden by JS and isn't forced to blue
|
||||
include pseudo-elements so gradient/overlay layers are covered */
|
||||
.pot-ring,
|
||||
.pot-ring::before,
|
||||
.pot-ring::after {
|
||||
background: var(--pot-ring-color, #007bff) !important;
|
||||
background-image: none !important;
|
||||
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.12) !important;
|
||||
border-radius: 50% !important;
|
||||
}
|
||||
|
||||
/* Ghostwire button-grid: ensure first-row spacing below the first 4 items */
|
||||
.device-content[data-device="makropad ghostwire"] .button-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, auto); /* 3 buttons + pot in first row */
|
||||
column-gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
/* clear default margins on children, then add vertical gap under first row only */
|
||||
.device-content[data-device="makropad ghostwire"] .button-grid > * {
|
||||
margin: 0;
|
||||
}
|
||||
.device-content[data-device="makropad ghostwire"] .button-grid > *:nth-child(-n+4) {
|
||||
margin-bottom: 12px; /* increased space under first row */
|
||||
}
|
||||
|
||||
/* responsive fallback: collapse to 2 columns on narrow widths */
|
||||
@media (max-width: 480px) {
|
||||
.device-content[data-device="makropad ghostwire"] .button-grid {
|
||||
grid-template-columns: repeat(2, auto);
|
||||
}
|
||||
.device-content[data-device="makropad ghostwire"] .button-grid > *:nth-child(-n+2) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-wrapper">
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-top">
|
||||
<div class="logo" role="banner" aria-label="Dreckshub">
|
||||
<span class="logo-text">
|
||||
<span class="logo-base">Drecks</span><span class="logo-highlight">hub</span>
|
||||
</span>
|
||||
</div>
|
||||
<nav class="tabs">
|
||||
<button class="tab-button active" data-target="tab-1">Main</button>
|
||||
<button class="tab-button" data-target="tab-2">Placeholder 1</button>
|
||||
<button class="tab-button" data-target="tab-3">Sliders</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-bottom">
|
||||
<button id="settings-btn" class="settings-btn">⚙ Settings</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="content-area">
|
||||
<section id="tab-1" class="tab-content active">
|
||||
<!-- Makropad-specific content -->
|
||||
<div class="device-content" data-device="makropad">
|
||||
<h2>Makropad — Main</h2>
|
||||
<p>Content for Makropad on the first tab.</p>
|
||||
</div>
|
||||
|
||||
<!-- Makropad Ghostwire-specific content -->
|
||||
<div class="device-content" data-device="makropad ghostwire" style="display:none;">
|
||||
<h2>Makropad Ghostwire — Main</h2>
|
||||
<p>Alternate content for Makropad Ghostwire on the first tab.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="tab-2" class="tab-content">
|
||||
<!-- Makropad-specific two-column layout -->
|
||||
<div class="device-content" data-device="makropad">
|
||||
<div class="makropad-two-col" aria-hidden="false">
|
||||
<div class="left-group">
|
||||
<div class="grid-3cols">
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
</div>
|
||||
<!-- spacer row (empty) to match the requested layout) -->
|
||||
<div class="grid-3cols spacer-row">
|
||||
<div></div><div></div><div></div>
|
||||
</div>
|
||||
<div class="grid-3cols">
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
</div>
|
||||
<div class="grid-3cols">
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="vertical-sep" aria-hidden="true"></div>
|
||||
|
||||
<div class="right-group">
|
||||
<div class="grid-3cols">
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
</div>
|
||||
<div class="grid-3cols">
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
</div>
|
||||
<div class="grid-3cols">
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
</div>
|
||||
<div class="grid-3cols">
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
<button class="grid-button">b</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Makropad Ghostwire-specific grid -->
|
||||
<div class="device-content" data-device="makropad ghostwire" style="display:none;">
|
||||
<div class="button-grid" aria-hidden="false">
|
||||
<!-- different arrangement / labels for Ghostwire -->
|
||||
<button class="grid-button">1</button>
|
||||
<button class="grid-button">2</button>
|
||||
<button class="grid-button">3</button>
|
||||
<div class="potentiometer" role="img" aria-label="ghost pot" tabindex="-1">
|
||||
<div class="pot-ring" style="--deg: 90deg;"></div>
|
||||
<div class="pot-inner"><span class="pot-value">50</span></div>
|
||||
</div>
|
||||
<!-- remaining placeholders -->
|
||||
<button class="grid-button">4</button>
|
||||
<button class="grid-button">5</button>
|
||||
<button class="grid-button">6</button>
|
||||
<button class="grid-button">7</button>
|
||||
<button class="grid-button">8</button>
|
||||
<button class="grid-button">9</button>
|
||||
<button class="grid-button">10</button>
|
||||
<button class="grid-button">11</button>
|
||||
<button class="grid-button">12</button>
|
||||
<button class="grid-button">13</button>
|
||||
<button class="grid-button">14</button>
|
||||
<button class="grid-button">15</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="tab-3" class="tab-content">
|
||||
<div class="main-layout">
|
||||
<!-- Sliders Panel for vertical sliders -->
|
||||
<div class="sliders-panel">
|
||||
<div class="slider-group">
|
||||
<div class="slider-row">
|
||||
<div class="slider-tag-generator">
|
||||
<input type="text" id="tag-input-slider0" class="tag-input" placeholder="Enter Program Name">
|
||||
<div id="tag-container-slider0" class="tag-container"></div>
|
||||
</div>
|
||||
<div class="slider-controls">
|
||||
<input type="range" min="0" max="100" value="0" id="slider0" class="vertical-slider">
|
||||
<span id="slider0-value-bubble" class="value-bubble" aria-hidden="true">50</span>
|
||||
<button class="slider-btn" id="slider0-btn"></button>
|
||||
</div>
|
||||
<div class="slider-separator" aria-hidden="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="slider-group">
|
||||
<div class="slider-row">
|
||||
<div class="slider-tag-generator">
|
||||
<input type="text" id="tag-input-slider1" class="tag-input" placeholder="Enter Program Name">
|
||||
<div id="tag-container-slider1" class="tag-container"></div>
|
||||
</div>
|
||||
<div class="slider-controls">
|
||||
<input type="range" min="0" max="100" value="0" id="slider1" class="vertical-slider">
|
||||
<span id="slider1-value-bubble" class="value-bubble" aria-hidden="true">0</span>
|
||||
<button class="slider-btn" id="slider1-btn"></button>
|
||||
</div>
|
||||
<div class="slider-separator" aria-hidden="true"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="slider-group">
|
||||
<div class="slider-row">
|
||||
<div class="slider-tag-generator">
|
||||
<input type="text" id="tag-input-slider2" class="tag-input" placeholder="Enter Program Name">
|
||||
<div id="tag-container-slider2" class="tag-container"></div>
|
||||
</div>
|
||||
<div class="slider-controls">
|
||||
<input type="range" min="0" max="100" value="0" id="slider2" class="vertical-slider">
|
||||
<span id="slider2-value-bubble" class="value-bubble" aria-hidden="true">0</span>
|
||||
<button class="slider-btn" id="slider2-btn"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Simple settings panel toggled by settings button -->
|
||||
<div id="settings-overlay" class="settings-overlay" aria-hidden="true">
|
||||
<div class="settings-window" role="dialog" aria-modal="true" aria-labelledby="settings-title">
|
||||
<div class="settings-topbar">
|
||||
<div id="settings-title" class="settings-title">Settings</div>
|
||||
<nav class="settings-tabs">
|
||||
<button class="settings-tab-button active" data-target="settings-tab-1">General</button>
|
||||
<button class="settings-tab-button" data-target="settings-tab-2">Customisation</button>
|
||||
<button class="settings-tab-button" data-target="settings-tab-3">Placeholder B</button>
|
||||
</nav>
|
||||
<button class="settings-close" id="close-settings">✕</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-content">
|
||||
<section id="settings-tab-1" class="settings-tab-content active">
|
||||
<!-- left column: keep both dropdowns on the left stacked -->
|
||||
<div class="settings-left-column">
|
||||
<div class="settings-row">
|
||||
<label for="device-select">Device</label>
|
||||
<select id="device-select" class="settings-select" aria-label="Device selection">
|
||||
<option value="Makropad">Makropad</option>
|
||||
<option value="Makropad Ghostwire">Makropad Ghostwire</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="settings-row" style="margin-top:8px;">
|
||||
<label for="com-port-select">COM Port</label>
|
||||
<select id="com-port-select" class="settings-select" aria-label="COM port selection">
|
||||
<option value="">(none)</option>
|
||||
</select>
|
||||
<button id="com-refresh-btn" class="settings-close" title="Refresh COM ports">⟳</button>
|
||||
|
||||
<!-- Connect / Disconnect controls -->
|
||||
<button id="connect-com-btn" class="assign-btn connect-state" style="margin-left:8px;">Connect</button>
|
||||
</div>
|
||||
|
||||
<!-- new: autoconnect checkbox under the two dropdowns -->
|
||||
<div class="settings-row" style="margin-top:8px;">
|
||||
<label for="autoconnect">Auto-connect</label>
|
||||
<input type="checkbox" id="autoconnect" class="settings-checkbox" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- right column (can hold extra controls later) -->
|
||||
<div class="settings-right-column">
|
||||
<div class="settings-checkbox-row">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="startup-with-system" class="settings-checkbox" />
|
||||
Startup with system
|
||||
</label>
|
||||
</div>
|
||||
<div class="settings-checkbox-row" style="margin-top:8px;">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="close-to-tray" class="settings-checkbox" />
|
||||
Close to tray
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="settings-tab-2" class="settings-tab-content">
|
||||
<div class="customisation-grid">
|
||||
<!-- new layout: 3 columns for controls -->
|
||||
<div class="customisation-column">
|
||||
<div class="customisation-row">
|
||||
<label for="cust-bg">Background</label>
|
||||
<div class="color-control">
|
||||
<input type="color" id="cust-bg" name="cust-bg" value="#0d0d0f" data-default="#0d0d0f" />
|
||||
<button type="button" class="reset-default" data-target="cust-bg" title="Reset to default"><span class="reset-arrow">↩︎</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="customisation-row">
|
||||
<label for="cust-sidebar">Sidebar</label>
|
||||
<div class="color-control">
|
||||
<input type="color" id="cust-sidebar" name="cust-sidebar" value="#111111" data-default="#111111" />
|
||||
<button type="button" class="reset-default" data-target="cust-sidebar" title="Reset to default"><span class="reset-arrow">↩︎</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="customisation-column">
|
||||
<div class="customisation-row">
|
||||
<label for="cust-button">Button (grid)</label>
|
||||
<div class="color-control">
|
||||
<input type="color" id="cust-button" name="cust-button" value="#1b1b1b" data-default="#1b1b1b" />
|
||||
<button type="button" class="reset-default" data-target="cust-button" title="Reset to default"><span class="reset-arrow">↩︎</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="customisation-row">
|
||||
<label for="cust-pot">Potentiometer ring</label>
|
||||
<div class="color-control">
|
||||
<input type="color" id="cust-pot" name="cust-pot" value="#007bff" data-default="#007bff" />
|
||||
<button type="button" class="reset-default" data-target="cust-pot" title="Reset to default"><span class="reset-arrow">↩︎</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="customisation-column">
|
||||
<div class="customisation-row">
|
||||
<label for="cust-accent">Accent (links / highlights)</label>
|
||||
<div class="color-control">
|
||||
<input type="color" id="cust-accent" name="cust-accent" value="#0f66d6" data-default="#0f66d6" />
|
||||
<button type="button" class="reset-default" data-target="cust-accent" title="Reset to default"><span class="reset-arrow">↩︎</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="settings-tab-3" class="settings-tab-content">
|
||||
<h4>Placeholder B</h4>
|
||||
<p>Content for placeholder B.</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- wiring for colour inputs + reset buttons (applies cust-pot to .pot-ring elements) -->
|
||||
<script>
|
||||
(function () {
|
||||
function applyColorToPotRing(color) {
|
||||
// set on :root so pseudoelements that read the CSS var update
|
||||
document.documentElement.style.setProperty('--pot-ring-color', color);
|
||||
|
||||
document.querySelectorAll('.pot-ring').forEach(el => {
|
||||
// also set the custom property on the element itself (higher specificity)
|
||||
el.style.setProperty('--pot-ring-color', color, '');
|
||||
|
||||
// clear any background-image and force a plain background color on the element
|
||||
el.style.setProperty('background-image', 'none', 'important');
|
||||
el.style.setProperty('background-color', color, 'important');
|
||||
el.style.setProperty('background', color, 'important');
|
||||
|
||||
// ensure inline box-shadow as fallback
|
||||
el.style.boxShadow = 'inset 0 0 0 1px rgba(0,0,0,0.12)';
|
||||
|
||||
// small toggle to force paint/reflow for stubborn cases
|
||||
el.dataset.potRepaint = '1';
|
||||
window.requestAnimationFrame(() => { delete el.dataset.potRepaint; });
|
||||
});
|
||||
}
|
||||
|
||||
function applyAccentColor(color) {
|
||||
// set on :root so all inputs using var(--accent-color) update
|
||||
document.documentElement.style.setProperty('--accent-color', color);
|
||||
// also ensure existing range inputs get the accent color (fallback)
|
||||
document.querySelectorAll('input[type="range"]').forEach(r => {
|
||||
r.style.setProperty('accent-color', color);
|
||||
});
|
||||
}
|
||||
|
||||
function applyInputValue(input) {
|
||||
if (!input) return;
|
||||
const id = input.id;
|
||||
const val = input.value;
|
||||
if (id === 'cust-pot') {
|
||||
applyColorToPotRing(val);
|
||||
}
|
||||
// add more mappings here if you want other inputs to affect other UI parts
|
||||
}
|
||||
|
||||
// wire color inputs that include data-default
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// initialize all color inputs
|
||||
document.querySelectorAll('input[type="color"][id]').forEach(input => {
|
||||
applyInputValue(input);
|
||||
// also initialize accent if this is the cust-accent input
|
||||
if (input.id === 'cust-accent') applyAccentColor(input.value);
|
||||
input.addEventListener('input', () => applyInputValue(input));
|
||||
input.addEventListener('change', () => applyInputValue(input));
|
||||
});
|
||||
|
||||
// ensure range inputs reflect the accent variable initially
|
||||
const custAccent = document.getElementById('cust-accent');
|
||||
if (custAccent) applyAccentColor(custAccent.value);
|
||||
|
||||
// wire reset buttons
|
||||
document.querySelectorAll('.reset-default[data-target]').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
const targetId = btn.getAttribute('data-target');
|
||||
const input = document.getElementById(targetId);
|
||||
if (!input) return;
|
||||
const def = input.getAttribute('data-default') || '#000000';
|
||||
input.value = def;
|
||||
// trigger input/change handlers
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
});
|
||||
});
|
||||
|
||||
// reapply cust-pot when new .pot-ring elements appear
|
||||
const obs = new MutationObserver(() => {
|
||||
const custPot = document.getElementById('cust-pot');
|
||||
if (custPot) applyInputValue(custPot);
|
||||
});
|
||||
obs.observe(document.body, { childList: true, subtree: true });
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script src="renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user