mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-07-26 12:08:15 +00:00
1024 lines
54 KiB
HTML
1024 lines
54 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>KoalaSync</title>
|
|
<script src="theme-init.js"></script>
|
|
<style>
|
|
@font-face {
|
|
font-family: 'Twemoji Country Flags';
|
|
unicode-range: U+1F1E6-1F1FF, U+1F3F4, U+E0062-E0063, U+E0065, U+E0067, U+E006C, U+E006E, U+E0073-E0074, U+E0077, U+E007F;
|
|
src: url('assets/TwemojiCountryFlags.woff2') format('woff2');
|
|
font-display: swap;
|
|
}
|
|
|
|
:root {
|
|
/* 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);
|
|
--text-on-warm: oklch(0.16 0.025 45);
|
|
|
|
--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: oklch(0.68 0.14 45);
|
|
color-scheme: dark;
|
|
}
|
|
|
|
html.theme-light {
|
|
--bg-base: oklch(0.965 0.012 105);
|
|
--surface: oklch(0.995 0.006 100);
|
|
--surface-deep: oklch(0.935 0.018 110);
|
|
--surface-alt: oklch(0.91 0.022 115);
|
|
--border-soft: oklch(0.28 0.035 140 / 0.10);
|
|
--border-strong: oklch(0.73 0.035 125);
|
|
--text-primary: oklch(0.21 0.03 140);
|
|
--text-secondary: oklch(0.43 0.03 135);
|
|
--accent-green: oklch(0.56 0.13 150);
|
|
--accent-green-hover: oklch(0.49 0.13 150);
|
|
--accent-terracotta: oklch(0.64 0.14 45);
|
|
--danger: oklch(0.57 0.17 25);
|
|
--text-on-green: oklch(0.16 0.03 140);
|
|
--text-on-warm: oklch(0.18 0.03 45);
|
|
color-scheme: light;
|
|
}
|
|
|
|
body {
|
|
width: 360px;
|
|
margin: 0;
|
|
padding: 18px;
|
|
box-sizing: border-box;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
font-family: 'Twemoji Country Flags', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
font-size: 14px;
|
|
background-image:
|
|
radial-gradient(circle at 12% -8%, oklch(0.68 0.13 150 / 0.12), transparent 42%),
|
|
radial-gradient(circle at 105% 12%, oklch(0.62 0.14 45 / 0.08), transparent 34%);
|
|
transition: background-color 0.25s ease, color 0.25s ease;
|
|
}
|
|
|
|
html.theme-light body {
|
|
background-image:
|
|
radial-gradient(circle at 8% -5%, oklch(0.72 0.11 145 / 0.24), transparent 40%),
|
|
radial-gradient(circle at 105% 5%, oklch(0.82 0.12 65 / 0.2), transparent 38%);
|
|
}
|
|
|
|
.header-row {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 14px;
|
|
padding: 2px 2px 0;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 18px;
|
|
margin: 0;
|
|
color: var(--accent);
|
|
letter-spacing: 1px;
|
|
text-transform: uppercase;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
h1 img {
|
|
width: 42px;
|
|
height: 42px;
|
|
object-fit: contain;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.popup-version {
|
|
margin-left: auto;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
font-size: 10px;
|
|
color: var(--text-muted);
|
|
text-decoration: none;
|
|
opacity: 0.72;
|
|
transition: opacity 0.2s, color 0.2s;
|
|
}
|
|
.popup-version:hover {
|
|
opacity: 1;
|
|
color: var(--accent);
|
|
}
|
|
.popup-version svg {
|
|
width: 12px;
|
|
height: 12px;
|
|
display: block;
|
|
}
|
|
|
|
/* Tabs */
|
|
.tabs {
|
|
display: flex;
|
|
gap: 4px;
|
|
background: var(--card);
|
|
padding: 4px;
|
|
border-radius: var(--radius);
|
|
margin-bottom: 16px;
|
|
border: 1px solid var(--border-soft);
|
|
}
|
|
|
|
.tab-btn {
|
|
flex: 1;
|
|
padding: 8px;
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
border-radius: 8px;
|
|
min-height: 36px;
|
|
transition: background-color 0.2s, color 0.2s, box-shadow 0.2s;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.tab-btn.active {
|
|
background: var(--accent);
|
|
color: var(--text-on-green);
|
|
box-shadow: 0 5px 14px oklch(0.38 0.10 150 / 0.22);
|
|
}
|
|
|
|
.tab-content {
|
|
display: none;
|
|
}
|
|
|
|
.tab-content.active {
|
|
display: block;
|
|
animation: fadeIn 0.3s ease-out;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(4px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
/* Forms */
|
|
.form-group {
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
font-size: 11px;
|
|
text-transform: uppercase;
|
|
color: var(--text-muted);
|
|
margin-bottom: 4px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
input, select, option {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
background: var(--card);
|
|
border: 1px solid var(--border-strong);
|
|
color: var(--text);
|
|
padding: 10px;
|
|
border-radius: 8px;
|
|
outline: none;
|
|
font-family: inherit;
|
|
min-height: 40px;
|
|
transition: border-color 0.2s, box-shadow 0.2s, background-color 0.2s;
|
|
}
|
|
|
|
@supports (appearance: base-select) {
|
|
#langSelector,
|
|
#langSelector::picker(select) {
|
|
appearance: base-select;
|
|
}
|
|
#langSelector::picker(select) {
|
|
background-color: var(--bg);
|
|
border: 1px solid var(--border-strong);
|
|
border-radius: 8px;
|
|
padding: 4px;
|
|
font-family: 'Twemoji Country Flags', inherit;
|
|
}
|
|
}
|
|
|
|
input:focus,
|
|
select:focus {
|
|
border-color: var(--accent);
|
|
box-shadow: 0 0 0 3px oklch(0.68 0.13 150 / 0.18);
|
|
}
|
|
|
|
button.primary {
|
|
width: 100%;
|
|
padding: 12px;
|
|
background: var(--accent);
|
|
border: none;
|
|
color: var(--text-on-green);
|
|
font-weight: 700;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s, box-shadow 0.2s, transform 0.2s;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
button.primary:hover {
|
|
background: var(--accent-hover);
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
button.secondary {
|
|
width: 100%;
|
|
padding: 10px;
|
|
background: var(--border-strong);
|
|
border: none;
|
|
color: var(--text);
|
|
font-weight: 600;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
button.secondary:hover {
|
|
background: oklch(0.38 0.035 125);
|
|
}
|
|
|
|
details > summary:focus-visible {
|
|
outline: 2px solid var(--accent-hover) !important;
|
|
outline-offset: 3px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
button:focus-visible,
|
|
a:focus-visible,
|
|
input:focus-visible,
|
|
select:focus-visible {
|
|
outline: 2px solid var(--accent-hover);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
button:disabled {
|
|
cursor: not-allowed;
|
|
opacity: 0.48;
|
|
transform: none !important;
|
|
}
|
|
|
|
.copy-success {
|
|
background: var(--success) !important;
|
|
border-color: var(--success) !important;
|
|
color: var(--text-on-green) !important;
|
|
opacity: 1 !important;
|
|
}
|
|
|
|
/* Info Cards */
|
|
.info-card {
|
|
background: var(--card);
|
|
padding: 12px;
|
|
border-radius: 10px;
|
|
margin-bottom: 12px;
|
|
border: 1px solid var(--border-strong);
|
|
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.peer-item {
|
|
padding: 10px 0;
|
|
border-bottom: 1px solid var(--border-strong);
|
|
}
|
|
|
|
.peer-item:last-child { border: 0; }
|
|
|
|
.status-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
margin-right: 6px;
|
|
}
|
|
|
|
.status-online { background: var(--success); box-shadow: 0 0 8px var(--success); }
|
|
.status-offline { background: var(--error); }
|
|
|
|
/* Logs */
|
|
#logList {
|
|
max-height: 200px;
|
|
overflow-y: auto;
|
|
font-size: 10px;
|
|
font-family: monospace;
|
|
background: #000;
|
|
padding: 8px;
|
|
border-radius: 8px;
|
|
color: #ccc;
|
|
}
|
|
|
|
.log-entry { margin-bottom: 4px; }
|
|
.log-error { color: var(--error); }
|
|
.log-success { color: var(--success); }
|
|
|
|
/* Invite Link */
|
|
.invite-box {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin-top: 8px;
|
|
}
|
|
.invite-box input { flex: 1; font-size: 11px; }
|
|
.invite-box button { width: 40px; padding: 0; }
|
|
|
|
/* Toast Notifications */
|
|
#toast-container {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 9999;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
pointer-events: none;
|
|
}
|
|
.toast {
|
|
pointer-events: auto;
|
|
padding: 10px 16px;
|
|
margin-bottom: 6px;
|
|
border-radius: 8px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
max-width: 280px;
|
|
text-align: center;
|
|
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: var(--text-on-green); }
|
|
.toast-error { background: var(--error); color: white; }
|
|
.toast-info { background: var(--accent); color: var(--text-on-green); }
|
|
.toast-warning { background: var(--accent-terracotta); color: var(--text-on-warm); }
|
|
|
|
@keyframes toastSlideIn {
|
|
from { opacity: 0; transform: translateY(-20px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
@keyframes toastFadeOut {
|
|
from { opacity: 1; }
|
|
to { opacity: 0; transform: translateY(-10px); }
|
|
}
|
|
|
|
/* Eucalyptus confetti: tiny leaves bursting from the copy-invite
|
|
button (spawned by popup.js, mirrors the website's effect). The
|
|
outer span owns the flight path — fast burst that brakes at its
|
|
peak, then sinks — while the inner leaf rocks around its stem. */
|
|
.click-leaf {
|
|
position: fixed;
|
|
z-index: 3000;
|
|
margin: -5px 0 0 -4px;
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
animation: leafBurst var(--leaf-dur, 1s) forwards;
|
|
will-change: transform, opacity;
|
|
}
|
|
.click-leaf i {
|
|
display: block;
|
|
width: 7px;
|
|
height: 10px;
|
|
border-radius: 0 70% 0 70%;
|
|
background: linear-gradient(135deg, oklch(0.68 0.13 150 / 0.95), oklch(0.5 0.1 145 / 0.85));
|
|
transform: rotate(var(--leaf-rot, 0deg));
|
|
animation: leafRock var(--flutter-dur, 0.55s) ease-in-out infinite alternate;
|
|
}
|
|
.click-leaf-sage i {
|
|
background: linear-gradient(135deg, oklch(0.75 0.09 140 / 0.9), oklch(0.58 0.08 145 / 0.8));
|
|
}
|
|
.click-leaf-amber i {
|
|
background: linear-gradient(135deg, oklch(0.68 0.14 55 / 0.95), oklch(0.55 0.13 40 / 0.85));
|
|
}
|
|
@keyframes leafBurst {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translate(0, 0) scale(var(--leaf-scale, 1));
|
|
animation-timing-function: cubic-bezier(0.16, 0.84, 0.44, 1);
|
|
}
|
|
10% { opacity: 1; }
|
|
45% {
|
|
opacity: 1;
|
|
transform: translate(var(--leaf-x, 0), var(--leaf-y, -14px)) scale(var(--leaf-scale, 1));
|
|
animation-timing-function: cubic-bezier(0.55, 0.06, 0.68, 0.19);
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
transform: translate(calc(var(--leaf-x, 0) * 1.4), calc(var(--leaf-y, -14px) + 30px)) scale(calc(var(--leaf-scale, 1) * 0.92));
|
|
}
|
|
}
|
|
@keyframes leafRock {
|
|
from { transform: rotate(calc(var(--leaf-rot, 0deg) - 24deg)); }
|
|
to { transform: rotate(calc(var(--leaf-rot, 0deg) + 24deg)); }
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.click-leaf { display: none; }
|
|
}
|
|
|
|
@keyframes fadeSlideIn {
|
|
from { opacity: 0; transform: translateX(5px); }
|
|
to { opacity: 1; transform: translateX(0); }
|
|
}
|
|
.tab-active-animate {
|
|
animation: fadeSlideIn 0.15s ease-out forwards;
|
|
}
|
|
|
|
/* Toggle Switch CSS */
|
|
.toggle-switch {
|
|
position: relative;
|
|
display: inline-block;
|
|
width: 36px;
|
|
height: 20px;
|
|
}
|
|
.toggle-switch input {
|
|
opacity: 0;
|
|
width: 0;
|
|
height: 0;
|
|
}
|
|
.slider {
|
|
position: absolute;
|
|
cursor: pointer;
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
background-color: var(--border-strong);
|
|
transition: .3s;
|
|
border-radius: 20px;
|
|
}
|
|
.slider:before {
|
|
position: absolute;
|
|
content: "";
|
|
height: 14px;
|
|
width: 14px;
|
|
left: 3px;
|
|
bottom: 3px;
|
|
background-color: #a3a294;
|
|
transition: .3s;
|
|
border-radius: 50%;
|
|
}
|
|
input:checked + .slider {
|
|
background-color: var(--accent);
|
|
}
|
|
input:checked + .slider:before {
|
|
transform: translateX(16px);
|
|
background-color: white;
|
|
}
|
|
.popup-footer {
|
|
margin-top: 20px;
|
|
text-align: center;
|
|
border-top: 1px solid rgba(255,255,255,0.05);
|
|
padding-top: 10px;
|
|
color: var(--text-muted);
|
|
font-size: 10px;
|
|
opacity: 0.78;
|
|
}
|
|
|
|
/* Unified interaction layer: the popup is a compact control surface,
|
|
so every actionable target gets the same tactile and keyboard cues. */
|
|
button,
|
|
select,
|
|
summary,
|
|
a {
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
button:not(:disabled):active,
|
|
.tab-btn:not(:disabled):active {
|
|
transform: translateY(1px) scale(0.985);
|
|
}
|
|
details.form-group {
|
|
background: var(--card);
|
|
border: 1px solid var(--border-strong);
|
|
border-radius: var(--radius);
|
|
margin-bottom: 12px;
|
|
transition: border-color 0.2s, background-color 0.2s;
|
|
overflow: hidden;
|
|
}
|
|
details.form-group:hover {
|
|
border-color: var(--accent);
|
|
}
|
|
details.form-group > summary {
|
|
min-height: 38px;
|
|
padding: 10px 14px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
cursor: pointer;
|
|
font-weight: 700;
|
|
font-size: 13px;
|
|
color: var(--text-primary);
|
|
user-select: none;
|
|
list-style: none;
|
|
}
|
|
details.form-group > summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
details.form-group > summary::after {
|
|
content: "";
|
|
display: inline-block;
|
|
width: 6px;
|
|
height: 6px;
|
|
border-right: 2px solid var(--text-muted);
|
|
border-bottom: 2px solid var(--text-muted);
|
|
transform: rotate(-45deg);
|
|
transition: transform 0.2s ease;
|
|
margin-right: 4px;
|
|
}
|
|
details[open].form-group > summary::after {
|
|
transform: rotate(45deg);
|
|
}
|
|
details.form-group > .details-content {
|
|
padding: 4px 14px 14px 14px;
|
|
border-top: 1px solid var(--border-soft);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
#section-join > .info-card,
|
|
#section-active > .info-card,
|
|
#tab-sync .info-card {
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
#section-active > .info-card:first-child::before,
|
|
#connStatus::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0 auto 0 0;
|
|
width: 3px;
|
|
background: var(--accent);
|
|
}
|
|
#playBtn,
|
|
#pauseBtn,
|
|
#forceSyncBtn {
|
|
min-height: 42px;
|
|
}
|
|
#leaveBtn {
|
|
background: transparent;
|
|
border: 1px solid color-mix(in oklch, var(--error), transparent 35%);
|
|
}
|
|
#leaveBtn:hover {
|
|
background: color-mix(in oklch, var(--error), transparent 88%);
|
|
}
|
|
.popup-footer a {
|
|
color: var(--text-muted);
|
|
text-decoration: none;
|
|
transition: color 0.2s, opacity 0.2s;
|
|
}
|
|
.popup-footer a:hover {
|
|
color: var(--accent);
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Light theme polish: preserve the same depth hierarchy without
|
|
carrying dark-only inline colors into form controls and cards. */
|
|
html.theme-light .tabs,
|
|
html.theme-light .info-card,
|
|
html.theme-light details.form-group,
|
|
html.theme-light .form-group {
|
|
box-shadow: 0 8px 24px oklch(0.22 0.035 140 / 0.07);
|
|
}
|
|
html.theme-light input,
|
|
html.theme-light select,
|
|
html.theme-light option {
|
|
color: var(--text) !important;
|
|
background-color: var(--surface) !important;
|
|
}
|
|
html.theme-light button.secondary {
|
|
background: var(--surface-alt);
|
|
color: var(--text);
|
|
border: 1px solid var(--border-strong);
|
|
}
|
|
html.theme-light button.secondary:hover {
|
|
background: oklch(0.86 0.035 125);
|
|
}
|
|
html.theme-light .mock-card,
|
|
html.theme-light .peer-item {
|
|
border-color: var(--border-strong);
|
|
}
|
|
html.theme-light .slider:before {
|
|
background-color: oklch(0.62 0.025 125);
|
|
}
|
|
html.theme-light input:checked + .slider:before {
|
|
background-color: white;
|
|
}
|
|
html.theme-light .popup-footer {
|
|
border-top-color: var(--border-soft);
|
|
}
|
|
html.theme-light .tab-btn.active {
|
|
color: #fff;
|
|
}
|
|
.feature-hint {
|
|
display: inline-block;
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
background: var(--accent);
|
|
margin-left: 4px;
|
|
animation: featurePulse 2s ease-in-out infinite;
|
|
cursor: help;
|
|
vertical-align: middle;
|
|
}
|
|
@keyframes featurePulse {
|
|
0%, 100% { opacity: 0.4; transform: scale(1); }
|
|
50% { opacity: 1; transform: scale(1.3); }
|
|
}
|
|
@keyframes spotlightPulse {
|
|
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); }
|
|
50% { transform: translateY(-4px); }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="toast-container"></div>
|
|
<main>
|
|
<div class="header-row">
|
|
<h1 data-i18n="APP_TITLE"><img src="icons/icon128.png" alt="KoalaSync Logo">KoalaSync</h1>
|
|
<a href="https://github.com/Shik3i/KoalaSync" target="_blank" class="popup-version">
|
|
<svg viewBox="0 0 16 16" fill="currentColor">
|
|
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
|
|
</svg>
|
|
<span id="popupVersion">v0.0.0</span>
|
|
</a>
|
|
</div>
|
|
|
|
<div class="tabs">
|
|
<button class="tab-btn active" data-tab="tab-room" data-i18n="TAB_ROOM" data-i18n-title="TAB_ROOM_TOOLTIP" title="Room settings and connection">Room</button>
|
|
<button class="tab-btn" data-tab="tab-sync" data-i18n="TAB_SYNC" data-i18n-title="TAB_SYNC_TOOLTIP" title="Video sync controls and remote actions">Sync</button>
|
|
<button class="tab-btn" data-tab="tab-settings" data-i18n="TAB_SETTINGS" data-i18n-title="TAB_SETTINGS_TOOLTIP" title="Extension preferences">Settings</button>
|
|
<button class="tab-btn" data-tab="tab-dev" data-i18n="TAB_STATUS" data-i18n-title="TAB_STATUS_TOOLTIP" title="Advanced Diagnostics & Logs">Status</button>
|
|
<button id="devToolsTabBtn" class="tab-btn" data-tab="tab-devtools" style="display:none;">Dev</button>
|
|
</div>
|
|
|
|
<!-- Room Tab -->
|
|
<div id="tab-room" class="tab-content active">
|
|
|
|
<!-- 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, 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 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;">
|
|
<button id="serverOfficial" class="tab-btn active" style="flex:1; padding:6px; font-size:11px;" data-i18n="BTN_SERVER_OFFICIAL" data-i18n-title="BTN_SERVER_OFFICIAL_TOOLTIP" title="Use the official reliable server">Official</button>
|
|
<button id="serverCustom" class="tab-btn" style="flex:1; padding:6px; font-size:11px;" data-i18n="BTN_SERVER_CUSTOM" data-i18n-title="BTN_SERVER_CUSTOM_TOOLTIP" title="Connect to your own self-hosted server">Custom</button>
|
|
</div>
|
|
<input type="text" id="serverUrl" data-i18n-placeholder="PLACEHOLDER_SERVER_URL" placeholder="wss://your-server:3000" style="display:none; margin-top:8px;">
|
|
</div>
|
|
</div>
|
|
<!-- Room credentials — distinct group below the server picker. -->
|
|
<div class="form-group">
|
|
<label for="roomId" title="The unique identifier for your sync room" data-i18n="LABEL_ROOM_ID" data-i18n-title="LABEL_ROOM_ID_TOOLTIP">Room ID</label>
|
|
<input type="text" id="roomId" data-i18n-placeholder="PLACEHOLDER_ROOM_ID" data-i18n-title="PLACEHOLDER_ROOM_ID_TOOLTIP" placeholder="Enter Room ID" title="The unique ID of the room you want to join">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="password" title="Optional password to restrict room access" data-i18n="LABEL_PASSWORD" data-i18n-title="LABEL_PASSWORD_TOOLTIP">Password (Optional)</label>
|
|
<input type="password" id="password" data-i18n-placeholder="PLACEHOLDER_PASSWORD" data-i18n-title="PLACEHOLDER_PASSWORD_TOOLTIP" placeholder="Room Password (optional)" title="Password for the room (leave empty if none)">
|
|
</div>
|
|
<div id="roomError" style="display:none; color:var(--error); font-size:11px; margin-bottom:8px; text-align:center;"></div>
|
|
<button id="joinBtn" class="primary" data-i18n="BTN_JOIN_ROOM" data-i18n-title="BTN_JOIN_ROOM_TOOLTIP" title="Connect to the room">Join Room</button>
|
|
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 1.5rem; margin-bottom: 8px;">
|
|
<label style="margin:0;" title="List of publicly available rooms on this server" data-i18n="LABEL_PUBLIC_ROOMS" data-i18n-title="LABEL_PUBLIC_ROOMS_TOOLTIP">Public Rooms</label>
|
|
<button id="refreshRooms" class="secondary" style="background:transparent; border:none; color:var(--accent); font-size:10px; cursor:pointer;" data-i18n="BTN_REFRESH" data-i18n-title="BTN_REFRESH_TOOLTIP" title="Refresh the list of public rooms">REFRESH</button>
|
|
</div>
|
|
<div id="publicRooms" class="info-card" style="max-height: 120px; overflow-y: auto; padding: 4px;">
|
|
<div style="text-align:center; color: var(--text-muted); font-size: 11px; padding: 10px;" data-i18n="PUBLIC_ROOMS_REFRESHING">Refreshing...</div>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
|
|
<!-- ACTIVE SECTION: Visible when in a room -->
|
|
<div id="section-active" style="display:none;">
|
|
<div class="info-card" style="margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center; border-left: 4px solid var(--accent);">
|
|
<div>
|
|
<label style="margin-bottom: 0;" title="The room you are currently connected to" data-i18n="LABEL_ACTIVE_ROOM" data-i18n-title="LABEL_ACTIVE_ROOM_TOOLTIP">Active Room</label>
|
|
<div id="activeRoomId" style="font-weight: 700; color: var(--accent); font-size: 16px; letter-spacing: 1px;">NONE</div>
|
|
</div>
|
|
<div id="activeServer" style="font-size: 10px; color: var(--text-muted); text-align: right; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">Official Server</div>
|
|
</div>
|
|
|
|
<div class="info-card" style="margin-bottom: 20px;">
|
|
<label title="Share this link with friends so they can join" data-i18n="LABEL_INVITE_LINK" data-i18n-title="LABEL_INVITE_LINK_TOOLTIP">Invite Link</label>
|
|
<div class="invite-box">
|
|
<input type="text" id="inviteLink" readonly>
|
|
<button id="copyInvite" class="secondary" title="Copy invite link" aria-label="Copy invite link">📋</button>
|
|
</div>
|
|
</div>
|
|
|
|
<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:var(--text-on-green);"></span>
|
|
</div>
|
|
<div id="hostControlToggleRow" style="display:none; align-items:center; justify-content:space-between; gap:12px; margin-top:12px;">
|
|
<label for="hostControlToggle" style="font-size:12px; color:var(--text); line-height:1.3; margin:0; text-transform:none; font-weight:400;" data-i18n="LABEL_HOST_ONLY_TOGGLE">Only I can control playback</label>
|
|
<label class="toggle-switch" style="flex:none;">
|
|
<input type="checkbox" id="hostControlToggle">
|
|
<span class="slider"></span>
|
|
</label>
|
|
</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>
|
|
|
|
<div style="margin-bottom: 20px;">
|
|
<label title="Other users currently connected to this room" data-i18n="LABEL_PEERS_IN_ROOM" data-i18n-title="LABEL_PEERS_IN_ROOM_TOOLTIP">Peers in Room</label>
|
|
<div id="peerList" class="info-card">
|
|
<div style="text-align:center; color: var(--text-muted); font-size: 12px;" data-i18n="NO_PEERS_CONNECTED">No peers connected</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button id="leaveBtn" class="secondary" style="color: var(--error); border-color: var(--error);" data-i18n="BTN_LEAVE_ROOM">Leave Room</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sync Tab -->
|
|
<div id="tab-sync" class="tab-content">
|
|
<!-- SYNC ACTIVE: Visible when in a room -->
|
|
<div id="sync-active">
|
|
<div class="form-group" style="position: relative;">
|
|
<label for="targetTab" title="Choose the browser tab containing the video to sync" data-i18n="LABEL_SELECT_VIDEO" data-i18n-title="LABEL_SELECT_VIDEO_TOOLTIP">Select Video</label>
|
|
<select id="targetTab">
|
|
<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: 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>
|
|
</div>
|
|
|
|
<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 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); 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, 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: var(--text); 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>
|
|
</div>
|
|
|
|
<!-- NEW: Last Action Status Card -->
|
|
<label title="Shows the most recent play, pause, or seek command" data-i18n="LABEL_LAST_ACTIVITY" data-i18n-title="LABEL_LAST_ACTIVITY_TOOLTIP">Last Activity Status</label>
|
|
<div id="lastActionCard" class="info-card" style="margin-bottom: 15px; max-height: 120px; overflow-y: auto;">
|
|
<div style="text-align:center; color: var(--text-muted); font-size: 10px;" data-i18n="NO_RECENT_COMMANDS">No recent commands</div>
|
|
</div>
|
|
|
|
<!-- Episode Auto-Sync Lobby Status -->
|
|
<div id="episodeLobbyCard" class="info-card" style="display:none; margin-bottom: 15px; border-left: 4px solid var(--star); animation: fadeIn 0.3s ease-out;">
|
|
<div style="display:flex; align-items:center; gap: 6px; margin-bottom: 6px;">
|
|
<span style="font-size: 16px;">⏳</span>
|
|
<span style="font-weight: 700; color: var(--star); font-size: 12px;" data-i18n="LOBBY_HEADER">EPISODE LOBBY</span>
|
|
</div>
|
|
<div id="lobbyTitle" style="font-size: 11px; color: var(--text); margin-bottom: 6px; font-weight: 600;"></div>
|
|
<div id="lobbyPeerStatus" style="font-size: 10px; color: var(--text-muted); margin-bottom: 8px;"></div>
|
|
<button id="cancelLobbyBtn" class="secondary" style="margin-top: 4px; padding: 6px 10px; font-size: 11px; width: auto; display: block;" title="Cancel lobby and play anyway" data-i18n="BTN_SKIP_PLAY" data-i18n-title="BTN_SKIP_PLAY_TOOLTIP">Skip & Play anyway</button>
|
|
</div>
|
|
|
|
<div id="peerListSync" class="info-card" style="display:none;"></div>
|
|
</div>
|
|
|
|
<!-- SYNC INACTIVE: Visible when not in a room -->
|
|
<div id="sync-inactive" style="display:none; text-align: center; padding: 20px 10px;">
|
|
<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, 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">
|
|
<details class="form-group" open>
|
|
<summary title="Change your username, theme, and language." data-i18n="LABEL_SETTINGS_GROUP_PROFILE" data-i18n-title="LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP">👤 Profile & Theme</summary>
|
|
<div class="details-content">
|
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
|
<label for="username" 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>
|
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
|
<label id="themeLabel" for="themeSelector" style="margin-bottom: 0;" title="Follow the system theme or choose a fixed appearance">Appearance</label>
|
|
<select id="themeSelector" aria-label="Appearance" style="width: 150px; background: var(--bg); border: 1px solid var(--border-strong); color: var(--text); padding: 6px 10px; border-radius: 8px; font-size: 13px; cursor: pointer; outline: none; font-family: inherit;">
|
|
<option id="themeOptionSystem" value="system">💻 System</option>
|
|
<option id="themeOptionDark" value="dark">🌙 Dark</option>
|
|
<option id="themeOptionLight" value="light">☀️ Light</option>
|
|
</select>
|
|
</div>
|
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
|
<label for="langSelector" 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: 150px; background: var(--bg); border: 1px solid var(--border-strong); color: var(--text); 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>
|
|
<option value="es">🇪🇸 Español</option>
|
|
<option value="pt-BR">🇧🇷 Português (Brasil)</option>
|
|
<option value="ru">🇷🇺 Русский</option>
|
|
<option value="it">🇮🇹 Italiano</option>
|
|
<option value="pl">🇵🇱 Polski</option>
|
|
<option value="tr">🇹🇷 Türkçe</option>
|
|
<option value="nl">🇳🇱 Nederlands</option>
|
|
<option value="ja">🇯🇵 日本語</option>
|
|
<option value="ko">🇰🇷 한국어</option>
|
|
<option value="zh">🇨🇳 中文</option>
|
|
<option value="uk">🇺🇦 Українська</option>
|
|
<option value="pt">🇵🇹 Português (Portugal)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<details class="form-group">
|
|
<summary 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 class="details-content">
|
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
|
<label for="sendTabTitle" 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>
|
|
<label class="toggle-switch">
|
|
<input type="checkbox" id="sendTabTitle" checked>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
|
<label for="mediaTitlePrivacyMode" 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: 150px; background: var(--bg); border: 1px solid var(--border-strong); color: var(--text); 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>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<details class="form-group">
|
|
<summary title="Customize playback, notifications, and audio preferences." data-i18n="LABEL_SETTINGS_GROUP_SYNC" data-i18n-title="LABEL_SETTINGS_GROUP_SYNC_TOOLTIP">⚡ Playback & Sync</summary>
|
|
<div class="details-content">
|
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
|
<label for="autoSyncNextEpisode" 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">
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
|
<label for="filterNoise" 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>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
|
<label for="autoCopyInvite" 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>
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
|
<label for="browserNotifications" 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">
|
|
<span class="slider"></span>
|
|
</label>
|
|
</div>
|
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
|
|
<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>
|
|
<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>
|
|
</details>
|
|
|
|
<details class="form-group">
|
|
<summary title="Tools for fixing connection issues." data-i18n="LABEL_TROUBLESHOOTING" data-i18n-title="LABEL_TROUBLESHOOTING_TOOLTIP">🔧 Troubleshooting</summary>
|
|
<div class="details-content" style="gap: 8px;">
|
|
<button id="restartTourBtn" class="secondary" style="width: 100%; font-size: 11px;" title="Restart the onboarding tutorial" data-i18n="BTN_RESTART_TOUR" data-i18n-title="BTN_RESTART_TOUR_TOOLTIP">Restart Tutorial</button>
|
|
<button id="regenId" class="secondary" style="width: 100%; font-size: 11px; margin-top: 4px;" title="Regenerate your internal ID and reconnect" data-i18n="BTN_REGEN_ID" data-i18n-title="BTN_REGEN_ID_TOOLTIP">Regenerate Peer ID</button>
|
|
<p style="font-size: 9px; color: var(--text-muted); margin-top: 4px; text-align: center;" data-i18n="REGEN_ID_DESC">Use this if you see "Duplicate Identity" errors.</p>
|
|
<p style="font-size: 9px; text-align: center; margin-top: 4px; border-top: 1px solid var(--border-soft); padding-top: 8px;"><a href="https://github.com/Shik3i/KoalaSync/issues" target="_blank" style="color: var(--accent); text-decoration: none;" data-i18n="REGEN_ID_OTHER_ISSUE">Other issue? Open a GitHub Issue</a></p>
|
|
</div>
|
|
</details>
|
|
|
|
<div class="popup-footer">
|
|
<div style="font-size: 9px; color: var(--text-muted); opacity: 0.6; margin-bottom: 6px; line-height: 1.3;" data-i18n="FOOTER_SUPPORT_PROMPT">Enjoying KoalaSync? Leave a review!</div>
|
|
<a id="settingsReviewLink" href="#" target="_blank" data-i18n="FOOTER_REVIEW">★ Rate us</a>
|
|
<span> · </span>
|
|
<a id="settingsSupportLink" href="#" target="_blank" data-i18n="FOOTER_SUPPORT">Support KoalaSync</a>
|
|
<span> · </span>
|
|
<span id="settingsVersion">v0.0.0</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dev Tab -->
|
|
<div id="tab-dev" class="tab-content">
|
|
<label title="Current WebSocket connection state" data-i18n="LABEL_CONN_STATUS" data-i18n-title="LABEL_CONN_STATUS_TOOLTIP">Connection Status</label>
|
|
<div id="connStatus" class="info-card" style="display:flex; align-items:center; gap: 10px;">
|
|
<span id="connDot" class="status-dot status-offline"></span>
|
|
<span style="flex:1; display:flex; align-items:center; gap:6px; min-width:0;">
|
|
<span id="connText">Disconnected</span>
|
|
<span id="connInfo" data-i18n-title="STATUS_IDLE_TOOLTIP" title="KoalaSync is ready. Join or create a room to connect and start syncing." style="display:none; cursor:help; flex:none; width:15px; height:15px; line-height:14px; text-align:center; border-radius:50%; border:1px solid var(--text-muted); color:var(--text-muted); font-size:10px; font-weight:700; font-style:normal;">i</span>
|
|
</span>
|
|
<span id="connPing" style="font-size:11px; font-family:monospace; font-weight:600; opacity:0.8;"></span>
|
|
<button id="retryBtn" class="secondary" style="display:none; width: auto; padding: 4px 8px; font-size: 10px; margin: 0;" title="Attempt to reconnect to the server" data-i18n="BTN_RETRY" data-i18n-title="BTN_RETRY_TOOLTIP">RETRY</button>
|
|
<button id="copyLogs" class="btn secondary" style="width: auto; padding: 4px 10px; font-size: 11px; margin: 0;" title="Copy logs to clipboard for sharing" data-i18n="BTN_COPY_LOGS" data-i18n-title="BTN_COPY_LOGS_TOOLTIP">Copy Logs</button>
|
|
</div>
|
|
|
|
<label title="Technical details about the currently selected video element" data-i18n="LABEL_VIDEO_DEBUG" data-i18n-title="LABEL_VIDEO_DEBUG_TOOLTIP">Video Debug Info</label>
|
|
<div id="videoDebug" class="info-card" style="font-size: 10px; font-family: monospace; color: var(--text-muted); max-height: 250px; overflow-y: auto; line-height: 1.4;">
|
|
No tab selected or video detected.
|
|
</div>
|
|
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom: 8px;">
|
|
<label title="Chronological log of all sync commands in the room" data-i18n="LABEL_HISTORY" data-i18n-title="LABEL_HISTORY_TOOLTIP">Full Action History</label>
|
|
</div>
|
|
<div id="historyList" class="info-card" style="max-height: 120px; overflow-y: auto; font-size: 10px; margin-bottom: 15px;">
|
|
<div style="text-align:center; color: var(--text-muted); font-size: 11px;" data-i18n="HISTORY_EMPTY">No activity yet</div>
|
|
</div>
|
|
|
|
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom: 8px;">
|
|
<label title="Technical connection logs for debugging" data-i18n="LABEL_LOGS" data-i18n-title="LABEL_LOGS_TOOLTIP">Logs (Last 50)</label>
|
|
<button id="clearLogs" style="background:transparent; border:none; color:var(--accent); font-size:10px; cursor:pointer;" title="Clear log output" data-i18n="BTN_CLEAR" data-i18n-title="BTN_CLEAR_TOOLTIP">CLEAR</button>
|
|
</div>
|
|
<div id="logList"></div>
|
|
|
|
<div class="popup-footer">
|
|
<a href="https://github.com/Shik3i/KoalaSync" target="_blank" style="color: var(--text-muted); text-decoration: none; font-size: 10px; opacity: 0.6; display: block;" data-i18n="LABEL_GITHUB">GitHub Repository</a>
|
|
<div style="margin-top: 4px;">
|
|
<div style="font-size: 9px; color: var(--text-muted); opacity: 0.6; margin-bottom: 6px; line-height: 1.3;" data-i18n="FOOTER_SUPPORT_PROMPT">Enjoying KoalaSync? Leave a review!</div>
|
|
<a id="devReviewLink" href="#" target="_blank" data-i18n="FOOTER_REVIEW">★ Rate us</a>
|
|
<span> · </span>
|
|
<a id="devSupportLink" href="#" target="_blank" data-i18n="FOOTER_SUPPORT">Support KoalaSync</a>
|
|
<span> · </span>
|
|
<span id="appVersion">v0.0.0</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="tab-devtools" class="tab-content">
|
|
<label>Remote Seek</label>
|
|
<div class="info-card" style="display:flex; gap:8px; margin-bottom:15px;">
|
|
<button id="remoteSeekBack" class="secondary" style="flex:1; font-size:12px;">-30s</button>
|
|
<button id="remoteSeekForward" class="secondary" style="flex:1; font-size:12px;">+30s</button>
|
|
</div>
|
|
<button id="remoteSeekFiveMin" class="secondary" style="width:100%; font-size:12px; margin-bottom:15px;">Seek 5:00</button>
|
|
<div style="font-size:10px; color:var(--text-muted); text-align:center; margin-top:10px; opacity:0.7;">
|
|
Build: <span id="buildIdentifier">__BUILD_TIMESTAMP__</span>
|
|
</div>
|
|
</div>
|
|
|
|
</main>
|
|
|
|
<script src="popup.js" type="module"></script>
|
|
|
|
<!-- 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(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 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>
|
|
</div>
|
|
<h2 id="onboarding-title" style="color:var(--accent); margin:0 0 6px; font-size:14px; font-weight:700; text-align:left;">Welcome to KoalaSync!</h2>
|
|
<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: 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>
|
|
|
|
<div style="display:flex; gap:8px; justify-content:space-between; align-items:center;">
|
|
<button id="onboarding-skip" class="secondary" style="width:auto; padding:6px 12px; margin:0; font-size:11px;" title="Skip the tutorial" data-i18n="BTN_ONBOARDING_SKIP" data-i18n-title="BTN_ONBOARDING_SKIP_TOOLTIP">Skip</button>
|
|
<button id="onboarding-next" class="primary" style="width:auto; padding:6px 16px; margin:0; font-size:11px;" title="Go to next step" data-i18n="BTN_ONBOARDING_NEXT" data-i18n-title="BTN_ONBOARDING_NEXT_TOOLTIP">Next</button>
|
|
</div>
|
|
|
|
<!-- Tooltip pointer arrow -->
|
|
<div id="onboarding-arrow" style="position:absolute; width:0; height:0; border:6px solid transparent; transition:all 0.3s ease;"></div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|