mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-10 05:01:28 -10:00
* fix(tui): cache settled alt-screen transcript * fix(tui): invalidate settled alt-screen cache on in-place status row updates setDoctorStatusRow, setSandboxSetupStatusRow, and setCompactStatusRow all mutate an already-flushed transcript row in place (m.transcript[i] = row) without invalidating the alt-screen settled cache. Since none of frontier, width, or row count change on such an update, settleTranscript's rebuild guard never fires and the View fast path serves the stale cached snapshot - e.g. the doctor connectivity spinner freezes on its first frame. Zero altScreenSettledWidth on the in-place path, mirroring the existing pattern in toggleTranscriptRow, so the next settle rebuilds the cache. Addresses PR #647 review feedback from Vasanthdev2004. * fix(tui): invalidate settled transcript cache * fix(tui): invalidate settled file selection cards
126 lines
3.4 KiB
Go
126 lines
3.4 KiB
Go
package tui
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
tea "charm.land/bubbletea/v2"
|
|
)
|
|
|
|
const sandboxSetupStatusRowID = "sandbox/setup"
|
|
|
|
func (m model) startSandboxSetupCommand(args string) (model, tea.Cmd) {
|
|
if strings.TrimSpace(args) != "" {
|
|
m = m.setSandboxSetupStatusRow(sandboxSetupUsageText(commandStatusBlocked, "Usage: /sandbox-setup"))
|
|
return m, nil
|
|
}
|
|
if m.pending {
|
|
m = m.setSandboxSetupStatusRow(sandboxSetupUsageText(commandStatusWarning, "Cannot run sandbox setup while a run is active."))
|
|
return m, nil
|
|
}
|
|
if m.sandboxSetupInFlight {
|
|
m = m.setSandboxSetupStatusRow(sandboxSetupUsageText(commandStatusWarning, "Sandbox setup is already running."))
|
|
return m, nil
|
|
}
|
|
if m.sandboxSetupCommand == nil {
|
|
m = m.setSandboxSetupStatusRow(sandboxSetupUsageText(commandStatusWarning, "Sandbox setup is not available in this TUI session."))
|
|
return m, nil
|
|
}
|
|
|
|
m.sandboxSetupSeq++
|
|
id := m.sandboxSetupSeq
|
|
run := m.sandboxSetupCommand
|
|
ctx := m.ctx
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
m.sandboxSetupInFlight = true
|
|
m = m.setSandboxSetupStatusRow(sandboxSetupRunningText())
|
|
return m, func() tea.Msg {
|
|
return sandboxSetupCommandResultMsg{id: id, result: run(ctx)}
|
|
}
|
|
}
|
|
|
|
func sandboxSetupUsageText(status commandStatus, message string) string {
|
|
return renderCommandOutput(commandOutput{
|
|
Title: "Sandbox setup",
|
|
Status: status,
|
|
Sections: []commandSection{{
|
|
Title: "Command",
|
|
Lines: []string{message},
|
|
}},
|
|
Hints: []string{"run zero sandbox setup from the shell for the same setup path"},
|
|
})
|
|
}
|
|
|
|
func sandboxSetupRunningText() string {
|
|
return renderCommandOutput(commandOutput{
|
|
Title: "Sandbox setup",
|
|
Status: commandStatusInfo,
|
|
Sections: []commandSection{{
|
|
Title: "Status",
|
|
Lines: []string{"Running native sandbox setup for this platform."},
|
|
}},
|
|
})
|
|
}
|
|
|
|
func sandboxSetupResultText(result SandboxSetupCommandResult) string {
|
|
status := commandStatusOK
|
|
if result.ExitCode != 0 {
|
|
status = commandStatusBlocked
|
|
}
|
|
lines := sandboxSetupResultLines(result)
|
|
if len(lines) == 0 {
|
|
if result.ExitCode == 0 {
|
|
lines = []string{"Sandbox setup completed."}
|
|
} else {
|
|
lines = []string{"Sandbox setup failed."}
|
|
}
|
|
}
|
|
return renderCommandOutput(commandOutput{
|
|
Title: "Sandbox setup",
|
|
Status: status,
|
|
Sections: []commandSection{{
|
|
Title: "Result",
|
|
Fields: []commandField{{
|
|
Key: "exit",
|
|
Value: fmt.Sprintf("%d", result.ExitCode),
|
|
}},
|
|
Lines: lines,
|
|
}},
|
|
Hints: []string{"run /doctor to verify sandbox status"},
|
|
})
|
|
}
|
|
|
|
func sandboxSetupResultLines(result SandboxSetupCommandResult) []string {
|
|
lines := []string{}
|
|
for _, text := range []string{result.Output, result.Error} {
|
|
for _, line := range strings.Split(text, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if line != "" {
|
|
lines = append(lines, line)
|
|
}
|
|
}
|
|
}
|
|
return lines
|
|
}
|
|
|
|
func (m model) setSandboxSetupStatusRow(text string) model {
|
|
row := transcriptRow{kind: rowSystem, id: sandboxSetupStatusRowID, tool: "sandbox", text: text}
|
|
for i := len(m.transcript) - 1; i >= 0; i-- {
|
|
if m.transcript[i].id == sandboxSetupStatusRowID {
|
|
m.transcript[i] = row
|
|
// Settled alt-screen rows are cached across frames; force a rebuild
|
|
// so an in-place update is reflected immediately instead of serving
|
|
// the stale cached snapshot.
|
|
if i < m.flushed {
|
|
m.altScreenSettledWidth = 0
|
|
}
|
|
return m
|
|
}
|
|
}
|
|
m.transcript = appendTranscriptRow(m.transcript, row)
|
|
return m
|
|
}
|