From 529c7eacdff31217488bf4df3f6d03d68a42cfef Mon Sep 17 00:00:00 2001 From: sawka Date: Wed, 4 Dec 2024 22:43:54 -0800 Subject: [PATCH 1/7] bg docs --- docs/docs/customization.mdx | 4 ++ docs/docs/presets.mdx | 87 ++++++++++++++++++++++++++++++++----- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/docs/docs/customization.mdx b/docs/docs/customization.mdx index ad5f93603a..129562d2a1 100644 --- a/docs/docs/customization.mdx +++ b/docs/docs/customization.mdx @@ -65,6 +65,10 @@ You can also suppress the help widgets in the bottom right by setting the config
+## Tab Backgrounds + +Wave supports powerful custom backgrounds for your tabs using images, patterns, gradients, and colors. You can add your own image backgrounds by creating a background preset. See our [Background Configuration](/presets#background-configurations) documentation for examples and detailed setup instructions. + ## Presets For more advanced customization, to set up multiple AI models, and your own tab backgrounds, check out our [Presets Documentation](./presets). diff --git a/docs/docs/presets.mdx b/docs/docs/presets.mdx index 6b2dd56112..883f364327 100644 --- a/docs/docs/presets.mdx +++ b/docs/docs/presets.mdx @@ -18,7 +18,7 @@ wsh editconfig presets.json ::: -### File format +## File format Presets follow the following format: @@ -61,7 +61,7 @@ A complete example of a preset for an AI model is the following: } ``` -### Preset type +## Preset Types The type of the preset determines where it can be discovered in the app. Currently, the two types that will be discovered in the app are `bg` and `ai`. @@ -90,7 +90,7 @@ Configs in a preset are applied in order to override the default config values, ::: -#### AI configurations +## AI Configurations | Key Name | Type | Function | | ------------- | ------ | -------------------------------------------------------------------------------------------------- | @@ -106,15 +106,80 @@ Configs in a preset are applied in order to override the default config values, | ai:maxtokens | int | max tokens to pass to API | | ai:timeoutms | int | timeout (in milliseconds) for AI calls | -#### Background configurations + -| Key Name | Type | Function | -| -------------------- | ------ | ----------------------------------------------------------------------------------------------- | -| bg:\* | bool | reset all existing bg keys | -| bg:opacity | float | the opacity of the background | -| bg:blendmode | string | the [blend mode](https://developer.mozilla.org/en-US/docs/Web/CSS/blend-mode) of the background | -| bg:bordercolor | string | the color of the border | -| bg:activebordercolor | string | the color of the border when a block is active | +## Background Configurations + +Wave's background system harnesses the full power of CSS backgrounds, letting you create rich visual effects through the "background" attribute. You can apply solid colors, gradients (both linear and radial), images, and even blend multiple elements together. The system offers preset configurations while maintaining the flexibility of pure CSS, making it both powerful and easy to use. + +| Key Name | Type | Function | +| -------------------- | ------ | ------------------------------------------------------------------------------------------------------- | +| bg:\* | bool | reset all existing bg keys (recommended to prevent any existing background settings from carrying over) | +| bg | string | CSS `background` attribute for the tab (supports colors, gradients images, etc.) | +| bg:opacity | float | the opacity of the background (defaults to 0.5) | +| bg:blendmode | string | the [blend mode](https://developer.mozilla.org/en-US/docs/Web/CSS/blend-mode) of the background | +| bg:bordercolor | string | the color of the border when a block is not active (rarely used) | +| bg:activebordercolor | string | the color of the border when a block is active | + +### Simple solid color with opacity: + +```json +{ + "bg@blue": { + "display:name": "Blue", + "bg:*": true, + "bg": "blue", + "bg:opacity": 0.3, + "bg:activebordercolor": "rgba(0, 0, 255, 1.0)" + } +} +``` + +### Complex gradient combining multiple effects: + +```json +{ + "bg@duskhorizon": { + "display:name": "Dusk Horizon", + "bg:*": true, + "bg": "linear-gradient(0deg, rgba(128,0,0,1) 0%, rgba(204,85,0,0.7) 20%, rgba(255,140,0,0.6) 45%, rgba(160,90,160,0.5) 65%, rgba(60,60,120,1) 100%), radial-gradient(circle at 30% 30%, rgba(255,255,255,0.1), transparent 60%), radial-gradient(circle at 70% 70%, rgba(255,255,255,0.05), transparent 70%)", + "bg:opacity": 0.9, + "bg:blendmode": "overlay" + } +} +``` + +### Repeating pattern background: + +```json +{ + "bg@pattern": { + "display:name": "Diamond Pattern", + "bg:*": true, + "bg": "url('/path/to/pattern.png') repeat", + "bg:opacity": 0.15 + } +} +``` + +### Full-cover background image: + +```json +{ + "bg@cover": { + "display:name": "Ocean Scene", + "bg:*": true, + "bg": "url('/path/to/ocean.jpg') center/cover no-repeat", + "bg:opacity": 0.2 + } +} +``` + +Replace image URLs with your own assets. All examples use reduced opacity to work well with dark themes. + +:::info +Background images can be specified using URLs or local file paths. While URLs are supported, it's recommended to download and serve images locally for better reliability. For local files, you must use absolute paths or paths starting with `~` (e.g. `~/Downloads/background.png`). The system will automatically rewrite local paths to ensure proper access. We support all common web image formats: PNG, JPEG/JPG, WebP, GIF, and SVG. +::: #### Unset a default value From 2d3c1118cbf14e486ede21f62a9041e0a1c513ef Mon Sep 17 00:00:00 2001 From: sawka Date: Wed, 4 Dec 2024 23:10:55 -0800 Subject: [PATCH 2/7] write a simple wsh setbg command --- cmd/wsh/cmd/wshcmd-setbg.go | 110 ++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 cmd/wsh/cmd/wshcmd-setbg.go diff --git a/cmd/wsh/cmd/wshcmd-setbg.go b/cmd/wsh/cmd/wshcmd-setbg.go new file mode 100644 index 0000000000..a73426e232 --- /dev/null +++ b/cmd/wsh/cmd/wshcmd-setbg.go @@ -0,0 +1,110 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + "github.com/wavetermdev/waveterm/pkg/util/utilfn" + "github.com/wavetermdev/waveterm/pkg/wavebase" + "github.com/wavetermdev/waveterm/pkg/wshrpc" + "github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient" +) + +var setBgCmd = &cobra.Command{ + Use: "setbg [--opacity value] [--tile] image-path", + Short: "set background image for a tab", + RunE: setBgRun, + PreRunE: preRunSetupRpcClient, +} + +var ( + setBgOpacity float64 + setBgTile bool +) + +func init() { + rootCmd.AddCommand(setBgCmd) + setBgCmd.Flags().Float64Var(&setBgOpacity, "opacity", 0.5, "background opacity (0.0-1.0)") + setBgCmd.Flags().BoolVar(&setBgTile, "tile", false, "tile the background image instead of cover") +} + +func setBgRun(cmd *cobra.Command, args []string) (rtnErr error) { + defer func() { + sendActivity("setbg", rtnErr == nil) + }() + + if len(args) != 1 { + OutputHelpMessage(cmd) + return fmt.Errorf("setbg requires a path to an image file") + } + + if setBgOpacity < 0 || setBgOpacity > 1 { + return fmt.Errorf("opacity must be between 0.0 and 1.0") + } + + // Get absolute path and escape it for URL + imgPath := args[0] + absPath, err := filepath.Abs(wavebase.ExpandHomeDirSafe(imgPath)) + if err != nil { + return fmt.Errorf("resolving image path: %v", err) + } + + fileInfo, err := os.Stat(absPath) + if err != nil { + return fmt.Errorf("cannot access image file: %v", err) + } + if fileInfo.IsDir() { + return fmt.Errorf("path is a directory, not an image file") + } + + mimeType := utilfn.DetectMimeType(absPath, fileInfo, true) + switch mimeType { + case "image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml": + // Valid image type + default: + return fmt.Errorf("file does not appear to be a valid image (detected type: %s)", mimeType) + } + + // Create URL-safe path + escapedPath := strings.ReplaceAll(absPath, "'", "\\'") + + // Construct background style + bgStyle := fmt.Sprintf("url('%s')", escapedPath) + if setBgTile { + bgStyle += " repeat" + } else { + bgStyle += " center/cover no-repeat" + } + + // Create metadata + meta := map[string]interface{}{ + "bg:*": true, + "bg": bgStyle, + "bg:opacity": setBgOpacity, + } + + // Resolve tab reference + oRef, err := resolveSimpleId("tab") + if err != nil { + return err + } + + // Send RPC request + setMetaWshCmd := wshrpc.CommandSetMetaData{ + ORef: *oRef, + Meta: meta, + } + err = wshclient.SetMetaCommand(RpcClient, setMetaWshCmd, &wshrpc.RpcOpts{Timeout: 2000}) + if err != nil { + return fmt.Errorf("setting background: %v", err) + } + + WriteStdout("background set\n") + return nil +} From 6d56cdca5f72e0e56dcd6d248e601ed6350a8ae8 Mon Sep 17 00:00:00 2001 From: sawka Date: Wed, 4 Dec 2024 23:15:17 -0800 Subject: [PATCH 3/7] document wsh setbg --- docs/docs/wsh.mdx | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/docs/wsh.mdx b/docs/docs/wsh.mdx index a2f27abff2..be6dc57ea4 100644 --- a/docs/docs/wsh.mdx +++ b/docs/docs/wsh.mdx @@ -145,6 +145,47 @@ wsh editconfig widgets.json --- +## setbg + +The `setbg` command allows you to set a background image for the current tab with customizable opacity and tiling options. + +```bash +wsh setbg [--opacity value] [--tile] image-path +``` + +The command accepts a path to an image file and will set it as the background for the current tab. The image can be displayed either as a cover (default) or tiled, and you can adjust its opacity. + +Flags: + +- `--opacity value` - set the background opacity (0.0-1.0, default 0.5) +- `--tile` - tile the background image instead of using cover mode + +Supported image formats: JPEG, PNG, GIF, WebP, and SVG. + +Examples: + +```bash +# Set a background image with default settings +wsh setbg ~/pictures/background.jpg + +# Set a background with custom opacity +wsh setbg --opacity 0.3 ~/pictures/light-pattern.png + +# Set a tiled background +wsh setbg --tile --opacity 0.2 ~/pictures/texture.png + +# Set a vector background +wsh setbg ~/pictures/pattern.svg +``` + +The command will validate that: + +- The provided path points to an accessible image file +- The file is a supported image format +- The opacity value is between 0.0 and 1.0 + +--- + ## run The `run` command creates a new terminal command block and executes a specified command within it. The command can be provided either as arguments after `--` or using the `-c` flag. Unless the `-x` or `-X` flags are passed, commands can be re-executed by pressing `Enter` once the command has finished running. From dabd11aa393e2e8d523361c7e0a2cf312385b264 Mon Sep 17 00:00:00 2001 From: sawka Date: Wed, 4 Dec 2024 23:20:06 -0800 Subject: [PATCH 4/7] integrate wsh setbg --- docs/docs/customization.mdx | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/docs/customization.mdx b/docs/docs/customization.mdx index 129562d2a1..64b4e8d681 100644 --- a/docs/docs/customization.mdx +++ b/docs/docs/customization.mdx @@ -67,7 +67,26 @@ You can also suppress the help widgets in the bottom right by setting the config ## Tab Backgrounds -Wave supports powerful custom backgrounds for your tabs using images, patterns, gradients, and colors. You can add your own image backgrounds by creating a background preset. See our [Background Configuration](/presets#background-configurations) documentation for examples and detailed setup instructions. +Wave supports powerful custom backgrounds for your tabs using images, patterns, gradients, and colors. The quickest way to set an image background is using the `wsh setbg` command: + +```bash +# Set a background image with 50% opacity (default) +wsh setbg ~/pictures/background.jpg + +# Set a more transparent background +wsh setbg --opacity 0.3 ~/pictures/light-pattern.png + +# Create a tiled pattern background +wsh setbg --tile --opacity 0.2 ~/pictures/texture.png +``` + +You can use any JPEG, PNG, GIF, WebP, or SVG image as your background. To remove a background, clear the background metadata: + +```bash +wsh setmeta -b tab "bg:*=true" +``` + +For more advanced customization options including gradients, colors, and saving your own background presets, check out our [Background Configuration](/presets#background-configurations) documentation. ## Presets From 327db8a261f6b8349dae4759c26cee994574ce26 Mon Sep 17 00:00:00 2001 From: sawka Date: Wed, 4 Dec 2024 23:41:11 -0800 Subject: [PATCH 5/7] css colors, checkpoint --- cmd/wsh/cmd/csscolormap.go | 147 ++++++++++++++++++++++++++++++++++++ cmd/wsh/cmd/wshcmd-setbg.go | 99 +++++++++++++++--------- 2 files changed, 212 insertions(+), 34 deletions(-) create mode 100644 cmd/wsh/cmd/csscolormap.go diff --git a/cmd/wsh/cmd/csscolormap.go b/cmd/wsh/cmd/csscolormap.go new file mode 100644 index 0000000000..f2b52485e4 --- /dev/null +++ b/cmd/wsh/cmd/csscolormap.go @@ -0,0 +1,147 @@ +// Copyright 2024, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +var CssColorNames = map[string]bool{ + "aliceblue": true, + "antiquewhite": true, + "aqua": true, + "aquamarine": true, + "azure": true, + "beige": true, + "bisque": true, + "black": true, + "blanchedalmond": true, + "blue": true, + "blueviolet": true, + "brown": true, + "burlywood": true, + "cadetblue": true, + "chartreuse": true, + "chocolate": true, + "coral": true, + "cornflowerblue": true, + "cornsilk": true, + "crimson": true, + "cyan": true, + "darkblue": true, + "darkcyan": true, + "darkgoldenrod": true, + "darkgray": true, + "darkgreen": true, + "darkkhaki": true, + "darkmagenta": true, + "darkolivegreen": true, + "darkorange": true, + "darkorchid": true, + "darkred": true, + "darksalmon": true, + "darkseagreen": true, + "darkslateblue": true, + "darkslategray": true, + "darkturquoise": true, + "darkviolet": true, + "deeppink": true, + "deepskyblue": true, + "dimgray": true, + "dodgerblue": true, + "firebrick": true, + "floralwhite": true, + "forestgreen": true, + "fuchsia": true, + "gainsboro": true, + "ghostwhite": true, + "gold": true, + "goldenrod": true, + "gray": true, + "green": true, + "greenyellow": true, + "honeydew": true, + "hotpink": true, + "indianred": true, + "indigo": true, + "ivory": true, + "khaki": true, + "lavender": true, + "lavenderblush": true, + "lawngreen": true, + "lemonchiffon": true, + "lightblue": true, + "lightcoral": true, + "lightcyan": true, + "lightgoldenrodyellow": true, + "lightgray": true, + "lightgreen": true, + "lightpink": true, + "lightsalmon": true, + "lightseagreen": true, + "lightskyblue": true, + "lightslategray": true, + "lightsteelblue": true, + "lightyellow": true, + "lime": true, + "limegreen": true, + "linen": true, + "magenta": true, + "maroon": true, + "mediumaquamarine": true, + "mediumblue": true, + "mediumorchid": true, + "mediumpurple": true, + "mediumseagreen": true, + "mediumslateblue": true, + "mediumspringgreen": true, + "mediumturquoise": true, + "mediumvioletred": true, + "midnightblue": true, + "mintcream": true, + "mistyrose": true, + "moccasin": true, + "navajowhite": true, + "navy": true, + "oldlace": true, + "olive": true, + "olivedrab": true, + "orange": true, + "orangered": true, + "orchid": true, + "palegoldenrod": true, + "palegreen": true, + "paleturquoise": true, + "palevioletred": true, + "papayawhip": true, + "peachpuff": true, + "peru": true, + "pink": true, + "plum": true, + "powderblue": true, + "purple": true, + "red": true, + "rosybrown": true, + "royalblue": true, + "saddlebrown": true, + "salmon": true, + "sandybrown": true, + "seagreen": true, + "seashell": true, + "sienna": true, + "silver": true, + "skyblue": true, + "slateblue": true, + "slategray": true, + "snow": true, + "springgreen": true, + "steelblue": true, + "tan": true, + "teal": true, + "thistle": true, + "tomato": true, + "turquoise": true, + "violet": true, + "wheat": true, + "white": true, + "whitesmoke": true, + "yellow": true, + "yellowgreen": true, +} diff --git a/cmd/wsh/cmd/wshcmd-setbg.go b/cmd/wsh/cmd/wshcmd-setbg.go index a73426e232..70a11e46f0 100644 --- a/cmd/wsh/cmd/wshcmd-setbg.go +++ b/cmd/wsh/cmd/wshcmd-setbg.go @@ -4,6 +4,7 @@ package cmd import ( + "encoding/hex" "fmt" "os" "path/filepath" @@ -17,8 +18,12 @@ import ( ) var setBgCmd = &cobra.Command{ - Use: "setbg [--opacity value] [--tile] image-path", - Short: "set background image for a tab", + Use: "setbg [--opacity value] [--tile] (image-path|\"#color\"|color-name)", + Short: "set background image or color for a tab", + Long: `Set a background image or color for a tab. Colors can be specified as: + - A quoted hex value like "#ff0000" (quotes required to prevent # being interpreted as a shell comment) + - A CSS color name like "blue" or "forestgreen" +Or provide a path to a supported image file (jpg, png, gif, webp, or svg).`, RunE: setBgRun, PreRunE: preRunSetupRpcClient, } @@ -34,6 +39,21 @@ func init() { setBgCmd.Flags().BoolVar(&setBgTile, "tile", false, "tile the background image instead of cover") } +func validateHexColor(color string) error { + if !strings.HasPrefix(color, "#") { + return fmt.Errorf("color must start with #") + } + colorHex := color[1:] + if len(colorHex) != 6 && len(colorHex) != 8 { + return fmt.Errorf("color must be in #RRGGBB or #RRGGBBAA format") + } + _, err := hex.DecodeString(colorHex) + if err != nil { + return fmt.Errorf("invalid hex color: %v", err) + } + return nil +} + func setBgRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { sendActivity("setbg", rtnErr == nil) @@ -41,45 +61,56 @@ func setBgRun(cmd *cobra.Command, args []string) (rtnErr error) { if len(args) != 1 { OutputHelpMessage(cmd) - return fmt.Errorf("setbg requires a path to an image file") + return fmt.Errorf("setbg requires an image path or color value") } if setBgOpacity < 0 || setBgOpacity > 1 { return fmt.Errorf("opacity must be between 0.0 and 1.0") } - // Get absolute path and escape it for URL - imgPath := args[0] - absPath, err := filepath.Abs(wavebase.ExpandHomeDirSafe(imgPath)) - if err != nil { - return fmt.Errorf("resolving image path: %v", err) - } - - fileInfo, err := os.Stat(absPath) - if err != nil { - return fmt.Errorf("cannot access image file: %v", err) - } - if fileInfo.IsDir() { - return fmt.Errorf("path is a directory, not an image file") - } - - mimeType := utilfn.DetectMimeType(absPath, fileInfo, true) - switch mimeType { - case "image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml": - // Valid image type - default: - return fmt.Errorf("file does not appear to be a valid image (detected type: %s)", mimeType) - } - - // Create URL-safe path - escapedPath := strings.ReplaceAll(absPath, "'", "\\'") - - // Construct background style - bgStyle := fmt.Sprintf("url('%s')", escapedPath) - if setBgTile { - bgStyle += " repeat" + var bgStyle string + input := args[0] + + // Check for hex color + if strings.HasPrefix(input, "#") { + if err := validateHexColor(input); err != nil { + return err + } + bgStyle = input + } else if CssColorNames[strings.ToLower(input)] { + // Handle CSS color name + bgStyle = strings.ToLower(input) } else { - bgStyle += " center/cover no-repeat" + // Handle image input + absPath, err := filepath.Abs(wavebase.ExpandHomeDirSafe(input)) + if err != nil { + return fmt.Errorf("resolving image path: %v", err) + } + + fileInfo, err := os.Stat(absPath) + if err != nil { + return fmt.Errorf("cannot access image file: %v", err) + } + if fileInfo.IsDir() { + return fmt.Errorf("path is a directory, not an image file") + } + + mimeType := utilfn.DetectMimeType(absPath, fileInfo, true) + switch mimeType { + case "image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml": + // Valid image type + default: + return fmt.Errorf("file does not appear to be a valid image (detected type: %s)", mimeType) + } + + // Create URL-safe path + escapedPath := strings.ReplaceAll(absPath, "'", "\\'") + bgStyle = fmt.Sprintf("url('%s')", escapedPath) + if setBgTile { + bgStyle += " repeat" + } else { + bgStyle += " center/cover no-repeat" + } } // Create metadata From baaa00a6dc0be300960fd22be73cb2efbe370930 Mon Sep 17 00:00:00 2001 From: sawka Date: Thu, 5 Dec 2024 00:04:14 -0800 Subject: [PATCH 6/7] support --clear, --center, --size, and bare --opacity setting --- cmd/wsh/cmd/wshcmd-setbg.go | 151 +++++++++++++++++++++++------------- 1 file changed, 98 insertions(+), 53 deletions(-) diff --git a/cmd/wsh/cmd/wshcmd-setbg.go b/cmd/wsh/cmd/wshcmd-setbg.go index 70a11e46f0..543ae48d42 100644 --- a/cmd/wsh/cmd/wshcmd-setbg.go +++ b/cmd/wsh/cmd/wshcmd-setbg.go @@ -5,6 +5,7 @@ package cmd import ( "encoding/hex" + "encoding/json" "fmt" "os" "path/filepath" @@ -18,12 +19,19 @@ import ( ) var setBgCmd = &cobra.Command{ - Use: "setbg [--opacity value] [--tile] (image-path|\"#color\"|color-name)", + Use: "setbg [--opacity value] [--tile|--center] [--scale value] (image-path|\"#color\"|color-name)", Short: "set background image or color for a tab", Long: `Set a background image or color for a tab. Colors can be specified as: - A quoted hex value like "#ff0000" (quotes required to prevent # being interpreted as a shell comment) - A CSS color name like "blue" or "forestgreen" -Or provide a path to a supported image file (jpg, png, gif, webp, or svg).`, +Or provide a path to a supported image file (jpg, png, gif, webp, or svg). + +You can also: + - Use --clear to remove the background + - Use --opacity without other arguments to change just the opacity + - Use --center for centered images without scaling (good for logos) + - Use --scale with --center to control image size + - Use --print to see the metadata without applying it`, RunE: setBgRun, PreRunE: preRunSetupRpcClient, } @@ -31,12 +39,23 @@ Or provide a path to a supported image file (jpg, png, gif, webp, or svg).`, var ( setBgOpacity float64 setBgTile bool + setBgCenter bool + setBgSize string + setBgClear bool + setBgPrint bool ) func init() { rootCmd.AddCommand(setBgCmd) setBgCmd.Flags().Float64Var(&setBgOpacity, "opacity", 0.5, "background opacity (0.0-1.0)") - setBgCmd.Flags().BoolVar(&setBgTile, "tile", false, "tile the background image instead of cover") + setBgCmd.Flags().BoolVar(&setBgTile, "tile", false, "tile the background image") + setBgCmd.Flags().BoolVar(&setBgCenter, "center", false, "center the image without scaling") + setBgCmd.Flags().StringVar(&setBgSize, "size", "auto", "scale factor for centered images (px, %, or auto)") + setBgCmd.Flags().BoolVar(&setBgClear, "clear", false, "clear the background") + setBgCmd.Flags().BoolVar(&setBgPrint, "print", false, "print the metadata without applying it") + + // Make tile and center mutually exclusive + setBgCmd.MarkFlagsMutuallyExclusive("tile", "center") } func validateHexColor(color string) error { @@ -59,65 +78,91 @@ func setBgRun(cmd *cobra.Command, args []string) (rtnErr error) { sendActivity("setbg", rtnErr == nil) }() - if len(args) != 1 { - OutputHelpMessage(cmd) - return fmt.Errorf("setbg requires an image path or color value") - } - - if setBgOpacity < 0 || setBgOpacity > 1 { - return fmt.Errorf("opacity must be between 0.0 and 1.0") - } - - var bgStyle string - input := args[0] + // Create base metadata + meta := map[string]interface{}{} - // Check for hex color - if strings.HasPrefix(input, "#") { - if err := validateHexColor(input); err != nil { - return err + // Handle opacity-only change or clear + if len(args) == 0 { + if !cmd.Flags().Changed("opacity") && !setBgClear { + OutputHelpMessage(cmd) + return fmt.Errorf("setbg requires an image path or color value") } - bgStyle = input - } else if CssColorNames[strings.ToLower(input)] { - // Handle CSS color name - bgStyle = strings.ToLower(input) - } else { - // Handle image input - absPath, err := filepath.Abs(wavebase.ExpandHomeDirSafe(input)) - if err != nil { - return fmt.Errorf("resolving image path: %v", err) + if setBgOpacity < 0 || setBgOpacity > 1 { + return fmt.Errorf("opacity must be between 0.0 and 1.0") } - - fileInfo, err := os.Stat(absPath) - if err != nil { - return fmt.Errorf("cannot access image file: %v", err) + if cmd.Flags().Changed("opacity") { + meta["bg:opacity"] = setBgOpacity } - if fileInfo.IsDir() { - return fmt.Errorf("path is a directory, not an image file") - } - - mimeType := utilfn.DetectMimeType(absPath, fileInfo, true) - switch mimeType { - case "image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml": - // Valid image type - default: - return fmt.Errorf("file does not appear to be a valid image (detected type: %s)", mimeType) + } else if len(args) > 1 { + OutputHelpMessage(cmd) + return fmt.Errorf("too many arguments") + } else { + // Handle background setting + meta["bg:*"] = true + if setBgOpacity < 0 || setBgOpacity > 1 { + return fmt.Errorf("opacity must be between 0.0 and 1.0") } - - // Create URL-safe path - escapedPath := strings.ReplaceAll(absPath, "'", "\\'") - bgStyle = fmt.Sprintf("url('%s')", escapedPath) - if setBgTile { - bgStyle += " repeat" + meta["bg:opacity"] = setBgOpacity + + input := args[0] + var bgStyle string + + // Check for hex color + if strings.HasPrefix(input, "#") { + if err := validateHexColor(input); err != nil { + return err + } + bgStyle = input + } else if CssColorNames[strings.ToLower(input)] { + // Handle CSS color name + bgStyle = strings.ToLower(input) } else { - bgStyle += " center/cover no-repeat" + // Handle image input + absPath, err := filepath.Abs(wavebase.ExpandHomeDirSafe(input)) + if err != nil { + return fmt.Errorf("resolving image path: %v", err) + } + + fileInfo, err := os.Stat(absPath) + if err != nil { + return fmt.Errorf("cannot access image file: %v", err) + } + if fileInfo.IsDir() { + return fmt.Errorf("path is a directory, not an image file") + } + + mimeType := utilfn.DetectMimeType(absPath, fileInfo, true) + switch mimeType { + case "image/jpeg", "image/png", "image/gif", "image/webp", "image/svg+xml": + // Valid image type + default: + return fmt.Errorf("file does not appear to be a valid image (detected type: %s)", mimeType) + } + + // Create URL-safe path + escapedPath := strings.ReplaceAll(absPath, "'", "\\'") + bgStyle = fmt.Sprintf("url('%s')", escapedPath) + + switch { + case setBgTile: + bgStyle += " repeat" + case setBgCenter: + bgStyle += fmt.Sprintf(" no-repeat center/%s", setBgSize) + default: + bgStyle += " center/cover no-repeat" + } } + + meta["bg"] = bgStyle } - // Create metadata - meta := map[string]interface{}{ - "bg:*": true, - "bg": bgStyle, - "bg:opacity": setBgOpacity, + if setBgPrint { + jsonBytes, err := json.MarshalIndent(meta, "", " ") + if err != nil { + return fmt.Errorf("error formatting metadata: %v", err) + } + WriteStdout(string(jsonBytes) + "\n") + return nil } // Resolve tab reference From 7425b2b505ef20fffe4ca44d98da5c5f727b0fe3 Mon Sep 17 00:00:00 2001 From: sawka Date: Thu, 5 Dec 2024 00:15:58 -0800 Subject: [PATCH 7/7] update all the documentation for the new wsh setbg functionality --- cmd/wsh/cmd/wshcmd-setbg.go | 2 +- docs/docs/customization.mdx | 24 +++++++++++++++----- docs/docs/presets.mdx | 24 ++++++++++++++++++++ docs/docs/wsh.mdx | 45 +++++++++++++++++++++++++++++-------- 4 files changed, 79 insertions(+), 16 deletions(-) diff --git a/cmd/wsh/cmd/wshcmd-setbg.go b/cmd/wsh/cmd/wshcmd-setbg.go index 543ae48d42..b40e2fcf8b 100644 --- a/cmd/wsh/cmd/wshcmd-setbg.go +++ b/cmd/wsh/cmd/wshcmd-setbg.go @@ -50,7 +50,7 @@ func init() { setBgCmd.Flags().Float64Var(&setBgOpacity, "opacity", 0.5, "background opacity (0.0-1.0)") setBgCmd.Flags().BoolVar(&setBgTile, "tile", false, "tile the background image") setBgCmd.Flags().BoolVar(&setBgCenter, "center", false, "center the image without scaling") - setBgCmd.Flags().StringVar(&setBgSize, "size", "auto", "scale factor for centered images (px, %, or auto)") + setBgCmd.Flags().StringVar(&setBgSize, "size", "auto", "size for centered images (px, %, or auto)") setBgCmd.Flags().BoolVar(&setBgClear, "clear", false, "clear the background") setBgCmd.Flags().BoolVar(&setBgPrint, "print", false, "print the metadata without applying it") diff --git a/docs/docs/customization.mdx b/docs/docs/customization.mdx index 64b4e8d681..ff3d308034 100644 --- a/docs/docs/customization.mdx +++ b/docs/docs/customization.mdx @@ -70,20 +70,32 @@ You can also suppress the help widgets in the bottom right by setting the config Wave supports powerful custom backgrounds for your tabs using images, patterns, gradients, and colors. The quickest way to set an image background is using the `wsh setbg` command: ```bash -# Set a background image with 50% opacity (default) +# Set an image background with 50% opacity (default) wsh setbg ~/pictures/background.jpg -# Set a more transparent background +# Set a color background (use quotes to prevent # being interpreted as a shell comment) +wsh setbg "#ff0000" # hex color +wsh setbg forestgreen # CSS color name + +# Adjust opacity wsh setbg --opacity 0.3 ~/pictures/light-pattern.png +wsh setbg --opacity 0.7 # change only opacity of current background + +# Image positioning options +wsh setbg --tile ~/pictures/texture.png # create tiled pattern +wsh setbg --center ~/pictures/logo.png # center without scaling +wsh setbg --center --size 200px ~/pictures/logo.png # center with specific size (px, %, auto) -# Create a tiled pattern background -wsh setbg --tile --opacity 0.2 ~/pictures/texture.png +# Remove background +wsh setbg --clear ``` -You can use any JPEG, PNG, GIF, WebP, or SVG image as your background. To remove a background, clear the background metadata: +You can use any JPEG, PNG, GIF, WebP, or SVG image as your background. The `--center` option is particularly useful for logos or icons where you want to maintain the original size. + +To preview the metadata for any background without applying it, use the `--print` flag: ```bash -wsh setmeta -b tab "bg:*=true" +wsh setbg --print "#ff0000" ``` For more advanced customization options including gradients, colors, and saving your own background presets, check out our [Background Configuration](/presets#background-configurations) documentation. diff --git a/docs/docs/presets.mdx b/docs/docs/presets.mdx index 883f364327..31b15115e1 100644 --- a/docs/docs/presets.mdx +++ b/docs/docs/presets.mdx @@ -181,6 +181,30 @@ Replace image URLs with your own assets. All examples use reduced opacity to wor Background images can be specified using URLs or local file paths. While URLs are supported, it's recommended to download and serve images locally for better reliability. For local files, you must use absolute paths or paths starting with `~` (e.g. `~/Downloads/background.png`). The system will automatically rewrite local paths to ensure proper access. We support all common web image formats: PNG, JPEG/JPG, WebP, GIF, and SVG. ::: +:::tip +You can use `wsh setbg --print` to help generate the JSON for your background presets. For example: + +```bash +# Preview the metadata for a gradient background + wsh setbg --print "#ff0000" +{ + "bg:*": true, + "bg": "#ff0000", + "bg:opacity": 0.5 +} + +# Preview a centered logo configuration +wsh setbg --print --center --opacity 0.3 ~/logo.png +{ + "bg:*": true, + "bg": "url('/absolute/path/to/logo.png') no-repeat center/auto", + "bg:opacity": 0.3 +} +``` + +Copy the output and use it as a starting point for your preset configuration, just add the required `display:name` field! +::: + #### Unset a default value To unset a default value in a preset, add an override that sets it to an empty string, like `""`. diff --git a/docs/docs/wsh.mdx b/docs/docs/wsh.mdx index be6dc57ea4..6766e78f7a 100644 --- a/docs/docs/wsh.mdx +++ b/docs/docs/wsh.mdx @@ -147,25 +147,33 @@ wsh editconfig widgets.json ## setbg -The `setbg` command allows you to set a background image for the current tab with customizable opacity and tiling options. +The `setbg` command allows you to set a background image or color for the current tab with various customization options. ```bash -wsh setbg [--opacity value] [--tile] image-path +wsh setbg [--opacity value] [--tile|--center] [--size value] (image-path|"#color"|color-name) ``` -The command accepts a path to an image file and will set it as the background for the current tab. The image can be displayed either as a cover (default) or tiled, and you can adjust its opacity. +You can set a background using: + +- An image file (displayed as cover, tiled, or centered) +- A hex color (must be quoted like "#ff0000") +- A CSS color name (like "blue" or "forestgreen") Flags: - `--opacity value` - set the background opacity (0.0-1.0, default 0.5) - `--tile` - tile the background image instead of using cover mode +- `--center` - center the image without scaling (good for logos) +- `--size` - size for centered images (px, %, or auto) +- `--clear` - remove the background +- `--print` - show the metadata without applying it Supported image formats: JPEG, PNG, GIF, WebP, and SVG. Examples: ```bash -# Set a background image with default settings +# Set an image background with default settings wsh setbg ~/pictures/background.jpg # Set a background with custom opacity @@ -174,15 +182,34 @@ wsh setbg --opacity 0.3 ~/pictures/light-pattern.png # Set a tiled background wsh setbg --tile --opacity 0.2 ~/pictures/texture.png -# Set a vector background -wsh setbg ~/pictures/pattern.svg +# Center an image (good for logos) +wsh setbg --center ~/pictures/logo.png +wsh setbg --center --size 200px ~/pictures/logo.png + +# Set color backgrounds +wsh setbg "#ff0000" # hex color (requires quotes) +wsh setbg forestgreen # CSS color name + +# Change just the opacity of current background +wsh setbg --opacity 0.7 + +# Remove background +wsh setbg --clear + +# Preview the metadata +wsh setbg --print "#ff0000" ``` -The command will validate that: +The command validates that: -- The provided path points to an accessible image file -- The file is a supported image format +- Color values are valid hex codes or CSS color names +- Image paths point to accessible, supported image files - The opacity value is between 0.0 and 1.0 +- The center and tile options are not used together + +:::tip +Use `--print` to preview the metadata for any background configuration without applying it. You can then copy this JSON representation to use as a [Background Preset](/presets#background-configurations) +::: ---