Conversation
Trimmed to the relevant yt-sheet-view-model fragment (dropped the 19MB full-page asset save) and redacted real playlist names before committing to this public repo. Confirms the newer sheet-based popup renders all playlists directly in the DOM with no continuation tokens, and the create-new control lives in the sheet footer rather than the list.
Diffing an unselected vs. selected playlist row across the two captures found aria-pressed on the row's inner button as the real locale- independent selection signal, replacing the earlier assumption that only locale-text aria-label was available. Also documents the create-new- playlist footer button's structural (non-text) selector path.
Chords previously matched key case-insensitively with no shift distinction. Introduces a separate shiftChords map matched via evt.shiftKey so Shift+P (playlist picker, issue #16) stays unambiguous from plain p (queue overlay) regardless of layout-dependent key casing.
Case/space-insensitive substring match plus edit-distance-2 typo tolerance against the whole playlist name, per issue #16's resolved match algorithm (deliberately not a full fzf-style scorer).
Scrapes/toggles the native yt-sheet-view-model "Save to playlist" sheet off-screen (issue #16), same DOM-driving trick queue-overlay.js uses for "Add to queue". Row/state/footer selectors are verified against the captured DOM in docs/probes/save-to-playlist-dom-findings.md. How the watch page's own action row opens this sheet was never captured in the probes, so the caller supplies the trigger element rather than the driver guessing a selector for it (flagged in docs/ai/questions-for-K.md).
browser.storage.local-backed, 5 min TTL per issue #16's resolved default. Caches the playlist name catalog only, not per-video membership, since aria-pressed state reflects the currently-open video and would be wrong to serve to a different one from cache.
Pure reducer module (playlist-overlay-state.js, fully unit tested) drives query typing, up/down highlight, space-checkbox toggling capped at 5, enter-confirm resolution (checked set, implicit single-select, or create-new), and the nested create-new sub-dialog per issue #16's resolved UX. playlist-overlay.js is the DOM painter consuming that state, left untested at the unit level same as jump-overlay.js's createJumpOverlay.
Ctrl+A,Shift+P now opens the playlist overlay: gated behind a best-effort isLoggedIn signal, loads the catalog from the shared cache or a live native-popup scrape, drives query/highlight/checkbox/ enter through the existing overlay-state reducer, and adds to playlists sequentially (idempotent per row, since the native button is a toggle) with a progress badge reused from jump-overlay's BADGE_STYLE. Create-new is wired through driveCreateNewPlaylist. findSaveToPlaylistTrigger stays an explicit null stub — the watch page's native Save button was never captured in DOM probes, so the whole feature self-heals to a silent no-op rather than driving a fabricated selector. Tracked as the blocking open question for M7.
Extends keyboard-quickstart.md and keyboard-shortcuts.md with the Ctrl+A,Shift+P playlist picker (behavior, config shape, shiftChords merge semantics, verification coverage). Adds an Unreleased changelog entry. Files four open questions in questions-for-K.md surfaced while building issue #16: the still-uncaptured native Save-trigger button (blocking — feature currently no-ops on the live site), the best-effort login-detection selector, the best-effort create-new post-click UI assumption, and the name-collision limitation of keying playlists by name with no stable id available.
Live DOM probe of the watch page's action row found the real trigger: Save is the sole yt-button-view-model-wrapped child of #flexible-item-buttons, distinct from Download's wrapper. Replaces the null stub that kept Ctrl+A, Shift+P inert, resolving Q13.
The logged-out path silently no-op'd, giving no feedback that the chord was even recognized. Reuses the existing badge pattern to tell the user to sign in instead.
The sheet was genuinely visible on screen for the whole add/remove sequence, not just briefly. Hides it via inline style as soon as rows are found, instead of relying solely on the unverified Escape-close to make it disappear afterward.
The picker never showed which playlists the video was already in (loadPlaylistCatalog dropped the scraped selected flag, and the cache-hit path skipped scraping entirely). Now always scrapes fresh, pre-checks membership, and unchecking a pre-checked row on confirm removes the video from that playlist instead of only ever adding.
hideOpenSheet's !important overrides were never undone, so YouTube's reused sheet DOM node stayed permanently hidden after the first drive, breaking the native Save button on subsequent opens.
Escape wasn't reliably closing YouTube's Save-to-playlist sheet, leaving it open on screen at the end of a drive sequence. Close now polls for the sheet actually disappearing and re-clicks the trigger button as a fallback instead of trusting Escape blindly.
Toggle every row in a single open/close session instead of N open+verify-close round trips, and skip re-clicking an already-open trigger (which was toggling it shut and returning stale rows on a same-cycle reopen). Also stop unhiding the sheet before confirming close, so the verification wait is no longer visible on screen.
…scroll Space sometimes leaks through to YouTube's own play/pause, so the playlist overlay's toggle key is now sourced from shortcuts.config.json (playlistKeys.toggle) instead of hardcoded. Adds ArrowRight/ArrowLeft as a non-conflicting check/uncheck alternative, also configurable. The highlighted row now scrolls into view as the selector moves past the panel's visible area. Issue #16.
…ield Create-new never worked live: the native "Create new playlist" click opens a separate yt-dialog-view-model with a <textarea> title field, not the <input>/[contenteditable] the driver looked for inside the original sheet, so nothing was ever typed and the empty dialog was left sitting open. Submits via the dialog's own primary button instead of an Enter keydown, since Enter just inserts a newline in a textarea. Leaves the visibility dropdown untouched (defaults to Private already). Issue #16.
Creating a playlist worked but left the native dialog visibly open. Submitting is async so a click doesn't guarantee it closes itself in time; verifies the close the same way as the save-to-playlist sheet (poll, then fall back to Escape) rather than trusting the click. Issue #16.
Escape didn't close the create-playlist dialog either (reported live after the previous fix), same unreliability already seen with the save-to-playlist sheet. Falls back to clicking the dialog's own Cancel button instead of a synthetic keydown. Issue #16.
createNewPlaylistOnSite closed the save-to-playlist sheet then addVideoToPlaylists immediately reopened it to check the new playlist, since the new entry always lands in toAdd. That close/reopen race left the sheet visibly stuck open. Only close here on failure now; success leaves it hidden-but-open for the immediate reopen to reuse.
content.js's IIFE closure held loadPlaylistCatalog/addVideoToPlaylists/ createNewPlaylistOnSite/handlePlaylistKey with zero test coverage - every behavior fix in this area (30ef16b, 7cf4888, 7e9fc6a, a41f324, ...) only got verified by a live YouTube re-test. Moved into src/ui/playlist-controller.js as a doc/win-injectable factory, same pattern as playlist-popup-driver.js and queue-overlay.js; content.js now just constructs it and forwards events. Behavior is unchanged. 20 new tests pin down the sequences that took multiple live-debug rounds to get right: idempotent per-row toggling, one open/close per add batch, and the close-only-on-failure fix from 30ef16b so a create-then-add reopen doesn't race a redundant close.
…attern README's shortcut table never got the queue (Ctrl+A p) or playlist picker (Ctrl+A Shift+P) chords that docs/keyboard-quickstart.md already documented since they shipped. CONTRIBUTING's Tests section now points new DOM-driving code at src/ (doc/win-injectable factory) instead of content.js's closure, per the gap playlist-controller.js just fixed.
Formalizes the default: minor bump for features/shortcuts/layout changes, patch bump for fixes. Referenced when scoping release PRs.
Playlist picker (issue #16) is fully live and covered by unit tests; per the versioning policy this is a minor bump.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ctrl+A, Shift+P): fuzzy-search overlay to add/remove the current watch-page video from playlists or create a new one, without leaving the keyboard. Gated to logged-in users; membership pre-checked; toggle/check/uncheck keys and scroll-follow configurable.src/ui/playlist-controller.js, previously untested logic incontent.js's closure, now covered bytests/unit/playlist-controller.test.js(20 new tests).Test plan
bash scripts/test.sh— 336/336 passingbash scripts/check.sh— syntax, edition-dir cleanliness, manifest parity all green