Guide for adding new configuration settings to Wave Terminal. Use when adding a new setting to the configuration system, implementing a new config key, or adding user-customizable settings.
This guide explains how to add a new configuration setting to Wave Terminal's hierarchical configuration system.
Wave Terminal uses a hierarchical configuration system with:
pkg/wconfig/settingsconfig.goschema/settings.jsonpkg/wconfig/defaultconfig/settings.json~/.config/waveterm/settings.jsonpkg/waveobj/wtypemeta.godocs/docs/config.mdxSettings cascade from defaults → user settings → connection config → block overrides.
Edit pkg/wconfig/settingsconfig.go and add your new field to the SettingsType struct:
type SettingsType struct {
// ... existing fields ...
// Add your new field with appropriate JSON tag
MyNewSetting string `json:"mynew:setting,omitempty"`
// For different types:
MyBoolSetting bool `json:"mynew:boolsetting,omitempty"`
MyNumberSetting float64 `json:"mynew:numbersetting,omitempty"`
MyIntSetting *int64 `json:"mynew:intsetting,omitempty"` // Use pointer for optional ints
MyArraySetting []string `json:"mynew:arraysetting,omitempty"`
}
Naming Conventions:
term:, window:, ai:, web:, app:)omitempty tag to exclude empty values from JSONType Guidelines:
*int64 and *float64 for optional numeric values*bool for optional boolean values (or bool if default is false)string for text values[]string for arraysfloat64 for numbers that can be decimalsNamespace Organization:
app:* - Application-level settingsterm:* - Terminal-specific settingswindow:* - Window and UI settingsai:* - AI-related settingsweb:* - Web browser settingseditor:* - Code editor settingsconn:* - Connection settingsIf your setting should support block-level overrides, also add it to pkg/waveobj/wtypemeta.go:
type MetaTSType struct {
// ... existing fields ...
// Add your new field with matching JSON tag and type
MyNewSetting *string `json:"mynew:setting,omitempty"` // Use pointer for optional values
// For different types:
MyBoolSetting *bool `json:"mynew:boolsetting,omitempty"`
MyNumberSetting *float64 `json:"mynew:numbersetting,omitempty"`
MyIntSetting *int `json:"mynew:intsetting,omitempty"`
MyArraySetting []string `json:"mynew:arraysetting,omitempty"`
}
Block Metadata Guidelines:
*string, *bool, *int, *float64) for optional overridesIf your setting should have a default value, add it to pkg/wconfig/defaultconfig/settings.json:
{
"ai:preset": "ai@global",
"ai:model": "gpt-5-mini",
// ... existing defaults ...
"mynew:setting": "default value",
"mynew:boolsetting": true,
"mynew:numbersetting": 42.5,
"mynew:intsetting": 100
}
Default Value Guidelines:
false is the correct defaultAdd your new setting to the configuration table in docs/docs/config.mdx:
| Key Name | Type | Function |
| ------------------- | -------- | ----------------------------------------- |
| mynew:setting | string | Description of what this setting controls |
| mynew:boolsetting | bool | Enable/disable some feature |
| mynew:numbersetting | float | Numeric setting for some parameter |
| mynew:intsetting | int | Integer setting for some configuration |
| mynew:arraysetting | string[] | Array of strings for multiple values |
Documentation Guidelines:
<VersionBadge version="v0.14" />Run the generate task to automatically regenerate the JSON schema and TypeScript types:
task generate
What this does:
task build:schema (automatically generates JSON schema from Go structs)frontend/types/gotypes.d.tsImportant: The JSON schema in schema/settings.json is automatically generated from the Go struct definitions - you don't need to edit it manually.
Access your new setting in React components:
import { getOverrideConfigAtom, getSettingsKeyAtom, useAtomValue } from "@/store/global";
// In a React component
const MyComponent = ({ blockId }: { blockId: string }) => {
// Use override config atom for hierarchical resolution
// This automatically checks: block metadata → connection config → global settings → default
const mySettingAtom = getOverrideConfigAtom(blockId, "mynew:setting");
const mySetting = useAtomValue(mySettingAtom) ?? "fallback value";
// For global-only settings (no block overrides)
const globalOnlySetting = useAtomValue(getSettingsKeyAtom("mynew:globalsetting")) ?? "fallback";
return <div>Setting value: {mySetting}</div>;
};
Frontend Configuration Patterns:
// 1. Settings with block-level overrides (recommended for most view/display settings)
const termFontSize = useAtomValue(getOverrideConfigAtom(blockId, "term:fontsize")) ?? 12;
// 2. Global-only settings (app-wide settings that don't vary by block)
const appGlobalHotkey = useAtomValue(getSettingsKeyAtom("app:globalhotkey")) ?? "";
// 3. Connection-specific settings
const connStatus = useAtomValue(getConnStatusAtom(connectionName));
When to use each pattern:
getOverrideConfigAtom() for settings that can vary by block or connection (most UI/display settings)getSettingsKeyAtom() for app-level settings that are always global?? operatorAccess settings in Go code:
// Get the full config
fullConfig := wconfig.GetWatcher().GetFullConfig()
// Access your setting
myValue := fullConfig.Settings.MyNewSetting
// For optional values (pointers)
if fullConfig.Settings.MyIntSetting != nil {
intValue := *fullConfig.Settings.MyIntSetting
// Use intValue
}
Use case: Add a setting to hide the AI button globally
pkg/wconfig/settingsconfig.go)type SettingsType struct {
// ... existing fields ...
AppHideAiButton bool `json:"app:hideaibutton,omitempty"`
}
pkg/wconfig/defaultconfig/settings.json){
"app:hideaibutton": false
}
docs/docs/config.mdx)| app:hideaibutton <VersionBadge version="v0.14" /> | bool | Hide the AI button in the tab bar (defaults to false) |
task generate
import { getSettingsKeyAtom } from "@/store/global";
const TabBar = () => {
const hideAiButton = useAtomValue(getSettingsKeyAtom("app:hideaibutton"));
if (hideAiButton) {
return null; // Don't render AI button
}
return <button>AI</button>;
};
# Set in settings file
wsh setconfig app:hideaibutton=true
# Or edit ~/.config/waveterm/settings.json
{
"app:hideaibutton": true
}
Use case: Add a terminal bell sound setting that can be overridden per block
pkg/wconfig/settingsconfig.go)type SettingsType struct {
// ... existing fields ...
TermBellSound string `json:"term:bellsound,omitempty"`
}
pkg/waveobj/wtypemeta.go)type MetaTSType struct {
// ... existing fields ...
TermBellSound *string `json:"term:bellsound,omitempty"` // Pointer for optional override
}
pkg/wconfig/defaultconfig/settings.json){
"term:bellsound": "default"
}
docs/docs/config.mdx)| term:bellsound <VersionBadge version="v0.14" /> | string | Sound to play for terminal bell ("default", "none", or custom sound file path) |
task generate
import { getOverrideConfigAtom } from "@/store/global";
const TerminalView = ({ blockId }: { blockId: string }) => {
// Use override config for hierarchical resolution
const bellSoundAtom = getOverrideConfigAtom(blockId, "term:bellsound");
const bellSound = useAtomValue(bellSoundAtom) ?? "default";
const playBellSound = () => {
if (bellSound === "none") return;
// Play the bell sound
};
return <div>Terminal with bell: {bellSound}</div>;
};
# Set globally in settings file
wsh setconfig term:bellsound="custom.wav"
# Set for current block only
wsh setmeta term:bellsound="none"
# Set for specific block
wsh setmeta --block BLOCK_ID term:bellsound="beep"
# Or edit ~/.config/waveterm/settings.json
{
"term:bellsound": "custom.wav"
}
Each namespace can have a "clear" field for resetting all settings in that namespace:
AppClear bool `json:"app:*,omitempty"`
TermClear bool `json:"term:*,omitempty"`
*bool, *int64, *float64) for truly optional settingsSettings can be overridden at the block level using metadata:
import { RpcApi } from "@/app/store/wshclientapi";
import { TabRpcClient } from "@/app/store/wshrpcutil";
import { WOS } from "@/store/global";
// Set block-specific override
await RpcApi.SetMetaCommand(TabRpcClient, {
oref: WOS.makeORef("block", blockId),
meta: { "mynew:setting": "block-specific value" },
});
task generateProblem: TypeScript types not updated, schema out of sync
Solution: Always run task generate after modifying Go structs
Problem: Settings uses string, metadata uses *int
Solution: Ensure types match (except metadata uses pointers for optionals)
Problem: Component breaks if setting is undefined
Solution: Always use ?? operator with fallback:
const value = useAtomValue(getSettingsKeyAtom("key")) ?? "default";
Problem: Using getSettingsKeyAtom() for settings that need block overrides
Solution: Use getOverrideConfigAtom() for any setting in MetaTSType
term:fontsize not term:fsbool for simple on/off settings (no pointer if false is default)*bool only if you need to distinguish unset from false*int64/*float64 for optional numeric valuesstring for text, paths, or enum-like values[]string for lists<VersionBadge version="v0.x" />When adding a new configuration setting:
SettingsType in pkg/wconfig/settingsconfig.goMetaTSType in pkg/waveobj/wtypemeta.go (if block override needed)pkg/wconfig/defaultconfig/settings.json (if needed)docs/docs/config.mdxtask generate to update TypeScript typesgetOverrideConfigAtom or getSettingsKeyAtom) in frontenddocs/docs/config.mdx - User-facing configuration docspkg/wconfig/settingsconfig.go - Go struct definitionspkg/waveobj/wtypemeta.go - Block metadata definitions