Skip to content

feat(actions): add dual-press control to setup View sub-modes (#540)#548

Merged
niklam merged 2 commits into
masterfrom
ir-540
May 14, 2026
Merged

feat(actions): add dual-press control to setup View sub-modes (#540)#548
niklam merged 2 commits into
masterfrom
ir-540

Conversation

@niklam
Copy link
Copy Markdown
Owner

@niklam niklam commented May 14, 2026

Related Issue

Fixes #540

What changed?

PR #541 added read-only "View …" sub-modes to the seven directional setup actions. This PR turns those View sub-modes into one-key dual-press controls: while the key shows the live telemetry value, a short press fires one direction and a long press fires the opposite — replacing two physical keys (separate Increase + Decrease) with one that also displays the value.

  • @iracedeck/deck-core — new DualPressTracker (per-context tap/long-press timing keyed by action context id). Two new global settings: dualPressThresholdMs (200–2000 ms, default 500) and dualPressDirections (tap-increases | tap-decreases, default tap-increases), with getDualPressThresholdMs() / getDualPressDirections() readers. Both are plugin-wide so the tap-vs-long-press rule stays consistent across every setup action.
  • @iracedeck/iracing-actions — each of the 7 setup actions gains a per-action dualPressEnabled opt-in (default on). When enabled, a View sub-mode records key-down in onKeyDown and dispatches the resolved direction in onKeyUp via the existing global key bindings. setup-view.ts maps each View id to its underlying adjustment mode (getAdjustmentModeForView); setup-chassis adds dispatch-only weight-jacker key bindings (no matching adjustment mode existed). Directional adjustment, toggle, and encoder behaviour are untouched.
  • @iracedeck/pi-components — new dual-press-overrides partial (the per-action enable checkbox), global threshold + directions rows in the Common Settings accordion, and a shared ird-supporting-text CSS class for help text (replaces inline styles across global-common-settings.ejs and global-flag-flash.ejs).
  • Docs / website / skill — all 7 website setup action pages document the dual-press control; tap direction and threshold are documented as plugin-wide Global Common Settings. .claude/rules/pi-templates.md documents the ird-supporting-text class.

How to test

  • pnpm install && pnpm build && pnpm test && pnpm lint:fix && pnpm format:fix — all green (3944 tests).
  • In iRacing on a Stream Deck:
    • Configure brake-bias + / - global key bindings, drop a "Setup Brakes" key, pick view-brake-bias. Verify the live value renders.
    • Tap → bias moves one way; hold ~1 s → bias moves the other way.
    • Global Common Settings → Dual-Press → Directions: switch to "Tap decreases" → directions invert on the next press.
    • Toggle the per-action Enable dual-press off → the key goes back to a read-only display; the value still updates from telemetry.
    • Global Common Settings → Long-press threshold: 250 ms / 1500 ms → the tap/long-press boundary moves accordingly.

Checklist

  • Linked to an approved issue
  • New code has unit tests
  • Changes registered in all applicable plugins (Stream Deck, Mirabox)
  • No unrelated changes included

Summary by CodeRabbit

  • New Features

    • Dual-press support for View sub-modes (short vs long press perform opposite adjustments).
    • Global dual-press settings: long-press threshold and tap-direction.
    • New Weight Jacker controls in chassis setup.
    • Per-action “Enable dual-press” toggle and UI updates to expose it.
  • Documentation

    • Shared CSS Classes doc and updated setup action guides describing dual-press.
  • Tests

    • Added/expanded test coverage for dual-press behavior and global settings validation.

Review Change Stack

PR #541 added read-only "View …" sub-modes to the seven directional setup
actions. This turns those View sub-modes into one-key dual-press controls:
while the key shows the live telemetry value, a short press fires one
direction and a long press fires the opposite — replacing two physical keys
(separate Increase + Decrease) with one that also displays the value.

- deck-core: new DualPressTracker (per-context tap/long-press timing),
  dualPressThresholdMs (200-2000 ms, default 500) and dualPressDirections
  ("tap-increases" | "tap-decreases") global settings, with
  getDualPressThresholdMs() / getDualPressDirections() readers.
- iracing-actions: each setup action gains a per-action dualPressEnabled
  opt-in (default on); View sub-modes record key-down on onKeyDown and
  dispatch the resolved direction on onKeyUp via the existing global key
  bindings. setup-view.ts maps each View id to its adjustment mode;
  setup-chassis adds dispatch-only weight-jacker key bindings.
- pi-components: dual-press opt-in partial, global threshold + directions
  rows in Common Settings, and a shared ird-supporting-text CSS class for
  help text (replaces inline styles).
- Docs/website/skill updated; tap direction and threshold are documented as
  plugin-wide Global Common Settings.
@github-actions github-actions Bot added the type: feature New feature label May 14, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62dcbe85-9480-48fb-b526-bd2bf68ce618

📥 Commits

Reviewing files that changed from the base of the PR and between 484517a and 925524c.

📒 Files selected for processing (9)
  • packages/deck-core/src/dual-press.test.ts
  • packages/iracing-actions/src/actions/setup-aero/setup-aero.test.ts
  • packages/iracing-actions/src/actions/setup-chassis/setup-chassis.test.ts
  • packages/iracing-actions/src/actions/setup-engine/setup-engine.test.ts
  • packages/iracing-actions/src/actions/setup-fuel/setup-fuel.test.ts
  • packages/iracing-actions/src/actions/setup-hybrid/setup-hybrid.test.ts
  • packages/iracing-actions/src/actions/setup-traction/setup-traction.test.ts
  • packages/website/src/content/docs/docs/actions/car-setup/setup-chassis.md
  • packages/website/src/content/docs/docs/actions/car-setup/setup-engine.md
✅ Files skipped from review due to trivial changes (2)
  • packages/website/src/content/docs/docs/actions/car-setup/setup-chassis.md
  • packages/website/src/content/docs/docs/actions/car-setup/setup-engine.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/deck-core/src/dual-press.test.ts

📝 Walkthrough

Walkthrough

Implements opt-in dual-press across directional setup actions: core tracker + global settings, PI controls and CSS, per-action opt-in and onKeyUp dispatch that maps short/long presses to increase/decrease, tests, and documentation updates.

Changes

Dual-press mode for setup actions

Layer / File(s) Summary
Core dual-press module and global settings
packages/deck-core/src/dual-press.ts, packages/deck-core/src/global-settings.ts, packages/deck-core/src/index.ts, packages/deck-core/src/dual-press.test.ts, packages/deck-core/src/global-settings.test.ts
Adds DualPressTracker, fallback constants and readers (getDualPressThresholdMs, getDualPressDirections), schema fields dualPressThresholdMs and dualPressDirections, re-exports, and tests covering tracker semantics and settings validation.
View adjustment mode helpers and weight jacker bindings
packages/iracing-actions/src/shared/setup-view.ts, packages/iracing-actions/src/actions/data/key-bindings.json
Adds adjustmentMode to VIEW_DEFS, exports getAdjustmentModeForView(), tightens renderer input types, and adds four weight-jacker key bindings to chassis mapping.
Property Inspector UI components and styling
packages/pi-components/partials/head-common.ejs, packages/pi-components/partials/dual-press-overrides.ejs, packages/pi-components/partials/global-common-settings.ejs, packages/pi-components/partials/global-flag-flash.ejs
Introduces .ird-supporting-text CSS, a dual-press-overrides partial with dualPressEnabled checkbox, and a Dual-Press block (threshold + directions) in Global Common Settings; replaces inline supporting-text styles with the class.
Setup action EJS template updates
packages/iracing-actions/src/actions/setup-{aero,brakes,chassis,engine,fuel,hybrid,traction}/*.ejs
Includes dual-press-overrides partial and refactors client-side visibility logic to hide Direction and show the dual-press toggle for view-* sub-modes; unifies change/input handlers.
Setup action dual-press implementation (all seven actions)
packages/iracing-actions/src/actions/setup-{aero,brakes,chassis,engine,fuel,hybrid,traction}/*.ts
Adds dualPressEnabled setting (boolean/string coercion, default true), per-action DualPressTracker, records keyDown when enabled, implements onKeyUp to compute tap/long outcome, maps adjustmentMode+direction to global keys, dispatches tapBinding, and conditions active binding display on dual-press state.
Test infrastructure and per-action dual-press coverage
packages/deck-core/src/simhub-service.test.ts, per-action *.test.ts files, packages/deck-core/src/dual-press.test.ts, packages/deck-core/src/global-settings.test.ts
Adds DualPressTracker tests, extends global-settings tests, and injects DualPressTracker mocks + dual-press reader stubs into all action test suites; per-action tests validate recording, dispatch, inversion, thresholds, and clearing behavior.
Documentation updates
.claude/rules/pi-templates.md, .claude/skills/iracedeck-actions/SKILL.md, packages/website/src/content/docs/docs/actions/car-setup/*.md
Documents new PI CSS classes and dual-press controls, updates skill/category descriptions and website docs for each setup action describing the Enable dual-press toggle, global Directions, and Long-press threshold (defaults and next-press behavior).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • niklam/iracedeck#547: Main PR builds directly on #547's View sub-mode infrastructure, adding dual-press interactivity and dualPressEnabled toggle to the same setup action view-mode flows.

Suggested reviewers

  • kelchm

🐰 A key now dances fast or slow,
Short tap or long? The directions flow!
One button rules them all, so neat—
Dual-press magic, quick and fleet!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% 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
Title check ✅ Passed The PR title clearly summarizes the main change: adding dual-press control to View sub-modes in setup actions, with issue reference #540.
Description check ✅ Passed The PR description follows the template, includes Related Issue (#540), detailed What changed explanation, How to test steps, and a completed checklist.
Linked Issues check ✅ Passed The PR comprehensively implements all coding requirements from issue #540: dual-press infrastructure, per-action opt-in, global settings, UI components, and comprehensive test coverage across all setup actions.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing dual-press support as specified in issue #540. Documentation, templates, and utility additions are all within scope.

✏️ 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 ir-540

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
packages/website/src/content/docs/docs/actions/car-setup/setup-chassis.md (1)

14-35: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Resolve contradictory View-mode behavior wording.

This section documents interactive dual-press View behavior, but the later Modes text still says View modes are read-only. Please align both sections so users don’t get conflicting guidance.

🤖 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 `@packages/website/src/content/docs/docs/actions/car-setup/setup-chassis.md`
around lines 14 - 35, The docs currently contradict: the "Dual-press control"
section explains interactive dual-press behavior for View modes (including the
"Enable dual-press" Property Inspector setting and the global "Dual-Press →
Directions" and "Long-press threshold (ms)") but the later "Modes" text calls
View modes read-only; update the "Modes" text to either remove the blanket
"read-only" claim or add an explicit exception clarifying that View modes are
read-only only when "Enable dual-press" is off and otherwise dispatch adjustment
bindings (e.g., Diff Preload → Diff Preload +/−, Weight Jacker bindings in
Global Settings → Setup Chassis); ensure the wording references "Enable
dual-press", "Global Common Settings → Dual-Press → Directions" and "Long-press
threshold (ms)" so readers know the toggle and global settings control
interactive behavior.
🧹 Nitpick comments (2)
packages/iracing-actions/src/actions/setup-hybrid/setup-hybrid.test.ts (1)

68-76: ⚡ Quick win

Add explicit tests for View dual-press key-up dispatch.

The mock now supports dual-press, but this suite still doesn’t assert the new onKeyUp behavior (dualPressEnabled=true dispatch, false no dispatch). Adding those cases would protect the new path from regressions.

Suggested test skeleton
+it("dispatches mapped tap/long direction on keyUp for View mode", async () => {
+  const action = new SetupHybrid();
+  const tracker = (action as any).dualPress;
+  tracker.computeOutcome.mockReturnValue("increase");
+
+  await action.onKeyUp(fakeEvent("action-1", {
+    setting: "view-mguk-deploy-mode",
+    dualPressEnabled: true,
+  }) as any);
+
+  expect(mockTapBinding).toHaveBeenCalledWith("setupHybridMgukDeployModeIncrease");
+});
+
+it("does not dispatch on keyUp when dual-press is disabled", async () => {
+  const action = new SetupHybrid();
+  await action.onKeyUp(fakeEvent("action-1", {
+    setting: "view-mguk-deploy-mode",
+    dualPressEnabled: false,
+  }) as any);
+  expect(mockTapBinding).not.toHaveBeenCalled();
+});
🤖 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 `@packages/iracing-actions/src/actions/setup-hybrid/setup-hybrid.test.ts`
around lines 68 - 76, Add explicit tests in setup-hybrid.test.ts that exercise
the onKeyUp dual-press dispatch path: use the existing MockDualPressTracker (and
helper mocks getDualPressThresholdMs/getDualPressDirections) to simulate a
computed dual-press outcome and assert that when dualPressEnabled=true the
dispatcher/action for dual-press is called on key-up, and when
dualPressEnabled=false no dual-press dispatch occurs; specifically, create two
cases that stub MockDualPressTracker.computeOutcome to return a dual-press
result and verify dispatch behavior on the component/module's onKeyUp handler,
and another case where dualPressEnabled is false to verify no dispatch.
packages/iracing-actions/src/actions/setup-fuel/setup-fuel.test.ts (1)

60-67: ⚡ Quick win

Add action-level tests for dual-press View behavior on key-up.

This suite mocks dual-press primitives but doesn’t currently assert the new onKeyUp dispatch path (enabled/disabled modes). Adding those cases will lock in the intended behavior.

Suggested test skeleton
+it("dispatches View adjustment on keyUp when dual-press is enabled", async () => {
+  const action = new SetupFuel();
+  const tracker = (action as any).dualPress;
+  tracker.computeOutcome.mockReturnValue("increase");
+
+  await action.onKeyUp(fakeEvent("action-1", {
+    setting: "view-fuel-mixture",
+    dualPressEnabled: true,
+  }) as any);
+
+  expect(mockTapBinding).toHaveBeenCalledWith("setupFuelFuelMixtureIncrease");
+});
+
+it("does not dispatch on keyUp when dual-press is disabled", async () => {
+  const action = new SetupFuel();
+  await action.onKeyUp(fakeEvent("action-1", {
+    setting: "view-fuel-mixture",
+    dualPressEnabled: false,
+  }) as any);
+  expect(mockTapBinding).not.toHaveBeenCalled();
+});
🤖 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 `@packages/iracing-actions/src/actions/setup-fuel/setup-fuel.test.ts` around
lines 60 - 67, Add tests in setup-fuel.test.ts that assert the action dispatch
path on key-up depending on dual-press mode: when getDualPressDirections is
mocked to "tap-increases" simulate a keyUp through the action's handler (use the
existing MockDualPressTracker and set computeOutcome to return the dual-press
outcome you expect) and assert the onKeyUp dispatch branch was called (spy on
the action dispatch/mock and verify payload); then add a complementary test
where getDualPressDirections is mocked to a disabled value and verify the
onKeyUp dispatch branch is not invoked. Use the existing MockDualPressTracker
methods (recordKeyDown, computeOutcome, clear, hasPending) to drive state and
ensure you reset/restore mocks between tests.
🤖 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 `@packages/deck-core/src/dual-press.test.ts`:
- Around line 186-191: The test is writing a recognized value so it never
exercises the fallback; change the stored passthrough value written via
updateGlobalSettings to an invalid string (e.g. "invalid-direction" or
"tap-unknown") after calling initGlobalSettings(createMockAdapter(),
createMockLogger()) so that getDualPressDirections() must hit the fallback path
and then assert the expected fallback result (the default/normalized value your
code returns). Use the same helpers mentioned (initGlobalSettings,
updateGlobalSettings, getDualPressDirections) to locate and modify the test.

In `@packages/iracing-actions/src/actions/setup-traction/setup-traction.test.ts`:
- Around line 66-73: The test adds DualPressTracker mocks but doesn't assert
runtime behavior; add tests that use the MockDualPressTracker (DualPressTracker,
recordKeyDown, computeOutcome, clear, hasPending) and
getDualPressThresholdMs/getDualPressDirections to simulate short and long key
presses and verify the View's onKeyUp dispatches the expected actions for
"tap-increases" vs long-press outcomes, assert computeOutcome was called with
the right timestamps and that clear/hasPending are used appropriately, and add a
test where dualPressEnabled=false to confirm the code falls back to single-press
behavior (no dual-press outcome dispatched).

In `@packages/website/src/content/docs/docs/actions/car-setup/setup-engine.md`:
- Around line 14-29: The page currently contradicts itself by describing View
entries as both read-only and interactive with dual-press; update the "Modes"
section language to match the "View …" and "Dual-press control" sections by
stating that View modes normally show a live readout but, when the
property-inspector setting "Enable dual-press" is on (default on) they also
accept short/long presses that map to the corresponding adjustment bindings;
also add a brief note in the Modes text referencing the global Dual-Press
direction and Long-press threshold settings so users know where to change tap
behavior.

---

Outside diff comments:
In `@packages/website/src/content/docs/docs/actions/car-setup/setup-chassis.md`:
- Around line 14-35: The docs currently contradict: the "Dual-press control"
section explains interactive dual-press behavior for View modes (including the
"Enable dual-press" Property Inspector setting and the global "Dual-Press →
Directions" and "Long-press threshold (ms)") but the later "Modes" text calls
View modes read-only; update the "Modes" text to either remove the blanket
"read-only" claim or add an explicit exception clarifying that View modes are
read-only only when "Enable dual-press" is off and otherwise dispatch adjustment
bindings (e.g., Diff Preload → Diff Preload +/−, Weight Jacker bindings in
Global Settings → Setup Chassis); ensure the wording references "Enable
dual-press", "Global Common Settings → Dual-Press → Directions" and "Long-press
threshold (ms)" so readers know the toggle and global settings control
interactive behavior.

---

Nitpick comments:
In `@packages/iracing-actions/src/actions/setup-fuel/setup-fuel.test.ts`:
- Around line 60-67: Add tests in setup-fuel.test.ts that assert the action
dispatch path on key-up depending on dual-press mode: when
getDualPressDirections is mocked to "tap-increases" simulate a keyUp through the
action's handler (use the existing MockDualPressTracker and set computeOutcome
to return the dual-press outcome you expect) and assert the onKeyUp dispatch
branch was called (spy on the action dispatch/mock and verify payload); then add
a complementary test where getDualPressDirections is mocked to a disabled value
and verify the onKeyUp dispatch branch is not invoked. Use the existing
MockDualPressTracker methods (recordKeyDown, computeOutcome, clear, hasPending)
to drive state and ensure you reset/restore mocks between tests.

In `@packages/iracing-actions/src/actions/setup-hybrid/setup-hybrid.test.ts`:
- Around line 68-76: Add explicit tests in setup-hybrid.test.ts that exercise
the onKeyUp dual-press dispatch path: use the existing MockDualPressTracker (and
helper mocks getDualPressThresholdMs/getDualPressDirections) to simulate a
computed dual-press outcome and assert that when dualPressEnabled=true the
dispatcher/action for dual-press is called on key-up, and when
dualPressEnabled=false no dual-press dispatch occurs; specifically, create two
cases that stub MockDualPressTracker.computeOutcome to return a dual-press
result and verify dispatch behavior on the component/module's onKeyUp handler,
and another case where dualPressEnabled is false to verify no dispatch.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dca80caf-e045-4e80-bfd9-934a930a8ec7

📥 Commits

Reviewing files that changed from the base of the PR and between 00368bc and 484517a.

📒 Files selected for processing (42)
  • .claude/rules/pi-templates.md
  • .claude/skills/iracedeck-actions/SKILL.md
  • packages/deck-core/src/dual-press.test.ts
  • packages/deck-core/src/dual-press.ts
  • packages/deck-core/src/global-settings.test.ts
  • packages/deck-core/src/global-settings.ts
  • packages/deck-core/src/index.ts
  • packages/deck-core/src/simhub-service.test.ts
  • packages/iracing-actions/src/actions/data/key-bindings.json
  • packages/iracing-actions/src/actions/setup-aero/setup-aero.ejs
  • packages/iracing-actions/src/actions/setup-aero/setup-aero.test.ts
  • packages/iracing-actions/src/actions/setup-aero/setup-aero.ts
  • packages/iracing-actions/src/actions/setup-brakes/setup-brakes.ejs
  • packages/iracing-actions/src/actions/setup-brakes/setup-brakes.test.ts
  • packages/iracing-actions/src/actions/setup-brakes/setup-brakes.ts
  • packages/iracing-actions/src/actions/setup-chassis/setup-chassis.ejs
  • packages/iracing-actions/src/actions/setup-chassis/setup-chassis.test.ts
  • packages/iracing-actions/src/actions/setup-chassis/setup-chassis.ts
  • packages/iracing-actions/src/actions/setup-engine/setup-engine.ejs
  • packages/iracing-actions/src/actions/setup-engine/setup-engine.test.ts
  • packages/iracing-actions/src/actions/setup-engine/setup-engine.ts
  • packages/iracing-actions/src/actions/setup-fuel/setup-fuel.ejs
  • packages/iracing-actions/src/actions/setup-fuel/setup-fuel.test.ts
  • packages/iracing-actions/src/actions/setup-fuel/setup-fuel.ts
  • packages/iracing-actions/src/actions/setup-hybrid/setup-hybrid.ejs
  • packages/iracing-actions/src/actions/setup-hybrid/setup-hybrid.test.ts
  • packages/iracing-actions/src/actions/setup-hybrid/setup-hybrid.ts
  • packages/iracing-actions/src/actions/setup-traction/setup-traction.ejs
  • packages/iracing-actions/src/actions/setup-traction/setup-traction.test.ts
  • packages/iracing-actions/src/actions/setup-traction/setup-traction.ts
  • packages/iracing-actions/src/shared/setup-view.ts
  • packages/pi-components/partials/dual-press-overrides.ejs
  • packages/pi-components/partials/global-common-settings.ejs
  • packages/pi-components/partials/global-flag-flash.ejs
  • packages/pi-components/partials/head-common.ejs
  • packages/website/src/content/docs/docs/actions/car-setup/setup-aero.md
  • packages/website/src/content/docs/docs/actions/car-setup/setup-brakes.md
  • packages/website/src/content/docs/docs/actions/car-setup/setup-chassis.md
  • packages/website/src/content/docs/docs/actions/car-setup/setup-engine.md
  • packages/website/src/content/docs/docs/actions/car-setup/setup-fuel.md
  • packages/website/src/content/docs/docs/actions/car-setup/setup-hybrid.md
  • packages/website/src/content/docs/docs/actions/car-setup/setup-traction.md

Comment thread packages/deck-core/src/dual-press.test.ts
- dual-press.test.ts: the "unrecognised value" case now genuinely exercises
  the getDualPressDirections fallback by stubbing getGlobalSettings (the
  schema enum rejects invalid values, so they can't reach the reader via the
  normal cache path). Added a sanity assertion that the spy is intercepting.
- Added full dual-press behavior tests to the remaining six setup actions
  (traction, fuel, engine, chassis, hybrid, aero) — previously only
  setup-brakes had them. setup-hybrid's variant asserts the merged
  hold/release path still routes non-View key-ups to releaseBinding.
- setup-engine.md / setup-chassis.md: the "Modes" section no longer calls
  View modes read-only — reconciled with the dual-press "View sub-modes"
  section.
@niklam niklam merged commit 547f85e into master May 14, 2026
5 checks passed
@niklam niklam deleted the ir-540 branch May 14, 2026 11:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(actions): dual-press mode for directional setup actions (tap=configured direction, long-press=opposite)

1 participant