diff --git a/internal/tui/components/chat/editor/editor.go b/internal/tui/components/chat/editor/editor.go index 4b719f6b674176c79f42e96013c670182f0d282d..de1a98b34595613594e83063cc12add4ba820c84 100644 --- a/internal/tui/components/chat/editor/editor.go +++ b/internal/tui/components/chat/editor/editor.go @@ -6,6 +6,7 @@ import ( "math/rand" "net/http" "os" + "os/exec" "path/filepath" "runtime" "slices" @@ -95,7 +96,7 @@ type OpenEditorMsg struct { func (m *editorCmp) openEditor(value string) tea.Cmd { editor := os.Getenv("EDITOR") if editor == "" { - // Use platform-appropriate default editor. + // Use platform-appropriate default editor if runtime.GOOS == "windows" { editor = "notepad" } else { @@ -111,11 +112,11 @@ func (m *editorCmp) openEditor(value string) tea.Cmd { if _, err := tmpfile.WriteString(value); err != nil { return util.ReportError(err) } - - // Build the full shell command with the file argument. - cmdStr := fmt.Sprintf("%s %s", editor, tmpfile.Name()) - - return util.ExecShell(context.TODO(), cmdStr, func(err error) tea.Msg { + c := exec.CommandContext(context.TODO(), editor, tmpfile.Name()) + c.Stdin = os.Stdin + c.Stdout = os.Stdout + c.Stderr = os.Stderr + return tea.ExecProcess(c, func(err error) tea.Msg { if err != nil { return util.ReportError(err) } diff --git a/internal/tui/util/shell.go b/internal/tui/util/shell.go deleted file mode 100644 index 422831649d043c24265ebaafaa58bcc041214557..0000000000000000000000000000000000000000 --- a/internal/tui/util/shell.go +++ /dev/null @@ -1,58 +0,0 @@ -package util - -import ( - "context" - "io" - "strings" - - tea "charm.land/bubbletea/v2" - "mvdan.cc/sh/v3/interp" - "mvdan.cc/sh/v3/syntax" -) - -// shellCommand wraps a shell interpreter to implement tea.ExecCommand. -type shellCommand struct { - ctx context.Context - file *syntax.File - stdin io.Reader - stdout io.Writer - stderr io.Writer -} - -func (s *shellCommand) SetStdin(r io.Reader) { - s.stdin = r -} - -func (s *shellCommand) SetStdout(w io.Writer) { - s.stdout = w -} - -func (s *shellCommand) SetStderr(w io.Writer) { - s.stderr = w -} - -func (s *shellCommand) Run() error { - runner, err := interp.New( - interp.StdIO(s.stdin, s.stdout, s.stderr), - ) - if err != nil { - return err - } - return runner.Run(s.ctx, s.file) -} - -// ExecShell executes a shell command string using tea.Exec. -// The command is parsed and executed via mvdan.cc/sh/v3/interp, allowing -// proper handling of shell syntax like quotes and arguments. -func ExecShell(ctx context.Context, cmdStr string, callback tea.ExecCallback) tea.Cmd { - parsed, err := syntax.NewParser().Parse(strings.NewReader(cmdStr), "") - if err != nil { - return ReportError(err) - } - - cmd := &shellCommand{ - ctx: ctx, - file: parsed, - } - return tea.Exec(cmd, callback) -}