Skip to content

Commit 015e778

Browse files
authored
feat: more customization commands (#175)
1 parent 74748d8 commit 015e778

25 files changed

+360
-35
lines changed

cmd/internal/config.go

+101-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"strconv"
66
"strings"
7+
"time"
78

89
tuikitIO "github.com/jahvon/tuikit/io"
910
"github.com/jahvon/tuikit/types"
@@ -44,7 +45,7 @@ func registerConfigResetCmd(ctx *context.Context, configCmd *cobra.Command) {
4445
func resetConfigFunc(ctx *context.Context, _ *cobra.Command, _ []string) {
4546
logger := ctx.Logger
4647
form, err := views.NewForm(
47-
io.Theme(),
48+
io.Theme(ctx.Config.Theme.String()),
4849
ctx.StdIn(),
4950
ctx.StdOut(),
5051
&views.FormField{
@@ -80,6 +81,9 @@ func registerSetConfigCmd(ctx *context.Context, configCmd *cobra.Command) {
8081
registerSetWorkspaceModeCmd(ctx, setCmd)
8182
registerSetLogModeCmd(ctx, setCmd)
8283
registerSetTUICmd(ctx, setCmd)
84+
registerSetNotificationsCmd(ctx, setCmd)
85+
registerSetThemeCmd(ctx, setCmd)
86+
registerSetTimeoutCmd(ctx, setCmd)
8387
configCmd.AddCommand(setCmd)
8488
}
8589

@@ -190,6 +194,102 @@ func setInteractiveFunc(ctx *context.Context, _ *cobra.Command, args []string) {
190194
logger.PlainTextSuccess("Interactive UI " + strVal)
191195
}
192196

197+
func registerSetNotificationsCmd(ctx *context.Context, setCmd *cobra.Command) {
198+
notificationsCmd := &cobra.Command{
199+
Use: "notifications [true|false]",
200+
Short: "Enable or disable notifications.",
201+
Args: cobra.ExactArgs(1),
202+
ValidArgs: []string{"true", "false"},
203+
Run: func(cmd *cobra.Command, args []string) { setNotificationsFunc(ctx, cmd, args) },
204+
}
205+
RegisterFlag(ctx, notificationsCmd, *flags.SetSoundNotificationFlag)
206+
setCmd.AddCommand(notificationsCmd)
207+
}
208+
209+
func setNotificationsFunc(ctx *context.Context, cmd *cobra.Command, args []string) {
210+
logger := ctx.Logger
211+
enabled, err := strconv.ParseBool(args[0])
212+
if err != nil {
213+
logger.FatalErr(errors.Wrap(err, "invalid boolean value"))
214+
}
215+
sound := flags.ValueFor[bool](ctx, cmd, *flags.SetSoundNotificationFlag, false)
216+
217+
userConfig := ctx.Config
218+
if userConfig.Interactive == nil {
219+
userConfig.Interactive = &config.Interactive{}
220+
}
221+
userConfig.Interactive.NotifyOnCompletion = &enabled
222+
if sound {
223+
userConfig.Interactive.SoundOnCompletion = &enabled
224+
}
225+
if err := filesystem.WriteConfig(userConfig); err != nil {
226+
logger.FatalErr(err)
227+
}
228+
strVal := "disabled"
229+
if enabled {
230+
strVal = "enabled"
231+
}
232+
logger.PlainTextSuccess("Notifications " + strVal)
233+
}
234+
235+
func registerSetThemeCmd(ctx *context.Context, setCmd *cobra.Command) {
236+
themeCmd := &cobra.Command{
237+
Use: "theme [default|dark|light|dracula|tokyo-night]",
238+
Short: "Set the theme for the TUI views",
239+
Args: cobra.ExactArgs(1),
240+
ValidArgs: []string{
241+
string(config.ConfigThemeDefault),
242+
string(config.ConfigThemeDark),
243+
string(config.ConfigThemeLight),
244+
string(config.ConfigThemeDracula),
245+
string(config.ConfigThemeTokyoNight),
246+
},
247+
Run: func(cmd *cobra.Command, args []string) { setThemeFunc(ctx, cmd, args) },
248+
}
249+
setCmd.AddCommand(themeCmd)
250+
}
251+
252+
func setThemeFunc(ctx *context.Context, _ *cobra.Command, args []string) {
253+
logger := ctx.Logger
254+
themeName := args[0]
255+
256+
userConfig := ctx.Config
257+
if userConfig.Interactive == nil {
258+
userConfig.Interactive = &config.Interactive{}
259+
}
260+
userConfig.Theme = config.ConfigTheme(themeName)
261+
if err := filesystem.WriteConfig(userConfig); err != nil {
262+
logger.FatalErr(err)
263+
}
264+
logger.PlainTextSuccess("Theme set to " + themeName)
265+
}
266+
267+
func registerSetTimeoutCmd(ctx *context.Context, setCmd *cobra.Command) {
268+
timeoutCmd := &cobra.Command{
269+
Use: "timeout DURATION",
270+
Short: "Set the default timeout for executables.",
271+
Args: cobra.ExactArgs(1),
272+
Run: func(cmd *cobra.Command, args []string) { setTimeoutFunc(ctx, cmd, args) },
273+
}
274+
setCmd.AddCommand(timeoutCmd)
275+
}
276+
277+
func setTimeoutFunc(ctx *context.Context, _ *cobra.Command, args []string) {
278+
logger := ctx.Logger
279+
timeoutStr := args[0]
280+
timeout, err := time.ParseDuration(timeoutStr)
281+
if err != nil {
282+
logger.FatalErr(errors.Wrap(err, "invalid duration"))
283+
}
284+
285+
userConfig := ctx.Config
286+
userConfig.DefaultTimeout = timeout
287+
if err := filesystem.WriteConfig(userConfig); err != nil {
288+
logger.FatalErr(err)
289+
}
290+
logger.PlainTextSuccess("Default timeout set to " + timeoutStr)
291+
}
292+
193293
func registerViewConfigCmd(ctx *context.Context, configCmd *cobra.Command) {
194294
viewCmd := &cobra.Command{
195295
Use: "view",

cmd/internal/exec.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func execFunc(ctx *context.Context, cmd *cobra.Command, verb executable.Verb, ar
116116
setAuthEnv(ctx, cmd, e)
117117
textInputs := pendingFormFields(ctx, e)
118118
if len(textInputs) > 0 {
119-
form, err := views.NewForm(io.Theme(), ctx.StdIn(), ctx.StdOut(), textInputs...)
119+
form, err := views.NewForm(io.Theme(ctx.Config.Theme.String()), ctx.StdIn(), ctx.StdOut(), textInputs...)
120120
if err != nil {
121121
logger.FatalErr(err)
122122
}
@@ -177,7 +177,7 @@ func runByRef(ctx *context.Context, cmd *cobra.Command, argsStr string) error {
177177
func setAuthEnv(ctx *context.Context, _ *cobra.Command, executable *executable.Executable) {
178178
if authRequired(ctx, executable) {
179179
form, err := views.NewForm(
180-
io.Theme(),
180+
io.Theme(ctx.Config.Theme.String()),
181181
ctx.StdIn(),
182182
ctx.StdOut(),
183183
&views.FormField{

cmd/internal/flags/types.go

+6
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,9 @@ var TemplateFilePathFlag = &Metadata{
163163
Default: "",
164164
Required: false,
165165
}
166+
167+
var SetSoundNotificationFlag = &Metadata{
168+
Name: "sound",
169+
Usage: "Update completion sound notification setting",
170+
Default: false,
171+
}

cmd/internal/helpers.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ func WaitForTUI(ctx *context.Context, cmd *cobra.Command) {
8888

8989
func printContext(ctx *context.Context, cmd *cobra.Command) {
9090
if TUIEnabled(ctx, cmd) {
91-
ctx.Logger.Println(io.Theme().RenderHeader(context.AppName, context.HeaderCtxKey, ctx.String(), 0))
91+
ctx.Logger.Println(io.Theme(ctx.Config.Theme.String()).
92+
RenderHeader(context.AppName, context.HeaderCtxKey, ctx.String(), 0))
9293
}
9394
}
9495

cmd/internal/library.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func libraryFunc(ctx *context.Context, cmd *cobra.Command, _ []string) {
8383
Tags: tagsFilter,
8484
Substring: subStr,
8585
},
86-
io.Theme(),
86+
io.Theme(ctx.Config.Theme.String()),
8787
runFunc,
8888
)
8989
SetView(ctx, cmd, libraryModel)

cmd/internal/secret.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func deleteSecretFunc(ctx *context.Context, _ *cobra.Command, args []string) {
4747
reference := args[0]
4848

4949
form, err := views.NewForm(
50-
io.Theme(),
50+
io.Theme(ctx.Config.Theme.String()),
5151
ctx.StdIn(),
5252
ctx.StdOut(),
5353
&views.FormField{
@@ -138,7 +138,7 @@ func setSecretFunc(ctx *context.Context, _ *cobra.Command, args []string) {
138138
switch {
139139
case len(args) == 1:
140140
form, err := views.NewForm(
141-
io.Theme(),
141+
io.Theme(ctx.Config.Theme.String()),
142142
ctx.StdIn(),
143143
ctx.StdOut(),
144144
&views.FormField{

cmd/internal/workspace.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ func deleteWsFunc(ctx *context.Context, _ *cobra.Command, args []string) {
169169
name := args[0]
170170

171171
form, err := views.NewForm(
172-
io.Theme(),
172+
io.Theme(ctx.Config.Theme.String()),
173173
ctx.StdIn(),
174174
ctx.StdOut(),
175175
&views.FormField{

docs/cli/flow_config_set.md

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ Update flow configuration values.
2121
* [flow config](flow_config.md) - Update flow configuration values.
2222
* [flow config set log-mode](flow_config_set_log-mode.md) - Set the default log mode.
2323
* [flow config set namespace](flow_config_set_namespace.md) - Change the current namespace.
24+
* [flow config set notifications](flow_config_set_notifications.md) - Enable or disable notifications.
25+
* [flow config set theme](flow_config_set_theme.md) - Set the theme for the TUI views
26+
* [flow config set timeout](flow_config_set_timeout.md) - Set the default timeout for executables.
2427
* [flow config set tui](flow_config_set_tui.md) - Enable or disable the interactive terminal UI experience.
2528
* [flow config set workspace-mode](flow_config_set_workspace-mode.md) - Switch between fixed and dynamic workspace modes.
2629

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
## flow config set notifications
2+
3+
Enable or disable notifications.
4+
5+
```
6+
flow config set notifications [true|false] [flags]
7+
```
8+
9+
### Options
10+
11+
```
12+
-h, --help help for notifications
13+
--sound Update completion sound notification setting
14+
```
15+
16+
### Options inherited from parent commands
17+
18+
```
19+
-x, --non-interactive Disable displaying flow output via terminal UI rendering. This is only needed if the interactive output is enabled by default in flow's configuration.
20+
--sync Sync flow cache and workspaces
21+
--verbosity int Log verbosity level (-1 to 1)
22+
```
23+
24+
### SEE ALSO
25+
26+
* [flow config set](flow_config_set.md) - Update flow configuration values.
27+

docs/cli/flow_config_set_theme.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## flow config set theme
2+
3+
Set the theme for the TUI views
4+
5+
```
6+
flow config set theme [default|dark|light|dracula|tokyo-night] [flags]
7+
```
8+
9+
### Options
10+
11+
```
12+
-h, --help help for theme
13+
```
14+
15+
### Options inherited from parent commands
16+
17+
```
18+
-x, --non-interactive Disable displaying flow output via terminal UI rendering. This is only needed if the interactive output is enabled by default in flow's configuration.
19+
--sync Sync flow cache and workspaces
20+
--verbosity int Log verbosity level (-1 to 1)
21+
```
22+
23+
### SEE ALSO
24+
25+
* [flow config set](flow_config_set.md) - Update flow configuration values.
26+

docs/cli/flow_config_set_timeout.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## flow config set timeout
2+
3+
Set the default timeout for executables.
4+
5+
```
6+
flow config set timeout DURATION [flags]
7+
```
8+
9+
### Options
10+
11+
```
12+
-h, --help help for timeout
13+
```
14+
15+
### Options inherited from parent commands
16+
17+
```
18+
-x, --non-interactive Disable displaying flow output via terminal UI rendering. This is only needed if the interactive output is enabled by default in flow's configuration.
19+
--sync Sync flow cache and workspaces
20+
--verbosity int Log verbosity level (-1 to 1)
21+
```
22+
23+
### SEE ALSO
24+
25+
* [flow config set](flow_config_set.md) - Update flow configuration values.
26+

docs/guide/interactive.md

+37
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,35 @@
33
The interactive TUI can be customized in the [flow config file](../types/config.md). Additionally,
44
there are several [flow config commands](../cli/flow_config.md) that can be used to change the TUI settings.
55

6+
> [!TIP]
7+
> You can view your current settings with the config view command:
8+
> ```shell
9+
> flow config view
10+
> ```
11+
12+
### Changing the TUI theme
13+
14+
There are several themes available in the TUI:
15+
- `default` (everforest)
16+
- `light`
17+
- `dark`
18+
- `dracula`
19+
- `tokyo-night`
20+
21+
Use the following command to change the theme:
22+
23+
```shell
24+
flow config set theme (default|light|dark|dracula|tokyo-night)
25+
```
26+
27+
### Changing desktop notification settings
28+
29+
Desktop notifications can be sent when executables are completed. Use the following command to enable or disable desktop notifications:
30+
31+
```shell
32+
flow config set notifications (true|false) # --sound
33+
```
34+
635
### Changing the log mode
736

837
There are 4 log modes available in the TUI:
@@ -37,6 +66,14 @@ There are 2 workspace modes available in the TUI:
3766

3867
See the [workspace guide](workspace.md) for more information on workspaces.
3968

69+
### Changing the default executable timeout
70+
71+
The global default executable timeout is 30 minutes. Use the following command to change the default executable timeout:
72+
73+
```shell
74+
flow config set timeout <duration>
75+
```
76+
4077
### Disable the TUI
4178

4279
In some cases, you may want to disable the interactive TUI (in CI/CD pipelines and containers, for example).

docs/guide/templating.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
Guide coming soon...
1+
flow can be used to render templates using the [flow template](../cli/flow_template.md) command.
2+
Templates are defined using the templating language. The `flow template` command can be used to render templates using the following syntax:
23

3-
In the meantime, see [Template](../types/template.md) and [flow template](../cli/flow_template.md).
4+
```shell

docs/schemas/config_schema.json

+17
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
"type": "string",
4646
"default": "logfmt"
4747
},
48+
"defaultTimeout": {
49+
"description": "The default timeout to use when running executables.\nThis should be a valid duration string.\n",
50+
"type": "string",
51+
"default": "30m"
52+
},
4853
"interactive": {
4954
"$ref": "#/definitions/Interactive"
5055
},
@@ -56,6 +61,18 @@
5661
"type": "string"
5762
}
5863
},
64+
"theme": {
65+
"description": "The theme of the interactive UI.",
66+
"type": "string",
67+
"default": "default",
68+
"enum": [
69+
"default",
70+
"dark",
71+
"light",
72+
"dracula",
73+
"tokyo-night"
74+
]
75+
},
5976
"workspaceMode": {
6077
"description": "The mode of the workspace. This can be either `fixed` or `dynamic`.\nIn `fixed` mode, the current workspace used at runtime is always the one set in the currentWorkspace config field.\nIn `dynamic` mode, the current workspace used at runtime is determined by the current directory.\nIf the current directory is within a workspace, that workspace is used.\n",
6178
"type": "string",

docs/types/config.md

+2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ Alternatively, a custom path can be set using the `FLOW_CONFIG_PATH` environment
2626
| `currentNamespace` | The name of the current namespace. Namespaces are used to reference executables in the CLI using the format `workspace:namespace/name`. If the namespace is not set, only executables defined without a namespace will be discovered. | `string` | | |
2727
| `currentWorkspace` | The name of the current workspace. This should match a key in the `workspaces` or `remoteWorkspaces` map. | `string` | | |
2828
| `defaultLogMode` | The default log mode to use when running executables. This can either be `hidden`, `json`, `logfmt` or `text` `hidden` will not display any logs. `json` will display logs in JSON format. `logfmt` will display logs with a log level, timestamp, and message. `text` will just display the log message. | `string` | logfmt | |
29+
| `defaultTimeout` | The default timeout to use when running executables. This should be a valid duration string. | `string` | 30m | |
2930
| `interactive` | | [Interactive](#Interactive) | <no value> | |
3031
| `templates` | A map of flowfile template names to their paths. | `map` (`string` -> `string`) | map[] | |
32+
| `theme` | The theme of the interactive UI. | `string` | default | |
3133
| `workspaceMode` | The mode of the workspace. This can be either `fixed` or `dynamic`. In `fixed` mode, the current workspace used at runtime is always the one set in the currentWorkspace config field. In `dynamic` mode, the current workspace used at runtime is determined by the current directory. If the current directory is within a workspace, that workspace is used. | `string` | dynamic | |
3234
| `workspaces` | Map of workspace names to their paths. The path should be a valid absolute path to the workspace directory. | `map` (`string` -> `string`) | <no value> | |
3335

0 commit comments

Comments
 (0)