Compare commits

..

11 Commits

Author SHA1 Message Date
Koala 82c09a5328 fix(popup): use hostname-aware domain matching to prevent false positives
The noise filter used naive String.includes() on the full URL, causing
blacklist entry 'x.com' to match netflix.com (since 'netflix.com'
contains 'x.com' as substring). This made Netflix tabs disappear when
the filter was enabled.

Fixed by extracting the URL hostname and matching at domain boundaries:
- Domain entries (x.com): hostname === domain || endsWith('.' + domain)
- Prefix entries (amazon.): startsWith || includes('.' + domain)
- Keyword entries (jira): substring fallback on full URL

Release v1.7.5
2026-05-25 13:26:01 +02:00
Koala 2fbeafeb3f fix(website): 'any' → 'almost any' for honesty 2026-05-25 13:01:59 +02:00
Koala 80f8c821cb docs: clarify that website and documentation changes do not need release tags 2026-05-25 13:00:38 +02:00
GitHub Action 7d3965a9fd chore(release): update versions to v1.7.4 [skip ci] 2026-05-25 10:59:16 +00:00
Koala 1aca6c37d4 fix(website): replace misleading 'local MP4s' with accurate platform list
The hero subtitle claimed support for 'local MP4s' which makes no sense
for a browser-based synchronization tool (local files can't be synced
across different computers). Changed to accurately reflect actual platform
support: YouTube, Twitch, Netflix, and any website with a video player.
2026-05-25 12:59:00 +02:00
GitHub Action aa740592dd chore(release): update versions to v1.7.3 [skip ci] 2026-05-25 10:52:55 +00:00
Koala 6f8bcf8478 fix(popup): replace innerHTML with DOM API to fix Firefox warnings
Firefox flagged unsafe innerHTML assignments:
- renderEmpty() container.innerHTML with template literal
- renderOnboardingStep() dots.innerHTML with mapped content
- updateLastActionUI() lastActionCard.innerHTML static string
- refreshRooms click handler publicRooms.innerHTML

Replaced all with createElement + textContent/replaceChildren.
2026-05-25 12:52:33 +02:00
GitHub Action 807a620fe9 chore(release): update versions to v1.7.2 [skip ci] 2026-05-25 10:46:16 +00:00
Koala 6e138f51d3 docs: enforce tag immutability and force-push policy in AI_INIT.md
- Tags are permanent once pushed; never reuse or move existing tags
- If a release is missing a fix, increment version and create new tag
- Force pushing (branches or tags) requires explicit user confirmation
- Added to Section 10 (Release Workflow) for AI agents
2026-05-25 12:43:36 +02:00
GitHub Action ed50e354ab chore(release): update versions to v1.7.0 [skip ci] 2026-05-25 10:40:34 +00:00
Koala 48bd503b5c fix(popup): seed lastKnownPeers in init() to prevent false join toasts
init() called updatePeerList() directly but never set lastKnownPeers.
On the first PEER_UPDATE message (heartbeat, play/pause, any state
change), detectPeerChanges() saw lastKnownPeers=[] and treated ALL
peers including self as newly joined — triggering 'name joined the room'
toast on every action while the popup was open.
2026-05-25 12:40:05 +02:00
6 changed files with 56 additions and 19 deletions
+9
View File
@@ -107,9 +107,18 @@ Before starting any task, committing, or pushing, you **MUST** run `git pull --r
- **ESLint Validation**: Run `npm run lint` (or `npx eslint .`). The output must show **zero errors and zero warnings**. ESLint is configured to catch undefined variables, unused vars, unreachable code, and other semantic issues. **NEVER** commit or push code that fails this check.
2. Commit all verified code changes and push to `main`.
3. Create and push a new tag. **MANDATORY**: Tags MUST start with a `v` (e.g., `v1.4.0`). The GitHub Actions release workflow is strictly configured to ignore any tags without the `v` prefix.
- **🚫 TAG IMMUTABILITY**: Once a tag is pushed to `origin`, it is **PERMANENT**. You MUST **NEVER** reuse, move, or force-push an existing tag — not even to "fix" a mistake. If a release is missing a fix, increment the version and create a **new** tag (e.g., `v1.7.0``v1.7.1`). Tags are immutable identifiers; moving them breaks CI pipelines, corrupts the release history, and causes unreproducible builds.
- **🚫 WHEN NOT TO TAG**: Do NOT create a release tag for changes that do NOT affect the shipped extension or server artifacts. Website text changes, documentation updates (`.md` files), and landing page content do NOT require a version tag. Tags trigger the full CI pipeline (Docker build, extension packaging, GitHub Release) — running this for a typo fix wastes CI resources and creates meaningless releases. Only tag when extension code (`extension/`), server code (`server/`), or shared protocol constants (`shared/`) have changed.
4. The CI will extract the version from the tag (e.g., `v1.4.0``1.4.0`), inject it into all source files, build the extension artifacts, publish the Docker image, and create a GitHub Release.
5. Verify the release builds on GitHub Actions.
### 🚫 Force Push Policy
> [!CAUTION]
> **Force pushing (`git push --force` or `git push -f`) is FORBIDDEN without explicit user confirmation.**
> - If a push is rejected due to a non-fast-forward conflict, you **MUST** run `git pull --rebase` first.
> - If a force push is absolutely required (e.g., squashed history, amended commits), you **MUST** ask the user for explicit permission with a clear explanation of why it's necessary. Never force-push autonomously.
> - This applies to both branches (`main`) and **tags** (see Tag Immutability above). Force-pushing tags is doubly destructive. Never do it.
### Adding a Protocol Event
1. Add the event name to `shared/constants.js`.
2. Run the build script (`node scripts/build-extension.js`).
+1 -1
View File
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "KoalaSync",
"version": "1.7.0",
"version": "1.7.5",
"description": "Watch party extension to synchronize video playback on YouTube, Twitch, Netflix, and HTML5 sites in real-time with friends.",
"permissions": [
"storage",
+41 -13
View File
@@ -109,6 +109,7 @@ async function init() {
localPeerId = res.peerId;
applyConnectionStatus(res.status);
updatePeerList(res.peers);
lastKnownPeers = res.peers || [];
if (res.lastActionState) updateLastActionUI(res.lastActionState, res.peers);
// Populate Tabs using the background's targetTabId
@@ -170,7 +171,11 @@ function updateUI(roomId, password, useCustomServer = false, serverUrl = '') {
function updateLastActionUI(state, peers) {
if (!state || !state.action) {
elements.lastActionCard.innerHTML = '<div style="text-align:center; color: var(--text-muted); font-size: 10px;">No recent commands</div>';
elements.lastActionCard.replaceChildren();
const el = document.createElement('div');
el.style.cssText = 'text-align:center; color: var(--text-muted); font-size: 10px;';
el.textContent = 'No recent commands';
elements.lastActionCard.appendChild(el);
return;
}
@@ -303,13 +308,21 @@ function renderEmpty(container, type) {
rooms: { icon: '\u{1F50D}', title: 'No active rooms', hint: 'Create a room or refresh to find public ones' }
};
const state = states[type] || { icon: '', title: '', hint: '' };
container.innerHTML = `
<div style="text-align:center; padding:16px 8px; color:var(--text-muted);">
<div style="font-size:24px; margin-bottom:6px;">${state.icon}</div>
<div style="font-size:12px; font-weight:600; margin-bottom:4px;">${state.title}</div>
<div style="font-size:10px; opacity:0.7;">${state.hint}</div>
</div>
`;
const wrapper = document.createElement('div');
wrapper.style.cssText = 'text-align:center; padding:16px 8px; color:var(--text-muted);';
const iconDiv = document.createElement('div');
iconDiv.style.cssText = 'font-size:24px; margin-bottom:6px;';
iconDiv.textContent = state.icon;
const titleDiv = document.createElement('div');
titleDiv.style.cssText = 'font-size:12px; font-weight:600; margin-bottom:4px;';
titleDiv.textContent = state.title;
const hintDiv = document.createElement('div');
hintDiv.style.cssText = 'font-size:10px; opacity:0.7;';
hintDiv.textContent = state.hint;
wrapper.appendChild(iconDiv);
wrapper.appendChild(titleDiv);
wrapper.appendChild(hintDiv);
container.replaceChildren(wrapper);
}
function updatePeerList(peers) {
@@ -518,7 +531,15 @@ async function populateTabs(providedPeers = null, providedTargetTabId = null) {
if (!tab.url || tab.url.startsWith('chrome://')) return false;
if (isFilterActive && tab.id !== parseInt(currentTargetTabId)) {
const urlStr = tab.url.toLowerCase();
if (BLACKLIST_DOMAINS.some(d => urlStr.includes(d.toLowerCase()))) return false;
if (BLACKLIST_DOMAINS.some(d => {
const domain = d.toLowerCase();
try {
const hostname = new URL(tab.url).hostname.toLowerCase();
if (domain.endsWith('.')) return hostname.startsWith(domain) || hostname.includes('.' + domain);
if (domain.includes('.')) return hostname === domain || hostname.endsWith('.' + domain);
} catch {}
return urlStr.includes(domain);
})) return false;
}
return true;
});
@@ -924,7 +945,11 @@ elements.createRoomBtn.addEventListener('click', () => {
});
elements.refreshRooms.addEventListener('click', () => {
elements.publicRooms.innerHTML = '<div style="text-align:center; padding: 10px; color:var(--text-muted);">Refreshing...</div>';
elements.publicRooms.replaceChildren();
const el = document.createElement('div');
el.style.cssText = 'text-align:center; padding: 10px; color:var(--text-muted);';
el.textContent = 'Refreshing...';
elements.publicRooms.appendChild(el);
chrome.runtime.sendMessage({ type: 'GET_ROOM_LIST' });
});
@@ -1343,9 +1368,12 @@ function renderOnboardingStep() {
title.textContent = step.title;
text.textContent = step.text;
dots.innerHTML = onboardingSteps.map((_, i) =>
`<div style="width:8px; height:8px; border-radius:50%; background:${i === onboardingStep ? 'var(--accent)' : '#475569'};"></div>`
).join('');
dots.replaceChildren();
onboardingSteps.forEach((_, i) => {
const dot = document.createElement('div');
dot.style.cssText = `width:8px; height:8px; border-radius:50%; background:${i === onboardingStep ? 'var(--accent)' : '#475569'};`;
dots.appendChild(dot);
});
nextBtn.textContent = onboardingStep === onboardingSteps.length - 1 ? 'Done!' : 'Next';
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "koalasync",
"version": "1.7.0",
"version": "1.7.5",
"description": "KoalaSync Build Scripts",
"private": true,
"scripts": {
+2 -2
View File
@@ -54,8 +54,8 @@
<span lang="de">Gemeinsam schauen.<br>Perfekt synchron.</span>
</h1>
<h2 class="hero-subtitle" data-reveal>
<span lang="en">A free, open-source watch party extension for YouTube, Twitch, and local MP4s. Built for reliable synchronization and data sovereignty.</span>
<span lang="de">Eine kostenlose, quelloffene Watch-Party-Erweiterung für YouTube, Twitch und lokale MP4s. Entwickelt für zuverlässige Synchronisation und Datenhoheit.</span>
<span lang="en">A free, open-source watch party extension for YouTube, Twitch, Netflix, and almost any website with a video player. Built for reliable synchronization and data sovereignty.</span>
<span lang="de">Eine kostenlose, quelloffene Watch-Party-Erweiterung für YouTube, Twitch, Netflix und fast jede Webseite mit einem Videoplayer. Entwickelt für zuverlässige Synchronisation und Datenhoheit.</span>
</h2>
<div class="cta-group" data-reveal>
<a href="https://chromewebstore.google.com/detail/koalasync/obbnmkmlaaddodakcbdljknjpagklifc" class="btn btn-primary">
+2 -2
View File
@@ -1,4 +1,4 @@
{
"version": "1.7.0",
"date": "2026-05-25T10:30:48Z"
"version": "1.7.5",
"date": "2026-05-25T14:00:00Z"
}