feat: report UI crashes instead of showing a blank window
Two white-screen reports are open — one after logging a 10 GHz QSO, one on
starting a DVK auto-call — and neither could be investigated, for the same
reason: a render error emptied the window and left NOTHING. No line in
opslog.log, no dialog, nothing the operator could send but the words "white
screen". A fault the user cannot report is a fault that cannot be fixed.
So before hunting either trigger, the reporting path:
- An error boundary catches a render throw, writes the error and component
stack to the app log through the new LogUIError binding, and shows it on
screen — selectable, with Copy and Reload.
- Global handlers catch what a boundary cannot: throws from timers, event
handlers and rejected promises. An auto-call loop lives entirely in
callbacks, which is exactly where the second report came from, and those
leave no trace at all today.
It deliberately does not try to resume: React cannot promise sane state after a
render throw, and a half-working logger would be worse than a clear stop.
This does not fix either crash. It makes the next occurrence arrive with its
cause attached, which is the step that has been missing.
This commit is contained in:
@@ -1267,6 +1267,27 @@ type StartupStatus struct {
|
||||
|
||||
// GetStartupStatus exposes whatever happened during startup so the UI
|
||||
// can show a useful error instead of just "db not initialized".
|
||||
// LogUIError records a crash or unhandled error from the interface in the app
|
||||
// log, with whatever context the window can give.
|
||||
//
|
||||
// Until this existed, a render error emptied the window and left NOTHING: no
|
||||
// line in opslog.log, no dialog. Two separate white-screen reports — one after
|
||||
// logging a 10 GHz QSO, one on starting an auto-call — could not be diagnosed at
|
||||
// all, because the only evidence lived in a console the operator had to know to
|
||||
// open. A fault the user cannot report is a fault that cannot be fixed.
|
||||
func (a *App) LogUIError(kind, message, stack string) {
|
||||
msg := strings.TrimSpace(message)
|
||||
if msg == "" {
|
||||
msg = "(no message)"
|
||||
}
|
||||
applog.Printf("ui %s: %s", strings.TrimSpace(kind), msg)
|
||||
if st := strings.TrimSpace(stack); st != "" {
|
||||
// The stack goes in whole: minified frames are still the difference between
|
||||
// "somewhere in the app" and one component.
|
||||
applog.Printf("ui stack: %s", st)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetStartupStatus() StartupStatus {
|
||||
return StartupStatus{
|
||||
OK: a.startupErr == "",
|
||||
|
||||
Reference in New Issue
Block a user