waveterm/aiprompts/config-system.md

13 KiB

Wave Terminal Configuration System

This document explains how Wave Terminal's configuration system works and provides step-by-step instructions for adding new configuration values.

Overview

Wave Terminal uses a hierarchical configuration system with the following components:

  1. Go Struct Definitions - Type-safe configuration structure in Go
  2. JSON Schema - Validation schema for configuration files
  3. Default Values - Built-in default configuration
  4. User Configuration - User-customizable settings in ~/.config/waveterm/settings.json
  5. Documentation - User-facing documentation

Configuration File Structure

Wave Terminal's configuration system is organized into several key directories and files:

waveterm/
├── pkg/wconfig/                          # Go configuration package
│   ├── settingsconfig.go                 # Main settings struct definitions
│   ├── defaultconfig/                    # Default configuration files
│   │   ├── settings.json                 # Default settings values
│   │   ├── termthemes.json              # Default terminal themes
│   │   ├── presets.json                 # Default background presets
│   │   └── widgets.json                 # Default widget configurations
│   └── ...                              # Other config-related Go files
├── schema/                               # JSON Schema definitions
│   ├── settings.json                     # Settings validation schema
│   └── ...                              # Other schema files
├── docs/docs/                           # User documentation
│   └── config.mdx                       # Configuration documentation
└── ~/.config/waveterm/                  # User config directory (runtime)
    ├── settings.json                    # User settings overrides
    ├── termthemes.json                  # User terminal themes
    ├── presets.json                     # User background presets
    ├── widgets.json                     # User widget configurations
    ├── bookmarks.json                   # Web bookmarks
    └── connections.json                 # SSH/remote connections

Key Files:

Configuration Architecture

Configuration Hierarchy

  1. Built-in Defaults (pkg/wconfig/defaultconfig/settings.json)
  2. User Settings (~/.config/waveterm/settings.json)
  3. Block-level Overrides (stored in block metadata)

Settings cascade from defaults → user settings → block overrides.

Block-Level Metadata Override System

Wave Terminal supports block-level configuration overrides through the metadata system. This allows settings to be applied globally, per-connection, or per-block:

  1. Global Settings (~/.config/waveterm/settings.json) - Apply to all blocks by default
  2. Connection Settings (in connections config) - Apply to all blocks using a specific connection
  3. Block Metadata - Override settings for individual blocks

Key Files for Block Overrides:

  • pkg/waveobj/wtypemeta.go - Defines the MetaTSType struct for block-level metadata
  • Block metadata fields should match the corresponding settings fields for consistency

Frontend Usage:

// Use getOverrideConfigAtom for hierarchical config resolution
const settingValue = useAtomValue(getOverrideConfigAtom(blockId, "namespace:setting"));

// This automatically resolves in order: block metadata → connection config → global settings → default

Setting Block Metadata:

# Set for current block
wsh setmeta namespace:setting=value

# Set for specific block
wsh setmeta --block BLOCK_ID namespace:setting=value

How to Add a New Configuration Value

Follow these steps to add a new configuration setting:

Step 1: Add to Go Struct Definition

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:

  • Use namespace prefixes (e.g., term:, window:, ai:, web:)
  • Use lowercase with colons as separators
  • Field names should be descriptive and follow Go naming conventions
  • Use omitempty tag to exclude empty values from JSON

Type Guidelines:

  • Use *int64 and *float64 for optional numeric values
  • Use *bool for optional boolean values
  • Use string for text values
  • Use []string for arrays
  • Use float64 for numbers that can be decimals

Step 1.5: Add to Block Metadata (Optional)

If 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:

  • Use pointer types (*string, *bool, *int, *float64) for optional overrides
  • JSON tags should exactly match the corresponding settings field
  • This enables the hierarchical config system: block metadata → connection config → global settings

Step 2: Add to JSON Schema

Edit schema/settings.json and add your field to the properties section:

{
  "$defs": {
    "SettingsType": {
      "properties": {
        // ... existing properties ...

        "mynew:setting": {
          "type": "string"
        },
        "mynew:boolsetting": {
          "type": "boolean"
        },
        "mynew:numbersetting": {
          "type": "number"
        },
        "mynew:intsetting": {
          "type": "integer"
        },
        "mynew:arraysetting": {
          "items": {
            "type": "string"
          },
          "type": "array"
        }
      }
    }
  }
}

Schema Type Mapping:

  • Go string → JSON Schema "string"
  • Go bool → JSON Schema "boolean"
  • Go float64 → JSON Schema "number"
  • Go int64 → JSON Schema "integer"
  • Go []string → JSON Schema "array" with "items": {"type": "string"}

Step 3: Set Default Value (Optional)

If 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:

  • Only add defaults for settings that should have non-zero/non-empty initial values
  • Ensure defaults make sense for the typical user experience
  • Keep defaults conservative and safe

Step 4: Update Documentation

Add 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      |

Also update the default configuration example in the same file if you added defaults.

Step 5: Regenerate Schema and TypeScript Types

Run the build tasks to regenerate schema and TypeScript types:

task build:schema
task generate

Or run them individually:

# Regenerate JSON schema
task build:schema

# Regenerate TypeScript types
go run cmd/generatets/main-generatets.go

This will update the schema files and frontend/types/gotypes.d.ts with your new settings.

Step 6: Use in Frontend Code

Access your new setting in React components:

import { getOverrideConfigAtom, 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)
const termFontSize = useAtomValue(getOverrideConfigAtom(blockId, "term:fontsize")) ?? 12;

// 2. Global-only settings
const appGlobalHotkey = useAtomValue(getSettingsKeyAtom("app:globalhotkey")) ?? "";

// 3. Connection-specific settings
const connStatus = useAtomValue(getConnStatusAtom(connectionName));

Step 7: Use in Backend Code

Access settings in Go code:

// Get the full config
fullConfig := wconfig.GetWatcher().GetFullConfig()

// Access your setting
myValue := fullConfig.Settings.MyNewSetting

Configuration Patterns

Namespace Organization

Settings are organized by namespace using colon separators:

  • app:* - Application-level settings
  • term:* - Terminal-specific settings
  • window:* - Window and UI settings
  • ai:* - AI-related settings
  • web:* - Web browser settings
  • editor:* - Code editor settings
  • conn:* - Connection settings

Clear/Reset Pattern

Each namespace can have a "clear" field for resetting all settings in that namespace:

AppClear  bool `json:"app:*,omitempty"`
TermClear bool `json:"term:*,omitempty"`

Optional vs Required Settings

  • Use pointer types (*bool, *int64, *float64) for truly optional settings
  • Use regular types for settings that should always have a value
  • Provide sensible defaults for important settings

Block-Level Overrides

Settings can be overridden at the block level using metadata:

// Set block-specific override
await RpcApi.SetMetaCommand(TabRpcClient, {
  oref: WOS.makeORef("block", blockId),
  meta: { "mynew:setting": "block-specific value" },
});

Example: Adding a New Terminal Setting

Let's walk through adding a new terminal setting term:bellsound with block-level override support:

1. Go Struct (settingsconfig.go)

type SettingsType struct {
    // ... existing fields ...
    TermBellSound string `json:"term:bellsound,omitempty"`
}

2. Block Metadata (wtypemeta.go)

type MetaTSType struct {
    // ... existing fields ...
    TermBellSound *string `json:"term:bellsound,omitempty"`  // Pointer for optional override
}

3. JSON Schema (schema/settings.json)

{
  "properties": {
    "term:bellsound": {
      "type": "string"
    }
  }
}

4. Default Value (defaultconfig/settings.json)

{
  "term:bellsound": "default"
}

5. Documentation (docs/config.mdx)

| term:bellsound | string | Sound to play for terminal bell ("default", "none", or custom sound file path) |

6. Frontend Usage

// Use override config for hierarchical resolution
const bellSoundAtom = getOverrideConfigAtom(blockId, "term:bellsound");
const bellSound = useAtomValue(bellSoundAtom) ?? "default";

7. Usage Examples

# Set globally
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"

Testing Your Configuration

  1. Build and run Wave Terminal with your changes
  2. Test default behavior - Ensure the default value works
  3. Test user override - Add your setting to ~/.config/waveterm/settings.json
  4. Test block override - Set block-specific metadata
  5. Verify schema validation - Ensure invalid values are rejected

Common Pitfalls