Skip to content

feat(#1): controls mapper (remap, skins, presets, live test)#196

Merged
cnotv merged 29 commits into
mainfrom
feat/1-controls-mapper
Jul 2, 2026
Merged

feat(#1): controls mapper (remap, skins, presets, live test)#196
cnotv merged 29 commits into
mainfrom
feat/1-controls-mapper

Conversation

@cnotv

@cnotv cnotv commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Closes #1

Summary

A standalone Controls Mapper at /tests/ControlsMapper where you can remap controls, style the on-screen widget, save/load configs, and test everything live. All data logic lives in the framework-agnostic @webgamekit/controls package; Pinia holds reactive state; Vue components are UI only.

Design

Goal — one place to:

  1. Remap bindings — assign keyboard keys, gamepad buttons, and faux-pad directions to actions, per device.
  2. Style the on-screen widget — a default skin swappable for other provided skins.
  3. Save/load configs — persist to localStorage, plus JSON file import/download.
  4. Live test — see which actions fire in real time (keyboard, gamepad, or dragging the pad).

Architecture

  • All data generation/handling is in packages/controls (pure functions + localStorage): getMapping/setMapping, assignBinding/removeBinding, createDefaultMapping, serializePreset/parsePreset, savePresets/loadPresets, CONTROL_SKINS.
  • src/stores/controlsMapper.ts (Pinia) holds reactive state and delegates to those functions.
  • src/components/ControlsMapper/* render UI only; the view is a thin wrapper (auto-routed).

Layout — two top-level tabs for mobile: Bindings (Add menu + Reset + device sub-tabs that auto-follow the last input) and Test (skin picker + live preview + tester). Each tab fits a single phone viewport.

Key Changes

@webgamekit/controls

  • getMapping() / setMapping() on the createControls instance (the issue listed these as done; only remapControlsOptions() actually existed).
  • Pure helpers: assignBinding, removeBinding (per-action conflict resolution), createDefaultMapping.
  • Preset JSON + storage: serializePreset, parsePreset, savePresets, loadPresets, PRESETS_STORAGE_KEY.
  • Skin registry data: CONTROL_SKINS, getDefaultSkinId.
  • Faux-pad controller now supports mouse drag (not just touch).

App

  • src/stores/controlsMapper.ts — reactive mapping / skin / presets / live actions / last device.
  • src/components/ControlsMapper/ — orchestrator + Bindings, Style, Presets (Add dropdown: save/import/download + dated links), Tester, plus useBindingCapture and useGamepadStick.
  • src/views/Tests/ControlsMapper/ControlsMapper.vue — thin routed view.
  • TouchControl.vueskin prop (default look unchanged), inline preview mode, external position (gamepad-driven) prop.

Behaviour

  • Device sub-tab auto-selects from the last input type (keyboard / gamepad / faux-pad).
  • Gamepad stick drives the faux-pad preview; the pad is draggable by mouse and touch.
  • Reset-to-defaults button; single-viewport, mobile-friendly layout with readable font sizes.

Test Plan

  • pnpm test:unit — 1315 tests pass (new: getMapping/setMapping, mapping helpers, presets round-trip + storage, skins, faux-pad mouse, store round-trips incl. lastDevice).
  • pnpm type-check, pnpm lint, stylelint — all clean.
  • Manual (verified at 390×844 via Playwright): open /tests/ControlsMapper, rebind a key/gamepad button, swap skins, save/import/download a config, drag the pad, watch actions fire live, confirm tabs auto-switch by input.

🤖 Generated with Claude Code

cnotv and others added 10 commits July 1, 2026 15:53
Add framework-agnostic controls data helpers (getMapping/setMapping,
assignBinding/removeBinding, createDefaultMapping, preset serialize/parse,
localStorage persistence, CONTROL_SKINS), a Pinia store holding reactive
state, and a standalone /tools/ControlsMapper view built from a reusable
ControlsMapper component tree (bindings, style, presets, tester). Add a
skin prop to TouchControl for previewable on-screen control styles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…alog

- Relocate the view to src/views/Tests (route /tests/ControlsMapper)
- Bindings use tabs per control device (keyboard/gamepad/faux-pad)
- Add resetToDefaults store action and a Reset to defaults button
- Make the view its own scroll container (global body scroll is locked)
- Move preset import/export into a reusable Dialog component

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…dated links)

Collapse presets into a single Add menu: save current config, import a
config file, download the current config, and a list of saved configs shown
as clickable dated links that apply on click. Remove the now-unused Dialog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two top-level tabs: Bindings (presets Add menu + reset + device bindings)
and Test (style + live tester). Bindings rows wrap for narrow screens and
fonts step up for readability on phones.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
radix Select forbids an empty-string SelectItem value; map the unset
faux-pad direction to a 'none' sentinel instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wrap each Tabs in an auto-height container so the shared height:100% resolves
to auto and TabsContent no longer creates inner scroll boxes (faux-pad). Add a
RotateCcw icon to the Reset button.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The panel's display:flex overrode the [hidden] attribute so the inactive tab
stayed visible and pushed content down; gate display behind :not([hidden]).
Set the view text color to --color-foreground so labels are not invisible
white on the light background.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a normalized position prop to TouchControl and a useGamepadStick composable
that polls the left stick each frame, so moving a gamepad joystick moves the
faux-pad preview in the Test tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Track the last input device in the store (via recordAction) and switch the
Bindings device sub-tab to match, so pressing a key, gamepad button, or using
the faux-pad focuses the matching tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add mouse drag support to the faux-pad controller (mousedown on the knob,
mousemove/up on window) so it works on desktop, extracted into a helper to
stay within the function-length limit. Stop useGamepadStick from re-emitting
an identical position each frame, which had been resetting the knob to center
and fighting drags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 1, 2026

Copy link
Copy Markdown

Deploy Preview for cnotv-generative-art ready!

Name Link
🔨 Latest commit 08cff4a
🔍 Latest deploy log https://app.netlify.com/projects/cnotv-generative-art/deploys/6a46e9b7df8a220008299a51
😎 Deploy Preview https://deploy-preview-196--cnotv-generative-art.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

cnotv and others added 19 commits July 1, 2026 22:57
Rebuild the mapper UI with the LobbyUI kit (LobbyUIRow, LobbyUIOptionToggle,
LobbyUIButton, LobbyUIConfigField) on a themed backdrop, and add a shared
useMenuFocus composable providing roving keyboard/gamepad focus over
[data-lui-row] rows (up/down rows, left/right controls, activate, select/number
editing) with a gamepad focus hint. Presets become buttons instead of a
dropdown; the device sub-tab and top tabs are toggles. Let LobbyUIOptionToggle
wrap so 3-option toggles fit narrow screens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ayout

- Load the Darumadrop One font in the view (matches other LobbyUI screens)
- LobbyUIButton: add size prop (sm/md/lg), remove borders and box-shadows
- LobbyUIOptionToggle: add size + per-option icons + hide-label-on-mobile;
  highlight the active option with a fill instead of a border
- Device toggle uses keyboard/gamepad/joystick icons (labels hidden on mobile)
- Listen (ghost) and Clear are small icon buttons (ear / x)
- Import + Download sit together as their own list; saved configs load by
  clicking their dated label
- Save (large) and Reset moved to the very bottom; skin toggle is small

Shared LobbyUIButton/LobbyUIOptionToggle changes are global and flatten
buttons across all lobbies.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Move Save/Reset up next to the title so they're reachable without scrolling
- Increase spacing between title, tabs, and options
- Add a Presets tab and move the preset import/download/saved-config list there
- Swap the Listen ear icon for a signal icon

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Listen uses a pencil icon, Clear uses an undo icon
- Drop-shadow on binding, preset, and toggle icons to match the LobbyUI text
- Nudge icons down to sit on the font's optical center
- Trigger fills the row so the action icons sit at the end
- More spacing under the header and the device tabs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Restore a focus border on LobbyUIButton/LobbyUIOptionToggle (transparent at
  rest, focus color when focused) so joypad/keyboard focus is visible
- Replace the faux-pad direction dropdown with prev/next arrow steppers that
  disable at the first/last option

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The playful font sat high in the default 1.5 line box, making text look
off-center inside the focus border; tighten line-height to 1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add LobbyUIIconButton (round icon button) and use it for listen/clear,
  preset delete, and the config-field arrows
- Rebuild LobbyUIConfigField as prev/next arrow steppers around a read-only
  value (no dropdown click, no underline); disable arrows at first/last / min/max
- Optically center button and toggle text via asymmetric vertical padding
  (keeps height, unlike line-height)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…arrows

- Lay out the binding rows as one grid (display: contents rows) so label,
  trigger, listen and clear align in the same columns across rows
- Revert LobbyUIConfigField to a focusable select/number with non-focusable
  decorative arrows, restoring the click-to-edit / click-to-confirm flow; drop
  the underline

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Gamepad capture now ignores buttons already held when Listen is activated
  and only binds a freshly-pressed button, so activating with the confirm
  button no longer instantly binds it
- Remove the device auto-switch on last input: it snapped back to the gamepad
  tab on every d-pad nav press, making it impossible to select/stay on another
  device tab with the pad

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the binding value itself the focusable control: click/tap/activate it to
listen, then press an input to set it. Replace the pencil/undo icons with a
single X that resets the binding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…aux-pad jump

- Hide the gamepad hint when focus leaves the panel (e.g. joypad cancel), via a
  focusout listener
- Pause menu navigation while a binding is being captured so the input only
  updates the keybinding
- Config field value can be changed with up/down as well as left/right
- Faux-pad tab lists all actions (including Jump) with a direction select, so
  Jump is no longer missing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Capture gamepad buttons on release so the confirm/activate button can be bound
without re-triggering navigation, and capture stick axis deflections
(axisN-left/right/up/down) so stick directions are bindable too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ab focus

- Non-directional faux-pad actions (jump) bind to a same-named on-screen button
  (None/Button) instead of a direction; movement actions keep directions
- Stop refocusing to the first row when switching top tabs, so focus stays on
  the tab toggle instead of jumping to Save

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… defer edits

- Binding capture interrupts if the other device is used (keyboard while
  capturing gamepad, or vice-versa)
- Skip live test recording while a binding is being captured
- Config-field edits (select/number) only apply on confirm; cycling previews
  and cancel reverts, so faux-pad directions no longer reset other bindings
  until confirmed

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…elds

Move the gamepad focus-hint chip into a reusable LobbyUIFocusHint component and
give lui-field__control a visible focus border so focused inputs are clear.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cnotv cnotv merged commit 6087268 into main Jul 2, 2026
11 checks passed
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.

Add controls mapping panel

1 participant