From 52c84e6ead809c061b76f3387ef79415532c9a72 Mon Sep 17 00:00:00 2001 From: danielxxomg Date: Sat, 27 Jun 2026 12:28:55 -0500 Subject: [PATCH 1/2] feat(tui): paste support in wizard inputs (F8) --- internal/tui/screens/wizard.go | 10 ++++ internal/tui/screens/wizard_test.go | 67 +++++++++++++++++++++++ openspec/changes/tui-personality/tasks.md | 4 +- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/internal/tui/screens/wizard.go b/internal/tui/screens/wizard.go index 2b69c60..69f708b 100644 --- a/internal/tui/screens/wizard.go +++ b/internal/tui/screens/wizard.go @@ -146,6 +146,16 @@ func (m *WizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { default: return m.handleNavigation(msg) } + + case tea.PasteMsg: + // Bracketed paste (tui-personality wizard-flow F8): append the pasted + // content to the active free-text input buffer. Only the profile-name + // step accepts free text; other steps ignore paste so selection state + // is not corrupted. + if m.Step == StepName { + m.NameInput += msg.Content + } + return m, nil } return m, nil } diff --git a/internal/tui/screens/wizard_test.go b/internal/tui/screens/wizard_test.go index e6d55bc..48de619 100644 --- a/internal/tui/screens/wizard_test.go +++ b/internal/tui/screens/wizard_test.go @@ -149,6 +149,73 @@ func TestWizardModel_NameStep_NamePersistsAcrossSteps(t *testing.T) { //nolint:p } } +// --- Paste support (tui-personality wizard-flow F8) --- + +// TestWizardModel_NameStep_PasteMsg verifies that a bracketed-paste event +// appends msg.Content to the active profile-name input buffer. Covers the +// spec scenarios "paste inserts pasted text" and "paste appends to existing +// text", plus edge cases with spaces and path separators. +func TestWizardModel_NameStep_PasteMsg(t *testing.T) { //nolint:paralleltest // not yet parallelized — shared state (os.Stderr/execCommand/config-file/struct) isolation pending + tests := []struct { + name string + initial string + paste string + wantInput string + }{ + {"empty input + paste inserts text", "", "work-laptop", "work-laptop"}, + {"non-empty input + paste appends", "work-", "laptop", "work-laptop"}, + {"paste with spaces", "", "my profile", "my profile"}, + {"paste with path separators", "", "/home/user/config", "/home/user/config"}, + {"paste appends to multi-segment", "foo-", "bar-baz", "foo-bar-baz"}, + } + + for _, tt := range tests { //nolint:paralleltest // subtests share table/struct state + t.Run(tt.name, func(t *testing.T) { //nolint:paralleltest // subtests share table/struct state + m := NewWizardModel("profile-create", []string{"github-gist"}) + m.NameInput = tt.initial + + _, _ = m.Update(tea.PasteMsg{Content: tt.paste}) + + if m.NameInput != tt.wantInput { + t.Errorf("NameInput after paste = %q, want %q", m.NameInput, tt.wantInput) + } + }) + } +} + +// TestWizardModel_NameStep_PasteThenBackspace verifies the spec scenario +// "regular keys still work after paste": after a paste populates the buffer, +// a Backspace keystroke removes the trailing rune as usual. +func TestWizardModel_NameStep_PasteThenBackspace(t *testing.T) { //nolint:paralleltest // not yet parallelized — shared state (os.Stderr/execCommand/config-file/struct) isolation pending + m := NewWizardModel("profile-create", []string{"github-gist"}) + + _, _ = m.Update(tea.PasteMsg{Content: "work-laptop"}) + _, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyBackspace}) + + if m.NameInput != "work-lapto" { + t.Errorf("NameInput after paste+backspace = %q, want %q", m.NameInput, "work-lapto") + } +} + +// TestWizardModel_PasteMsg_NonNameStepNoOp verifies paste only affects the +// active free-text step (StepName). On other steps the paste is ignored and +// must not corrupt the name buffer or selection state. +func TestWizardModel_PasteMsg_NonNameStepNoOp(t *testing.T) { //nolint:paralleltest // not yet parallelized — shared state (os.Stderr/execCommand/config-file/struct) isolation pending + m := NewWizardModel("profile-create", []string{"github-gist"}) + + // Advance past the name step to the provider step. + _, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter}) + if m.CurrentStep() != StepProvider { + t.Fatalf("setup: step = %d, want StepProvider", m.CurrentStep()) + } + + _, _ = m.Update(tea.PasteMsg{Content: "work-laptop"}) + + if m.NameInput != "" { + t.Errorf("NameInput should remain empty on non-name step, got %q", m.NameInput) + } +} + func TestWizardModel_ProfileName(t *testing.T) { //nolint:paralleltest // not yet parallelized — shared state (os.Stderr/execCommand/config-file/struct) isolation pending tests := []struct { name string diff --git a/openspec/changes/tui-personality/tasks.md b/openspec/changes/tui-personality/tasks.md index b56c116..af65d46 100644 --- a/openspec/changes/tui-personality/tasks.md +++ b/openspec/changes/tui-personality/tasks.md @@ -60,8 +60,8 @@ Chain strategy: stacked-to-main ## Phase 5: Paste Support (PR 3 — Tier 3) -- [ ] 5.1 [RED] `internal/tui/screens/wizard_test.go`: send `tea.PasteMsg{Content: "work-laptop"}`, assert input buffer equals `"work-laptop"`; send paste to pre-filled input, assert append -- [ ] 5.2 [GREEN] `internal/tui/screens/wizard.go`: add `case tea.PasteMsg:` in active textinput Update paths; append `msg.Content` to input buffer. **Note: field is `Content` not `Text`** (v2 API) +- [x] 5.1 [RED] `internal/tui/screens/wizard_test.go`: send `tea.PasteMsg{Content: "work-laptop"}`, assert input buffer equals `"work-laptop"`; send paste to pre-filled input, assert append +- [x] 5.2 [GREEN] `internal/tui/screens/wizard.go`: add `case tea.PasteMsg:` in active textinput Update paths; append `msg.Content` to input buffer. **Note: field is `Content` not `Text`** (v2 API) ## Phase 6: Verification & Coverage From a31253f351aa4d672a46448d668cc2c02373887e Mon Sep 17 00:00:00 2001 From: danielxxomg Date: Sat, 27 Jun 2026 12:37:39 -0500 Subject: [PATCH 2/2] =?UTF-8?q?docs(sdd):=20mark=20verification=20tasks=20?= =?UTF-8?q?complete=20=E2=80=94=20tui-personality=20PASS=20WITH=20WARNINGS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openspec/changes/tui-personality/tasks.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openspec/changes/tui-personality/tasks.md b/openspec/changes/tui-personality/tasks.md index af65d46..650a122 100644 --- a/openspec/changes/tui-personality/tasks.md +++ b/openspec/changes/tui-personality/tasks.md @@ -65,6 +65,6 @@ Chain strategy: stacked-to-main ## Phase 6: Verification & Coverage -- [ ] 6.1 Run `go test ./internal/tui/...` — all tests green -- [ ] 6.2 Run `go test -cover ./internal/tui/...` — verify ≥80% per package -- [ ] 6.3 Run `go vet ./...` and `golangci-lint run` — clean +- [x] 6.1 Run `go test ./internal/tui/...` — all tests green +- [x] 6.2 Run `go test -cover ./internal/tui/...` — verify ≥80% per package +- [x] 6.3 Run `go vet ./...` and `golangci-lint run` — clean