mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-19 08:44:22 +01:00
fix: more commands cleanup
This commit is contained in:
@@ -127,6 +127,12 @@ export namespace Config {
|
||||
if (result.keybinds?.switch_mode_reverse && !result.keybinds.switch_agent_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) {
|
||||
const os = await import("os")
|
||||
@@ -199,9 +205,12 @@ export namespace Config {
|
||||
.object({
|
||||
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"),
|
||||
switch_agent: z.string().optional().default("tab").describe("Next agent"),
|
||||
switch_agent_reverse: z.string().optional().default("shift+tab").describe("Previous agent"),
|
||||
app_exit: z.string().optional().default("ctrl+c,<leader>q").describe("Exit the application"),
|
||||
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_new: z.string().optional().default("<leader>n").describe("Create a new session"),
|
||||
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_interrupt: z.string().optional().default("esc").describe("Interrupt current 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_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"),
|
||||
@@ -234,14 +231,29 @@ export namespace Config {
|
||||
messages_copy: z.string().optional().default("<leader>y").describe("Copy message"),
|
||||
messages_undo: z.string().optional().default("<leader>u").describe("Undo 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
|
||||
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
|
||||
.string()
|
||||
.optional()
|
||||
.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_close: z.string().optional().default("none").describe("@deprecated Close file"),
|
||||
file_search: z.string().optional().default("none").describe("@deprecated Search file"),
|
||||
|
||||
@@ -1099,7 +1099,7 @@ export namespace Server {
|
||||
.post(
|
||||
"/tui/execute-command",
|
||||
describeRoute({
|
||||
description: "Execute a TUI command (e.g. switch_agent)",
|
||||
description: "Execute a TUI command (e.g. agent_cycle)",
|
||||
operationId: "tui.executeCommand",
|
||||
responses: {
|
||||
200: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
configured_endpoints: 36
|
||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-a881262c7de4ab59bdfbfc6e30a23c47dee465d7270ffb867b760b0103aff8ed.yml
|
||||
openapi_spec_hash: 7dbb6f96f5c26a25c849e50298f58586
|
||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-d0eaf92deaa53a25bbbc3181540ad73ed5a4aec6381ac08d8122e24318e5e455.yml
|
||||
openapi_spec_hash: 22196d859c0711e564b9538d988abda6
|
||||
config_hash: 8d85a768523cff92b85ef06c443d49fa
|
||||
|
||||
@@ -1655,19 +1655,25 @@ func (r ConfigShare) IsKnown() bool {
|
||||
}
|
||||
|
||||
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
|
||||
AppExit string `json:"app_exit,required"`
|
||||
// Show help dialog
|
||||
AppHelp string `json:"app_help,required"`
|
||||
// Open external editor
|
||||
EditorOpen string `json:"editor_open,required"`
|
||||
// Close file
|
||||
// @deprecated Close file
|
||||
FileClose string `json:"file_close,required"`
|
||||
// Split/unified diff
|
||||
// @deprecated Split/unified diff
|
||||
FileDiffToggle string `json:"file_diff_toggle,required"`
|
||||
// List files
|
||||
// @deprecated Currently not available. List files
|
||||
FileList string `json:"file_list,required"`
|
||||
// Search file
|
||||
// @deprecated Search file
|
||||
FileSearch string `json:"file_search,required"`
|
||||
// Clear input field
|
||||
InputClear string `json:"input_clear,required"`
|
||||
@@ -1689,15 +1695,15 @@ type KeybindsConfig struct {
|
||||
MessagesHalfPageUp string `json:"messages_half_page_up,required"`
|
||||
// Navigate to last message
|
||||
MessagesLast string `json:"messages_last,required"`
|
||||
// Toggle layout
|
||||
// @deprecated Toggle layout
|
||||
MessagesLayoutToggle string `json:"messages_layout_toggle,required"`
|
||||
// Navigate to next message
|
||||
// @deprecated Navigate to next message
|
||||
MessagesNext string `json:"messages_next,required"`
|
||||
// Scroll messages down by one page
|
||||
MessagesPageDown string `json:"messages_page_down,required"`
|
||||
// Scroll messages up by one page
|
||||
MessagesPageUp string `json:"messages_page_up,required"`
|
||||
// Navigate to previous message
|
||||
// @deprecated Navigate to previous message
|
||||
MessagesPrevious string `json:"messages_previous,required"`
|
||||
// Redo message
|
||||
MessagesRedo string `json:"messages_redo,required"`
|
||||
@@ -1705,6 +1711,10 @@ type KeybindsConfig struct {
|
||||
MessagesRevert string `json:"messages_revert,required"`
|
||||
// Undo message
|
||||
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
|
||||
ModelList string `json:"model_list,required"`
|
||||
// Create/update AGENTS.md
|
||||
@@ -1723,13 +1733,13 @@ type KeybindsConfig struct {
|
||||
SessionShare string `json:"session_share,required"`
|
||||
// Unshare current session
|
||||
SessionUnshare string `json:"session_unshare,required"`
|
||||
// Next agent
|
||||
// @deprecated use agent_cycle. Next agent
|
||||
SwitchAgent string `json:"switch_agent,required"`
|
||||
// Previous agent
|
||||
// @deprecated use agent_cycle_reverse. Previous agent
|
||||
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"`
|
||||
// @deprecated use switch_agent_reverse. Previous mode
|
||||
// @deprecated use agent_cycle_reverse. Previous mode
|
||||
SwitchModeReverse string `json:"switch_mode_reverse,required"`
|
||||
// List available themes
|
||||
ThemeList string `json:"theme_list,required"`
|
||||
@@ -1742,49 +1752,54 @@ type KeybindsConfig struct {
|
||||
|
||||
// keybindsConfigJSON contains the JSON metadata for the struct [KeybindsConfig]
|
||||
type keybindsConfigJSON struct {
|
||||
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
|
||||
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
|
||||
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) {
|
||||
|
||||
@@ -54,13 +54,13 @@ type EventListResponse struct {
|
||||
// [EventListResponseEventMessageRemovedProperties],
|
||||
// [EventListResponseEventMessagePartUpdatedProperties],
|
||||
// [EventListResponseEventMessagePartRemovedProperties],
|
||||
// [EventListResponseEventStorageWriteProperties],
|
||||
// [EventListResponseEventFileEditedProperties], [interface{}], [Permission],
|
||||
// [EventListResponseEventStorageWriteProperties], [Permission],
|
||||
// [EventListResponseEventPermissionRepliedProperties],
|
||||
// [EventListResponseEventFileEditedProperties],
|
||||
// [EventListResponseEventSessionUpdatedProperties],
|
||||
// [EventListResponseEventSessionDeletedProperties],
|
||||
// [EventListResponseEventSessionIdleProperties],
|
||||
// [EventListResponseEventSessionErrorProperties],
|
||||
// [EventListResponseEventSessionErrorProperties], [interface{}],
|
||||
// [EventListResponseEventFileWatcherUpdatedProperties],
|
||||
// [EventListResponseEventIdeInstalledProperties].
|
||||
Properties interface{} `json:"properties,required"`
|
||||
@@ -100,12 +100,11 @@ func (r *EventListResponse) UnmarshalJSON(data []byte) (err error) {
|
||||
// [EventListResponseEventMessageUpdated], [EventListResponseEventMessageRemoved],
|
||||
// [EventListResponseEventMessagePartUpdated],
|
||||
// [EventListResponseEventMessagePartRemoved],
|
||||
// [EventListResponseEventStorageWrite], [EventListResponseEventFileEdited],
|
||||
// [EventListResponseEventServerConnected],
|
||||
// [EventListResponseEventPermissionUpdated],
|
||||
// [EventListResponseEventPermissionReplied],
|
||||
// [EventListResponseEventStorageWrite], [EventListResponseEventPermissionUpdated],
|
||||
// [EventListResponseEventPermissionReplied], [EventListResponseEventFileEdited],
|
||||
// [EventListResponseEventSessionUpdated], [EventListResponseEventSessionDeleted],
|
||||
// [EventListResponseEventSessionIdle], [EventListResponseEventSessionError],
|
||||
// [EventListResponseEventServerConnected],
|
||||
// [EventListResponseEventFileWatcherUpdated],
|
||||
// [EventListResponseEventIdeInstalled].
|
||||
func (r EventListResponse) AsUnion() EventListResponseUnion {
|
||||
@@ -117,12 +116,11 @@ func (r EventListResponse) AsUnion() EventListResponseUnion {
|
||||
// [EventListResponseEventMessageUpdated], [EventListResponseEventMessageRemoved],
|
||||
// [EventListResponseEventMessagePartUpdated],
|
||||
// [EventListResponseEventMessagePartRemoved],
|
||||
// [EventListResponseEventStorageWrite], [EventListResponseEventFileEdited],
|
||||
// [EventListResponseEventServerConnected],
|
||||
// [EventListResponseEventPermissionUpdated],
|
||||
// [EventListResponseEventPermissionReplied],
|
||||
// [EventListResponseEventStorageWrite], [EventListResponseEventPermissionUpdated],
|
||||
// [EventListResponseEventPermissionReplied], [EventListResponseEventFileEdited],
|
||||
// [EventListResponseEventSessionUpdated], [EventListResponseEventSessionDeleted],
|
||||
// [EventListResponseEventSessionIdle], [EventListResponseEventSessionError],
|
||||
// [EventListResponseEventServerConnected],
|
||||
// [EventListResponseEventFileWatcherUpdated] or
|
||||
// [EventListResponseEventIdeInstalled].
|
||||
type EventListResponseUnion interface {
|
||||
@@ -168,16 +166,6 @@ func init() {
|
||||
Type: reflect.TypeOf(EventListResponseEventStorageWrite{}),
|
||||
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{
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventPermissionUpdated{}),
|
||||
@@ -188,6 +176,11 @@ func init() {
|
||||
Type: reflect.TypeOf(EventListResponseEventPermissionReplied{}),
|
||||
DiscriminatorValue: "permission.replied",
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventFileEdited{}),
|
||||
DiscriminatorValue: "file.edited",
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventSessionUpdated{}),
|
||||
@@ -208,6 +201,11 @@ func init() {
|
||||
Type: reflect.TypeOf(EventListResponseEventSessionError{}),
|
||||
DiscriminatorValue: "session.error",
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventServerConnected{}),
|
||||
DiscriminatorValue: "server.connected",
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.JSON,
|
||||
Type: reflect.TypeOf(EventListResponseEventFileWatcherUpdated{}),
|
||||
@@ -651,105 +649,6 @@ func (r EventListResponseEventStorageWriteType) IsKnown() bool {
|
||||
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 {
|
||||
Properties Permission `json:"properties,required"`
|
||||
Type EventListResponseEventPermissionUpdatedType `json:"type,required"`
|
||||
@@ -853,6 +752,66 @@ func (r EventListResponseEventPermissionRepliedType) IsKnown() bool {
|
||||
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 {
|
||||
Properties EventListResponseEventSessionUpdatedProperties `json:"properties,required"`
|
||||
Type EventListResponseEventSessionUpdatedType `json:"type,required"`
|
||||
@@ -1229,6 +1188,45 @@ func (r EventListResponseEventSessionErrorType) IsKnown() bool {
|
||||
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 {
|
||||
Properties EventListResponseEventFileWatcherUpdatedProperties `json:"properties,required"`
|
||||
Type EventListResponseEventFileWatcherUpdatedType `json:"type,required"`
|
||||
@@ -1376,21 +1374,21 @@ const (
|
||||
EventListResponseTypeMessagePartUpdated EventListResponseType = "message.part.updated"
|
||||
EventListResponseTypeMessagePartRemoved EventListResponseType = "message.part.removed"
|
||||
EventListResponseTypeStorageWrite EventListResponseType = "storage.write"
|
||||
EventListResponseTypeFileEdited EventListResponseType = "file.edited"
|
||||
EventListResponseTypeServerConnected EventListResponseType = "server.connected"
|
||||
EventListResponseTypePermissionUpdated EventListResponseType = "permission.updated"
|
||||
EventListResponseTypePermissionReplied EventListResponseType = "permission.replied"
|
||||
EventListResponseTypeFileEdited EventListResponseType = "file.edited"
|
||||
EventListResponseTypeSessionUpdated EventListResponseType = "session.updated"
|
||||
EventListResponseTypeSessionDeleted EventListResponseType = "session.deleted"
|
||||
EventListResponseTypeSessionIdle EventListResponseType = "session.idle"
|
||||
EventListResponseTypeSessionError EventListResponseType = "session.error"
|
||||
EventListResponseTypeServerConnected EventListResponseType = "server.connected"
|
||||
EventListResponseTypeFileWatcherUpdated EventListResponseType = "file.watcher.updated"
|
||||
EventListResponseTypeIdeInstalled EventListResponseType = "ide.installed"
|
||||
)
|
||||
|
||||
func (r EventListResponseType) IsKnown() bool {
|
||||
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 false
|
||||
|
||||
@@ -2025,9 +2025,9 @@ func (r toolStateCompletedTimeJSON) RawJSON() string {
|
||||
type ToolStateError struct {
|
||||
Error string `json:"error,required"`
|
||||
Input map[string]interface{} `json:"input,required"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
Status ToolStateErrorStatus `json:"status,required"`
|
||||
Time ToolStateErrorTime `json:"time,required"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
JSON toolStateErrorJSON `json:"-"`
|
||||
}
|
||||
|
||||
@@ -2035,9 +2035,9 @@ type ToolStateError struct {
|
||||
type toolStateErrorJSON struct {
|
||||
Error apijson.Field
|
||||
Input apijson.Field
|
||||
Metadata apijson.Field
|
||||
Status apijson.Field
|
||||
Time apijson.Field
|
||||
Metadata apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func (r *TuiService) ClearPrompt(ctx context.Context, opts ...option.RequestOpti
|
||||
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) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
path := "tui/execute-command"
|
||||
|
||||
@@ -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>) {
|
||||
return (options?.client ?? this._client).post<TuiExecuteCommandResponses, unknown, ThrowOnError>({
|
||||
|
||||
@@ -347,10 +347,10 @@ export type ToolStateError = {
|
||||
input: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
metadata: {
|
||||
error: string
|
||||
metadata?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
error: string
|
||||
time: {
|
||||
start: number
|
||||
end: number
|
||||
@@ -750,25 +750,29 @@ export type KeybindsConfig = {
|
||||
*/
|
||||
app_help: string
|
||||
/**
|
||||
* @deprecated use switch_agent. Next mode
|
||||
* Exit the application
|
||||
*/
|
||||
switch_mode: string
|
||||
/**
|
||||
* @deprecated use switch_agent_reverse. Previous mode
|
||||
*/
|
||||
switch_mode_reverse: string
|
||||
/**
|
||||
* Next agent
|
||||
*/
|
||||
switch_agent: string
|
||||
/**
|
||||
* Previous agent
|
||||
*/
|
||||
switch_agent_reverse: string
|
||||
app_exit: string
|
||||
/**
|
||||
* Open external editor
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -798,41 +802,65 @@ export type KeybindsConfig = {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
/**
|
||||
* Create/update AGENTS.md
|
||||
*/
|
||||
project_init: string
|
||||
agent_cycle_reverse: string
|
||||
/**
|
||||
* Clear input field
|
||||
*/
|
||||
@@ -850,61 +878,53 @@ export type KeybindsConfig = {
|
||||
*/
|
||||
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
|
||||
/**
|
||||
* Navigate to next message
|
||||
* @deprecated Navigate to next message
|
||||
*/
|
||||
messages_next: string
|
||||
/**
|
||||
* Navigate to first message
|
||||
*/
|
||||
messages_first: string
|
||||
/**
|
||||
* Navigate to last message
|
||||
*/
|
||||
messages_last: string
|
||||
/**
|
||||
* Toggle layout
|
||||
* @deprecated Toggle layout
|
||||
*/
|
||||
messages_layout_toggle: string
|
||||
/**
|
||||
* Copy message
|
||||
*/
|
||||
messages_copy: string
|
||||
/**
|
||||
* @deprecated use messages_undo. Revert message
|
||||
*/
|
||||
messages_revert: string
|
||||
/**
|
||||
* Undo message
|
||||
*/
|
||||
messages_undo: string
|
||||
/**
|
||||
* Redo message
|
||||
*/
|
||||
messages_redo: string
|
||||
/**
|
||||
* Exit the application
|
||||
*/
|
||||
app_exit: string
|
||||
}
|
||||
|
||||
export type AgentConfig = {
|
||||
@@ -1086,6 +1106,7 @@ export type Agent = {
|
||||
name: string
|
||||
description?: string
|
||||
mode: "subagent" | "primary" | "all"
|
||||
builtIn: boolean
|
||||
topP?: number
|
||||
temperature?: number
|
||||
permission: {
|
||||
|
||||
@@ -35,8 +35,6 @@ type State struct {
|
||||
Agent string `toml:"agent"`
|
||||
RecentlyUsedModels []ModelUsage `toml:"recently_used_models"`
|
||||
RecentlyUsedAgents []AgentUsage `toml:"recently_used_agents"`
|
||||
MessagesRight bool `toml:"messages_right"`
|
||||
SplitDiff bool `toml:"split_diff"`
|
||||
MessageHistory []Prompt `toml:"message_history"`
|
||||
ShowToolDetails *bool `toml:"show_tool_details"`
|
||||
ShowThinkingBlocks *bool `toml:"show_thinking_blocks"`
|
||||
|
||||
@@ -108,9 +108,12 @@ func (r CommandRegistry) Matches(msg tea.KeyPressMsg, leader bool) []Command {
|
||||
|
||||
const (
|
||||
AppHelpCommand CommandName = "app_help"
|
||||
SwitchAgentCommand CommandName = "switch_agent"
|
||||
SwitchAgentReverseCommand CommandName = "switch_agent_reverse"
|
||||
AppExitCommand CommandName = "app_exit"
|
||||
ThemeListCommand CommandName = "theme_list"
|
||||
ProjectInitCommand CommandName = "project_init"
|
||||
EditorOpenCommand CommandName = "editor_open"
|
||||
ToolDetailsCommand CommandName = "tool_details"
|
||||
ThinkingBlocksCommand CommandName = "thinking_blocks"
|
||||
SessionNewCommand CommandName = "session_new"
|
||||
SessionListCommand CommandName = "session_list"
|
||||
SessionShareCommand CommandName = "session_share"
|
||||
@@ -118,34 +121,25 @@ const (
|
||||
SessionInterruptCommand CommandName = "session_interrupt"
|
||||
SessionCompactCommand CommandName = "session_compact"
|
||||
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"
|
||||
MessagesPageDownCommand CommandName = "messages_page_down"
|
||||
MessagesHalfPageUpCommand CommandName = "messages_half_page_up"
|
||||
MessagesHalfPageDownCommand CommandName = "messages_half_page_down"
|
||||
|
||||
MessagesFirstCommand CommandName = "messages_first"
|
||||
MessagesLastCommand CommandName = "messages_last"
|
||||
|
||||
MessagesCopyCommand CommandName = "messages_copy"
|
||||
MessagesUndoCommand CommandName = "messages_undo"
|
||||
MessagesRedoCommand CommandName = "messages_redo"
|
||||
AppExitCommand CommandName = "app_exit"
|
||||
MessagesFirstCommand CommandName = "messages_first"
|
||||
MessagesLastCommand CommandName = "messages_last"
|
||||
MessagesCopyCommand CommandName = "messages_copy"
|
||||
MessagesUndoCommand CommandName = "messages_undo"
|
||||
MessagesRedoCommand CommandName = "messages_redo"
|
||||
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 {
|
||||
@@ -184,16 +178,6 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry {
|
||||
Keybindings: parseBindings("<leader>h"),
|
||||
Trigger: []string{"help"},
|
||||
},
|
||||
{
|
||||
Name: SwitchAgentCommand,
|
||||
Description: "next agent",
|
||||
Keybindings: parseBindings("tab"),
|
||||
},
|
||||
{
|
||||
Name: SwitchAgentReverseCommand,
|
||||
Description: "previous agent",
|
||||
Keybindings: parseBindings("shift+tab"),
|
||||
},
|
||||
{
|
||||
Name: EditorOpenCommand,
|
||||
Description: "open editor",
|
||||
@@ -258,12 +242,6 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry {
|
||||
Keybindings: parseBindings("<leader>m"),
|
||||
Trigger: []string{"models"},
|
||||
},
|
||||
{
|
||||
Name: AgentListCommand,
|
||||
Description: "list agents",
|
||||
Keybindings: parseBindings("<leader>a"),
|
||||
Trigger: []string{"agents"},
|
||||
},
|
||||
{
|
||||
Name: ModelCycleRecentCommand,
|
||||
Description: "next recent model",
|
||||
@@ -274,33 +252,28 @@ func LoadFromConfig(config *opencode.Config) CommandRegistry {
|
||||
Description: "previous recent model",
|
||||
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,
|
||||
Description: "list themes",
|
||||
Keybindings: parseBindings("<leader>t"),
|
||||
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,
|
||||
Description: "create/update AGENTS.md",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -132,7 +132,7 @@ func (m *statusComponent) View() string {
|
||||
modeForeground = t.BackgroundPanel()
|
||||
}
|
||||
|
||||
command := m.app.Commands[commands.SwitchAgentCommand]
|
||||
command := m.app.Commands[commands.AgentCycleCommand]
|
||||
kb := command.Keybindings[0]
|
||||
key := kb.Key
|
||||
if kb.RequiresLeader {
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"github.com/sst/opencode/internal/components/chat"
|
||||
cmdcomp "github.com/sst/opencode/internal/components/commands"
|
||||
"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/status"
|
||||
"github.com/sst/opencode/internal/components/toast"
|
||||
@@ -78,7 +77,6 @@ type Model struct {
|
||||
interruptKeyState InterruptKeyState
|
||||
exitKeyState ExitKeyState
|
||||
messagesRight bool
|
||||
fileViewer fileviewer.Model
|
||||
}
|
||||
|
||||
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.completions.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...)
|
||||
}
|
||||
@@ -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)
|
||||
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:
|
||||
msg.Height -= 2 // Make space for the status bar
|
||||
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
|
||||
a.exitKeyState = ExitKeyIdle
|
||||
a.editor.SetExitKeyInDebounce(false)
|
||||
case dialog.FindSelectedMsg:
|
||||
return a.openFile(msg.FilePath)
|
||||
case tea.PasteMsg, tea.ClipboardMsg:
|
||||
// Paste events: prioritize modal if active, otherwise editor
|
||||
if a.modal != nil {
|
||||
@@ -753,10 +736,6 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
|
||||
fv, cmd := a.fileViewer.Update(msg)
|
||||
a.fileViewer = fv
|
||||
cmds = append(cmds, cmd)
|
||||
|
||||
return a, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
@@ -806,26 +785,6 @@ func (a Model) 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) {
|
||||
t := theme.CurrentTheme()
|
||||
effectiveWidth := a.width - 4
|
||||
@@ -1014,11 +973,11 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) {
|
||||
case commands.AppHelpCommand:
|
||||
helpDialog := dialog.NewHelpDialog(a.app)
|
||||
a.modal = helpDialog
|
||||
case commands.SwitchAgentCommand:
|
||||
case commands.AgentCycleCommand:
|
||||
updated, cmd := a.app.SwitchAgent()
|
||||
a.app = updated
|
||||
cmds = append(cmds, cmd)
|
||||
case commands.SwitchAgentReverseCommand:
|
||||
case commands.AgentCycleReverseCommand:
|
||||
updated, cmd := a.app.SwitchAgentReverse()
|
||||
a.app = updated
|
||||
cmds = append(cmds, cmd)
|
||||
@@ -1197,21 +1156,6 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) {
|
||||
case commands.ThemeListCommand:
|
||||
themeDialog := dialog.NewThemeDialog()
|
||||
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:
|
||||
cmds = append(cmds, a.app.InitializeProject(context.Background()))
|
||||
case commands.InputClearCommand:
|
||||
@@ -1242,42 +1186,21 @@ func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) {
|
||||
a.messages = updated.(chat.MessagesComponent)
|
||||
cmds = append(cmds, cmd)
|
||||
case commands.MessagesPageUpCommand:
|
||||
if a.fileViewer.HasFile() {
|
||||
a.fileViewer, cmd = a.fileViewer.PageUp()
|
||||
cmds = append(cmds, cmd)
|
||||
} else {
|
||||
updated, cmd := a.messages.PageUp()
|
||||
a.messages = updated.(chat.MessagesComponent)
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
updated, cmd := a.messages.PageUp()
|
||||
a.messages = updated.(chat.MessagesComponent)
|
||||
cmds = append(cmds, cmd)
|
||||
case commands.MessagesPageDownCommand:
|
||||
if a.fileViewer.HasFile() {
|
||||
a.fileViewer, cmd = a.fileViewer.PageDown()
|
||||
cmds = append(cmds, cmd)
|
||||
} else {
|
||||
updated, cmd := a.messages.PageDown()
|
||||
a.messages = updated.(chat.MessagesComponent)
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
updated, cmd := a.messages.PageDown()
|
||||
a.messages = updated.(chat.MessagesComponent)
|
||||
cmds = append(cmds, cmd)
|
||||
case commands.MessagesHalfPageUpCommand:
|
||||
if a.fileViewer.HasFile() {
|
||||
a.fileViewer, cmd = a.fileViewer.HalfPageUp()
|
||||
cmds = append(cmds, cmd)
|
||||
} else {
|
||||
updated, cmd := a.messages.HalfPageUp()
|
||||
a.messages = updated.(chat.MessagesComponent)
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
updated, cmd := a.messages.HalfPageUp()
|
||||
a.messages = updated.(chat.MessagesComponent)
|
||||
cmds = append(cmds, cmd)
|
||||
case commands.MessagesHalfPageDownCommand:
|
||||
if a.fileViewer.HasFile() {
|
||||
a.fileViewer, cmd = a.fileViewer.HalfPageDown()
|
||||
cmds = append(cmds, cmd)
|
||||
} else {
|
||||
updated, cmd := a.messages.HalfPageDown()
|
||||
a.messages = updated.(chat.MessagesComponent)
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
|
||||
updated, cmd := a.messages.HalfPageDown()
|
||||
a.messages = updated.(chat.MessagesComponent)
|
||||
cmds = append(cmds, cmd)
|
||||
case commands.MessagesCopyCommand:
|
||||
updated, cmd := a.messages.CopyLastMessage()
|
||||
a.messages = updated.(chat.MessagesComponent)
|
||||
@@ -1327,8 +1250,6 @@ func NewModel(app *app.App) tea.Model {
|
||||
toastManager: toast.NewToastManager(),
|
||||
interruptKeyState: InterruptKeyIdle,
|
||||
exitKeyState: ExitKeyIdle,
|
||||
fileViewer: fileviewer.New(app),
|
||||
messagesRight: app.State.MessagesRight,
|
||||
}
|
||||
|
||||
return model
|
||||
|
||||
@@ -11,33 +11,19 @@ opencode has a list of keybinds that you can customize through the opencode conf
|
||||
"keybinds": {
|
||||
"leader": "ctrl+x",
|
||||
"app_help": "<leader>h",
|
||||
"switch_agent": "tab",
|
||||
"switch_agent_reverse": "shift+tab",
|
||||
|
||||
"app_exit": "ctrl+c,<leader>q",
|
||||
"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_list": "<leader>l",
|
||||
"session_share": "<leader>s",
|
||||
"session_unshare": "none",
|
||||
"session_export": "<leader>x",
|
||||
"session_interrupt": "esc",
|
||||
"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_down": "pgdown",
|
||||
"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_undo": "<leader>u",
|
||||
"messages_redo": "<leader>r",
|
||||
|
||||
"app_exit": "ctrl+c,<leader>q"
|
||||
"model_list": "<leader>m",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user