fix: more commands cleanup

This commit is contained in:
adamdotdevin
2025-08-15 07:43:30 -05:00
parent c875d11959
commit 6e0e87fb2a
17 changed files with 383 additions and 1149 deletions

View File

@@ -127,6 +127,12 @@ export namespace Config {
if (result.keybinds?.switch_mode_reverse && !result.keybinds.switch_agent_reverse) { if (result.keybinds?.switch_mode_reverse && !result.keybinds.switch_agent_reverse) {
result.keybinds.switch_agent_reverse = result.keybinds.switch_mode_reverse result.keybinds.switch_agent_reverse = result.keybinds.switch_mode_reverse
} }
if (result.keybinds?.switch_agent && !result.keybinds.agent_cycle) {
result.keybinds.agent_cycle = result.keybinds.switch_agent
}
if (result.keybinds?.switch_agent_reverse && !result.keybinds.agent_cycle_reverse) {
result.keybinds.agent_cycle_reverse = result.keybinds.switch_agent_reverse
}
if (!result.username) { if (!result.username) {
const os = await import("os") const os = await import("os")
@@ -199,9 +205,12 @@ export namespace Config {
.object({ .object({
leader: z.string().optional().default("ctrl+x").describe("Leader key for keybind combinations"), leader: z.string().optional().default("ctrl+x").describe("Leader key for keybind combinations"),
app_help: z.string().optional().default("<leader>h").describe("Show help dialog"), app_help: z.string().optional().default("<leader>h").describe("Show help dialog"),
switch_agent: z.string().optional().default("tab").describe("Next agent"), app_exit: z.string().optional().default("ctrl+c,<leader>q").describe("Exit the application"),
switch_agent_reverse: z.string().optional().default("shift+tab").describe("Previous agent"),
editor_open: z.string().optional().default("<leader>e").describe("Open external editor"), editor_open: z.string().optional().default("<leader>e").describe("Open external editor"),
theme_list: z.string().optional().default("<leader>t").describe("List available themes"),
project_init: z.string().optional().default("<leader>i").describe("Create/update AGENTS.md"),
tool_details: z.string().optional().default("<leader>d").describe("Toggle tool details"),
thinking_blocks: z.string().optional().default("<leader>b").describe("Toggle thinking blocks"),
session_export: z.string().optional().default("<leader>x").describe("Export session to editor"), session_export: z.string().optional().default("<leader>x").describe("Export session to editor"),
session_new: z.string().optional().default("<leader>n").describe("Create a new session"), session_new: z.string().optional().default("<leader>n").describe("Create a new session"),
session_list: z.string().optional().default("<leader>l").describe("List all sessions"), session_list: z.string().optional().default("<leader>l").describe("List all sessions"),
@@ -209,18 +218,6 @@ export namespace Config {
session_unshare: z.string().optional().default("none").describe("Unshare current session"), session_unshare: z.string().optional().default("none").describe("Unshare current session"),
session_interrupt: z.string().optional().default("esc").describe("Interrupt current session"), session_interrupt: z.string().optional().default("esc").describe("Interrupt current session"),
session_compact: z.string().optional().default("<leader>c").describe("Compact the session"), session_compact: z.string().optional().default("<leader>c").describe("Compact the session"),
tool_details: z.string().optional().default("<leader>d").describe("Toggle tool details"),
thinking_blocks: z.string().optional().default("<leader>b").describe("Toggle thinking blocks"),
model_list: z.string().optional().default("<leader>m").describe("List available models"),
agent_list: z.string().optional().default("<leader>a").describe("List agents"),
model_cycle_recent: z.string().optional().default("f2").describe("Next recent model"),
model_cycle_recent_reverse: z.string().optional().default("shift+f2").describe("Previous recent model"),
theme_list: z.string().optional().default("<leader>t").describe("List available themes"),
project_init: z.string().optional().default("<leader>i").describe("Create/update AGENTS.md"),
input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"),
input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),
input_submit: z.string().optional().default("enter").describe("Submit input"),
input_newline: z.string().optional().default("shift+enter,ctrl+j").describe("Insert newline in input"),
messages_page_up: z.string().optional().default("pgup").describe("Scroll messages up by one page"), messages_page_up: z.string().optional().default("pgup").describe("Scroll messages up by one page"),
messages_page_down: z.string().optional().default("pgdown").describe("Scroll messages down by one page"), messages_page_down: z.string().optional().default("pgdown").describe("Scroll messages down by one page"),
messages_half_page_up: z.string().optional().default("ctrl+alt+u").describe("Scroll messages up by half page"), messages_half_page_up: z.string().optional().default("ctrl+alt+u").describe("Scroll messages up by half page"),
@@ -234,14 +231,29 @@ export namespace Config {
messages_copy: z.string().optional().default("<leader>y").describe("Copy message"), messages_copy: z.string().optional().default("<leader>y").describe("Copy message"),
messages_undo: z.string().optional().default("<leader>u").describe("Undo message"), messages_undo: z.string().optional().default("<leader>u").describe("Undo message"),
messages_redo: z.string().optional().default("<leader>r").describe("Redo message"), messages_redo: z.string().optional().default("<leader>r").describe("Redo message"),
app_exit: z.string().optional().default("ctrl+c,<leader>q").describe("Exit the application"), model_list: z.string().optional().default("<leader>m").describe("List available models"),
model_cycle_recent: z.string().optional().default("f2").describe("Next recent model"),
model_cycle_recent_reverse: z.string().optional().default("shift+f2").describe("Previous recent model"),
agent_list: z.string().optional().default("<leader>a").describe("List agents"),
agent_cycle: z.string().optional().default("tab").describe("Next agent"),
agent_cycle_reverse: z.string().optional().default("shift+tab").describe("Previous agent"),
input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"),
input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),
input_submit: z.string().optional().default("enter").describe("Submit input"),
input_newline: z.string().optional().default("shift+enter,ctrl+j").describe("Insert newline in input"),
// Deprecated commands // Deprecated commands
switch_mode: z.string().optional().default("none").describe("@deprecated use switch_agent. Next mode"), switch_mode: z.string().optional().default("none").describe("@deprecated use agent_cycle. Next mode"),
switch_mode_reverse: z switch_mode_reverse: z
.string() .string()
.optional() .optional()
.default("none") .default("none")
.describe("@deprecated use switch_agent_reverse. Previous mode"), .describe("@deprecated use agent_cycle_reverse. Previous mode"),
switch_agent: z.string().optional().default("tab").describe("@deprecated use agent_cycle. Next agent"),
switch_agent_reverse: z
.string()
.optional()
.default("shift+tab")
.describe("@deprecated use agent_cycle_reverse. Previous agent"),
file_list: z.string().optional().default("none").describe("@deprecated Currently not available. List files"), file_list: z.string().optional().default("none").describe("@deprecated Currently not available. List files"),
file_close: z.string().optional().default("none").describe("@deprecated Close file"), file_close: z.string().optional().default("none").describe("@deprecated Close file"),
file_search: z.string().optional().default("none").describe("@deprecated Search file"), file_search: z.string().optional().default("none").describe("@deprecated Search file"),

View File

@@ -1099,7 +1099,7 @@ export namespace Server {
.post( .post(
"/tui/execute-command", "/tui/execute-command",
describeRoute({ describeRoute({
description: "Execute a TUI command (e.g. switch_agent)", description: "Execute a TUI command (e.g. agent_cycle)",
operationId: "tui.executeCommand", operationId: "tui.executeCommand",
responses: { responses: {
200: { 200: {

View File

@@ -1,4 +1,4 @@
configured_endpoints: 36 configured_endpoints: 36
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-a881262c7de4ab59bdfbfc6e30a23c47dee465d7270ffb867b760b0103aff8ed.yml openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-d0eaf92deaa53a25bbbc3181540ad73ed5a4aec6381ac08d8122e24318e5e455.yml
openapi_spec_hash: 7dbb6f96f5c26a25c849e50298f58586 openapi_spec_hash: 22196d859c0711e564b9538d988abda6
config_hash: 8d85a768523cff92b85ef06c443d49fa config_hash: 8d85a768523cff92b85ef06c443d49fa

View File

@@ -1655,19 +1655,25 @@ func (r ConfigShare) IsKnown() bool {
} }
type KeybindsConfig struct { type KeybindsConfig struct {
// Next agent
AgentCycle string `json:"agent_cycle,required"`
// Previous agent
AgentCycleReverse string `json:"agent_cycle_reverse,required"`
// List agents
AgentList string `json:"agent_list,required"`
// Exit the application // Exit the application
AppExit string `json:"app_exit,required"` AppExit string `json:"app_exit,required"`
// Show help dialog // Show help dialog
AppHelp string `json:"app_help,required"` AppHelp string `json:"app_help,required"`
// Open external editor // Open external editor
EditorOpen string `json:"editor_open,required"` EditorOpen string `json:"editor_open,required"`
// Close file // @deprecated Close file
FileClose string `json:"file_close,required"` FileClose string `json:"file_close,required"`
// Split/unified diff // @deprecated Split/unified diff
FileDiffToggle string `json:"file_diff_toggle,required"` FileDiffToggle string `json:"file_diff_toggle,required"`
// List files // @deprecated Currently not available. List files
FileList string `json:"file_list,required"` FileList string `json:"file_list,required"`
// Search file // @deprecated Search file
FileSearch string `json:"file_search,required"` FileSearch string `json:"file_search,required"`
// Clear input field // Clear input field
InputClear string `json:"input_clear,required"` InputClear string `json:"input_clear,required"`
@@ -1689,15 +1695,15 @@ type KeybindsConfig struct {
MessagesHalfPageUp string `json:"messages_half_page_up,required"` MessagesHalfPageUp string `json:"messages_half_page_up,required"`
// Navigate to last message // Navigate to last message
MessagesLast string `json:"messages_last,required"` MessagesLast string `json:"messages_last,required"`
// Toggle layout // @deprecated Toggle layout
MessagesLayoutToggle string `json:"messages_layout_toggle,required"` MessagesLayoutToggle string `json:"messages_layout_toggle,required"`
// Navigate to next message // @deprecated Navigate to next message
MessagesNext string `json:"messages_next,required"` MessagesNext string `json:"messages_next,required"`
// Scroll messages down by one page // Scroll messages down by one page
MessagesPageDown string `json:"messages_page_down,required"` MessagesPageDown string `json:"messages_page_down,required"`
// Scroll messages up by one page // Scroll messages up by one page
MessagesPageUp string `json:"messages_page_up,required"` MessagesPageUp string `json:"messages_page_up,required"`
// Navigate to previous message // @deprecated Navigate to previous message
MessagesPrevious string `json:"messages_previous,required"` MessagesPrevious string `json:"messages_previous,required"`
// Redo message // Redo message
MessagesRedo string `json:"messages_redo,required"` MessagesRedo string `json:"messages_redo,required"`
@@ -1705,6 +1711,10 @@ type KeybindsConfig struct {
MessagesRevert string `json:"messages_revert,required"` MessagesRevert string `json:"messages_revert,required"`
// Undo message // Undo message
MessagesUndo string `json:"messages_undo,required"` MessagesUndo string `json:"messages_undo,required"`
// Next recent model
ModelCycleRecent string `json:"model_cycle_recent,required"`
// Previous recent model
ModelCycleRecentReverse string `json:"model_cycle_recent_reverse,required"`
// List available models // List available models
ModelList string `json:"model_list,required"` ModelList string `json:"model_list,required"`
// Create/update AGENTS.md // Create/update AGENTS.md
@@ -1723,13 +1733,13 @@ type KeybindsConfig struct {
SessionShare string `json:"session_share,required"` SessionShare string `json:"session_share,required"`
// Unshare current session // Unshare current session
SessionUnshare string `json:"session_unshare,required"` SessionUnshare string `json:"session_unshare,required"`
// Next agent // @deprecated use agent_cycle. Next agent
SwitchAgent string `json:"switch_agent,required"` SwitchAgent string `json:"switch_agent,required"`
// Previous agent // @deprecated use agent_cycle_reverse. Previous agent
SwitchAgentReverse string `json:"switch_agent_reverse,required"` SwitchAgentReverse string `json:"switch_agent_reverse,required"`
// @deprecated use switch_agent. Next mode // @deprecated use agent_cycle. Next mode
SwitchMode string `json:"switch_mode,required"` SwitchMode string `json:"switch_mode,required"`
// @deprecated use switch_agent_reverse. Previous mode // @deprecated use agent_cycle_reverse. Previous mode
SwitchModeReverse string `json:"switch_mode_reverse,required"` SwitchModeReverse string `json:"switch_mode_reverse,required"`
// List available themes // List available themes
ThemeList string `json:"theme_list,required"` ThemeList string `json:"theme_list,required"`
@@ -1742,6 +1752,9 @@ type KeybindsConfig struct {
// keybindsConfigJSON contains the JSON metadata for the struct [KeybindsConfig] // keybindsConfigJSON contains the JSON metadata for the struct [KeybindsConfig]
type keybindsConfigJSON struct { type keybindsConfigJSON struct {
AgentCycle apijson.Field
AgentCycleReverse apijson.Field
AgentList apijson.Field
AppExit apijson.Field AppExit apijson.Field
AppHelp apijson.Field AppHelp apijson.Field
EditorOpen apijson.Field EditorOpen apijson.Field
@@ -1767,6 +1780,8 @@ type keybindsConfigJSON struct {
MessagesRedo apijson.Field MessagesRedo apijson.Field
MessagesRevert apijson.Field MessagesRevert apijson.Field
MessagesUndo apijson.Field MessagesUndo apijson.Field
ModelCycleRecent apijson.Field
ModelCycleRecentReverse apijson.Field
ModelList apijson.Field ModelList apijson.Field
ProjectInit apijson.Field ProjectInit apijson.Field
SessionCompact apijson.Field SessionCompact apijson.Field

View File

@@ -54,13 +54,13 @@ type EventListResponse struct {
// [EventListResponseEventMessageRemovedProperties], // [EventListResponseEventMessageRemovedProperties],
// [EventListResponseEventMessagePartUpdatedProperties], // [EventListResponseEventMessagePartUpdatedProperties],
// [EventListResponseEventMessagePartRemovedProperties], // [EventListResponseEventMessagePartRemovedProperties],
// [EventListResponseEventStorageWriteProperties], // [EventListResponseEventStorageWriteProperties], [Permission],
// [EventListResponseEventFileEditedProperties], [interface{}], [Permission],
// [EventListResponseEventPermissionRepliedProperties], // [EventListResponseEventPermissionRepliedProperties],
// [EventListResponseEventFileEditedProperties],
// [EventListResponseEventSessionUpdatedProperties], // [EventListResponseEventSessionUpdatedProperties],
// [EventListResponseEventSessionDeletedProperties], // [EventListResponseEventSessionDeletedProperties],
// [EventListResponseEventSessionIdleProperties], // [EventListResponseEventSessionIdleProperties],
// [EventListResponseEventSessionErrorProperties], // [EventListResponseEventSessionErrorProperties], [interface{}],
// [EventListResponseEventFileWatcherUpdatedProperties], // [EventListResponseEventFileWatcherUpdatedProperties],
// [EventListResponseEventIdeInstalledProperties]. // [EventListResponseEventIdeInstalledProperties].
Properties interface{} `json:"properties,required"` Properties interface{} `json:"properties,required"`
@@ -100,12 +100,11 @@ func (r *EventListResponse) UnmarshalJSON(data []byte) (err error) {
// [EventListResponseEventMessageUpdated], [EventListResponseEventMessageRemoved], // [EventListResponseEventMessageUpdated], [EventListResponseEventMessageRemoved],
// [EventListResponseEventMessagePartUpdated], // [EventListResponseEventMessagePartUpdated],
// [EventListResponseEventMessagePartRemoved], // [EventListResponseEventMessagePartRemoved],
// [EventListResponseEventStorageWrite], [EventListResponseEventFileEdited], // [EventListResponseEventStorageWrite], [EventListResponseEventPermissionUpdated],
// [EventListResponseEventServerConnected], // [EventListResponseEventPermissionReplied], [EventListResponseEventFileEdited],
// [EventListResponseEventPermissionUpdated],
// [EventListResponseEventPermissionReplied],
// [EventListResponseEventSessionUpdated], [EventListResponseEventSessionDeleted], // [EventListResponseEventSessionUpdated], [EventListResponseEventSessionDeleted],
// [EventListResponseEventSessionIdle], [EventListResponseEventSessionError], // [EventListResponseEventSessionIdle], [EventListResponseEventSessionError],
// [EventListResponseEventServerConnected],
// [EventListResponseEventFileWatcherUpdated], // [EventListResponseEventFileWatcherUpdated],
// [EventListResponseEventIdeInstalled]. // [EventListResponseEventIdeInstalled].
func (r EventListResponse) AsUnion() EventListResponseUnion { func (r EventListResponse) AsUnion() EventListResponseUnion {
@@ -117,12 +116,11 @@ func (r EventListResponse) AsUnion() EventListResponseUnion {
// [EventListResponseEventMessageUpdated], [EventListResponseEventMessageRemoved], // [EventListResponseEventMessageUpdated], [EventListResponseEventMessageRemoved],
// [EventListResponseEventMessagePartUpdated], // [EventListResponseEventMessagePartUpdated],
// [EventListResponseEventMessagePartRemoved], // [EventListResponseEventMessagePartRemoved],
// [EventListResponseEventStorageWrite], [EventListResponseEventFileEdited], // [EventListResponseEventStorageWrite], [EventListResponseEventPermissionUpdated],
// [EventListResponseEventServerConnected], // [EventListResponseEventPermissionReplied], [EventListResponseEventFileEdited],
// [EventListResponseEventPermissionUpdated],
// [EventListResponseEventPermissionReplied],
// [EventListResponseEventSessionUpdated], [EventListResponseEventSessionDeleted], // [EventListResponseEventSessionUpdated], [EventListResponseEventSessionDeleted],
// [EventListResponseEventSessionIdle], [EventListResponseEventSessionError], // [EventListResponseEventSessionIdle], [EventListResponseEventSessionError],
// [EventListResponseEventServerConnected],
// [EventListResponseEventFileWatcherUpdated] or // [EventListResponseEventFileWatcherUpdated] or
// [EventListResponseEventIdeInstalled]. // [EventListResponseEventIdeInstalled].
type EventListResponseUnion interface { type EventListResponseUnion interface {
@@ -168,16 +166,6 @@ func init() {
Type: reflect.TypeOf(EventListResponseEventStorageWrite{}), Type: reflect.TypeOf(EventListResponseEventStorageWrite{}),
DiscriminatorValue: "storage.write", DiscriminatorValue: "storage.write",
}, },
apijson.UnionVariant{
TypeFilter: gjson.JSON,
Type: reflect.TypeOf(EventListResponseEventFileEdited{}),
DiscriminatorValue: "file.edited",
},
apijson.UnionVariant{
TypeFilter: gjson.JSON,
Type: reflect.TypeOf(EventListResponseEventServerConnected{}),
DiscriminatorValue: "server.connected",
},
apijson.UnionVariant{ apijson.UnionVariant{
TypeFilter: gjson.JSON, TypeFilter: gjson.JSON,
Type: reflect.TypeOf(EventListResponseEventPermissionUpdated{}), Type: reflect.TypeOf(EventListResponseEventPermissionUpdated{}),
@@ -188,6 +176,11 @@ func init() {
Type: reflect.TypeOf(EventListResponseEventPermissionReplied{}), Type: reflect.TypeOf(EventListResponseEventPermissionReplied{}),
DiscriminatorValue: "permission.replied", DiscriminatorValue: "permission.replied",
}, },
apijson.UnionVariant{
TypeFilter: gjson.JSON,
Type: reflect.TypeOf(EventListResponseEventFileEdited{}),
DiscriminatorValue: "file.edited",
},
apijson.UnionVariant{ apijson.UnionVariant{
TypeFilter: gjson.JSON, TypeFilter: gjson.JSON,
Type: reflect.TypeOf(EventListResponseEventSessionUpdated{}), Type: reflect.TypeOf(EventListResponseEventSessionUpdated{}),
@@ -208,6 +201,11 @@ func init() {
Type: reflect.TypeOf(EventListResponseEventSessionError{}), Type: reflect.TypeOf(EventListResponseEventSessionError{}),
DiscriminatorValue: "session.error", DiscriminatorValue: "session.error",
}, },
apijson.UnionVariant{
TypeFilter: gjson.JSON,
Type: reflect.TypeOf(EventListResponseEventServerConnected{}),
DiscriminatorValue: "server.connected",
},
apijson.UnionVariant{ apijson.UnionVariant{
TypeFilter: gjson.JSON, TypeFilter: gjson.JSON,
Type: reflect.TypeOf(EventListResponseEventFileWatcherUpdated{}), Type: reflect.TypeOf(EventListResponseEventFileWatcherUpdated{}),
@@ -651,105 +649,6 @@ func (r EventListResponseEventStorageWriteType) IsKnown() bool {
return false return false
} }
type EventListResponseEventFileEdited struct {
Properties EventListResponseEventFileEditedProperties `json:"properties,required"`
Type EventListResponseEventFileEditedType `json:"type,required"`
JSON eventListResponseEventFileEditedJSON `json:"-"`
}
// eventListResponseEventFileEditedJSON contains the JSON metadata for the struct
// [EventListResponseEventFileEdited]
type eventListResponseEventFileEditedJSON struct {
Properties apijson.Field
Type apijson.Field
raw string
ExtraFields map[string]apijson.Field
}
func (r *EventListResponseEventFileEdited) UnmarshalJSON(data []byte) (err error) {
return apijson.UnmarshalRoot(data, r)
}
func (r eventListResponseEventFileEditedJSON) RawJSON() string {
return r.raw
}
func (r EventListResponseEventFileEdited) implementsEventListResponse() {}
type EventListResponseEventFileEditedProperties struct {
File string `json:"file,required"`
JSON eventListResponseEventFileEditedPropertiesJSON `json:"-"`
}
// eventListResponseEventFileEditedPropertiesJSON contains the JSON metadata for
// the struct [EventListResponseEventFileEditedProperties]
type eventListResponseEventFileEditedPropertiesJSON struct {
File apijson.Field
raw string
ExtraFields map[string]apijson.Field
}
func (r *EventListResponseEventFileEditedProperties) UnmarshalJSON(data []byte) (err error) {
return apijson.UnmarshalRoot(data, r)
}
func (r eventListResponseEventFileEditedPropertiesJSON) RawJSON() string {
return r.raw
}
type EventListResponseEventFileEditedType string
const (
EventListResponseEventFileEditedTypeFileEdited EventListResponseEventFileEditedType = "file.edited"
)
func (r EventListResponseEventFileEditedType) IsKnown() bool {
switch r {
case EventListResponseEventFileEditedTypeFileEdited:
return true
}
return false
}
type EventListResponseEventServerConnected struct {
Properties interface{} `json:"properties,required"`
Type EventListResponseEventServerConnectedType `json:"type,required"`
JSON eventListResponseEventServerConnectedJSON `json:"-"`
}
// eventListResponseEventServerConnectedJSON contains the JSON metadata for the
// struct [EventListResponseEventServerConnected]
type eventListResponseEventServerConnectedJSON struct {
Properties apijson.Field
Type apijson.Field
raw string
ExtraFields map[string]apijson.Field
}
func (r *EventListResponseEventServerConnected) UnmarshalJSON(data []byte) (err error) {
return apijson.UnmarshalRoot(data, r)
}
func (r eventListResponseEventServerConnectedJSON) RawJSON() string {
return r.raw
}
func (r EventListResponseEventServerConnected) implementsEventListResponse() {}
type EventListResponseEventServerConnectedType string
const (
EventListResponseEventServerConnectedTypeServerConnected EventListResponseEventServerConnectedType = "server.connected"
)
func (r EventListResponseEventServerConnectedType) IsKnown() bool {
switch r {
case EventListResponseEventServerConnectedTypeServerConnected:
return true
}
return false
}
type EventListResponseEventPermissionUpdated struct { type EventListResponseEventPermissionUpdated struct {
Properties Permission `json:"properties,required"` Properties Permission `json:"properties,required"`
Type EventListResponseEventPermissionUpdatedType `json:"type,required"` Type EventListResponseEventPermissionUpdatedType `json:"type,required"`
@@ -853,6 +752,66 @@ func (r EventListResponseEventPermissionRepliedType) IsKnown() bool {
return false return false
} }
type EventListResponseEventFileEdited struct {
Properties EventListResponseEventFileEditedProperties `json:"properties,required"`
Type EventListResponseEventFileEditedType `json:"type,required"`
JSON eventListResponseEventFileEditedJSON `json:"-"`
}
// eventListResponseEventFileEditedJSON contains the JSON metadata for the struct
// [EventListResponseEventFileEdited]
type eventListResponseEventFileEditedJSON struct {
Properties apijson.Field
Type apijson.Field
raw string
ExtraFields map[string]apijson.Field
}
func (r *EventListResponseEventFileEdited) UnmarshalJSON(data []byte) (err error) {
return apijson.UnmarshalRoot(data, r)
}
func (r eventListResponseEventFileEditedJSON) RawJSON() string {
return r.raw
}
func (r EventListResponseEventFileEdited) implementsEventListResponse() {}
type EventListResponseEventFileEditedProperties struct {
File string `json:"file,required"`
JSON eventListResponseEventFileEditedPropertiesJSON `json:"-"`
}
// eventListResponseEventFileEditedPropertiesJSON contains the JSON metadata for
// the struct [EventListResponseEventFileEditedProperties]
type eventListResponseEventFileEditedPropertiesJSON struct {
File apijson.Field
raw string
ExtraFields map[string]apijson.Field
}
func (r *EventListResponseEventFileEditedProperties) UnmarshalJSON(data []byte) (err error) {
return apijson.UnmarshalRoot(data, r)
}
func (r eventListResponseEventFileEditedPropertiesJSON) RawJSON() string {
return r.raw
}
type EventListResponseEventFileEditedType string
const (
EventListResponseEventFileEditedTypeFileEdited EventListResponseEventFileEditedType = "file.edited"
)
func (r EventListResponseEventFileEditedType) IsKnown() bool {
switch r {
case EventListResponseEventFileEditedTypeFileEdited:
return true
}
return false
}
type EventListResponseEventSessionUpdated struct { type EventListResponseEventSessionUpdated struct {
Properties EventListResponseEventSessionUpdatedProperties `json:"properties,required"` Properties EventListResponseEventSessionUpdatedProperties `json:"properties,required"`
Type EventListResponseEventSessionUpdatedType `json:"type,required"` Type EventListResponseEventSessionUpdatedType `json:"type,required"`
@@ -1229,6 +1188,45 @@ func (r EventListResponseEventSessionErrorType) IsKnown() bool {
return false return false
} }
type EventListResponseEventServerConnected struct {
Properties interface{} `json:"properties,required"`
Type EventListResponseEventServerConnectedType `json:"type,required"`
JSON eventListResponseEventServerConnectedJSON `json:"-"`
}
// eventListResponseEventServerConnectedJSON contains the JSON metadata for the
// struct [EventListResponseEventServerConnected]
type eventListResponseEventServerConnectedJSON struct {
Properties apijson.Field
Type apijson.Field
raw string
ExtraFields map[string]apijson.Field
}
func (r *EventListResponseEventServerConnected) UnmarshalJSON(data []byte) (err error) {
return apijson.UnmarshalRoot(data, r)
}
func (r eventListResponseEventServerConnectedJSON) RawJSON() string {
return r.raw
}
func (r EventListResponseEventServerConnected) implementsEventListResponse() {}
type EventListResponseEventServerConnectedType string
const (
EventListResponseEventServerConnectedTypeServerConnected EventListResponseEventServerConnectedType = "server.connected"
)
func (r EventListResponseEventServerConnectedType) IsKnown() bool {
switch r {
case EventListResponseEventServerConnectedTypeServerConnected:
return true
}
return false
}
type EventListResponseEventFileWatcherUpdated struct { type EventListResponseEventFileWatcherUpdated struct {
Properties EventListResponseEventFileWatcherUpdatedProperties `json:"properties,required"` Properties EventListResponseEventFileWatcherUpdatedProperties `json:"properties,required"`
Type EventListResponseEventFileWatcherUpdatedType `json:"type,required"` Type EventListResponseEventFileWatcherUpdatedType `json:"type,required"`
@@ -1376,21 +1374,21 @@ const (
EventListResponseTypeMessagePartUpdated EventListResponseType = "message.part.updated" EventListResponseTypeMessagePartUpdated EventListResponseType = "message.part.updated"
EventListResponseTypeMessagePartRemoved EventListResponseType = "message.part.removed" EventListResponseTypeMessagePartRemoved EventListResponseType = "message.part.removed"
EventListResponseTypeStorageWrite EventListResponseType = "storage.write" EventListResponseTypeStorageWrite EventListResponseType = "storage.write"
EventListResponseTypeFileEdited EventListResponseType = "file.edited"
EventListResponseTypeServerConnected EventListResponseType = "server.connected"
EventListResponseTypePermissionUpdated EventListResponseType = "permission.updated" EventListResponseTypePermissionUpdated EventListResponseType = "permission.updated"
EventListResponseTypePermissionReplied EventListResponseType = "permission.replied" EventListResponseTypePermissionReplied EventListResponseType = "permission.replied"
EventListResponseTypeFileEdited EventListResponseType = "file.edited"
EventListResponseTypeSessionUpdated EventListResponseType = "session.updated" EventListResponseTypeSessionUpdated EventListResponseType = "session.updated"
EventListResponseTypeSessionDeleted EventListResponseType = "session.deleted" EventListResponseTypeSessionDeleted EventListResponseType = "session.deleted"
EventListResponseTypeSessionIdle EventListResponseType = "session.idle" EventListResponseTypeSessionIdle EventListResponseType = "session.idle"
EventListResponseTypeSessionError EventListResponseType = "session.error" EventListResponseTypeSessionError EventListResponseType = "session.error"
EventListResponseTypeServerConnected EventListResponseType = "server.connected"
EventListResponseTypeFileWatcherUpdated EventListResponseType = "file.watcher.updated" EventListResponseTypeFileWatcherUpdated EventListResponseType = "file.watcher.updated"
EventListResponseTypeIdeInstalled EventListResponseType = "ide.installed" EventListResponseTypeIdeInstalled EventListResponseType = "ide.installed"
) )
func (r EventListResponseType) IsKnown() bool { func (r EventListResponseType) IsKnown() bool {
switch r { switch r {
case EventListResponseTypeInstallationUpdated, EventListResponseTypeLspClientDiagnostics, EventListResponseTypeMessageUpdated, EventListResponseTypeMessageRemoved, EventListResponseTypeMessagePartUpdated, EventListResponseTypeMessagePartRemoved, EventListResponseTypeStorageWrite, EventListResponseTypeFileEdited, EventListResponseTypeServerConnected, EventListResponseTypePermissionUpdated, EventListResponseTypePermissionReplied, EventListResponseTypeSessionUpdated, EventListResponseTypeSessionDeleted, EventListResponseTypeSessionIdle, EventListResponseTypeSessionError, EventListResponseTypeFileWatcherUpdated, EventListResponseTypeIdeInstalled: case EventListResponseTypeInstallationUpdated, EventListResponseTypeLspClientDiagnostics, EventListResponseTypeMessageUpdated, EventListResponseTypeMessageRemoved, EventListResponseTypeMessagePartUpdated, EventListResponseTypeMessagePartRemoved, EventListResponseTypeStorageWrite, EventListResponseTypePermissionUpdated, EventListResponseTypePermissionReplied, EventListResponseTypeFileEdited, EventListResponseTypeSessionUpdated, EventListResponseTypeSessionDeleted, EventListResponseTypeSessionIdle, EventListResponseTypeSessionError, EventListResponseTypeServerConnected, EventListResponseTypeFileWatcherUpdated, EventListResponseTypeIdeInstalled:
return true return true
} }
return false return false

View File

@@ -2025,9 +2025,9 @@ func (r toolStateCompletedTimeJSON) RawJSON() string {
type ToolStateError struct { type ToolStateError struct {
Error string `json:"error,required"` Error string `json:"error,required"`
Input map[string]interface{} `json:"input,required"` Input map[string]interface{} `json:"input,required"`
Metadata map[string]interface{} `json:"metadata"`
Status ToolStateErrorStatus `json:"status,required"` Status ToolStateErrorStatus `json:"status,required"`
Time ToolStateErrorTime `json:"time,required"` Time ToolStateErrorTime `json:"time,required"`
Metadata map[string]interface{} `json:"metadata"`
JSON toolStateErrorJSON `json:"-"` JSON toolStateErrorJSON `json:"-"`
} }
@@ -2035,9 +2035,9 @@ type ToolStateError struct {
type toolStateErrorJSON struct { type toolStateErrorJSON struct {
Error apijson.Field Error apijson.Field
Input apijson.Field Input apijson.Field
Metadata apijson.Field
Status apijson.Field Status apijson.Field
Time apijson.Field Time apijson.Field
Metadata apijson.Field
raw string raw string
ExtraFields map[string]apijson.Field ExtraFields map[string]apijson.Field
} }

View File

@@ -47,7 +47,7 @@ func (r *TuiService) ClearPrompt(ctx context.Context, opts ...option.RequestOpti
return return
} }
// Execute a TUI command (e.g. switch_agent) // Execute a TUI command (e.g. agent_cycle)
func (r *TuiService) ExecuteCommand(ctx context.Context, body TuiExecuteCommandParams, opts ...option.RequestOption) (res *bool, err error) { func (r *TuiService) ExecuteCommand(ctx context.Context, body TuiExecuteCommandParams, opts ...option.RequestOption) (res *bool, err error) {
opts = append(r.Options[:], opts...) opts = append(r.Options[:], opts...)
path := "tui/execute-command" path := "tui/execute-command"

View File

@@ -506,7 +506,7 @@ class Tui extends _HeyApiClient {
} }
/** /**
* Execute a TUI command (e.g. switch_agent) * Execute a TUI command (e.g. agent_cycle)
*/ */
public executeCommand<ThrowOnError extends boolean = false>(options?: Options<TuiExecuteCommandData, ThrowOnError>) { public executeCommand<ThrowOnError extends boolean = false>(options?: Options<TuiExecuteCommandData, ThrowOnError>) {
return (options?.client ?? this._client).post<TuiExecuteCommandResponses, unknown, ThrowOnError>({ return (options?.client ?? this._client).post<TuiExecuteCommandResponses, unknown, ThrowOnError>({

View File

@@ -347,10 +347,10 @@ export type ToolStateError = {
input: { input: {
[key: string]: unknown [key: string]: unknown
} }
metadata: { error: string
metadata?: {
[key: string]: unknown [key: string]: unknown
} }
error: string
time: { time: {
start: number start: number
end: number end: number
@@ -750,25 +750,29 @@ export type KeybindsConfig = {
*/ */
app_help: string app_help: string
/** /**
* @deprecated use switch_agent. Next mode * Exit the application
*/ */
switch_mode: string app_exit: string
/**
* @deprecated use switch_agent_reverse. Previous mode
*/
switch_mode_reverse: string
/**
* Next agent
*/
switch_agent: string
/**
* Previous agent
*/
switch_agent_reverse: string
/** /**
* Open external editor * Open external editor
*/ */
editor_open: string editor_open: string
/**
* List available themes
*/
theme_list: string
/**
* Create/update AGENTS.md
*/
project_init: string
/**
* Toggle tool details
*/
tool_details: string
/**
* Toggle thinking blocks
*/
thinking_blocks: string
/** /**
* Export session to editor * Export session to editor
*/ */
@@ -798,41 +802,65 @@ export type KeybindsConfig = {
*/ */
session_compact: string session_compact: string
/** /**
* Toggle tool details * Scroll messages up by one page
*/ */
tool_details: string messages_page_up: string
/** /**
* Toggle thinking blocks * Scroll messages down by one page
*/ */
thinking_blocks: string messages_page_down: string
/**
* Scroll messages up by half page
*/
messages_half_page_up: string
/**
* Scroll messages down by half page
*/
messages_half_page_down: string
/**
* Navigate to first message
*/
messages_first: string
/**
* Navigate to last message
*/
messages_last: string
/**
* Copy message
*/
messages_copy: string
/**
* Undo message
*/
messages_undo: string
/**
* Redo message
*/
messages_redo: string
/** /**
* List available models * List available models
*/ */
model_list: string model_list: string
/** /**
* List available themes * Next recent model
*/ */
theme_list: string model_cycle_recent: string
/** /**
* List files * Previous recent model
*/ */
file_list: string model_cycle_recent_reverse: string
/** /**
* Close file * List agents
*/ */
file_close: string agent_list: string
/** /**
* Search file * Next agent
*/ */
file_search: string agent_cycle: string
/** /**
* Split/unified diff * Previous agent
*/ */
file_diff_toggle: string agent_cycle_reverse: string
/**
* Create/update AGENTS.md
*/
project_init: string
/** /**
* Clear input field * Clear input field
*/ */
@@ -850,61 +878,53 @@ export type KeybindsConfig = {
*/ */
input_newline: string input_newline: string
/** /**
* Scroll messages up by one page * @deprecated use agent_cycle. Next mode
*/ */
messages_page_up: string switch_mode: string
/** /**
* Scroll messages down by one page * @deprecated use agent_cycle_reverse. Previous mode
*/ */
messages_page_down: string switch_mode_reverse: string
/** /**
* Scroll messages up by half page * @deprecated use agent_cycle. Next agent
*/ */
messages_half_page_up: string switch_agent: string
/** /**
* Scroll messages down by half page * @deprecated use agent_cycle_reverse. Previous agent
*/ */
messages_half_page_down: string switch_agent_reverse: string
/** /**
* Navigate to previous message * @deprecated Currently not available. List files
*/
file_list: string
/**
* @deprecated Close file
*/
file_close: string
/**
* @deprecated Search file
*/
file_search: string
/**
* @deprecated Split/unified diff
*/
file_diff_toggle: string
/**
* @deprecated Navigate to previous message
*/ */
messages_previous: string messages_previous: string
/** /**
* Navigate to next message * @deprecated Navigate to next message
*/ */
messages_next: string messages_next: string
/** /**
* Navigate to first message * @deprecated Toggle layout
*/
messages_first: string
/**
* Navigate to last message
*/
messages_last: string
/**
* Toggle layout
*/ */
messages_layout_toggle: string messages_layout_toggle: string
/**
* Copy message
*/
messages_copy: string
/** /**
* @deprecated use messages_undo. Revert message * @deprecated use messages_undo. Revert message
*/ */
messages_revert: string messages_revert: string
/**
* Undo message
*/
messages_undo: string
/**
* Redo message
*/
messages_redo: string
/**
* Exit the application
*/
app_exit: string
} }
export type AgentConfig = { export type AgentConfig = {
@@ -1086,6 +1106,7 @@ export type Agent = {
name: string name: string
description?: string description?: string
mode: "subagent" | "primary" | "all" mode: "subagent" | "primary" | "all"
builtIn: boolean
topP?: number topP?: number
temperature?: number temperature?: number
permission: { permission: {

View File

@@ -35,8 +35,6 @@ type State struct {
Agent string `toml:"agent"` Agent string `toml:"agent"`
RecentlyUsedModels []ModelUsage `toml:"recently_used_models"` RecentlyUsedModels []ModelUsage `toml:"recently_used_models"`
RecentlyUsedAgents []AgentUsage `toml:"recently_used_agents"` RecentlyUsedAgents []AgentUsage `toml:"recently_used_agents"`
MessagesRight bool `toml:"messages_right"`
SplitDiff bool `toml:"split_diff"`
MessageHistory []Prompt `toml:"message_history"` MessageHistory []Prompt `toml:"message_history"`
ShowToolDetails *bool `toml:"show_tool_details"` ShowToolDetails *bool `toml:"show_tool_details"`
ShowThinkingBlocks *bool `toml:"show_thinking_blocks"` ShowThinkingBlocks *bool `toml:"show_thinking_blocks"`

View File

@@ -108,9 +108,12 @@ func (r CommandRegistry) Matches(msg tea.KeyPressMsg, leader bool) []Command {
const ( const (
AppHelpCommand CommandName = "app_help" AppHelpCommand CommandName = "app_help"
SwitchAgentCommand CommandName = "switch_agent" AppExitCommand CommandName = "app_exit"
SwitchAgentReverseCommand CommandName = "switch_agent_reverse" ThemeListCommand CommandName = "theme_list"
ProjectInitCommand CommandName = "project_init"
EditorOpenCommand CommandName = "editor_open" EditorOpenCommand CommandName = "editor_open"
ToolDetailsCommand CommandName = "tool_details"
ThinkingBlocksCommand CommandName = "thinking_blocks"
SessionNewCommand CommandName = "session_new" SessionNewCommand CommandName = "session_new"
SessionListCommand CommandName = "session_list" SessionListCommand CommandName = "session_list"
SessionShareCommand CommandName = "session_share" SessionShareCommand CommandName = "session_share"
@@ -118,34 +121,25 @@ const (
SessionInterruptCommand CommandName = "session_interrupt" SessionInterruptCommand CommandName = "session_interrupt"
SessionCompactCommand CommandName = "session_compact" SessionCompactCommand CommandName = "session_compact"
SessionExportCommand CommandName = "session_export" SessionExportCommand CommandName = "session_export"
ToolDetailsCommand CommandName = "tool_details"
ThinkingBlocksCommand CommandName = "thinking_blocks"
ModelListCommand CommandName = "model_list"
AgentListCommand CommandName = "agent_list"
ModelCycleRecentCommand CommandName = "model_cycle_recent"
ModelCycleRecentReverseCommand CommandName = "model_cycle_recent_reverse"
ThemeListCommand CommandName = "theme_list"
FileListCommand CommandName = "file_list"
FileCloseCommand CommandName = "file_close"
FileSearchCommand CommandName = "file_search"
FileDiffToggleCommand CommandName = "file_diff_toggle"
ProjectInitCommand CommandName = "project_init"
InputClearCommand CommandName = "input_clear"
InputPasteCommand CommandName = "input_paste"
InputSubmitCommand CommandName = "input_submit"
InputNewlineCommand CommandName = "input_newline"
MessagesPageUpCommand CommandName = "messages_page_up" MessagesPageUpCommand CommandName = "messages_page_up"
MessagesPageDownCommand CommandName = "messages_page_down" MessagesPageDownCommand CommandName = "messages_page_down"
MessagesHalfPageUpCommand CommandName = "messages_half_page_up" MessagesHalfPageUpCommand CommandName = "messages_half_page_up"
MessagesHalfPageDownCommand CommandName = "messages_half_page_down" MessagesHalfPageDownCommand CommandName = "messages_half_page_down"
MessagesFirstCommand CommandName = "messages_first" MessagesFirstCommand CommandName = "messages_first"
MessagesLastCommand CommandName = "messages_last" MessagesLastCommand CommandName = "messages_last"
MessagesCopyCommand CommandName = "messages_copy" MessagesCopyCommand CommandName = "messages_copy"
MessagesUndoCommand CommandName = "messages_undo" MessagesUndoCommand CommandName = "messages_undo"
MessagesRedoCommand CommandName = "messages_redo" MessagesRedoCommand CommandName = "messages_redo"
AppExitCommand CommandName = "app_exit" ModelListCommand CommandName = "model_list"
ModelCycleRecentCommand CommandName = "model_cycle_recent"
ModelCycleRecentReverseCommand CommandName = "model_cycle_recent_reverse"
AgentListCommand CommandName = "agent_list"
AgentCycleCommand CommandName = "agent_cycle"
AgentCycleReverseCommand CommandName = "agent_cycle_reverse"
InputClearCommand CommandName = "input_clear"
InputPasteCommand CommandName = "input_paste"
InputSubmitCommand CommandName = "input_submit"
InputNewlineCommand CommandName = "input_newline"
) )
func (k Command) Matches(msg tea.KeyPressMsg, leader bool) bool { func (k Command) Matches(msg tea.KeyPressMsg, leader bool) bool {
@@ -184,16 +178,6 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry {
Keybindings: parseBindings("<leader>h"), Keybindings: parseBindings("<leader>h"),
Trigger: []string{"help"}, Trigger: []string{"help"},
}, },
{
Name: SwitchAgentCommand,
Description: "next agent",
Keybindings: parseBindings("tab"),
},
{
Name: SwitchAgentReverseCommand,
Description: "previous agent",
Keybindings: parseBindings("shift+tab"),
},
{ {
Name: EditorOpenCommand, Name: EditorOpenCommand,
Description: "open editor", Description: "open editor",
@@ -258,12 +242,6 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry {
Keybindings: parseBindings("<leader>m"), Keybindings: parseBindings("<leader>m"),
Trigger: []string{"models"}, Trigger: []string{"models"},
}, },
{
Name: AgentListCommand,
Description: "list agents",
Keybindings: parseBindings("<leader>a"),
Trigger: []string{"agents"},
},
{ {
Name: ModelCycleRecentCommand, Name: ModelCycleRecentCommand,
Description: "next recent model", Description: "next recent model",
@@ -274,33 +252,28 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry {
Description: "previous recent model", Description: "previous recent model",
Keybindings: parseBindings("shift+f2"), Keybindings: parseBindings("shift+f2"),
}, },
{
Name: AgentListCommand,
Description: "list agents",
Keybindings: parseBindings("<leader>a"),
Trigger: []string{"agents"},
},
{
Name: AgentCycleCommand,
Description: "next agent",
Keybindings: parseBindings("tab"),
},
{
Name: AgentCycleReverseCommand,
Description: "previous agent",
Keybindings: parseBindings("shift+tab"),
},
{ {
Name: ThemeListCommand, Name: ThemeListCommand,
Description: "list themes", Description: "list themes",
Keybindings: parseBindings("<leader>t"), Keybindings: parseBindings("<leader>t"),
Trigger: []string{"themes"}, Trigger: []string{"themes"},
}, },
// {
// Name: FileListCommand,
// Description: "list files",
// Keybindings: parseBindings("<leader>f"),
// Trigger: []string{"files"},
// },
{
Name: FileCloseCommand,
Description: "close file",
Keybindings: parseBindings("esc"),
},
{
Name: FileSearchCommand,
Description: "search file",
Keybindings: parseBindings("<leader>/"),
},
{
Name: FileDiffToggleCommand,
Description: "split/unified diff",
Keybindings: parseBindings("<leader>v"),
},
{ {
Name: ProjectInitCommand, Name: ProjectInitCommand,
Description: "create/update AGENTS.md", Description: "create/update AGENTS.md",

View File

@@ -1,236 +0,0 @@
package dialog
import (
"log/slog"
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/sst/opencode/internal/completions"
"github.com/sst/opencode/internal/components/list"
"github.com/sst/opencode/internal/components/modal"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
"github.com/sst/opencode/internal/util"
)
const (
findDialogWidth = 76
)
type FindSelectedMsg struct {
FilePath string
}
type FindDialogCloseMsg struct{}
type findInitialSuggestionsMsg struct {
suggestions []completions.CompletionSuggestion
}
type FindDialog interface {
layout.Modal
tea.Model
tea.ViewModel
SetWidth(width int)
SetHeight(height int)
IsEmpty() bool
}
// findItem is a custom list item for file suggestions
type findItem struct {
suggestion completions.CompletionSuggestion
}
func (f findItem) Render(
selected bool,
width int,
baseStyle styles.Style,
) string {
t := theme.CurrentTheme()
itemStyle := baseStyle.
Background(t.BackgroundPanel()).
Foreground(t.TextMuted())
if selected {
itemStyle = itemStyle.Foreground(t.Primary())
}
return itemStyle.PaddingLeft(1).Render(f.suggestion.Display(itemStyle))
}
func (f findItem) Selectable() bool {
return true
}
type findDialogComponent struct {
completionProvider completions.CompletionProvider
allSuggestions []completions.CompletionSuggestion
width, height int
modal *modal.Modal
searchDialog *SearchDialog
dialogWidth int
}
func (f *findDialogComponent) Init() tea.Cmd {
return tea.Batch(
f.loadInitialSuggestions(),
f.searchDialog.Init(),
)
}
func (f *findDialogComponent) loadInitialSuggestions() tea.Cmd {
return func() tea.Msg {
items, err := f.completionProvider.GetChildEntries("")
if err != nil {
slog.Error("Failed to get initial completion items", "error", err)
return findInitialSuggestionsMsg{suggestions: []completions.CompletionSuggestion{}}
}
return findInitialSuggestionsMsg{suggestions: items}
}
}
func (f *findDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case findInitialSuggestionsMsg:
// Handle initial suggestions setup
f.allSuggestions = msg.suggestions
// Calculate dialog width
f.dialogWidth = f.calculateDialogWidth()
// Initialize search dialog with calculated width
f.searchDialog = NewSearchDialog("Search files...", 10)
f.searchDialog.SetWidth(f.dialogWidth)
// Convert to list items
items := make([]list.Item, len(f.allSuggestions))
for i, suggestion := range f.allSuggestions {
items[i] = findItem{suggestion: suggestion}
}
f.searchDialog.SetItems(items)
// Update modal with calculated width
f.modal = modal.New(
modal.WithTitle("Find Files"),
modal.WithMaxWidth(f.dialogWidth+4),
)
return f, f.searchDialog.Init()
case []completions.CompletionSuggestion:
// Store suggestions and convert to findItem for the search dialog
f.allSuggestions = msg
items := make([]list.Item, len(msg))
for i, suggestion := range msg {
items[i] = findItem{suggestion: suggestion}
}
f.searchDialog.SetItems(items)
return f, nil
case SearchSelectionMsg:
// Handle selection from search dialog - now we can directly access the suggestion
if item, ok := msg.Item.(findItem); ok {
return f, f.selectFile(item.suggestion)
}
return f, nil
case SearchCancelledMsg:
return f, f.Close()
case SearchQueryChangedMsg:
// Update completion items based on search query
return f, func() tea.Msg {
items, err := f.completionProvider.GetChildEntries(msg.Query)
if err != nil {
slog.Error("Failed to get completion items", "error", err)
return []completions.CompletionSuggestion{}
}
return items
}
case tea.WindowSizeMsg:
f.width = msg.Width
f.height = msg.Height
// Recalculate width based on new viewport size
oldWidth := f.dialogWidth
f.dialogWidth = f.calculateDialogWidth()
if oldWidth != f.dialogWidth {
f.searchDialog.SetWidth(f.dialogWidth)
// Update modal max width too
f.modal = modal.New(
modal.WithTitle("Find Files"),
modal.WithMaxWidth(f.dialogWidth+4),
)
}
f.searchDialog.SetHeight(msg.Height)
}
// Forward all other messages to the search dialog
updatedDialog, cmd := f.searchDialog.Update(msg)
f.searchDialog = updatedDialog.(*SearchDialog)
return f, cmd
}
func (f *findDialogComponent) View() string {
return f.searchDialog.View()
}
func (f *findDialogComponent) calculateDialogWidth() int {
// Use fixed width unless viewport is smaller
if f.width > 0 && f.width < findDialogWidth+10 {
return f.width - 10
}
return findDialogWidth
}
func (f *findDialogComponent) SetWidth(width int) {
f.width = width
f.searchDialog.SetWidth(f.dialogWidth)
}
func (f *findDialogComponent) SetHeight(height int) {
f.height = height
}
func (f *findDialogComponent) IsEmpty() bool {
return f.searchDialog.GetQuery() == ""
}
func (f *findDialogComponent) selectFile(item completions.CompletionSuggestion) tea.Cmd {
return tea.Sequence(
f.Close(),
util.CmdHandler(FindSelectedMsg{
FilePath: item.Value,
}),
)
}
func (f *findDialogComponent) Render(background string) string {
return f.modal.Render(f.View(), background)
}
func (f *findDialogComponent) Close() tea.Cmd {
f.searchDialog.SetQuery("")
f.searchDialog.Blur()
return util.CmdHandler(modal.CloseModalMsg{})
}
func NewFindDialog(completionProvider completions.CompletionProvider) FindDialog {
component := &findDialogComponent{
completionProvider: completionProvider,
dialogWidth: findDialogWidth,
allSuggestions: []completions.CompletionSuggestion{},
}
// Create search dialog and modal with fixed width
component.searchDialog = NewSearchDialog("Search files...", 10)
component.searchDialog.SetWidth(findDialogWidth)
component.modal = modal.New(
modal.WithTitle("Find Files"),
modal.WithMaxWidth(findDialogWidth+4),
)
return component
}

View File

@@ -1,184 +0,0 @@
package dialog
import (
"github.com/charmbracelet/bubbles/v2/key"
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/charmbracelet/lipgloss/v2"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
"github.com/sst/opencode/internal/util"
)
// InitDialogCmp is a component that asks the user if they want to initialize the project.
type InitDialogCmp struct {
width, height int
selected int
keys initDialogKeyMap
}
// NewInitDialogCmp creates a new InitDialogCmp.
func NewInitDialogCmp() InitDialogCmp {
return InitDialogCmp{
selected: 0,
keys: initDialogKeyMap{},
}
}
type initDialogKeyMap struct {
Tab key.Binding
Left key.Binding
Right key.Binding
Enter key.Binding
Escape key.Binding
Y key.Binding
N key.Binding
}
// ShortHelp implements key.Map.
func (k initDialogKeyMap) ShortHelp() []key.Binding {
return []key.Binding{
key.NewBinding(
key.WithKeys("tab", "left", "right"),
key.WithHelp("tab/←/→", "toggle selection"),
),
key.NewBinding(
key.WithKeys("enter"),
key.WithHelp("enter", "confirm"),
),
key.NewBinding(
key.WithKeys("esc", "q"),
key.WithHelp("esc/q", "cancel"),
),
key.NewBinding(
key.WithKeys("y", "n"),
key.WithHelp("y/n", "yes/no"),
),
}
}
// FullHelp implements key.Map.
func (k initDialogKeyMap) FullHelp() [][]key.Binding {
return [][]key.Binding{k.ShortHelp()}
}
// Init implements tea.Model.
func (m InitDialogCmp) Init() tea.Cmd {
return nil
}
// Update implements tea.Model.
func (m InitDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch {
case key.Matches(msg, key.NewBinding(key.WithKeys("esc"))):
return m, util.CmdHandler(CloseInitDialogMsg{Initialize: false})
case key.Matches(msg, key.NewBinding(key.WithKeys("tab", "left", "right", "h", "l"))):
m.selected = (m.selected + 1) % 2
return m, nil
case key.Matches(msg, key.NewBinding(key.WithKeys("enter"))):
return m, util.CmdHandler(CloseInitDialogMsg{Initialize: m.selected == 0})
case key.Matches(msg, key.NewBinding(key.WithKeys("y"))):
return m, util.CmdHandler(CloseInitDialogMsg{Initialize: true})
case key.Matches(msg, key.NewBinding(key.WithKeys("n"))):
return m, util.CmdHandler(CloseInitDialogMsg{Initialize: false})
}
case tea.WindowSizeMsg:
m.width = msg.Width
m.height = msg.Height
}
return m, nil
}
// View implements tea.Model.
func (m InitDialogCmp) View() string {
t := theme.CurrentTheme()
baseStyle := styles.NewStyle().Foreground(t.Text())
// Calculate width needed for content
maxWidth := 60 // Width for explanation text
title := baseStyle.
Foreground(t.Primary()).
Bold(true).
Width(maxWidth).
Padding(0, 1).
Render("Initialize Project")
explanation := baseStyle.
Foreground(t.Text()).
Width(maxWidth).
Padding(0, 1).
Render("Initialization generates a new AGENTS.md file that contains information about your codebase, this file serves as memory for each project, you can freely add to it to help the agents be better at their job.")
question := baseStyle.
Foreground(t.Text()).
Width(maxWidth).
Padding(1, 1).
Render("Would you like to initialize this project?")
maxWidth = min(maxWidth, m.width-10)
yesStyle := baseStyle
noStyle := baseStyle
if m.selected == 0 {
yesStyle = yesStyle.
Background(t.Primary()).
Foreground(t.Background()).
Bold(true)
noStyle = noStyle.
Background(t.Background()).
Foreground(t.Primary())
} else {
noStyle = noStyle.
Background(t.Primary()).
Foreground(t.Background()).
Bold(true)
yesStyle = yesStyle.
Background(t.Background()).
Foreground(t.Primary())
}
yes := yesStyle.Padding(0, 3).Render("Yes")
no := noStyle.Padding(0, 3).Render("No")
buttons := lipgloss.JoinHorizontal(lipgloss.Center, yes, baseStyle.Render(" "), no)
buttons = baseStyle.
Width(maxWidth).
Padding(1, 0).
Render(buttons)
content := lipgloss.JoinVertical(
lipgloss.Left,
title,
baseStyle.Width(maxWidth).Render(""),
explanation,
question,
buttons,
baseStyle.Width(maxWidth).Render(""),
)
return baseStyle.Padding(1, 2).
Border(lipgloss.RoundedBorder()).
BorderBackground(t.Background()).
BorderForeground(t.TextMuted()).
Width(lipgloss.Width(content) + 4).
Render(content)
}
// SetSize sets the size of the component.
func (m *InitDialogCmp) SetSize(width, height int) {
m.width = width
m.height = height
}
// CloseInitDialogMsg is a message that is sent when the init dialog is closed.
type CloseInitDialogMsg struct {
Initialize bool
}
// ShowInitDialogMsg is a message that is sent to show the init dialog.
type ShowInitDialogMsg struct {
Show bool
}

View File

@@ -1,278 +0,0 @@
package fileviewer
import (
"fmt"
"strings"
tea "github.com/charmbracelet/bubbletea/v2"
"github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/commands"
"github.com/sst/opencode/internal/components/dialog"
"github.com/sst/opencode/internal/components/diff"
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
"github.com/sst/opencode/internal/util"
"github.com/sst/opencode/internal/viewport"
)
type DiffStyle int
const (
DiffStyleSplit DiffStyle = iota
DiffStyleUnified
)
type Model struct {
app *app.App
width, height int
viewport viewport.Model
filename *string
content *string
isDiff *bool
diffStyle DiffStyle
}
type fileRenderedMsg struct {
content string
}
func New(app *app.App) Model {
vp := viewport.New()
m := Model{
app: app,
viewport: vp,
diffStyle: DiffStyleUnified,
}
if app.State.SplitDiff {
m.diffStyle = DiffStyleSplit
}
return m
}
func (m Model) Init() tea.Cmd {
return m.viewport.Init()
}
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
var cmds []tea.Cmd
switch msg := msg.(type) {
case fileRenderedMsg:
m.viewport.SetContent(msg.content)
return m, util.CmdHandler(app.FileRenderedMsg{
FilePath: *m.filename,
})
case dialog.ThemeSelectedMsg:
return m, m.render()
case tea.KeyMsg:
switch msg.String() {
// TODO
}
}
vp, cmd := m.viewport.Update(msg)
m.viewport = vp
cmds = append(cmds, cmd)
return m, tea.Batch(cmds...)
}
func (m Model) View() string {
if !m.HasFile() {
return ""
}
header := *m.filename
header = styles.NewStyle().
Padding(1, 2).
Width(m.width).
Background(theme.CurrentTheme().BackgroundElement()).
Foreground(theme.CurrentTheme().Text()).
Render(header)
t := theme.CurrentTheme()
close := m.app.Key(commands.FileCloseCommand)
diffToggle := m.app.Key(commands.FileDiffToggleCommand)
if m.isDiff == nil || *m.isDiff == false {
diffToggle = ""
}
background := t.Background()
footer := layout.Render(
layout.FlexOptions{
Background: &background,
Direction: layout.Row,
Justify: layout.JustifyCenter,
Align: layout.AlignStretch,
Width: m.width - 2,
Gap: 5,
},
layout.FlexItem{
View: close,
},
layout.FlexItem{
View: diffToggle,
},
)
footer = styles.NewStyle().Background(t.Background()).Padding(0, 1).Render(footer)
return header + "\n" + m.viewport.View() + "\n" + footer
}
func (m *Model) Clear() (Model, tea.Cmd) {
m.filename = nil
m.content = nil
m.isDiff = nil
return *m, m.render()
}
func (m *Model) ToggleDiff() (Model, tea.Cmd) {
switch m.diffStyle {
case DiffStyleSplit:
m.diffStyle = DiffStyleUnified
default:
m.diffStyle = DiffStyleSplit
}
return *m, m.render()
}
func (m *Model) DiffStyle() DiffStyle {
return m.diffStyle
}
func (m Model) HasFile() bool {
return m.filename != nil && m.content != nil
}
func (m Model) Filename() string {
if m.filename == nil {
return ""
}
return *m.filename
}
func (m *Model) SetSize(width, height int) (Model, tea.Cmd) {
if m.width != width || m.height != height {
m.width = width
m.height = height
m.viewport.SetWidth(width)
m.viewport.SetHeight(height - 4)
return *m, m.render()
}
return *m, nil
}
func (m *Model) SetFile(filename string, content string, isDiff bool) (Model, tea.Cmd) {
m.filename = &filename
m.content = &content
m.isDiff = &isDiff
return *m, m.render()
}
func (m *Model) render() tea.Cmd {
if m.filename == nil || m.content == nil {
m.viewport.SetContent("")
return nil
}
return func() tea.Msg {
t := theme.CurrentTheme()
var rendered string
if m.isDiff != nil && *m.isDiff {
diffResult := ""
var err error
if m.diffStyle == DiffStyleSplit {
diffResult, err = diff.FormatDiff(
*m.filename,
*m.content,
diff.WithWidth(m.width),
)
} else if m.diffStyle == DiffStyleUnified {
diffResult, err = diff.FormatUnifiedDiff(
*m.filename,
*m.content,
diff.WithWidth(m.width),
)
}
if err != nil {
rendered = styles.NewStyle().
Foreground(t.Error()).
Render(fmt.Sprintf("Error rendering diff: %v", err))
} else {
rendered = strings.TrimRight(diffResult, "\n")
}
} else {
rendered = util.RenderFile(
*m.filename,
*m.content,
m.width,
)
}
rendered = styles.NewStyle().
Width(m.width).
Background(t.BackgroundPanel()).
Render(rendered)
return fileRenderedMsg{
content: rendered,
}
}
}
func (m *Model) ScrollTo(line int) {
m.viewport.SetYOffset(line)
}
func (m *Model) ScrollToBottom() {
m.viewport.GotoBottom()
}
func (m *Model) ScrollToTop() {
m.viewport.GotoTop()
}
func (m *Model) PageUp() (Model, tea.Cmd) {
m.viewport.ViewUp()
return *m, nil
}
func (m *Model) PageDown() (Model, tea.Cmd) {
m.viewport.ViewDown()
return *m, nil
}
func (m *Model) HalfPageUp() (Model, tea.Cmd) {
m.viewport.HalfViewUp()
return *m, nil
}
func (m *Model) HalfPageDown() (Model, tea.Cmd) {
m.viewport.HalfViewDown()
return *m, nil
}
func (m Model) AtTop() bool {
return m.viewport.AtTop()
}
func (m Model) AtBottom() bool {
return m.viewport.AtBottom()
}
func (m Model) ScrollPercent() float64 {
return m.viewport.ScrollPercent()
}
func (m Model) TotalLineCount() int {
return m.viewport.TotalLineCount()
}
func (m Model) VisibleLineCount() int {
return m.viewport.VisibleLineCount()
}

View File

@@ -132,7 +132,7 @@ func (m *statusComponent) View() string {
modeForeground = t.BackgroundPanel() modeForeground = t.BackgroundPanel()
} }
command := m.app.Commands[commands.SwitchAgentCommand] command := m.app.Commands[commands.AgentCycleCommand]
kb := command.Keybindings[0] kb := command.Keybindings[0]
key := kb.Key key := kb.Key
if kb.RequiresLeader { if kb.RequiresLeader {

View File

@@ -23,7 +23,6 @@ import (
"github.com/sst/opencode/internal/components/chat" "github.com/sst/opencode/internal/components/chat"
cmdcomp "github.com/sst/opencode/internal/components/commands" cmdcomp "github.com/sst/opencode/internal/components/commands"
"github.com/sst/opencode/internal/components/dialog" "github.com/sst/opencode/internal/components/dialog"
"github.com/sst/opencode/internal/components/fileviewer"
"github.com/sst/opencode/internal/components/modal" "github.com/sst/opencode/internal/components/modal"
"github.com/sst/opencode/internal/components/status" "github.com/sst/opencode/internal/components/status"
"github.com/sst/opencode/internal/components/toast" "github.com/sst/opencode/internal/components/toast"
@@ -78,7 +77,6 @@ type Model struct {
interruptKeyState InterruptKeyState interruptKeyState InterruptKeyState
exitKeyState ExitKeyState exitKeyState ExitKeyState
messagesRight bool messagesRight bool
fileViewer fileviewer.Model
} }
func (a Model) Init() tea.Cmd { func (a Model) Init() tea.Cmd {
@@ -94,13 +92,6 @@ func (a Model) Init() tea.Cmd {
cmds = append(cmds, a.status.Init()) cmds = append(cmds, a.status.Init())
cmds = append(cmds, a.completions.Init()) cmds = append(cmds, a.completions.Init())
cmds = append(cmds, a.toastManager.Init()) cmds = append(cmds, a.toastManager.Init())
cmds = append(cmds, a.fileViewer.Init())
// Check if we should show the init dialog
cmds = append(cmds, func() tea.Msg {
shouldShow := a.app.Info.Git && a.app.Info.Time.Initialized > 0
return dialog.ShowInitDialogMsg{Show: shouldShow}
})
return tea.Batch(cmds...) return tea.Batch(cmds...)
} }
@@ -586,12 +577,6 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
slog.Error("Server error", "name", err.Name, "message", err.Data.Message) slog.Error("Server error", "name", err.Name, "message", err.Data.Message)
return a, toast.NewErrorToast(err.Data.Message, toast.WithTitle(string(err.Name))) return a, toast.NewErrorToast(err.Data.Message, toast.WithTitle(string(err.Name)))
} }
case opencode.EventListResponseEventFileWatcherUpdated:
if a.fileViewer.HasFile() {
if a.fileViewer.Filename() == msg.Properties.File {
return a.openFile(msg.Properties.File)
}
}
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
msg.Height -= 2 // Make space for the status bar msg.Height -= 2 // Make space for the status bar
a.width, a.height = msg.Width, msg.Height a.width, a.height = msg.Width, msg.Height
@@ -653,8 +638,6 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// Reset exit key state after timeout // Reset exit key state after timeout
a.exitKeyState = ExitKeyIdle a.exitKeyState = ExitKeyIdle
a.editor.SetExitKeyInDebounce(false) a.editor.SetExitKeyInDebounce(false)
case dialog.FindSelectedMsg:
return a.openFile(msg.FilePath)
case tea.PasteMsg, tea.ClipboardMsg: case tea.PasteMsg, tea.ClipboardMsg:
// Paste events: prioritize modal if active, otherwise editor // Paste events: prioritize modal if active, otherwise editor
if a.modal != nil { if a.modal != nil {
@@ -753,10 +736,6 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
} }
fv, cmd := a.fileViewer.Update(msg)
a.fileViewer = fv
cmds = append(cmds, cmd)
return a, tea.Batch(cmds...) return a, tea.Batch(cmds...)
} }
@@ -806,26 +785,6 @@ func (a Model) Cleanup() {
a.status.Cleanup() a.status.Cleanup()
} }
func (a Model) openFile(filepath string) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
response, err := a.app.Client.File.Read(
context.Background(),
opencode.FileReadParams{
Path: opencode.F(filepath),
},
)
if err != nil {
slog.Error("Failed to read file", "error", err)
return a, toast.NewErrorToast("Failed to read file")
}
a.fileViewer, cmd = a.fileViewer.SetFile(
filepath,
response.Content,
response.Type == "patch",
)
return a, cmd
}
func (a Model) home() (string, int, int) { func (a Model) home() (string, int, int) {
t := theme.CurrentTheme() t := theme.CurrentTheme()
effectiveWidth := a.width - 4 effectiveWidth := a.width - 4
@@ -1014,11 +973,11 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) {
case commands.AppHelpCommand: case commands.AppHelpCommand:
helpDialog := dialog.NewHelpDialog(a.app) helpDialog := dialog.NewHelpDialog(a.app)
a.modal = helpDialog a.modal = helpDialog
case commands.SwitchAgentCommand: case commands.AgentCycleCommand:
updated, cmd := a.app.SwitchAgent() updated, cmd := a.app.SwitchAgent()
a.app = updated a.app = updated
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
case commands.SwitchAgentReverseCommand: case commands.AgentCycleReverseCommand:
updated, cmd := a.app.SwitchAgentReverse() updated, cmd := a.app.SwitchAgentReverse()
a.app = updated a.app = updated
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
@@ -1197,21 +1156,6 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) {
case commands.ThemeListCommand: case commands.ThemeListCommand:
themeDialog := dialog.NewThemeDialog() themeDialog := dialog.NewThemeDialog()
a.modal = themeDialog a.modal = themeDialog
// case commands.FileListCommand:
// a.editor.Blur()
// findDialog := dialog.NewFindDialog(a.fileProvider)
// cmds = append(cmds, findDialog.Init())
// a.modal = findDialog
case commands.FileCloseCommand:
a.fileViewer, cmd = a.fileViewer.Clear()
cmds = append(cmds, cmd)
case commands.FileDiffToggleCommand:
a.fileViewer, cmd = a.fileViewer.ToggleDiff()
cmds = append(cmds, cmd)
a.app.State.SplitDiff = a.fileViewer.DiffStyle() == fileviewer.DiffStyleSplit
cmds = append(cmds, a.app.SaveState())
case commands.FileSearchCommand:
return a, nil
case commands.ProjectInitCommand: case commands.ProjectInitCommand:
cmds = append(cmds, a.app.InitializeProject(context.Background())) cmds = append(cmds, a.app.InitializeProject(context.Background()))
case commands.InputClearCommand: case commands.InputClearCommand:
@@ -1242,42 +1186,21 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) {
a.messages = updated.(chat.MessagesComponent) a.messages = updated.(chat.MessagesComponent)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
case commands.MessagesPageUpCommand: case commands.MessagesPageUpCommand:
if a.fileViewer.HasFile() {
a.fileViewer, cmd = a.fileViewer.PageUp()
cmds = append(cmds, cmd)
} else {
updated, cmd := a.messages.PageUp() updated, cmd := a.messages.PageUp()
a.messages = updated.(chat.MessagesComponent) a.messages = updated.(chat.MessagesComponent)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
}
case commands.MessagesPageDownCommand: case commands.MessagesPageDownCommand:
if a.fileViewer.HasFile() {
a.fileViewer, cmd = a.fileViewer.PageDown()
cmds = append(cmds, cmd)
} else {
updated, cmd := a.messages.PageDown() updated, cmd := a.messages.PageDown()
a.messages = updated.(chat.MessagesComponent) a.messages = updated.(chat.MessagesComponent)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
}
case commands.MessagesHalfPageUpCommand: case commands.MessagesHalfPageUpCommand:
if a.fileViewer.HasFile() {
a.fileViewer, cmd = a.fileViewer.HalfPageUp()
cmds = append(cmds, cmd)
} else {
updated, cmd := a.messages.HalfPageUp() updated, cmd := a.messages.HalfPageUp()
a.messages = updated.(chat.MessagesComponent) a.messages = updated.(chat.MessagesComponent)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
}
case commands.MessagesHalfPageDownCommand: case commands.MessagesHalfPageDownCommand:
if a.fileViewer.HasFile() {
a.fileViewer, cmd = a.fileViewer.HalfPageDown()
cmds = append(cmds, cmd)
} else {
updated, cmd := a.messages.HalfPageDown() updated, cmd := a.messages.HalfPageDown()
a.messages = updated.(chat.MessagesComponent) a.messages = updated.(chat.MessagesComponent)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
}
case commands.MessagesCopyCommand: case commands.MessagesCopyCommand:
updated, cmd := a.messages.CopyLastMessage() updated, cmd := a.messages.CopyLastMessage()
a.messages = updated.(chat.MessagesComponent) a.messages = updated.(chat.MessagesComponent)
@@ -1327,8 +1250,6 @@ func NewModel(app *app.App) tea.Model {
toastManager: toast.NewToastManager(), toastManager: toast.NewToastManager(),
interruptKeyState: InterruptKeyIdle, interruptKeyState: InterruptKeyIdle,
exitKeyState: ExitKeyIdle, exitKeyState: ExitKeyIdle,
fileViewer: fileviewer.New(app),
messagesRight: app.State.MessagesRight,
} }
return model return model

View File

@@ -11,33 +11,19 @@ opencode has a list of keybinds that you can customize through the opencode conf
"keybinds": { "keybinds": {
"leader": "ctrl+x", "leader": "ctrl+x",
"app_help": "<leader>h", "app_help": "<leader>h",
"switch_agent": "tab", "app_exit": "ctrl+c,<leader>q",
"switch_agent_reverse": "shift+tab",
"editor_open": "<leader>e", "editor_open": "<leader>e",
"theme_list": "<leader>t",
"project_init": "<leader>i",
"tool_details": "<leader>d",
"thinking_blocks": "<leader>b",
"session_export": "<leader>x",
"session_new": "<leader>n", "session_new": "<leader>n",
"session_list": "<leader>l", "session_list": "<leader>l",
"session_share": "<leader>s", "session_share": "<leader>s",
"session_unshare": "none", "session_unshare": "none",
"session_export": "<leader>x",
"session_interrupt": "esc", "session_interrupt": "esc",
"session_compact": "<leader>c", "session_compact": "<leader>c",
"tool_details": "<leader>d",
"thinking_blocks": "<leader>b",
"model_list": "<leader>m",
"agent_list": "<leader>a",
"model_cycle_recent": "f2",
"model_cycle_recent_reverse": "shift+f2",
"theme_list": "<leader>t",
"project_init": "<leader>i",
"input_clear": "ctrl+c",
"input_paste": "ctrl+v",
"input_submit": "enter",
"input_newline": "shift+enter,ctrl+j",
"messages_page_up": "pgup", "messages_page_up": "pgup",
"messages_page_down": "pgdown", "messages_page_down": "pgdown",
"messages_half_page_up": "ctrl+alt+u", "messages_half_page_up": "ctrl+alt+u",
@@ -47,8 +33,16 @@ opencode has a list of keybinds that you can customize through the opencode conf
"messages_copy": "<leader>y", "messages_copy": "<leader>y",
"messages_undo": "<leader>u", "messages_undo": "<leader>u",
"messages_redo": "<leader>r", "messages_redo": "<leader>r",
"model_list": "<leader>m",
"app_exit": "ctrl+c,<leader>q" "model_cycle_recent": "f2",
"model_cycle_recent_reverse": "shift+f2",
"agent_list": "<leader>a",
"agent_cycle": "tab",
"agent_cycle_reverse": "shift+tab",
"input_clear": "ctrl+c",
"input_paste": "ctrl+v",
"input_submit": "enter",
"input_newline": "shift+enter,ctrl+j"
} }
} }
``` ```