82 lines
2.3 KiB
Go
82 lines
2.3 KiB
Go
package tui
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
)
|
|
|
|
// permissionRequest kommt vom Backend-Loop (E-11: "ask"-Modus) über
|
|
// Model.permCh. resp erwartet genau eine Antwort; requestPermission blockiert
|
|
// darauf im Loop-Goroutine, bis die TUI eine Taste verarbeitet hat.
|
|
type permissionRequest struct {
|
|
tool string
|
|
input json.RawMessage
|
|
resp chan bool
|
|
}
|
|
|
|
type permissionRequestMsg struct{ req permissionRequest }
|
|
|
|
func listenPermissionRequests(ch <-chan permissionRequest) tea.Cmd {
|
|
return func() tea.Msg {
|
|
req, ok := <-ch
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return permissionRequestMsg{req: req}
|
|
}
|
|
}
|
|
|
|
// requestPermission ist der Hook, den loop.RequestPermission aufruft (siehe
|
|
// New()). Läuft auf dem Goroutine des Backend-Loops, nicht dem der TUI —
|
|
// deshalb reine Channel-Kommunikation statt direktem Feldzugriff auf m.
|
|
func (m *Model) requestPermission(ctx context.Context, toolName string, input json.RawMessage) bool {
|
|
resp := make(chan bool, 1)
|
|
req := permissionRequest{tool: toolName, input: input, resp: resp}
|
|
|
|
select {
|
|
case m.permCh <- req:
|
|
case <-ctx.Done():
|
|
return false
|
|
}
|
|
|
|
select {
|
|
case decision := <-resp:
|
|
return decision
|
|
case <-ctx.Done():
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (m *Model) handlePermissionKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|
req := m.permReq
|
|
m.permReq = nil
|
|
|
|
allow := msg.String() == "y" || msg.String() == "Y"
|
|
req.resp <- allow
|
|
|
|
verb := "denied"
|
|
if allow {
|
|
verb = "allowed"
|
|
}
|
|
m.pushCommand(fmt.Sprintf("permission %s: %s", verb, req.tool))
|
|
return m, nil
|
|
}
|
|
|
|
// renderPermissionRequest baut den Transkript-Block für eine offene
|
|
// Rückfrage — bewusst kein Vollbild-Overlay (das hat vorher den Kontext
|
|
// verdeckt, den man für die Entscheidung braucht), sondern ein eigener,
|
|
// umrandeter Eintrag im normalen Nachrichtenfluss. Viewport, Eingabefeld und
|
|
// Statuszeile bleiben sichtbar; die Statuszeile zeigt zusätzlich einen
|
|
// Hinweis (siehe renderStatusLine).
|
|
func renderPermissionRequest(toolName string, input json.RawMessage) string {
|
|
in := strings.TrimSpace(prettyJSON(input))
|
|
if len(in) > 300 {
|
|
in = in[:300] + "…"
|
|
}
|
|
body := fmt.Sprintf("⚠ Rückfrage: %s\n%s\n\n[y] erlauben [n / Esc / beliebige andere Taste] ablehnen", toolName, in)
|
|
return stylePermissionBox.Render(body)
|
|
}
|