38 lines
1.1 KiB
Go
38 lines
1.1 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package prompt
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
|
|
"git.stormux.org/storm/fenrir/internal/screen"
|
|
)
|
|
|
|
// Unlike Detector, ShellDetector excludes passwords, questions and completion
|
|
// messages. It observes raw screens, independently of application speech filters.
|
|
type ShellDetector struct {
|
|
previous map[string]int
|
|
terminal int
|
|
}
|
|
|
|
var shellPattern = regexp.MustCompile(`^(?:[$#>]|\[[^\r\n]+\]\s*[$#>]|[a-zA-Z0-9._-]+[$#>]|[a-zA-Z0-9._-]+@[^\s<>]+.*[$#>])\s*$`)
|
|
|
|
func (d *ShellDetector) Observe(next screen.Snapshot) bool {
|
|
if !next.Active || next.Rows <= 0 || next.Cols <= 0 || len(next.Cells) != next.Rows*next.Cols {
|
|
return false
|
|
}
|
|
counts := make(map[string]int)
|
|
for row := range next.Rows {
|
|
counts[strings.TrimSpace(next.Text(row))]++
|
|
}
|
|
text := ""
|
|
if next.Y >= 0 && next.Y < next.Rows {
|
|
text = strings.TrimSpace(next.Text(next.Y))
|
|
}
|
|
fresh := d.previous != nil && d.terminal == next.Terminal && shellPattern.MatchString(text) && counts[text] > d.previous[text]
|
|
d.previous, d.terminal = counts, next.Terminal
|
|
return fresh
|
|
}
|