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