Commit Graph

173 Commits

Author SHA1 Message Date
ebed49237c style(tabpanel): convert tab indentation to 2 spaces 2026-08-10 20:57:16 -04:00
dc2b22eaa5 feat(settings): accent-colored input cursor in add-source form
Match the input's focusedTextColor with cursorColor=theme.accent on
both the name and URL fields.
2026-08-10 20:57:13 -04:00
2bb612ee07 fix(player): render help hint via content prop to avoid escaping
The babel-preset-solid JSX transform HTML-escapes static string
children (< > → &lt; &gt;), which opentui renders verbatim — so the
"< > seek" hint displayed its entities. Pass the string as the
content prop instead, which bypasses the transform.
2026-08-10 20:57:11 -04:00
dc855ab8a0 feat(shell): hold now-playing marquee at start between scroll passes
Previously the marquee looped continuously, cycling back to the
start the moment the text finished. Now it holds at the start for
SCROLL_HOLD_MS (10s), scrolls one pass at SCROLL_STEP_MS per char,
then holds again.
2026-08-10 20:57:08 -04:00
f976bdc2b7 fix(rows): keep episode rows exactly 3 lines — truncate, don't wrap
Feed and My Shows rows could grow to 4+ lines when a long title was
shrunk by the current pane: flexible text wrapped instead of
truncating, shifting every row below while scrolling. Add
wrapMode=none + truncate to flexible text and flexShrink=0 to
fixed-width cells so rows stay one line tall. Feed rows also move
the podcast name onto its own line. Adds a rendered-layout
regression test at 70 columns (35-col current pane).
2026-08-10 20:57:06 -04:00
1cf3361e59 fix(feed): stop refreshes from re-sorting the updated list
Two fixes to refresh order stability (My Shows / Feed sort by
lastUpdated):

- A refresh that fetches identical episodes no longer bumps
  lastUpdated (id-set comparison via sameEpisodes), so unchanged
  feeds keep their position instead of reordering every cycle.
- refreshAllFeeds now fetches in parallel and applies ONE atomic
  update instead of a per-feed setFeeds, which re-sorted the list
  once per completion and made order flap until the batch finished.

Adds feed-refresh regression tests with mocked clock.
2026-08-10 20:57:02 -04:00
ada441300a feat(layout): widen current pane to 50% — PANE_RATIO 2:5:3
Change the parent|current|preview split from 1:2:2 (20/40/40) to
2:5:3 (20/50/30) so the focused list gets more room. 2-pane tabs
now give current the combined 80%. Updates ratio comments and the
PaneRow test expectations.
2026-08-10 20:56:58 -04:00
6134dea044 feat(settings): configurable selection marker, default off
Adds a Selection Marker toggle under Settings → Preferences that
controls the ❯ cursor glyph on the focused row of every list.
Default off; rows keep a leading space for alignment either way.
Every list pane (tab strip, feed, my shows, discover, search,
settings, whitelist editor) reads the glyph through the shared
useSelectionMarker hook.

Also aligns the lead column across panes: page list rows drop
their extra box paddingLeft so labels start at the same column
as the tab strip (2 cells without nerd icons, 4 with), keeping
episode sub-rows aligned to the title.
2026-08-10 20:54:39 -04:00
93d5925dfd fix(waveform): add -readrate_initial_burst to eliminate audio lag
The decode head lagged the player by a constant ε (ffmpeg startup
latency) because ffmpeg paced at -readrate <speed> started behind mpv
and, advancing at the same rate, never caught up — bars were a few
seconds behind for the entire playback.

Add -readrate_initial_burst LEAD_SECONDS so ffmpeg emits 3s of audio
immediately on start, then paces at realtime after. The decode head
leads the player by a stable ~3s from the very first frame; read()
samples at the exact player position and always finds fresh samples.

Add a sustained render-loop test that simulates ~5s of real playback,
asserting ffmpeg stays alive, the decode head maintains a positive
lead, and read() returns full windows. Uses real wall-clock time
(documented exception) since ffmpeg's decode pacing can't be tested
deterministically.
2026-08-10 19:02:32 -04:00
de6d0ccbf6 feat(audio): podcast cover art in system Now Playing, CLI --play included
The cover was previously only wired through the UI hook; --play skipped it.
Also: Bun's fetch hangs in compiled binaries (Bun 1.3.8) — every cover
download silently failed in shipped builds, timing out against any host.
Cover staging now shells out to curl (present on macOS/Linux, 8s bound),
shared via src/utils/cover-art.ts so the UI and CLI paths stage the same
temp file and pass it to mpv as --cover-art-files (albumart track).
2026-08-10 18:18:58 -04:00
a9589e7686 feat(macos): ship PodTui.app bundle so Now Playing shows our icon
mpv owns the macOS Now Playing session (it plays the audio), and an
unbundled binary renders as a blank placeholder for the source-app icon.
macOS has no public API for a third party to claim session ownership
(MPNowPlayingSession is iOS-only; the private MRMediaRemoteSetNowPlayingApplication
was removed from the shared cache), so instead we make the OWNING process
carry our bundle: the darwin tarball now includes PodTui.app with mpv copied
into Contents/MacOS. AudioPlayer resolves the sibling mpv first (falling
back to PATH), LaunchServices attributes the process to com.mikefreno.podtui,
and Control Center shows the PodTui icon + name with podcast cover art.
AppIcon.icns generated from the Xcode icon-composer exports.
2026-08-10 17:56:54 -04:00
c52fa14e42 fix(player): correct click-to-seek by accounting for pane offset
MouseEvent coordinates are terminal-absolute, so the bar's seek handler
must subtract its own absolute left edge (renderable ref) instead of
treating the mouse x as bar-local. Clicking was off by the width of the
parent/Up pane (~20%) plus chrome. Also drop the empty played-text node
at position 0, which rendered a phantom space column and shifted the
drawn bar one char when playback started.
2026-08-10 17:53:46 -04:00
b280af484c feat(ui): nerd font icons on hard-defined list rows, graceful degrade
Tabs, Discover categories (replacing unused placeholder glyphs), Settings
sections, and the Feed 'Fetch More' row get Nerd Font glyphs (Font Awesome PUA
codepoints). When the terminal font isn't Nerd Font capable the glyphs render
nothing at all — no tofu, no layout gaps — via supportsNerdFonts() (env
allowlist + PODTUI_NERD_FONTS=1/0 override). Documented in README (Configuration
-> Fonts).
2026-08-10 16:50:21 -04:00
5dce21c038 feat(media): expose podcast name + cover art to system Now Playing
- mpv: --force-media-title '<podcast> — <episode>' so macOS Now Playing shows
  the podcast name instead of the download-hash filename (mediaTitle PlayOption)
- media registry: artist is now the human podcast title (customName ||
  podcast.title), falling back to podcastId
- downloads: write the podcast cover as a <base>.jpg sibling so mpv's
  cover-art-auto=exact picks it up for artwork; delete it with the download
2026-08-10 16:50:08 -04:00
d2c46631ef feat(shell): now-playing marquee in status bar (podcast — episode)
The bottom-bar now-playing segment shows '<podcast> — <episode>' (custom name
when set, feed resolved like advanceEpisode), takes the full remaining status-bar
width, and marquee-scrolls on a 300ms timer when the text overflows instead of
truncating at 40 chars. Static when it fits; no interval runs on fit/narrow bars.
2026-08-10 16:50:03 -04:00
67032460ff feat(player): click-to-seek progress bar; 2-row waveform bars with peak normalization
- ProgressBar: full-width played/remaining bar in the player pane, click-to-seek;
  waveform no longer handles seeking or the played/future color split (pure visual)
- bars: 2 terminal rows per bar (16 levels) via barChars, partial block in the
  top row so the column renders continuously
- fix bars maxing at audio start: disable cava autosens (silence gain-ramp),
  pre-warm the malloc'd FFT window with zeros, skip partial FFT windows
- createBarScaler peak follower + power curve replaces cava autosens for
  level-to-height mapping (src/utils/bar-mapping.ts, unit-tested)
2026-08-10 16:49:57 -04:00
0f3ffcf934 fix: whitelist editor keyboard/mouse navigation + highlight gating, My Shows w on focused show
Whitelist editor (Preferences > Auto Download Whitelist):
- input focus driven by nav.inputFocused() (SearchPage pattern) so Esc
  deterministically defocuses and j/k/Space/Enter browse the suggestions
- suggestions react to mouse: click focuses the row and toggles membership
- no accent bg / no marker on any suggestion while the input is focused
- s re-enters typing mode; suggestion rows scroll into view

Search page: recent-searches rows get no bg and no marker while the query
input is focused (previously the focused row fell into the inactive branch
and rendered the border color as a background).

My Shows: w toggles the focused show in/out of the auto-download whitelist
at the show list (depth 0), with a whitelist marker on the row and a hint in
the preview pane (scope: whitelist).
2026-08-10 16:40:00 -04:00
c813949d48 fix: remove fake RSS placeholder source; migrate persisted configs 2026-08-10 15:53:25 -04:00
eb220386ce feat: global auto-download with count/scope/whitelist + d/D/w keybinds 2026-08-10 15:53:25 -04:00
19eae4fd5a feat: sync waveform to live player position (mpv time-pos, position-window reads, smooth clock) 2026-08-10 15:51:02 -04:00
e70469b1ec fix: theme command palette input with accent 2026-08-10 15:50:51 -04:00
30b7ff57d3 refactor: seek via keybind router (< / >), free arrows for navigation 2026-08-10 15:50:51 -04:00
fd689aba04 fix: opentui subtree disposal on depth swap; theme inputs; recent-click 2026-08-10 15:46:53 -04:00
8ac1ec1162 bump VERSION to 0.3.1
Some checks failed
release / build (ubuntu-latest / x64) (push) Failing after 5m8s
release / build (macos-14 / arm64) (push) Has been cancelled
release / build (ubuntu-24.04-arm / arm64) (push) Has been cancelled
release / build (macos-15-intel / x64) (push) Has been cancelled
release / Attach to GitHub Release (push) Has been cancelled
2026-08-10 13:45:24 -04:00
4a94ff5910 feat: labeled loading spinners + [Fetch More] pagination on the feed list
Loading indicators: the braille spinner now carries a contextual label
(Refreshing…, Fetching…, Loading more…, Discovering…, Searching…) and is
shown in every loading state that previously rendered nothing — Discover
results, Search results fallback, and the empty Feed list.

Feed pagination: a focusable "[Fetch More]" row at the bottom of the flat
feed list advances every feed's loaded window by 50 episodes via the new
loadMoreAllFeeds/hasMoreAcrossAll store API. Behavior is a setting
(Fetch More: manual|auto, default manual) persisted in config.json; auto
fetches when focus reaches the bottom row. The button row is excluded
from episode focus so no episode is double-highlighted while it is active.
2026-08-10 10:38:20 -04:00
491a736c32 Restore center-pane borders, move title to top-left slot
- Current pane gets muted left/right borders only (no full box, no accent
  ring); border colors are passed only when a border is requested, since
  opentui flips borderless boxes to bordered when borderColor is supplied.
- Remove the Up / <current tab> / Detail titles above the panes; the current
  pane's title now renders once, top-left in the parent column's header slot.
- Drop the parentLabel/previewLabel props from PaneRow and all callers.
- Remove the tab/depth indicator from the bottom-left of the status bar.
- Tests measure column widths from the border glyphs and assert the
  left/right edges render muted regardless of focus.
2026-08-10 09:00:24 -04:00
12bd6be4bc Remove borders and accent ring from PaneRow panes
Make parent|current|preview fully borderless: no scrollbox borders and no
accent border highlight on the current column. focused still gates
scroll-following but never surfaces a separator. Update tests to measure
column widths from the header-label row and assert no border glyphs render.
2026-08-10 01:15:19 -04:00
b0bfa41028 bump VERSION to 0.3.0
Some checks failed
release / build (ubuntu-latest / x64) (push) Failing after 5m12s
release / build (macos-14 / arm64) (push) Has been cancelled
release / build (ubuntu-24.04-arm / arm64) (push) Has been cancelled
release / build (macos-15-intel / x64) (push) Has been cancelled
release / Attach to GitHub Release (push) Has been cancelled
2026-08-09 22:24:58 -04:00
25307f83e9 fix: up highlight made legible for transparent bg, bring back mouse nav 2026-08-09 22:21:30 -04:00
db285530b6 feat: private feeds, all input fields supersede keyboard nav 2026-08-09 15:39:02 -04:00
e1cdd6b2a5 finished hygenie 2026-08-09 14:38:33 -04:00
2abdbaa4e9 cleaning up code 2026-08-09 09:54:52 -04:00
c63e9e1b9c bump VERSION to 0.2.1
Some checks failed
release / build (ubuntu-latest / x64) (push) Failing after 51m15s
release / build (macos-14 / arm64) (push) Has been cancelled
release / build (ubuntu-24.04-arm / arm64) (push) Has been cancelled
release / build (macos-15-intel / x64) (push) Has been cancelled
release / Attach to GitHub Release (push) Has been cancelled
2026-08-08 08:02:53 -04:00
3ef19f80b8 drop oauth plan (copy config) 2026-08-08 07:10:59 -04:00
13a31aabdc docs: mark AUR package as pending registration reopen 2026-08-08 07:09:07 -04:00
2730fa3cae bump VERSION to 0.2.0 2026-08-07 19:32:48 -04:00
91a831c5f9 audio playback fixes 2026-08-07 19:28:57 -04:00
64d8b40e61 actual featured page 2026-08-07 18:59:25 -04:00
0cc15c8d90 fix lint: make bun tsc --noEmit actually pass (was a TS crash + latent errors)
tsconfig used jsx:"preserve"+jsxImportSource, which trips a TS 5.9
internal crash ("Expected sourceFile.imports[0] to be the synthesized JSX
runtime import") so tsc could never run clean. Switch to jsx:"react-jsx"
with the package's real jsx-runtime types, and fix the real errors that
surfaced:
- delete dead src/components/Navigation.tsx (imported ./Tab that doesn't
  exist; the component has no importers)
- PlaybackControls: fix relative path to @/utils/audio-player
- SourceBadge: drop dead module-level typeColor (bare 'theme')
- command palette: bind to the real 'command' keybind (:) instead of the
  never-defined 'command_list' action (palette was unreachable)
- yazi-pane-row test: destroy() must return Promise<void> as typed

Also fold in the package.json lint fix (bun tsc --noEmit) and doc polish.
2026-08-07 18:18:25 -04:00
1d3abd53d4 docs: add human-oriented CONTRIBUTING.md (repo map, FFI notes, gotchas, release & tap auto-sync workflow) 2026-08-07 18:09:51 -04:00
592cfd4093 fix search navigation 2026-08-07 17:46:27 -04:00
c9e3aa92ec fix release pipeline: vendored cava source, fftw in CI, runner arch, smoke test
- Vendor cava/cavacore.c + header (MIT, from karlstav/cava) — the FFI build
  referenced cava/cavacore.c which was never committed, so every CI runner
  failed at scripts/build-cavacore.sh and no release was possible.
- build-cavacore.sh: discover libfftw3.a across Homebrew and Debian/Ubuntu
  multiarch paths (FFTW_PREFIX override preserved).
- release.yml: install fftw before building cavacore; run the boot smoke test
  from a bunfig-free dir (the embedded runtime reads the CWD bunfig.toml and
  this repo's preload entry breaks it — 'preload not found'); use
  macos-15-intel for darwin-x64 (macos-latest is arm64).
- Makefile/build.ts: drop the no-op BUN_CONFIG=bunfig.standalone.toml compile
  dance (Bun never honored it; compile output is config-independent); delete
  bunfig.standalone.toml.
2026-08-07 14:27:35 -04:00
25fe7f6ac9 theme rendering 2026-08-07 13:42:03 -04:00
85cb9fba26 ui cleanup 2026-08-07 13:38:32 -04:00
c8d29ed59d fix navigation flow 2026-08-07 00:28:24 -04:00
b24c83711d refactor(keybinds): extract unified action dispatcher
Move the keybind router out of Shell into a reusable createDispatcher
in @/utils/dispatch, covering tabs (digits 1-6, [/]), h/l depth
drill/pop + fixed-pane swipe, modes, audio transport, quit/help, and
command, delegating pane/list actions over the nav.action event bus.

Shell now renders the active page full-width with a bottom status bar
carrying the tab strip; the old sidebar pane and its leftover sidebar
action handling are removed. Adds tests covering the dispatcher's
routing behavior.
2026-07-31 19:19:20 -04:00
866fcd7574 Merge branch 'rebuild-shell-chrome' 2026-07-31 19:06:18 -04:00
7c7d487ca6 refactor: move tab strip from left sidebar to bottom status bar
- Remove the left tab sidebar containing tab labels and now-playing
- Render tabs as compact labels in the command bar row instead
- Move now-playing display next to the selection count in the status bar
- All tabs remain functional; clicking a tab label switches the active tab
2026-07-31 19:05:43 -04:00
3f61303756 refactor(yazi): render depth-stack list tabs through YaziPaneRow primitive
Convert Discover, Feed, MyShows, and Settings pages from bespoke 3-column
flexbox JSX to render entirely via the shared <YaziPaneRow> parent|current|
preview primitive. Each page now supplies parent/current/preview content
accessors plus labels; the pane row handles layout, borders, focus styling,
and the muted placeholder for the blank parent slot at depth 0.

Replace Solid's children() helper in YaziPaneRow with a local normalizeContent
that hands the raw accessor to a reactive
expression. children() flattens to a stable resolved-nodes array and does not
re-resolve on truthy<->truthy root swaps (e.g. depth switching a pane's root
between a list fragment and an editor), which would freeze the previous
subtree. The insert effect from the reactive expression disposes and
remounts on element-identity change instead.

Add tests/yazi-pages-depth.test.ts covering the nav-store depth-stack
contract the pages depend on (push/pop stack growth, parent-slot data model
across root→child→grandchild→pop transitions).
2026-07-31 17:54:22 -04:00
139a258987 Merge branch 'rearchitect-nav-model'
# Conflicts:
#	src/utils/navigation.ts
2026-07-31 17:12:14 -04:00