Commit Graph

113 Commits

Author SHA1 Message Date
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
cfa4ef0c47 refactor(nav): route tab switches through single switchTab path
Replace reactive / usage in the navigation
store with imperative functions so behavior is identical under both the
client JSX runtime and the server build used by unit tests (where
/ are no-ops).

- Centralize tab-switch side effects (root-frame seed, pane/mode/
  command/visual reset) in , invoked by a single
   helper used by gotoTab/nextTab/prevTab.
- Convert depthStack/currentDepth/topFrame/selectedIds from
  to plain functions that recompute on every read while still tracking
  underlying signals in reactive contexts.
2026-07-31 16:59:20 -04:00
5b667367a5 feat(layout): add YaziPaneRow 3-pane layout primitive
Add the shared parent | current | preview row primitive that implements
yazi's mgr.ratio = [1, 3, 3] contract via Yoga flexGrow, so every list
tab renders an identical, layout-stable shell regardless of terminal
size.

- New YaziPaneRow component: three bordered columns grow at 1/7 : 3/7
  : 3/7 with flexBasis=0 (content can never stretch its slot). The
  current column carries the accent focus ring when `focused` is
  truthy; parent and preview stay muted. The blank parent keeps its
  1/7 slot with a muted placeholder rather than collapsing.
- PANE_RATIO.current 4 -> 3 to match yazi's [1, 3, 3].
- Add render-based tests covering the 1:3:3 ratios (incl. null
  parent/preview) and the focused/unfocused accent ring behavior.
- Configure the bun test preload for the solid JSX transform.
2026-07-31 16:58:33 -04:00
b1bb9d9a1e refactor: rearchitect navigation model with pure store and no sidebar pane
- Extract navigation state and logic into navigation-store.ts for
  testability and separation of concerns
- NavigationContext now only wraps the store as a Solid provider
- Remove SIDEBAR_PANE from the model; depth-tabs have a single
  focusable content pane (center/current column)
- Split LayerGraph and page imports into layer-graph.ts so the
  navigation utils stay free of JSX (unit-testable)
- Update Shell keybind dispatch: remove sidebar pane handling,
  simplify swipe to fixed-pane tabs only, clarify depth-tab h/l
- Add nav-model.test.ts covering depth stack, tab/pane focus, and
  selection semantics
2026-07-31 10:24:45 -04:00
97b2f61e5f pre-ui-rearch 2026-07-31 01:05:32 -04:00
d8f11040bc start revive 2026-07-30 21:25:03 -04:00
b7c4938c54 Auto-commit 2026-03-11 16:27 2026-03-11 16:27:26 -04:00
8196ac8e31 fix: implement page-specific tab depth navigation
- Changed nextPane/prevPane to use current tab's pane count instead of global TabsCount
- Added Page-specific pane counts mapping for accurate depth calculation
- Pages with 1 pane (Feed, Player) now skip depth navigation
- Fixed wrapping logic to respect each page's layout structure
2026-03-08 21:01:33 -04:00
f003377f0d some nav cleanup 2026-03-08 19:25:48 -04:00
1618588a30 cycle 2026-02-22 19:07:07 -05:00
c9a370a424 more keyboard handling 2026-02-21 00:46:36 -05:00