mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 19:11:25 +01:00
fix: address verified review findings (terminal-forms-1)
Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
21 files changed
+329
-53
No files matched your search
@@ -3,7 +3,14 @@
|
||||
|
||||
package terminal
|
||||
|
||||
import "charm.land/huh/v2"
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"charm.land/huh/v2"
|
||||
)
|
||||
|
||||
// BodyTitle labels the markdown body of issues, pull requests and comments.
|
||||
const BodyTitle = "Body (markdown):"
|
||||
@@ -11,9 +18,17 @@ const BodyTitle = "Body (markdown):"
|
||||
// maxVisibleOptions bounds how many options a multi-select shows before it scrolls.
|
||||
const maxVisibleOptions = 8
|
||||
|
||||
// notBlank rejects empty and whitespace-only answers, which huh.ValidateNotEmpty accepts.
|
||||
func notBlank(s string) error {
|
||||
if strings.TrimSpace(s) == "" {
|
||||
return errors.New("input cannot be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// titleField is the required one-line title of an issue, pull request or similar resource.
|
||||
func titleField(value *string) *huh.Input {
|
||||
return huh.NewInput().Title("Title (required):").Value(value).Validate(huh.ValidateNotEmpty())
|
||||
return huh.NewInput().Title("Title (required):").Value(value).Validate(notBlank)
|
||||
}
|
||||
|
||||
// bodyField is an inline multi-line markdown field. It has no external editor: forms run the
|
||||
@@ -22,6 +37,26 @@ func bodyField(title string, value *string) *huh.Text {
|
||||
return huh.NewText().Title(title).Value(value).ExternalEditor(false)
|
||||
}
|
||||
|
||||
// fieldText is how a multi-line field shows seed: its textarea turns every CR and LF into a line
|
||||
// break and a tab into four spaces, and drops other control characters.
|
||||
func fieldText(seed string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
if r == utf8.RuneError || (unicode.IsControl(r) && r != '\n') {
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}, strings.NewReplacer("\r", "\n", "\t", " ").Replace(seed))
|
||||
}
|
||||
|
||||
// fieldResult is the answer of a multi-line field seeded with seed: seed itself when the user
|
||||
// changed nothing, so a plain Enter does not rewrite existing text such as CRLF line endings.
|
||||
func fieldResult(seed, answer string) string {
|
||||
if answer == fieldText(seed) {
|
||||
return seed
|
||||
}
|
||||
return answer
|
||||
}
|
||||
|
||||
// multiSelect lists every option, scrolling past maxVisibleOptions. Without an explicit height,
|
||||
// huh sizes the list to its options minus the title line and hides the last option.
|
||||
func multiSelect[T comparable](title string, options []huh.Option[T], value *[]T) *huh.MultiSelect[T] {
|
||||
|
||||
Reference in new issue
Block a user