▍ humdrum codex / glint v1.0.2

change: remap keys — Ctrl+P toggles preview, Ctrl+F opens the picker

0a0d5ea01ef500c81bb68e121c020697b770e4c2
humdrum <me@humdrum.me> · 2026-06-28 16:06

parent a0797b7f

change: remap keys — Ctrl+P toggles preview, Ctrl+F opens the picker

Preview/edit toggle moves from Ctrl+R to Ctrl+P; the fuzzy picker moves from
Ctrl+P to Ctrl+F (and its dirty-discard confirm prompt updates to Ctrl+F).
Ctrl+R is now unbound. Tests and the dirty-confirm guard updated to match.

2 files changed

internal/app/app.go +4 −4
@@ -136,7 +136,7 @@ 	if msg.Type != tea.KeyCtrlQ {
 		a.quitArmed = false
 	}
 	// Disarm pending-discard unless the same action is being re-pressed.
-	if !(msg.Type == tea.KeyCtrlP && a.pending == discardPicker) &&
+	if !(msg.Type == tea.KeyCtrlF && a.pending == discardPicker) &&
 		!(msg.Type == tea.KeyCtrlD && a.pending == discardDaily) &&
 		!(msg.Type == tea.KeyCtrlN && a.pending == discardNew) {
 		a.pending = discardNone
@@ -152,14 +152,14 @@ 		}
 		return a, tea.Quit
 	case tea.KeyCtrlS:
 		return a.save()
-	case tea.KeyCtrlR:
+	case tea.KeyCtrlP:
 		return a.togglePreview()
 	case tea.KeyCtrlT:
 		return a.cycleTheme()
-	case tea.KeyCtrlP:
+	case tea.KeyCtrlF:
 		if a.editor.Dirty && a.pending != discardPicker {
 			a.pending = discardPicker
-			a.status = "Unsaved changes — Ctrl+P again to discard"
+			a.status = "Unsaved changes — Ctrl+F again to discard"
 			return a, nil
 		}
 		a.pending = discardNone
internal/app/app_test.go +21 −21
@@ -136,15 +136,15 @@ 		t.Errorf("Ctrl+S on unnamed buffer: mode = %d, want ModeSaveAs", a.mode)
 	}
 }
 
-func TestCtrlRTogglesPreview(t *testing.T) {
+func TestCtrlPTogglesPreview(t *testing.T) {
 	a := newApp()
 	a.Update(tea.WindowSizeMsg{Width: 80, Height: 24})
 	a.editor.SetContent([]byte("# hello"))
-	a.Update(tea.KeyMsg{Type: tea.KeyCtrlR})
+	a.Update(tea.KeyMsg{Type: tea.KeyCtrlP})
 	if a.mode != ModePreview {
 		t.Errorf("mode = %d, want ModePreview", a.mode)
 	}
-	a.Update(tea.KeyMsg{Type: tea.KeyCtrlR})
+	a.Update(tea.KeyMsg{Type: tea.KeyCtrlP})
 	if a.mode != ModeEditor {
 		t.Errorf("mode = %d, want ModeEditor after second toggle", a.mode)
 	}
@@ -154,21 +154,21 @@ func TestEscReturnsToEditor(t *testing.T) {
 	a := newApp()
 	a.Update(tea.WindowSizeMsg{Width: 80, Height: 24})
 	a.editor.SetContent([]byte("x"))
-	a.Update(tea.KeyMsg{Type: tea.KeyCtrlR})
+	a.Update(tea.KeyMsg{Type: tea.KeyCtrlP})
 	a.Update(tea.KeyMsg{Type: tea.KeyEsc})
 	if a.mode != ModeEditor {
 		t.Errorf("Esc should return to editor, mode = %d", a.mode)
 	}
 }
 
-func TestCtrlPOpensPicker(t *testing.T) {
+func TestCtrlFOpensPicker(t *testing.T) {
 	dir := t.TempDir()
 	os.WriteFile(filepath.Join(dir, "note.md"), []byte("x"), 0o644)
 	cfg := config.Default()
 	cfg.VaultDir = dir
 	a := New(cfg)
 	a.Update(tea.WindowSizeMsg{Width: 80, Height: 24})
-	a.Update(tea.KeyMsg{Type: tea.KeyCtrlP})
+	a.Update(tea.KeyMsg{Type: tea.KeyCtrlF})
 	if a.mode != ModePicker {
 		t.Errorf("mode = %d, want ModePicker", a.mode)
 	}
@@ -237,7 +237,7 @@ 		t.Errorf("daily file should exist: %v", err)
 	}
 }
 
-func TestCtrlPDirtyNeedsConfirm(t *testing.T) {
+func TestCtrlFDirtyNeedsConfirm(t *testing.T) {
 	dir := t.TempDir()
 	os.WriteFile(filepath.Join(dir, "note.md"), []byte("x"), 0o644)
 	cfg := config.Default()
@@ -246,22 +246,22 @@ 	a := New(cfg)
 	a.Update(tea.WindowSizeMsg{Width: 80, Height: 24})
 	// Make dirty
 	a.editor.HandleKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'z'}})
-	// First Ctrl+P — should arm, not open picker
-	a.Update(tea.KeyMsg{Type: tea.KeyCtrlP})
+	// First Ctrl+F — should arm, not open picker
+	a.Update(tea.KeyMsg{Type: tea.KeyCtrlF})
 	if a.mode != ModeEditor {
-		t.Errorf("first Ctrl+P on dirty editor: mode = %d, want ModeEditor", a.mode)
+		t.Errorf("first Ctrl+F on dirty editor: mode = %d, want ModeEditor", a.mode)
 	}
 	if !strings.Contains(a.status, "discard") {
-		t.Errorf("first Ctrl+P on dirty editor: status = %q, want it to contain 'discard'", a.status)
+		t.Errorf("first Ctrl+F on dirty editor: status = %q, want it to contain 'discard'", a.status)
 	}
-	// Second Ctrl+P — should open picker
-	a.Update(tea.KeyMsg{Type: tea.KeyCtrlP})
+	// Second Ctrl+F — should open picker
+	a.Update(tea.KeyMsg{Type: tea.KeyCtrlF})
 	if a.mode != ModePicker {
-		t.Errorf("second Ctrl+P: mode = %d, want ModePicker", a.mode)
+		t.Errorf("second Ctrl+F: mode = %d, want ModePicker", a.mode)
 	}
 }
 
-func TestCtrlPDirtyDisarmedByOtherKey(t *testing.T) {
+func TestCtrlFDirtyDisarmedByOtherKey(t *testing.T) {
 	dir := t.TempDir()
 	os.WriteFile(filepath.Join(dir, "note.md"), []byte("x"), 0o644)
 	cfg := config.Default()
@@ -270,14 +270,14 @@ 	a := New(cfg)
 	a.Update(tea.WindowSizeMsg{Width: 80, Height: 24})
 	// Make dirty
 	a.editor.HandleKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'z'}})
-	// First Ctrl+P — arms
-	a.Update(tea.KeyMsg{Type: tea.KeyCtrlP})
+	// First Ctrl+F — arms
+	a.Update(tea.KeyMsg{Type: tea.KeyCtrlF})
 	// Press a different key — disarms pending
 	a.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'a'}})
-	// Second Ctrl+P — should re-arm (not open) because pending was cleared
-	a.Update(tea.KeyMsg{Type: tea.KeyCtrlP})
+	// Second Ctrl+F — should re-arm (not open) because pending was cleared
+	a.Update(tea.KeyMsg{Type: tea.KeyCtrlF})
 	if a.mode != ModeEditor {
-		t.Errorf("Ctrl+P after disarm should re-arm, not open picker: mode = %d", a.mode)
+		t.Errorf("Ctrl+F after disarm should re-arm, not open picker: mode = %d", a.mode)
 	}
 }
 
@@ -336,7 +336,7 @@ 	cfg := config.Default()
 	cfg.VaultDir = dir
 	a := New(cfg)
 	a.Update(tea.WindowSizeMsg{Width: 80, Height: 24})
-	a.Update(tea.KeyMsg{Type: tea.KeyCtrlP}) // open picker
+	a.Update(tea.KeyMsg{Type: tea.KeyCtrlF}) // open picker (Ctrl+F)
 	if a.mode != ModePicker {
 		t.Fatalf("expected picker mode")
 	}