Skip to content

Commit 77d13ee

Browse files
committed
feat: dedicated s/S for season/show jumps. add ctrl for configs
Signed-off-by: ygelfand <yuri@shlitz.com>
1 parent 2da9796 commit 77d13ee

File tree

7 files changed

+70
-23
lines changed

7 files changed

+70
-23
lines changed

docs/content/config.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Configuration Reference
22

3-
`plexctl` stores its configuration in a YAML file at `~/.plexctl.yaml`. While many of these settings are managed through the TUI (pressing `s` or `l`), you can gain granular control by editing the file directly.
3+
`plexctl` stores its configuration in a YAML file at `~/.plexctl.yaml`. While many of these settings are managed through the TUI (pressing `ctrl+s` or `ctrl+l`), you can gain granular control by editing the file directly.
44

55
## Full Example
66

@@ -128,14 +128,14 @@ Force a specific view mode (`list` or `poster`) for this library, overriding the
128128

129129
While you can edit `~/.plexctl.yaml` manually, most common settings can be managed directly within the `plexctl` TUI using dedicated configuration overlays.
130130

131-
### Global Settings (`s`)
132-
Pressing **`s`** at any time opens the Global Settings overlay. From here you can:
131+
### Global Settings (`ctrl+s`)
132+
Pressing **`ctrl+s`** at any time opens the Global Settings overlay. From here you can:
133133
- **Change Theme:** Cycle through available color palettes (Nord, Dracula, etc.) and see changes instantly.
134134
- **Set Icon Type:** Switch between Nerd Fonts, Emojis, and ASCII icons.
135135
- **Sidebar Layout:** Adjust the `library_name_format` to show or hide icons and names.
136136

137-
### Library Management (`l`)
138-
Pressing **`l`** opens the Library Configuration overlay. This allows you to manage the appearance of your sidebar:
137+
### Library Management (`ctrl+l`)
138+
Pressing **`ctrl+l`** opens the Library Configuration overlay. This allows you to manage the appearance of your sidebar:
139139
- **Hide Libraries:** Toggle the visibility of specific libraries (e.g., hide a "Photos" or "Home Movies" section).
140140
- **Custom Icons:** Browse and pick icons for each specific library using an integrated icon picker (supports Emoji and Nerd Fonts).
141141
- **Reorder Sidebar:** Use **`K`** and **`J`** (Shift+Up/Down) to drag and drop libraries into your preferred order.

docs/content/tui.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ The plexctl Interactive Interface (TUI) provides a rich, full-screen experience
1717
- **`p`**: Play selected item.
1818
- **`ctrl+p`**: Play selected item in TCT mode (terminal-based video rendering).
1919
- **`/`**: Open global fuzzy search.
20-
- **`l`**: Open library configuration (show/hide/icon picker).
21-
- **`s`**: Open global settings (theme/icon type).
20+
- **`ctrl+l`**: Open library configuration (show/hide/icon picker).
21+
- **`ctrl+s`**: Open global settings (theme/icon type).
2222
- **`r`**: Refresh current view / trigger reindex (on search page).
2323
- **`x`**: Stop current playback.
2424
- **`h`**: Jump directly to Home tab.
@@ -31,7 +31,7 @@ The plexctl Interactive Interface (TUI) provides a rich, full-screen experience
3131
Pressing `/` opens a global fuzzy search overlay. It searches across all indexed libraries for titles, actors, and directors. Results are updated instantly as you type.
3232

3333
### Library Configuration
34-
Pressing `l` opens the configuration manager. Here you can:
34+
Pressing `ctrl+l` opens the configuration manager. Here you can:
3535
- **Hide/Show** specific library sections.
3636
- **Assign Custom Icons** using Emojis, Nerd Fonts, or ASCII.
3737
- **Reorder** how libraries appear in your sidebar.

internal/tui/tui.go

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,9 @@ func (c *Controller) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
231231
return c, c.player.Stop()
232232
}
233233
return c, tea.Quit
234-
case "s":
234+
case "ctrl+s":
235235
return c, c.navigator.Push(settings.NewSettingsOverlayModel(c.theme))
236-
case "l":
236+
case "ctrl+l":
237237
return c, c.navigator.Push(tuiconfig.NewConfigOverlayModel(c.data.Libraries))
238238
case "h":
239239
return c, c.tabManager.SetActive(0)
@@ -332,15 +332,29 @@ func (c *Controller) handleSelectMedia(msg ui.SelectMediaMsg) tea.Cmd {
332332
}
333333

334334
func (c *Controller) handleJumpToDetail(msg ui.JumpToDetailMsg) tea.Cmd {
335-
for i, tab := range c.tabManager.tabModels {
336-
if libTab, ok := tab.(*view.MediaView); ok {
337-
if libTab.GetSectionID() == msg.SectionID {
338-
initCmd := c.tabManager.SetActive(i)
339-
c.returnTabIdx = msg.ReturnTabIdx
340-
return tea.Batch(initCmd, libTab.ShowDetail(msg.RatingKey, msg.Type))
335+
slog.Debug("Controller: handleJumpToDetail", "key", msg.RatingKey, "type", msg.Type, "section", msg.SectionID)
336+
337+
// 1. If SectionID is specified, find that specific tab
338+
if msg.SectionID != "" {
339+
for i, tab := range c.tabManager.tabModels {
340+
if libTab, ok := tab.(*view.MediaView); ok {
341+
if libTab.GetSectionID() == msg.SectionID {
342+
slog.Debug("Controller: Found matching library tab", "idx", i)
343+
initCmd := c.tabManager.SetActive(i)
344+
c.returnTabIdx = msg.ReturnTabIdx
345+
return tea.Batch(initCmd, libTab.ShowDetail(msg.RatingKey, msg.Type))
346+
}
341347
}
342348
}
343349
}
350+
351+
// 2. Fallback: If no section specified OR not found, try the active tab
352+
if activeTab, ok := c.tabManager.ActiveModel().(*view.MediaView); ok {
353+
slog.Debug("Controller: Applying jump to active library tab")
354+
return activeTab.ShowDetail(msg.RatingKey, msg.Type)
355+
}
356+
357+
slog.Warn("Controller: No matching library tab found for jump", "section", msg.SectionID)
344358
return nil
345359
}
346360

@@ -456,8 +470,8 @@ func (c *Controller) showHelp() tea.Cmd {
456470
keys := []ui.HelpKey{
457471
{Key: "tab", Desc: "Switch Library"},
458472
{Key: "h", Desc: "Home"},
459-
{Key: "s", Desc: "Global Settings"},
460-
{Key: "l", Desc: "Libraries"},
473+
{Key: "ctrl+s", Desc: "Global Settings"},
474+
{Key: "ctrl+l", Desc: "Libraries"},
461475
{Key: "u", Desc: "Switch User"},
462476
{Key: "p", Desc: "Play Selected"},
463477
{Key: "x", Desc: "Stop Playback"},
@@ -524,7 +538,7 @@ func (c *Controller) renderBaseView() string {
524538
Background(c.theme.BrightBlack()).
525539
Foreground(c.theme.White()).
526540
Padding(0, 1).
527-
Render(" q: quit | tab: switch lib | h: home | s: settings | l: libs | u: user | p: play | x: stop ")
541+
Render(" q: quit | tab: switch lib | h: home | ctrl+s: settings | ctrl+l: libs | u: user | p: play | x: stop ")
528542

529543
return lipgloss.JoinVertical(lipgloss.Left, mainArea, footer)
530544
}

internal/tui/view/detail/episode_detail.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,24 @@ func (v *EpisodeDetailView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
6060
switch msg.String() {
6161
case "esc", "backspace":
6262
return v, func() tea.Msg { return BackMsg{} }
63+
case "s":
64+
if v.Metadata != nil && v.Metadata.ParentRatingKey != nil {
65+
return v, func() tea.Msg {
66+
return ui.JumpToDetailMsg{
67+
RatingKey: *v.Metadata.ParentRatingKey,
68+
Type: "season",
69+
}
70+
}
71+
}
72+
case "S":
73+
if v.Metadata != nil && v.Metadata.GrandparentRatingKey != nil {
74+
return v, func() tea.Msg {
75+
return ui.JumpToDetailMsg{
76+
RatingKey: *v.Metadata.GrandparentRatingKey,
77+
Type: "show",
78+
}
79+
}
80+
}
6381
}
6482
}
6583

@@ -72,6 +90,8 @@ func (v *EpisodeDetailView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
7290
func (v *EpisodeDetailView) HelpKeys() []ui.HelpKey {
7391
return []ui.HelpKey{
7492
{Key: "esc", Desc: "Back"},
93+
{Key: "s", Desc: "Go to Season"},
94+
{Key: "S", Desc: "Go to Show"},
7595
{Key: "j/up", Desc: "Scroll Synopsis Up"},
7696
{Key: "k/down", Desc: "Scroll Synopsis Down"},
7797
}
@@ -121,5 +141,5 @@ func (v *EpisodeDetailView) View() string {
121141
mainLayout := v.RenderPosterAndInfo(infoSection)
122142

123143
return lipgloss.NewStyle().Padding(1, 2).Render(mainLayout) +
124-
"\n\n " + dimStyle.Render("[p] Play | [esc] Back | [↑/↓] Scroll Synopsis")
144+
"\n\n " + dimStyle.Render("[p] Play | [s] Season | [S] Show | [esc] Back | [↑/↓] Scroll Synopsis")
125145
}

internal/tui/view/detail/manager.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ func (m *DetailManager) Update(msg tea.Msg) (tea.Cmd, bool) {
2626
return nil, false
2727
}
2828

29-
// Handle Back navigation globally for detail views
3029
if _, ok := msg.(BackMsg); ok {
3130
if rc, ok := m.View.(ui.RootChecker); !ok || rc.IsAtRoot() {
3231
m.View = nil
@@ -42,10 +41,12 @@ func (m *DetailManager) Update(msg tea.Msg) (tea.Cmd, bool) {
4241
var cmd tea.Cmd
4342
m.View, cmd = m.View.Update(msg)
4443

45-
// Everything is handled by the detail view EXCEPT library data messages.
4644
if _, ok := msg.(ui.MediaPageMsg); ok {
4745
return cmd, false
4846
}
47+
if _, ok := msg.(ui.JumpToDetailMsg); ok {
48+
return cmd, false
49+
}
4950

5051
return cmd, true
5152
}

internal/tui/view/detail/season_detail.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@ func (v *SeasonDetailView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
9999
}
100100
case "esc", "backspace":
101101
return v, func() tea.Msg { return BackMsg{} }
102+
case "S":
103+
if v.Metadata != nil && v.Metadata.ParentRatingKey != nil {
104+
return v, func() tea.Msg {
105+
return ui.JumpToDetailMsg{
106+
RatingKey: *v.Metadata.ParentRatingKey,
107+
Type: "show",
108+
}
109+
}
110+
}
102111
}
103112
}
104113

@@ -120,6 +129,7 @@ func (v *SeasonDetailView) HelpKeys() []ui.HelpKey {
120129
}
121130
return []ui.HelpKey{
122131
{Key: "enter", Desc: "View Episode Details"},
132+
{Key: "S", Desc: "Go to Show"},
123133
{Key: "esc", Desc: "Back"},
124134
{Key: "j/up", Desc: "Move Up / Scroll"},
125135
{Key: "k/down", Desc: "Move Down / Scroll"},
@@ -165,5 +175,5 @@ func (v *SeasonDetailView) View() string {
165175
Render(v.episodeList.View())
166176

167177
return lipgloss.NewStyle().Padding(1, 2).Render(lipgloss.JoinVertical(lipgloss.Left, mainLayout, listContent)) +
168-
"\n\n " + lipgloss.NewStyle().Foreground(v.Theme.BrightBlack()).Render("[enter] View Details | [p] Play | [esc] Back")
178+
"\n\n " + lipgloss.NewStyle().Foreground(v.Theme.BrightBlack()).Render("[enter] Details | [p] Play | [S] Show | [esc] Back")
169179
}

internal/tui/view/media.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ func (v *MediaView) ShowDetail(ratingKey, mediaType string) tea.Cmd {
301301
switch mediaType {
302302
case "show":
303303
dv = detail.NewShowDetailView(ratingKey, v.theme)
304+
case "season":
305+
dv = detail.NewSeasonDetailView(ratingKey, v.theme)
304306
case "episode":
305307
dv = detail.NewEpisodeDetailView(ratingKey, v.theme)
306308
default:

0 commit comments

Comments
 (0)