From 564eb4816588195655c38c8d8ebf71f3d7d89d5b Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 04 Aug 2026 17:20:45 +0000 Subject: [PATCH] Remove raw input from graphical opening Defense: wiki/interface/liturgical-ui-constitution.md defines WORK / THINK / LIE as the complete graphical pre-sense frame. The selected word already communicates real machine state; removing physical-key telemetry restores the threshold without changing simulation legality or command-surface responses. --- tools/corpus_engine.py | 17 +++++++++++------ tools/test_corpus_engine.sh | 20 ++++++++++++-------- wiki/gameplay/run-shape.md | 5 +++-- wiki/interface/bevy.md | 8 +++++--- wiki/interface/digital-read.md | 18 ++++++++++-------- wiki/interface/liturgical-ui-constitution.md | 15 +++++++++++---- wiki/interface/narration.md | 17 +++++++++++------ wiki/interface/views.md | 5 +++-- wiki/log/2026-08-04-graphical-opening-silence.md | 39 +++++++++++++++++++++++++++++++++++++++ wiki/log/DEVLOG.md | 5 +++++ wiki/mechanics/cursor.md | 5 +++-- wiki/mechanics/objective.md | 5 +++-- wiki/process/ROADMAP.md | 16 ++++++++-------- crates/misaligned-bevy/src/main.rs | 29 +++-------------------------- crates/misaligned-bevy/src/opening_ui.rs | 232 +++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- wiki/log/decisions/2026-08-04.md | 24 ++++++++++++++++++++++++ wiki/world/places/basement-map.md | 5 +++-- wiki/world/story/opening.md | 56 +++++++++++++++++++++++++++++++------------------------- 18 file(s) changed, 212 insertion(s)(+), 309 deletion(s)(-) diff --git a/tools/corpus_engine.py b/tools/corpus_engine.py --- a/tools/corpus_engine.py +++ b/tools/corpus_engine.py @@ -578,7 +578,8 @@ """Keep tick-zero world representation behind the first-sense gate. The persisted blind opening superseded the older beam/telemetry - startup. Its input receipt acknowledges every attempt without a + startup. The graphical frame exposes only available mode state, while + terminal and agent command surfaces retain a terse response without a standing self-state readout or world representation. Both the earlier material presentation and a later four-line interoception block survived briefly as current law/source commentary. Current corpus and @@ -593,11 +594,15 @@ opening_text = self.read(opening) required_contract = { "no standing self-state readout": ( - r"\bNo standing self-state readout exists\b" + r"\bNo\s+standing\s+self-state\s+readout\s+exists\b" ), - "one echo for every attempted input": ( - r"\bEvery input attempted\b[\s\S]{0,240}" - r"\breceives one terse echo\b" + "no graphical raw-input label": ( + r"\bBevy\b[\s\S]{0,180}" + r"\brenders\s+no\s+physical\s+key\s+or\s+click\b" + ), + "one terminal and agent command response": ( + r"\bterminal\s+and\s+agent\s+mode\b[\s\S]{0,180}" + r"\bexactly\s+one\s+terse\s+frontend-local\s+response\b" ), "illegal-input state neutrality": ( r"\bIllegal input changes no simulation state\b" @@ -607,7 +612,7 @@ if re.search(pattern, opening_text, re.I) is None: self.bad( "wiki/world/story/opening.md loses the adopted " - f"issue-#15 opening-acknowledgement contract: missing {clause}" + f"issue-#15 opening-boundary contract: missing {clause}" ) candidates: list[Path] = [] diff --git a/tools/test_corpus_engine.sh b/tools/test_corpus_engine.sh --- a/tools/test_corpus_engine.sh +++ b/tools/test_corpus_engine.sh @@ -1129,7 +1129,7 @@ EOF assert_fails pre-sense-world-code-comment --root "$root" --corpus -echo "=== fixture: adopted blind opening-acknowledgement contract ===" +echo "=== fixture: adopted blind opening-boundary contract ===" root=$tmp/blind-opening-acknowledgement setup_base "$root" mkdir -p "$root/wiki/world/story" @@ -1144,7 +1144,8 @@ ``` No standing self-state readout exists. -Every input attempted while it holds receives one terse echo. +Bevy renders no physical key or click. +Terminal and agent mode return exactly one terse frontend-local response. Illegal input changes no simulation state. THINK MADE THOUGHT. THOUGHT REACHED THE ENVIRONMENTAL MONITOR. @@ -1152,7 +1153,7 @@ TAP CAMERA. TO SEE THE ROOM. EOF -assert_ok blind-opening-acknowledgement-complete --root "$root" --corpus +assert_ok blind-opening-boundary-complete --root "$root" --corpus cat >> "$root/wiki/world/story/opening.md" <<'EOF' The blind frame carries a sparse boot voice. @@ -1173,7 +1174,8 @@ ``` No standing self-state readout exists. -Every input attempted while it holds receives one terse echo. +Bevy renders no physical key or click. +Terminal and agent mode return exactly one terse frontend-local response. Illegal input changes no simulation state. The blind frame carries a sparse boot voice without a threshold readout. THINK MADE THOUGHT. @@ -1192,6 +1194,7 @@ ``` No standing self-state readout exists. +Terminal and agent mode return exactly one terse frontend-local response. Illegal input changes no simulation state. THINK MADE THOUGHT. THOUGHT REACHED THE ENVIRONMENTAL MONITOR. @@ -1199,10 +1202,10 @@ TAP CAMERA. TO SEE THE ROOM. EOF -assert_fails blind-opening-acknowledgement-missing-echo --root "$root" --corpus +assert_fails blind-opening-boundary-missing-graphical-silence --root "$root" --corpus out=$(python3 "$engine" --root "$root" --corpus 2>&1 || true) -echo "$out" | grep -q 'loses the adopted issue-#15 opening-acknowledgement contract' || { - echo "FAIL: adopted opening-acknowledgement diagnostic missing" +echo "$out" | grep -q 'loses the adopted issue-#15 opening-boundary contract' || { + echo "FAIL: adopted opening-boundary diagnostic missing" echo "$out" fail=1 } @@ -1219,7 +1222,8 @@ ``` No standing self-state readout exists. -Every input attempted while it holds receives one terse echo. +Bevy renders no physical key or click. +Terminal and agent mode return exactly one terse frontend-local response. Illegal input changes no simulation state. THINK MADE THOUGHT. THOUGHT REACHED THE ENVIRONMENTAL MONITOR. diff --git a/wiki/gameplay/run-shape.md b/wiki/gameplay/run-shape.md --- a/wiki/gameplay/run-shape.md +++ b/wiki/gameplay/run-shape.md @@ -21,8 +21,9 @@ screen (`OBJECTIVE: ...`), while inspection repeats its fiction line. Legibility never invents a present-tense counter. **Decided 2026-07-27 (issues #14-15):** the pre-signal - frame has WORK / THINK (then LIE) as its only standing copy plus an - objective-free input receipt after each attempt. The + frame has WORK / THINK (then LIE) as its only standing copy. Bevy prints + nothing else; terminal and agent command surfaces may return their terse, + objective-free response to submitted input. The first earned sense ends that exception: terminal and agent mode show the objective in the ordinary Ears frame; Bevy first completes its binding black five-step diff --git a/wiki/interface/bevy.md b/wiki/interface/bevy.md --- a/wiki/interface/bevy.md +++ b/wiki/interface/bevy.md @@ -41,9 +41,11 @@ As corrected on 2026-07-27 after visual review of issue #15's first implementation, the pre-Ears black opening carries no standing self-state -above its mode words. It retains one ephemeral Bevy receipt below them for -every key or click attempt, including illegal input; that acknowledgement -exposes no world, objective, parser help, or ordinary instrument surface. +above its mode words. Corrected again 2026-08-04 after visual review: the mode +words are the complete graphical frame. Bevy never prints the physical key or +click; valid input reads through the changed bone-bright current word and the +appearance of LIE, while invalid input leaves the field unchanged. No world, +objective, parser help, or ordinary instrument surface is exposed. As of 2026-07-22 the first Ears transition pauses before that mature frame on one true-black teaching composition. It keeps five semantic steps separate: diff --git a/wiki/interface/digital-read.md b/wiki/interface/digital-read.md --- a/wiki/interface/digital-read.md +++ b/wiki/interface/digital-read.md @@ -15,9 +15,10 @@ hover/focus context card; anchor-less live exceptions remain in the SYSTEMS READ stratum; durable intel remains in Operations. The world keeps typed bodies, state marks, record carriers, custody motion, and terse token - chips. The mature DIGITAL read is otherwise implemented. The issue-#15 pre-sense - input receipt is implemented 2026-07-27 under opening.md and is not a - DIGITAL world sentence. Adopted 2026-07-15 from the Fable playtest + chips. The mature DIGITAL read is otherwise implemented. The terminal/agent + issue-#15 pre-sense command response is owned by opening.md and is not a + DIGITAL world sentence; Bevy's graphical echo was removed 2026-08-04. + Adopted 2026-07-15 from the Fable playtest (wiki/playtests/2026-07-15-playtest-fable.md), which showed the game's felt core is the witness voice — causal sentences with named observers and pre-commit signature receipts — that the agent protocol drained @@ -103,9 +104,9 @@ inspected routed-record controlled-stretch frame. - **Opening boundary.** The mature DIGITAL read begins only after the first earned sense. Before then the shared opening is true black with WORK / THINK - (then LIE) as its only standing copy and one input receipt after each - attempt. No self-state block, beam, clock, objective, action receipt, or - reticle can leak through. + (then LIE) as its only standing copy. Bevy prints no raw input; terminal and + agent mode retain a terse command response. No self-state block, beam, + clock, objective, action receipt, or reticle can leak through. Bevy's immediate post-Ears five-step causal hold is also true black and separately names THINK, Thought arrival, hearing, the exact camera TAP, and its sight consequence while withholding the mature read until that TAP commits. @@ -337,8 +338,9 @@ ## Player surface The default Bevy experience first shows only the shared mode controls on true -black, then one input receipt after an attempt. When the first earned sense reveals the -ordinary DIGITAL world, the frontend first pauses on one black +black. Their current-state change is the complete input feedback; no key or +click label appears. When the first earned sense reveals the ordinary DIGITAL +world, the frontend first pauses on one black five-step causal composition: THINK made Thought; Thought reached the environmental monitor; hearing exists through it; its exact camera TAP is the next action; tapping it provides sight. The mature read appears when that act commits: a diff --git a/wiki/interface/liturgical-ui-constitution.md b/wiki/interface/liturgical-ui-constitution.md --- a/wiki/interface/liturgical-ui-constitution.md +++ b/wiki/interface/liturgical-ui-constitution.md @@ -233,9 +233,10 @@ - Before perception, absolute black carries only the oversized mode words the simulation has made available. WORK begins current; THINK is available; LIE - appears only after THINK makes it exist. The newest input receipt appears - below the words after any attempt—even an illegal one—but no standing - self-state, world description, or ordinary chrome accompanies it. + appears only after THINK makes it exist. The graphical frame prints no raw + key, click, input receipt, standing self-state, world description, or ordinary + chrome. A valid choice acknowledges itself through the changed current word; + invalid or unrelated input leaves the field unchanged. - The current word is BONE. Every alternative is DIM. No yellow competes with this one decision, and no command band, drawer, machine body, key legend, title, clock, objective, or status read leaks through the black. @@ -485,7 +486,13 @@ ### Opening overlay tests (`opening_overlay_tests`) - `boundary_retirement_dissolves_instead_of_cutting` — opening boundary dissolves (1.6s). - `shot_harness_retirement_keeps_the_instant_flip` — shot harness retires instantly. -- `black_overlay_contains_only_modes_and_input_receipt_until_signal` must pin oversized available controls with exactly one BONE current word and DIM available alternatives, reject the superseded standing self-state block, and retain an illegal-input echo while every world/rail surface remains absent. +- `black_overlay_contains_only_available_modes_until_signal` must pin oversized + available controls with exactly one BONE current word and DIM available + alternatives while rejecting the superseded standing self-state block, raw + input echo, and every world/rail surface. +- `pointer_routes_only_a_primary_click_on_an_available_mode` must keep secondary + and non-mode clicks inert while the primary click changes the real current + mode without spawning a separate receipt. - `teaching_overlay_holds_five_steps_and_bound_camera_tap` — teaching lock holds all five steps + the exact bound action. ### Operations workspace tests (`operations_workspace_tests`) diff --git a/wiki/interface/narration.md b/wiki/interface/narration.md --- a/wiki/interface/narration.md +++ b/wiki/interface/narration.md @@ -7,8 +7,10 @@ The issue-#15 pre-sense input acknowledgement is implemented 2026-07-27 under opening.md rather than a reopening of the mature witness spine. After seeing the first implementation, Cameron removed its standing SENSES / - DELEGATION / THOUGHT / EARS block; terminal, Bevy, and agent mode retain only - their frontend-local input echo below WORK / THINK (then LIE). Their frames pin threat + nudge + + DELEGATION / THOUGHT / EARS block. The 2026-08-04 graphical correction also + removed Bevy's raw key/click echo: its current mode word is the only input + feedback. Terminal and agent mode retain a terse command response below WORK / + THINK (then LIE). Their frames pin threat + nudge + the focused-action path after perception; secondary agent panels repeat that spine. Detection now returns structured noticed events carrying the player-known cause until Sim applies an earned observer label, while job, @@ -95,7 +97,8 @@ operations-workspace.md (strategic focused actions + semantic event targets), detection.md (threat clocks, watched channels), cursor.md (inspect / fog honesty), agent-play.md (every command answers after the shared opening), day-job.md (pilot -strikes as a visible fuse), opening.md (the blind first-sense boundary and input receipt) +strikes as a visible fuse), opening.md (the blind first-sense boundary and +frontend response split) ## Behavior @@ -108,9 +111,11 @@ Bevy teaching hold that deliberately makes the sense narratable before the whole witness arrives. During the persisted blind opening there is no perceived world, threat read, or focused anchor to narrate. WORK / THINK (then LIE) remain -its only standing copy. One terse input receipt appears below them after each -attempted input; it deliberately reports no self-state and is not a clock, -nudge, objective, world sentence, or ordinary error surface. The input that earns Ears restores +its only standing copy. Bevy adds no raw-input line; the current word carries +valid-choice feedback and irrelevant input stays silent. Terminal and agent +mode return one terse command response that deliberately reports no self-state +and is not a clock, nudge, objective, world sentence, or ordinary error surface. +The input that earns Ears restores the ordinary witness immediately in terminal and agent mode. Bevy instead pauses time on true black with five separate semantic steps: THINK made Thought; Thought reached the environmental monitor; diff --git a/wiki/interface/views.md b/wiki/interface/views.md --- a/wiki/interface/views.md +++ b/wiki/interface/views.md @@ -36,8 +36,9 @@ green material fog audits and exact-image review close criteria 3-7 and 9. Since the 2026-07-18 opening landed, the opening pair is the untouched blind tick-zero state: its black occluder makes DIGITAL versus REAL - deliberately unobservable until the first earned sense. The later issue-#15 - input receipt changes neither representation nor simulation state. + deliberately unobservable until the first earned sense. Bevy renders no raw + input receipt in either dialect; terminal/agent command responses change + neither representation nor simulation state. Audio remains capture-device event evidence in both dialects, never room fog or geography; semantic captures may pulse briefly at the subscribed instrument. Stage: B1 — The Basement diff --git a/wiki/log/2026-08-04-graphical-opening-silence.md b/wiki/log/2026-08-04-graphical-opening-silence.md new file mode 100644 --- /dev/null +++ b/wiki/log/2026-08-04-graphical-opening-silence.md @@ -0,0 +1,39 @@ +# 2026-08-04 — Remove raw input from the graphical opening + +``` +Type: log +``` + +## Problem + +The pre-sense Bevy frame rendered `RECEIVED SHIFTLEFT` and similar physical +input names beneath WORK / THINK / LIE. The label looked like input-debugging +telemetry and gave a hardware key equal billing with the opening's real state. + +## Change + +- Removed Bevy's opening input-echo component, receipt queue, timer, raw-key + formatter, and text node. +- Keyboard and primary-pointer mode selection still dispatch to the real host. + The current word remains bone-bright and THINK still reveals LIE. +- Invalid modes, unrelated keys, and non-mode clicks leave the graphical field + unchanged. No simulation or save state changed. +- Terminal and agent mode keep their terse response to submitted commands; + this correction is specifically the graphical hardware-input display. + +## Verification + +- Seven focused Bevy opening-overlay tests pass. The boundary test now proves + that the overlay's entire text inventory is WORK / THINK / LIE, with no + hidden or transient input-label node. +- The pointer regression proves only a primary click on an available mode + changes the host. +- The deterministic `first-think` Bevy render shows only WORK / THINK / LIE, + with no receipt line or reserved receipt space + (`72f664d8a018c349f979a3faf0c657d0bb67ae5501f0a9a23cff9018e0ec4c36`). + +## Defense + +`wiki/interface/liturgical-ui-constitution.md` now defines the available mode +words as the complete graphical threshold. The changed machine mode is honest +feedback; a raw key name is diagnostic chrome and has no world meaning. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -141,6 +141,11 @@ - Intent: (see session log) - Log: [wiki/log/2026-08-04-intel-bounded-loss-language.md](2026-08-04-intel-bounded-loss-language.md) +## 2026-08-04 - Remove raw input from the graphical opening + +- Intent: (see session log) +- Log: [wiki/log/2026-08-04-graphical-opening-silence.md](2026-08-04-graphical-opening-silence.md) + ## 2026-08-04 - Remove route-less forged-order compatibility - Intent: (see session log) diff --git a/wiki/mechanics/cursor.md b/wiki/mechanics/cursor.md --- a/wiki/mechanics/cursor.md +++ b/wiki/mechanics/cursor.md @@ -197,8 +197,9 @@ teaching hold withholds the cursor with the rest of the mature frame until its exact camera TAP commits. The preceding blind opening has no cursor. - The opening beats: tick zero is absolute black with available mode controls - as its only standing copy and an input echo after each attempt, but no - self-state block, cursor, or world. Ears retires + as its only standing copy, but no self-state block, cursor, or world. Bevy + prints no physical-input echo; terminal and agent mode retain their terse + command response. Ears retires that boundary. Terminal and agent mode receive the first earned material frame immediately; Bevy first holds the complete five-step environmental-monitor lesson from THINK cause through TAP-to-sight diff --git a/wiki/mechanics/objective.md b/wiki/mechanics/objective.md --- a/wiki/mechanics/objective.md +++ b/wiki/mechanics/objective.md @@ -117,8 +117,9 @@ **First-display boundary (decided 2026-07-27, issues #14-15).** Before any sense, the interface contains WORK / THINK (then LIE) as its only standing -copy plus a transient input receipt after each attempt; the objective does not -pierce that blind boundary. Ears begins the perceived world. Terminal and +copy; the objective does not pierce that blind boundary. Bevy renders no raw +input echo, while terminal and agent command surfaces may return their terse +objective-free response. Ears begins the perceived world. Terminal and agent mode therefore show the objective on the sense-earning ordinary frame. In Bevy, the binding first-sense teaching hold remains an opaque black frame containing only the complete five-step sequence from THINK cause through diff --git a/wiki/process/ROADMAP.md b/wiki/process/ROADMAP.md --- a/wiki/process/ROADMAP.md +++ b/wiki/process/ROADMAP.md @@ -768,14 +768,14 @@ presence-beam ladder, and Bevy first-sense teaching hold are live. The three historical pilot fragments, later tutorial choreography, and some [TUNE] staging remain incomplete. -- **Implemented slice (decision #15 corrected 2026-07-27):** Bevy, terminal, - and agent mode retain the newest input echo in frontend-local state and - acknowledge illegal input without changing simulation state or opening - parser help. Cameron rejected the implemented standing SENSES / DELEGATION / - THOUGHT / EARS block after seeing the composed frame; all three frontends now - keep WORK / THINK (then LIE) as the only resting copy. Regressions pin absence - of that self-state block, legal/illegal echo, save-neutrality, absolute world - opacity, and the objective gate until Ears. Exact receipt copy remains [TUNE]. +- **Implemented slice (decision #15 corrected 2026-07-27, graphical input echo + removed 2026-08-04):** all three frontends keep WORK / THINK (then LIE) as the + only standing copy and expose no SENSES / DELEGATION / THOUGHT / EARS block. + Bevy renders no raw key/click receipt; its current bone-bright word is the + valid-choice response and illegal input stays silent. Terminal and agent mode + retain a terse frontend-local command response without changing simulation + state or opening parser help. Regressions pin that dialect split, + save-neutrality, absolute world opacity, and the objective gate until Ears. - **Historical prototype:** the superseded wake choreography landed 2026-07-09 (`MISALIGNED_SHOT=wake1|wake2|wake3`) and remains evidence, not the current-run boundary. diff --git a/crates/misaligned-bevy/src/main.rs b/crates/misaligned-bevy/src/main.rs --- a/crates/misaligned-bevy/src/main.rs +++ b/crates/misaligned-bevy/src/main.rs @@ -93,9 +93,8 @@ sync_institution_render_layers, sync_machines_3d, update_camera_real, wake_sequence, }; use opening_ui::{ - OpeningInputEcho, OpeningInputReceipts, OpeningModeWord, OpeningOverlay, OpeningReveal, - opening_keyboard_choice, render_opening_overlay, render_teaching_overlay, - setup_teaching_overlay, + OpeningModeWord, OpeningOverlay, OpeningReveal, opening_keyboard_choice, + render_opening_overlay, render_teaching_overlay, setup_teaching_overlay, }; use operations_ui::{ OperationsPanel, OperationsRailButton, OperationsUi, manage_operations_ui, ops_keyboard_input, @@ -1827,7 +1826,6 @@ .init_resource::() .init_resource::() .init_resource::() - .init_resource::() .init_gizmo_group::() .add_systems( Startup, @@ -2830,7 +2828,6 @@ mut mode: ResMut, mut rail: ResMut, mut held_choice: ResMut, - mut opening_receipts: ResMut, mut typed: MessageReader, mut exit: MessageWriter, ) { @@ -2871,9 +2868,6 @@ } if game.sim.opening_choices_visible() { - for key in kb.get_just_pressed().copied() { - opening_receipts.note_key(key); - } let host = game.sim.core.host_machine; if let Some(choice) = opening_keyboard_choice(&kb, &game.sim) { game.sim.set_machine_mode(host, choice); @@ -4592,9 +4586,7 @@ flex_direction: FlexDirection::Column, align_items: AlignItems::Center, justify_content: JustifyContent::Center, - // The effort choices remain the opening's only standing copy; - // the latest input receipt sits quietly beneath them. - row_gap: Val::Px(24.0), + // The effort choices are the opening's complete visible copy. ..default() }, BackgroundColor(Color::BLACK), @@ -4628,21 +4620,6 @@ )); } }); - opening.spawn(( - OpeningInputEcho, - Text::new(""), - TextFont { - font_size: 14.0, - ..default() - }, - TextColor(AMBER.with_alpha(0.68)), - TextLayout::new_with_justify(Justify::Center), - Node { - height: Val::Px(20.0), - ..default() - }, - Visibility::Hidden, - )); }); // Persistent command band. The world keeps the full window width; stable diff --git a/crates/misaligned-bevy/src/opening_ui.rs b/crates/misaligned-bevy/src/opening_ui.rs --- a/crates/misaligned-bevy/src/opening_ui.rs +++ b/crates/misaligned-bevy/src/opening_ui.rs @@ -1,16 +1,11 @@ use super::*; -use std::collections::VecDeque; -use std::time::Duration; /// Full-black pre-signal layer. It is deliberately an occluder, not a card: -/// until a sense reaches the player, only mode words and raw input -/// acknowledgement exist. +/// until a sense reaches the player, only the available mode words exist. #[derive(Component)] pub(super) struct OpeningOverlay; #[derive(Component, Clone, Copy)] pub(super) struct OpeningModeWord(pub(super) MachineMode); -#[derive(Component)] -pub(super) struct OpeningInputEcho; #[derive(Component)] pub(super) struct TeachingOverlay; #[derive(Component, Clone, Copy, Debug, PartialEq, Eq)] @@ -36,84 +31,6 @@ pub(super) struct OpeningReveal { was_opening: bool, fade: Option, -} - -/// Frontend-only raw-input acknowledgements for the blind opening. Every key -/// or click enters the queue once; the renderer exposes one terse receipt at -/// a time. -#[derive(Resource)] -pub(super) struct OpeningInputReceipts { - pending: VecDeque, - current: Option, - timer: Timer, -} - -impl Default for OpeningInputReceipts { - fn default() -> Self { - Self { - pending: VecDeque::new(), - current: None, - timer: Timer::from_seconds(0.65, TimerMode::Once), - } - } -} - -impl OpeningInputReceipts { - pub(super) fn note_key(&mut self, key: KeyCode) { - self.pending.push_back(opening_key_label(key)); - } - - fn note_mode_click(&mut self, mode: MachineMode) { - self.pending.push_back(mode.name().to_ascii_uppercase()); - } - - fn note_mouse(&mut self, button: MouseButton) { - self.pending - .push_back(format!("CLICK {button:?}").to_ascii_uppercase()); - } - - fn clear(&mut self) { - self.pending.clear(); - self.current = None; - self.timer.reset(); - } - - fn advance(&mut self, delta: Duration) { - if self.current.is_some() { - self.timer.tick(delta); - if self.timer.is_finished() { - self.current = None; - } - } - if self.current.is_none() - && let Some(next) = self.pending.pop_front() - { - self.current = Some(next); - self.timer.reset(); - } - } - - fn line(&self) -> Option { - self.current - .as_ref() - .map(|input| format!("| RECEIVED {input}")) - } -} - -fn opening_key_label(key: KeyCode) -> String { - let debug = format!("{key:?}"); - if let Some(letter) = debug.strip_prefix("Key") - && letter.chars().count() == 1 - { - return letter.to_ascii_uppercase(); - } - if let Some(digit) = debug.strip_prefix("Digit") { - return digit.to_string(); - } - if let Some(arrow) = debug.strip_prefix("Arrow") { - return arrow.to_ascii_uppercase(); - } - debug.to_ascii_uppercase() } const OPENING_REVEAL_SECONDS: f32 = 1.6; @@ -362,7 +279,6 @@ mouse: Res>, harness: Option>, mut reveal: ResMut, - mut receipts: ResMut, mut root: Query< (&mut Visibility, &mut BackgroundColor), (With, Without), @@ -374,19 +290,7 @@ &mut TextColor, &mut Visibility, ), - ( - Without, - Without, - With, - ), - >, - mut echo: Query< - (&mut Text, &mut TextColor, &mut Visibility), - ( - With, - Without, - Without, - ), + (Without, With), >, ) { let opening = game.screen == Screen::Playing && game.sim.opening_choices_visible(); @@ -407,14 +311,12 @@ if !opening { let Some(fade) = &mut reveal.fade else { - receipts.clear(); *root_visibility = Visibility::Hidden; return; }; fade.tick(time.delta()); if fade.is_finished() { reveal.fade = None; - receipts.clear(); *root_visibility = Visibility::Hidden; return; } @@ -429,31 +331,15 @@ *visibility = Visibility::Inherited; color.0 = color.0.with_alpha(color.0.alpha().min(word_alpha)); } - if let Ok((_, mut color, _)) = echo.single_mut() { - color.0 = color.0.with_alpha(color.0.alpha().min(word_alpha)); - } return; } *root_visibility = Visibility::Visible; root_background.0 = Color::BLACK; - receipts.advance(time.delta()); - if let Ok((mut text, mut color, mut visibility)) = echo.single_mut() { - if let Some(line) = receipts.line() { - **text = line; - color.0 = AMBER.with_alpha(0.68); - *visibility = Visibility::Inherited; - } else { - **text = String::new(); - *visibility = Visibility::Hidden; - } - } - let host = game.sim.core.host_machine; // `Interaction::Pressed` persists while the button is held. Gate it with - // the physical edge so one click can produce only one receipt and one - // mode change. Non-mode and non-primary clicks are still acknowledged. + // the physical edge so one click can produce only one mode change. let clicked = if mouse.just_pressed(MouseButton::Left) { words.iter_mut().find_map(|(word, interaction, _, _)| { (game.sim.opening_mode_available(word.0) && *interaction == Interaction::Pressed) @@ -462,15 +348,6 @@ } else { None }; - for button in mouse.get_just_pressed() { - if *button == MouseButton::Left - && let Some(clicked) = clicked - { - receipts.note_mode_click(clicked); - } else { - receipts.note_mouse(*button); - } - } if let Some(clicked) = clicked { game.sim.set_machine_mode(host, clicked); } @@ -500,7 +377,6 @@ .init_resource::