Comments, phase 3 — search, the thread find, announcements, and a11y
Board search reaches comment bodies through a search-owned transient
index: the first live-query keystroke sweeps comments/*/index.md
off-actor (.draft and comments/.trash excluded), keystrokes re-filter
in memory, the index discards on clear — the snapshot stays O(cards).
⌘F routes by focus: the comments pane gets an app-owned find bar
spanning the whole rendered thread (next/prev cross rows with
wraparound); body and composer keep NSTextFinder; Find Next/Previous
graduate from FutureCommands. Foreign comment changes speak
path-shaped beside the announcer's ladder ("New comment on 'X'",
plural folds), narrowed by EchoLedger receipts consumed through
CommentPath.classify — and that read fixed a latent footprint bug
where a comment receipt resolved against the card's attachment
listing, read .absent, and classified the user's own write as
foreign. The pane completes its a11y story: flattened comment
elements with Edit/Delete/Reveal custom actions (un-flattening
during inline edit), phrase-table vocabulary, labeled composer and
sort control, and an audit over the open pane on a comment-seeded
fixture (runnable only where automation permission exists).
Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -265,6 +265,34 @@ public enum BoardAnnouncer {
|
||||
return AccessibilityPhrases.boardChanged(facts.diff)
|
||||
}
|
||||
|
||||
// MARK: - A card window's thread
|
||||
|
||||
/// **What a card window's landed thread re-read says out loud** — the ladder's sibling for the one
|
||||
/// kind of change the board's snapshot cannot see (10-accessibility.md ▸ Comments;
|
||||
/// 01-storage-format.md ▸ Enhanced schema's path shape).
|
||||
///
|
||||
/// ### Why it is beside the ladder rather than a sixth rung on it
|
||||
///
|
||||
/// `speech(for:)` answers "what does *this board reload* say", and its rationing — one sentence per
|
||||
/// reload debounce — is about the board window's own churn. A thread change is not in that reload's
|
||||
/// picture at all: comments are outside the snapshot, so the facts arrive from the card window's
|
||||
/// own re-read, on a different window, about a different element. Folding it into the ladder would
|
||||
/// mean either the board reload waiting on a window-scoped disk read (which 01 rules out) or the
|
||||
/// ladder silently dropping a comment sentence whenever a banner also moved.
|
||||
///
|
||||
/// What it *does* share is everything that matters: the same doctrine (foreign only — the narrowing
|
||||
/// is the caller's, through `CommentThreadChanges.excluding(_:)`), the same rationing (one optional
|
||||
/// sentence, chosen by precedence), the same vocabulary (`AccessibilityPhrases`), and the same
|
||||
/// posting seam (`AccessibilityAnnouncer.post`, medium priority, attributed to the key window —
|
||||
/// which for a thread change is the card window the user is looking at).
|
||||
///
|
||||
/// `nil` — silence — for a reload that changed nothing in the thread, which is the overwhelming
|
||||
/// majority of them, and for one whose every change the app itself wrote.
|
||||
public static func commentSpeech(for changes: CommentThreadChanges, onCard title: String?) -> String? {
|
||||
guard !changes.isEmpty else { return nil }
|
||||
return AccessibilityPhrases.commentsChanged(changes, onCard: title)
|
||||
}
|
||||
|
||||
/// A standing condition that was not there before and is now — announced with the banner row's
|
||||
/// own label, so the sentence a VoiceOver user hears is the sentence the strip is showing.
|
||||
private static func raisedCondition(_ facts: ReloadFacts) -> String? {
|
||||
|
||||
Reference in New Issue
Block a user