// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package review import "git.stormux.org/storm/cthulhu/internal/atspi" // DocumentDiagnostics records bounded metadata for one navigation command. It // never reads native objects or retains accessible text for diagnostic purposes. type DocumentDiagnostics struct { Attempts []DocumentAttempt `json:"attempts"` CaptureAttempts int `json:"capture_attempts"` CursorRecreated bool `json:"cursor_recreated"` FocusRecovered bool `json:"focus_recovered"` } type DocumentAttempt struct { Focus atspi.ObjectID `json:"focus"` Before DocumentState `json:"before"` Synced DocumentState `json:"synced"` After DocumentState `json:"after"` Recovery string `json:"recovery"` Stage string `json:"stage"` Error string `json:"error,omitempty"` } type DocumentState struct { Document atspi.ObjectID `json:"document"` Object atspi.ObjectID `json:"object"` Focus atspi.ObjectID `json:"focus"` TextParent atspi.ObjectID `json:"text_parent"` Offset int `json:"offset"` Origin string `json:"origin"` Active bool `json:"active"` } func (cursor *DocumentCursor) diagnostic_state() DocumentState { if cursor == nil { return DocumentState{} } cursor.mu.Lock() defer cursor.mu.Unlock() if len(cursor.places) == 0 { return DocumentState{} } place := cursor.places[len(cursor.places)-1] origin := "native" switch place.origin { case StructuralNavigation: origin = "structural" case CaretNavigation: origin = "caret" } return DocumentState{Document: place.document.ID(), Object: place.object.ID(), Focus: place.focus.ID(), TextParent: place.textParent.ID(), Offset: place.offset, Origin: origin, Active: cursor.active == place.document} } func (result *Result) record_document_attempt(cursor *DocumentCursor, attempt DocumentAttempt, err error) { attempt.After = cursor.diagnostic_state() if err != nil { attempt.Error = err.Error() } result.DocumentDiagnostics.Attempts = append(result.DocumentDiagnostics.Attempts, attempt) }