Adjust view/info keybindings and improve read-only view mode

This commit is contained in:
vrubelroman 2026-04-24 10:08:33 +03:00
parent 3c2016eaf0
commit d22a40e987
2 changed files with 76 additions and 21 deletions

View file

@ -38,7 +38,7 @@ func DefaultKeyMap() KeyMap {
Help: key.NewBinding(key.WithKeys("f1", "?"), key.WithHelp("F1/?", "help")), Help: key.NewBinding(key.WithKeys("f1", "?"), key.WithHelp("F1/?", "help")),
View: key.NewBinding(key.WithKeys("f3", "v"), key.WithHelp("F3/v", "view")), View: key.NewBinding(key.WithKeys("f3", "v"), key.WithHelp("F3/v", "view")),
Edit: key.NewBinding(key.WithKeys("f4", "e"), key.WithHelp("F4/e", "edit")), Edit: key.NewBinding(key.WithKeys("f4", "e"), key.WithHelp("F4/e", "edit")),
Info: key.NewBinding(key.WithKeys("i"), key.WithHelp("i", "info")), Info: key.NewBinding(key.WithKeys("f9", "i"), key.WithHelp("F9/i", "info")),
SelectText: key.NewBinding(key.WithKeys("ctrl+t"), key.WithHelp("C-t", "text select")), SelectText: key.NewBinding(key.WithKeys("ctrl+t"), key.WithHelp("C-t", "text select")),
ToggleHidden: key.NewBinding(key.WithKeys("."), key.WithHelp(".", "hidden")), ToggleHidden: key.NewBinding(key.WithKeys("."), key.WithHelp(".", "hidden")),
CycleTheme: key.NewBinding(key.WithKeys("t"), key.WithHelp("t", "theme")), CycleTheme: key.NewBinding(key.WithKeys("t"), key.WithHelp("t", "theme")),
@ -67,7 +67,7 @@ func DefaultKeyMap() KeyMap {
} }
func (k KeyMap) ShortHelp() []key.Binding { func (k KeyMap) ShortHelp() []key.Binding {
return []key.Binding{k.Help, k.Copy, k.Move, k.Delete, k.Quit} return []key.Binding{k.Help, k.View, k.Copy, k.Move, k.Delete, k.Info, k.Quit}
} }
func (k KeyMap) FullHelp() [][]key.Binding { func (k KeyMap) FullHelp() [][]key.Binding {

View file

@ -143,11 +143,13 @@ type Model struct {
width int width int
height int height int
left BrowserPane left BrowserPane
right BrowserPane right BrowserPane
active PaneID active PaneID
infoMode bool infoMode bool
selectMode bool selectMode bool
viewMode bool
viewPrevInfo bool
previewModel viewport.Model previewModel viewport.Model
previewData vfs.Preview previewData vfs.Preview
@ -223,7 +225,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if selected, ok := m.activePane().Selected(); ok && selected.Path == msg.entryPath { if selected, ok := m.activePane().Selected(); ok && selected.Path == msg.entryPath {
m.applyPreview(msg.preview) m.applyPreview(msg.preview)
} }
if m.selectMode && msg.preview.Kind != vfs.PreviewKindText { if m.selectMode && !m.viewMode && msg.preview.Kind != vfs.PreviewKindText {
m.selectMode = false m.selectMode = false
return m, enableMouseCmd() return m, enableMouseCmd()
} }
@ -394,6 +396,26 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.modal.kind != modalNone { if m.modal.kind != modalNone {
return m.handleModalKey(msg) return m.handleModalKey(msg)
} }
if m.viewMode {
switch {
case key.Matches(msg, m.keys.View), key.Matches(msg, m.keys.Cancel), msg.String() == "q":
return m.exitViewMode()
case key.Matches(msg, m.keys.Up):
m.previewModel.LineUp(1)
return m, nil
case key.Matches(msg, m.keys.Down):
m.previewModel.LineDown(1)
return m, nil
case key.Matches(msg, m.keys.PageUp):
m.previewModel.LineUp(max(m.previewModel.Height-2, 1))
return m, nil
case key.Matches(msg, m.keys.PageDown):
m.previewModel.LineDown(max(m.previewModel.Height-2, 1))
return m, nil
default:
return m, nil
}
}
switch { switch {
case key.Matches(msg, m.keys.Quit): case key.Matches(msg, m.keys.Quit):
@ -525,7 +547,7 @@ func (m Model) View() string {
parts := make([]string, 0, 3) parts := make([]string, 0, 3)
parts = append(parts, panels) parts = append(parts, panels)
if m.cfg.UI.ShowFooter { if m.cfg.UI.ShowFooter && !m.viewMode {
parts = append(parts, renderFooter(m)) parts = append(parts, renderFooter(m))
} }
@ -871,20 +893,34 @@ func (m *Model) handleDelete() (tea.Model, tea.Cmd) {
func (m *Model) handleView() (tea.Model, tea.Cmd) { func (m *Model) handleView() (tea.Model, tea.Cmd) {
selected, ok := m.activePane().Selected() selected, ok := m.activePane().Selected()
if !ok || selected.IsParent || selected.IsDir { if !ok || selected.IsParent || selected.IsDir {
m.status = "Preview refreshed" m.status = "Select a file to view"
return m, m.loadPreviewCmd() return m, nil
}
if m.viewMode {
return m.exitViewMode()
} }
command, name, err := externalCommand("PAGER", []string{"less", "more"}, selected.Path) m.viewPrevInfo = m.infoMode
if err != nil { m.infoMode = true
m.status = "Preview refreshed in center pane" m.selectMode = true
return m, m.loadPreviewCmd() m.viewMode = true
} m.resizePreview()
m.syncPreviewContent()
m.status = "View mode: F3/Esc/q to close"
return m, tea.Batch(m.loadPreviewCmd(), disableMouseCmd())
}
m.status = fmt.Sprintf("Opening %s with %s", selected.DisplayName(), name) func (m *Model) exitViewMode() (tea.Model, tea.Cmd) {
return m, tea.ExecProcess(command, func(err error) tea.Msg { if !m.viewMode {
return opMsg{kind: opView, sourcePath: selected.Path, err: err} return m, nil
}) }
m.viewMode = false
m.selectMode = false
m.infoMode = m.viewPrevInfo
m.resizePreview()
m.syncPreviewContent()
m.status = "View mode: off"
return m, tea.Batch(m.loadPreviewCmd(), enableMouseCmd())
} }
func (m *Model) handleOpenExternal() (tea.Model, tea.Cmd) { func (m *Model) handleOpenExternal() (tea.Model, tea.Cmd) {
@ -926,6 +962,19 @@ func (m *Model) handleEdit() (tea.Model, tea.Cmd) {
} }
func (m *Model) handleMouse(msg tea.MouseMsg) (tea.Model, tea.Cmd) { func (m *Model) handleMouse(msg tea.MouseMsg) (tea.Model, tea.Cmd) {
if m.viewMode {
switch {
case msg.Action == tea.MouseActionPress && msg.Button == tea.MouseButtonWheelUp:
m.previewModel.LineUp(3)
return m, nil
case msg.Action == tea.MouseActionPress && msg.Button == tea.MouseButtonWheelDown:
m.previewModel.LineDown(3)
return m, nil
default:
return m, nil
}
}
switch { switch {
case msg.Action == tea.MouseActionMotion: case msg.Action == tea.MouseActionMotion:
paneID, index, ok := m.mouseTarget(msg.X, msg.Y) paneID, index, ok := m.mouseTarget(msg.X, msg.Y)
@ -1033,6 +1082,10 @@ func (m *Model) toggleInfo() (tea.Model, tea.Cmd) {
} }
func (m *Model) toggleSelectMode() (tea.Model, tea.Cmd) { func (m *Model) toggleSelectMode() (tea.Model, tea.Cmd) {
if m.viewMode {
m.status = "Close view mode first (F3/Esc/q)"
return m, nil
}
if m.selectMode { if m.selectMode {
m.selectMode = false m.selectMode = false
m.status = "Text selection mode: off" m.status = "Text selection mode: off"
@ -1120,7 +1173,9 @@ func (m *Model) openHelpModal() {
" r refresh both panes", " r refresh both panes",
"", "",
"View and Panels", "View and Panels",
" i toggle preview/info pane", " F9 / i toggle preview/info pane",
" F3 / v open read-only view mode",
" F3 / Esc / q close view mode",
" Ctrl+t toggle text selection mode in text preview", " Ctrl+t toggle text selection mode in text preview",
" Space calculate selected directory size", " Space calculate selected directory size",
" s cycle sort mode", " s cycle sort mode",