Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .kilocode/skills/waveenv/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Create a narrowing whenever you are writing a component (or group of components)

```ts
import {
BlockMetaKeyAtomFnType, // only if you use getBlockMetaKeyAtom
MetaKeyAtomFnType, // only if you use getBlockMetaKeyAtom or getTabMetaKeyAtom
ConnConfigKeyAtomFnType, // only if you use getConnConfigKeyAtom
SettingsKeyAtomFnType, // only if you use getSettingsKeyAtom
WaveEnv,
Expand Down Expand Up @@ -77,12 +77,14 @@ export type MyEnv = WaveEnvSubset<{

// --- key-parameterized atom factories: enumerate the keys you use ---
getSettingsKeyAtom: SettingsKeyAtomFnType<"app:focusfollowscursor" | "window:magnifiedblockopacity">;
getBlockMetaKeyAtom: BlockMetaKeyAtomFnType<"view" | "frame:title" | "connection">;
getBlockMetaKeyAtom: MetaKeyAtomFnType<"view" | "frame:title" | "connection">;
getTabMetaKeyAtom: MetaKeyAtomFnType<"tabid" | "name">;
getConnConfigKeyAtom: ConnConfigKeyAtomFnType<"conn:wshenabled">;

// --- other atom helpers: copy verbatim ---
getConnStatusAtom: WaveEnv["getConnStatusAtom"];
getLocalHostDisplayNameAtom: WaveEnv["getLocalHostDisplayNameAtom"];
getConfigBackgroundAtom: WaveEnv["getConfigBackgroundAtom"];
}>;
```

Expand All @@ -104,7 +106,8 @@ Every `WaveEnvSubset<T>` automatically includes the mock fields — you never ne
| `wos` | `wos: WaveEnv["wos"]` | Take the whole `wos` object (no sub-typing needed), but **only add it if `wos` is actually used**. |
| `services` | `services: { svc: WaveEnv["services"]["svc"]; }` | List each service used; take the whole service object (no method-level narrowing). |
| `getSettingsKeyAtom` | `SettingsKeyAtomFnType<"key1" \| "key2">` | Union all settings keys accessed. |
| `getBlockMetaKeyAtom` | `BlockMetaKeyAtomFnType<"key1" \| "key2">` | Union all block meta keys accessed. |
| `getBlockMetaKeyAtom` | `MetaKeyAtomFnType<"key1" \| "key2">` | Union all block meta keys accessed. |
| `getTabMetaKeyAtom` | `MetaKeyAtomFnType<"key1" \| "key2">` | Union all tab meta keys accessed. |
| `getConnConfigKeyAtom` | `ConnConfigKeyAtomFnType<"key1">` | Union all conn config keys accessed. |
| All other `WaveEnv` fields | `WaveEnv["fieldName"]` | Copy type verbatim. |

Expand Down
37 changes: 28 additions & 9 deletions cmd/generateschema/main-generateschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const WaveSchemaSettingsFileName = "schema/settings.json"
const WaveSchemaConnectionsFileName = "schema/connections.json"
const WaveSchemaAiPresetsFileName = "schema/aipresets.json"
const WaveSchemaWidgetsFileName = "schema/widgets.json"
const WaveSchemaBgPresetsFileName = "schema/bgpresets.json"
const WaveSchemaBackgroundsFileName = "schema/backgrounds.json"
const WaveSchemaWaveAIFileName = "schema/waveai.json"

// ViewNameType is a string type whose JSON Schema offers enum suggestions for the most
Expand Down Expand Up @@ -105,8 +105,26 @@ type WidgetsMetaSchemaHints struct {
TermDurable *bool `json:"term:durable,omitempty"`
}

func generateSchema(template any, dir string) error {
// allowNullValues wraps the top-level additionalProperties of a map schema with
// anyOf: [originalSchema, {type: "null"}] so that setting a key to null is valid
// (e.g. "bg@foo": null to remove a default entry).
func allowNullValues(schema *jsonschema.Schema) {
if schema.AdditionalProperties != nil && schema.AdditionalProperties != jsonschema.TrueSchema && schema.AdditionalProperties != jsonschema.FalseSchema {
original := schema.AdditionalProperties
schema.AdditionalProperties = &jsonschema.Schema{
AnyOf: []*jsonschema.Schema{
original,
{Type: "null"},
},
}
}
}

func generateSchema(template any, dir string, allowNull bool) error {
settingsSchema := jsonschema.Reflect(template)
if allowNull {
allowNullValues(settingsSchema)
}

jsonSettingsSchema, err := json.MarshalIndent(settingsSchema, "", " ")
if err != nil {
Expand Down Expand Up @@ -147,6 +165,7 @@ func generateWidgetsSchema(dir string) error {

widgetsTemplate := make(map[string]wconfig.WidgetConfigType)
widgetsSchema := r.Reflect(&widgetsTemplate)
allowNullValues(widgetsSchema)

jsonWidgetsSchema, err := json.MarshalIndent(widgetsSchema, "", " ")
if err != nil {
Expand All @@ -163,19 +182,19 @@ func generateWidgetsSchema(dir string) error {
}

func main() {
err := generateSchema(&wconfig.SettingsType{}, WaveSchemaSettingsFileName)
err := generateSchema(&wconfig.SettingsType{}, WaveSchemaSettingsFileName, false)
if err != nil {
log.Fatalf("settings schema error: %v", err)
}

connectionTemplate := make(map[string]wconfig.ConnKeywords)
err = generateSchema(&connectionTemplate, WaveSchemaConnectionsFileName)
err = generateSchema(&connectionTemplate, WaveSchemaConnectionsFileName, false)
if err != nil {
log.Fatalf("connections schema error: %v", err)
}

aiPresetsTemplate := make(map[string]wconfig.AiSettingsType)
err = generateSchema(&aiPresetsTemplate, WaveSchemaAiPresetsFileName)
err = generateSchema(&aiPresetsTemplate, WaveSchemaAiPresetsFileName, false)
if err != nil {
log.Fatalf("ai presets schema error: %v", err)
}
Expand All @@ -185,14 +204,14 @@ func main() {
log.Fatalf("widgets schema error: %v", err)
}

bgPresetsTemplate := make(map[string]wconfig.BgPresetsType)
err = generateSchema(&bgPresetsTemplate, WaveSchemaBgPresetsFileName)
backgroundsTemplate := make(map[string]wconfig.BackgroundConfigType)
err = generateSchema(&backgroundsTemplate, WaveSchemaBackgroundsFileName, true)
if err != nil {
log.Fatalf("bg presets schema error: %v", err)
log.Fatalf("backgrounds schema error: %v", err)
}

waveAITemplate := make(map[string]wconfig.AIModeConfigType)
err = generateSchema(&waveAITemplate, WaveSchemaWaveAIFileName)
err = generateSchema(&waveAITemplate, WaveSchemaWaveAIFileName, false)
if err != nil {
log.Fatalf("waveai schema error: %v", err)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/server/main-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ func main() {
createMainWshClient()
sigutil.InstallShutdownSignalHandlers(doShutdown)
sigutil.InstallSIGUSR1Handler()
wconfig.MigratePresetsBackgrounds()
startConfigWatcher()
aiusechat.InitAIModeConfigWatcher()
maybeStartPprofServer()
Expand Down
57 changes: 47 additions & 10 deletions cmd/wsh/cmd/wshcmd-setbg.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

var setBgCmd = &cobra.Command{
Use: "setbg [--opacity value] [--tile|--center] [--scale value] (image-path|\"#color\"|color-name)",
Use: "setbg [--opacity value] [--tile|--center] [--scale value] [--border-color color] [--active-border-color color] (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)
Expand All @@ -31,18 +31,22 @@ You can also:
- 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 --border-color to set the block frame border color
- Use --active-border-color to set the block frame focused border color
- Use --print to see the metadata without applying it`,
RunE: setBgRun,
PreRunE: preRunSetupRpcClient,
}

var (
setBgOpacity float64
setBgTile bool
setBgCenter bool
setBgSize string
setBgClear bool
setBgPrint bool
setBgOpacity float64
setBgTile bool
setBgCenter bool
setBgSize string
setBgClear bool
setBgPrint bool
setBgBorderColor string
setBgActiveBorderColor string
)

func init() {
Expand All @@ -53,8 +57,9 @@ func init() {
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")
setBgCmd.Flags().StringVar(&setBgBorderColor, "border-color", "", "block frame border color (#RRGGBB, #RRGGBBAA, or CSS color name)")
setBgCmd.Flags().StringVar(&setBgActiveBorderColor, "active-border-color", "", "block frame focused border color (#RRGGBB, #RRGGBBAA, or CSS color name)")

// Make tile and center mutually exclusive
setBgCmd.MarkFlagsMutuallyExclusive("tile", "center")
}

Expand All @@ -73,17 +78,41 @@ func validateHexColor(color string) error {
return nil
}

func validateColor(color string) error {
if strings.HasPrefix(color, "#") {
return validateHexColor(color)
}
if !CssColorNames[strings.ToLower(color)] {
return fmt.Errorf("invalid color %q: must be a hex color (#RRGGBB or #RRGGBBAA) or a CSS color name", color)
}
return nil
}

func setBgRun(cmd *cobra.Command, args []string) (rtnErr error) {
defer func() {
sendActivity("setbg", rtnErr == nil)
}()

borderColorChanged := cmd.Flags().Changed("border-color")
activeBorderColorChanged := cmd.Flags().Changed("active-border-color")

if borderColorChanged {
if err := validateColor(setBgBorderColor); err != nil {
return fmt.Errorf("--border-color: %v", err)
}
}
if activeBorderColorChanged {
if err := validateColor(setBgActiveBorderColor); err != nil {
return fmt.Errorf("--active-border-color: %v", err)
}
}

// Create base metadata
meta := map[string]interface{}{}

// Handle opacity-only change or clear
if len(args) == 0 {
if !cmd.Flags().Changed("opacity") && !setBgClear {
if !cmd.Flags().Changed("opacity") && !setBgClear && !borderColorChanged && !activeBorderColorChanged {
OutputHelpMessage(cmd)
return fmt.Errorf("setbg requires an image path or color value")
}
Expand All @@ -92,7 +121,7 @@ func setBgRun(cmd *cobra.Command, args []string) (rtnErr error) {
}
if setBgClear {
meta["bg:*"] = true
} else {
} else if cmd.Flags().Changed("opacity") {
meta["bg:opacity"] = setBgOpacity
}
} else if len(args) > 1 {
Expand All @@ -101,6 +130,7 @@ func setBgRun(cmd *cobra.Command, args []string) (rtnErr error) {
} else {
// Handle background setting
meta["bg:*"] = true
meta["tab:background"] = nil
if setBgOpacity < 0 || setBgOpacity > 1 {
return fmt.Errorf("opacity must be between 0.0 and 1.0")
}
Expand Down Expand Up @@ -159,6 +189,13 @@ func setBgRun(cmd *cobra.Command, args []string) (rtnErr error) {
meta["bg"] = bgStyle
}

if borderColorChanged {
meta["bg:bordercolor"] = setBgBorderColor
}
if activeBorderColorChanged {
meta["bg:activebordercolor"] = setBgActiveBorderColor
}

if setBgPrint {
jsonBytes, err := json.MarshalIndent(meta, "", " ")
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions docs/docs/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: "Configuration"

import { Kbd } from "@site/src/components/kbd";
import { PlatformProvider, PlatformSelectorButton } from "@site/src/components/platformcontext";
import { VersionBadge } from "@site/src/components/versionbadge";
import { VersionBadge, DeprecatedBadge } from "@site/src/components/versionbadge";

<PlatformProvider>

Expand Down Expand Up @@ -92,7 +92,8 @@ wsh editconfig
| autoupdate:intervalms | float64 | time in milliseconds to wait between update checks (requires app restart) |
| autoupdate:installonquit | bool | whether to automatically install updates on quit (requires app restart) |
| autoupdate:channel | string | the auto update channel "latest" (stable builds), or "beta" (updated more frequently) (requires app restart) |
| tab:preset | string | a "bg@" preset to automatically apply to new tabs. e.g. `bg@green`. should match the preset key |
| tab:preset <DeprecatedBadge /> | string | a "bg@" preset to automatically apply to new tabs. e.g. `bg@green`. should match the preset key. deprecated in favor of `tab:background` |
| tab:background <VersionBadge version="v0.14.4" /> | string | a "bg@" preset to automatically apply to new tabs. e.g. `bg@green`. should match the preset key |
| tab:confirmclose | bool | if set to true, a confirmation dialog will be shown before closing a tab (defaults to false) |
| widget:showhelp | bool | whether to show help/tips widgets in right sidebar |
| window:transparent | bool | set to true to enable window transparency (cannot be combined with `window:blur`) (macOS and Windows only, requires app restart, see [note on Windows compatibility](https://www.electronjs.org/docs/latest/tutorial/custom-window-styles#limitations)) |
Expand Down
8 changes: 4 additions & 4 deletions docs/docs/customization.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ title: "Customization"

Right click on any tab to bring up a menu which allows you to rename the tab and select different backgrounds.

It is also possible to create your own themes using custom colors, gradients, images and more by editing your presets.json config file. To see how Wave's built in tab themes are defined, you can check out our [default presets file](https://github.com/wavetermdev/waveterm/blob/main/pkg/wconfig/defaultconfig/presets.json).
It is also possible to create your own background themes using custom colors, gradients, images and more by editing your backgrounds.json config file. To see how Wave's built-in tab backgrounds are defined, you can check out the [default backgrounds.json file](https://github.com/wavetermdev/waveterm/blob/main/pkg/wconfig/defaultconfig/backgrounds.json).

To apply a tab background to all new tabs by default, set the key `tab:background` in your [Wave Config File](/config) to one of the background preset keys (e.g. `"bg@ocean-depths"`). The available built-in background keys can be found in the [default backgrounds.json file](https://github.com/wavetermdev/waveterm/blob/main/pkg/wconfig/defaultconfig/backgrounds.json).

## Terminal Customization

Expand All @@ -26,8 +28,6 @@ in the [default termthemes.json file](https://github.com/wavetermdev/waveterm/bl

If you add your own termthemes.json file in the config directory, you can also add your own custom terminal themes (just follow the same format).

You can set the key `tab:preset` in your [Wave Config File](/config) to apply a theme to all new tabs.

#### Font Size

From the same context menu you can also change the font-size of the terminal. To change the default font size across all of your (non-overridden) terminals, you can set the config key `term:fontsize` to the size you want. e.g. `{ "term:fontsize": 14}`.
Expand Down Expand Up @@ -79,6 +79,6 @@ To preview the metadata for any background without applying it, use the `--print
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.
For more advanced customization options including gradients, colors, and saving your own custom backgrounds, check out our [Tab Backgrounds](/tab-backgrounds) documentation.


2 changes: 1 addition & 1 deletion docs/docs/releasenotes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ New minor release that introduces Wave's connected computing extensions. We've i

### v0.9.2 &mdash; Nov 11, 2024

New minor release with bug fixes and new features! Fixed the bug around making Wave fullscreen (also affecting certain window managers like Hyprland). We've also put a lot of work into the doc site (https://docs.waveterm.dev), including documenting how [Widgets](./widgets) and [Presets](./presets) work!
New minor release with bug fixes and new features! Fixed the bug around making Wave fullscreen (also affecting certain window managers like Hyprland). We've also put a lot of work into the doc site (https://docs.waveterm.dev), including documenting how [Widgets](./widgets) and Presets work!

- Updated documentation
- Wave AI now supports the Anthropic API! Checkout the [FAQ](./faq) for how to use the Claude models with Wave AI.
Expand Down
Loading