Replaces the four-hop signal-stub pattern (drop -> store-only update,
engine never told) with explicit action helpers that mutate the engine
and the store in one step.
actions/:
- addObject / addCharacter / addShape: call editor.sceneManager.create
to load the THREE.js object, then update the Zustand store. addCharacter
also stamps userData.isCharacter on the loaded object
- deleteObject: thin wrapper over editor.deleteObject (which already
goes through SceneUtils to update the store)
- selectObject / deselectObject: invoke sceneManager.select_object;
Editor.publishSelect already writes the selection to the store
- setTransformMode: drives editor.change_mode + store + the legacy
selectedMode signal during the migration window
- setCameraAspect: drives editor.changeRenderCameraAspectRatio + store +
the legacy cameraAspectRatio signal
- index.ts barrel
Wiring updates:
- PageEditor drop handler (gallery -> 3D) now calls actions/addObject
and actions/addCharacter with the editor and world position
- DragAndDrop/DndAsset takes the editor at pointerdown time and calls
actions/* on drop. ItemElement passes the editor in via EngineContext
- AspectRatioMenu calls actions/setCameraAspect; PageEditor's bottom-bar
aspect-ratio select uses the action too
- Controls3D's mode toggle (move/rotate/scale) goes through
actions/setTransformMode and the four little redundant handlers are
collapsed into one
- ImageTo3DExperience's "Open in 3D Editor" button uses getActiveEditor
to pick up the live editor across pages
Plumbing:
- New module-level activeEditor handle in EngineContext (with
getActiveEditor / setActiveEditor) so non-React callers and cross-page
actions can reach the engine; EngineProvider sets it on init and
clears on unmount
- Drop the now-orphaned signals/objectGroup, signals/characterGroups,
signals/shape entirely. Their job (store update + engine call) lives
in actions/. signals/index.ts loses the corresponding re-exports
- Remove the placeholder addLocalSplat onSuccess in Controls3D (a
developer-test path for direct splat loading; the real splat upload
goes through GlobalFileDropHandler) and delete the now-confirmed-dead
recordScene method in editor.ts
- video_generation.ts zipBlob: type as Blob | null (matches the
UploadStudioShot signature; pre-existing TS error)
Net effect on typecheck: 144 -> 147 errors in PageEnigma + experiences;
zero errors introduced in actions/, PageEnigmaStore, or DragAndDrop. All
new errors are pre-existing issues in adjacent files surfaced by the
edits, not regressions.
editor.ts surgery (1864 -> 1665 lines):
- Drop imports + field declarations + constructor instantiations for
AudioEngine, TransformEngine, EmotionEngine, LipSyncEngine,
CharacterAnimationEngine, and TimeLine; drop the
Queue/QueueNames/fromEngineActions/toEngineActions imports
- Drop timeline state fields: can_playback, playback_location,
current_frame, last_scrub. generating_preview stays (it tracks the
stylize preview render, not the timeline)
- Replace Queue.publish in changeRenderCameraAspectRatio, newScene,
loadScene, and publishSelect with direct store actions
(usePageEnigmaStore.getState().setSelectedObject / resetScene)
- Replace timeline.characters lookups (used by SceneManager outliner)
with a getCharactersByUuid helper that reads the store
- Replace timeline.isCharacter (passed into SceneManager) with an
isCharacterUuid helper that checks the store's character list
- recordScene becomes a no-op stub: timeline-driven frame capture is
gone, video generation needs to drive its own frame loop. TODO marker
for the rewiring
- generateVideo and togglePlayback drop their timeline.is_playing /
scrubber gating; renderSingleFrame drops the timeline.update branch
- Bonus: kill the meraAspectRatio typo (a pre-existing bug in the
recordScene branch I deleted)
Cascading fixes:
- save_manager.ts loadScene/loadCache: drop calls to the deleted
timeline.scrub and timeline.checkEditorCanPlay
- video_generation.ts videoProcessingPipeline: stub the
engineFrameBuffers.payloadZip path with a TODO; the video upload now
always errors via the existing handleError. The full rewiring is the
same TODO as recordScene
- Controls3D UploadModalSplat onSuccess: drop the
editor.timeline.addLocalSplat call
- SidePanelTabs: drop a long-broken import of useMouseEventsSidePanel
(the Timeline/utils path it pointed to was already deleted before
this refactor); resize handle gets a TODO marker for Phase 6
Net effect on typecheck: 162 -> 144 errors in PageEnigma. Every
remaining error in PageEnigma predates this branch; no new TypeScript
errors introduced by the refactor.
Removes references to the deleted Queue/timeline subsystem from:
- Engine helpers: scene_manager_api, video_generation, helper. Their
Queue.publish calls (UPDATE_CHARACTER_ID/ADD_OBJECT/POP_A_TOAST/
DELETE_OBJECT) now write to the new Zustand store / addToast directly.
- Editor/EditorState: drop TimelineState class and its JSON variant; trim
EditorState/EditorStateJson down to just sceneState and metadata.
- Editor/scene.ts: drop the timeline animation-clip seeding path; restore
placeholder_manager (incorrectly classified as dead in the audit).
- Signals: collapse signals/objectGroup, signals/characterGroups, and
signals/shape to non-timeline scaffolds that update the store directly
with a TODO marker for the engine-side rewiring. Drop
loadObjectData/loadCharacterData/updateCharacters/updateObjectGroup
files (all timeline-only). Strip dndTimelineHeight + overTimeline from
signals/dragAndDrop. Add setEditorState helper to signals/engine.
- Comps: drop Queue.publish calls. Aspect-ratio menu and stylize-tab
state transitions go through signal setters; TOGGLE_CAMERA_STATE,
REFRESH_PREVIEW, GENERATE_VIDEO are stubbed with TODOs pending the
editor.ts rewiring. DragComponent simplified (timeline-drop branch was
unreachable). PageEditor drops timelineHeight/dndTimelineHeight layout
math.
- PageEnigma drops the useQueueHandler call.
The branch still does not compile: editor.ts (1864 lines) is the next
target -- it instantiates TimeLine + the five timeline engines and
publishes select/aspect-ratio events through the queue. Cleaning it up
is the focused work for the next commit.
Phase 1-3 of the 3D page refactor:
- Delete the timeline + event-queue subsystem (Queue/, useQueueHandler,
Editor/timeline.ts, Editor/Engines/, clips/, and the timeline-only
signal slices: timeline, timing, audioGroup, cameraGroup,
promptTravelGroup, utils/publishClip)
- Delete dead-code files with no live importers: lipsync.js,
retargeting.ts, preview_video_generation.ts, shaders.js,
placeholder_manager.ts, both composable_engines/*.tsx stubs,
contexts/TrackContext/, comps/TimerGrid, comps/ControlsVideo
- Add PageEnigmaStore.ts with the full state shape that will replace
preact-signals (scene objects, cameras, transform mode, layout, error
overlays, generation/stylize state)
- Strip signals/index.ts of deleted slice exports and reduce
signals/sizing.ts to just sidePanelWidth
Branch does not yet compile -- subsequent commits will strip
Queue/timeline references from editor.ts, the remaining signal files,
and component consumers, then build out actions/ and event hooks.
- Pressing **Enter** in any prompt box now adds a new line instead of
submitting - fixes complaints about accidental generations
- **Shift+Enter** is the new submit shortcut
- Desktop app gets an "Enter to generate" toggle in Settings → General
(off by default) for users who prefer the old behavior
- Applies to all prompt boxes in the Artcraft desktop app and the
website
- Ctrl+Z/Cmd+Z was calling redo() instead of undo(); fixed and added
missing Ctrl+Shift+Z/Cmd+Shift+Z and Ctrl+Y redo bindings
- Wrap all async keybind handlers in try/finally so isProcessing is
always cleared even if undo/redo/copy/paste throws
- Fix === typo in disableHotkeyInput (comparison instead of assignment),
and use full value replacement so the Preact signal update is reactive
- Quick actions on gallery cards: Recreate, Share, and Download buttons
now appear on hover directly on every image/video card, so users can act
on content without opening the lightbox first.
- Media type and model badges are larger and restyled for better
readability on thumbnails.
- The library page now uses the same card component as the generation
gallery, so any future card improvements land everywhere at once.
- Library grid tightened from up to 6 columns to a cleaner max of 5 per
row.
- Removed the dotted background on the library page so skeletons and
cards sit on a solid surface.
## Artcraft website: unified lightbox, recreate, desktop CTA, prompt
fixes
**Lightbox**
- Gallery modal and `/media/:id` page now share one details component -
no more drift.
- Author sticks to the top when you scroll; `X` moved to the details
panel; "Show more" prompt toggle added to the media page.
- Shows aspect ratio, resolution, duration, and audio on/off.
**Recreate (new)**
- Button in the lightbox restores prompt, refs (image / end-frame /
video / audio), model, aspect, resolution, duration, audio toggle, and
input mode from a past generation.
- Routes to Create Image vs Create Video based on the saved model class,
not URL guessing.
- Does not auto-generate.
**Desktop CTA**
- Dismissible banner above the promptbox linking to the desktop app.
Hidden on mobile, reappears 24h after dismissal.
**Model names**
- Model selector uses `full_name` from the OmniGen API so new models
show up correctly without a code change.
- Added Seedance 2.0 Fast to the shared model list for the lightbox
details panel.
**Promptbox `@mentions`**
- Triggers correctly after Chinese, punctuation, or emoji - not just
whitespace.
- Case-insensitive: `@image1` highlights as `@Image1`.
fixes:
- prompt box no longer overlaps bottom buttons or wastes space on big
screens
- long unbroken text wraps instead of escaping the box
- resizing the window updates the layout without needing to refocus