// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package app import ( "context" "fmt" "strconv" "strings" "unicode" "git.stormux.org/storm/cthulhu/internal/input" "git.stormux.org/storm/cthulhu/internal/interaction" "git.stormux.org/storm/cthulhu/internal/review" ) type documentListSource interface { ListDocument(context.Context, review.DocumentTarget, int) (review.DocumentListSnapshot, error) SelectDocumentList(review.DocumentListSnapshot, int) error } func (ui *internalUI) browserListMessage(message string) { if ui.gate.Locked() || ui.gate.Suspended() { return } ui.speech.Enqueue(speechRequest{Text: message, Role: "system", Cause: "browser-list"}) if ui.braille != nil { ui.braille.EnqueueMessage(message) } } func documentListTarget(binding input.DocumentListBinding) review.DocumentTarget { switch binding.Command { case input.DocumentListAnnotations: return review.DocumentAnnotation case input.DocumentListInternalFrames: return review.DocumentInternalFrame case input.DocumentListMath: return review.DocumentMath } target, _, _ := documentReviewCommand(binding.Next) return target } func (ui *internalUI) browserList(ctx context.Context, binding input.DocumentListBinding) (*interaction.Session, review.DocumentListSnapshot, error) { if ui.documents == nil { return nil, review.DocumentListSnapshot{}, review.ErrNoCurrent } list, err := ui.documents.ListDocument(ctx, documentListTarget(binding), binding.Level) if err != nil { return nil, list, err } if len(list.Entries) == 0 { return nil, list, nil } items := make([]interaction.Item, 0, len(list.Entries)) for index, position := range list.Entries { snapshot := position.Snapshot label := position.Label if label == "" { label = snapshot.Name } if label == "" && snapshot.Role != "password text" { label = snapshot.Text } if label == "" { label = snapshot.Role } label = strings.Join(strings.FieldsFunc(label, func(r rune) bool { return unicode.IsControl(r) || unicode.IsSpace(r) }), " ") // Keep the existing form label bound without splitting UTF-8. labelRunes := []rune(label) if len(labelRunes) > 225 { label = string(labelRunes[:225]) } if label == "" { label = snapshot.Role } if snapshot.HeadingLevel > 0 { label += fmt.Sprintf(", heading level %d", snapshot.HeadingLevel) } if snapshot.Role == "check box" || snapshot.Role == "radio button" || snapshot.Role == "toggle button" { if snapshot.Checked { label += ", checked" } else { label += ", not checked" } } items = append(items, interaction.Item{ID: strconv.Itoa(index), Label: label}) } title := fmt.Sprintf("%s: %d items found", binding.Title, len(items)) if len(items) == 1 { title = binding.Title + ": 1 item found" } session, err := interaction.NewNavigationSession(title, items, list.Selected) return session, list, err }