feat(tui): jot target indicator and entry detail refresh - #18
Conversation
Displays "Jotting into: # Meeting Notes" (or similar) above the jot textarea so the user knows which entry will receive the jot. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add screenEntryDetail case to jotCompleteMsg handler so the viewport reloads after jotting into a viewed entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| // Reload day detail by reinitializing on current screen | ||
| return m.loadDayDetail() | ||
| case screenEntryDetail: | ||
| return m.loadEntryDetail(m.entry.ID) |
Check failure
Code scanning / gavel
Detect critical bugs, logic errors, and potential runtime failures Error
| func (m pickerModel) jotTargetLabel() string { | ||
| if m.jotTarget == nil { | ||
| return "Jotting into: new entry" | ||
| } |
Check warning
Code scanning / gavel
Identify code smells that impact maintainability and readability Warning
|
|
||
| func TestJotTargetIndicator(t *testing.T) { | ||
| now := time.Now() | ||
| today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) |
Check warning
Code scanning / gavel
Assess test quality, coverage gaps, and testing best practices Warning test
| ID: "other01", | ||
| Content: "# Meeting Notes\n\nSome notes", | ||
| CreatedAt: today.Add(10 * time.Hour), | ||
| UpdatedAt: today.Add(10 * time.Hour), | ||
| } | ||
|
|
||
| store := &mockStorage{ | ||
| entries: map[string][]entry.Entry{ | ||
| today.Format("2006-01-02"): {otherEntry}, | ||
| }, | ||
| byID: map[string]entry.Entry{ | ||
| "other01": otherEntry, | ||
| }, | ||
| } | ||
|
|
||
| cfg := TUIConfig{Editor: "vi", DefaultTemplate: "", Theme: presets["default-dark"]} | ||
| m := newTUIModel(store, cfg) | ||
| m.screen = screenToday | ||
| m.todayFocus = focusEntryList | ||
| sized, _ := m.Update(tea.WindowSizeMsg{Width: 80, Height: 24}) | ||
| m = sized.(pickerModel) | ||
|
|
||
| // Set up entry list | ||
| items := []list.Item{entryItem{entry: otherEntry}} | ||
| m.todayList = list.New(items, list.NewDefaultDelegate(), 80, 20) | ||
|
|
||
| // Start jot — target should be otherEntry | ||
| started, _ := m.startJot() | ||
| m = started.(pickerModel) | ||
|
|
||
| view := m.View() | ||
| stripped := stripANSI(view) | ||
| if !strings.Contains(stripped, "Jotting into:") { | ||
| t.Error("Expected 'Jotting into:' indicator in view") | ||
| } | ||
| if !strings.Contains(stripped, "Meeting Notes") { | ||
| t.Error("Expected target entry preview in jot indicator") | ||
| } | ||
| } | ||
|
|
||
| func TestJotFromEntryDetail_RefreshesEntry(t *testing.T) { | ||
| now := time.Now() | ||
| today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) | ||
|
|
||
| viewedEntry := entry.Entry{ | ||
| ID: "viewed01", | ||
| Content: "# Viewed Entry\n\nOriginal content", | ||
| CreatedAt: today.Add(10 * time.Hour), | ||
| UpdatedAt: today.Add(10 * time.Hour), | ||
| } | ||
|
|
||
| // Store has an updated version of the entry (simulating post-jot state) | ||
| updatedEntry := viewedEntry | ||
| updatedEntry.Content = "# Viewed Entry\n\nOriginal content\n- **14:00** new jot" | ||
|
|
||
| store := &mockStorage{ | ||
| entries: map[string][]entry.Entry{ |
Check warning
Code scanning / gavel
Identify code smells that impact maintainability and readability Warning test
| // Start jot — target should be otherEntry | ||
| started, _ := m.startJot() | ||
| m = started.(pickerModel) | ||
|
|
||
| view := m.View() | ||
| stripped := stripANSI(view) |
Check warning
Code scanning / gavel
Assess test quality, coverage gaps, and testing best practices Warning test
| func TestJotIntoSelectedEntry_TodayScreen(t *testing.T) { | ||
| now := time.Now() | ||
| today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) | ||
|
|
||
| dailyEntry := entry.Entry{ | ||
| ID: "daily01", | ||
| Content: "# Daily\n\nDaily content", | ||
| CreatedAt: today.Add(8 * time.Hour), | ||
| UpdatedAt: today.Add(8 * time.Hour), | ||
| } | ||
| otherEntry := entry.Entry{ | ||
| ID: "other01", | ||
| Content: "# Side Notes", | ||
| CreatedAt: today.Add(10 * time.Hour), | ||
| UpdatedAt: today.Add(10 * time.Hour), | ||
| } | ||
|
|
||
| store := &mockStorage{ | ||
| entries: map[string][]entry.Entry{ | ||
| today.Format("2006-01-02"): {otherEntry, dailyEntry}, | ||
| }, | ||
| byID: map[string]entry.Entry{ | ||
| "daily01": dailyEntry, | ||
| "other01": otherEntry, | ||
| }, | ||
| } | ||
|
|
||
| cfg := TUIConfig{Editor: "vi", DefaultTemplate: ""} | ||
| m := newTUIModel(store, cfg) | ||
| m.screen = screenToday | ||
| m.dailyEntry = &dailyEntry | ||
| m.todayFocus = focusEntryList | ||
| sized, _ := m.Update(tea.WindowSizeMsg{Width: 80, Height: 24}) | ||
| m = sized.(pickerModel) | ||
|
|
||
| // Set up entry list with other entry selected | ||
| items := []list.Item{entryItem{entry: otherEntry}} | ||
| m.todayList = list.New(items, list.NewDefaultDelegate(), 80, 20) | ||
|
|
||
| // Start jot | ||
| started, _ := m.startJot() | ||
| m = started.(pickerModel) | ||
| m.jotInput.SetValue("Quick thought") | ||
|
|
||
| // Submit | ||
| updatedModel, cmd := m.updateJotInput(tea.KeyMsg{Type: tea.KeyEnter}) | ||
| m = updatedModel.(pickerModel) | ||
|
|
||
| if cmd == nil { | ||
| t.Fatal("Expected jot command") | ||
| } | ||
|
|
||
| result := cmd() | ||
| if jotMsg, ok := result.(jotCompleteMsg); ok { | ||
| if jotMsg.err != nil { | ||
| t.Fatalf("Jot failed: %v", jotMsg.err) | ||
| } | ||
| } | ||
|
|
||
| // Verify jot went to other01 | ||
| updated := store.byID["other01"] | ||
| if !strings.Contains(updated.Content, "Quick thought") { | ||
| t.Errorf("Expected jot in other01, content: %s", updated.Content) | ||
| } | ||
|
|
||
| // Verify daily was NOT modified |
Check warning
Code scanning / gavel
Assess test quality, coverage gaps, and testing best practices Warning test
Summary
Test plan
task test)task build)🤖 Generated with Claude Code