Redesign website and extension with koala nature theme

v3.0.0 "The Greens Update" palette: forest-green oklch color system
(accent green + terracotta secondary) replaces the indigo/slate scheme
across the landing page and the extension popup/options. The blob
background becomes an animated bamboo forest overlay (parallax stalks,
falling leaves, canopy, ground mist, fireflies, light sweep, grain)
with prefers-reduced-motion support. Layout, structure and copy are
unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
KoalaDev
2026-07-10 19:18:40 +02:00
parent 05246ac485
commit aa52d319e1
21 changed files with 1611 additions and 715 deletions
+29 -14
View File
@@ -1,12 +1,27 @@
:root {
--bg: #0f172a;
--card: #1e293b;
--panel: #172033;
--accent: #6366f1;
--accent-hover: #818cf8;
--text: #f8fafc;
--text-muted: #94a3b8;
--border: #334155;
/* v3.0.0 "The Greens Update" nature palette (shared with the website) */
--bg-base: oklch(0.20 0.025 140);
--surface: oklch(0.27 0.035 130);
--surface-deep: oklch(0.16 0.025 135);
--surface-alt: oklch(0.23 0.03 130);
--border-soft: rgba(255, 255, 255, 0.06);
--border-strong: oklch(0.32 0.03 125);
--text-primary: oklch(0.96 0.01 90);
--text-secondary: oklch(0.78 0.02 90);
--accent-green: oklch(0.68 0.13 150);
--accent-green-hover: oklch(0.75 0.13 150);
--accent-terracotta: oklch(0.62 0.14 45);
--danger: oklch(0.55 0.15 25);
--text-on-green: oklch(0.14 0.02 90);
--bg: var(--bg-base);
--card: var(--surface);
--panel: var(--surface-alt);
--accent: var(--accent-green);
--accent-hover: var(--accent-green-hover);
--text: var(--text-primary);
--text-muted: var(--text-secondary);
--border: var(--border-strong);
--radius: 8px;
}
@@ -125,12 +140,12 @@ h2 {
transition: border-color 0.2s, box-shadow 0.2s;
}
.preset-card:hover {
border-color: rgba(99, 102, 241, 0.4);
border-color: rgba(86, 174, 108, 0.4);
}
.preset-card:has(input:checked) {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(99, 102, 241, 0.35);
box-shadow: 0 0 0 1px rgba(86, 174, 108, 0.35);
}
.preset-card input {
@@ -141,8 +156,8 @@ h2 {
display: grid;
gap: 12px;
padding: 16px;
background: rgba(15, 23, 42, 0.58);
border: 1px solid rgba(148, 163, 184, 0.16);
background: rgba(16, 25, 14, 0.58);
border: 1px solid rgba(163, 162, 148, 0.16);
border-radius: var(--radius);
transition: opacity 0.3s;
}
@@ -204,7 +219,7 @@ input[type="number"]:focus {
position: absolute;
cursor: pointer;
inset: 0;
background-color: #334155;
background-color: var(--border-strong);
transition: .3s;
border-radius: 22px;
}
@@ -216,7 +231,7 @@ input[type="number"]:focus {
width: 16px;
left: 3px;
bottom: 3px;
background-color: #94a3b8;
background-color: var(--text-secondary);
transition: .3s;
border-radius: 50%;
}
+1 -1
View File
@@ -430,7 +430,7 @@
if (Date.now() - hcmLastUserGestureAt > HCM_USER_GESTURE_MS) return 'involuntary';
return 'deliberate';
return 'deliberate';
}
+62 -47
View File
@@ -12,14 +12,29 @@
}
:root {
--bg: #0f172a;
--card: #1e293b;
--accent: #6366f1;
--accent-hover: #818cf8;
--text: #f8fafc;
--text-muted: #94a3b8;
--success: #22c55e;
--error: #ef4444;
/* v3.0.0 "The Greens Update" nature palette (shared with the website) */
--bg-base: oklch(0.20 0.025 140);
--surface: oklch(0.27 0.035 130);
--surface-deep: oklch(0.16 0.025 135);
--surface-alt: oklch(0.23 0.03 130);
--border-soft: rgba(255, 255, 255, 0.06);
--border-strong: oklch(0.32 0.03 125);
--text-primary: oklch(0.96 0.01 90);
--text-secondary: oklch(0.78 0.02 90);
--accent-green: oklch(0.68 0.13 150);
--accent-green-hover: oklch(0.75 0.13 150);
--accent-terracotta: oklch(0.62 0.14 45);
--danger: oklch(0.55 0.15 25);
--text-on-green: oklch(0.14 0.02 90);
--bg: var(--surface-deep);
--card: var(--surface);
--accent: var(--accent-green);
--accent-hover: var(--accent-green-hover);
--text: var(--text-primary);
--text-muted: var(--text-secondary);
--success: var(--accent-green);
--error: var(--danger);
--radius: 12px;
--star: #fbbf24;
}
@@ -103,7 +118,7 @@
.tab-btn.active {
background: var(--accent);
color: white;
color: var(--text-on-green);
}
.tab-content {
@@ -138,7 +153,7 @@
width: 100%;
box-sizing: border-box;
background: var(--card);
border: 1px solid #334155;
border: 1px solid var(--border-strong);
color: white;
padding: 10px;
border-radius: 8px;
@@ -153,7 +168,7 @@
}
#langSelector::picker(select) {
background-color: var(--bg);
border: 1px solid #334155;
border: 1px solid var(--border-strong);
border-radius: 8px;
padding: 4px;
font-family: 'Twemoji Country Flags', inherit;
@@ -162,7 +177,7 @@
input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
box-shadow: 0 0 0 2px rgba(86, 174, 108, 0.2);
}
button.primary {
@@ -170,7 +185,7 @@
padding: 12px;
background: var(--accent);
border: none;
color: white;
color: var(--text-on-green);
font-weight: 700;
border-radius: 8px;
cursor: pointer;
@@ -186,7 +201,7 @@
button.secondary {
width: 100%;
padding: 10px;
background: #334155;
background: var(--border-strong);
border: none;
color: white;
font-weight: 600;
@@ -201,12 +216,12 @@
padding: 12px;
border-radius: 8px;
margin-bottom: 12px;
border: 1px solid #334155;
border: 1px solid var(--border-strong);
}
.peer-item {
padding: 8px 0;
border-bottom: 1px solid #334155;
border-bottom: 1px solid var(--border-strong);
}
.peer-item:last-child { border: 0; }
@@ -271,9 +286,9 @@
animation: toastSlideIn 0.3s ease-out, toastFadeOut 0.3s ease-in 2.7s forwards;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.toast-success { background: var(--success); color: white; }
.toast-success { background: var(--success); color: var(--text-on-green); }
.toast-error { background: var(--error); color: white; }
.toast-info { background: var(--accent); color: white; }
.toast-info { background: var(--accent); color: var(--text-on-green); }
.toast-warning { background: #f59e0b; color: white; }
@keyframes toastSlideIn {
@@ -309,7 +324,7 @@
position: absolute;
cursor: pointer;
top: 0; left: 0; right: 0; bottom: 0;
background-color: #334155;
background-color: var(--border-strong);
transition: .3s;
border-radius: 20px;
}
@@ -320,7 +335,7 @@
width: 14px;
left: 3px;
bottom: 3px;
background-color: #94a3b8;
background-color: #a3a294;
transition: .3s;
border-radius: 50%;
}
@@ -365,8 +380,8 @@
50% { opacity: 1; transform: scale(1.3); }
}
@keyframes spotlightPulse {
0%, 100% { box-shadow: 0 0 0 9999px rgba(15, 23, 42, 0.65), 0 0 0 0px var(--accent); }
50% { box-shadow: 0 0 0 9999px rgba(15, 23, 42, 0.65), 0 0 0 6px var(--accent); }
0%, 100% { box-shadow: 0 0 0 9999px rgba(16, 25, 14, 0.65), 0 0 0 0px var(--accent); }
50% { box-shadow: 0 0 0 9999px rgba(16, 25, 14, 0.65), 0 0 0 6px var(--accent); }
}
@keyframes floatHint {
0%, 100% { transform: translateY(0); }
@@ -399,12 +414,12 @@
<!-- JOIN SECTION: Visible when not in a room -->
<div id="section-join">
<button id="createRoomBtn" class="primary" style="margin-bottom: 16px; padding: 14px; font-size: 15px; background: linear-gradient(135deg, #6366f1, #a855f7); box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);" data-i18n="BTN_CREATE_ROOM">+ Create New Room</button>
<details style="margin-bottom: 12px; background: var(--card); border: 1px solid #334155; border-radius: 8px; padding: 8px;">
<button id="createRoomBtn" class="primary" style="margin-bottom: 16px; padding: 14px; font-size: 15px; background: linear-gradient(135deg, var(--accent-green), var(--accent-terracotta)); color: var(--text-on-green); box-shadow: 0 4px 12px rgba(86, 174, 108, 0.4);" data-i18n="BTN_CREATE_ROOM">+ Create New Room</button>
<details style="margin-bottom: 12px; background: var(--card); border: 1px solid var(--border-strong); border-radius: 8px; padding: 8px;">
<summary style="font-size: 11px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; cursor: pointer; outline: none;" data-i18n="MANUAL_CONNECT_HEADER">Manual Connect / Advanced</summary>
<div style="margin-top: 12px;">
<!-- Server selection — its own concern, kept visually separate from the room fields. -->
<div style="background: var(--bg); border: 1px solid #334155; border-radius: 8px; padding: 10px; margin-bottom: 14px;">
<div style="background: var(--bg); border: 1px solid var(--border-strong); border-radius: 8px; padding: 10px; margin-bottom: 14px;">
<div class="form-group" style="margin-bottom:0;">
<label title="Select which KoalaSync server to use" data-i18n="LABEL_SERVER">Server</label>
<div style="display:flex; gap:4px; margin-bottom:0;">
@@ -458,7 +473,7 @@
<div id="hostControlCard" class="info-card" style="margin-bottom: 20px; display:none;">
<div style="display:flex; justify-content:space-between; align-items:center;">
<label style="margin:0;" title="Restrict who can control playback in this room" data-i18n="LABEL_HOST_CONTROL" data-i18n-title="LABEL_HOST_CONTROL_TOOLTIP">Host Control</label>
<span id="hostRoleBadge" style="font-size:10px; font-weight:700; text-transform:uppercase; padding:2px 8px; border-radius:8px; background:var(--accent); color:#fff;"></span>
<span id="hostRoleBadge" style="font-size:10px; font-weight:700; text-transform:uppercase; padding:2px 8px; border-radius:8px; background:var(--accent); color:var(--text-on-green);"></span>
</div>
<div id="hostControlToggleRow" style="display:none; align-items:center; justify-content:space-between; gap:12px; margin-top:12px;">
<span style="font-size:12px; color:var(--text); line-height:1.3;" data-i18n="LABEL_HOST_ONLY_TOGGLE">Only I can control playback</span>
@@ -467,7 +482,7 @@
<span class="slider"></span>
</label>
</div>
<div id="hostControlCohostHint" style="display:none; margin-top:10px; padding-top:10px; border-top:1px solid #334155; font-size:11px; color:var(--text-muted); line-height:1.4;" data-i18n="NOTICE_COHOST_HINT">Tip: grant control to individual viewers in the participant list below.</div>
<div id="hostControlCohostHint" style="display:none; margin-top:10px; padding-top:10px; border-top:1px solid var(--border-strong); font-size:11px; color:var(--text-muted); line-height:1.4;" data-i18n="NOTICE_COHOST_HINT">Tip: grant control to individual viewers in the participant list below.</div>
<div id="hostControlGuestNote" style="display:none; margin-top:10px; font-size:12px; color:var(--text-muted);" data-i18n="NOTICE_HOST_CONTROLS">The host controls playback for everyone.</div>
</div>
@@ -492,7 +507,7 @@
<option value="" data-i18n="OPTION_SELECT_TAB">-- Select a Tab --</option>
</select>
<!-- Hint Tooltip -->
<div id="targetTabHint" style="display: none; position: absolute; top: -25px; right: 0; background: var(--accent); color: white; padding: 4px 8px; border-radius: 6px; font-size: 11px; font-weight: bold; pointer-events: none; animation: floatHint 2s ease-in-out infinite; box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4); z-index: 10;" data-i18n="HINT_SELECT_VIDEO">
<div id="targetTabHint" style="display: none; position: absolute; top: -25px; right: 0; background: var(--accent); color: var(--text-on-green); padding: 4px 8px; border-radius: 6px; font-size: 11px; font-weight: bold; pointer-events: none; animation: floatHint 2s ease-in-out infinite; box-shadow: 0 4px 12px rgba(86, 174, 108, 0.4); z-index: 10;" data-i18n="HINT_SELECT_VIDEO">
Select your video here!
<div style="position: absolute; bottom: -4px; right: 20px; width: 8px; height: 8px; background: var(--accent); transform: rotate(45deg);"></div>
</div>
@@ -500,16 +515,16 @@
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px;">
<label style="margin: 0;" data-i18n="LABEL_REMOTE_CONTROL">Remote Control</label>
<button id="syncTabCopyInvite" title="Copy Invite Link" style="background:transparent; border: 1px solid #334155; border-radius: 6px; padding: 4px 8px; font-size: 11px; cursor:pointer; opacity:0.8; transition: opacity 0.2s; color: var(--text-muted); display: flex; align-items: center; gap: 4px; white-space: nowrap;" data-i18n="BTN_COPY_INVITE" data-i18n-title="BTN_COPY_INVITE_TOOLTIP">📋 Invite Link</button>
<button id="syncTabCopyInvite" title="Copy Invite Link" style="background:transparent; border: 1px solid var(--border-strong); border-radius: 6px; padding: 4px 8px; font-size: 11px; cursor:pointer; opacity:0.8; transition: opacity 0.2s; color: var(--text-muted); display: flex; align-items: center; gap: 4px; white-space: nowrap;" data-i18n="BTN_COPY_INVITE" data-i18n-title="BTN_COPY_INVITE_TOOLTIP">📋 Invite Link</button>
</div>
<div style="display: flex; gap: 8px; margin-bottom: 12px;">
<button id="playBtn" class="primary" style="flex:1; background: var(--success);" title="Send a Play command to everyone" data-i18n="BTN_PLAY" data-i18n-title="BTN_PLAY_TOOLTIP">▶ Play</button>
<button id="pauseBtn" class="primary" style="flex:1; background: var(--error);" title="Send a Pause command to everyone" data-i18n="BTN_PAUSE" data-i18n-title="BTN_PAUSE_TOOLTIP">⏸ Pause</button>
<button id="pauseBtn" class="primary" style="flex:1; background: var(--error); color: #fff;" title="Send a Pause command to everyone" data-i18n="BTN_PAUSE" data-i18n-title="BTN_PAUSE_TOOLTIP">⏸ Pause</button>
</div>
<div style="display: flex; gap: 8px; margin-bottom: 15px; align-items: stretch;">
<button id="forceSyncBtn" class="primary" style="background: linear-gradient(135deg, #6366f1, #a855f7); flex: 1;" title="Force all users to sync up" data-i18n="BTN_SYNC" data-i18n-title="BTN_SYNC_TOOLTIP">⚡ SYNC</button>
<select id="forceSyncMode" style="flex: 1; background: var(--card); border: 1px solid #334155; color: white; padding: 10px 8px; border-radius: 8px; font-size: 11px; font-family: inherit; cursor: pointer; align-self: stretch;" title="Choose sync target" data-i18n-title="OPTION_JUMP_MODE_TOOLTIP">
<button id="forceSyncBtn" class="primary" style="background: linear-gradient(135deg, var(--accent-green), var(--accent-terracotta)); color: var(--text-on-green); flex: 1;" title="Force all users to sync up" data-i18n="BTN_SYNC" data-i18n-title="BTN_SYNC_TOOLTIP">⚡ SYNC</button>
<select id="forceSyncMode" style="flex: 1; background: var(--card); border: 1px solid var(--border-strong); color: white; padding: 10px 8px; border-radius: 8px; font-size: 11px; font-family: inherit; cursor: pointer; align-self: stretch;" title="Choose sync target" data-i18n-title="OPTION_JUMP_MODE_TOOLTIP">
<option value="jump-to-others" data-i18n="OPTION_JUMP_TO_OTHERS">Jump to Others</option>
<option value="jump-to-me" data-i18n="OPTION_JUMP_TO_ME">Jump to Me</option>
</select>
@@ -540,18 +555,18 @@
<div style="font-size: 32px; margin-bottom: 12px;">🔒</div>
<h3 style="margin: 0 0 8px 0; color: var(--accent); font-size: 15px;" data-i18n="LOBBY_CONNECT_FIRST">Connect to a room first</h3>
<p style="color: var(--text-muted); font-size: 12px; margin-bottom: 20px;" data-i18n="LOBBY_CONNECT_FIRST_DESC">You need to join a room via an invite link or create a new one to sync videos.</p>
<button id="syncTabCreateRoomBtn" class="primary" style="padding: 12px; font-size: 14px; background: linear-gradient(135deg, #6366f1, #a855f7); box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);" title="Create a new random room and join it" data-i18n="BTN_CREATE_ROOM_ALT" data-i18n-title="BTN_CREATE_ROOM_ALT_TOOLTIP">Create New Room</button>
<button id="syncTabCreateRoomBtn" class="primary" style="padding: 12px; font-size: 14px; background: linear-gradient(135deg, var(--accent-green), var(--accent-terracotta)); color: var(--text-on-green); box-shadow: 0 4px 12px rgba(86, 174, 108, 0.4);" title="Create a new random room and join it" data-i18n="BTN_CREATE_ROOM_ALT" data-i18n-title="BTN_CREATE_ROOM_ALT_TOOLTIP">Create New Room</button>
</div>
</div>
<!-- Settings Tab -->
<div id="tab-settings" class="tab-content">
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #334155;">
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);">
<label style="margin-bottom: 0;" title="Username helps others identify you." data-i18n="LABEL_USERNAME" data-i18n-title="LABEL_USERNAME_TOOLTIP">Your Username</label>
<input type="text" id="username" data-i18n-placeholder="PLACEHOLDER_USERNAME" placeholder="Anonymous Koala" maxlength="20" style="width: 150px;">
</div>
<details class="form-group" style="background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #334155;">
<details class="form-group" style="background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);">
<summary style="cursor: pointer; font-size: 12px; font-weight: 700; color: var(--text);" title="Choose which titles are sent to the room." data-i18n="LABEL_PRIVACY_SETTINGS" data-i18n-title="LABEL_PRIVACY_SETTINGS_TOOLTIP">Privacy Settings</summary>
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px;">
<label style="margin-bottom: 0; cursor: help;" title="Send the selected browser tab title to the room." data-i18n="LABEL_SEND_TAB_TITLE" data-i18n-title="LABEL_SEND_TAB_TITLE_TOOLTIP">Send tab title</label>
@@ -562,7 +577,7 @@
</div>
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px;">
<label style="margin-bottom: 0; cursor: help;" title="Choose how media titles are sent to the room." data-i18n="LABEL_MEDIA_TITLE_PRIVACY" data-i18n-title="LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP">Media title</label>
<select id="mediaTitlePrivacyMode" style="width: 165px; background: var(--bg); border: 1px solid #334155; color: white; padding: 6px 10px; border-radius: 8px; font-size: 12px; cursor: pointer; outline: none; font-family: inherit;">
<select id="mediaTitlePrivacyMode" style="width: 165px; background: var(--bg); border: 1px solid var(--border-strong); color: white; padding: 6px 10px; border-radius: 8px; font-size: 12px; cursor: pointer; outline: none; font-family: inherit;">
<option value="full" data-i18n="OPTION_TITLE_PRIVACY_FULL">Send full title</option>
<option value="episode" data-i18n="OPTION_TITLE_PRIVACY_EPISODE">Send episode ID</option>
<option value="hidden" data-i18n="OPTION_TITLE_PRIVACY_HIDDEN">Do not send</option>
@@ -570,7 +585,7 @@
</div>
</details>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #334155;">
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);">
<label style="margin-bottom: 0; cursor: help;" title="Filters out non-video tabs and unrelated domains to keep the list clean" data-i18n="LABEL_HIDE_CLUTTER" data-i18n-title="LABEL_HIDE_CLUTTER_TOOLTIP">Hide Clutter Tabs</label>
<label class="toggle-switch">
<input type="checkbox" id="filterNoise" checked>
@@ -578,7 +593,7 @@
</label>
</div>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #334155;">
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);">
<label style="margin-bottom: 0; cursor: help;" title="Pauses automatically and waits for all peers when an episode changes, then sync-starts together." data-i18n="LABEL_AUTO_SYNC_NEXT" data-i18n-title="LABEL_AUTO_SYNC_NEXT_TOOLTIP">Auto-Sync Next Episode</label>
<label class="toggle-switch">
<input type="checkbox" id="autoSyncNextEpisode">
@@ -586,7 +601,7 @@
</label>
</div>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #334155;">
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);">
<label style="margin-bottom: 0; cursor: help;" title="Automatically copies the invite link to your clipboard when creating a new room." data-i18n="LABEL_AUTO_COPY_INVITE" data-i18n-title="LABEL_AUTO_COPY_INVITE_TOOLTIP">Auto-Copy Invite Link</label>
<label class="toggle-switch">
<input type="checkbox" id="autoCopyInvite" checked>
@@ -594,7 +609,7 @@
</label>
</div>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #334155;">
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);">
<label style="margin-bottom: 0; cursor: help;" title="Shows native system notifications when someone joins/leaves or plays/pauses." data-i18n="LABEL_NOTIFICATIONS" data-i18n-title="LABEL_NOTIFICATIONS_TOOLTIP">Browser Notifications</label>
<label class="toggle-switch">
<input type="checkbox" id="browserNotifications">
@@ -602,16 +617,16 @@
</label>
</div>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px 12px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #334155;">
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px 12px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);">
<div style="display: flex; align-items: center; gap: 8px;">
<label id="audioProcessingLabel" style="margin-bottom: 0; cursor: help; white-space: nowrap; font-size: 13px;" data-i18n="LABEL_AUDIO_PROCESSING" data-i18n-title="LABEL_AUDIO_PROCESSING_TOOLTIP">Audio Processing</label>
</div>
<a id="audioSettingsLink" href="#" style="font-size: 11px; color: var(--accent); text-decoration: none; white-space: nowrap; background: rgba(99,102,241,0.12); padding: 5px 12px; border-radius: 6px; font-weight: 600; transition: background 0.2s;" data-i18n="AUDIO_OPEN_SETTINGS">Open</a>
<a id="audioSettingsLink" href="#" style="font-size: 11px; color: var(--accent); text-decoration: none; white-space: nowrap; background: rgba(86, 174, 108, 0.12); padding: 5px 12px; border-radius: 6px; font-weight: 600; transition: background 0.2s;" data-i18n="AUDIO_OPEN_SETTINGS">Open</a>
</div>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid #334155;">
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between; background: var(--card); padding: 10px; border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--border-strong);">
<label style="margin-bottom: 0;" data-i18n="LABEL_LANGUAGE" data-i18n-title="LABEL_LANGUAGE_TOOLTIP" title="Choose your preferred extension language">App Language</label>
<select id="langSelector" style="width: 165px; background: var(--bg); border: 1px solid #334155; color: white; padding: 6px 10px; border-radius: 8px; font-size: 13px; cursor: pointer; outline: none; font-family: inherit;">
<select id="langSelector" style="width: 165px; background: var(--bg); border: 1px solid var(--border-strong); color: white; padding: 6px 10px; border-radius: 8px; font-size: 13px; cursor: pointer; outline: none; font-family: inherit;">
<option value="en">🇬🇧 English</option>
<option value="de">🇩🇪 Deutsch</option>
<option value="fr">🇫🇷 Français</option>
@@ -710,10 +725,10 @@
<!-- Onboarding Overlay -->
<div id="onboarding-overlay" style="display:none; position:fixed; inset:0; background:transparent; z-index:1000; pointer-events:auto;">
<!-- Spotlight element -->
<div id="onboarding-spotlight" style="position:fixed; pointer-events:none; border:2px solid var(--accent); box-shadow:0 0 0 9999px rgba(15, 23, 42, 0.65); border-radius:8px; transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1); display:none; opacity:0; z-index:1001; animation: spotlightPulse 2s infinite ease-in-out;"></div>
<div id="onboarding-spotlight" style="position:fixed; pointer-events:none; border:2px solid var(--accent); box-shadow:0 0 0 9999px rgba(16, 25, 14, 0.65); border-radius:8px; transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1); display:none; opacity:0; z-index:1001; animation: spotlightPulse 2s infinite ease-in-out;"></div>
<!-- Onboarding Card -->
<div id="onboarding-card" style="position:fixed; background:var(--card); padding:16px 20px; border-radius:12px; width:280px; box-shadow:0 8px 32px rgba(0,0,0,0.5); z-index:1002; transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1); box-sizing:border-box; border:1px solid #334155; opacity:0; transform: scale(0.9); display:none;">
<div id="onboarding-card" style="position:fixed; background:var(--card); padding:16px 20px; border-radius:12px; width:280px; box-shadow:0 8px 32px rgba(0,0,0,0.5); z-index:1002; transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1); box-sizing:border-box; border:1px solid var(--border-strong); opacity:0; transform: scale(0.9); display:none;">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;">
<span id="onboarding-icon" style="font-size:24px;">👋</span>
<span id="onboarding-step-indicator" style="font-size:10px; font-weight:700; color:var(--accent); text-transform:uppercase; letter-spacing:0.5px;">Step 1 of 5</span>
@@ -722,7 +737,7 @@
<p id="onboarding-text" style="color:var(--text-muted); font-size:12px; margin:0 0 16px; line-height:1.4; text-align:left;">Let's get you started.</p>
<!-- Progress Bar -->
<div id="onboarding-progress-container" style="height:4px; background:#334155; border-radius:2px; margin-bottom:16px; overflow:hidden;">
<div id="onboarding-progress-container" style="height:4px; background: var(--border-strong); border-radius:2px; margin-bottom:16px; overflow:hidden;">
<div id="onboarding-progress-bar" style="height:100%; width:20%; background:var(--accent); transition:width 0.3s ease;"></div>
</div>
+13 -13
View File
@@ -529,7 +529,7 @@ function updateLastActionUI(state, peers) {
if (pId === localPeerId) return;
const pName = (typeof peer === 'object' && peer.username) ? peer.username : pId.substring(0, 4);
const isAcked = safeAcks.includes(pId) || pId === state.senderId;
const color = isAcked ? 'var(--success)' : '#475569';
const color = isAcked ? 'var(--success)' : '#4c5142';
const icon = isAcked ? '✓' : '...';
const avatar = getAvatarForName(pName);
@@ -1024,7 +1024,7 @@ function applyConnectionStatus(status) {
elements.connDot.style.background = '#9ca3af';
elements.connDot.style.boxShadow = 'none';
} else if (!connected) {
elements.connDot.style.background = '#ef4444';
elements.connDot.style.background = '#b94642';
elements.connDot.style.boxShadow = 'none';
} else {
elements.connDot.style.background = '';
@@ -1071,11 +1071,11 @@ function updatePingDisplay(pingMs) {
}
elements.connPing.textContent = `${Math.round(pingMs)}ms`;
if (pingMs < 50) {
elements.connPing.style.color = '#22c55e';
elements.connPing.style.color = '#56ae6c';
} else if (pingMs < 150) {
elements.connPing.style.color = '#f59e0b';
} else {
elements.connPing.style.color = '#ef4444';
elements.connPing.style.color = '#b94642';
}
}
@@ -1096,7 +1096,7 @@ function updateHistory(history) {
entry.style.cssText = 'margin-bottom: 4px; border-bottom: 1px solid rgba(255,255,255,0.05); padding-bottom: 2px;';
const timeSpan = document.createElement('span');
timeSpan.style.color = '#64748b';
timeSpan.style.color = '#6b705e';
timeSpan.textContent = `[${time}] `;
const actionBold = document.createElement('b');
@@ -2259,7 +2259,7 @@ function refreshDebugInfo() {
const addSection = (title) => {
const div = document.createElement('div');
div.style.cssText = 'margin: 8px 0 4px 0; border-bottom: 1px solid #334155; padding-bottom: 2px; color: var(--accent); font-weight: bold; font-size: 10px; text-transform: uppercase;';
div.style.cssText = 'margin: 8px 0 4px 0; border-bottom: 1px solid #2f3625; padding-bottom: 2px; color: var(--accent); font-weight: bold; font-size: 10px; text-transform: uppercase;';
div.textContent = title;
elements.videoDebug.appendChild(div);
};
@@ -2268,7 +2268,7 @@ function refreshDebugInfo() {
// No video found — show diagnostic info
addSection('Video Detection');
const notFound = document.createElement('div');
notFound.style.cssText = 'color: #ef4444; font-weight: 700; margin-bottom: 8px;';
notFound.style.cssText = 'color: #b94642; font-weight: 700; margin-bottom: 8px;';
notFound.textContent = 'NO VIDEO ELEMENT FOUND';
elements.videoDebug.appendChild(notFound);
@@ -2294,12 +2294,12 @@ function refreshDebugInfo() {
// Video found — full debug
addSection('Playback');
addField('State', state.paused ? 'PAUSED' : 'PLAYING', state.paused ? 'var(--text-muted)' : '#22c55e');
addField('State', state.paused ? 'PAUSED' : 'PLAYING', state.paused ? 'var(--text-muted)' : '#56ae6c');
addField('Time', `${state.currentTime.toFixed(2)}s / ${(state.duration || 0).toFixed(2)}s`);
addField('ReadyState', `${state.readyState} (${state.readyStateLabel || '?'})`,
state.readyState >= 3 ? '#22c55e' : '#fbbf24');
state.readyState >= 3 ? '#56ae6c' : '#fbbf24');
addField('Network', `${state.networkState} (${state.networkStateLabel || '?'})`,
state.networkState === 1 ? '#22c55e' : state.networkState === 3 ? '#ef4444' : 'var(--text-muted)');
state.networkState === 1 ? '#56ae6c' : state.networkState === 3 ? '#b94642' : 'var(--text-muted)');
addField('Buffered', state.buffered || '?');
if (state.nativeCurrentTime != null || state.nativeDuration != null) {
addField('Native Time', `${state.nativeCurrentTime ?? '?'}s / ${state.nativeDuration ?? '?'}s`);
@@ -2323,7 +2323,7 @@ function refreshDebugInfo() {
addSection('Dimensions');
const dimsOk = state.videoWidth > 0 && state.videoHeight > 0;
addField('Resolution', `${state.videoWidth}x${state.videoHeight}`, dimsOk ? '#22c55e' : '#ef4444');
addField('Resolution', `${state.videoWidth}x${state.videoHeight}`, dimsOk ? '#56ae6c' : '#b94642');
if (!dimsOk) {
const dimHint = document.createElement('div');
dimHint.style.cssText = 'color: #fbbf24; font-size: 9px; margin: 2px 0 6px 12px;';
@@ -2333,8 +2333,8 @@ function refreshDebugInfo() {
if (state.error) {
addSection('Media Error');
addField('Code', String(state.error.code), '#ef4444');
addField('Message', state.error.message || '?', '#ef4444');
addField('Code', String(state.error.code), '#b94642');
addField('Message', state.error.message || '?', '#b94642');
}
addSection('Detection');