// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package app import ( "git.stormux.org/storm/cthulhu/internal/atspi" "git.stormux.org/storm/cthulhu/internal/input" "git.stormux.org/storm/cthulhu/internal/presentation" "git.stormux.org/storm/cthulhu/internal/review" ) func (handler *eventReviewHandler) current_line(command input.Command) { if handler.focusGate.Locked() || handler.focusGate.Suspended() { return } handler.syncForeground() if handler.focusGate.Locked() || handler.focusGate.Suspended() || handler.objectReview == nil || !handler.state.current.ID().Valid() { return } handler.state.pending.terminalFocus.clear() handler.state.pending.terminalDeletion.clear() handler.state.pending.terminalRepaint.clear() style := review.FlatSpeak if command == input.SpellCurrentLine { style = review.FlatSpell } else if command == input.PhoneticCurrentLine { style = review.FlatPhonetic } browse := handler.browser != nil && handler.browser.ready && handler.browser.mode == review.BrowseMode if err := handler.objectReview.ReadCurrentLine(browse, style); err != nil { handler.logger.Warn("queue current line", "error", err) } } func (handler *eventReviewHandler) present_current_line(result review.Result) { if !handler.objectReview.CurrentDocumentResult(result) || result.DocumentFocus != handler.objectReview.Focus() || handler.focusGate.Locked() || handler.focusGate.Suspended() { return } if result.CurrentLineBrowse && (handler.browser == nil || !handler.browser.acceptsDocumentResult(review.Result{ DocumentFocus: result.DocumentFocus, DocumentPosition: result.DocumentPosition, DocumentCaretCommand: review.CaretReadLine, Err: result.Err, })) { return } request := speechRequest{Text: "Could not read current line.", Role: "system", Cause: "current-line"} if result.Err == nil { // Native reports require the matching focused source and an unbroken // capture through the application. Missing privacy data stays local. context := result.DocumentCaretContext nativeSource := len(context) > 0 && result.DocumentFocus.Valid() && result.DocumentPosition.Snapshot.ID() == result.DocumentFocus && context[0].ID() == result.DocumentFocus && context[len(context)-1].Role == "application" if result.CurrentLineBrowse || nativeSource { sources := atspi.DocumentLine{Contents: []atspi.DocumentContent{{Snapshot: result.DocumentPosition.Snapshot}}, SourceContexts: [][]atspi.Snapshot{result.DocumentCaretContext}} request.origin = document_line_speech_origin(sources) if result.DocumentCaretLine != nil { if origin := document_line_speech_origin(*result.DocumentCaretLine); origin != speechOriginLocal { request.origin = origin } } } text := result.DocumentCaretText request.Text = presentation.FlatReviewText(text, false) request.Role = result.DocumentPosition.Snapshot.Role set_review_spelling(&request, text, result.FlatSpeech) } handler.speech.Enqueue(request) }