From 97db2d74a5985f2f352aa214a8b5d755c164ad00 Mon Sep 17 00:00:00 2001 From: Kotmin <70173732+Kotmin@users.noreply.github.com> Date: Mon, 10 Aug 2026 14:17:24 +0200 Subject: [PATCH 1/7] docs(keyboard): lead spec with Configuration, add remap examples Move Configuration to the top of the keyboard-shortcuts spec so the customization options are found before the command reference. Add prefix-change, chord-remap, and non-QWERTY remap examples, and note the default chords are chosen to avoid colliding with YouTube's own single-key shortcuts. --- docs/specs/keyboard-shortcuts.md | 150 +++++++++++++++++-------------- 1 file changed, 82 insertions(+), 68 deletions(-) diff --git a/docs/specs/keyboard-shortcuts.md b/docs/specs/keyboard-shortcuts.md index dc6861b..b8cbf64 100644 --- a/docs/specs/keyboard-shortcuts.md +++ b/docs/specs/keyboard-shortcuts.md @@ -4,6 +4,88 @@ Status: implemented 2026-07-17 (all editions; shared source). Playlist picker (`Ctrl+A, Shift+P`) added 2026-08-07 (issue #16) — see below; blocked on two unverified DOM assumptions, see `docs/ai/questions-for-K.md`. +## Configuration + +Everything: prefix, chords, `shiftChords`, speeds, `homeUrl`, is +user-remappable via settings, no code change needed. The default chords are +deliberately chosen to never collide with YouTube's own single-key shortcuts +(`k`, `j`, `l`, `f`, `m`, digits, arrows, …, see `RESERVED_YOUTUBE_KEYS`): +they only ever fire as a **prefix + key combo**, so the same letter that's a +YouTube shortcut on its own can safely be a chord after the prefix. The +prefix's fallback also adapts to the platform: macOS resolves to `⌘A` +(`{ key: 'a', ctrl: false, meta: true }`) automatically, Windows/Linux to +`Ctrl+A`, detected via `isMacPlatform()` (`src/core/platform.js`) and only +applied when no `keymap.prefix` is already stored (an existing stored value, +valid or user-set, always wins; `DEFAULT_KEYMAP` itself stays OS-agnostic). + +### Remap examples + +Overrides live in `browser.storage.local` under +`videodefaults_settings.keymap`, sanitized by `normalizeKeymap()` on every +read, and merged onto the defaults key-by-key: a partial override only +touches what you set, everything else keeps working: + +- **Change the prefix**, e.g. to `Ctrl+Space` instead of `Ctrl+A`: + ```json + { "prefix": { "key": " ", "ctrl": true, "meta": false } } + ``` +- **Remap a single chord**, e.g. move "go home" off `y` onto `g`, leaving + every other default chord (`o`, `p`, `v`, `b`, `n`, `h`) untouched: + ```json + { "chords": { "g": "go-home" } } + ``` +- **Non-QWERTY or additional keyboard**: `key` matches `evt.key`, the + character your active layout actually produces, not the physical key + position. On an AZERTY layout the physical `Ctrl+A` key types `q`, so + either set `prefix.key` to whatever character that key produces on your + layout, or point it at a different physical key entirely: + ```json + { "prefix": { "key": "q", "ctrl": true, "meta": false } } + ``` + +Full validation rules: `prefix` must include `ctrl` or `meta` (a plain-key +prefix is rejected so single-key YouTube shortcuts can never be shadowed); +`chords`/`shiftChords` keys must be a single a-z0-9 character mapping to a +known command; `homeUrl` must be `https://*.youtube.com`; `speeds` values are +validated by `validateSpeed()` (`src/core/speed.js`) and fall back to that +command's default otherwise. Settings changes apply live (storage listener); +no reload needed. + +### Defaults reference + +`DEFAULT_KEYMAP` and the default speed-shortcut values are sourced from the +checked-in `src/core/shortcuts.config.json`, sanitized at module load through +the same `normalizeKeymap()` / `normalizeSpeedShortcuts()` functions used for +storage-provided overrides (`src/core/keyboard-shortcuts.js`): retuning a +default is a one-file edit: + +```json +{ + "prefix": { "key": "a", "ctrl": true, "meta": false }, + "chords": { + "o": "show-jump-labels", + "p": "show-queue-labels", + "y": "go-home", + "v": "set-speed-1", + "b": "set-speed-2", + "n": "set-speed-3", + "h": "toggle-auto-apply" + }, + "shiftChords": { + "p": "show-playlist-labels" + }, + "homeUrl": "https://www.youtube.com/", + "speeds": { + "set-speed-1": 1, + "set-speed-2": 1.5, + "set-speed-3": 2.0 + } +} +``` + +No options UI yet: edit via storage or wait for the options page +(see `docs/ai/questions-for-K.md` Q8). + ## Model A prefix chord, like tmux: press the **prefix** (default `Ctrl+A`), then a @@ -182,74 +264,6 @@ real site today** (both flagged `UNVERIFIED` in source, see shape in `driveCreateNewPlaylist` are best-effort assumptions, not independently DOM-captured. -## Configuration - -`DEFAULT_KEYMAP` and the default speed-shortcut values are sourced from the -checked-in `src/core/shortcuts.config.json`, sanitized at module load through -the same `normalizeKeymap()` / `normalizeSpeedShortcuts()` functions used for -storage-provided overrides (`src/core/keyboard-shortcuts.js`) — retuning a -shortcut key or a speed value is a one-file edit, no code change needed: - -```json -{ - "prefix": { "key": "a", "ctrl": true, "meta": false }, - "chords": { - "o": "show-jump-labels", - "p": "show-queue-labels", - "y": "go-home", - "v": "set-speed-1", - "b": "set-speed-2", - "n": "set-speed-3", - "h": "toggle-auto-apply" - }, - "shiftChords": { - "p": "show-playlist-labels" - }, - "homeUrl": "https://www.youtube.com/", - "speeds": { - "set-speed-1": 1, - "set-speed-2": 1.5, - "set-speed-3": 2.0 - } -} -``` - -The keymap can also be overridden per-install in settings -(`browser.storage.local`, key `videodefaults_settings.keymap`), sanitized by -the same `normalizeKeymap()` on every read: - -- `prefix` must include `ctrl` or `meta` (macOS users can set - `{ "key": "a", "ctrl": false, "meta": true }` for `⌘A`); a plain-key prefix - is rejected so single-key YouTube shortcuts can never be shadowed. - `RESERVED_YOUTUBE_KEYS` documents YouTube's own bindings. -- `chords` maps single keys to known commands; unknown commands are dropped. - A stored override is merged onto the defaults key-by-key, not swapped in - wholesale — a partial override (e.g. only remapping `o`) keeps every other - default chord (`p`, `y`, `v`, `b`, `n`, `h`) working. -- `shiftChords` is a separate map, same merge/validation rules as `chords`, - looked up only when the chord key is pressed with `Shift` held — pressing - `p` with Shift does **not** fall back to the unshifted `chords.p` - (`show-queue-labels`) if no `shiftChords.p` entry exists; it's simply - unbound. -- `homeUrl` must be an `https://*.youtube.com` URL (blocks `javascript:` and - third-party redirect targets). -- `speeds` maps each `set-speed-*` command to a numeric value validated by - `validateSpeed()` (`src/core/speed.js`); out-of-range or non-numeric entries - fall back to that command's default (see table above) — exposed as - `SPEED_SHORTCUTS`. -- Settings changes apply live (storage listener); no reload needed. - -The prefix's fallback default is platform-aware: on macOS it resolves to -`{ key: 'a', ctrl: false, meta: true }` (`⌘A`) instead of the OS-agnostic -`Ctrl+A`, detected via `isMacPlatform()` (`src/core/platform.js`) and threaded -through as the `isMac` parameter on `normalizeKeymap()` / `applyDefaults()` / -`migrateSettings()`. This only applies when no `keymap.prefix` is already -stored — an existing stored value (valid or user-set) always wins, and -`DEFAULT_KEYMAP` itself stays OS-agnostic (`isMac` defaults to `false`). - -No options UI yet — edit via storage or wait for the options page -(see `docs/ai/questions-for-K.md` Q8). - ## Verification - Unit: `tests/unit/keyboard-shortcuts.test.js` (state machine, keymap From 101d377649592d5b73bdcace7b340c3b196a4e72 Mon Sep 17 00:00:00 2001 From: Kotmin <70173732+Kotmin@users.noreply.github.com> Date: Mon, 10 Aug 2026 14:17:46 +0200 Subject: [PATCH 2/7] docs(readme): rename Status to Browser Support, link Edge/Arc listings Point Edge and Arc at the Chrome Web Store listing directly (both are Chromium-based, confirmed via Microsoft/Arc docs) instead of a bare dash, and note Edge needs 'Allow extensions from other stores' enabled first while Arc installs without extra setup. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0c498e9..8bce6c9 100644 --- a/README.md +++ b/README.md @@ -27,15 +27,16 @@ Full reference: [docs/keyboard-quickstart.md](docs/keyboard-quickstart.md). Edit `src/core/shortcuts.config.json` to change the prefix key, any chord binding, or the three preset speed values. Speed values must fall within the allowed range (`0.25`–`4.0`), but only use values YouTube itself will actually honor for your account — e.g. speeds above `2x` are a YouTube Premium feature, so a non-Premium account silently caps at `2x` regardless of what's configured here. Respect the platform's own limits when picking a value. Details: [docs/specs/keyboard-shortcuts.md](docs/specs/keyboard-shortcuts.md). -## Status +## Browser Support -Active development on `dev` branch. Also works on Chromium-based browsers. +Active development on `dev` branch. | Browser | Status | Listing | | --- | --- | --- | | 🦊 Firefox (AMO) | Published | [addons.mozilla.org/.../videodefaults](https://addons.mozilla.org/en-US/firefox/addon/videodefaults/) | | 🌐 Chrome (Web Store) | Published | [chromewebstore.google.com/.../videodefaults](https://chromewebstore.google.com/detail/videodefaults/olbdclkanolgkhfghooecilkhbghadob) | -| 🟦 Edge (Add-ons) | Not yet submitted | — | +| 🟦 Edge | Not yet submitted | [chromewebstore.google.com/.../videodefaults](https://chromewebstore.google.com/detail/videodefaults/olbdclkanolgkhfghooecilkhbghadob) (Chromium-based, requires enabling "Allow extensions from other stores" in `edge://extensions`) | +| 🧭 Arc | Not yet submitted | [chromewebstore.google.com/.../videodefaults](https://chromewebstore.google.com/detail/videodefaults/olbdclkanolgkhfghooecilkhbghadob) (Chromium-based, installs directly, no extra setting needed) | ## Requirements From 6942ef91471c62b8fa553f38ef36d46ff5cc1bf1 Mon Sep 17 00:00:00 2001 From: Kotmin <70173732+Kotmin@users.noreply.github.com> Date: Mon, 10 Aug 2026 14:22:58 +0200 Subject: [PATCH 3/7] docs(readme): star-annotate Edge/Arc status, move caveat out of table Drop the inline parenthetical from the Listing column and mark Edge and Arc as Unofficial* in Status instead, with the Chromium-install caveat explained once below the table. --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8bce6c9..11f6fc9 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,13 @@ Active development on `dev` branch. | --- | --- | --- | | 🦊 Firefox (AMO) | Published | [addons.mozilla.org/.../videodefaults](https://addons.mozilla.org/en-US/firefox/addon/videodefaults/) | | 🌐 Chrome (Web Store) | Published | [chromewebstore.google.com/.../videodefaults](https://chromewebstore.google.com/detail/videodefaults/olbdclkanolgkhfghooecilkhbghadob) | -| 🟦 Edge | Not yet submitted | [chromewebstore.google.com/.../videodefaults](https://chromewebstore.google.com/detail/videodefaults/olbdclkanolgkhfghooecilkhbghadob) (Chromium-based, requires enabling "Allow extensions from other stores" in `edge://extensions`) | -| 🧭 Arc | Not yet submitted | [chromewebstore.google.com/.../videodefaults](https://chromewebstore.google.com/detail/videodefaults/olbdclkanolgkhfghooecilkhbghadob) (Chromium-based, installs directly, no extra setting needed) | +| 🟦 Edge | Unofficial* | [chromewebstore.google.com/.../videodefaults](https://chromewebstore.google.com/detail/videodefaults/olbdclkanolgkhfghooecilkhbghadob) | +| 🧭 Arc | Unofficial* | [chromewebstore.google.com/.../videodefaults](https://chromewebstore.google.com/detail/videodefaults/olbdclkanolgkhfghooecilkhbghadob) | + +\* Not yet submitted to Edge Add-ons or Arc's own store. Both are +Chromium-based and can install the Chrome Web Store listing above instead. +Edge requires enabling "Allow extensions from other stores" in +`edge://extensions` first; Arc installs directly, no extra setting needed. ## Requirements From 9cddb69860738aa26cab7165de2be6cf04616da2 Mon Sep 17 00:00:00 2001 From: Kotmin <70173732+Kotmin@users.noreply.github.com> Date: Sun, 16 Aug 2026 16:30:02 +0200 Subject: [PATCH 4/7] fix(queue-overlay): match menuitem role on popup item's inner button YouTube moved role="menuitem" off yt-list-item-view-model onto its inner button/anchor (wrapper is now role="presentation"), so the queue popup opened but the "Add to queue" item was never found, silently no-opping. Select the descendant instead of the wrapper. --- docs/probes/add-to-queue-dom-findings.md | 22 ++++++++++++++++++++++ src/ui/queue-overlay.js | 7 +++++-- tests/fixtures/youtube-watch-basic.html | 4 +++- tests/unit/queue-overlay.test.js | 2 +- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/docs/probes/add-to-queue-dom-findings.md b/docs/probes/add-to-queue-dom-findings.md index 9ae9ab6..53dd2e8 100644 --- a/docs/probes/add-to-queue-dom-findings.md +++ b/docs/probes/add-to-queue-dom-findings.md @@ -292,3 +292,25 @@ genuinely never replaces the node — it falls back to clicking whatever's there pre-existing behavior rather than failing outright. Covered by two new unit tests in `tests/unit/queue-overlay.test.js`: one simulating a poll that returns the stale node for a couple of ticks before the fresh one appears, one simulating a node that never changes at all. + +## Follow-up: YouTube moved `role="menuitem"` off the item wrapper (2026-08-16) + +User reported the queue overlay regressing to a no-op: the "..." trigger still opened a popup +(visible only as a brief touch-feedback ripple), but no video was ever added. Captured a fresh +live DOM (`playwright` launched directly per [[feedback_firefox_testing]] — the `mcp__playwright__*` +tools still fail to launch a browser in this sandbox) against a real watch page. + +Root cause: `yt-list-item-view-model`, which previously carried `role="menuitem"` itself, now +carries `role="presentation"` — the role moved to its inner interactive child +(` + diff --git a/tests/unit/queue-overlay.test.js b/tests/unit/queue-overlay.test.js index ea165c3..bb9bf34 100644 --- a/tests/unit/queue-overlay.test.js +++ b/tests/unit/queue-overlay.test.js @@ -77,7 +77,7 @@ describe('activateQueueTarget', () => { querySelector: (sel) => { assert.equal( sel, - 'ytd-popup-container yt-list-item-view-model[role="menuitem"], ' + 'ytd-popup-container yt-list-item-view-model [role="menuitem"], ' + 'ytd-popup-container ytd-menu-service-item-renderer', ); queried = true; From af8b3fa6c4264ece5b9955e366edcf6bb40af671 Mon Sep 17 00:00:00 2001 From: Kotmin <70173732+Kotmin@users.noreply.github.com> Date: Sun, 16 Aug 2026 16:34:49 +0200 Subject: [PATCH 5/7] feat(playlist): add best-effort Shorts fallback for save-to-playlist trigger findSaveToPlaylistTrigger only looked for the /watch page's action-row button, so Ctrl+A Shift+P silently no-op'd on the Shorts standalone player (issue #20). A captured Shorts-page JSON snapshot confirms YouTube offers "Save to playlist" there via the header "..." menu, so add ytd-reel-player-header-renderer ytd-menu-renderer button as a fallback, derived from JSON renderer names and this codebase's established naming convention but not yet confirmed against a live DOM. --- README.md | 2 +- docs/ai/questions-for-K.md | 24 ++++++++++ docs/keyboard-quickstart.md | 2 +- docs/probes/save-trigger-dom-findings.md | 46 +++++++++++++++++++ .../youtube/youtube-site-adapter.js | 19 +++++++- tests/fixtures/youtube-shorts-basic.html | 29 ++++++++++++ tests/unit/youtube-site-adapter.test.js | 24 +++++++++- 7 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/youtube-shorts-basic.html diff --git a/README.md b/README.md index 11f6fc9..5cea55e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Press `Ctrl+A`, then a command key (tmux-style prefix — outside that window ev | --- | --- | | `Ctrl+A` `o` | Open jump overlay (labels clickable elements) | | `Ctrl+A` `p` | Open queue overlay (add a video to the Up Next queue) | -| `Ctrl+A` `Shift+P` | Open playlist picker for the current video (fuzzy-search, multi-add, create new) — watch page only, requires being logged in | +| `Ctrl+A` `Shift+P` | Open playlist picker for the current video (fuzzy-search, multi-add, create new) — watch page, and Shorts on a best-effort basis, requires being logged in | | `Ctrl+A` `y` | Go home | | `Ctrl+A` `v` | Set speed to preset 1 (default `1x`) | | `Ctrl+A` `b` | Set speed to preset 2 (default `1.5x`) | diff --git a/docs/ai/questions-for-K.md b/docs/ai/questions-for-K.md index 1bcce8e..4649fbe 100644 --- a/docs/ai/questions-for-K.md +++ b/docs/ai/questions-for-K.md @@ -162,3 +162,27 @@ to whichever matches first). signal was found in probes to key on instead. Flag if this turns out to matter in practice (e.g. your account actually has duplicate-named playlists). + +--- + +Dated 2026-08-16. Surfaced while implementing issue #20 (Shorts playlist- +picker gap). + +## Q17 — Shorts "Save to playlist" trigger selector, JSON-inferred only + +`findSaveToPlaylistTrigger` now falls back to +`ytd-reel-player-header-renderer ytd-menu-renderer button` when the +existing `/watch`-page selector finds nothing, to cover the Shorts +standalone player (`/shorts/`). This is derived only from a captured +Shorts-page JSON snapshot's renderer names (`reelPlayerHeaderRenderer`, +`menuRenderer`, both confirming a "Save to playlist" item) plus this +codebase's own already-confirmed `` -> `ytd-foo-renderer` +naming convention — it has **not** been checked against a live/rendered +Shorts DOM (no working headless browser in this sandbox this session +either; see `docs/probes/save-trigger-dom-findings.md` follow-up section +for the exact reasoning and what would falsify it). +**Default:** ship the selector above. If wrong, same failure mode as every +other best-effort selector in this file: `findSaveToPlaylistTrigger` +returns `null`, `openPlaylistOverlay` no-ops, safe no-op with no user- +visible error, just `Shift+P` staying silently unavailable on Shorts. +Please confirm the real selector live when convenient. diff --git a/docs/keyboard-quickstart.md b/docs/keyboard-quickstart.md index 5e68569..ee38a33 100644 --- a/docs/keyboard-quickstart.md +++ b/docs/keyboard-quickstart.md @@ -13,7 +13,7 @@ want via the extension's stored settings, same as any other override. | `Ctrl+A` | Prefix — arms the next key as a command (default binding, configurable) | Global | | `Ctrl+A` then `o` | Open jump overlay (labels clickable elements) | Global | | `Ctrl+A` then `p` | Open queue overlay (labels videos with an "Add to queue" option; typing a label adds that video next) | Global | -| `Ctrl+A` then `Shift+P` | Open playlist picker for the current video (fuzzy-search playlists, checkbox multi-add, create new) — watch page only, requires being logged in | Watch page | +| `Ctrl+A` then `Shift+P` | Open playlist picker for the current video (fuzzy-search playlists, checkbox multi-add, create new) — requires being logged in | Watch page, and Shorts on a best-effort basis (see issue #20) | | `Ctrl+A` then `y` | Go home (click YouTube logo, or navigate to configured home URL) | Global | | `Ctrl+A` then `v` | Set playback speed to preset 1 (default `1×`) | Global | | `Ctrl+A` then `b` | Set playback speed to preset 2 (default `1.5×`) | Global | diff --git a/docs/probes/save-trigger-dom-findings.md b/docs/probes/save-trigger-dom-findings.md index f876d30..f8ad395 100644 --- a/docs/probes/save-trigger-dom-findings.md +++ b/docs/probes/save-trigger-dom-findings.md @@ -67,3 +67,49 @@ Live capture only (headless Firefox via the `playwright` devDependency, run as a throwaway script, not committed) — no HTML fragment saved alongside this doc since the page contains no personal/account data in the logged-out state captured. + +## Follow-up 2026-08-16: Shorts trigger, JSON-inferred only (issue #20) + +Resolves the "Shorts / other layouts" gap flagged above. A captured live +JSON snapshot of a real Shorts URL (`docs/probes/*short_page.html`) +confirms Shorts genuinely offers "Save to playlist" — the JSON path +`overlay.reelPlayerOverlayRenderer.reelPlayerHeaderSupportedRenderers +.reelPlayerHeaderRenderer.menu.menuRenderer.items` contains a +`listItemViewModel` with `title.content == "Save to playlist"`, alongside +"Description", "Ambient mode", etc. This is the "..." menu near the +channel name/timestamp at the top of the Shorts player, not the right-hand +like/comment/share/remix action rail — that rail (`reelActionBarViewModel` +in the same JSON) only exposes those four actions and has no menu. + +`reelPlayerHeaderRenderer` and the sibling `menuRenderer` are both +`Renderer`-suffixed (legacy, non-"ViewModel") JSON node types. This +codebase has already confirmed, independently, that this JSON node family +maps 1:1 to a `` custom element (see +`ytd-menu-service-item-renderer`, `ytd-video-renderer` in +`src/ui/queue-overlay.js`, and `ytd-menu-renderer` in the "Action row +structure" section above). Combined with the "..." trigger being the +extremely common, well-established `ytd-menu-renderer button` pattern +already used elsewhere in this exact codebase (see above), the selector +shipped for `findSaveToPlaylistTrigger`'s Shorts fallback is: + +``` +ytd-reel-player-header-renderer ytd-menu-renderer button +``` + +**Still unverified — this is JSON-inferred, not live-DOM-confirmed.** No +headless browser worked in this sandbox for this session either (same +`navigator.webdriver` / hydration ceiling documented in +`add-to-queue-dom-findings.md`). What would falsify this: + +- `ytd-reel-player-header-renderer` not existing as a real custom element + tag at all (the naming convention breaking for this particular renderer). +- The header's "..." trigger not being wrapped in `ytd-menu-renderer`, or + not being a plain ` + + + + + + + + + + + + + + diff --git a/tests/unit/youtube-site-adapter.test.js b/tests/unit/youtube-site-adapter.test.js index 79fdaf7..f5f3a7a 100644 --- a/tests/unit/youtube-site-adapter.test.js +++ b/tests/unit/youtube-site-adapter.test.js @@ -105,6 +105,7 @@ describe('isLoggedIn', () => { describe('findSaveToPlaylistTrigger', () => { const SAVE_SELECTOR = '#flexible-item-buttons > yt-button-view-model button[aria-label]'; + const SHORTS_SELECTOR = 'ytd-reel-player-header-renderer ytd-menu-renderer button'; it('returns the button matching the Save wrapper selector', () => { const btn = {}; @@ -112,10 +113,31 @@ describe('findSaveToPlaylistTrigger', () => { assert.equal(findSaveToPlaylistTrigger(doc), btn); }); - it('returns null when the Save wrapper is absent', () => { + it('returns null when neither the watch-page nor the Shorts selector matches', () => { const doc = { querySelector: () => null }; assert.equal(findSaveToPlaylistTrigger(doc), null); }); + + it('prefers the watch-page selector over the Shorts fallback when both match', () => { + const watchBtn = {}; + const shortsBtn = {}; + const doc = { + querySelector: (sel) => { + if (sel === SAVE_SELECTOR) return watchBtn; + if (sel === SHORTS_SELECTOR) return shortsBtn; + return null; + }, + }; + assert.equal(findSaveToPlaylistTrigger(doc), watchBtn); + }); + + it('falls back to the Shorts header menu button when the watch-page selector finds nothing', () => { + const shortsBtn = {}; + const doc = { + querySelector: (sel) => (sel === SHORTS_SELECTOR ? shortsBtn : null), + }; + assert.equal(findSaveToPlaylistTrigger(doc), shortsBtn); + }); }); function makeWindow(href) { From bf66082d1b28da9ae2315910e85742c3c81d30fb Mon Sep 17 00:00:00 2001 From: Kotmin <70173732+Kotmin@users.noreply.github.com> Date: Sun, 16 Aug 2026 16:36:46 +0200 Subject: [PATCH 6/7] docs(changelog): note queue-overlay and Shorts save-trigger fixes --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f4bca4..087ea84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html) ## [Unreleased] +### Fixed +- Queue overlay ("Add to queue") stopped working everywhere: YouTube moved `role="menuitem"` off the popup item wrapper onto its inner button/link, so the item was never found after the trigger opened the menu. +- Playlist picker (`Ctrl+A, Shift+P`) no-op'd on the Shorts standalone player; it now also checks the Shorts header "..." menu for the Save-to-playlist trigger (best-effort, not yet live-confirmed — see `docs/ai/questions-for-K.md`). + ## [0.3.0] - 2026-08-10 ### Added From a7746c871729233ee5ab9b80379e2bc60920349e Mon Sep 17 00:00:00 2001 From: Kotmin <70173732+Kotmin@users.noreply.github.com> Date: Sun, 16 Aug 2026 17:25:50 +0200 Subject: [PATCH 7/7] chore(release): bump to 0.3.1 Patch release covering the queue-overlay menuitem-selector fix and the Shorts save-to-playlist trigger fallback. --- CHANGELOG.md | 2 ++ apps/chrome-extension/manifest.json | 2 +- apps/edge-extension/manifest.json | 2 +- apps/firefox-extension/manifest.json | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 087ea84..1e5e214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html) ## [Unreleased] +## [0.3.1] - 2026-08-16 + ### Fixed - Queue overlay ("Add to queue") stopped working everywhere: YouTube moved `role="menuitem"` off the popup item wrapper onto its inner button/link, so the item was never found after the trigger opened the menu. - Playlist picker (`Ctrl+A, Shift+P`) no-op'd on the Shorts standalone player; it now also checks the Shorts header "..." menu for the Save-to-playlist trigger (best-effort, not yet live-confirmed — see `docs/ai/questions-for-K.md`). diff --git a/apps/chrome-extension/manifest.json b/apps/chrome-extension/manifest.json index 587633b..936f9b1 100644 --- a/apps/chrome-extension/manifest.json +++ b/apps/chrome-extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "VideoDefaults", - "version": "0.3.0", + "version": "0.3.1", "description": "Preserves default video playback settings, starting with YouTube playback speed.", "permissions": [ "storage" diff --git a/apps/edge-extension/manifest.json b/apps/edge-extension/manifest.json index 587633b..936f9b1 100644 --- a/apps/edge-extension/manifest.json +++ b/apps/edge-extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "VideoDefaults", - "version": "0.3.0", + "version": "0.3.1", "description": "Preserves default video playback settings, starting with YouTube playback speed.", "permissions": [ "storage" diff --git a/apps/firefox-extension/manifest.json b/apps/firefox-extension/manifest.json index 7ebd826..8ed9195 100644 --- a/apps/firefox-extension/manifest.json +++ b/apps/firefox-extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "VideoDefaults", - "version": "0.3.0", + "version": "0.3.1", "description": "Preserves default video playback settings, starting with YouTube playback speed.", "permissions": [ "storage"