From a12f1cc881b05390177c2f83aca15c63b814667d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 04:15:55 +0000 Subject: [PATCH 1/6] Initial plan From 80c766effd19db2919c9ef8929f363f65a46a506 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 04:23:41 +0000 Subject: [PATCH 2/6] feat: compact home keymap display Agent-Logs-Url: https://github.com/harumiWeb/eitango/sessions/b6e0bc1e-b5eb-4a68-82ab-6957ad250239 Co-authored-by: harumiWeb <164025931+harumiWeb@users.noreply.github.com> --- assets/locale/en.toml | 3 +++ assets/locale/ja.toml | 3 +++ internal/app/view.go | 43 +++++++++++++++++++++++++++++--------- internal/app/view_test.go | 6 ++++++ internal/i18n/i18n_test.go | 1 + internal/i18n/keys.go | 3 +++ 6 files changed, 49 insertions(+), 10 deletions(-) diff --git a/assets/locale/en.toml b/assets/locale/en.toml index 9df98d1..c53a136 100644 --- a/assets/locale/en.toml +++ b/assets/locale/en.toml @@ -18,6 +18,9 @@ update = "Update available" update_detail = "%s is available (current: %s)" update_hint = "Run `eitango version` to see release details" keys = "Tab=mode Enter=start/resume n=new r=review s=stats c=settings q=quit" +key_start = "start" +key_toggle_mode = "mode" +key_new = "new" [settings] title = "Home settings" diff --git a/assets/locale/ja.toml b/assets/locale/ja.toml index fbe97f3..172ef27 100644 --- a/assets/locale/ja.toml +++ b/assets/locale/ja.toml @@ -18,6 +18,9 @@ update = "更新があります" update_detail = "%s を利用できます (現在: %s)" update_hint = "`eitango version` でリリース情報を確認できます" keys = "Tab=回答方式 Enter=開始/再開 n=新規 r=復習 s=統計 c=設定 q=終了" +key_start = "開始" +key_toggle_mode = "方式切替" +key_new = "新規" [settings] title = "ホーム設定" diff --git a/internal/app/view.go b/internal/app/view.go index 440e419..7877c33 100644 --- a/internal/app/view.go +++ b/internal/app/view.go @@ -83,16 +83,7 @@ func (m RootModel) renderHome() string { } lines = append(lines, "", - m.styles.Muted.Render(m.renderInlineGuides( - keymap.ContextHome, - keymap.ActionToggleAnswerMode, - keymap.ActionConfirm, - keymap.ActionNewSession, - keymap.ActionReview, - keymap.ActionStats, - keymap.ActionSettings, - keymap.ActionQuit, - )), + m.styles.Muted.Render(m.renderHomeGuides()), ) return m.styles.Panel.Render(strings.Join(lines, "\n")) } @@ -859,6 +850,38 @@ func (m RootModel) renderInlineGuides(ctx keymap.Context, actions ...keymap.Acti return strings.Join(parts, " ") } +type homeGuideItem struct { + action keymap.Action + label string +} + +func (m RootModel) renderHomeGuides() string { + primary := m.renderHomeGuideLine( + homeGuideItem{action: keymap.ActionConfirm, label: i18n.T(i18n.HomeKeyStart)}, + homeGuideItem{action: keymap.ActionToggleAnswerMode, label: i18n.T(i18n.HomeKeyToggleMode)}, + homeGuideItem{action: keymap.ActionQuit, label: i18n.T(i18n.KeyQuit)}, + ) + secondary := m.renderHomeGuideLine( + homeGuideItem{action: keymap.ActionNewSession, label: i18n.T(i18n.HomeKeyNew)}, + homeGuideItem{action: keymap.ActionReview, label: i18n.T(i18n.KeyReview)}, + homeGuideItem{action: keymap.ActionStats, label: i18n.T(i18n.KeyStats)}, + homeGuideItem{action: keymap.ActionSettings, label: i18n.T(i18n.KeySettings)}, + ) + return strings.TrimSpace(strings.Join([]string{primary, secondary}, "\n")) +} + +func (m RootModel) renderHomeGuideLine(items ...homeGuideItem) string { + parts := make([]string, 0, len(items)) + for _, item := range items { + keys := m.keymap.Keys(keymap.ContextHome, item.action) + if len(keys) == 0 || item.label == "" { + continue + } + parts = append(parts, fmt.Sprintf("[%s]%s", keymap.FormatKeys(keys[:1]), item.label)) + } + return strings.Join(parts, " ") +} + func (m RootModel) keymapFilterLabel(filter keymap.Context) string { if filter == "" { return i18n.T(i18n.KeymapFilterAll) diff --git a/internal/app/view_test.go b/internal/app/view_test.go index 66c827b..c6eec2d 100644 --- a/internal/app/view_test.go +++ b/internal/app/view_test.go @@ -221,6 +221,12 @@ func TestRenderHomeShowsWaitToday(t *testing.T) { if !strings.Contains(got, i18n.T(i18n.AnswerModeWrite)) { t.Fatalf("renderHome() missing selected answer mode:\n%s", got) } + if !strings.Contains(got, "[Enter]"+i18n.T(i18n.HomeKeyStart)+" [Tab]"+i18n.T(i18n.HomeKeyToggleMode)+" [q]"+i18n.T(i18n.KeyQuit)) { + t.Fatalf("renderHome() missing primary compact key guide:\n%s", got) + } + if !strings.Contains(got, "[n]"+i18n.T(i18n.HomeKeyNew)+" [r]"+i18n.T(i18n.KeyReview)+" [s]"+i18n.T(i18n.KeyStats)+" [c]"+i18n.T(i18n.KeySettings)) { + t.Fatalf("renderHome() missing secondary compact key guide:\n%s", got) + } } func TestRenderHomeLocalizesActiveSessionMode(t *testing.T) { diff --git a/internal/i18n/i18n_test.go b/internal/i18n/i18n_test.go index daf6c2a..e68d407 100644 --- a/internal/i18n/i18n_test.go +++ b/internal/i18n/i18n_test.go @@ -84,6 +84,7 @@ func TestAllJAKeysExistInEN(t *testing.T) { i18n.HomeConfirmBody, i18n.HomeConfirmCurrent, i18n.HomeConfirmTarget, i18n.HomeConfirmKeys, i18n.HomeUpdate, i18n.HomeUpdateDetail, i18n.HomeUpdateHint, i18n.HomeKeys, + i18n.HomeKeyStart, i18n.HomeKeyToggleMode, i18n.HomeKeyNew, i18n.SettingsTitle, i18n.SettingsQuestions, i18n.SettingsWriteDifficulty, i18n.SettingsWriteDifficultyBasic, i18n.SettingsWriteDifficultyHard, i18n.SettingsAudioEnabled, i18n.SettingsAudioAutoplay, diff --git a/internal/i18n/keys.go b/internal/i18n/keys.go index a74362e..6931430 100644 --- a/internal/i18n/keys.go +++ b/internal/i18n/keys.go @@ -22,6 +22,9 @@ const ( HomeUpdateDetail = "home.update_detail" HomeUpdateHint = "home.update_hint" HomeKeys = "home.keys" + HomeKeyStart = "home.key_start" + HomeKeyToggleMode = "home.key_toggle_mode" + HomeKeyNew = "home.key_new" ) // Home settings overlay From 923a0f9ce3010d2f23bdac53ff2da8bf22edf00c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 04:25:00 +0000 Subject: [PATCH 3/6] test: derive home key guide expectations from keymap Agent-Logs-Url: https://github.com/harumiWeb/eitango/sessions/b6e0bc1e-b5eb-4a68-82ab-6957ad250239 Co-authored-by: harumiWeb <164025931+harumiWeb@users.noreply.github.com> --- internal/app/view.go | 3 ++- internal/app/view_test.go | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/internal/app/view.go b/internal/app/view.go index 7877c33..ef552df 100644 --- a/internal/app/view.go +++ b/internal/app/view.go @@ -877,7 +877,8 @@ func (m RootModel) renderHomeGuideLine(items ...homeGuideItem) string { if len(keys) == 0 || item.label == "" { continue } - parts = append(parts, fmt.Sprintf("[%s]%s", keymap.FormatKeys(keys[:1]), item.label)) + primaryKey := keymap.FormatKeys([]string{keys[0]}) + parts = append(parts, fmt.Sprintf("[%s]%s", primaryKey, item.label)) } return strings.Join(parts, " ") } diff --git a/internal/app/view_test.go b/internal/app/view_test.go index c6eec2d..263b725 100644 --- a/internal/app/view_test.go +++ b/internal/app/view_test.go @@ -221,10 +221,21 @@ func TestRenderHomeShowsWaitToday(t *testing.T) { if !strings.Contains(got, i18n.T(i18n.AnswerModeWrite)) { t.Fatalf("renderHome() missing selected answer mode:\n%s", got) } - if !strings.Contains(got, "[Enter]"+i18n.T(i18n.HomeKeyStart)+" [Tab]"+i18n.T(i18n.HomeKeyToggleMode)+" [q]"+i18n.T(i18n.KeyQuit)) { + primary := strings.Join([]string{ + "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionConfirm)[0]}) + "]" + i18n.T(i18n.HomeKeyStart), + "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionToggleAnswerMode)[0]}) + "]" + i18n.T(i18n.HomeKeyToggleMode), + "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionQuit)[0]}) + "]" + i18n.T(i18n.KeyQuit), + }, " ") + if !strings.Contains(got, primary) { t.Fatalf("renderHome() missing primary compact key guide:\n%s", got) } - if !strings.Contains(got, "[n]"+i18n.T(i18n.HomeKeyNew)+" [r]"+i18n.T(i18n.KeyReview)+" [s]"+i18n.T(i18n.KeyStats)+" [c]"+i18n.T(i18n.KeySettings)) { + secondary := strings.Join([]string{ + "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionNewSession)[0]}) + "]" + i18n.T(i18n.HomeKeyNew), + "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionReview)[0]}) + "]" + i18n.T(i18n.KeyReview), + "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionStats)[0]}) + "]" + i18n.T(i18n.KeyStats), + "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionSettings)[0]}) + "]" + i18n.T(i18n.KeySettings), + }, " ") + if !strings.Contains(got, secondary) { t.Fatalf("renderHome() missing secondary compact key guide:\n%s", got) } } From 1a1d7d77c36604c9df65e8f6dc5186db7a026044 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 04:26:19 +0000 Subject: [PATCH 4/6] test: reuse home guide helper in view assertions Agent-Logs-Url: https://github.com/harumiWeb/eitango/sessions/b6e0bc1e-b5eb-4a68-82ab-6957ad250239 Co-authored-by: harumiWeb <164025931+harumiWeb@users.noreply.github.com> --- internal/app/view_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/app/view_test.go b/internal/app/view_test.go index 263b725..8a20446 100644 --- a/internal/app/view_test.go +++ b/internal/app/view_test.go @@ -221,20 +221,20 @@ func TestRenderHomeShowsWaitToday(t *testing.T) { if !strings.Contains(got, i18n.T(i18n.AnswerModeWrite)) { t.Fatalf("renderHome() missing selected answer mode:\n%s", got) } - primary := strings.Join([]string{ - "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionConfirm)[0]}) + "]" + i18n.T(i18n.HomeKeyStart), - "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionToggleAnswerMode)[0]}) + "]" + i18n.T(i18n.HomeKeyToggleMode), - "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionQuit)[0]}) + "]" + i18n.T(i18n.KeyQuit), - }, " ") + primary := model.renderHomeGuideLine( + homeGuideItem{action: keymap.ActionConfirm, label: i18n.T(i18n.HomeKeyStart)}, + homeGuideItem{action: keymap.ActionToggleAnswerMode, label: i18n.T(i18n.HomeKeyToggleMode)}, + homeGuideItem{action: keymap.ActionQuit, label: i18n.T(i18n.KeyQuit)}, + ) if !strings.Contains(got, primary) { t.Fatalf("renderHome() missing primary compact key guide:\n%s", got) } - secondary := strings.Join([]string{ - "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionNewSession)[0]}) + "]" + i18n.T(i18n.HomeKeyNew), - "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionReview)[0]}) + "]" + i18n.T(i18n.KeyReview), - "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionStats)[0]}) + "]" + i18n.T(i18n.KeyStats), - "[" + keymap.FormatKeys([]string{model.keymap.Keys(keymap.ContextHome, keymap.ActionSettings)[0]}) + "]" + i18n.T(i18n.KeySettings), - }, " ") + secondary := model.renderHomeGuideLine( + homeGuideItem{action: keymap.ActionNewSession, label: i18n.T(i18n.HomeKeyNew)}, + homeGuideItem{action: keymap.ActionReview, label: i18n.T(i18n.KeyReview)}, + homeGuideItem{action: keymap.ActionStats, label: i18n.T(i18n.KeyStats)}, + homeGuideItem{action: keymap.ActionSettings, label: i18n.T(i18n.KeySettings)}, + ) if !strings.Contains(got, secondary) { t.Fatalf("renderHome() missing secondary compact key guide:\n%s", got) } From 8c924c9d4300a70c554edd593edce979907dba07 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 04:27:37 +0000 Subject: [PATCH 5/6] refactor: share home and inline guide rendering Agent-Logs-Url: https://github.com/harumiWeb/eitango/sessions/b6e0bc1e-b5eb-4a68-82ab-6957ad250239 Co-authored-by: harumiWeb <164025931+harumiWeb@users.noreply.github.com> --- internal/app/view.go | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/internal/app/view.go b/internal/app/view.go index ef552df..2e737c0 100644 --- a/internal/app/view.go +++ b/internal/app/view.go @@ -839,13 +839,28 @@ func (m RootModel) binding(ctx keymap.Context, action keymap.Action) key.Binding } func (m RootModel) renderInlineGuides(ctx keymap.Context, actions ...keymap.Action) string { + return m.renderActionGuides(ctx, nil, actions...) +} + +func (m RootModel) renderActionGuides(ctx keymap.Context, labels map[keymap.Action]string, actions ...keymap.Action) string { parts := make([]string, 0, len(actions)) for _, action := range actions { - help := m.binding(ctx, action).Help() - if help.Key == "" || help.Desc == "" { + keys := m.keymap.Keys(ctx, action) + if len(keys) == 0 { + continue + } + if labels != nil { + label := labels[action] + if label == "" { + continue + } + parts = append(parts, fmt.Sprintf("[%s]%s", keymap.FormatKeys([]string{keys[0]}), label)) continue } - parts = append(parts, fmt.Sprintf("%s=%s", help.Key, help.Desc)) + help := m.binding(ctx, action).Help() + if help.Key != "" && help.Desc != "" { + parts = append(parts, fmt.Sprintf("%s=%s", help.Key, help.Desc)) + } } return strings.Join(parts, " ") } @@ -871,16 +886,13 @@ func (m RootModel) renderHomeGuides() string { } func (m RootModel) renderHomeGuideLine(items ...homeGuideItem) string { - parts := make([]string, 0, len(items)) + actions := make([]keymap.Action, 0, len(items)) + labels := make(map[keymap.Action]string, len(items)) for _, item := range items { - keys := m.keymap.Keys(keymap.ContextHome, item.action) - if len(keys) == 0 || item.label == "" { - continue - } - primaryKey := keymap.FormatKeys([]string{keys[0]}) - parts = append(parts, fmt.Sprintf("[%s]%s", primaryKey, item.label)) + actions = append(actions, item.action) + labels[item.action] = item.label } - return strings.Join(parts, " ") + return m.renderActionGuides(keymap.ContextHome, labels, actions...) } func (m RootModel) keymapFilterLabel(filter keymap.Context) string { From f76b1b257bfc00499bde81b99adc10ebf1a1148c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 04:28:45 +0000 Subject: [PATCH 6/6] refactor: extract bracketed home guide helper Agent-Logs-Url: https://github.com/harumiWeb/eitango/sessions/b6e0bc1e-b5eb-4a68-82ab-6957ad250239 Co-authored-by: harumiWeb <164025931+harumiWeb@users.noreply.github.com> --- internal/app/view.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/internal/app/view.go b/internal/app/view.go index 2e737c0..156406b 100644 --- a/internal/app/view.go +++ b/internal/app/view.go @@ -846,15 +846,13 @@ func (m RootModel) renderActionGuides(ctx keymap.Context, labels map[keymap.Acti parts := make([]string, 0, len(actions)) for _, action := range actions { keys := m.keymap.Keys(ctx, action) - if len(keys) == 0 { - continue - } if labels != nil { - label := labels[action] - if label == "" { - continue + if part, ok := renderBracketedGuide(keys, labels[action]); ok { + parts = append(parts, part) } - parts = append(parts, fmt.Sprintf("[%s]%s", keymap.FormatKeys([]string{keys[0]}), label)) + continue + } + if len(keys) == 0 { continue } help := m.binding(ctx, action).Help() @@ -895,6 +893,13 @@ func (m RootModel) renderHomeGuideLine(items ...homeGuideItem) string { return m.renderActionGuides(keymap.ContextHome, labels, actions...) } +func renderBracketedGuide(keys []string, label string) (string, bool) { + if len(keys) == 0 || label == "" { + return "", false + } + return fmt.Sprintf("[%s]%s", keymap.FormatKeys([]string{keys[0]}), label), true +} + func (m RootModel) keymapFilterLabel(filter keymap.Context) string { if filter == "" { return i18n.T(i18n.KeymapFilterAll)