diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5da471e..a64c5af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -114,17 +114,13 @@ jobs: sed -i "s/New v[0-9]\+\.[0-9]\+\.[0-9]\+ Release/New v$VERSION Release/g" README.md echo " ✓ README.md -> v$VERSION" - # 7. website/sitemap.xml — lastmod dates - sed -i "s/[0-9-]*<\/lastmod>/$(date +%Y-%m-%d)<\/lastmod>/g" website/sitemap.xml - echo " ✓ website/sitemap.xml -> lastmod $(date +%Y-%m-%d)" - echo "Version injection complete." - name: Commit and push version updates back to main run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - git add extension/manifest.base.json shared/constants.js package.json website/version.json website/template.html README.md website/sitemap.xml + git add extension/manifest.base.json shared/constants.js package.json website/version.json website/template.html README.md git commit -m "chore(release): update versions to $GITHUB_REF_NAME [skip ci]" || echo "No changes to commit" git push origin HEAD:main env: diff --git a/.gitignore b/.gitignore index 869a74a..e193446 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ extension/shared/ # Auto-generated website build output website/www/ +website/.avif-cache.json # Temporary scratch files scratch/ diff --git a/assets/MarketingCopy.md b/assets/MarketingCopy.md index b7c2d5d..e5b2227 100644 --- a/assets/MarketingCopy.md +++ b/assets/MarketingCopy.md @@ -16,7 +16,7 @@ Ready-to-paste copy for product listings, launch pages, directory submissions, a ## 2. Three-Sentence Overview -> KoalaSync is a lightweight browser extension that keeps you and your friends perfectly in sync on YouTube, Netflix, Twitch, Prime Video, Jellyfin, Emby, and almost any other site with an HTML5 video player — press play once and everyone stays together. It's built privacy-first: no accounts, no telemetry, and the official relay server runs entirely in volatile RAM with zero persistence, so nothing about your sessions is ever stored. Open source under the MIT license and fully self-hostable with a single Docker command, KoalaSync is a transparent watch-party tool that works everywhere and respects your data sovereignty. +> KoalaSync is a lightweight browser extension that keeps you and your friends perfectly in sync on YouTube, Netflix, Twitch, Prime Video, Jellyfin, Emby, and almost any other site with an HTML5 video player — press play once and everyone stays together. It's built privacy-first: no accounts, no telemetry, and the official relay server runs entirely in volatile RAM with zero persistence, so nothing about your sessions is ever stored. Open source under the MIT license and fully self-hostable with a single Docker Compose command, KoalaSync is a transparent watch-party tool that works everywhere and respects your data sovereignty. --- @@ -38,7 +38,7 @@ Most watch-party tools fall into one of two traps: they only work on a short all ### Built for people who actually want to read the code -KoalaSync is MIT-licensed open source, built by a solo developer. Audit it, fork it, change it. The extension is dependency-free with a direct Socket.IO wire implementation — no opaque libraries, no framework bloat, no surprise third-party SDKs. Want full sovereignty? Self-host your own relay with a single Docker command and keep all watch-party coordination traffic inside your own infrastructure. The official public relay is there when you don't care, self-hosting is there when you do. +KoalaSync is MIT-licensed open source, built by a solo developer. Audit it, fork it, change it. The extension is dependency-free with a direct Socket.IO wire implementation — no opaque libraries, no framework bloat, no surprise third-party SDKs. Want full sovereignty? Set a unique `SERVER_SALT` in the supplied Compose file, then start your own relay with a single Docker Compose command and keep all watch-party coordination traffic inside your own infrastructure. The official public relay is there when you don't care, self-hosting is there when you do. ### Little touches you'll notice diff --git a/assets/StoreAssets/StoreDescription.md b/assets/StoreAssets/StoreDescription.md index d2ae400..57b29fd 100644 --- a/assets/StoreAssets/StoreDescription.md +++ b/assets/StoreAssets/StoreDescription.md @@ -1,6 +1,6 @@ KoalaSync: Private Watch Parties for Emby, Jellyfin, Plex, Netflix & YouTube -Tired of counting down "3, 2, 1, Play" over voice chat? KoalaSync keeps you and your friends perfectly in sync. Whether you are streaming from your own self-hosted media server like Emby, Jellyfin or Plex, or watching on a major platform like Netflix, Prime Video or YouTube — KoalaSync is designed for smooth, browser-based watch parties. +Tired of counting down "3, 2, 1, Play" over voice chat? KoalaSync keeps you and your friends perfectly in sync. Whether you are streaming from your own self-hosted media server like Emby, Jellyfin or Plex, or watching on a major platform like Netflix, Prime Video or YouTube, KoalaSync is designed for smooth, browser-based watch parties. ✨ CORE FEATURES @@ -12,7 +12,7 @@ No account required. No tracking. Just create a room, invite your friends, and s • Smart Binge-Watching: When a new episode loads, KoalaSync automatically pauses the lobby until everyone is ready. No spoilers, no one left behind. • Smart Audio Compressor: Tired of quiet dialogue and suddenly loud action scenes? Balance whispering, explosions, and music with a single click while you watch. • One-Click Invites: Send a smart invite link to your friends. When they open it, KoalaSync automatically configures the room so they can join instantly. -• 13 Languages: Enjoy a native experience with a fully translated user interface. +• 15 Languages: Enjoy a native experience in English, German, French, Spanish, Brazilian and European Portuguese, Russian, Italian, Polish, Turkish, Dutch, Japanese, Korean, Simplified Chinese, and Ukrainian. @@ -33,14 +33,14 @@ KoalaSync is built for private watch parties without unnecessary data collection 3. Share the invite link with your friends. 4. Open your favorite streaming site or media server. 5. Select the active video tab. -6. Press play — everyone stays perfectly in sync. +6. Press play. Everyone stays perfectly in sync. ⚙️ UNDER THE HOOD KoalaSync is lightweight, transparent, and built with privacy in mind. -• On-Demand Relay: Playback state is synchronized through a custom WebSocket-based relay server. No persistent connection — the relay is only active while you're in a room. No background traffic, no idle connections. +• On-Demand Relay: Playback state is synchronized through a custom WebSocket-based relay server. There is no permanent background connection. The relay connection is maintained only while you're in a room and closes when you leave or automatically after two hours without a selected-video heartbeat. • No Media Streaming: KoalaSync does not stream, proxy, upload, download, or redistribute any video content. Everyone watches from their own browser on the original website. • Temporary Room State Only: The relay server only coordinates room state such as play, pause, seek position, active target, nickname, and readiness status. • Docker Self-Hosting: The relay server can be self-hosted with Docker if you prefer to run your own private instance. diff --git a/docs/AI_INIT.md b/docs/AI_INIT.md index 8a6674c..7fddb36 100644 --- a/docs/AI_INIT.md +++ b/docs/AI_INIT.md @@ -21,8 +21,8 @@ KoalaSync is a specialized tool for **synchronized video playback** across multi - `extension/`: Browser Extension (Chrome & Firefox, Manifest V3). Contains background service worker, content scripts, and popup UI. - `server/`: Node.js Relay Server using Socket.IO (WebSocket-only). - `website/`: **Landing Page** & Invitation Bridge (Marketing, Tutorials, and Downloads). - - **`build.cjs`**: Zero-dependency static site compiler. Translates `template.html` + `locales/*.json` → `www/`. Also minifies CSS/JS automatically. - - **`www/` is auto-generated**: Never edit files in `www/` directly. Always edit source files (`template.html`, `style.css`, `app.js`, `lang-init.js`, `locales/*.json`) and run `node website/build.cjs` to regenerate. CSS/JS are output as `.min.*` files — a built-in cleanup step removes stale artifacts on each build. + - **`build.cjs`**: Static site compiler using the repository's build dependencies. Translates `template.html` + `locales/*.json` → `www/`, generates the sitemap, and minifies HTML/CSS/JS. + - **`www/` is auto-generated**: Never edit files in `www/` directly. Always edit source files (`template.html`, `style.css`, `styles/*.css`, `app.js`, `lang-init.js`, `locales/*.json`) and run `node website/build.cjs` to regenerate. `style.css` is the development manifest; `style.legacy.css` is an unloaded, byte-identical migration reference. Production creates page-specific and render-priority CSS bundles from `styles/*.css`; CSS/JS are output as `.min.*` files and stale generated assets are removed on each build. - `shared/`: **Single Source of Truth** for protocol constants, event names, blacklist data, and generated usernames. - `scripts/`: Development utilities (e.g., `build-extension.cjs`). - `docker-compose.yml`: Root-level orchestration for the relay server. @@ -64,14 +64,8 @@ The popup UI follows a strict design system. Do not modify these variables or th - **Font**: System font stack. **MANDATORY**: No external CDNs or Google Fonts to ensure 100% privacy. - **Popup Width**: Fixed at `320px`. - **Tab Structure**: Must maintain the visible **Room**, **Sync**, **Settings**, and **Status** tabs. The hidden **Dev** tab is available only for developer diagnostics. -- **CSS Variables**: - | Variable | Value | Purpose | - | :--- | :--- | :--- | - | `--bg` | `#0f172a` | Main background | - | `--card` | `#1e293b` | Form and info cards | - | `--accent` | `#6366f1` | Primary actions and branding | - | `--success` | `#22c55e` | Success states / Online dot | - | `--error` | `#ef4444` | Errors / Offline dot | +- **Appearance modes**: Preserve `system`, `light`, and `dark` behavior and the Eucalyptus, Cyber, and Graphite palettes. +- **CSS variables**: Use the semantic tokens defined in `popup.html` (`--bg`, `--card`, `--accent`, `--text`, `--text-muted`, `--success`, `--warning`, and `--error`). Do not hard-code the retired indigo/slate palette or bypass palette-specific token overrides. ## 7. Non-Negotiables (Core Logic) The following features are critical and must not be removed or fundamentally altered: @@ -149,11 +143,11 @@ Before starting any task, committing, or pushing, you **MUST** run `git pull --r 3. Implement the handler in `server/index.js` and `background.js`. ### Making Website Changes -1. Edit source files in `website/` (`template.html`, `style.css`, `app.js`, `lang-init.js`, or `locales/*.json`). +1. Edit source files in `website/` (`template.html`, `style.css`, `styles/*.css`, `app.js`, `lang-init.js`, or `locales/*.json`). 2. Run the compiler: `node website/build.cjs`. This generates the multilingual pages in `www/` and minifies CSS/JS. -3. Verify the output: `node --check website/www/app.min.js && node --check website/www/lang-init.min.js`. +3. Verify the sources and generated contract: `node --check website/app.js`, `node --check website/lang-init.js`, `node scripts/test-website-locales.mjs`, and `node scripts/test-website-theme.mjs`. 4. Test locally: `npx serve website/www` or `python3 -m http.server 8080 -d website/www`. -5. Commit both source changes and the updated `www/` output. +5. Commit the source changes only. `website/www/` is generated and gitignored. ### Testing Locally 1. Run the build script: `node scripts/build-extension.cjs`. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5c74c9e..15b2b56 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,10 +1,29 @@ # KoalaSync Changelog -All notable changes to the KoalaSync browser extension and relay server. +All notable changes to the KoalaSync browser extension, relay server, website, and release tooling. --- -## Unreleased +## [v2.6.0] — Unreleased + +### Added +- **Website: Expanded comparison guides** — Added fully localized comparison pages for Kosmi, Scener, and TwoSeven, plus improved existing alternatives pages and navigation. +- **Website: AI-readable product profile** — Expanded `llms.txt` with fit guidance, limitations, architecture, privacy, installation, self-hosting, and support information, and linked it from every localized landing-page head. +- **Website: Accessibility regression coverage** — Added automated checks for light-theme step numbers, legal-link cues, heading hierarchy, readable self-hosting surfaces, and reduced-motion behavior. +- **Extension: Appearance controls** — Added localized system, light, and dark theme options with an early theme initializer to avoid flashes during popup startup. + +### Changed +- **Website: Koala nature redesign** — Rebuilt the landing page around a responsive day/night forest theme with richer motion, an improved interactive demo, clearer onboarding, polished legal/join/error pages, and stronger mobile behavior. +- **Extension: Popup and settings redesign** — Unified controls, status surfaces, colors, icons, badges, and accessibility behavior; settings are now organized into mutually exclusive accordion groups. +- **Website: Faster rendering and builds** — Split page CSS by responsibility, removed startup forced reflows, added responsive mascot assets, cached AVIF generation, subset the flag font, minified generated HTML, and added integrity metadata for scripts and styles. +- **Website: SEO and localization** — Added richer structured metadata, AI-crawler guidance, dynamic sitemap generation, and complete translations for the expanded alternatives content. +- **Website: Safer self-hosting example** — Added an explicit `SERVER_SALT` replacement value, optional admin-metrics configuration, loopback-only relay publishing, and consistent host-Caddy proxy targets without requiring a separate `.env` file. + +### Fixed +- **Extension: Episode Lobby reliability** — Prevented the lobby from remaining stuck in a loading state and stopped episode transitions from triggering on non-episodic media. +- **Extension: Tab-title normalization** — Notification counters such as `(14)`, `[7]`, and `(99+)` are removed reliably without stripping legitimate large numeric titles. +- **Website and extension: Theme contrast and navigation** — Fixed light-theme contrast, language highlighting, mobile join navigation, and several control-label and tooltip inconsistencies. +- **Website: Build and accessibility correctness** — Replaced the regex HTML minifier with conservative parser-based `htmlnano`, stopped tracking generated sitemap output, restored readable light-theme step numbers, fixed legal-page and comparison-card contrast, repaired heading order, and disabled all film motion when reduced motion is requested. --- diff --git a/docs/KNOWN_LIMITATIONS.md b/docs/KNOWN_LIMITATIONS.md index 71695a6..703386e 100644 --- a/docs/KNOWN_LIMITATIONS.md +++ b/docs/KNOWN_LIMITATIONS.md @@ -18,8 +18,9 @@ KoalaSync is a **privacy-preserving watch-party sync tool**, not a security prod - **Rooms are ephemeral.** They exist for a few hours and are auto-reaped. There are no accounts, no persistent storage, no money, and no sensitive content on the relay. -- **The relay is a dumb, stateless message bus.** It forwards play/pause/seek between - peers who *chose* to watch together and joined via an invite link shared out-of-band. +- **The relay is a zero-persistence, in-memory message bus.** It temporarily holds room + and peer state while forwarding play/pause/seek between peers who *chose* to watch + together and joined via an invite link shared out-of-band. - **Participants are invited.** Anyone in a room was let in. The social contract is "we're watching a video together," not "mutually distrusting parties." diff --git a/examples/Caddyfile.example b/examples/Caddyfile.example index a7a701d..e74721e 100644 --- a/examples/Caddyfile.example +++ b/examples/Caddyfile.example @@ -18,6 +18,14 @@ # # Clean URLs support (resolves /join to join.html, etc.) # try_files {path} {path}.html {path}/ # file_server +# +# # Serve the themed 404 page for unknown URLs (without this block, +# # Caddy returns an empty 404 response) +# handle_errors { +# @notfound expression {err.status_code} == 404 +# rewrite @notfound /404.html +# file_server +# } # } # # syncserver.koalastuff.net { @@ -57,6 +65,14 @@ sync.koalastuff.net { try_files {path} {path}.html {path}/ file_server + # Serve the themed 404 page for unknown URLs (without this block, + # Caddy returns an empty 404 response) + handle_errors { + @notfound expression {err.status_code} == 404 + rewrite @notfound /404.html + file_server + } + # Static Caching for high-performance PageSpeed (1 year with validation) @static { file diff --git a/extension/audio-options.css b/extension/audio-options.css index cfd3d85..8c3be91 100644 --- a/extension/audio-options.css +++ b/extension/audio-options.css @@ -1,13 +1,46 @@ :root { - --bg: #0f172a; - --card: #1e293b; - --panel: #172033; - --accent: #6366f1; - --accent-hover: #818cf8; - --text: #f8fafc; - --text-muted: #94a3b8; - --border: #334155; + /* KoalaSync nature system: forest neutrals, eucalyptus brand, warm supporting accents. */ + --bg-base: oklch(0.23 0.028 155); + --surface: oklch(0.30 0.035 150); + --surface-deep: oklch(0.205 0.025 155); + --surface-alt: oklch(0.265 0.032 150); + --border-soft: oklch(0.88 0.025 145 / 0.09); + --border-strong: oklch(0.39 0.038 145); + --text-primary: oklch(0.96 0.01 90); + --text-secondary: oklch(0.78 0.02 90); + --accent-green: oklch(0.73 0.13 155); + --accent-green-hover: oklch(0.79 0.12 155); + --accent-terracotta: oklch(0.62 0.14 45); + --success-green: oklch(0.72 0.14 135); + --warning-amber: oklch(0.76 0.14 80); + --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; + 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.52 0.13 155); + --accent-green-hover: oklch(0.45 0.12 155); + --text-on-green: oklch(0.98 0.005 100); + color-scheme: light; } * { @@ -55,7 +88,7 @@ body { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; - text-transform: uppercase; + letter-spacing: 0.04em; } h1, @@ -83,6 +116,10 @@ h2 { box-shadow: 0 2px 8px rgba(0,0,0,0.15); } +html.theme-light .panel { + box-shadow: 0 12px 30px oklch(0.22 0.035 140 / 0.09); +} + .muted-panel { color: var(--text-muted); } @@ -101,7 +138,7 @@ h2 { color: var(--text-muted); font-size: 12px; font-weight: 700; - text-transform: uppercase; + letter-spacing: 0.01em; } .preset-group { @@ -125,12 +162,12 @@ h2 { transition: border-color 0.2s, box-shadow 0.2s; } .preset-card:hover { - border-color: rgba(99, 102, 241, 0.4); + border-color: color-mix(in oklch, var(--accent), transparent 60%); } .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 color-mix(in oklch, var(--accent), transparent 65%); } .preset-card input { @@ -141,8 +178,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: var(--surface-deep); + border: 1px solid var(--border-soft); border-radius: var(--radius); transition: opacity 0.3s; } @@ -158,7 +195,7 @@ h2 { color: var(--text-muted); font-size: 11px; font-weight: 700; - text-transform: uppercase; + letter-spacing: 0.01em; } input[type="range"] { @@ -204,7 +241,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 +253,7 @@ input[type="number"]:focus { width: 16px; left: 3px; bottom: 3px; - background-color: #94a3b8; + background-color: var(--text-secondary); transition: .3s; border-radius: 50%; } diff --git a/extension/audio-options.html b/extension/audio-options.html index e88401a..86181c8 100644 --- a/extension/audio-options.html +++ b/extension/audio-options.html @@ -3,6 +3,7 @@ KoalaSync Audio Settings + diff --git a/extension/background.js b/extension/background.js index c2f595d..aa1a32f 100644 --- a/extension/background.js +++ b/extension/background.js @@ -1,7 +1,7 @@ import { EVENTS, CONTROL_MODES, CAPABILITIES, PROTOCOL_VERSION, OFFICIAL_SERVER_URL, OFFICIAL_SERVER_TOKEN, EPISODE_LOBBY_TIMEOUT, FORCE_SYNC_TIMEOUT, HEARTBEAT_INTERVAL } from './shared/constants.js'; import { generateUsername } from './shared/names.js'; import { loadLocale, getMessage, getSystemLanguage } from './i18n.js'; -import { sameEpisode } from './episode-utils.js'; +import { sameEpisode, extractEpisodeId } from './episode-utils.js'; import { applyTitlePrivacyToPayload, sanitizeSharedTitle, sanitizeTabTitle, normalizeSendTabTitle, normalizeTitlePrivacyMode } from './title-privacy.js'; import { initTabManager } from './modules/tab-manager.js'; import './page-api-seek-overrides.js'; @@ -790,13 +790,13 @@ function updateBadgeStatus() { if (status === 'reconnecting') { chrome.action.setBadgeText({ text: '...' }); - chrome.action.setBadgeBackgroundColor({ color: '#f59e0b' }); + chrome.action.setBadgeBackgroundColor({ color: '#c96736' }); } else if (status === 'connecting') { chrome.action.setBadgeText({ text: '...' }); - chrome.action.setBadgeBackgroundColor({ color: '#fbbf24' }); + chrome.action.setBadgeBackgroundColor({ color: '#de7949' }); } else if (status === 'connected' && currentRoom && currentTabId) { chrome.action.setBadgeText({ text: 'ON' }); - chrome.action.setBadgeBackgroundColor({ color: '#22c55e' }); + chrome.action.setBadgeBackgroundColor({ color: '#56ae6c' }); } else { chrome.action.setBadgeText({ text: '' }); } @@ -1325,6 +1325,9 @@ function handleServerEvent(event, data) { } if (storageInitialized) chrome.storage.session.set({ currentRoom }); chrome.runtime.sendMessage({ type: 'PEER_UPDATE', peers: currentRoom.peers }).catch(() => {}); + if (episodeLobby) { + checkEpisodeLobbyCompletion(); + } } } } @@ -2476,6 +2479,11 @@ async function handleAsyncMessage(message, sender, sendResponse) { } const newTitle = message.payload && message.payload.newTitle; + if (newTitle && extractEpisodeId(newTitle) === null) { + addLog(`Episode change detected ("${newTitle}") but no episode ID was found; ignoring.`, 'info'); + sendResponse({ status: 'not_an_episode' }); + return; + } if (!newTitle) { sendResponse({ status: 'no_title' }); return; @@ -2566,6 +2574,13 @@ async function handleAsyncMessage(message, sender, sendResponse) { sendResponse({ status: 'lobby_created' }); } else if (message.type === 'EPISODE_READY_LOCAL') { + if (sender.tab) { + const senderTabId = sender.tab.id; + if (!currentTabId || currentTabId !== senderTabId) { + sendResponse({ status: 'ignored_unselected_tab' }); + return; + } + } // Content script confirmed it loaded the lobby episode if (episodeLobby && message.payload && sameEpisode(message.payload.title, episodeLobby.expectedTitle)) { if (!episodeLobby.readyPeers.includes(peerId)) { @@ -2604,6 +2619,13 @@ async function handleAsyncMessage(message, sender, sendResponse) { } sendResponse({ status: 'ok' }); } else if (message.type === 'CONTENT_BOOT') { + if (sender.tab) { + const senderTabId = sender.tab.id; + if (!currentTabId || currentTabId !== senderTabId) { + sendResponse({ status: 'ignored_unselected_tab' }); + return; + } + } // Content script re-injected, check if there's an active lobby if (episodeLobby) { sendResponse({ lobbyActive: true, expectedTitle: episodeLobby.expectedTitle }); diff --git a/extension/content.js b/extension/content.js index 6a12f7e..aa05517 100644 --- a/extension/content.js +++ b/extension/content.js @@ -424,13 +424,13 @@ const host = hcmEl('div', 'all:initial'); const root = host.attachShadow({ mode: 'open' }); - const wrap = hcmEl('div', 'position:fixed;z-index:2147483647;left:50%;bottom:32px;transform:translateX(-50%);background:#1f2937;color:#f9fafb;font:14px/1.4 system-ui,sans-serif;padding:16px 18px;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,.45);max-width:360px;border:1px solid #374151'); + const wrap = hcmEl('div', 'position:fixed;z-index:2147483647;left:50%;bottom:32px;transform:translateX(-50%);background:#212a17;color:#f4f2ea;font:14px/1.4 system-ui,sans-serif;padding:16px 18px;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,.45);max-width:360px;border:1px solid #2f3625'); wrap.setAttribute('role', 'dialog'); const title = hcmEl('div', 'font-weight:600;margin-bottom:6px', hcmStrings.title); - const body = hcmEl('div', 'margin-bottom:12px;color:#d1d5db', hcmStrings.body); + const body = hcmEl('div', 'margin-bottom:12px;color:#bcb7a9', hcmStrings.body); const btnRow = hcmEl('div', 'display:flex;gap:8px;justify-content:flex-end'); - const soloBtn = hcmEl('button', 'background:#374151;color:#f9fafb;border:0;padding:8px 12px;border-radius:8px;cursor:pointer', hcmStrings.solo); - const stayBtn = hcmEl('button', 'background:#10b981;color:#062a20;border:0;padding:8px 12px;border-radius:8px;cursor:pointer;font-weight:600', hcmStrings.stay); + const soloBtn = hcmEl('button', 'background:#2f3625;color:#f4f2ea;border:0;padding:8px 12px;border-radius:8px;cursor:pointer', hcmStrings.solo); + const stayBtn = hcmEl('button', 'background:#56ae6c;color:#0a1a0d;border:0;padding:8px 12px;border-radius:8px;cursor:pointer;font-weight:600', hcmStrings.stay); btnRow.append(soloBtn, stayBtn); wrap.append(title, body, btnRow); root.appendChild(wrap); @@ -494,7 +494,7 @@ } const host = hcmEl('div', 'all:initial'); const root = host.attachShadow({ mode: 'open' }); - const b = hcmEl('div', 'position:fixed;z-index:2147483646;right:16px;bottom:16px;background:#b45309;color:#fff;font:13px/1.3 system-ui,sans-serif;padding:8px 12px;border-radius:10px;box-shadow:0 6px 20px rgba(0,0,0,.4);cursor:pointer;display:flex;align-items:center;gap:8px'); + const b = hcmEl('div', 'position:fixed;z-index:2147483646;right:16px;bottom:16px;background:#c96736;color:#fff;font:13px/1.3 system-ui,sans-serif;padding:8px 12px;border-radius:10px;box-shadow:0 6px 20px rgba(0,0,0,.4);cursor:pointer;display:flex;align-items:center;gap:8px'); b.append(hcmEl('span', null, '● ' + hcmStrings.badge), hcmEl('span', 'text-decoration:underline', hcmStrings.resync)); b.addEventListener('click', hcmExitDesync); root.appendChild(b); @@ -758,7 +758,8 @@ // Only trigger if: we had a previous title, the title changed, // a video exists, and we're near the start of new content. if (lastKnownMediaTitle && currentTitle - && !sameEpisode(currentTitle, lastKnownMediaTitle) + && !sameEpisode(currentTitle, lastKnownMediaTitle) + && extractEpisodeId(currentTitle) !== null && video && current !== null && current < 5 && video.readyState >= 1) { @@ -798,7 +799,11 @@ const current = video ? getSyncCurrentTime(video) : null; if (video && currentTitle && sameEpisode(currentTitle, expectedTitle) - && current !== null && current < 5 && video.readyState >= 1) { + && current !== null && video.readyState >= 1) { + if (current >= 5) { + expectedSeekTime = 0; + video.currentTime = 0; + } // Match! Pause at start and report ready. if (!video.paused) { _setSuppress('paused'); diff --git a/extension/i18n.js b/extension/i18n.js index 026de47..11d2ec5 100644 --- a/extension/i18n.js +++ b/extension/i18n.js @@ -12,6 +12,9 @@ let currentLanguage = null; */ export async function loadLocale(langCode) { const resolvedLang = SUPPORTED_LANGUAGES.includes(langCode) ? langCode : DEFAULT_LANGUAGE; + if (typeof document !== 'undefined') { + document.documentElement.lang = resolvedLang; + } if (currentLanguage === resolvedLang && Object.keys(activeDictionary).length > 0) { return; @@ -90,6 +93,8 @@ export function getMessage(key, placeholders = null) { * Performs dynamic DOM replacements for elements carrying data-i18n attributes. */ export function translateDOM() { + if (typeof document === 'undefined') return; + // 1. Text Content document.querySelectorAll('[data-i18n]').forEach(el => { const key = el.getAttribute('data-i18n'); diff --git a/extension/icons/pause.svg b/extension/icons/pause.svg new file mode 100644 index 0000000..0b65dc4 --- /dev/null +++ b/extension/icons/pause.svg @@ -0,0 +1,3 @@ + + + diff --git a/extension/icons/play.svg b/extension/icons/play.svg new file mode 100644 index 0000000..f4239cf --- /dev/null +++ b/extension/icons/play.svg @@ -0,0 +1,3 @@ + + + diff --git a/extension/icons/sync.svg b/extension/icons/sync.svg new file mode 100644 index 0000000..fb41276 --- /dev/null +++ b/extension/icons/sync.svg @@ -0,0 +1,3 @@ + + + diff --git a/extension/locales/de.json b/extension/locales/de.json index 7c8eb95..6507be7 100644 --- a/extension/locales/de.json +++ b/extension/locales/de.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Fernsteuerung", "BTN_COPY_INVITE": "📋 Einladungslink", "BTN_COPY_INVITE_TOOLTIP": "Einladungslink kopieren", - "BTN_PLAY": "▶ Abspielen", + "BTN_PLAY": "Abspielen", "BTN_PLAY_TOOLTIP": "Sende einen Play-Befehl an alle", - "BTN_PAUSE": "⏸ Pause", + "BTN_PAUSE": "Pause", "BTN_PAUSE_TOOLTIP": "Sende einen Pause-Befehl an alle", - "BTN_SYNC": "⚡ SYNC", + "BTN_SYNC": "Sync", "BTN_SYNC_TOOLTIP": "Erzwinge die Synchronisation aller Teilnehmer", "OPTION_JUMP_TO_OTHERS": "Zu anderen springen", "OPTION_JUMP_TO_ME": "Zu mir springen", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Wendet Audioeffekte wie Kompression auf die Videowiedergabe an", "AUDIO_OPEN_SETTINGS": "Öffnen", "NEW_FEATURE_AUDIO": "Neu: Audio-Verarbeitung — probiere den Kompressor aus!", + "NEW_FEATURE_THEME": "Neu: Themes — wechsle zum klassischen Look oder minimalem Graphit.", "AUDIO_BACK": "← Zurück", "AUDIO_PAGE_TITLE": "Audio-Einstellungen", "AUDIO_MASTER_TOGGLE": "Audio-Verarbeitung", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Tab-Titel senden", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Sendet den Titel des ausgewählten Browser-Tabs an den Raum.", "LABEL_MEDIA_TITLE_PRIVACY": "Medientitel", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Lege fest, wie Medientitel an den Raum gesendet werden." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Lege fest, wie Medientitel an den Raum gesendet werden.", + "LABEL_SETTINGS_GROUP_PROFILE": "Profil & Aussehen", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Ändere deinen Benutzernamen, dein Design und deine Spracheinstellungen.", + "LABEL_SETTINGS_GROUP_SYNC": "Wiedergabe & Sync", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Passe Wiedergabe-, Benachrichtigungs- und Audioeinstellungen an." } diff --git a/extension/locales/en.json b/extension/locales/en.json index 9d041e4..25f5c04 100644 --- a/extension/locales/en.json +++ b/extension/locales/en.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Remote Control", "BTN_COPY_INVITE": "📋 Invite Link", "BTN_COPY_INVITE_TOOLTIP": "Copy Invite Link", - "BTN_PLAY": "▶ Play", + "BTN_PLAY": "Play", "BTN_PLAY_TOOLTIP": "Send a Play command to everyone", - "BTN_PAUSE": "⏸ Pause", + "BTN_PAUSE": "Pause", "BTN_PAUSE_TOOLTIP": "Send a Pause command to everyone", - "BTN_SYNC": "⚡ SYNC", + "BTN_SYNC": "Sync", "BTN_SYNC_TOOLTIP": "Force all users to sync up", "OPTION_JUMP_TO_OTHERS": "Jump to Others", "OPTION_JUMP_TO_ME": "Jump to Me", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Apply audio effects like compression to video playback", "AUDIO_OPEN_SETTINGS": "Open", "NEW_FEATURE_AUDIO": "New: Audio Processing — try the compressor!", + "NEW_FEATURE_THEME": "New: Themes — switch to the classic look or minimal graphite.", "AUDIO_BACK": "← Back", "AUDIO_PAGE_TITLE": "Audio Settings", "AUDIO_MASTER_TOGGLE": "Audio Processing", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Send tab title", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Send the selected browser tab title to the room.", "LABEL_MEDIA_TITLE_PRIVACY": "Media title", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Choose how media titles are sent to the room." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Choose how media titles are sent to the room.", + "LABEL_SETTINGS_GROUP_PROFILE": "Profile & Appearance", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Change your username, theme, and language preferences.", + "LABEL_SETTINGS_GROUP_SYNC": "Playback & Sync", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Customize playback, notifications, and audio preferences." } diff --git a/extension/locales/es.json b/extension/locales/es.json index 768e312..624e419 100644 --- a/extension/locales/es.json +++ b/extension/locales/es.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Control remoto", "BTN_COPY_INVITE": "📋 Enlace de invitación", "BTN_COPY_INVITE_TOOLTIP": "Copiar enlace de invitación", - "BTN_PLAY": "▶ Reproducir", + "BTN_PLAY": "Reproducir", "BTN_PLAY_TOOLTIP": "Enviar un comando de reproducción a todos", - "BTN_PAUSE": "⏸ Pausar", + "BTN_PAUSE": "Pausar", "BTN_PAUSE_TOOLTIP": "Enviar un comando de pausa a todos", - "BTN_SYNC": "⚡ SYNC", + "BTN_SYNC": "Sync", "BTN_SYNC_TOOLTIP": "Forzar la sincronización de todos los usuarios", "OPTION_JUMP_TO_OTHERS": "Ir a la posición de otros", "OPTION_JUMP_TO_ME": "Traerlos a mi posición", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Aplica efectos de audio como compresión a la reproducción de video", "AUDIO_OPEN_SETTINGS": "Abrir", "NEW_FEATURE_AUDIO": "Nuevo: Procesamiento de audio — ¡prueba el compresor!", + "NEW_FEATURE_THEME": "Nuevo: Temas — cambia al aspecto clásico o al grafito minimalista.", "AUDIO_BACK": "← Volver", "AUDIO_PAGE_TITLE": "Configuración de audio", "AUDIO_MASTER_TOGGLE": "Procesamiento de audio", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Enviar título de pestaña", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Envía a la sala el título de la pestaña del navegador seleccionada.", "LABEL_MEDIA_TITLE_PRIVACY": "Título multimedia", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Elige cómo se envían los títulos multimedia a la sala." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Elige cómo se envían los títulos multimedia a la sala.", + "LABEL_SETTINGS_GROUP_PROFILE": "Perfil y Apariencia", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Cambia tu nombre de usuario, tema y preferencias de idioma.", + "LABEL_SETTINGS_GROUP_SYNC": "Reproducción y Sincronización", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Personaliza la reproducción, las notificaciones y las preferencias de audio." } diff --git a/extension/locales/fr.json b/extension/locales/fr.json index ec85121..67e0408 100644 --- a/extension/locales/fr.json +++ b/extension/locales/fr.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Contrôle à distance", "BTN_COPY_INVITE": "📋 Lien d'invitation", "BTN_COPY_INVITE_TOOLTIP": "Copier le lien d'invitation", - "BTN_PLAY": "▶ Lecture", + "BTN_PLAY": "Lecture", "BTN_PLAY_TOOLTIP": "Envoyer une commande Lecture à tout le monde", - "BTN_PAUSE": "⏸ Pause", + "BTN_PAUSE": "Pause", "BTN_PAUSE_TOOLTIP": "Envoyer une commande Pause à tout le monde", - "BTN_SYNC": "⚡ SYNC", + "BTN_SYNC": "Sync", "BTN_SYNC_TOOLTIP": "Forcer tous les utilisateurs à se synchroniser", "OPTION_JUMP_TO_OTHERS": "Rejoindre les autres", "OPTION_JUMP_TO_ME": "Les amener à moi", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Applique des effets audio comme la compression à la lecture vidéo", "AUDIO_OPEN_SETTINGS": "Ouvrir", "NEW_FEATURE_AUDIO": "Nouveau : Traitement audio — essayez le compresseur !", + "NEW_FEATURE_THEME": "Nouveau : Thèmes — passez au look classique ou au graphite minimal.", "AUDIO_BACK": "← Retour", "AUDIO_PAGE_TITLE": "Paramètres audio", "AUDIO_MASTER_TOGGLE": "Traitement audio", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Envoyer le titre de l’onglet", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Envoie au salon le titre de l’onglet de navigateur sélectionné.", "LABEL_MEDIA_TITLE_PRIVACY": "Titre du média", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Choisissez comment les titres des médias sont envoyés au salon." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Choisissez comment les titres des médias sont envoyés au salon.", + "LABEL_SETTINGS_GROUP_PROFILE": "Profil & Apparence", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Modifiez votre nom d'utilisateur, votre thème et vos préférences linguistiques.", + "LABEL_SETTINGS_GROUP_SYNC": "Lecture & Sync", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Personnalisez la lecture, les notifications et les préférences audio." } diff --git a/extension/locales/it.json b/extension/locales/it.json index cca98b9..bb72c95 100644 --- a/extension/locales/it.json +++ b/extension/locales/it.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Telecomando", "BTN_COPY_INVITE": "📋 Link di Invito", "BTN_COPY_INVITE_TOOLTIP": "Copia il link di invito", - "BTN_PLAY": "▶ Riproduci", + "BTN_PLAY": "Riproduci", "BTN_PLAY_TOOLTIP": "Avvia la riproduzione per tutti", - "BTN_PAUSE": "⏸ Pausa", + "BTN_PAUSE": "Pausa", "BTN_PAUSE_TOOLTIP": "Metti in pausa per tutti", - "BTN_SYNC": "⚡ SINCRO", + "BTN_SYNC": "Sincro", "BTN_SYNC_TOOLTIP": "Forza la sincronizzazione per tutti", "OPTION_JUMP_TO_OTHERS": "Vai alla posizione degli altri", "OPTION_JUMP_TO_ME": "Portali alla mia posizione", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Applica effetti sonori (come il compressore) ai video", "AUDIO_OPEN_SETTINGS": "Apri", "NEW_FEATURE_AUDIO": "Novità: Elaborazione Audio — prova il compressore!", + "NEW_FEATURE_THEME": "Nuovo: Temi — passa all'aspetto classico o al grafite minimale.", "AUDIO_BACK": "← Indietro", "AUDIO_PAGE_TITLE": "Impostazioni Audio", "AUDIO_MASTER_TOGGLE": "Elaborazione Audio", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Invia titolo scheda", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Invia alla stanza il titolo della scheda del browser selezionata.", "LABEL_MEDIA_TITLE_PRIVACY": "Titolo multimediale", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Scegli come inviare alla stanza i titoli multimediali." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Scegli come inviare alla stanza i titoli multimediali.", + "LABEL_SETTINGS_GROUP_PROFILE": "Profilo & Aspetto", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Modifica il tuo nome utente, tema e preferenze di lingua.", + "LABEL_SETTINGS_GROUP_SYNC": "Riproduzione & Sync", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Personalizza la riproduzione, le notifiche e le preferenze audio." } diff --git a/extension/locales/ja.json b/extension/locales/ja.json index 67f7843..70162a8 100644 --- a/extension/locales/ja.json +++ b/extension/locales/ja.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "リモートコントロール", "BTN_COPY_INVITE": "📋 招待リンク", "BTN_COPY_INVITE_TOOLTIP": "招待リンクをコピー", - "BTN_PLAY": "▶ 再生", + "BTN_PLAY": "再生", "BTN_PLAY_TOOLTIP": "全員に再生コマンドを送信", - "BTN_PAUSE": "⏸ 一時停止", + "BTN_PAUSE": "一時停止", "BTN_PAUSE_TOOLTIP": "全員に一時停止コマンドを送信", - "BTN_SYNC": "⚡ 同期", + "BTN_SYNC": "同期", "BTN_SYNC_TOOLTIP": "すべてのユーザーを強制的に同期", "OPTION_JUMP_TO_OTHERS": "他の人に合わせる", "OPTION_JUMP_TO_ME": "自分に合わせる", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "動画再生に圧縮などのオーディオエフェクトを適用します", "AUDIO_OPEN_SETTINGS": "開く", "NEW_FEATURE_AUDIO": "新機能: オーディオ処理 — コンプレッサーを試してみよう!", + "NEW_FEATURE_THEME": "新機能:テーマ — クラシックな外観やミニマルなグラファイトに切り替え。", "AUDIO_BACK": "← 戻る", "AUDIO_PAGE_TITLE": "オーディオ設定", "AUDIO_MASTER_TOGGLE": "オーディオ処理", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "タブタイトルを送信", "LABEL_SEND_TAB_TITLE_TOOLTIP": "選択中のブラウザータブのタイトルをルームに送信します。", "LABEL_MEDIA_TITLE_PRIVACY": "メディアタイトル", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "メディアタイトルをルームに送信する方法を選択します。" + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "メディアタイトルをルームに送信する方法を選択します。", + "LABEL_SETTINGS_GROUP_PROFILE": "プロフィールと外観", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "ユーザー名、テーマ、言語設定を変更します。", + "LABEL_SETTINGS_GROUP_SYNC": "再生と同期", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "再生、通知、オーディオ設定をカスタマイズします。" } diff --git a/extension/locales/ko.json b/extension/locales/ko.json index 87e4356..d40a320 100644 --- a/extension/locales/ko.json +++ b/extension/locales/ko.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "원격 제어", "BTN_COPY_INVITE": "📋 초대 링크", "BTN_COPY_INVITE_TOOLTIP": "초대 링크 복사", - "BTN_PLAY": "▶ 재생", + "BTN_PLAY": "재생", "BTN_PLAY_TOOLTIP": "모든 사람에게 재생 명령 전송", - "BTN_PAUSE": "⏸ 일시정지", + "BTN_PAUSE": "일시정지", "BTN_PAUSE_TOOLTIP": "모든 사람에게 일시정지 명령 전송", - "BTN_SYNC": "⚡ 동기화", + "BTN_SYNC": "동기화", "BTN_SYNC_TOOLTIP": "모든 사용자의 강제 동기화", "OPTION_JUMP_TO_OTHERS": "다른 사람에게 이동", "OPTION_JUMP_TO_ME": "나에게 이동", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "비디오 재생에 압축과 같은 오디오 효과를 적용합니다", "AUDIO_OPEN_SETTINGS": "열기", "NEW_FEATURE_AUDIO": "신규: 오디오 처리 — 컴프레서를 사용해보세요!", + "NEW_FEATURE_THEME": "신규: 테마 — 클래식 룩이나 미니멀 그래파이트로 전환하세요.", "AUDIO_BACK": "← 뒤로", "AUDIO_PAGE_TITLE": "오디오 설정", "AUDIO_MASTER_TOGGLE": "오디오 처리", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "탭 제목 보내기", "LABEL_SEND_TAB_TITLE_TOOLTIP": "선택한 브라우저 탭 제목을 방에 보냅니다.", "LABEL_MEDIA_TITLE_PRIVACY": "미디어 제목", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "미디어 제목을 방에 보내는 방식을 선택합니다." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "미디어 제목을 방에 보내는 방식을 선택합니다.", + "LABEL_SETTINGS_GROUP_PROFILE": "프로필 및 테마", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "사용자 이름, 테마 및 언어 설정을 변경합니다.", + "LABEL_SETTINGS_GROUP_SYNC": "재생 및 동기화", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "재생, 알림 및 오디오 설정을 맞춤설정합니다." } diff --git a/extension/locales/nl.json b/extension/locales/nl.json index 96bfc50..dd2a201 100644 --- a/extension/locales/nl.json +++ b/extension/locales/nl.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Afstandsbediening", "BTN_COPY_INVITE": "📋 Uitnodigingslink", "BTN_COPY_INVITE_TOOLTIP": "Uitnodigingslink kopiëren", - "BTN_PLAY": "▶ Afspelen", + "BTN_PLAY": "Afspelen", "BTN_PLAY_TOOLTIP": "Stuur een afspeelopdracht naar iedereen", - "BTN_PAUSE": "⏸ Pauzeren", + "BTN_PAUSE": "Pauzeren", "BTN_PAUSE_TOOLTIP": "Stuur een pauzeeropdracht naar iedereen", - "BTN_SYNC": "⚡ SYNC", + "BTN_SYNC": "Sync", "BTN_SYNC_TOOLTIP": "Dwing alle gebruikers om te synchroniseren", "OPTION_JUMP_TO_OTHERS": "Spring naar anderen", "OPTION_JUMP_TO_ME": "Spring naar mij", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Past audio-effecten zoals compressie toe op video-afspelen", "AUDIO_OPEN_SETTINGS": "Openen", "NEW_FEATURE_AUDIO": "Nieuw: Audioverwerking — probeer de compressor!", + "NEW_FEATURE_THEME": "Nieuw: Thema's — schakel naar de klassieke look of minimaal grafiet.", "AUDIO_BACK": "← Terug", "AUDIO_PAGE_TITLE": "Audio-instellingen", "AUDIO_MASTER_TOGGLE": "Audioverwerking", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Tabtitel verzenden", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Stuurt de titel van het geselecteerde browsertabblad naar de kamer.", "LABEL_MEDIA_TITLE_PRIVACY": "Mediatitel", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Kies hoe mediatitels naar de kamer worden gestuurd." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Kies hoe mediatitels naar de kamer worden gestuurd.", + "LABEL_SETTINGS_GROUP_PROFILE": "Profiel & Weergave", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Wijzig uw gebruikersnaam, thema en taalvoorkeuren.", + "LABEL_SETTINGS_GROUP_SYNC": "Afspelen & Sync", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Pas afspelen, meldingen en audiovoorkeuren aan." } diff --git a/extension/locales/pl.json b/extension/locales/pl.json index ceb4aa1..dde3e3d 100644 --- a/extension/locales/pl.json +++ b/extension/locales/pl.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Zdalne sterowanie", "BTN_COPY_INVITE": "📋 Link do zaproszenia", "BTN_COPY_INVITE_TOOLTIP": "Kopiuj link zaproszenia", - "BTN_PLAY": "▶ Odtwórz", + "BTN_PLAY": "Odtwórz", "BTN_PLAY_TOOLTIP": "Wyślij polecenie odtwarzania do wszystkich", - "BTN_PAUSE": "⏸ Pauza", + "BTN_PAUSE": "Pauza", "BTN_PAUSE_TOOLTIP": "Wyślij polecenie pauzy do wszystkich", - "BTN_SYNC": "⚡ SYNC", + "BTN_SYNC": "Sync", "BTN_SYNC_TOOLTIP": "Wymuś synchronizację wszystkich użytkowników", "OPTION_JUMP_TO_OTHERS": "Skocz do innych", "OPTION_JUMP_TO_ME": "Skocz do mnie", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Stosuje efekty dźwiękowe, takie jak kompresja, do odtwarzania wideo", "AUDIO_OPEN_SETTINGS": "Otwórz", "NEW_FEATURE_AUDIO": "Nowość: Przetwarzanie dźwięku — wypróbuj kompresor!", + "NEW_FEATURE_THEME": "Nowość: Motywy — przełącz na klasyczny wygląd lub minimalny grafit.", "AUDIO_BACK": "← Wstecz", "AUDIO_PAGE_TITLE": "Ustawienia dźwięku", "AUDIO_MASTER_TOGGLE": "Przetwarzanie dźwięku", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Wysyłaj tytuł karty", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Wysyła do pokoju tytuł wybranej karty przeglądarki.", "LABEL_MEDIA_TITLE_PRIVACY": "Tytuł multimediów", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Wybierz, jak tytuły multimediów są wysyłane do pokoju." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Wybierz, jak tytuły multimediów są wysyłane do pokoju.", + "LABEL_SETTINGS_GROUP_PROFILE": "Profil i Wygląd", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Zmień swoją nazwę użytkownika, motyw i preferencje językowe.", + "LABEL_SETTINGS_GROUP_SYNC": "Odtwarzanie i Sync", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Dostosuj odtwarzanie, powiadomienia i preferencje audio." } diff --git a/extension/locales/pt-BR.json b/extension/locales/pt-BR.json index 5556955..1d07782 100644 --- a/extension/locales/pt-BR.json +++ b/extension/locales/pt-BR.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Controle remoto", "BTN_COPY_INVITE": "📋 Link de convite", "BTN_COPY_INVITE_TOOLTIP": "Copiar link de convite", - "BTN_PLAY": "▶ Reproduzir", + "BTN_PLAY": "Reproduzir", "BTN_PLAY_TOOLTIP": "Iniciar a reprodução para todos", - "BTN_PAUSE": "⏸ Pausar", + "BTN_PAUSE": "Pausar", "BTN_PAUSE_TOOLTIP": "Pausar o vídeo para todos", - "BTN_SYNC": "⚡ SINCRO", + "BTN_SYNC": "Sincro", "BTN_SYNC_TOOLTIP": "Forçar todos os usuários a sincronizar", "OPTION_JUMP_TO_OTHERS": "Ir para a posição dos outros", "OPTION_JUMP_TO_ME": "Trazer para a minha posição", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Aplica efeitos de áudio (como compressão) aos vídeos", "AUDIO_OPEN_SETTINGS": "Abrir", "NEW_FEATURE_AUDIO": "Novo: Processamento de áudio — experimente o compressor!", + "NEW_FEATURE_THEME": "Novo: Temas — mude para o visual clássico ou grafite minimalista.", "AUDIO_BACK": "← Voltar", "AUDIO_PAGE_TITLE": "Configurações de áudio", "AUDIO_MASTER_TOGGLE": "Processamento de áudio", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Enviar título da aba", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Envia para a sala o título da aba selecionada do navegador.", "LABEL_MEDIA_TITLE_PRIVACY": "Título da mídia", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Escolha como os títulos de mídia são enviados para a sala." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Escolha como os títulos de mídia são enviados para a sala.", + "LABEL_SETTINGS_GROUP_PROFILE": "Perfil e Aparência", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Altere o seu nome de usuário, tema e preferências de idioma.", + "LABEL_SETTINGS_GROUP_SYNC": "Reprodução e Sincronização", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Personalize a reprodução, as notificações e as preferências de áudio." } diff --git a/extension/locales/pt.json b/extension/locales/pt.json index b6426dd..b360401 100644 --- a/extension/locales/pt.json +++ b/extension/locales/pt.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Controlo Remoto", "BTN_COPY_INVITE": "📋 Link de Convite", "BTN_COPY_INVITE_TOOLTIP": "Copiar Link de Convite", - "BTN_PLAY": "▶ Reproduzir", + "BTN_PLAY": "Reproduzir", "BTN_PLAY_TOOLTIP": "Iniciar a reprodução para todos", - "BTN_PAUSE": "⏸ Pausa", + "BTN_PAUSE": "Pausa", "BTN_PAUSE_TOOLTIP": "Pausar o vídeo para todos", - "BTN_SYNC": "⚡ SINCRO", + "BTN_SYNC": "Sincro", "BTN_SYNC_TOOLTIP": "Forçar sincronização de todos os utilizadores", "OPTION_JUMP_TO_OTHERS": "Ir para a posição dos outros", "OPTION_JUMP_TO_ME": "Trazer para a minha posição", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Aplica efeitos de áudio (como compressão) aos vídeos", "AUDIO_OPEN_SETTINGS": "Abrir", "NEW_FEATURE_AUDIO": "Novo: Processamento de áudio — experimente o compressor!", + "NEW_FEATURE_THEME": "Novo: Temas — mude para o visual clássico ou grafite minimalista.", "AUDIO_BACK": "← Voltar", "AUDIO_PAGE_TITLE": "Configurações de Áudio", "AUDIO_MASTER_TOGGLE": "Processamento de Áudio", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Enviar título do separador", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Envia para a sala o título do separador do navegador selecionado.", "LABEL_MEDIA_TITLE_PRIVACY": "Título multimédia", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Escolhe como os títulos multimédia são enviados para a sala." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Escolhe como os títulos multimédia são enviados para a sala.", + "LABEL_SETTINGS_GROUP_PROFILE": "Perfil e Aparência", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Altere o seu nome de utilizador, tema e preferências de idioma.", + "LABEL_SETTINGS_GROUP_SYNC": "Reprodução e Sincronização", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Personalize a reprodução, as notificações e as preferências de áudio." } diff --git a/extension/locales/ru.json b/extension/locales/ru.json index 9660bd3..7aa0e35 100644 --- a/extension/locales/ru.json +++ b/extension/locales/ru.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Дистанционное управление", "BTN_COPY_INVITE": "📋 Ссылка", "BTN_COPY_INVITE_TOOLTIP": "Копировать ссылку-приглашение", - "BTN_PLAY": "▶ Запуск", + "BTN_PLAY": "Запуск", "BTN_PLAY_TOOLTIP": "Отправить команду воспроизведения всем", - "BTN_PAUSE": "⏸ Пауза", + "BTN_PAUSE": "Пауза", "BTN_PAUSE_TOOLTIP": "Отправить команду паузы всем", - "BTN_SYNC": "⚡ СИНХРО", + "BTN_SYNC": "Синхро", "BTN_SYNC_TOOLTIP": "Принудительно синхронизировать всех участников", "OPTION_JUMP_TO_OTHERS": "Перейти к другим", "OPTION_JUMP_TO_ME": "Синхронизировать по мне", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Применяет аудиоэффекты, такие как сжатие, к воспроизведению видео", "AUDIO_OPEN_SETTINGS": "Открыть", "NEW_FEATURE_AUDIO": "Новое: Обработка звука — попробуйте компрессор!", + "NEW_FEATURE_THEME": "Новое: Темы — переключитесь на классический вид или минимальный графит.", "AUDIO_BACK": "← Назад", "AUDIO_PAGE_TITLE": "Настройки звука", "AUDIO_MASTER_TOGGLE": "Обработка звука", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Отправлять название вкладки", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Отправляет в комнату название выбранной вкладки браузера.", "LABEL_MEDIA_TITLE_PRIVACY": "Название медиа", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Выберите, как названия медиа отправляются в комнату." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Выберите, как названия медиа отправляются в комнату.", + "LABEL_SETTINGS_GROUP_PROFILE": "Профиль и Оформление", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Измените имя пользователя, тему и настройки языка.", + "LABEL_SETTINGS_GROUP_SYNC": "Воспроизведение и Синхронизация", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Настройте воспроизведение, уведомления и параметры звука." } diff --git a/extension/locales/tr.json b/extension/locales/tr.json index 2cb5101..b5b29da 100644 --- a/extension/locales/tr.json +++ b/extension/locales/tr.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Uzaktan Kumanda", "BTN_COPY_INVITE": "📋 Davet Linki", "BTN_COPY_INVITE_TOOLTIP": "Davet Linkini Kopyala", - "BTN_PLAY": "▶ Oynat", + "BTN_PLAY": "Oynat", "BTN_PLAY_TOOLTIP": "Herkese Oynat komutu gönder", - "BTN_PAUSE": "⏸ Duraklat", + "BTN_PAUSE": "Duraklat", "BTN_PAUSE_TOOLTIP": "Herkese Duraklat komutu gönder", - "BTN_SYNC": "⚡ SYNC", + "BTN_SYNC": "Sync", "BTN_SYNC_TOOLTIP": "Tüm kullanıcıları senkronize etmeye zorla", "OPTION_JUMP_TO_OTHERS": "Diğerlerine Atla", "OPTION_JUMP_TO_ME": "Bana Atla", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Video oynatımına sıkıştırma gibi ses efektleri uygular", "AUDIO_OPEN_SETTINGS": "Aç", "NEW_FEATURE_AUDIO": "Yeni: Ses işleme — kompresörü deneyin!", + "NEW_FEATURE_THEME": "Yeni: Temalar — klasik görünüme veya minimal grafite geçin.", "AUDIO_BACK": "← Geri", "AUDIO_PAGE_TITLE": "Ses ayarları", "AUDIO_MASTER_TOGGLE": "Ses işleme", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Sekme başlığını gönder", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Seçili tarayıcı sekmesinin başlığını odaya gönderir.", "LABEL_MEDIA_TITLE_PRIVACY": "Medya başlığı", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Medya başlıklarının odaya nasıl gönderileceğini seçin." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Medya başlıklarının odaya nasıl gönderileceğini seçin.", + "LABEL_SETTINGS_GROUP_PROFILE": "Profil ve Görünüm", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Kullanıcı adınızı, temanızı ve dil tercihlerinizi değiştirin.", + "LABEL_SETTINGS_GROUP_SYNC": "Oynatma ve Eşitleme", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Oynatma, bildirim ve ses tercihlerini özelleştirin." } diff --git a/extension/locales/uk.json b/extension/locales/uk.json index 3de331b..22aa7d5 100644 --- a/extension/locales/uk.json +++ b/extension/locales/uk.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "Пульт дистанційного керування", "BTN_COPY_INVITE": "📋 Посилання на запрошення", "BTN_COPY_INVITE_TOOLTIP": "Копіювати посилання для запрошення", - "BTN_PLAY": "▶ Відтворити", + "BTN_PLAY": "Відтворити", "BTN_PLAY_TOOLTIP": "Надішліть усім команду Play", - "BTN_PAUSE": "⏸ Пауза", + "BTN_PAUSE": "Пауза", "BTN_PAUSE_TOOLTIP": "Надішліть команду паузи всім", - "BTN_SYNC": "⚡ СИНХР", + "BTN_SYNC": "Синхр", "BTN_SYNC_TOOLTIP": "Змусити всіх користувачів синхронізуватися", "OPTION_JUMP_TO_OTHERS": "Перейти до інших", "OPTION_JUMP_TO_ME": "Перейти до мене", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "Застосуйте звукові ефекти, такі як стиснення, до відтворення відео", "AUDIO_OPEN_SETTINGS": "Відкрити", "NEW_FEATURE_AUDIO": "Новинка: обробка звуку — спробуйте компресор!", + "NEW_FEATURE_THEME": "Нове: Теми — перемкніться на класичний вигляд або мінімальний графіт.", "AUDIO_BACK": "← Назад", "AUDIO_PAGE_TITLE": "Параметри звуку", "AUDIO_MASTER_TOGGLE": "Обробка аудіо", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "Надсилати назву вкладки", "LABEL_SEND_TAB_TITLE_TOOLTIP": "Надсилає до кімнати назву вибраної вкладки браузера.", "LABEL_MEDIA_TITLE_PRIVACY": "Назва медіа", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Виберіть, як назви медіа надсилати до кімнати." + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "Виберіть, як назви медіа надсилати до кімнати.", + "LABEL_SETTINGS_GROUP_PROFILE": "Профіль та Вигляд", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "Змініть ім'я користувача, тему та мовні налаштування.", + "LABEL_SETTINGS_GROUP_SYNC": "Відтворення та Синхронізація", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "Налаштуйте відтворення, сповіщення та параметри звуку." } diff --git a/extension/locales/zh.json b/extension/locales/zh.json index 7330d74..dda1b53 100644 --- a/extension/locales/zh.json +++ b/extension/locales/zh.json @@ -70,11 +70,11 @@ "LABEL_REMOTE_CONTROL": "遥控", "BTN_COPY_INVITE": "📋 邀请链接", "BTN_COPY_INVITE_TOOLTIP": "复制邀请链接", - "BTN_PLAY": "▶ 播放", + "BTN_PLAY": "播放", "BTN_PLAY_TOOLTIP": "向所有人发送播放命令", - "BTN_PAUSE": "⏸ 暂停", + "BTN_PAUSE": "暂停", "BTN_PAUSE_TOOLTIP": "向所有人发送暂停命令", - "BTN_SYNC": "⚡ 同步", + "BTN_SYNC": "同步", "BTN_SYNC_TOOLTIP": "强制所有用户同步", "OPTION_JUMP_TO_OTHERS": "跳转到其他", "OPTION_JUMP_TO_ME": "跳到我这里", @@ -209,6 +209,7 @@ "LABEL_AUDIO_PROCESSING_TOOLTIP": "将压缩等音频效果应用于视频播放", "AUDIO_OPEN_SETTINGS": "打开", "NEW_FEATURE_AUDIO": "新功能:音频处理 - 尝试压缩器!", + "NEW_FEATURE_THEME": "新功能:主题 — 切换到经典外观或极简石墨风格。", "AUDIO_BACK": "← 返回", "AUDIO_PAGE_TITLE": "音频设置", "AUDIO_MASTER_TOGGLE": "音频处理", @@ -238,5 +239,9 @@ "LABEL_SEND_TAB_TITLE": "发送标签页标题", "LABEL_SEND_TAB_TITLE_TOOLTIP": "将所选浏览器标签页的标题发送到房间。", "LABEL_MEDIA_TITLE_PRIVACY": "媒体标题", - "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "选择媒体标题发送到房间的方式。" + "LABEL_MEDIA_TITLE_PRIVACY_TOOLTIP": "选择媒体标题发送到房间的方式。", + "LABEL_SETTINGS_GROUP_PROFILE": "个人资料与外观", + "LABEL_SETTINGS_GROUP_PROFILE_TOOLTIP": "更改您的用户名、主题和语言首选项。", + "LABEL_SETTINGS_GROUP_SYNC": "播放与同步", + "LABEL_SETTINGS_GROUP_SYNC_TOOLTIP": "自定义播放、通知和音频首选项。" } diff --git a/extension/popup.html b/extension/popup.html index df2188c..6f1b518 100644 --- a/extension/popup.html +++ b/extension/popup.html @@ -1,8 +1,9 @@ - + KoalaSync +