359 lines
13 KiB
HTML
359 lines
13 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Holesail Browser – Settings</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||
<style>
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
:root {
|
||
--bg: #09090b;
|
||
--surface: #111113;
|
||
--card: #18181b;
|
||
--elevated: #1f1f23;
|
||
--border: #27272a;
|
||
--border2: #3f3f46;
|
||
--text: #fafafa;
|
||
--text2: #a1a1aa;
|
||
--text3: #71717a;
|
||
--text4: #52525b;
|
||
--cyan: #22d3ee;
|
||
--cyan-dim: rgba(34,211,238,.12);
|
||
--green: #4ade80;
|
||
--red: #f43f5e;
|
||
--red-dim: rgba(244,63,94,.12);
|
||
--amber: #fbbf24;
|
||
}
|
||
|
||
html, body {
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
font-family: 'Inter', system-ui, sans-serif;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
-webkit-font-smoothing: antialiased;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
::-webkit-scrollbar { width: 6px; }
|
||
::-webkit-scrollbar-track { background: transparent; }
|
||
::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 99px; }
|
||
|
||
.page-wrap {
|
||
max-width: 580px;
|
||
margin: 0 auto;
|
||
padding: 32px 20px 60px;
|
||
}
|
||
|
||
/* Header */
|
||
.page-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 28px;
|
||
padding-bottom: 20px;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
.logo-icon {
|
||
width: 36px;
|
||
height: 36px;
|
||
background: var(--cyan-dim);
|
||
border: 1px solid rgba(34,211,238,.2);
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
.logo-icon svg { width: 20px; height: 20px; color: var(--cyan); }
|
||
.header-text h1 { font-size: 17px; font-weight: 700; color: var(--text); letter-spacing: -0.02em; }
|
||
.header-text p { font-size: 12.5px; color: var(--text3); margin-top: 2px; }
|
||
|
||
.dashboard-link {
|
||
margin-left: auto;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 12.5px;
|
||
font-weight: 500;
|
||
color: var(--cyan);
|
||
text-decoration: none;
|
||
padding: 6px 12px;
|
||
border-radius: 6px;
|
||
border: 1px solid rgba(34,211,238,.2);
|
||
background: var(--cyan-dim);
|
||
transition: all 0.15s;
|
||
}
|
||
.dashboard-link:hover { background: rgba(34,211,238,.2); }
|
||
.dashboard-link svg { width: 13px; height: 13px; }
|
||
|
||
/* Cards / sections */
|
||
.section-card {
|
||
background: var(--card);
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
margin-bottom: 12px;
|
||
}
|
||
.section-card-header {
|
||
padding: 13px 18px;
|
||
border-bottom: 1px solid var(--border);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.section-card-header svg { width: 15px; height: 15px; color: var(--text3); flex-shrink: 0; }
|
||
.section-card-title { font-size: 13px; font-weight: 600; color: var(--text); }
|
||
.section-card-body { padding: 16px 18px; display: flex; flex-direction: column; gap: 14px; }
|
||
|
||
/* Fields */
|
||
.field { display: flex; flex-direction: column; gap: 5px; }
|
||
.field-label { font-size: 12.5px; font-weight: 500; color: var(--text2); }
|
||
.field-hint { font-size: 11.5px; color: var(--text4); line-height: 1.5; }
|
||
|
||
input[type="text"],
|
||
input[type="number"] {
|
||
background: var(--elevated);
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
color: var(--text);
|
||
font-family: inherit;
|
||
font-size: 13px;
|
||
padding: 7px 10px;
|
||
outline: none;
|
||
transition: border-color 0.15s;
|
||
width: 100%;
|
||
max-width: 220px;
|
||
}
|
||
input[type="text"]:focus,
|
||
input[type="number"]:focus { border-color: var(--cyan); }
|
||
input::placeholder { color: var(--text4); }
|
||
input[type="number"]:invalid { border-color: var(--red); }
|
||
|
||
.mono-input { font-family: 'JetBrains Mono', monospace; font-size: 12px; }
|
||
|
||
/* Checkbox row */
|
||
.checkbox-row {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 10px;
|
||
}
|
||
.checkbox-row input[type="checkbox"] {
|
||
width: 15px;
|
||
height: 15px;
|
||
min-width: 15px;
|
||
accent-color: var(--cyan);
|
||
cursor: pointer;
|
||
margin-top: 1px;
|
||
max-width: none;
|
||
}
|
||
.checkbox-row .check-text { display: flex; flex-direction: column; gap: 3px; }
|
||
.checkbox-row label { font-size: 13px; color: var(--text2); cursor: pointer; font-weight: 500; }
|
||
.checkbox-row .field-hint { margin-top: 0; }
|
||
|
||
/* Divider */
|
||
.divider { height: 1px; background: var(--border); }
|
||
|
||
/* Buttons */
|
||
.btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
padding: 8px 16px;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
border: none;
|
||
transition: all 0.15s;
|
||
}
|
||
.btn svg { width: 14px; height: 14px; }
|
||
.btn-primary { background: var(--cyan); color: #09090b; }
|
||
.btn-primary:hover { background: #06b6d4; }
|
||
.btn-secondary { background: var(--elevated); color: var(--text2); border: 1px solid var(--border); }
|
||
.btn-secondary:hover { background: var(--border); color: var(--text); }
|
||
|
||
.actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-top: 20px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
/* Toast */
|
||
.toast {
|
||
position: fixed;
|
||
bottom: 20px;
|
||
right: 20px;
|
||
background: var(--elevated);
|
||
border: 1px solid var(--border2);
|
||
border-radius: 8px;
|
||
padding: 9px 14px;
|
||
font-size: 13px;
|
||
color: var(--text);
|
||
box-shadow: 0 8px 24px rgba(0,0,0,.4);
|
||
transform: translateY(6px);
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: all 0.2s ease;
|
||
max-width: 280px;
|
||
z-index: 100;
|
||
}
|
||
.toast.show { opacity: 1; transform: translateY(0); }
|
||
.toast.err { border-color: rgba(244,63,94,.3); color: var(--red); }
|
||
.toast.show:not(.err) { border-color: rgba(74,222,128,.3); color: var(--green); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="page-wrap">
|
||
|
||
<div class="page-header">
|
||
<div class="logo-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
||
<path d="M3 17l4-8 4 4 4-6 4 10"/>
|
||
<path d="M3 21h18"/>
|
||
</svg>
|
||
</div>
|
||
<div class="header-text">
|
||
<h1>Holesail Browser</h1>
|
||
<p>Extension settings — changes apply immediately after saving</p>
|
||
</div>
|
||
<a href="dashboard.html" target="_blank" class="dashboard-link">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||
<rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/>
|
||
<rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/>
|
||
</svg>
|
||
Dashboard
|
||
</a>
|
||
</div>
|
||
|
||
<!-- Proxy -->
|
||
<div class="section-card">
|
||
<div class="section-card-header">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
|
||
<span class="section-card-title">Proxy</span>
|
||
</div>
|
||
<div class="section-card-body">
|
||
<div class="field">
|
||
<label class="field-label" for="proxyPort">HTTPS proxy port</label>
|
||
<input type="number" id="proxyPort" min="1" max="65535" placeholder="8443">
|
||
<span class="field-hint">Port for the local HTTPS proxy serving virtual hosts. Default 8443.</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Server defaults -->
|
||
<div class="section-card">
|
||
<div class="section-card-header">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="6" height="6" rx="1"/><rect x="16" y="3" width="6" height="6" rx="1"/><rect x="9" y="15" width="6" height="6" rx="1"/><line x1="5" y1="9" x2="12" y2="15"/><line x1="19" y1="9" x2="12" y2="15"/></svg>
|
||
<span class="section-card-title">Server defaults</span>
|
||
</div>
|
||
<div class="section-card-body">
|
||
<div class="field">
|
||
<label class="field-label" for="defaultServerPort">Default port</label>
|
||
<input type="number" id="defaultServerPort" min="1" max="65535" placeholder="3000">
|
||
</div>
|
||
<div class="field">
|
||
<label class="field-label" for="defaultServerHost">Default host</label>
|
||
<input type="text" id="defaultServerHost" placeholder="127.0.0.1" class="mono-input">
|
||
</div>
|
||
<div class="checkbox-row">
|
||
<input type="checkbox" id="defaultSecure" checked>
|
||
<div class="check-text">
|
||
<label for="defaultSecure">Secure mode</label>
|
||
<span class="field-hint">Use secure hs://s000… URLs by default</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Timeouts -->
|
||
<div class="section-card">
|
||
<div class="section-card-header">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12,6 12,12 16,14"/></svg>
|
||
<span class="section-card-title">API timeouts</span>
|
||
</div>
|
||
<div class="section-card-body">
|
||
<div class="field">
|
||
<label class="field-label" for="requestTimeoutMs">Request timeout (ms)</label>
|
||
<input type="number" id="requestTimeoutMs" min="0" max="300000" step="1000" placeholder="0">
|
||
<span class="field-hint">Applied to Holesail.request() calls. Use 0 for no timeout.</span>
|
||
</div>
|
||
<div class="field">
|
||
<label class="field-label" for="readyTimeoutMs">Ready timeout (ms)</label>
|
||
<input type="number" id="readyTimeoutMs" min="0" max="60000" step="1000" placeholder="0">
|
||
<span class="field-hint">Reject Holesail.ready() after this many ms. Use 0 to wait indefinitely.</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Behaviour -->
|
||
<div class="section-card">
|
||
<div class="section-card-header">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
|
||
<span class="section-card-title">Behaviour & notifications</span>
|
||
</div>
|
||
<div class="section-card-body">
|
||
<div class="checkbox-row">
|
||
<input type="checkbox" id="disableOnFileUrls">
|
||
<div class="check-text">
|
||
<label for="disableOnFileUrls">Disable on file:// URLs</label>
|
||
<span class="field-hint">Skip injecting the Holesail API on local HTML files</span>
|
||
</div>
|
||
</div>
|
||
<div class="divider"></div>
|
||
<div class="checkbox-row">
|
||
<input type="checkbox" id="notifyOnDisconnect">
|
||
<div class="check-text">
|
||
<label for="notifyOnDisconnect">Disconnect notification</label>
|
||
<span class="field-hint">Show a browser notification when the native host disconnects</span>
|
||
</div>
|
||
</div>
|
||
<div class="divider"></div>
|
||
<div class="checkbox-row">
|
||
<input type="checkbox" id="debug">
|
||
<div class="check-text">
|
||
<label for="debug">Debug mode</label>
|
||
<span class="field-hint">Enable verbose logging for troubleshooting</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Certificate -->
|
||
<div class="section-card">
|
||
<div class="section-card-header">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
|
||
<span class="section-card-title">Certificate authority</span>
|
||
</div>
|
||
<div class="section-card-body">
|
||
<p class="field-hint" style="line-height:1.6;">Install the Holesail root CA so Chrome trusts HTTPS for <code style="font-family:'JetBrains Mono',monospace;font-size:11px;color:#22d3ee;">*.hole.sail</code> virtual host domains. Requires the native host to be connected.</p>
|
||
<div>
|
||
<button type="button" class="btn btn-secondary" id="installCaBtn">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7,10 12,15 17,10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||
Install root CA
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="actions">
|
||
<button type="button" class="btn btn-primary" id="btnSave">Save settings</button>
|
||
<button type="button" class="btn btn-secondary" id="btnReset">Reset to defaults</button>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="toast" id="toast"></div>
|
||
|
||
<script src="options.js"></script>
|
||
</body>
|
||
</html>
|