Repository navigation
feat(ui): add the v3 player with Chapters, Up next and capture Undo - #466
Merged
Merged
Conversation
screens.mjs gains ten player states: now-playing, mini-player, capture, chapters, up-next, up-next-empty, no-chapters, video, local-player and media-error, and `set player <label>` runs them. A player state plays the scene's episode until its media loads, pauses it at a fixed second, saves it there and opens the full player. capture, chapters and the two up-next states press the full player's button by its exact text, so they fail at once on a player without it. `set player` and `set all` go on past a failed state and fail at the end. seed deletes episode notes, so a capture cannot change a later state's shots. fixtures.mjs serves each library episode as silence as long as its feed says, streamed so an hour costs no memory. #213 carries the prototype's eight JSON chapters, and Signal & Noise gains a 10 s H.264 test pattern that ffmpeg makes on first request and an episode whose media answers 404. ui.mjs gains --exact, so "Chapters" does not also match "Chapters (8)".
disablePlugin resolves before the save PodNotes starts on unload, and that save merges its unsaved edits into data.json. seed wrote the next scene in that window, so a late save could carry the previous state's feeds or progress into it. seed now awaits flushSaves() on the disabled instance, which resolves once the unload save is done.
normalizePlaybackRate rounded to tenths, so 0.75, 1.25 and 1.75 became 0.8, 1.3 and 1.8. It now rounds to hundredths; the rate commands still step by 0.1. normalizeSkipLength moves from the API to src/utility so the player can label its skip buttons with the same rule. size: 98,241 gzip (+2 vs master 98,239)
QueuePage becomes QueueList with an empty label, so the player's Up next panel can render the same keyed rows and the same one-write drag. A drag now cancels on Escape through a scope it pushes for its lifetime: inside the full player, the player's scope takes Escape before a window listener could hear it. The grip carries data-ignore-swipe, so on a phone a downward drag starts none of Obsidian's swipe gestures. size: 98,298 gzip (+59 vs master 98,239)
A menu now knows the control that opened it. Focus goes back there before a picked item runs, so an item that opens a note keeps the focus it sets, or when the menu closes with nothing picked, so the next Escape reaches the view. rateMenu offers 0.75 to 2 with the live rate checked, and noteEntry is the episode menu's Open note or Create note, for the player's capture bar to share. The Menu mock keeps its items and can pick or close. size: 98,347 gzip (+108 vs master 98,239)
Rows, the mini player and list totals rounded anything under a minute up to "1 min", so a nearly finished episode read "1 min left" with seconds of audio remaining. Under a minute they now show seconds, such as "45 s left".
The full player is one Player component: a head with collapse and the episode menu; a stage with the artwork, a file tile for a local file or artwork that fails to load (#394), the video with fullscreen, the media error with Retry, or a panel at compact widths; and a deck with the title, the podcast or folder link, a chaptered Scrubber, the current chapter, a rate pill menu, skip buttons labeled with the configured lengths, play and pause, a volume popover, Timestamp and Create or Open note (#347), and the Chapters and Up next switch. Wide views put the panels in a right column with tabs. Up next is the shared QueueList, so it reorders in place with one write per move. Escape closes the volume popover before the player, and Space plays and pauses off controls. EpisodePlayer, PlayerOverlay, ChapterList, Loading, the obsidian Button, Icon and Slider wrappers, Progressbar, Image and their helpers go. The e2e targets and the verify skill's player notes follow the new markup. size: 97,980 gzip (-259 vs master 98,239)
…es exactly what it inserted
CaptureService now confirms each successful capture, whatever started it (the
player, the mini player, the commands, the headset or lock screen), with an
Undo notice: "Timestamp added to {note}", or "Created {note}" when the
capture created the episode note.
Undo takes back exactly what the capture inserted, and only while it is
still exactly where it was put:
- in an editor, while the same tab still shows the same note in Source or
Live Preview and the inserted range is untouched; text typed after it
stays, and a selection the capture replaced comes back;
- in the episode note, while the note still ends with the appended text,
through the note's open editor when one edits it, else through a
vault.process that aborts when the end changed. A note the capture created
is kept; only the timestamp goes.
When the text was edited since, Undo leaves it and says so. The Undo is
disarmed when the plugin unloads (dismissUndoNotices in onunload).
The full player's Timestamp button reads "Added" with a check for 1.2 s
after a capture. The player and mini player no longer show their own
"Timestamp added to" Notice.
size: 98,396 gzip (+157 vs master 98,239)
… API and the saved default 1.25 round-trips through the IAPI playbackRate setter and through the saved default speed, and a 2.21.1 library's one-decimal default speed (1.3) loads and saves unchanged with no normalization warning. size: 98,396 gzip (+157 vs master 98,239)
…link's The tightening pass over the new player measured three candidates against 98,396 gzip: - folding ChaptersPanel into Player saves 93 bytes, under the 100-byte bar, so it stays a component; - one shared reset for the player's bare buttons costs 3 bytes more than the duplicates it removes, so the per-group resets stay; - the current chapter label's own rule (min-width 0, font-size inherit) merges into the podcast link rule it already shares, with no visual change. size: 98,386 gzip (+147 vs master 98,239)
Obsidian's menu closes on Escape but leaves the key unhandled, and Obsidian then sends focus from a sidebar view to the main area's most recent leaf. With the player in the right sidebar and a note open, Escape on the episode menu moved focus into the note, so the next Escape went to the note instead of closing the volume slider or the player. The menu now marks the Escape that closes it as handled, so focus stays on the control that opened it. size: 98,419 gzip (+180 vs master 98,239)
…local artwork and the Escape order In real Obsidian 1.14.4: - J-TS-12: the full player's Timestamp goes into a Live Preview note at the caret, reads "Added", and Undo through the Notice leaves the note byte for byte as before; with no editor open it creates the episode note, Undo keeps the note without the line, and a second capture's Undo leaves it as before. - J-TS-13: the same from the mini player, into a note and into an episode note the user has, whose missing final newline Undo takes back too. - J-TS-14: Capture Timestamp with nothing loaded shows "Play an episode to capture" and writes nothing. - A video episode with 30 queued keeps its 16:9 frame at 320 px, after Up next covered the stage, and at 820 px beside Up next. - Up next reorders by a slow drag and by an arrow key, writing data.json once per completed move. - #394: a local file shows its file tile, never an image, in the player and the mini player. - With a note open in the main area, Escape closes the episode menu, then the volume slider, then the player, and focus stays in the view. size: 98,419 gzip (+180 vs master 98,239)
size: 98,419 gzip (+180 vs master 98,239)
Obsidian centers a button's content, so chapter rows centered their time and title instead of starting each at the left as the design shows. A long note or podcast name made an Undo notice four or five lines tall; it now stops at two, with Undo beside it. size: 98,566 gzip (+242 vs master f295af1 98,324)
Capture feedback is an Obsidian notice, so the capture state keeps notices in its shots, fails unless one says "Timestamp added to", and clears older notices before it presses Timestamp.
…ice with its episode A chapters fetch that failed, as offline, was memoized as an empty list, so Chapters stayed hidden for the session. Only loaded chapters are kept now, and a reopened player takes them on its first frame instead of showing Up next until a promise settles. A panel choice resets when the chapters change, so a Chapters choice no longer comes back on its own on a later episode. size: 98,605 gzip (+281 vs master f295af1 98,324)
…unloaded CaptureService awaits the note write before it offers Undo, so a capture still running when the plugin unloaded showed an armed Undo after dismissUndoNotices had run. onunload now disposes the service, and a disposed service offers no Undo. size: 98,623 gzip (+299 vs master f295af1 98,324)
…a error over an open panel Fullscreen shows only the video frame, whose one control was Fullscreen. The frame now carries a play/pause button over the video, showing its icon while paused or under the pointer, as the replaced player's overlay did. A media error now takes the stage before a compact panel, so its message and Retry show when Up next or Chapters was open. size: 98,770 gzip (+446 vs master f295af1 98,324)
…tabs to their panel The Timestamp button's accessible name now follows what it shows, Timestamp and then Added. The wide player's tabs carry ids and aria-controls, and the tab panel is labelled by the selected tab. The scrubber shows an empty track, not width NaN%, before the duration is known, and the menu's Escape comment says why it holds on desktop and does nothing on a phone. size: 98,825 gzip (+501 vs master f295af1 98,324)
A failed run stayed cached as a rejected promise, so every later request for the video answered 500 until the fixture server restarted. size: 98,825 gzip (+501 vs master f295af1 98,324)
…bsidian The player journey presses Space with the focus on the player's title and checks that api.isPlaying flips both ways, then opens the podcast's page from the link under the title. J-TS-15 writes a line after a capture into the episode note and checks that Undo leaves both lines and says so. A unit test covers the link for a saved and an unsaved podcast. size: 98,825 gzip (+501 vs master f295af1 98,324)
…d time The Undo notice now wraps its message in a span, so dismissNotice found no text to click and J-FEED-05 and the shared-feed Remove journey timed out. The synced-progress journeys from master read the replaced player's .episode-player status line; they read the new player's elapsed time now. size: 98,825 gzip (+501 vs master f295af1 98,324)
…ing a media error A slower chapters load for an earlier episode could replace the cached chapters of the episode on screen, so reopening the player fetched again and showed Up next first. At compact widths a media error takes the stage, so the panel switches now read off and are disabled until the media loads. size: 98,854 gzip (+530 vs master f295af1 98,324)
… off during a media error A one-entry chapters cache let a slower or earlier load evict the chapters of the episode on screen, also after a return to that episode. Chapters now stay per address for the session, so no load order can evict them. During a media error at compact widths the current chapter label no longer opens a panel, and disabled switches no longer take the hover style. size: 98,859 gzip (+535 vs master f295af1 98,324)
Deploying podnotes with
|
| Latest commit: |
d11513d
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://25d35bb3.podnotes.pages.dev |
| Branch Preview URL: | https://feat-ui-player.podnotes.pages.dev |
Undo found an episode note capture by the note's ending, so after the user wrote the same line again, an older Undo removed the later line and kept its own. Undo also refused once the note's tab closed or switched to Reading view, though the note had not changed. Each capture now keeps where its text went: the offset, the text and the selection it replaced. Undo takes the text out of an editor open on the note, else out of the saved file in one vault.process, and only while that exact range still holds exactly the inserted text. Text written after the capture stays. Otherwise Undo leaves the note and says so. size: 98,943 gzip (+607 vs master b14edf1 98,336)
…ansport during a media error
Matching the inserted text at its old offset was not identity: a copy of the capture pasted before it sat at that offset, so Undo deleted the copy. Undo also refused while a closing tab's save was still pending, with a message that said the note had changed. A capture now keeps the note's text before and after it. Undo restores the text before only while the note, in its open editor or else its saved file, equals the text after. Any other state refuses, including an edit anywhere and a save still pending, with a message that holds in every case. In an editor, Undo replaces only the changed stretch, so the caret and the editor's own undo history stay. size: 98,927 gzip (+591 vs master b14edf1 98,336)
…he saw it The unit vault fake now has cachedRead, read, modify and process, with a cache that a change on disk does not update, as Obsidian's. A unit test and J-TS-19 change the episode note behind that cache and click Undo: it must refuse and keep the newer text. Both fail when Undo reads with cachedRead and writes with modify instead of one vault.process.
This was referenced Oct 15, 2026
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.
Why
The player was still the Svelte 4 player: a volume and a rate slider, the queue listed under it, and artwork you had to right-click to create a note. A capture gave feedback only from the mini player and could not be taken back. This PR builds the v3 player from the design spec and prototype and deletes what it replaces. Fixes #347 and #394.
What changed
Scope
Deviations from the design
API changes
playbackRatesetter of the public API and the saved default speed keep hundredths, so 1.25 stays 1.25 instead of becoming 1.3. The type and the data shape are unchanged. The rate menu's 0.75, 1.25 and 1.75 need it.Blast Radius
Every user sees the new player. Captures from commands and the headset, silent until now, show a notice. Capture and Undo write only to the note that took the capture, and Undo refuses rather than guess. The player stays out of PlayerController's position and save logic.
Verification
Size (gzip,
npm run size):Screenshots from Obsidian 1.14.4. Each sheet has a row per player state, with master, this branch and the design prototype side by side. Capture, Chapters, Up next and an empty Up next have no master column, because master has no such state.
Sidebar 320, light and dark:


Pane 820, light and dark:


Phone 390 (emulated), light and dark:


Capture into an open note, then Undo: screen recording

Known issue
While the full player is open, keys a note handles itself, such as Enter and Escape in its find bar, do not reach the note. The player's key scope has done this since the overlay player (#434). It is unreleased. The fix registers the player's keys on the view's own scope, and it belongs in its own PR.
Docs at release
playbackRatesetter keeps hundredths (1.25 stays 1.25).Note
Add v3 full player with Chapters, Up next panels and capture Undo
formatMinutesnow shows seconds for durations under 59.5s (min 1 s); MiniPlayer no longer shows its own success notice and usessourceOflabels; Escape during queue drag now ends the drag via an Obsidian keymap scope.Macroscope summarized d11513d.