Skip to content

feat: behavioral-audit UI and interaction fixes (front-end, 2/2) - #215

Merged
chhoumann merged 6 commits into
masterfrom
chhoumann/audit-ui-fixes
Jun 22, 2026
Merged

chhoumann merged 6 commits into
masterfrom
chhoumann/audit-ui-fixes

Conversation

@chhoumann

Copy link
Copy Markdown
Owner

Summary

Part 2 of 2 of the behavioral-audit fixes: the UI / interaction half (player, chapters, context menu, playlists, podcast view). This supersedes #214, which GitHub auto-closed when its stacked base branch was deleted on the #213 merge; the branch is unchanged in content and has been rebased onto master (which now contains #213).

#214 reached CI green + Codex sign-off (👍) with all review threads resolved before it was closed; this PR is that same reviewed branch, rebased.

What's added / fixed

Player, chapters, accessible seek

  • Video fullscreen button (no native controls, iOS fallback).
  • Chapter-list active highlight tracks playback time and follows a clicked chapter; stale chapters cleared on episode switch; out-of-order fetches ignored.
  • Progress bar accessible name + aria-valuetext; chapter list aria-controls + descriptive seek labels.
  • Restore-position and end-of-episode cleanup match by composite identity (with a legacy title-only fallback), so a same-titled episode from another podcast isn't resumed/removed by mistake.

Context menu, playlists, validation

  • Keyboard/mobile reachable overflow (kebab) opens the episode menu via showAtPosition (was right-click only).
  • Playlist separator only renders when there are playlists; dropped download promise is settled.
  • Playlist/queue grid tiles show their name (card grows so the name can't clip/overlap the count).
  • Playlist name validation: reject empty, duplicate, and reserved built-in names; never overwrite an existing playlist; keyed list so per-row delete state can't drift; icon dropdown resets after add.
  • Queue context-menu/cleanup checks stay title-based (the queue is title-identified by design).
  • Removed unreachable repeat-toggle dead code; reorder-modal buttons enlarged to a comfortable touch target.

Podcast view

  • Refresh actually refreshes all feeds in the Latest Episodes view; no duplicate loading indicators for a selected feed.
  • Whitespace-only search treated as empty; open playlist live-updates; feed-load and search failures (invalid feed URL, iTunes errors) surfaced instead of failing silently.

Verification

  • lint, format:check, typecheck, build, check:a11y: clean.
  • Vitest: 743 passing (includes the regression tests added for every Codex finding during feat: behavioral-audit UI and interaction fixes (front-end, 2/2) #214's review).
  • Real Obsidian (dev vault): progress-bar aria, reorder touch targets, playlist tile names (no overlap), empty/duplicate/reserved name guards, and the focusable kebab confirmed live with no console errors.

- add a fullscreen button to video playback (no native controls)
- fix the chapter-list active-row highlight to track currentTime (the
  reactive statement had no dependencies) and follow a clicked chapter
- clear stale chapters on episode switch and ignore out-of-order fetches
- give the progress bar an accessible name + human-readable aria-valuetext,
  and the chapter list aria-controls + descriptive seek labels
- restore by composite key and end-of-episode cleanup by composite key, so a
  same-titled episode from another podcast isn't resumed/removed by mistake
…tion

- add a keyboard/mobile reachable overflow (kebab) affordance that opens the
  episode context menu via showAtPosition (was right-click only)
- only render the playlist context-menu separator when there are playlists
- settle the dropped download promise from the context menu
- show the playlist/queue name on grid tiles (and stop the name from
  clipping/overlapping the count by letting the card grow)
- validate playlist names: reject empty names and refuse to overwrite an
  existing playlist (data loss); key the list so per-row delete state can't
  drift; reset the icon dropdown after add
- remove the unreachable repeat-toggle dead code
- enlarge the reorder-modal action buttons to a comfortable touch target
- make Refresh actually refresh all feeds in the Latest Episodes view
- avoid duplicate loading indicators for a selected feed
- treat a whitespace-only search as empty and live-update an open playlist
- surface feed-load and search failures (invalid feed URL, iTunes errors) to
  the user instead of failing silently
The queue identifies episodes by title everywhere (queue.add dedupes and
queue.remove filters by title), so the context-menu membership check, the
reorder index lookup, and the end-of-episode queue cleanup must match by title
too. A composite-key check offered "Add to Queue" for a same-titled episode
from another podcast, which queue.add then no-opped. Addresses the Codex
review on #214.
Add isSameStoredEpisode, which matches a stored playlist/favorite entry by
composite key and, for LEGACY entries saved without podcastName, by title alone
(without matching a different podcast's same title). Use it for the favorites and
playlist context-menu checks and the end-of-episode playlist cleanup, so a legacy
entry is no longer treated as absent (duplicated on add, skipped on cleanup).
Addresses the Codex review on #214.
A custom playlist named like a built-in (Queue/Favorites/Local Files/Played)
is ambiguous because those tiles come from separate stores, so an open custom
playlist could silently switch to the built-in list on refresh. Reject those
names on create. Addresses the follow-up Codex review on #214.
@chhoumann
chhoumann merged commit 894d93d into master Jun 22, 2026
2 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4dcabc18d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

live = get(playlists)[name];
if (!live) {
// A custom playlist deleted while open: fall back to Latest Episodes.
showLatestEpisodes();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't force the player back to Latest Episodes

When an episode is started from a custom playlist, handleClickEpisode leaves selectedPlaylist populated while switching to ViewState.Player; if that playlist is then deleted from settings during playback, this new branch runs from the playlists subscription and showLatestEpisodes() flips viewState back to EpisodeList. That unexpectedly knocks the user out of the player even though the playlist list is not open, so guard this fallback to the visible episode-list view or clear selectedPlaylist when entering the player.

Useful? React with 👍 / 👎.

github-actions Bot pushed a commit that referenced this pull request Jun 22, 2026
# [2.17.0](2.16.0...2.17.0) (2026-06-22)

### Bug Fixes

* behavioral-audit logic and robustness fixes (back-end, 1/2) ([#213](#213)) ([4e2845d](4e2845d))
* **download:** default per-episode download path and migrate empty default ([#183](#183)) ([#186](#186)) ([46a6486](46a6486))
* **download:** prevent Android crash and create missing folders on download ([#178](#178)) ([ecd09d5](ecd09d5)), closes [#113](#113) [#86](#86) [#113](#113) [#86](#86)
* **lifecycle:** defer mobile podcast view startup ([#208](#208)) ([8bf9ce4](8bf9ce4))
* **notes:** cap note path length and harden folder creation ([#22](#22), [#87](#87)) ([#192](#192)) ([858e280](858e280)), closes [#87-class](#87)
* **playback:** persist listened time during playback ([#33](#33)) ([#190](#190)) ([e3433c2](e3433c2)), closes [#191](#191) [#108](#108) [#163](#163) [#183](#183)
* **playback:** play local files and downloads on iOS via resource path ([#100](#100)) ([#184](#184)) ([12c503a](12c503a))
* **player:** clear progress on episode switch to stop end-of-playback glitch ([#94](#94)) ([#194](#194)) ([59dccb3](59dccb3))
* **player:** reveal PodNotes view on Play with PodNotes so local files play ([#84](#84)) ([#198](#198)) ([5953625](5953625))
* **settings:** show labelled Add/Remove buttons in podcast search ([#109](#109)) ([#195](#195)) ([33399f5](33399f5))
* show downloaded episodes in the Local Files playlist ([#176](#176)) ([#177](#177)) ([184188c](184188c))
* **timestamps:** capture into the cursor's table cell without breaking the row ([#165](#165)) ([#203](#203)) ([964e342](964e342))
* **transcription:** always transcribe the currently playing episode ([#182](#182)) ([62b488a](62b488a)), closes [#107](#107)
* **uri:** preserve '+' in episode titles and paths for timestamp links ([#181](#181)) ([8ad7aa5](8ad7aa5)), closes [#164](#164)
* **view:** reliably reveal PodNotes view via command + ribbon icon ([#55](#55)) ([#199](#199)) ([fa1d708](fa1d708))

### Features

* add podcast segment links ([#205](#205)) ([d97e59e](d97e59e))
* **api:** expose generated episode transcripts ([e264465](e264465)), closes [#105](#105)
* behavioral-audit UI and interaction fixes (front-end, 2/2) ([#215](#215)) ([894d93d](894d93d))
* **commands:** add playback rate and media timestamp controls ([#206](#206)) ([a8bb44a](a8bb44a))
* **devx:** isolated per-worktree Obsidian E2E vault wrapper ([#188](#188)) ([a8b7a4a](a8b7a4a))
* **episodes:** add a setting to control the Latest Episodes list length ([#114](#114)) ([#200](#200)) ([7b3e3c6](7b3e3c6))
* **notes:** add {{episodelink}} template tag to resume an episode from its note ([#35](#35)) ([#193](#193)) ([8c1ddd6](8c1ddd6))
* **notes:** add podcast feed-level notes ([#163](#163)) ([#187](#187)) ([db0de47](db0de47)), closes [#161](#161) [#160](#160)
* **notes:** ship a Bases-friendly default episode note template ([#160](#160)) ([#201](#201)) ([209431d](209431d)), closes [#163](#163) [#183](#183)
* **player:** scale episode title font size to its length ([#81](#81)) ([#202](#202)) ([659b6b8](659b6b8))
* **player:** support video episode playback ([#209](#209)) ([f92a91f](f92a91f))
* **queue:** add setting to disable queue auto-population and auto-advance ([#108](#108)) ([#185](#185)) ([cf3d73c](cf3d73c))
* **queue:** allow reordering the playback queue ([#80](#80)) ([#179](#179)) ([d994d61](d994d61)), closes [#173](#173)
* **settings:** import/export settings & templates ([#180](#180)) ([a27d23d](a27d23d)), closes [#162](#162) [#162](#162)
* **templates:** add {{currentDate}}, {{episodeNumber}}, {{duration}} template variables ([#189](#189)) ([ec573eb](ec573eb)), closes [#75](#75) [#34](#34) [#88](#88) [163/#186](#186)
* **templates:** add episode chapters tag ([#207](#207)) ([9c98863](9c98863))
* **transcripts:** opt-in speaker diarization for transcripts ([#168](#168)) ([#204](#204)) ([a96e12f](a96e12f))
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 2.17.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@chhoumann
chhoumann deleted the chhoumann/audit-ui-fixes branch June 29, 2026 05:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant