99 lines
2.9 KiB
Go
99 lines
2.9 KiB
Go
// 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
|
|
}
|