Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/tasks/18-selection-menu.md
Fimeg d4ea575404 tasks: settle viewtop scene composition + selection design; cross-link
TASK-43 gains the §Scene composition design settled 2026-07-30: the
texture/content wall, pose (geometry, the agent's) and expose (legibility,
gate-veiled) as authority-split verbs, the selection surface as a scene element
not a layer surface, and the primary-selection/data-control source now wired
(234a631) with its owed refinements named.

TASK-18: the selection chip's Hyprland surface layer is terminal (the tap bug
is layer-shell); its backends are portable. TASK-30: viewtop's wire table is one
of its surfaces, grown by pose/expose. TASK-17: the OSK z-order this task fought
is structurally enforced under viewtop.
2026-07-30 11:36:50 -04:00

136 lines
6.8 KiB
Markdown

# TASK 18 — shell: highlight/selection action menu
**Status:** shipped-partial 2026-07-29, live on blueline. Component first;
backends light up as they earn it (doctrine §6). **Read Aloud is no longer
gated** — TTS is Qwen3-TTS on `10.10.20.123:7865`, with the old contract shimmed
on `:7863`, as of 2026-07-29.
## Shipped 2026-07-29
- **Selection detection is settled: compositor-level, no per-app hooks, no
viewtop.** blueline's Hyprland advertises `zwp_primary_selection_device_manager_v1`
and both data-control managers, so `wl-paste --primary --watch` sees every
selection. Verified firing on device. This closes the open question below.
- `services/Selection.qml` — watcher, 220 ms settle debounce (a drag fires
continuously as it grows), pointer anchor via `hyprctl cursorpos`.
**Opt-in** (`Config.options.selection.enable`, default false) because it
observes every highlight on the device including passwords; the watcher is
killed while locked and selection text is never persisted, logged or trailed.
- `modules/souveraine/selection/` — chip → action list. `WlrLayer.Overlay`,
`keyboardFocus: None`. The chip shows a **character count, never a preview**:
it floats over the app that owns the selection, so a preview both duplicates
what is on screen and creates a surface that can outlive its context.
- `services/DeviceEvidence.qml` — reports to the state machine's existing
`Request::Input { trigger }` with an `intent` label, so a new input surface is
not another isolated actor (DEVICE-STATE-MACHINE §1). **The OSK still does not
report and should**: an open keyboard is stronger presence evidence than §4's
`touch.active` +0.1. Testable hypothesis: if stevia's
`zwp_virtual_keyboard_v1` events do not reset Hyprland's idle notifier, the
screen can dim mid-sentence while typing.
**Anchoring is limited and it shaped the UX.** No Wayland protocol exposes the
selection's bounding rectangle — Android does this at the toolkit layer
(ActionMode/FloatingToolbar), Apple in-app. So "tap on the highlighted text" is
not directly detectable; we know *what* is selected, never *where*. Hence the
chip near the touch point. True selection-rect anchoring is the one thing viewtop
would buy — but only for *first-party* surfaces, where we own both ends and can
carry geometry through `wire`. Third-party opaque textures stay
pointer-hint-anchored: the texture/content wall is the Wayland contract itself,
not a gap viewtop closes. See TASK-43 §Scene composition.
**Still broken (Casey, on device 2026-07-29): the chip is not tappable or
dismissable.** It appears, and it correctly follows a new selection, but neither
the body nor the ✕ responds. Two attempts failed: a fullscreen shield with
`mask: Region { item: card }`, then a window sized to the card and moved by
margins (`147f7f2`, last state). Untested by Casey. Next step is to determine
whether the layer surface receives touch at all — the `WlrKeyboardFocus.None` +
`ExclusionMode.Ignore` combination on Overlay is the prime suspect, and DialHost
(which works) uses `OnDemand`. It also "fights a bit with Firefox's own highlight
selection stuff".
Not yet wired: both agent actions (need the side-shoot / primary-conversation
seams in `Ai.qml`) and all three reference lookups. They render with the reason
they cannot act rather than dead-tapping.
**The selection surface is being rebuilt for viewtop (TASK-43 §Scene
composition).** There it is a scene element Annie composes, not a
`WlrLayer.Overlay` client — so the tap / z-order bug above is Hyprland-terminal;
do not spend a third attempt on it. The selection *backends* (`Speech.speak`
Read Aloud, the `Ai.qml` side-shoot / primary-conversation seams, the reference
lookups) are surface-agnostic; finish those, they carry straight across to
viewtop.
**Repo:** `~/Projects/souveraine` `surfaces/quickshell` (shell surface),
plus per-action backends noted below.
## Priority order
Get the component itself solid and fluid — surface type, anchor
behavior, animation — **before** wiring any backend. The stevia keyboard
shipped glitchy because the surface work was rushed; do not repeat that.
A menu whose actions are stubs is acceptable; a janky menu is not.
## Action set
### 1. Reference / Lookup — deterministic, local/offline preferred
No agent round-trip. Whether these may *ever* touch the agent is an open
decision (below); default stance: strictly local/dumb.
- Define / Dictionary
- Thesaurus (synonyms/antonyms)
- Translate
- Search the web
- Look up (Wikipedia-style)
- Pronunciation
### 2. Agent Actions — routes through Ani, agent-context-aware
- **Talk about this** → spins off a **new side-shoot conversation**,
seeded with the selection
- **Add to our conversation** → appends to the current **primary**
conversation
- **Explain** — context-aware (uses surrounding text; needs
connectivity). Deliberately contrasts with dictionary Define.
- Summarize / simplify
- Fact-check / verify
### 3. Read Aloud — its own section
Single TTS action on the **literal selected text**; no agent ingestion.
Backend reality (verified end-to-end on device 2026-07-21): STT live
(faster-whisper, `10.10.20.19:7862`) **and TTS live** (VibeVoice,
`10.10.20.19:7861`, `POST /audio/speech` `{input, voice, model}`
mp3). The shell already has the seam: `services/Speech.qml`
(`Speech.speak(text)`) reads the who→voice mapping from souveraine
`/v1/config` and plays via mpv — the sidebar Speak button uses it, and
this menu's Read Aloud action should call the same singleton. Gate:
`Config.options.speech.tts.enable`.
### 4. Utility
- Copy / Share (system defaults)
- Add to notes/memory (bookmark into Ani's memory, **no** new
conversation)
- Create reminder/task from selection
## Open design decisions (Casey's to make, ask before building past them)
- **Flat vs progressive disclosure.** Leaning progressive: top level =
Read Aloud + Agent Actions + "More…", Reference behind the second tap
— the full set is long for a phone screen.
- **Reference actions and the agent:** strictly local/dumb, or may they
fall through to the agent? Default: never touch the agent.
- ~~**Selection-detection mechanism.**~~ **Settled 2026-07-29**: compositor-level
via primary-selection + data-control. Works everywhere, no per-app wiring.
- **Z-order / exclusion contract:** which layer the menu renders on and
how it coexists with the OSK. The stevia pill/menu layer-collision bug
(fixed by pill→`WlrLayer.Top` + oskLift, souveraine 271e4f2) is the
cautionary precedent — settle the contract before polishing visuals.
## Connects to
- TASK-17 (stevia keyboard) — shares the OSK z-order/exclusion problem.
- `services/Ai.qml` — side-shoot vs primary-conversation seams for the
Agent Actions section.
- `modules/settings/SpeechConfig.qml` / `Config.options.speech` — the
Read Aloud gate; single source of truth for both CLIs and the shell.