From dc01071498c4ca0bc9804e4d75ab5ac09d66df9e Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Fri, 15 Aug 2025 13:21:02 -0500 Subject: [PATCH] feat: add scroll speed to config (#1968) --- packages/opencode/src/config/config.ts | 5 + packages/sdk/go/.stats.yml | 4 +- packages/sdk/go/api.md | 2 +- packages/sdk/go/config.go | 127 +++++++++++------- packages/sdk/go/session.go | 13 +- packages/sdk/go/session_test.go | 7 +- packages/sdk/js/src/gen/types.gen.ts | 9 ++ packages/tui/internal/app/app.go | 4 +- packages/tui/internal/app/state.go | 1 - .../tui/internal/components/chat/messages.go | 6 +- 10 files changed, 116 insertions(+), 62 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 1b30bc6b..5f3edca7 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -274,6 +274,10 @@ export namespace Config { ref: "KeybindsConfig", }) + export const TUI = z.object({ + scroll_speed: z.number().min(1).optional().default(2).describe("TUI scroll speed"), + }) + export const Layout = z.enum(["auto", "stretch"]).openapi({ ref: "LayoutConfig", }) @@ -284,6 +288,7 @@ export namespace Config { $schema: z.string().optional().describe("JSON schema reference for configuration validation"), theme: z.string().optional().describe("Theme name to use for the interface"), keybinds: Keybinds.optional().describe("Custom keybind configurations"), + tui: TUI.optional().describe("TUI specific settings"), plugin: z.string().array().optional(), snapshot: z.boolean().optional(), share: z diff --git a/packages/sdk/go/.stats.yml b/packages/sdk/go/.stats.yml index 156ca97e..09dd20f4 100644 --- a/packages/sdk/go/.stats.yml +++ b/packages/sdk/go/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 39 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-b751e5c3cd3ab7fc61b18fbbfd6092b7264ea2ee305858aa538b884e7c492090.yml -openapi_spec_hash: a61f8b1d9b834cf321f0cb7805cc8522 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-e4b6496e5f2c68fa8b3ea1b88e40041eaf5ce2652001344df80bf130675d1766.yml +openapi_spec_hash: df474311dc9e4a89cd483bd8b8d971d8 config_hash: eab3723c4c2232a6ba1821151259d6da diff --git a/packages/sdk/go/api.md b/packages/sdk/go/api.md index 7be0d3a4..cf548532 100644 --- a/packages/sdk/go/api.md +++ b/packages/sdk/go/api.md @@ -110,7 +110,7 @@ Response Types: Methods: -- client.Session.New(ctx context.Context) (opencode.Session, error) +- client.Session.New(ctx context.Context, body opencode.SessionNewParams) (opencode.Session, error) - client.Session.Update(ctx context.Context, id string, body opencode.SessionUpdateParams) (opencode.Session, error) - client.Session.List(ctx context.Context) ([]opencode.Session, error) - client.Session.Delete(ctx context.Context, id string) (bool, error) diff --git a/packages/sdk/go/config.go b/packages/sdk/go/config.go index ee2cf719..e83bd57e 100644 --- a/packages/sdk/go/config.go +++ b/packages/sdk/go/config.go @@ -80,6 +80,8 @@ type Config struct { Snapshot bool `json:"snapshot"` // Theme name to use for the interface Theme string `json:"theme"` + // TUI specific settings + Tui ConfigTui `json:"tui"` // Custom username to display in conversations instead of system username Username string `json:"username"` JSON configJSON `json:"-"` @@ -108,6 +110,7 @@ type configJSON struct { SmallModel apijson.Field Snapshot apijson.Field Theme apijson.Field + Tui apijson.Field Username apijson.Field raw string ExtraFields map[string]apijson.Field @@ -1654,6 +1657,28 @@ func (r ConfigShare) IsKnown() bool { return false } +// TUI specific settings +type ConfigTui struct { + // TUI scroll speed + ScrollSpeed float64 `json:"scroll_speed,required"` + JSON configTuiJSON `json:"-"` +} + +// configTuiJSON contains the JSON metadata for the struct [ConfigTui] +type configTuiJSON struct { + ScrollSpeed apijson.Field + raw string + ExtraFields map[string]apijson.Field +} + +func (r *ConfigTui) UnmarshalJSON(data []byte) (err error) { + return apijson.UnmarshalRoot(data, r) +} + +func (r configTuiJSON) RawJSON() string { + return r.raw +} + type KeybindsConfig struct { // Next agent AgentCycle string `json:"agent_cycle,required"` @@ -1719,6 +1744,10 @@ type KeybindsConfig struct { ModelList string `json:"model_list,required"` // Create/update AGENTS.md ProjectInit string `json:"project_init,required"` + // Cycle to next child session + SessionChildCycle string `json:"session_child_cycle,required"` + // Cycle to previous child session + SessionChildCycleReverse string `json:"session_child_cycle_reverse,required"` // Compact the session SessionCompact string `json:"session_compact,required"` // Export session to editor @@ -1752,54 +1781,56 @@ type KeybindsConfig struct { // keybindsConfigJSON contains the JSON metadata for the struct [KeybindsConfig] type keybindsConfigJSON struct { - AgentCycle apijson.Field - AgentCycleReverse apijson.Field - AgentList apijson.Field - AppExit apijson.Field - AppHelp apijson.Field - EditorOpen apijson.Field - FileClose apijson.Field - FileDiffToggle apijson.Field - FileList apijson.Field - FileSearch apijson.Field - InputClear apijson.Field - InputNewline apijson.Field - InputPaste apijson.Field - InputSubmit apijson.Field - Leader apijson.Field - MessagesCopy apijson.Field - MessagesFirst apijson.Field - MessagesHalfPageDown apijson.Field - MessagesHalfPageUp apijson.Field - MessagesLast apijson.Field - MessagesLayoutToggle apijson.Field - MessagesNext apijson.Field - MessagesPageDown apijson.Field - MessagesPageUp apijson.Field - MessagesPrevious apijson.Field - MessagesRedo apijson.Field - MessagesRevert apijson.Field - MessagesUndo apijson.Field - ModelCycleRecent apijson.Field - ModelCycleRecentReverse apijson.Field - ModelList apijson.Field - ProjectInit apijson.Field - SessionCompact apijson.Field - SessionExport apijson.Field - SessionInterrupt apijson.Field - SessionList apijson.Field - SessionNew apijson.Field - SessionShare apijson.Field - SessionUnshare apijson.Field - SwitchAgent apijson.Field - SwitchAgentReverse apijson.Field - SwitchMode apijson.Field - SwitchModeReverse apijson.Field - ThemeList apijson.Field - ThinkingBlocks apijson.Field - ToolDetails apijson.Field - raw string - ExtraFields map[string]apijson.Field + AgentCycle apijson.Field + AgentCycleReverse apijson.Field + AgentList apijson.Field + AppExit apijson.Field + AppHelp apijson.Field + EditorOpen apijson.Field + FileClose apijson.Field + FileDiffToggle apijson.Field + FileList apijson.Field + FileSearch apijson.Field + InputClear apijson.Field + InputNewline apijson.Field + InputPaste apijson.Field + InputSubmit apijson.Field + Leader apijson.Field + MessagesCopy apijson.Field + MessagesFirst apijson.Field + MessagesHalfPageDown apijson.Field + MessagesHalfPageUp apijson.Field + MessagesLast apijson.Field + MessagesLayoutToggle apijson.Field + MessagesNext apijson.Field + MessagesPageDown apijson.Field + MessagesPageUp apijson.Field + MessagesPrevious apijson.Field + MessagesRedo apijson.Field + MessagesRevert apijson.Field + MessagesUndo apijson.Field + ModelCycleRecent apijson.Field + ModelCycleRecentReverse apijson.Field + ModelList apijson.Field + ProjectInit apijson.Field + SessionChildCycle apijson.Field + SessionChildCycleReverse apijson.Field + SessionCompact apijson.Field + SessionExport apijson.Field + SessionInterrupt apijson.Field + SessionList apijson.Field + SessionNew apijson.Field + SessionShare apijson.Field + SessionUnshare apijson.Field + SwitchAgent apijson.Field + SwitchAgentReverse apijson.Field + SwitchMode apijson.Field + SwitchModeReverse apijson.Field + ThemeList apijson.Field + ThinkingBlocks apijson.Field + ToolDetails apijson.Field + raw string + ExtraFields map[string]apijson.Field } func (r *KeybindsConfig) UnmarshalJSON(data []byte) (err error) { diff --git a/packages/sdk/go/session.go b/packages/sdk/go/session.go index 0ab9c7d9..9cc0492c 100644 --- a/packages/sdk/go/session.go +++ b/packages/sdk/go/session.go @@ -39,10 +39,10 @@ func NewSessionService(opts ...option.RequestOption) (r *SessionService) { } // Create a new session -func (r *SessionService) New(ctx context.Context, opts ...option.RequestOption) (res *Session, err error) { +func (r *SessionService) New(ctx context.Context, body SessionNewParams, opts ...option.RequestOption) (res *Session, err error) { opts = append(r.Options[:], opts...) path := "session" - err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, nil, &res, opts...) + err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) return } @@ -2324,6 +2324,15 @@ func (r sessionMessagesResponseJSON) RawJSON() string { return r.raw } +type SessionNewParams struct { + ParentID param.Field[string] `json:"parentID"` + Title param.Field[string] `json:"title"` +} + +func (r SessionNewParams) MarshalJSON() (data []byte, err error) { + return apijson.MarshalRoot(r) +} + type SessionUpdateParams struct { Title param.Field[string] `json:"title"` } diff --git a/packages/sdk/go/session_test.go b/packages/sdk/go/session_test.go index 2f5e1110..58e68dc1 100644 --- a/packages/sdk/go/session_test.go +++ b/packages/sdk/go/session_test.go @@ -13,7 +13,7 @@ import ( "github.com/sst/opencode-sdk-go/option" ) -func TestSessionNew(t *testing.T) { +func TestSessionNewWithOptionalParams(t *testing.T) { t.Skip("skipped: tests are disabled for the time being") baseURL := "http://localhost:4010" if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok { @@ -25,7 +25,10 @@ func TestSessionNew(t *testing.T) { client := opencode.NewClient( option.WithBaseURL(baseURL), ) - _, err := client.Session.New(context.TODO()) + _, err := client.Session.New(context.TODO(), opencode.SessionNewParams{ + ParentID: opencode.F("parentID"), + Title: opencode.F("title"), + }) if err != nil { var apierr *opencode.Error if errors.As(err, &apierr) { diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 645af010..79932b05 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -576,6 +576,15 @@ export type Config = { * Custom keybind configurations */ keybinds?: KeybindsConfig + /** + * TUI specific settings + */ + tui?: { + /** + * TUI scroll speed + */ + scroll_speed: number + } plugin?: Array snapshot?: boolean /** diff --git a/packages/tui/internal/app/app.go b/packages/tui/internal/app/app.go index eff00f29..f046daae 100644 --- a/packages/tui/internal/app/app.go +++ b/packages/tui/internal/app/app.go @@ -50,6 +50,7 @@ type App struct { compactCancel context.CancelFunc IsLeaderSequence bool IsBashMode bool + ScrollSpeed int } func (a *App) Agent() *opencode.Agent { @@ -198,6 +199,7 @@ func New( InitialPrompt: initialPrompt, InitialAgent: initialAgent, InitialSession: initialSession, + ScrollSpeed: int(configInfo.Tui.ScrollSpeed), } return app, nil @@ -725,7 +727,7 @@ func (a *App) MarkProjectInitialized(ctx context.Context) error { } func (a *App) CreateSession(ctx context.Context) (*opencode.Session, error) { - session, err := a.Client.Session.New(ctx) + session, err := a.Client.Session.New(ctx, opencode.SessionNewParams{}) if err != nil { return nil, err } diff --git a/packages/tui/internal/app/state.go b/packages/tui/internal/app/state.go index 2876eb3a..cc65eea5 100644 --- a/packages/tui/internal/app/state.go +++ b/packages/tui/internal/app/state.go @@ -28,7 +28,6 @@ type AgentModel struct { type State struct { Theme string `toml:"theme"` - ScrollSpeed *int `toml:"scroll_speed"` AgentModel map[string]AgentModel `toml:"agent_model"` Provider string `toml:"provider"` Model string `toml:"model"` diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go index 109a734a..0be2f462 100644 --- a/packages/tui/internal/components/chat/messages.go +++ b/packages/tui/internal/components/chat/messages.go @@ -1194,11 +1194,7 @@ func NewMessagesComponent(app *app.App) MessagesComponent { vp := viewport.New() vp.KeyMap = viewport.KeyMap{} - if app.State.ScrollSpeed != nil && *app.State.ScrollSpeed > 0 { - vp.MouseWheelDelta = *app.State.ScrollSpeed - } else { - vp.MouseWheelDelta = 2 - } + vp.MouseWheelDelta = app.ScrollSpeed // Default to showing tool details, hidden thinking blocks showToolDetails := true