mirror of
https://github.com/boolean-maybe/tiki
synced 2026-04-21 13:37:20 +00:00
reassign action palette key
This commit is contained in:
parent
ef25f5ff98
commit
7abddcd4c6
7 changed files with 98 additions and 232 deletions
|
|
@ -4,7 +4,7 @@ import "path"
|
||||||
|
|
||||||
// AITool defines a supported AI coding assistant.
|
// AITool defines a supported AI coding assistant.
|
||||||
// To add a new tool, add an entry to the aiTools slice below.
|
// To add a new tool, add an entry to the aiTools slice below.
|
||||||
// NOTE: the action palette (press *) surfaces available actions; update docs if tool names change.
|
// NOTE: the action palette (press Ctrl+A) surfaces available actions; update docs if tool names change.
|
||||||
type AITool struct {
|
type AITool struct {
|
||||||
Key string // config identifier: "claude", "gemini", "codex", "opencode"
|
Key string // config identifier: "claude", "gemini", "codex", "opencode"
|
||||||
DisplayName string // human-readable label for UI: "Claude Code"
|
DisplayName string // human-readable label for UI: "Claude Code"
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ func DefaultGlobalActions() *ActionRegistry {
|
||||||
r.Register(Action{ID: ActionQuit, Key: tcell.KeyRune, Rune: 'q', Label: "Quit", ShowInHeader: true})
|
r.Register(Action{ID: ActionQuit, Key: tcell.KeyRune, Rune: 'q', Label: "Quit", ShowInHeader: true})
|
||||||
r.Register(Action{ID: ActionRefresh, Key: tcell.KeyRune, Rune: 'r', Label: "Refresh", ShowInHeader: true})
|
r.Register(Action{ID: ActionRefresh, Key: tcell.KeyRune, Rune: 'r', Label: "Refresh", ShowInHeader: true})
|
||||||
r.Register(Action{ID: ActionToggleHeader, Key: tcell.KeyF10, Label: "Toggle Header", ShowInHeader: true})
|
r.Register(Action{ID: ActionToggleHeader, Key: tcell.KeyF10, Label: "Toggle Header", ShowInHeader: true})
|
||||||
r.Register(Action{ID: ActionOpenPalette, Key: tcell.KeyRune, Rune: '*', Label: "All", ShowInHeader: true, HideFromPalette: true})
|
r.Register(Action{ID: ActionOpenPalette, Key: tcell.KeyCtrlA, Modifier: tcell.ModCtrl, Label: "All", ShowInHeader: true, HideFromPalette: true})
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -419,7 +419,7 @@ func TestDefaultGlobalActions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionOpenPalette should show in header with label "All"
|
// ActionOpenPalette should show in header with label "All" and use Ctrl+A binding
|
||||||
for _, a := range actions {
|
for _, a := range actions {
|
||||||
if a.ID == ActionOpenPalette {
|
if a.ID == ActionOpenPalette {
|
||||||
if !a.ShowInHeader {
|
if !a.ShowInHeader {
|
||||||
|
|
@ -428,6 +428,15 @@ func TestDefaultGlobalActions(t *testing.T) {
|
||||||
if a.Label != "All" {
|
if a.Label != "All" {
|
||||||
t.Errorf("ActionOpenPalette label = %q, want %q", a.Label, "All")
|
t.Errorf("ActionOpenPalette label = %q, want %q", a.Label, "All")
|
||||||
}
|
}
|
||||||
|
if a.Key != tcell.KeyCtrlA {
|
||||||
|
t.Errorf("ActionOpenPalette Key = %v, want KeyCtrlA", a.Key)
|
||||||
|
}
|
||||||
|
if a.Modifier != tcell.ModCtrl {
|
||||||
|
t.Errorf("ActionOpenPalette Modifier = %v, want ModCtrl", a.Modifier)
|
||||||
|
}
|
||||||
|
if a.Rune != 0 {
|
||||||
|
t.Errorf("ActionOpenPalette Rune = %v, want 0", a.Rune)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !a.ShowInHeader {
|
if !a.ShowInHeader {
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,14 @@ func (ir *InputRouter) SetPaletteConfig(pc *model.ActionPaletteConfig) {
|
||||||
func (ir *InputRouter) HandleInput(event *tcell.EventKey, currentView *ViewEntry) bool {
|
func (ir *InputRouter) HandleInput(event *tcell.EventKey, currentView *ViewEntry) bool {
|
||||||
slog.Debug("input received", "name", event.Name(), "key", int(event.Key()), "rune", string(event.Rune()), "modifiers", int(event.Modifiers()))
|
slog.Debug("input received", "name", event.Name(), "key", int(event.Key()), "rune", string(event.Rune()), "modifiers", int(event.Modifiers()))
|
||||||
|
|
||||||
// if the input box is focused, let it handle all input (including '*' and F10)
|
// palette fires regardless of focus context (Ctrl+A can't conflict with typing)
|
||||||
|
if action := ir.globalActions.Match(event); action != nil {
|
||||||
|
if action.ID == ActionOpenPalette {
|
||||||
|
return ir.handleGlobalAction(action.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the input box is focused, let it handle all remaining input (including F10)
|
||||||
if activeView := ir.navController.GetActiveView(); activeView != nil {
|
if activeView := ir.navController.GetActiveView(); activeView != nil {
|
||||||
if iv, ok := activeView.(InputableView); ok && iv.IsInputBoxFocused() {
|
if iv, ok := activeView.(InputableView); ok && iv.IsInputBoxFocused() {
|
||||||
return false
|
return false
|
||||||
|
|
@ -114,11 +121,9 @@ func (ir *InputRouter) HandleInput(event *tcell.EventKey, currentView *ViewEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
// pre-gate: global actions that must fire before task-edit Prepare() and before
|
// pre-gate: global actions that must fire before task-edit Prepare() and before
|
||||||
// search/fullscreen/editor gates. ActionOpenPalette is suppressed in TaskEditView
|
// search/fullscreen/editor gates
|
||||||
// because '*' is a typeable rune that should be inserted into fields.
|
|
||||||
if action := ir.globalActions.Match(event); action != nil {
|
if action := ir.globalActions.Match(event); action != nil {
|
||||||
isTaskEdit := currentView != nil && currentView.ViewID == model.TaskEditViewID
|
if action.ID == ActionToggleHeader {
|
||||||
if action.ID == ActionToggleHeader || (action.ID == ActionOpenPalette && !isTaskEdit) {
|
|
||||||
return ir.handleGlobalAction(action.ID)
|
return ir.handleGlobalAction(action.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/boolean-maybe/tiki/controller"
|
||||||
"github.com/boolean-maybe/tiki/model"
|
"github.com/boolean-maybe/tiki/model"
|
||||||
taskpkg "github.com/boolean-maybe/tiki/task"
|
taskpkg "github.com/boolean-maybe/tiki/task"
|
||||||
"github.com/boolean-maybe/tiki/testutil"
|
"github.com/boolean-maybe/tiki/testutil"
|
||||||
"github.com/boolean-maybe/tiki/view/taskdetail"
|
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
)
|
)
|
||||||
|
|
@ -17,10 +16,10 @@ func TestActionPalette_OpenAndClose(t *testing.T) {
|
||||||
defer ta.Cleanup()
|
defer ta.Cleanup()
|
||||||
ta.Draw()
|
ta.Draw()
|
||||||
|
|
||||||
// * opens the palette
|
// Ctrl+A opens the palette
|
||||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
ta.SendKey(tcell.KeyCtrlA, 0, tcell.ModCtrl)
|
||||||
if !ta.GetPaletteConfig().IsVisible() {
|
if !ta.GetPaletteConfig().IsVisible() {
|
||||||
t.Fatal("palette should be visible after pressing '*'")
|
t.Fatal("palette should be visible after pressing Ctrl+A")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Esc closes it
|
// Esc closes it
|
||||||
|
|
@ -60,7 +59,7 @@ func TestActionPalette_ModalBlocksGlobals(t *testing.T) {
|
||||||
startVisible := hc.IsVisible()
|
startVisible := hc.IsVisible()
|
||||||
|
|
||||||
// open palette
|
// open palette
|
||||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
ta.SendKey(tcell.KeyCtrlA, 0, tcell.ModCtrl)
|
||||||
if !ta.GetPaletteConfig().IsVisible() {
|
if !ta.GetPaletteConfig().IsVisible() {
|
||||||
t.Fatal("palette should be open")
|
t.Fatal("palette should be open")
|
||||||
}
|
}
|
||||||
|
|
@ -76,13 +75,13 @@ func TestActionPalette_ModalBlocksGlobals(t *testing.T) {
|
||||||
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestActionPalette_AsteriskFiltersInPalette(t *testing.T) {
|
func TestActionPalette_AsteriskIsFilterTextInPalette(t *testing.T) {
|
||||||
ta := testutil.NewTestApp(t)
|
ta := testutil.NewTestApp(t)
|
||||||
defer ta.Cleanup()
|
defer ta.Cleanup()
|
||||||
ta.Draw()
|
ta.Draw()
|
||||||
|
|
||||||
// open palette
|
// open palette
|
||||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
ta.SendKey(tcell.KeyCtrlA, 0, tcell.ModCtrl)
|
||||||
if !ta.GetPaletteConfig().IsVisible() {
|
if !ta.GetPaletteConfig().IsVisible() {
|
||||||
t.Fatal("palette should be open")
|
t.Fatal("palette should be open")
|
||||||
}
|
}
|
||||||
|
|
@ -98,215 +97,68 @@ func TestActionPalette_AsteriskFiltersInPalette(t *testing.T) {
|
||||||
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getEditView type-asserts the active view to *taskdetail.TaskEditView.
|
func TestActionPalette_AsteriskDoesNotOpenPalette(t *testing.T) {
|
||||||
func getEditView(t *testing.T, ta *testutil.TestApp) *taskdetail.TaskEditView {
|
ta := testutil.NewTestApp(t)
|
||||||
t.Helper()
|
defer ta.Cleanup()
|
||||||
|
ta.Draw()
|
||||||
|
|
||||||
|
// send '*' as a rune on the plugin view
|
||||||
|
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
||||||
|
|
||||||
|
if ta.GetPaletteConfig().IsVisible() {
|
||||||
|
t.Fatal("palette should NOT open when '*' is pressed — only Ctrl+A should open it")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestActionPalette_OpensInTaskEdit(t *testing.T) {
|
||||||
|
ta := testutil.NewTestApp(t)
|
||||||
|
defer ta.Cleanup()
|
||||||
|
|
||||||
|
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
||||||
|
t.Fatalf("create task: %v", err)
|
||||||
|
}
|
||||||
|
if err := ta.TaskStore.Reload(); err != nil {
|
||||||
|
t.Fatalf("reload: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
||||||
|
TaskID: "TIKI-1",
|
||||||
|
Focus: model.EditFieldTitle,
|
||||||
|
}))
|
||||||
|
ta.Draw()
|
||||||
|
|
||||||
|
// Ctrl+A should open the palette even in task edit
|
||||||
|
ta.SendKey(tcell.KeyCtrlA, 0, tcell.ModCtrl)
|
||||||
|
|
||||||
|
if !ta.GetPaletteConfig().IsVisible() {
|
||||||
|
t.Fatal("palette should open when Ctrl+A is pressed in task edit view")
|
||||||
|
}
|
||||||
|
|
||||||
|
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestActionPalette_OpensWithInputBoxFocused(t *testing.T) {
|
||||||
|
ta := testutil.NewTestApp(t)
|
||||||
|
defer ta.Cleanup()
|
||||||
|
|
||||||
|
ta.NavController.PushView(model.MakePluginViewID("Kanban"), nil)
|
||||||
|
ta.Draw()
|
||||||
|
|
||||||
|
// open search to focus input box
|
||||||
|
ta.SendKey(tcell.KeyRune, '/', tcell.ModNone)
|
||||||
|
|
||||||
v := ta.NavController.GetActiveView()
|
v := ta.NavController.GetActiveView()
|
||||||
ev, ok := v.(*taskdetail.TaskEditView)
|
iv, ok := v.(controller.InputableView)
|
||||||
if !ok {
|
if !ok || !iv.IsInputBoxFocused() {
|
||||||
t.Fatalf("expected *taskdetail.TaskEditView, got %T", v)
|
t.Fatal("input box should be focused after '/'")
|
||||||
}
|
|
||||||
return ev
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionPalette_BlockedInTaskEdit_DraftFirstKey(t *testing.T) {
|
|
||||||
ta := testutil.NewTestApp(t)
|
|
||||||
defer ta.Cleanup()
|
|
||||||
|
|
||||||
ta.NavController.PushView(model.MakePluginViewID("Kanban"), nil)
|
|
||||||
ta.Draw()
|
|
||||||
|
|
||||||
// press 'n' to create new task (draft path, title focused)
|
|
||||||
ta.SendKey(tcell.KeyRune, 'n', tcell.ModNone)
|
|
||||||
|
|
||||||
// very first key in edit view is '*'
|
|
||||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
|
||||||
|
|
||||||
if ta.GetPaletteConfig().IsVisible() {
|
|
||||||
t.Fatal("palette should NOT open when '*' is pressed in task edit (draft, first key)")
|
|
||||||
}
|
|
||||||
ev := getEditView(t, ta)
|
|
||||||
if got := ev.GetEditedTitle(); got != "*" {
|
|
||||||
t.Fatalf("title = %q, want %q", got, "*")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionPalette_BlockedInTaskEdit_ExistingFirstKey(t *testing.T) {
|
|
||||||
ta := testutil.NewTestApp(t)
|
|
||||||
defer ta.Cleanup()
|
|
||||||
|
|
||||||
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
|
||||||
t.Fatalf("create task: %v", err)
|
|
||||||
}
|
|
||||||
if err := ta.TaskStore.Reload(); err != nil {
|
|
||||||
t.Fatalf("reload: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// push task edit directly (non-draft path)
|
|
||||||
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
|
||||||
TaskID: "TIKI-1",
|
|
||||||
Focus: model.EditFieldTitle,
|
|
||||||
}))
|
|
||||||
ta.Draw()
|
|
||||||
|
|
||||||
// first key is '*'
|
|
||||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
|
||||||
|
|
||||||
if ta.GetPaletteConfig().IsVisible() {
|
|
||||||
t.Fatal("palette should NOT open when '*' is pressed in task edit (existing, first key)")
|
|
||||||
}
|
|
||||||
ev := getEditView(t, ta)
|
|
||||||
if got := ev.GetEditedTitle(); got != "Test*" {
|
|
||||||
t.Fatalf("title = %q, want %q", got, "Test*")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionPalette_BlockedInTaskEdit_TitleMidText(t *testing.T) {
|
|
||||||
ta := testutil.NewTestApp(t)
|
|
||||||
defer ta.Cleanup()
|
|
||||||
|
|
||||||
ta.NavController.PushView(model.MakePluginViewID("Kanban"), nil)
|
|
||||||
ta.Draw()
|
|
||||||
|
|
||||||
ta.SendKey(tcell.KeyRune, 'n', tcell.ModNone)
|
|
||||||
ta.SendText("ab*cd")
|
|
||||||
|
|
||||||
if ta.GetPaletteConfig().IsVisible() {
|
|
||||||
t.Fatal("palette should NOT open when '*' is typed mid-title")
|
|
||||||
}
|
|
||||||
ev := getEditView(t, ta)
|
|
||||||
if got := ev.GetEditedTitle(); got != "ab*cd" {
|
|
||||||
t.Fatalf("title = %q, want %q", got, "ab*cd")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionPalette_BlockedInTaskEdit_Description(t *testing.T) {
|
|
||||||
ta := testutil.NewTestApp(t)
|
|
||||||
defer ta.Cleanup()
|
|
||||||
|
|
||||||
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
|
||||||
t.Fatalf("create task: %v", err)
|
|
||||||
}
|
|
||||||
if err := ta.TaskStore.Reload(); err != nil {
|
|
||||||
t.Fatalf("reload: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
|
||||||
TaskID: "TIKI-1",
|
|
||||||
Focus: model.EditFieldDescription,
|
|
||||||
DescOnly: true,
|
|
||||||
}))
|
|
||||||
ta.Draw()
|
|
||||||
|
|
||||||
ta.SendText("x*y")
|
|
||||||
|
|
||||||
if ta.GetPaletteConfig().IsVisible() {
|
|
||||||
t.Fatal("palette should NOT open when '*' is typed in description")
|
|
||||||
}
|
|
||||||
ev := getEditView(t, ta)
|
|
||||||
desc := ev.GetEditedDescription()
|
|
||||||
if !strings.Contains(desc, "x*y") {
|
|
||||||
t.Fatalf("description = %q, should contain %q", desc, "x*y")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionPalette_BlockedInTaskEdit_Tags(t *testing.T) {
|
|
||||||
ta := testutil.NewTestApp(t)
|
|
||||||
defer ta.Cleanup()
|
|
||||||
|
|
||||||
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
|
||||||
t.Fatalf("create task: %v", err)
|
|
||||||
}
|
|
||||||
if err := ta.TaskStore.Reload(); err != nil {
|
|
||||||
t.Fatalf("reload: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
|
||||||
TaskID: "TIKI-1",
|
|
||||||
TagsOnly: true,
|
|
||||||
}))
|
|
||||||
ta.Draw()
|
|
||||||
|
|
||||||
ta.SendText("tag*val")
|
|
||||||
|
|
||||||
if ta.GetPaletteConfig().IsVisible() {
|
|
||||||
t.Fatal("palette should NOT open when '*' is typed in tags")
|
|
||||||
}
|
|
||||||
ev := getEditView(t, ta)
|
|
||||||
tags := ev.GetEditedTags()
|
|
||||||
if len(tags) != 1 || tags[0] != "tag*val" {
|
|
||||||
t.Fatalf("tags = %v, want [tag*val]", tags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionPalette_BlockedInTaskEdit_Assignee(t *testing.T) {
|
|
||||||
ta := testutil.NewTestApp(t)
|
|
||||||
defer ta.Cleanup()
|
|
||||||
|
|
||||||
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
|
||||||
t.Fatalf("create task: %v", err)
|
|
||||||
}
|
|
||||||
if err := ta.TaskStore.Reload(); err != nil {
|
|
||||||
t.Fatalf("reload: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// push task edit (default title focus)
|
|
||||||
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
|
||||||
TaskID: "TIKI-1",
|
|
||||||
Focus: model.EditFieldTitle,
|
|
||||||
}))
|
|
||||||
ta.Draw()
|
|
||||||
|
|
||||||
// tab 5× to Assignee: Title→Status→Type→Priority→Points→Assignee
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
ta.SendKey(tcell.KeyTab, 0, tcell.ModNone)
|
|
||||||
}
|
|
||||||
|
|
||||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
|
||||||
|
|
||||||
if ta.GetPaletteConfig().IsVisible() {
|
|
||||||
t.Fatal("palette should NOT open when '*' is pressed on Assignee field")
|
|
||||||
}
|
|
||||||
editTask := ta.EditingTask()
|
|
||||||
if editTask == nil {
|
|
||||||
t.Fatal("expected editing task to be non-nil")
|
|
||||||
}
|
|
||||||
if editTask.Assignee != "Unassigned*" {
|
|
||||||
t.Fatalf("assignee = %q, want %q ('*' appended to default value)", editTask.Assignee, "Unassigned*")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionPalette_BlockedInTaskEdit_NonTypeableField(t *testing.T) {
|
|
||||||
ta := testutil.NewTestApp(t)
|
|
||||||
defer ta.Cleanup()
|
|
||||||
|
|
||||||
if err := testutil.CreateTestTask(ta.TaskDir, "TIKI-1", "Test", taskpkg.StatusReady, taskpkg.TypeStory); err != nil {
|
|
||||||
t.Fatalf("create task: %v", err)
|
|
||||||
}
|
|
||||||
if err := ta.TaskStore.Reload(); err != nil {
|
|
||||||
t.Fatalf("reload: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// push task edit (default title focus)
|
|
||||||
ta.NavController.PushView(model.TaskEditViewID, model.EncodeTaskEditParams(model.TaskEditParams{
|
|
||||||
TaskID: "TIKI-1",
|
|
||||||
Focus: model.EditFieldTitle,
|
|
||||||
}))
|
|
||||||
ta.Draw()
|
|
||||||
|
|
||||||
// tab 1× to Status (non-typeable field)
|
|
||||||
ta.SendKey(tcell.KeyTab, 0, tcell.ModNone)
|
|
||||||
|
|
||||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
|
||||||
|
|
||||||
if ta.GetPaletteConfig().IsVisible() {
|
|
||||||
t.Fatal("palette should NOT open when '*' is pressed on Status field")
|
|
||||||
}
|
|
||||||
editTask := ta.EditingTask()
|
|
||||||
if editTask == nil {
|
|
||||||
t.Fatal("expected editing task to be non-nil")
|
|
||||||
}
|
|
||||||
if editTask.Status != taskpkg.StatusReady {
|
|
||||||
t.Fatalf("status = %v, want %v (rune should be silently ignored)", editTask.Status, taskpkg.StatusReady)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ctrl+A should open the palette even with input box focused
|
||||||
|
ta.SendKey(tcell.KeyCtrlA, 0, tcell.ModCtrl)
|
||||||
|
|
||||||
|
if !ta.GetPaletteConfig().IsVisible() {
|
||||||
|
t.Fatal("palette should open when Ctrl+A is pressed with input box focused")
|
||||||
|
}
|
||||||
|
|
||||||
|
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -372,7 +372,7 @@ func TestInputAction_EmptySearchEnterIsNoOp(t *testing.T) {
|
||||||
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInputAction_PaletteBlockedDuringModal(t *testing.T) {
|
func TestInputAction_PaletteOpensDuringModal(t *testing.T) {
|
||||||
ta := setupInputActionTest(t)
|
ta := setupInputActionTest(t)
|
||||||
defer ta.Cleanup()
|
defer ta.Cleanup()
|
||||||
|
|
||||||
|
|
@ -383,13 +383,13 @@ func TestInputAction_PaletteBlockedDuringModal(t *testing.T) {
|
||||||
t.Fatal("input box should be focused")
|
t.Fatal("input box should be focused")
|
||||||
}
|
}
|
||||||
|
|
||||||
// '*' should be typed into the input box as text, not open the palette
|
// Ctrl+A should open the palette even while input box is focused
|
||||||
ta.SendKey(tcell.KeyRune, '*', tcell.ModNone)
|
ta.SendKey(tcell.KeyCtrlA, 0, tcell.ModCtrl)
|
||||||
if ta.GetPaletteConfig().IsVisible() {
|
if !ta.GetPaletteConfig().IsVisible() {
|
||||||
t.Fatal("palette should not open while input box is editing")
|
t.Fatal("palette should open when Ctrl+A is pressed with input box focused")
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancel and clean up
|
// clean up
|
||||||
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
ta.SendKey(tcell.KeyEscape, 0, tcell.ModNone)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ func (dv *DokiView) build() {
|
||||||
// The default Help plugin previously used this with embedded markdown:
|
// The default Help plugin previously used this with embedded markdown:
|
||||||
// cnt := map[string]string{"Help": helpMd, "tiki.md": tikiMd, "view.md": customMd}
|
// cnt := map[string]string{"Help": helpMd, "tiki.md": tikiMd, "view.md": customMd}
|
||||||
// provider := &internalDokiProvider{content: cnt}
|
// provider := &internalDokiProvider{content: cnt}
|
||||||
// That usage was replaced by the action palette (press * to open).
|
// That usage was replaced by the action palette (press Ctrl+A to open).
|
||||||
case "internal":
|
case "internal":
|
||||||
provider := &internalDokiProvider{content: map[string]string{}}
|
provider := &internalDokiProvider{content: map[string]string{}}
|
||||||
content, err = provider.FetchContent(nav.NavElement{Text: dv.pluginDef.Text})
|
content, err = provider.FetchContent(nav.NavElement{Text: dv.pluginDef.Text})
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue