Files
artcraft/frontend/tsconfig.json
T
Tanish Mankuandbflatastic e3f48033f9 Keybind settings: New menu and revamped keybind support (#1699)
## Summary

Adds a unified, remappable keyboard-shortcut system
(`@storyteller/keybinds`) across the webapp and desktop app, replacing
the scattered hardcoded `keydown` listeners in the 3D editor, 2D editor,
and moodboard with one registry, two presets (**Gamer** = today's
defaults, **Blender-like**), a Settings → Keybinds pane, and a hold-Ctrl
/ pinnable cheatsheet on every editor surface.

This PR was approved in June and then sat behind ~195 commits on `main`
(timeline, build/record modes, lucide migration). It has been merged
with current `main` and extended to cover the features that landed
since. **Nothing changes by default** — the Gamer preset reproduces
every existing key exactly.

## Architecture

- **Registry of `ActionDef`s** keyed by surface-namespaced ids
(`pagescene.timeline.playPause`), bound to physical `event.code`.
Resolution: user override → preset delta → base bindings. Persisted in
the `artcraft-keybinds` zustand store.
- **Five surfaces**: `global`, `pagescene` (3D), `pagedraw` (2D),
`moodboard` (canvas), `moodboard-grid` (board library / inspector /
presentation).
- **Declared availability** — `ActionDef.when(ctx)` gates decide whether
a binding is live for the current editor state (`sceneMode`, `encoding`,
`timelineExpanded`, `timelineSelection`, …). An unavailable binding
falls through *without consuming the event*, which is what lets Space /
arrows / Delete / Escape be shared by context-exclusive actions. A
sampled-context checker (`actionsCoAvailable`) proves the exclusivity in
tests and drives the conflict UI in Settings.
- **Global surface is a pure trigger layer**: features register behavior
with `useGlobalAction(id, handler)`; a window capture-phase dispatcher
fires above the modal key-swallow, and an unregistered binding is inert
per host. First inhabitant: Ctrl/Cmd+B sidebar. This is the intended
home for the upcoming privacy-blur toggle (one `ActionDef` + one binding
+ one `useGlobalAction` call).
- Cross-surface conflict detection (global ↔ everything, both
directions); preset-integrity tests guard every preset against
same-surface collisions.
- **Video editor keeps its own keybinds system** — deliberate non-goal.

## What's in it

**3D editor (pagescene)**
- Timeline actions: Space play/pause, ←/→ frame step or keyframe/strip
nudge, Home/End, `I` add keyframe (Blender parity), Shift+T expand,
Delete/Backspace on a valid timeline selection (exact complement of
scene-object delete).
- Record mode: Tab toggles build/record, `C` capture still, `R` record
video (bare letters — Firefox eats Ctrl+Shift+S/E), Escape cancels an
in-flight encode (overlay Cancel button too). Record mode is immutable:
all scene-mutating actions gate off, Escape no longer drops out of the
render cam.
- **Per-edit timeline undo** — every timeline edit is one Ctrl+Z step
(`TimelineEditAction` with whole-timeline snapshots; drags and the
easing popover coalesce into single steps). Save now only moves the
Cancel baseline; Cancel is itself undoable.
- The old "Shortcuts" modal is replaced by the live cheatsheet, pinnable
from the same button.
- Dead code removed: `MouseControls.onkeydown`, `OnboardingHelper`, the
hardcoded `Help` view, three unused pagedraw hotkey hooks.

**2D editor (pagedraw)**
- Tool hotkeys 1–5 with live key badges on the toolbar icons; the shape
key cycles rectangle → circle → triangle (submenu-aware).
- Ctrl+= / Ctrl+- (and numpad) resize the active tool. The eraser now
has its own size (was sharing the brush's) and its own slider popout;
also fixes the eraser button never highlighting.

**Moodboard**
- Canvas keys unchanged (now registry-backed), plus Ctrl+0 reset
viewport.
- Grid: rate 0–5 / `P` pick / Delete / Escape, inspector and
presentation ←/→/Space/Esc — all rebindable. Escape now closes the
inspector; arrows no longer navigate while typing in the tag field.

**Settings & cheatsheet**
- Settings → Keybinds: preset picker, name/key search, per-action rebind
with conflict warnings, per-section reset, and a bottom "danger zone"
reset-all with a two-step confirm (2 s lockout + visible 5 s
auto-disarm).
- Cheatsheet: hold Ctrl (⌘) 3 s to peek; **General → "Keep shortcut
cheatsheet open"** makes it stick until Esc / click-outside; the 3D
"Shortcuts" button pins it.

## Testing

- `@storyteller/keybinds` unit tests (29): resolution layering,
overrides, per-surface/global reset, conflict detection incl.
global↔surface, matcher semantics, and preset integrity across all
presets.
- Builds green: `keybinds`, `ui-pagescene`, `ui-pagedraw`,
`ui-moodboard`, `ui-settings-modal`, `artcraft-webapp`, `artcraft`
(desktop).
- Manual QA pass completed on the webapp for every area above (settings
pane, cheatsheets, Blender modal transforms, timeline/record keys,
pagedraw toolbar, global surface, reset UX, pinned cheatsheet, timeline
undo, Escape contexts).

## Notes for review

- The keybinds store is unversioned-migration-free on purpose — the PR
was never merged, so there's no persisted shape to migrate from.
- Space-hold pan (moodboard) and held WASD camera movement stay as
gestures, resolved from the same store but outside the discrete-action
dispatch.

---------

Co-authored-by: bflatastic <bombayapirat@gmail.com>
2026-08-25 00:34:26 -07:00

230 lines
4.1 KiB
JSON

{
"extends": "./tsconfig.base.json",
"compileOnSave": false,
"files": [],
"references": [
{
"path": "./libs/api"
},
{
"path": "./libs/login"
},
{
"path": "./apps/artcraft"
},
{
"path": "./libs/components/toaster"
},
{
"path": "./libs/components/close-button"
},
{
"path": "./libs/components/modal"
},
{
"path": "./libs/components/settings-modal"
},
{
"path": "./libs/components/button"
},
{
"path": "./libs/components/input"
},
{
"path": "./libs/components/label"
},
{
"path": "./libs/components/select"
},
{
"path": "./libs/components/tooltip"
},
{
"path": "./libs/tauri-api"
},
{
"path": "./libs/tauri-events"
},
{
"path": "./libs/tauri-utils"
},
{
"path": "./libs/components/demo-modal"
},
{
"path": "./apps/artcraft-website"
},
{
"path": "./apps/video-info-website"
},
{
"path": "./apps/artcraft-webapp"
},
{
"path": "./libs/components/gallery-modal"
},
{
"path": "./libs/components/lightbox-modal"
},
{
"path": "./libs/components/tab-selector"
},
{
"path": "./libs/components/loading-spinner"
},
{
"path": "./libs/soundboard"
},
{
"path": "./libs/components/promptbox"
},
{
"path": "./libs/components/popover"
},
{
"path": "./libs/components/camera-settings-modal"
},
{
"path": "./libs/components/slider-v2"
},
{
"path": "./libs/common"
},
{
"path": "./libs/components/login-modal"
},
{
"path": "./libs/components/switch"
},
{
"path": "./libs/build-env"
},
{
"path": "./libs/components/menu-icon-selector"
},
{
"path": "./libs/components/badge"
},
{
"path": "./libs/components/button-dropdown"
},
{
"path": "./libs/components/button-icon"
},
{
"path": "./libs/components/button-icon-select"
},
{
"path": "./libs/components/button-link"
},
{
"path": "./libs/components/file-uploader"
},
{
"path": "./libs/components/gravatar"
},
{
"path": "./libs/components/loading"
},
{
"path": "./libs/components/list-dropdown"
},
{
"path": "./libs/components/pagination"
},
{
"path": "./libs/components/search"
},
{
"path": "./libs/components/upload-modal"
},
{
"path": "./libs/components/action-reminder-modal"
},
{
"path": "./libs/components/model-selector"
},
{
"path": "./libs/components/create-3d-modal"
},
{
"path": "./libs/components/image-input"
},
{
"path": "./libs/components/pricing-modal"
},
{
"path": "./libs/model-list"
},
{
"path": "./libs/analytics/google-analytics"
},
{
"path": "./libs/components/provider-setup-modal"
},
{
"path": "./libs/components/progress"
},
{
"path": "./libs/components/provider-billing-modal"
},
{
"path": "./libs/state/credits"
},
{
"path": "./libs/state/subscription"
},
{
"path": "./libs/components/help-menu"
},
{
"path": "./libs/api-enums"
},
{
"path": "./libs/components/viewer-3d"
},
{
"path": "./libs/markdown-content"
},
{
"path": "./apps/genhub"
},
{
"path": "./libs/components/pagedraw"
},
{
"path": "./libs/components/checkbox"
},
{
"path": "./libs/components/pagescene"
},
{
"path": "./libs/components/vfx"
},
{
"path": "./libs/components/pricing-table"
},
{
"path": "./libs/components/video-editor"
},
{
"path": "./libs/components/generation-list"
},
{
"path": "./libs/components/moodboard"
},
{
"path": "./libs/components/keybinds"
},
{
"path": "./libs/omni-gen"
},
{
"path": "./libs/components/audio-player"
},
{
"path": "./libs/icons"
}
]
}