office: say exactly what to do next; l opens lazygit and t opens the tool chooser everywhere; a remote run gets a terminal to ask in
This commit is contained in:
47
tui/board.go
47
tui/board.go
@@ -183,27 +183,17 @@ func (m boardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
if m.tools {
|
||||
switch msg.String() {
|
||||
case "esc", "q", "t":
|
||||
key := msg.String()
|
||||
if key == "esc" || key == "q" || key == "t" {
|
||||
m.tools = false
|
||||
return m, nil
|
||||
case "up", "k":
|
||||
m.toolAt = max(0, m.toolAt-1)
|
||||
return m, nil
|
||||
case "down", "j":
|
||||
m.toolAt = min(len(tools)-1, m.toolAt+1)
|
||||
return m, nil
|
||||
case "g":
|
||||
m.toolAt = 0
|
||||
return m, nil
|
||||
case "G":
|
||||
m.toolAt = len(tools) - 1
|
||||
return m, nil
|
||||
case "enter":
|
||||
t := tools[m.toolAt]
|
||||
m.tools = false
|
||||
if err := openTool(m.folder, t.name); err != nil {
|
||||
m.err = err
|
||||
}
|
||||
if consumed, chosen := toolPickerKeys(key, &m.toolAt); consumed {
|
||||
if chosen != "" {
|
||||
m.tools = false
|
||||
if err := openTool(m.folder, chosen); err != nil {
|
||||
m.err = err
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@@ -247,6 +237,12 @@ func (m boardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case "t":
|
||||
m.tools = true
|
||||
return m, nil
|
||||
case "l":
|
||||
// lazygit in the project folder, the same key as on the other panes.
|
||||
if err := openTool(m.folder, "lazygit"); err != nil {
|
||||
m.err = err
|
||||
}
|
||||
return m, nil
|
||||
case "/":
|
||||
m.typing = true
|
||||
return m, nil
|
||||
@@ -337,7 +333,7 @@ func (m boardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.needsTab--
|
||||
}
|
||||
return m, nil
|
||||
case "]", "l":
|
||||
case "]":
|
||||
if m.focus == focusNeeds && m.needsTab < 1 {
|
||||
m.needsTab++
|
||||
}
|
||||
@@ -406,15 +402,7 @@ func (m boardModel) View() string {
|
||||
}, m.width, m.height)
|
||||
}
|
||||
if m.tools {
|
||||
entries := make([][2]string, 0, len(tools))
|
||||
for i, t := range tools {
|
||||
mark := " "
|
||||
if i == m.toolAt {
|
||||
mark = "▸ "
|
||||
}
|
||||
entries = append(entries, [2]string{mark + t.name, t.about})
|
||||
}
|
||||
return helpOverlay("open a tool pane here", entries, m.width, m.height)
|
||||
return toolMenu(m.toolAt, m.width, m.height)
|
||||
}
|
||||
if m.showHelp {
|
||||
return helpOverlay("keys on this panel", [][2]string{
|
||||
@@ -519,6 +507,7 @@ func (m boardModel) footerLine() string {
|
||||
keyHint("u", "unblock"),
|
||||
keyHint("c", "cancel"),
|
||||
keyHint("t", "tools"),
|
||||
keyHint("l", "lazygit"),
|
||||
keyHint("/", "filter"),
|
||||
keyHint("?", "keys"),
|
||||
keyHint("tab", "panel"),
|
||||
|
||||
@@ -52,8 +52,10 @@ type projectModel struct {
|
||||
err error
|
||||
readAt time.Time
|
||||
|
||||
git gitState
|
||||
wts []worktreeRow
|
||||
git gitState
|
||||
wts []worktreeRow
|
||||
tools bool
|
||||
toolAt int
|
||||
|
||||
tab int
|
||||
scroll int
|
||||
@@ -168,6 +170,25 @@ func (m projectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, tea.Batch(m.load(), tickEvery(10*time.Second))
|
||||
|
||||
case tea.KeyMsg:
|
||||
// The tool chooser owns every key while it is open, before the
|
||||
// confirmation dialogs and before the tabs.
|
||||
if m.tools {
|
||||
key := msg.String()
|
||||
if key == "esc" || key == "q" || key == "t" {
|
||||
m.tools = false
|
||||
return m, nil
|
||||
}
|
||||
if consumed, chosen := toolPickerKeys(key, &m.toolAt); consumed {
|
||||
if chosen != "" {
|
||||
m.tools = false
|
||||
if err := openTool(m.folder, chosen); err != nil {
|
||||
m.err = err
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
if m.confirmSwitch {
|
||||
switch msg.String() {
|
||||
case "y":
|
||||
@@ -204,8 +225,11 @@ func (m projectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case "r":
|
||||
return m, m.load()
|
||||
case "t":
|
||||
// lazygit, in the project folder: it is the tool that can stage,
|
||||
// exclude and commit. Nothing here reimplements it.
|
||||
m.tools = true
|
||||
return m, nil
|
||||
case "l":
|
||||
// lazygit in the project folder: the tool that stages, excludes and
|
||||
// commits. `t` is the chooser, as it is on every other pane.
|
||||
if err := openTool(m.folder, "lazygit"); err != nil {
|
||||
m.err = err
|
||||
}
|
||||
@@ -224,13 +248,13 @@ func (m projectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
case "(", "[", "h":
|
||||
case "(", "[":
|
||||
if m.tab > 0 {
|
||||
m.tab--
|
||||
m.scroll = 0
|
||||
}
|
||||
return m, nil
|
||||
case ")", "]", "l", "tab":
|
||||
case ")", "]", "tab":
|
||||
if m.tab < len(projectTabs)-1 {
|
||||
m.tab++
|
||||
m.scroll = 0
|
||||
@@ -289,10 +313,14 @@ func (m projectModel) View() string {
|
||||
{"esc or n", "stay here"},
|
||||
}, m.width, m.height)
|
||||
}
|
||||
if m.tools {
|
||||
return toolMenu(m.toolAt, m.width, m.height)
|
||||
}
|
||||
if m.showHelp {
|
||||
return helpOverlay("the project", [][2]string{
|
||||
{"( )", "overview, git, facts, costs, settings, projects"},
|
||||
{"t", "open lazygit in this folder"},
|
||||
{"t", "open a tool pane here (lazygit, yazi, nvim, …)"},
|
||||
{"l", "lazygit, straight away"},
|
||||
{"[ ] h l", "previous and next tab"},
|
||||
{"↑ ↓ j k", "scroll"},
|
||||
{"r", "read again"},
|
||||
@@ -344,7 +372,8 @@ func (m projectModel) View() string {
|
||||
|
||||
footer := hintLine([]string{
|
||||
keyHint("( )", "tab"),
|
||||
keyHint("t", "lazygit"),
|
||||
keyHint("t", "tools"),
|
||||
keyHint("l", "lazygit"),
|
||||
keyHint("↑↓", "scroll"),
|
||||
keyHint("r", "refresh"),
|
||||
keyHint("R", "rebuild"),
|
||||
|
||||
@@ -215,3 +215,40 @@ func switchWorkspace(name string) error {
|
||||
}
|
||||
return fmt.Errorf("no workspace called %q", name)
|
||||
}
|
||||
|
||||
// toolMenu is the chooser used by every pane that offers `t`. It lives here so
|
||||
// the board and the project screen cannot drift apart in how they list the same
|
||||
// tools.
|
||||
func toolMenu(at, width, height int) string {
|
||||
entries := make([][2]string, 0, len(tools))
|
||||
for i, t := range tools {
|
||||
mark := " "
|
||||
if i == at {
|
||||
mark = "▸ "
|
||||
}
|
||||
entries = append(entries, [2]string{mark + t.name, t.about})
|
||||
}
|
||||
return helpOverlay("open a tool pane here", entries, width, height)
|
||||
}
|
||||
|
||||
// toolPickerKeys handles the chooser and says whether the key was consumed.
|
||||
// `selected` is the tool to open, or empty.
|
||||
func toolPickerKeys(key string, at *int) (consumed bool, selected string) {
|
||||
switch key {
|
||||
case "up", "k":
|
||||
*at = max(0, *at-1)
|
||||
return true, ""
|
||||
case "down", "j":
|
||||
*at = min(len(tools)-1, *at+1)
|
||||
return true, ""
|
||||
case "g":
|
||||
*at = 0
|
||||
return true, ""
|
||||
case "G":
|
||||
*at = len(tools) - 1
|
||||
return true, ""
|
||||
case "enter":
|
||||
return true, tools[*at].name
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user