Merge branch 'dev' into project
This commit is contained in:
commit
11afe56f63
178 changed files with 4236 additions and 6777 deletions
|
|
@ -820,11 +820,13 @@ func (a *App) SendCommand(ctx context.Context, command string, args string) (*Ap
|
|||
opencode.SessionCommandParams{
|
||||
Command: opencode.F(command),
|
||||
Arguments: opencode.F(args),
|
||||
Agent: opencode.F(a.Agents[a.AgentIndex].Name),
|
||||
Model: opencode.F(a.State.Provider + "/" + a.State.Model),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
slog.Error("Failed to execute command", "error", err)
|
||||
return toast.NewErrorToast("Failed to execute command")
|
||||
return toast.NewErrorToast(fmt.Sprintf("Failed to execute command: %v", err))()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
|
@ -856,7 +858,7 @@ func (a *App) SendShell(ctx context.Context, command string) (*App, tea.Cmd) {
|
|||
)
|
||||
if err != nil {
|
||||
slog.Error("Failed to submit shell command", "error", err)
|
||||
return toast.NewErrorToast("Failed to submit shell command")()
|
||||
return toast.NewErrorToast(fmt.Sprintf("Failed to submit shell command: %v", err))()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
|
|
|||
|
|
@ -92,6 +92,12 @@ func (r CommandRegistry) Sorted() []Command {
|
|||
if b.Name == AppExitCommand {
|
||||
return -1
|
||||
}
|
||||
if a.Custom && !b.Custom {
|
||||
return 1
|
||||
}
|
||||
if !a.Custom && b.Custom {
|
||||
return -1
|
||||
}
|
||||
|
||||
return strings.Compare(string(a.Name), string(b.Name))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -92,7 +92,41 @@ func (c *CommandCompletionProvider) GetChildEntries(
|
|||
}
|
||||
|
||||
matches := fuzzy.RankFindFold(query, commandNames)
|
||||
sort.Sort(matches)
|
||||
|
||||
// Custom sort to prioritize exact matches
|
||||
sort.Slice(matches, func(i, j int) bool {
|
||||
// Check for exact match (case-insensitive)
|
||||
iExact := strings.EqualFold(matches[i].Target, query)
|
||||
jExact := strings.EqualFold(matches[j].Target, query)
|
||||
|
||||
// Exact matches come first
|
||||
if iExact && !jExact {
|
||||
return true
|
||||
}
|
||||
if !iExact && jExact {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check for prefix match (case-insensitive)
|
||||
iPrefix := strings.HasPrefix(strings.ToLower(matches[i].Target), strings.ToLower(query))
|
||||
jPrefix := strings.HasPrefix(strings.ToLower(matches[j].Target), strings.ToLower(query))
|
||||
|
||||
// Prefix matches come before fuzzy matches
|
||||
if iPrefix && !jPrefix {
|
||||
return true
|
||||
}
|
||||
if !iPrefix && jPrefix {
|
||||
return false
|
||||
}
|
||||
|
||||
// Otherwise, sort by fuzzy match score (lower distance is better)
|
||||
if matches[i].Distance != matches[j].Distance {
|
||||
return matches[i].Distance < matches[j].Distance
|
||||
}
|
||||
|
||||
// If distances are equal, sort by original index (stable sort)
|
||||
return matches[i].OriginalIndex < matches[j].OriginalIndex
|
||||
})
|
||||
|
||||
// Convert matches to completion items, deduplicating by command name
|
||||
items := []CompletionSuggestion{}
|
||||
|
|
|
|||
|
|
@ -489,11 +489,22 @@ func (m *editorComponent) Submit() (tea.Model, tea.Cmd) {
|
|||
|
||||
var cmds []tea.Cmd
|
||||
if strings.HasPrefix(value, "/") {
|
||||
value = value[1:]
|
||||
commandName := strings.Split(value, " ")[0]
|
||||
// Expand attachments in the value to get actual content
|
||||
expandedValue := value
|
||||
attachments := m.textarea.GetAttachments()
|
||||
for _, att := range attachments {
|
||||
if att.Type == "text" && att.Source != nil {
|
||||
if textSource, ok := att.Source.(*attachment.TextSource); ok {
|
||||
expandedValue = strings.Replace(expandedValue, att.Display, textSource.Value, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expandedValue = expandedValue[1:] // Remove the "/"
|
||||
commandName := strings.Split(expandedValue, " ")[0]
|
||||
command := m.app.Commands[commands.CommandName(commandName)]
|
||||
if command.Custom {
|
||||
args := strings.TrimPrefix(value, command.PrimaryTrigger()+" ")
|
||||
args := strings.TrimPrefix(expandedValue, command.PrimaryTrigger()+" ")
|
||||
cmds = append(
|
||||
cmds,
|
||||
util.CmdHandler(app.SendCommand{Command: string(command.Name), Args: args}),
|
||||
|
|
|
|||
|
|
@ -1173,10 +1173,10 @@ func (m *messagesComponent) UndoLastMessage() (tea.Model, tea.Cmd) {
|
|||
)
|
||||
if err != nil {
|
||||
slog.Error("Failed to undo message", "error", err)
|
||||
return toast.NewErrorToast("Failed to undo message")
|
||||
return toast.NewErrorToast("Failed to undo message")()
|
||||
}
|
||||
if response == nil {
|
||||
return toast.NewErrorToast("Failed to undo message")
|
||||
return toast.NewErrorToast("Failed to undo message")()
|
||||
}
|
||||
return app.MessageRevertedMsg{Session: *response, Message: revertedMessage}
|
||||
}
|
||||
|
|
@ -1241,10 +1241,10 @@ func (m *messagesComponent) RedoLastMessage() (tea.Model, tea.Cmd) {
|
|||
)
|
||||
if err != nil {
|
||||
slog.Error("Failed to unrevert session", "error", err)
|
||||
return toast.NewErrorToast("Failed to redo message")
|
||||
return toast.NewErrorToast("Failed to redo message")()
|
||||
}
|
||||
if response == nil {
|
||||
return toast.NewErrorToast("Failed to redo message")
|
||||
return toast.NewErrorToast("Failed to redo message")()
|
||||
}
|
||||
return app.SessionUnrevertedMsg{Session: *response}
|
||||
}
|
||||
|
|
@ -1261,10 +1261,10 @@ func (m *messagesComponent) RedoLastMessage() (tea.Model, tea.Cmd) {
|
|||
)
|
||||
if err != nil {
|
||||
slog.Error("Failed to redo message", "error", err)
|
||||
return toast.NewErrorToast("Failed to redo message")
|
||||
return toast.NewErrorToast("Failed to redo message")()
|
||||
}
|
||||
if response == nil {
|
||||
return toast.NewErrorToast("Failed to redo message")
|
||||
return toast.NewErrorToast("Failed to redo message")()
|
||||
}
|
||||
return app.MessageRevertedMsg{Session: *response, Message: revertedMessage}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
)
|
||||
if err != nil {
|
||||
slog.Error("Failed to respond to permission request", "error", err)
|
||||
return toast.NewErrorToast("Failed to respond to permission request")
|
||||
return toast.NewErrorToast("Failed to respond to permission request")()
|
||||
}
|
||||
slog.Debug("Responded to permission request", "response", resp)
|
||||
return nil
|
||||
|
|
@ -915,9 +915,10 @@ func (a Model) Cleanup() {
|
|||
func (a Model) home() (string, int, int) {
|
||||
t := theme.CurrentTheme()
|
||||
effectiveWidth := a.width - 4
|
||||
baseStyle := styles.NewStyle().Background(t.Background())
|
||||
baseStyle := styles.NewStyle().Foreground(t.Text()).Background(t.Background())
|
||||
base := baseStyle.Render
|
||||
muted := styles.NewStyle().Foreground(t.TextMuted()).Background(t.Background()).Render
|
||||
highlight := styles.NewStyle().Foreground(t.Accent()).Background(t.Background()).Render
|
||||
|
||||
open := `
|
||||
█▀▀█ █▀▀█ █▀▀ █▀▀▄
|
||||
|
|
@ -952,9 +953,9 @@ func (a Model) home() (string, int, int) {
|
|||
)
|
||||
|
||||
// Use limit of 4 for vscode, 6 for others
|
||||
limit := 6
|
||||
limit := 4
|
||||
if util.IsVSCode() {
|
||||
limit = 4
|
||||
limit = 2
|
||||
}
|
||||
|
||||
showVscode := util.IsVSCode()
|
||||
|
|
@ -971,15 +972,23 @@ func (a Model) home() (string, int, int) {
|
|||
styles.WhitespaceStyle(t.Background()),
|
||||
)
|
||||
|
||||
grok := highlight("Grok Code is free for a limited time")
|
||||
grok = lipgloss.PlaceHorizontal(
|
||||
effectiveWidth,
|
||||
lipgloss.Center,
|
||||
grok,
|
||||
styles.WhitespaceStyle(t.Background()),
|
||||
)
|
||||
|
||||
lines := []string{}
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, logoAndVersion)
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, cmds)
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, "")
|
||||
lines = append(lines, grok)
|
||||
lines = append(lines, "")
|
||||
|
||||
mainHeight := lipgloss.Height(strings.Join(lines, "\n"))
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ import (
|
|||
"github.com/sst/opencode/internal/styles"
|
||||
)
|
||||
|
||||
var shimmerStart = time.Now()
|
||||
var (
|
||||
shimmerStart = time.Now()
|
||||
trueColorSupport = hasTrueColor()
|
||||
)
|
||||
|
||||
// Shimmer renders text with a moving foreground highlight.
|
||||
// bg is the background color, dim is the base text color, bright is the highlight color.
|
||||
|
|
@ -32,7 +35,7 @@ func Shimmer(s string, bg compat.AdaptiveColor, _ compat.AdaptiveColor, _ compat
|
|||
elapsed := time.Since(shimmerStart).Seconds()
|
||||
pos := (math.Mod(elapsed, sweep) / sweep) * period
|
||||
|
||||
half := 4.0
|
||||
half := 2.0
|
||||
|
||||
type seg struct {
|
||||
useHex bool
|
||||
|
|
@ -41,60 +44,52 @@ func Shimmer(s string, bg compat.AdaptiveColor, _ compat.AdaptiveColor, _ compat
|
|||
faint bool
|
||||
text string
|
||||
}
|
||||
var segs []seg
|
||||
segs := make([]seg, 0, n/4)
|
||||
|
||||
useHex := hasTrueColor()
|
||||
useHex := trueColorSupport
|
||||
for i, r := range runes {
|
||||
ip := float64(i + pad)
|
||||
dist := math.Abs(ip - pos)
|
||||
t := 0.0
|
||||
if dist <= half {
|
||||
x := math.Pi * (dist / half)
|
||||
t = 0.5 * (1.0 + math.Cos(x))
|
||||
}
|
||||
// Cosine brightness: base + amp*t (quantized for grouping)
|
||||
base := 0.55
|
||||
amp := 0.45
|
||||
brightness := base
|
||||
if t > 0 {
|
||||
brightness = base + amp*t
|
||||
}
|
||||
lvl := int(math.Round(brightness * 255.0))
|
||||
if !useHex {
|
||||
step := 24 // ~11 steps across range for non-truecolor
|
||||
lvl = int(math.Round(float64(lvl)/float64(step))) * step
|
||||
}
|
||||
|
||||
bold := lvl >= 208
|
||||
faint := lvl <= 128
|
||||
|
||||
// truecolor if possible; else fallback to modifiers only
|
||||
bold := false
|
||||
faint := true
|
||||
hex := ""
|
||||
if useHex {
|
||||
if lvl < 0 {
|
||||
lvl = 0
|
||||
|
||||
if dist <= half {
|
||||
// Simple 3-level brightness based on distance
|
||||
if dist <= half/3 {
|
||||
// Center: brightest
|
||||
bold = true
|
||||
faint = false
|
||||
if useHex {
|
||||
hex = "#ffffff"
|
||||
}
|
||||
} else {
|
||||
// Edge: medium bright
|
||||
bold = false
|
||||
faint = false
|
||||
if useHex {
|
||||
hex = "#cccccc"
|
||||
}
|
||||
}
|
||||
if lvl > 255 {
|
||||
lvl = 255
|
||||
}
|
||||
hex = rgbHex(lvl, lvl, lvl)
|
||||
}
|
||||
|
||||
if len(segs) == 0 {
|
||||
if len(segs) == 0 ||
|
||||
segs[len(segs)-1].useHex != useHex ||
|
||||
segs[len(segs)-1].hex != hex ||
|
||||
segs[len(segs)-1].bold != bold ||
|
||||
segs[len(segs)-1].faint != faint {
|
||||
segs = append(segs, seg{useHex: useHex, hex: hex, bold: bold, faint: faint, text: string(r)})
|
||||
} else {
|
||||
last := &segs[len(segs)-1]
|
||||
if last.useHex == useHex && last.hex == hex && last.bold == bold && last.faint == faint {
|
||||
last.text += string(r)
|
||||
} else {
|
||||
segs = append(segs, seg{useHex: useHex, hex: hex, bold: bold, faint: faint, text: string(r)})
|
||||
}
|
||||
segs[len(segs)-1].text += string(r)
|
||||
}
|
||||
}
|
||||
|
||||
baseStyle := styles.NewStyle().Background(bg)
|
||||
var b strings.Builder
|
||||
b.Grow(len(s) * 2)
|
||||
for _, g := range segs {
|
||||
st := styles.NewStyle().Background(bg)
|
||||
st := baseStyle
|
||||
if g.useHex && g.hex != "" {
|
||||
c := compat.AdaptiveColor{Dark: lipgloss.Color(g.hex), Light: lipgloss.Color(g.hex)}
|
||||
st = st.Foreground(c)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue