Text overlay support
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
// 3. Renders an 8x4 key grid showing which slots are free vs occupied.
|
||||
// 4. Walks the user through a series of huh forms:
|
||||
// - Pick a key slot (0-31)
|
||||
// - Choose action type: module function or raw shell command
|
||||
// - Choose action type: module function, shell command, or toggle key (with poll)
|
||||
// - If module: pick module → function → customize params (with defaults)
|
||||
// - If command: type a shell command
|
||||
// - Pick an icon from icons_dir (or type a custom filename)
|
||||
@@ -22,10 +22,10 @@
|
||||
//
|
||||
// ## Expanding this tool
|
||||
//
|
||||
// To add new TUI steps (e.g. toggle key setup with poll config, or a
|
||||
// "delete key" flow), add a new function following the pattern of
|
||||
// configureModuleKey/configureCommandKey: build huh forms, collect values
|
||||
// into a keyEntry, return it for the confirm/append step.
|
||||
// To add new TUI steps (e.g. a "delete key" flow), add a new function
|
||||
// following the pattern of configureModuleKey/configureCommandKey/
|
||||
// configureToggleKey: build huh forms, collect values into a keyEntry,
|
||||
// return it for the confirm/append step.
|
||||
//
|
||||
// To support new key types in the YAML output, update keyEntry and
|
||||
// renderKeySnippet() in yaml.go.
|
||||
@@ -185,29 +185,51 @@ func runOnce(cfgPath string) error {
|
||||
var entry keyEntry
|
||||
entry.Index = keyIdx
|
||||
|
||||
if actionType == "module" {
|
||||
// Module path: module → function → params (with defaults pre-filled).
|
||||
switch actionType {
|
||||
case "module":
|
||||
entry, err = configureModuleKey(keyIdx, reg)
|
||||
} else {
|
||||
// Command path: just a shell command string.
|
||||
case "toggle":
|
||||
entry, err = configureToggleKey(keyIdx, reg)
|
||||
default:
|
||||
entry, err = configureCommandKey(keyIdx)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Step 4: Pick an icon file.
|
||||
icon, err := pickIcon(cfg.IconsDir)
|
||||
if err != nil {
|
||||
return err
|
||||
// Step 4: Pick icon(s).
|
||||
if entry.IsToggle {
|
||||
fmt.Println(lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("12")).Render(" Icon for ON state:"))
|
||||
iconTrue, err := pickIcon(cfg.IconsDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry.IconTrue = iconTrue
|
||||
|
||||
fmt.Println(lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("12")).Render(" Icon for OFF state:"))
|
||||
iconFalse, err := pickIcon(cfg.IconsDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry.IconFalse = iconFalse
|
||||
} else {
|
||||
icon, err := pickIcon(cfg.IconsDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry.Icon = icon
|
||||
}
|
||||
entry.Icon = icon
|
||||
|
||||
// Step 5: Show summary and confirm before writing.
|
||||
fmt.Println()
|
||||
fmt.Println(lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("12")).Render(" Summary:"))
|
||||
fmt.Printf(" Key: %d\n", entry.Index)
|
||||
fmt.Printf(" Icon: %s\n", entry.Icon)
|
||||
if entry.IsToggle {
|
||||
fmt.Printf(" Icon ON: %s\n", entry.IconTrue)
|
||||
fmt.Printf(" Icon OFF: %s\n", entry.IconFalse)
|
||||
} else {
|
||||
fmt.Printf(" Icon: %s\n", entry.Icon)
|
||||
}
|
||||
if entry.Module != "" {
|
||||
fmt.Printf(" Module: %s\n", entry.Module)
|
||||
fmt.Printf(" Function: %s\n", entry.Function)
|
||||
@@ -220,6 +242,27 @@ func runOnce(cfgPath string) error {
|
||||
} else {
|
||||
fmt.Printf(" Command: %s\n", entry.Command)
|
||||
}
|
||||
if entry.IsToggle {
|
||||
fmt.Println(" Poll:")
|
||||
if entry.PollModule != "" {
|
||||
fmt.Printf(" Module: %s\n", entry.PollModule)
|
||||
fmt.Printf(" Function: %s\n", entry.PollFunction)
|
||||
if len(entry.PollParams) > 0 {
|
||||
fmt.Println(" Params:")
|
||||
for k, v := range entry.PollParams {
|
||||
fmt.Printf(" %s: %s\n", k, v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf(" Command: %s\n", entry.PollCommand)
|
||||
}
|
||||
if entry.PollMatch != "" {
|
||||
fmt.Printf(" Match: %s\n", entry.PollMatch)
|
||||
}
|
||||
if entry.PollInterval != "" {
|
||||
fmt.Printf(" Interval: %s\n", entry.PollInterval)
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
var confirm bool
|
||||
@@ -281,10 +324,8 @@ func pickKeySlot(keys map[int]config.KeyConfig) (int, error) {
|
||||
return keyIdx, nil
|
||||
}
|
||||
|
||||
// pickActionType asks whether to configure a module function or a raw shell command.
|
||||
//
|
||||
// FUTURE: add "Toggle key (with poll)" as a third option, which would walk
|
||||
// through icon_true/icon_false and poll config setup.
|
||||
// pickActionType asks whether to configure a module function, a raw shell command,
|
||||
// or a toggle key with poll-based state checking.
|
||||
func pickActionType() (string, error) {
|
||||
var actionType string
|
||||
form := huh.NewForm(
|
||||
@@ -292,8 +333,9 @@ func pickActionType() (string, error) {
|
||||
huh.NewSelect[string]().
|
||||
Title("What should this key do?").
|
||||
Options(
|
||||
huh.NewOption("Module function (Slack, etc.)", "module"),
|
||||
huh.NewOption("Module function (Slack, OBS, etc.)", "module"),
|
||||
huh.NewOption("Shell command", "command"),
|
||||
huh.NewOption("Toggle key (with poll)", "toggle"),
|
||||
).
|
||||
Value(&actionType),
|
||||
),
|
||||
@@ -434,6 +476,123 @@ func configureCommandKey(keyIdx int) (keyEntry, error) {
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// configureToggleKey walks the user through setting up a toggle key:
|
||||
// press action (module or command) + poll configuration (module or command,
|
||||
// match string, interval). Icons are handled separately in runOnce().
|
||||
func configureToggleKey(keyIdx int, reg *modules.Registry) (keyEntry, error) {
|
||||
entry := keyEntry{Index: keyIdx, IsToggle: true}
|
||||
|
||||
// Ask what happens when the key is pressed.
|
||||
var pressType string
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewSelect[string]().
|
||||
Title("What should pressing this key do?").
|
||||
Options(
|
||||
huh.NewOption("Module function", "module"),
|
||||
huh.NewOption("Shell command", "command"),
|
||||
).
|
||||
Value(&pressType),
|
||||
),
|
||||
)
|
||||
if err := form.Run(); err != nil {
|
||||
return entry, err
|
||||
}
|
||||
|
||||
if pressType == "module" {
|
||||
modEntry, err := configureModuleKey(keyIdx, reg)
|
||||
if err != nil {
|
||||
return entry, err
|
||||
}
|
||||
entry.Module = modEntry.Module
|
||||
entry.Function = modEntry.Function
|
||||
entry.Params = modEntry.Params
|
||||
} else {
|
||||
cmdEntry, err := configureCommandKey(keyIdx)
|
||||
if err != nil {
|
||||
return entry, err
|
||||
}
|
||||
entry.Command = cmdEntry.Command
|
||||
}
|
||||
|
||||
// Configure the poll block.
|
||||
if err := configurePoll(&entry, reg); err != nil {
|
||||
return entry, err
|
||||
}
|
||||
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// configurePoll walks through poll configuration for a toggle key: how to check
|
||||
// state (module function or shell command), optional match string, and interval.
|
||||
func configurePoll(entry *keyEntry, reg *modules.Registry) error {
|
||||
var pollType string
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewSelect[string]().
|
||||
Title("How should the key check its state?").
|
||||
Options(
|
||||
huh.NewOption("Module function", "module"),
|
||||
huh.NewOption("Shell command", "command"),
|
||||
).
|
||||
Value(&pollType),
|
||||
),
|
||||
)
|
||||
if err := form.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if pollType == "module" {
|
||||
modEntry, err := configureModuleKey(entry.Index, reg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry.PollModule = modEntry.Module
|
||||
entry.PollFunction = modEntry.Function
|
||||
entry.PollParams = modEntry.Params
|
||||
} else {
|
||||
var cmd string
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title("Poll command").
|
||||
Description("Shell command to check key state").
|
||||
Value(&cmd).
|
||||
Placeholder("e.g. pgrep -x myapp"),
|
||||
),
|
||||
)
|
||||
if err := form.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
entry.PollCommand = cmd
|
||||
}
|
||||
|
||||
// Match string and interval.
|
||||
var match, interval string
|
||||
interval = "2s"
|
||||
form = huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().
|
||||
Title("Match string").
|
||||
Description("Substring in output that means ON (leave empty to use exit code)").
|
||||
Value(&match).
|
||||
Placeholder("optional"),
|
||||
huh.NewInput().
|
||||
Title("Poll interval").
|
||||
Description("How often to check state").
|
||||
Value(&interval).
|
||||
Placeholder("2s"),
|
||||
),
|
||||
)
|
||||
if err := form.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
entry.PollMatch = match
|
||||
entry.PollInterval = interval
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// pickIcon lets the user select an icon file.
|
||||
//
|
||||
// If icons_dir has image files, shows a select list of them plus a "type custom"
|
||||
|
||||
Reference in New Issue
Block a user