Skip to content

refactor: unify debugger run-state + execution controls into one always-visible bar#168

Merged
stid merged 8 commits into
masterfrom
refactor/debugger-exec-bar
May 25, 2026
Merged

refactor: unify debugger run-state + execution controls into one always-visible bar#168
stid merged 8 commits into
masterfrom
refactor/debugger-exec-bar

Conversation

@stid

@stid stid commented May 25, 2026

Copy link
Copy Markdown
Owner

Problem

The debugger was impossible to read at a glance: the word "Status" labelled three unrelated things — run-state (RUNNING/PAUSED), the active CPU engine (WASM/JS), and the CPU flag register — all as similar coloured badges. And run/pause/step/reset controls were duplicated across four places (Overview card, Disassembly toolbar, the dead CompactExecutionControls, and the left CRT panel) with inconsistent labels/styling. Paused was styled like a warning (amber), reading as an error.

Approach (Spec-Driven Development)

Built via SDD (spec → plan → tasks → failing tests → red-green → audit; artifacts local in docs/specs/debugger-exec-bar/).

  • One always-visible execution bar (ExecutionControlsCluster) in the right-panel tab row — visible on every tab (Info/Inspector/Debugger). It is the single home for run/pause/step/reset + the engine switch, and owns the one global F5/F10 listener.
  • RunStateBadge — run-state by three redundant channels (word + glyph + colour), never colour-only; role="status" aria-live. Paused is info blue, not warning amber — a breakpoint pause is intentional, not a fault. Shows the halt address when paused.
  • "Status" no longer means three things: the engine panel label became "Active:", the CPU flags panel header became "Flags:".
  • Left CRT panel is settings-only now (Save/Load + Backspace/Timing); run/pause/reset/engine moved to the bar.
  • Removed the duplicate controls + run-state badges from the Disassembly toolbar (keeps →PC/F8/F9 navigation), deleted the superseded ExecutionControls and the dead CompactExecutionControls.

Tests

One failing test per acceptance criterion (AC-1..AC-11) written first, then driven to green. Existing Actions/AppContent/DebuggerLayout/Disassembler tests updated for the relocated controls. yarn test:ci (eslint + tsc + vitest): 689 passed / 19 skipped. Cross-path audit: PASS (every AC bound to its handler file + an AC-N (RENDER) test).

Visual verification (browser)

  • Running: green ▶ RUNNING, Step disabled, Pause shown.
  • Paused: blue ⏸ PAUSED $0000, Step enabled, Run shown.
  • Bar visible on the Info tab (always-visible); engine JS/WASM switch present, WASM highlighted.
  • Fixed an overflow where the bar clipped at narrow widths — the row now wraps.

Version bumped 4.47.0 → 4.48.0.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Consolidated execution controls into a single execution cluster and added a run-state badge.
  • UI Improvements

    • Actions/settings panel simplified to backspace, cycle-accurate timing, save/load state.
    • Debugger overview reworked to show "Flags" group; disassembly UX now uses resize-aware rendering and reduced execution controls (→PC, F8, F9).
  • Tests

    • Test suites added/updated to match the simplified UI and new cluster/badge.
  • Version

    • Updated to 4.48.0

Review Change Stack

stid and others added 5 commits May 24, 2026 20:45
… 4.48.0

Phase 1-4 of Spec-Driven Development for unifying the debugger's run-state
indicator and execution controls. Adds docs/specs/debugger-exec-bar/{spec,plan,
tasks}.md and one failing test per acceptance criterion (AC-1..AC-11) ahead of
implementation. Version bumped 4.47.0 -> 4.48.0 (first commit of branch).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single glanceable run-state indicator: word + non-colour glyph + colour, paused
uses the info token (distinct from warning/error), role=status aria-live, halt
address when paused. Export ExecutionState for the prop type (plan amendment).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single execution-control surface: run-state badge + Step/Run-Pause/Reset + engine
switch, one global F5/F10 listener. Reset = keyDown('Tab'). Engine labelled
'Engine:' (distinct from run-state).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- AC-9: Actions becomes settings-only (run/pause/reset/engine removed).
- AC-7: engine panel relabelled (Active:, not Status:); CPU flags relabelled Flags.
- Mount the always-visible ExecutionControlsCluster in the right-panel tab row;
  it owns run/pause/step/reset + engine switch on every tab.
- Strip the duplicate controls + run-state badge from the Disassembly toolbar
  (keeps →PC/F8/F9 navigation only) and delete the dead CompactExecutionControls.
- Delete ExecutionControls (superseded by the bar); remove its Overview card.
- Update existing Actions/AppContent/DebuggerLayout/Disassembler tests for the
  relocated controls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The tab row + run-state badge + Step/Run-Pause/Reset + engine switch overflowed
the right edge at common widths (Reset clipped, engine switch off-screen). Make
the tab row and the cluster flex-wrap so the bar drops to its own line when
space is tight (still inline at wide widths). Verified in-browser: running
(green ▶) and paused (blue ⏸ $addr) states both fully visible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: aaae4247-4600-4433-8235-807d9deb8126

📥 Commits

Reviewing files that changed from the base of the PR and between e0cdf2b and c669d05.

📒 Files selected for processing (2)
  • src/components/ExecutionControlsCluster.tsx
  • src/components/__tests__/Actions-debugger-exec-bar.vitest.test.tsx

📝 Walkthrough

Walkthrough

Execution and engine controls were moved into ExecutionControlsCluster and RunStateBadge; Actions became a left-side settings panel (backspace, cycle timing, save/load). Disassembler and DebuggerLayout removed inline execution UI; EmulationContext now exports ExecutionState and AppContent integrates the new cluster and navigation state.

Changes

Execution Controls Refactoring

Layer / File(s) Summary
Actions component narrowed to settings-only panel
src/components/Actions.tsx, src/components/__tests__/Actions.vitest.test.tsx, src/components/__tests__/Actions-debugger-exec-bar.vitest.test.tsx
ActionsProps removes execution and engine props; Actions renders SUPPORT BACKSPACE, CYCLE TIMING, SAVE STATE, and LOAD STATE only. Tests assert execution/engine UI absence and hidden file input attributes.
ExecutionControlsCluster and RunStateBadge
src/components/ExecutionControlsCluster.tsx, src/components/RunStateBadge.tsx, src/components/__tests__/ExecutionControlsCluster-debugger-exec-bar.vitest.test.tsx, src/components/__tests__/RunStateBadge-debugger-exec-bar.vitest.test.tsx
New ExecutionControlsCluster provides Step/Run-Pause/Reset and engine switching, with F5/F10 global key handling and reset via workerManager.keyDown('Tab'). RunStateBadge presents execution state and optional halt address with accessibility attributes. Tests validate state-driven labels, action dispatch, and single control cluster rendering.
AppContent integration of execution controls and navigation
src/components/AppContent.tsx, src/components/__tests__/AppContent.vitest.test.tsx
AppContent imports ExecutionControlsCluster, removes pause/resume wiring from Actions, refactors handleEngineSwitch, and lifts pendingNavigation state/handlers for breakpoint/run-to-cursor navigation. Tests expand EmulationContext mock (executionState, currentPC, step) and adjust hidden-input/paste/mobile-input test wiring.
DebuggerLayout CPU State restructuring
src/components/DebuggerLayout.tsx, src/components/__tests__/DebuggerLayout.vitest.test.tsx, src/components/__tests__/DebuggerLayout-debugger-exec-bar.vitest.test.tsx
DebuggerLayout no longer renders inline execution controls, refactors CPU State into a "Flags" group with per-flag styling and reorganized register/cycles/stack JSX. Tests assert "Flags" label and address-state persistence across tabs.
DisassemblerPaginated: navigation-only, ResizeObserver
src/components/DisassemblerPaginated.tsx, src/components/__tests__/DisassemblerPaginated.vitest.test.tsx
Adds ResizeObserver/debounced row measurement, tightens operand typing/address math, emits operandType only when present, refactors run-to-cursor cleanup and paused debug polling, and removes step/run/pause/reset UI leaving only →PC and F8/F9 shortcuts. Tests focus on memory/disassembly/navigation/breakpoint behaviors.
EngineSwitcher label and version update
src/components/EngineSwitcher.tsx, src/components/__tests__/EngineSwitcher-debugger-exec-bar.vitest.test.tsx, src/version.ts
EngineSwitcher presents engine badge label as "Active:" (formerly "Status:") and APP_VERSION updated to 4.48.0. Tests verify reserved "Status" usage.
EmulationContext: export ExecutionState, reorganize events
src/contexts/EmulationContext.tsx
Exports ExecutionState type and reorganizes the context events section; EmulationProvider refactors subscription/handler code while preserving observable behavior.

Sequence Diagram(s)

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly Related PRs

  • stid/Apple1JS#156: Modifies engine-switch behavior and related UI near Actions/AppContent changes.
  • stid/Apple1JS#155: Related edits to Actions.tsx styling and UI tokenization that touch the same component surface.

Poem

🐰 I hopped through props and buttons wide,
Pulled execution out to stand beside,
Settings stayed gentle on the left so neat,
A cluster hums where run and engines meet,
Rebuilt and tidy — now the UI's fleet.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically describes the main architectural change: consolidating debugger run-state and execution controls into a unified, always-visible bar component.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/debugger-exec-bar

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/components/__tests__/Actions-debugger-exec-bar.vitest.test.tsx (1)

12-29: ⚡ Quick win

Remove the unknown double-cast so this test enforces the real prop contract.

Line 29 bypasses TypeScript checks and lets removed props linger, so this test won’t catch ActionsProps drift.

Proposed cleanup
-const props = {
-    onReset: vi.fn(),
-    onBS: vi.fn(),
-    supportBS: true,
-    onSaveState: vi.fn(),
-    onLoadState: vi.fn(),
-    onPauseResume: vi.fn(),
-    isPaused: false,
-    onRefocus: vi.fn(),
-    onCycleAccurateTiming: vi.fn(),
-    cycleAccurateTiming: true,
-    currentEngine: 'WASM',
-    wasmAvailable: true,
-    isSwitchingEngine: false,
-    onEngineSwitch: vi.fn(),
-} as unknown as React.ComponentProps<typeof Actions>;
+const props: React.ComponentProps<typeof Actions> = {
+    onBS: vi.fn(),
+    supportBS: true,
+    onSaveState: vi.fn(),
+    onLoadState: vi.fn(),
+    onRefocus: vi.fn(),
+    onCycleAccurateTiming: vi.fn(),
+    cycleAccurateTiming: true,
+};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/__tests__/Actions-debugger-exec-bar.vitest.test.tsx` around
lines 12 - 29, The test currently double-casts the props object with "as unknown
as React.ComponentProps<typeof Actions>", which bypasses TypeScript checks and
allows stale/removed props to remain; remove the "as unknown as ..." double-cast
so the local props variable is typed directly as React.ComponentProps<typeof
Actions> (or replace the literal with a factory/helper that returns a
correctly-typed ActionsProps), update the props object to satisfy the real
Actions prop type (remove any props no longer in ActionsProps and add required
ones), and ensure the test compiles against the actual Actions prop contract
instead of circumventing it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/__tests__/DebuggerLayout-debugger-exec-bar.vitest.test.tsx`:
- Around line 50-52: Add an explicit negative assertion to the AC-7 test that
checks the flag register: after the existing
expect(screen.getByText(/Flags/i)).toBeInTheDocument(); add an assertion using
screen.queryByText(/Status/i) and expect(...).not.toBeInTheDocument() so the
test also fails if the old "Status" label reappears; update the test block (the
test containing the Flags assertion) to include this negative check.

In `@src/components/ExecutionControlsCluster.tsx`:
- Around line 42-54: The global keydown handler in ExecutionControlsCluster.tsx
(inside the useEffect's onKeyDown) prevents default but doesn't stop
propagation, so handled shortcuts ('F10', ' ' (Space) when isPaused, and 'F5')
still reach AppContent's hidden-input and get forwarded to
workerManager.keyDown; update onKeyDown to call e.stopPropagation() (or
e.stopImmediatePropagation()) after e.preventDefault() for those handled
branches (the ones that call step() or handleRunPause()) so the key events do
not propagate to AppContent/workerManager.

In `@src/version.ts`:
- Line 1: The APP_VERSION constant currently set to '4.48.0' should be bumped
only at the patch level for this refactor PR—update the APP_VERSION export
(symbol: APP_VERSION) to the next patch (e.g., '4.48.1') so the version reflects
a patch increment rather than a minor/major change.

---

Nitpick comments:
In `@src/components/__tests__/Actions-debugger-exec-bar.vitest.test.tsx`:
- Around line 12-29: The test currently double-casts the props object with "as
unknown as React.ComponentProps<typeof Actions>", which bypasses TypeScript
checks and allows stale/removed props to remain; remove the "as unknown as ..."
double-cast so the local props variable is typed directly as
React.ComponentProps<typeof Actions> (or replace the literal with a
factory/helper that returns a correctly-typed ActionsProps), update the props
object to satisfy the real Actions prop type (remove any props no longer in
ActionsProps and add required ones), and ensure the test compiles against the
actual Actions prop contract instead of circumventing it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 94e0c9bf-738a-474a-aeab-f324b8b34a2a

📥 Commits

Reviewing files that changed from the base of the PR and between 9cd2511 and 7362027.

📒 Files selected for processing (21)
  • src/components/Actions.tsx
  • src/components/AppContent.tsx
  • src/components/CompactExecutionControls.tsx
  • src/components/DebuggerLayout.tsx
  • src/components/DisassemblerPaginated.tsx
  • src/components/EngineSwitcher.tsx
  • src/components/ExecutionControls.tsx
  • src/components/ExecutionControlsCluster.tsx
  • src/components/RunStateBadge.tsx
  • src/components/__tests__/Actions-debugger-exec-bar.vitest.test.tsx
  • src/components/__tests__/Actions.vitest.test.tsx
  • src/components/__tests__/AppContent.vitest.test.tsx
  • src/components/__tests__/CompactExecutionControls.vitest.test.tsx
  • src/components/__tests__/DebuggerLayout-debugger-exec-bar.vitest.test.tsx
  • src/components/__tests__/DebuggerLayout.vitest.test.tsx
  • src/components/__tests__/DisassemblerPaginated.vitest.test.tsx
  • src/components/__tests__/EngineSwitcher-debugger-exec-bar.vitest.test.tsx
  • src/components/__tests__/ExecutionControlsCluster-debugger-exec-bar.vitest.test.tsx
  • src/components/__tests__/RunStateBadge-debugger-exec-bar.vitest.test.tsx
  • src/contexts/EmulationContext.tsx
  • src/version.ts
💤 Files with no reviewable changes (3)
  • src/components/tests/CompactExecutionControls.vitest.test.tsx
  • src/components/CompactExecutionControls.tsx
  • src/components/ExecutionControls.tsx

Comment on lines +42 to +54
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if ((e.key === 'F10' || e.key === ' ') && isPaused) {
e.preventDefault();
step();
} else if (e.key === 'F5') {
e.preventDefault();
handleRunPause();
}
};
window.addEventListener('keydown', onKeyDown);
return () => window.removeEventListener('keydown', onKeyDown);
}, [isPaused, step, handleRunPause]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the file and print the relevant region with line numbers
ls -la src/components/ExecutionControlsCluster.tsx
sed -n '1,140p' src/components/ExecutionControlsCluster.tsx | cat -n

# Search for the hidden input / AppContent forwarding path for keydown -> workerManager.keyDown
rg -n "keyDown|keydown" src/components src | head -n 80
rg -n "workerManager\.keyDown|keyDown\(" src | head -n 80
rg -n "ExecutionControlsCluster" -S src | head -n 40

# Also locate AppContent and any key forwarding listeners/handlers
rg -n "AppContent" src | head -n 80
rg -n "hidden.*input|input.*hidden" src | head -n 80

Repository: stid/Apple1JS

Length of output: 25875


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant portion of the component (with line numbers)
echo "=== src/components/ExecutionControlsCluster.tsx (first 200 lines) ==="
sed -n '1,200p' src/components/ExecutionControlsCluster.tsx | cat -n

# Trace keydown -> workerManager.keyDown and any hidden-input key forwarding
echo "=== rg keydown/keyDown in src ==="
rg -n "keydown|keyDown" src | head -n 200

echo "=== rg workerManager.keyDown ==="
rg -n "workerManager\.keyDown" src || true

echo "=== Locate AppContent ==="
rg -n "AppContent" src | head -n 100

echo "=== Locate hidden input ==="
rg -n "hidden.*input|input.*hidden" src | head -n 100

Repository: stid/Apple1JS

Length of output: 20684


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the keydown/hidden input handling in AppContent
echo "=== src/components/AppContent.tsx (lines ~80-200) ==="
sed -n '80,230p' src/components/AppContent.tsx | cat -n

echo "=== src/components/AppContent.tsx (lines ~230-330) ==="
sed -n '230,330p' src/components/AppContent.tsx | cat -n

echo "=== Search AppContent tests for F5/F10/Space ==="
rg -n "'F5'|\"F5\"|'F10'|\"F10\"|Space| ' ' |\" \"|\\bkey:\\s*'F5'|\\bkey:\\s*'F10'" src/components/__tests__/AppContent.vitest.test.tsx src/components/__tests__ -S || true

Repository: stid/Apple1JS

Length of output: 13237


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect WorkerManager.keyDown passthrough
echo "=== src/services/WorkerManager.ts around keyDown ==="
sed -n '160,240p' src/services/WorkerManager.ts | cat -n

# Inspect apple1 WorkerAPI keyDown mapping
echo "=== src/apple1/WorkerAPI.ts around keyDown ==="
rg -n "keyDown\\(key: string\\)" -n src/apple1/WorkerAPI.ts
sed -n '450,560p' src/apple1/WorkerAPI.ts | cat -n

# Search for handling of F5/F10/space in WorkerAPI / other key mapping
echo "=== search for F5/F10/Space in apple1 ==="
rg -n "'F5'|\"F5\"|'F10'|\"F10\"|\\bSpace\\b|key === ' '" src/apple1 | head -n 200

Repository: stid/Apple1JS

Length of output: 7619


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== rg for F5/F10/Space in AppContent tests ==="
rg -n "F5|F10|Space|' '" src/components/__tests__/AppContent.vitest.test.tsx || true

echo "=== rg for F5/F10/Space in ExecutionControlsCluster tests ==="
rg -n "F5|F10|Space|' '" src/components/__tests__/ExecutionControlsCluster-debugger-exec-bar.vitest.test.tsx || true

echo "=== Show ExecutionControlsCluster-debugger-exec-bar test file ==="
sed -n '1,220p' src/components/__tests__/ExecutionControlsCluster-debugger-exec-bar.vitest.test.tsx | cat -n

Repository: stid/Apple1JS

Length of output: 5764


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the keyboard.write implementation usage
rg -n "keyboard\\.write\\(" src/apple1 src | head -n 50

# Locate keyboard class / write method
rg -n "class .*Keyboard|function .*Keyboard|keyboard.*write|write\\(key" src/apple1 | head -n 80

# Search for explicit handling of F5/F10/space keys in apple1
rg -n "'F5'|\"F5\"|'F10'|\"F10\"|key === 'F5'|key === 'F10'|key === ' '|key === \" \"|Space" src/apple1 | head -n 200

# Also search in apple1 for 'function key' or similar
rg -n "F5|F10|function key|space" src/apple1 | head -n 200

Repository: stid/Apple1JS

Length of output: 1892


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== src/apple1/WebKeyboard.ts ==="
sed -n '1,220p' src/apple1/WebKeyboard.ts | cat -n

echo "=== src/apple1/KeyboardLogic.ts ==="
sed -n '1,220p' src/apple1/KeyboardLogic.ts | cat -n

Repository: stid/Apple1JS

Length of output: 4978


Stop Space (and handled shortcuts) from reaching the emulator keyboard input
In ExecutionControlsCluster.tsx, the global window keydown handler prevents default but doesn’t stop propagation, so AppContent’s hidden-input keydown listener still forwards the same key to workerManager.keyDown(e.key). For Space (' '), WebKeyboard.write treats single-character keys as ASCII (so it injects a space strobe into the Apple 1) while the shortcut simultaneously triggers step() when paused.

Suggested fix (capture + stopPropagation for handled shortcuts)
 useEffect(() => {
     const onKeyDown = (e: KeyboardEvent) => {
+        if (e.metaKey || e.ctrlKey || e.altKey) return;
+
         if ((e.key === 'F10' || e.key === ' ') && isPaused) {
             e.preventDefault();
+            e.stopPropagation();
             step();
         } else if (e.key === 'F5') {
             e.preventDefault();
+            e.stopPropagation();
             handleRunPause();
         }
     };
-    window.addEventListener('keydown', onKeyDown);
-    return () => window.removeEventListener('keydown', onKeyDown);
+    window.addEventListener('keydown', onKeyDown, true);
+    return () => window.removeEventListener('keydown', onKeyDown, true);
 }, [isPaused, step, handleRunPause]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ExecutionControlsCluster.tsx` around lines 42 - 54, The global
keydown handler in ExecutionControlsCluster.tsx (inside the useEffect's
onKeyDown) prevents default but doesn't stop propagation, so handled shortcuts
('F10', ' ' (Space) when isPaused, and 'F5') still reach AppContent's
hidden-input and get forwarded to workerManager.keyDown; update onKeyDown to
call e.stopPropagation() (or e.stopImmediatePropagation()) after
e.preventDefault() for those handled branches (the ones that call step() or
handleRunPause()) so the key events do not propagate to
AppContent/workerManager.

Comment thread src/version.ts
@@ -1 +1 @@
export const APP_VERSION = '4.47.0';
export const APP_VERSION = '4.48.0';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use a patch bump for this refactor PR.

For a refactor/ PR, this should be a patch increment, not minor. Update APP_VERSION from 4.47.0 to 4.47.1 (or next patch if already advanced).

As per coding guidelines: "src/version.ts: Bump src/version.ts before every PR — use patch/minor/major per branch prefix (refactor/ = patch)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/version.ts` at line 1, The APP_VERSION constant currently set to '4.48.0'
should be bumped only at the patch level for this refactor PR—update the
APP_VERSION export (symbol: APP_VERSION) to the next patch (e.g., '4.48.1') so
the version reflects a patch increment rather than a minor/major change.

…ke conflict

The always-visible bar's global keydown bound Space (when paused) to Step; Space
also propagated to the hidden input and was injected into the Apple 1. Bind only
the non-printable function keys (F10 step, F5 run/pause) and ignore modifier
combos. Add a negative 'Status' assertion to the AC-7 flags test.

Addresses CodeRabbit review on #168.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/ExecutionControlsCluster.tsx (1)

48-54: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Ignore Shift-modified shortcuts in the global key handler.

Line 48 filters meta/ctrl/alt but not shift, so Shift+F5 / Shift+F10 still trigger emulator controls. That conflicts with the stated “ignore modifier combos” behavior and can steal browser shortcuts.

Suggested patch
-            if (e.metaKey || e.ctrlKey || e.altKey) return;
+            if (e.metaKey || e.ctrlKey || e.altKey || e.shiftKey) return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ExecutionControlsCluster.tsx` around lines 48 - 54, The global
keydown handler currently ignores meta/ctrl/alt but not Shift, so add shift to
the early-modifier check: treat e.shiftKey the same as
e.metaKey/e.ctrlKey/e.altKey and return early if any modifier is present before
handling F10/F5; update the handler around the checks that call step(),
handleRunPause(), and reference isPaused so Shift+F5/Shift+F10 no longer trigger
emulator controls.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/components/ExecutionControlsCluster.tsx`:
- Around line 48-54: The global keydown handler currently ignores meta/ctrl/alt
but not Shift, so add shift to the early-modifier check: treat e.shiftKey the
same as e.metaKey/e.ctrlKey/e.altKey and return early if any modifier is present
before handling F10/F5; update the handler around the checks that call step(),
handleRunPause(), and reference isPaused so Shift+F5/Shift+F10 no longer trigger
emulator controls.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 879d9ce2-f2df-4536-a448-f2efae96f731

📥 Commits

Reviewing files that changed from the base of the PR and between 7362027 and e0cdf2b.

📒 Files selected for processing (2)
  • src/components/ExecutionControlsCluster.tsx
  • src/components/__tests__/DebuggerLayout-debugger-exec-bar.vitest.test.tsx

stid and others added 2 commits May 24, 2026 22:08
Address CodeRabbit findings on PR #168:
- ExecutionControlsCluster: bail on Shift modifier too, so Shift+F5/F10 no
  longer hijack run/pause/step (matches the "modifier combos left to
  browser/OS" intent).
- Actions-debugger-exec-bar test: type props directly as ActionsProps instead
  of an `as unknown as` double-cast, so stale execution/engine props can't
  silently linger.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant