mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
## 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>
230 lines
4.1 KiB
JSON
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"
|
|
}
|
|
]
|
|
}
|