Commit Graph
14491 Commits
Author SHA1 Message Date
Tanish Manku e8db3f577a PageScene: commit panel transform session on mouseup / Enter / Ctrl+Z
Follow-up to 72071c0066. The session-based panel transform recording
fixed pollution but introduced a new bug: while editing the same
object's panel without changing selection, the session never
committed, so Ctrl+Z fell through to the most recent prior entry —
typically the CreateAction for the shape, which deleted it on undo.

Add three document-level commit triggers covering the natural editing
boundaries the user expects:

- mouseup (any document-level mouseup): catches the end of an
  InputVector numeric-scrub drag and any click that moves focus
  elsewhere after typing.
- Enter key: explicit "done editing" commit even when the input
  retains focus.
- Ctrl+Z / Ctrl+Y (capture phase): flushes the pending session before
  the engine's keymap handler runs undo/redo, so the pending edit is
  what gets reverted instead of the prior entry.

Capture phase on the keydown listener is load-bearing: keymap.ts
attaches its handler in bubble phase via useViewportKeyboard. Capture
fires first, so the session commits before keymap dispatches.

mouseup commit is a no-op when no session is open, so attaching it
to document doesn't cost anything for unrelated clicks.

Color session is unchanged (focus/blur boundaries on the color
input still work and don't have this issue).
2026-05-01 04:57:07 -07:00
Tanish Manku 5d3cf8bc4a Revert "PageScene: coalesce burst undo entries via UndoableAction.tryMerge"
This reverts commit 1155b12b32.
2026-05-01 04:56:07 -07:00
Tanish Manku 1155b12b32 PageScene: coalesce burst undo entries via UndoableAction.tryMerge
The previous panel-undo fix used session boundaries (open on first
edit, commit on selection change / unmount). It overshot: while
editing the same object's panel without changing selection, the
session never committed, so Ctrl+Z fell through to the most recent
*previous* entry — typically the CreateAction that added the shape.
Result: editing a shape's transform then hitting Ctrl+Z deleted the
shape.

Replace session boundaries with action-level coalescing:

- Add optional `tryMerge(other)` to the UndoableAction interface.
  HistoryManager.record now offers each new action to the previous
  entry's tryMerge first; if absorbed, the new action drops out and
  the existing entry's `after` extends to cover the burst.
- TransformAction: constructor now takes (editor, uuid, before, after)
  directly; drop the begin/commit pattern. tryMerge merges a follow-up
  TransformAction whose `before` matches this one's `after` (chain
  continuity) — same uuid only.
- ColorAction: same treatment. tryMerge folds the native color
  picker's per-pixel onChange burst into a single undo entry.

Caller-side: drop all session machinery from the panel
(transformSessionRef, colorSessionRef, lastUuidRef, beginPanelTransform,
commitPanelTransform, the unmount-only effect). On every input
change the panel calls `transformObject` / `setObjectColor`, which
snapshot before, apply, snapshot after, and record. The manager
handles the rest.

Gizmo path in editor.ts updated to the new TransformAction shape:
capture before-snap at dragstart on a small `activeDragStart` ref,
build TransformAction(before, after) at dragend.

Side benefit: gizmo-drag and panel-edit bursts on the same object
now coalesce into a single undo entry across input sources, since
both produce TransformActions whose chain continues.
2026-05-01 04:51:11 -07:00
Tanish Manku c6c0b193eb PageScene: SceneUtils takes Deps, no Editor reference (P2)
Closes the last "subsystem holds an Editor ref" holdout. SceneUtils
now takes a SceneUtilsDeps surface (Phase 2 idiom — same shape as
SaveManagerDeps, CameraControllerDeps, SelectionBridgeDeps,
GizmoControllerDeps) instead of the full Editor.

Deps surface — 9 single-purpose getters/callbacks for the cross-
subsystem reaches the methods need:
- Gizmo lifecycle (getGizmoControl/detachGizmo/removeGizmoFromScene)
  used by removeTransformControls
- Outline pass (getOutlinePass) used by removeTransformControls
- Selection (publishSelect/clearSelected) used by deleteObject and
  removeTransformControls
- Camera name (getCameraName) used by deleteObject's ::CAM:: guard
- Selected object lookup (getSelectedObject) used by getSelectedSum
- Three scene root (getThreeScene) — provided for future use; the
  current deleteObject still goes through this.scene.scene since
  Scene is an injected primitive, not a subsystem

Editor wires deps inline at SceneUtils construction. Constructor
signature flips: was (editor, scene); now (scene, deps).

Re P2 holdouts: ViewportController already used Deps
(ViewportEngineRefs); roadmap memory was stale. PostProcessingPipeline
and GizmoController.configure only take primitives (renderer/scene/
camera/dom), no subsystem coupling — converting them to options
objects would be pure style and was deferred.
2026-05-01 04:41:22 -07:00
Tanish Manku 72071c0066 PageScene: stop panel undo pollution from color + transform inputs
Two related session-boundary bugs in ControlPanelSceneObject — both
caused N undo entries per user gesture instead of one.

Bug A (color picker):
- Native <input type="color"> fires onChange on every slider pixel
  while the dialog is open, not just on commit. The previous one-shot
  setObjectColor recorded one ColorAction per fire, so a single
  white→red pick became dozens of micro-entries. Ctrl+Z reverted only
  the last micro-step → user reported "undo doesn't work."
- Fix: add ColorSession to actions/setObjectColor (apply/commit
  pair). Panel opens it on input.onFocus, applies on onChange (visual
  feedback only), commits on onBlur (one entry per pick).

Bug B (panel transform inputs — keystrokes AND scrub drags):
- The panel's [currentSceneObject] useEffect cleanup was committing
  the transform session on every frame the selected obj's transform
  changed. The chain: panel writes to engine -> next frame
  editor.ts:744 sees getSelectedSum changed -> updateSelectedUI()
  pushes a fresh objectPanel into Zustand -> currentSceneObject
  reference changes -> useEffect cleanup fires -> commitPanelTransform.
  Result: one entry per keystroke, dozens per scrub drag.
- Fix: track lastUuidRef. Commit the session only when the selection
  uuid actually changes (real boundary), not on every same-uuid
  re-push. Move the unmount-commit into a separate effect.

Both fixes use the same shape: a session ref opened on first edit
and committed at a properly-throttled boundary (focus/blur for color,
uuid-change/unmount for transform). Mirrors the engine-side
TransformAction begin/commit pattern.
2026-05-01 04:40:02 -07:00
Tanish Manku 3bfb405e8e 3D SaveManager cleanup and History Manager architecture (#1466) 2026-05-01 04:06:08 -07:00
Tanish Manku 5e05877619 3D editor.ts cleanup (#1461) 2026-05-01 00:10:50 -07:00
Tanish Manku fec717057e Re-add shaders.js used by chromakey.js 2026-04-30 14:32:53 -07:00
Tanish Manku 9ad3b86524 Audio: Move sound system into SoundManager, fix bootstrap + master toggle (#1455)
- Move sound catalog registration off PageScene mount and into a single
SoundManager.install() call in GlobalSettingsManager so sounds exist at
app boot (previously silent until PageScene mounted).
- Honor the play_sounds master toggle in all five event playback sites
via SoundManager.playEvent helpers. Settings preview keeps firing
regardless (handled by SoundManager.playPreview).
- Replace per-callsite GetAppPreferences + SoundRegistry boilerplate in
the four generation event hooks and the file-deleted handler with
one-line SoundManager.* calls.
- Dedupe SOUND_OPTIONS: AudioSettingsPane now reads
SoundManager.OPTIONS, derived from a single declarative SOUNDS array
(label-bearing entries are user-selectable; others are still registered
for code-driven use).
- Narrow the soundboard public barrel to SoundManager + SoundOption
only; SoundRegistry and SoundEffect are now lib-internal.
2026-04-30 09:07:28 -07:00
Tanish Manku 2b5e509a29 PageScene: move engine lifecycle flags + canvas refs into PageSceneStore
The last batch of PageScene-owned signals — `is3DPageMounted`,
`is3DEditorInitialized`, `is3DSceneLoaded`, and the three canvas-ref
signals (sceneContainer, editorCanvas, camViewCanvas) — now live on
the Zustand store. EngineProvider, the canvas components, the
useEditorCanvas hook, and the cross-app TopBar consumer all read from
the store instead. The helper signals in editor.ts are gone; the
`set3DPageMounted` / `setIs3DEditorInitialized` / `setIs3DSceneLoaded`
exports stay as thin wrappers around the store actions for existing
callers.
2026-04-29 15:16:29 -07:00
Tanish Manku 28db87c1b2 PageScene + libs: drop camera/promptBox signals, switch shared libs to plain props
PromptBox3D and CameraSettingsModal now accept plain values + setters
instead of `Signal<T>` props. The camera/focalLengthDragging/
isPromptBoxFocused signals are gone — store is the single source of
truth. Engine code (editor/save_manager/scene) reads/writes via
`usePageSceneStore.getState()` and store actions. Demo asset signals
are now plain constants (they were never reactive).
2026-04-29 15:12:13 -07:00
Tanish Manku 292cf33f57 PageScene: route setCameraAspect + save_manager directly through PageSceneStore 2026-04-29 15:00:48 -07:00
Tanish Manku 58b7cd0cd4 PageScene: route useFreeCam through PageSceneStore for prompt-box focus check 2026-04-29 14:59:54 -07:00
Tanish Manku 101b735c35 PageScene: migrate outliner signals to PageSceneStore 2026-04-29 14:59:06 -07:00
Tanish Manku 32291ce080 PageScene: migrate objectPanel signals to PageSceneStore 2026-04-29 14:56:05 -07:00
Tanish Manku cd9c06d2ce PageScene: migrate precisionSelector signals to PageSceneStore 2026-04-29 14:54:24 -07:00
Tanish Manku a780779f7a PageScene: migrate dragAndDrop signals to PageSceneStore 2026-04-29 14:53:10 -07:00
Tanish Manku 076b7fe4a4 PageScene: extend PageSceneStore with drag/objectPanel/outliner/precisionSelector fields 2026-04-29 14:52:05 -07:00
Tanish Manku 7eef728d22 PageScene: trim api_manager + drop dead model/error files
api_manager.ts goes from 460 lines to ~135. The video-render
pipeline (uploadMediaFrameGeneration, stylizeVideo,
StylizeVideoParams, getMediaBatch, getMediaFile, uploadMedia,
gltfExport) was unreachable after videoGeneration was deleted.
Surviving public methods:

- saveSceneState (called by save_manager)
- loadSceneState (called by save_manager)
- uploadMediaSceneThumbnail (used internally by saveSceneState)

Also drops the local ArtStyle / Visibility / MediaFile types —
they only fed the deleted methods. The ArtStyle enum has a
separate copy in ~/enums/ArtStyle.ts that other code uses.

Delete two files that became unreferenced:
- engine/APIManagerResponseError.ts (duplicate of the local class
  in api_manager.ts; nothing imported it)
- models/generationOptions.ts (IGenerationOptions/GenerationOptions
  fed editor.generation_options, deleted last commit)

Net: -1 typecheck error, -326 lines.
2026-04-29 14:12:03 -07:00
Tanish Manku d09c7320fd PageScene: delete misc dead files surfaced by stylize cleanup
Files with zero importers across the artcraft app:

- constants/RandomTexts.ts — RandomTextsPositive/Negative seeded
  prompt suggestions in the deleted StylizeTab.
- composable_engines/scene_format.tsx — empty (1 line).
- constants/page.ts and the matching currentPage signal in
  signals/global.ts — Pages enum + signal, neither read.
- enums/StylizeTabPages.ts and enums/AudioTabPages.ts — re-exported
  via enums/index.ts but no consumer.
- models/track.ts, models/message.ts, models/voice.ts, models/tts.ts
  — timeline / TTS / audio-panel types from deleted features.
- styleList.ts — used to seed selectedArtStyle's default; that
  store field is gone.
- models/types.tsx had a duplicate AudioTabPages enum (separate
  from the deleted one in enums/) — drop the duplicate too.

models/index.ts and enums/index.ts trimmed to match. Error count
unchanged (151) — none of these had broken type errors anyway.
2026-04-29 14:08:08 -07:00
Tanish Manku 0ca91e29b7 PageScene: drop orphaned stylize state
Strip the stylize fields that nothing reads anymore now that the
StyleMoreOptions / StyleSelection UI is gone. From PageSceneStore:
selectedArtStyle, upscale, faceDetail, styleStrength, lipSync,
cinematic, enginePreProcessing, globalIPAMediaToken, adapterImage,
positivePrompt, negativePrompt, isUserInputPositive,
isUserInputNegative, showNegativePrompt — and the matching setters
+ resetSceneGenerationMetadata. Drop the styleList / ArtStyle
imports; both go unused here.

From editor.ts: drop negative_prompt, art_style,
engine_preprocessing, generation_options, globalIpAdapterImage.
Keep positive_prompt — PromptBox3D writes it via setEnginePrompt
(PageEditor.tsx) and save_manager round-trips it.

Trim save_manager.loadFromJson to the prompt-only path (legacy
saves with stylize keys load fine; the now-removed if-guards just
silently ignored them anyway). Trim SceneMetadata to
{ positivePrompt, cameraAspectRatio } and the matching
SceneGenereationMetaData interface.

ControlsTopButtons.handleResetScene drops its now-removed store
call.
2026-04-29 14:07:03 -07:00
Tanish Manku 8af4657393 PageScene: delete orphan StyleMoreOptions and StyleSelection UI
Both component trees survived earlier cleanup but never render
from PageEditor or PageScene. They were the last UI consumers
of the stylize store fields; deleting them sets up a follow-up
that drops those store fields entirely.

Net: -17 typecheck errors (the orphan files were the source of
many missing-import errors).
2026-04-29 14:04:02 -07:00
Tanish Manku f8ee94e538 Revert moodboard-html files committed by mistake on this branch
cardLayout.ts, MoodboardHtmlColorPicker.tsx, findDropTarget.ts, and
CardNodeHtml.tsx were swept into the PageEnigma -> PageScene rename
commit (76add858c9) on this 3D-refactor branch. They belong on
moodboard-page-html.

The file content is preserved on the local-only moodboard-rescue
branch (pointing at 01e0e27037). To carry them back over:

  git checkout moodboard-page-html
  git checkout moodboard-rescue --     frontend/apps/artcraft/app/src/pages/PageMoodboard/cardLayout.ts     frontend/apps/artcraft/app/src/pages/PageMoodboardHtml/MoodboardHtmlColorPicker.tsx     frontend/apps/artcraft/app/src/pages/PageMoodboardHtml/interactions/findDropTarget.ts     frontend/apps/artcraft/app/src/pages/PageMoodboardHtml/nodes/CardNodeHtml.tsx
  git commit
2026-04-29 12:29:58 -07:00
Tanish Manku 01e0e27037 PageScene: route PoseModeSelector through getActiveEditor()
The Enter Pose Mode button reached through a window.__mouseControls
global handle that MouseControls' constructor set up. With
getActiveEditor() already exposing the live editor instance for
non-React callers (used in the camera-view toggle, the keymap),
the global is unnecessary.

Drop the (window as any).__mouseControls = this assignment in
MouseControls and the matching Window augmentation in
PoseModeSelector. Replace the click handler's reach with
getActiveEditor()?.mouse_controls?.toggleFKMode() — same shape
the keymap entry already uses.
2026-04-29 11:33:30 -07:00
Tanish Manku 794f65b517 PageScene: delete unused models/sidePanel.ts
The Tab interface in this file was the type for the deleted
SidePanel/tabList.tsx tab list. Grep across app/src/ confirms no
consumer remains; AssetModal uses its own local AssetTab type.
Drop the file and the matching re-export in models/index.ts.
2026-04-29 11:32:48 -07:00
Tanish Manku c53b407952 PageScene: drop unused imports surfaced by deletions
EditorStates is no longer referenced from editor.ts now that
PREVIEW is gone from the enum and switchPreview/switchEdit are
deleted. EngineCanvases.tsx never used useContext/EngineContext;
the imports were leftover from earlier work.

Net: -3 typecheck errors.
2026-04-29 10:03:23 -07:00
Tanish Manku e0611e0147 PageScene: drop document.mouseover gate + dead selectedCanvas
The mouseOverEventHandler / enableFreeCamControls /
disableFreeCamControls trio existed to track when the cursor was on
the viewport canvas and toggle FreeCam.enabled. With useFreeCam
gating itself on editorState === CAMERA_VIEW, that gate is
redundant; the only remaining consumer of selectedCanvas was
MouseControls.onkeydown's space-bar branch, which is unreachable
after the previous keyboard rewrite.

Removes the last document.addEventListener call from PageScene.
2026-04-29 10:02:09 -07:00
Tanish Manku 8a03449128 PageScene: keymap + useViewportKeyboard hook
Replace MouseControls.onkeydown's giant if-else chain with a
declarative keymap:

- engine/keymap.ts: KeyBinding type + buildKeymap() returning every
  one-shot viewport shortcut as one entry. Each carries label /
  group metadata so a future Ctrl-hold cheatsheet overlay can read
  the same array. dispatchBinding() does the modifier match
  (treating Ctrl and Meta as equivalent for macOS).
- hooks/useViewportKeyboard.ts: attaches a single keydown listener
  that gates on hotkeyStatus + editable-element checks, then walks
  the keymap. Held movement keys (W/A/S/D etc.) live in useFreeCam
  since they're continuous; this hook is for one-shots only.

The listener is on document, not the canvas, deliberately: canvas
focus is finicky and would require the user to click the viewport
before T/R/G work. The editable-element guard prevents the keymap
from firing while inputs are focused.

MouseControls.onkeydown is now unreachable (SceneManager's window
listeners are no-op since the previous commit, and useViewportKeyboard
goes straight to keymap dispatch). The class itself stays for now —
it still implements selection / FK / pointer-lock state.
2026-04-29 10:00:56 -07:00
Tanish Manku e4af73c9ac PageScene: useViewportPointer hook (canvas-scoped)
The old SceneManager.attachEventListeners() attached window-level
mousemove / click / mousedown / mouseup / keydown handlers in dev
mode and dispatched them into MouseControls. Replace with two pieces:

- hooks/useViewportPointer.ts: canvas-scoped pointer listeners
  that call into editor.mouse_controls. NDC pointer coords are
  computed via the new engine/pointer.ts helpers (ndcFromClient,
  applyNdcToVector2). Selection clicks suppress while the
  free-cam owns input (CAMERA_VIEW state).
- engine/pointer.ts: pure NDC coordinate translation, no DOM
  side-effects.

SceneManager.attachEventListeners / detachEventListeners are now
no-ops; kept for back-compat with the editor lifecycle so future
follow-ups can carve them out cleanly.

MouseControls itself still owns selection / FK / drag state — its
dispatch methods (onMouseMove, onMouseClick, etc.) are now invoked
by the React hook instead of window listeners. A future PR can
dissolve MouseControls further; the goal here is "no window
listeners" + "React owns lifecycle".
2026-04-29 09:58:24 -07:00
Tanish Manku efa9a54bee PageScene: useFreeCam hook + delete free_cam.ts
The 446-line FreeCam class is gone. Its responsibilities split:

- Math (move/rotate vectors, pan/zoom from inputs, lookAt) lives in
  cameraMath.ts as pure functions, plus a freeCamFrameTick() that
  integrates the per-frame state.
- Listener attachment + drag/key state ownership moves into
  hooks/useFreeCam.ts. Listeners are scoped to the canvas (no
  window/document handlers); held-key state is reset on
  CAMERA_VIEW exit so a stale W keypress can't drift the camera.
- The shared FreeCamControlState struct flows through Editor via
  editor.setFreeCamState(state); the render loop reads it and
  calls freeCamFrameTick() each frame.

Side effects: keybinds_controls.ts now imports
isPointerLockSupported from browserChecks; helper.ts and
PageEditor's camera-select handler stop poking
.cameraViewControls and reset velocity via freeCamState instead.
PreviewEngineCamera's camera-view toggle now just flips the store
state — useFreeCam picks it up on its own (no more
enableFreeCamControls/disableFreeCamControls call site).
2026-04-29 09:55:40 -07:00
Tanish Manku 718b8261fd PageScene: extract cameraMath + browserChecks modules
Pure-function helpers carved out of FreeCam in preparation for the
hook rewrite:

- engine/cameraMath.ts: HeldMoveKeys / HeldRotateKeys types,
  moveVectorFromKeys, rotationVectorFromKeys, panFromDrag,
  zoomFromWheel, lookAtFromCamera, lerpVelocity. No DOM access, no
  signals.
- engine/browserChecks.ts: isSafariOrWebKit, hasApplePay, isSafari,
  isPointerLockSupported. Unrelated to camera math; were just
  hitchhiking in free_cam.ts.

free_cam.ts re-exports the browser checks for back-compat with
keybinds_controls.ts; both consumers will switch to the direct
imports when free_cam.ts is deleted in the next commit.
2026-04-29 09:50:16 -07:00
Tanish Manku 61c4936c29 PageScene: useEditorCanvas hook + tabIndex on viewport canvas
The Phase 6 hooks (useFreeCam, useViewportPointer, useViewportKeyboard)
need a React-shaped accessor for the canvas DOM node so their
useEffects re-run when the canvas mounts/unmounts. Promote the
existing editorCanvasSignal / camViewCanvasSignal into useEditorCanvas
/ useCamViewCanvas hooks that bridge signal -> React state via a
subscribe.

Add tabIndex={0} and outline:none to the editor canvas so it can
receive keyboard focus without showing a focus ring.
2026-04-29 09:48:59 -07:00
Tanish Manku c23e652499 PageScene: wire camera-view toggle button
handleButtonCameraView in PreviewEngineCamera was a TODO no-op.
Flip editorState (EDIT <-> CAMERA_VIEW) via the store, then call
editor.enableFreeCamControls() / disableFreeCamControls() on the
active editor. Drop the stale console.log useEffect that fired on
every state change.
2026-04-29 09:48:02 -07:00
Tanish Manku a26ebce483 PageScene: delete video generation entirely
The 3D editor no longer generates videos. Remove:

- engine/video_generation.ts (the VideoGeneration class)
- comps/GenerateModals/ (modal + MyMovies/Sharing/Activity/cards)
- comps/PreviewFrameImage.tsx (PREVIEW-state-only preview overlay)
- comps/ControlsTopButtons/TestFeaturesButtons.tsx (dev-only,
  rendered with debug={false})
- ~/components/signaled/TopBar/MyMoviesButton.tsx (was orphaned;
  no consumer)

editor.ts: drop the VideoGeneration import, field, instantiation,
and the dead helpers it powered (useCachedMediaTokens,
stopPlaybackAndUploadVideo, switchPreview/switchEdit, generateFrame,
generateVideo, checkAndUseCache).

PageSceneStore: drop previewSrc/viewMyMovies/generateMovieId/
generateProgress fields and setters.

EditorStates: drop the PREVIEW variant; PageEditor's state machine is
now EDIT <-> CAMERA_VIEW only. PREVIEW was set only by the deleted
video_generation.ts and read by PreviewFrameImage (deleted) and a few
gates in PreviewEngineCamera + ControlPanelSceneObject (now removed).

Net: -8 typecheck errors.
2026-04-29 09:47:14 -07:00
Tanish Manku 1d3da065a2 PageScene: drop dead side-panel store fields
sidePanelWidth, sidePanelVisible, dndSidePanelWidth and their setters
are no longer read by any rendered component. Drop them from
PageSceneStore and the lone setSidePanelWidth(340) call in
PageEditor's mount effect. PreviewFrameImage's width calc no longer
subtracts the (always-zero) panel width.
2026-04-29 09:42:38 -07:00
Tanish Manku b4a3eb88bf PageScene: delete dead side-panel tree
The <SidePanel /> component was never rendered from PageEditor.tsx; the
entire comps/SidePanel/, comps/SidePanelMenu/, and most of comps/SidePanelTabs/
tree was unreachable. Relocate the few files still consumed by
AssetModal (sharedComps/ItemElement{,s}.tsx, hooks/useUserObjects,
hooks/useFeaturedObjects, utilities/fetchMediaItems + misc) to
comps/AssetMenu/{shared,hooks,utilities}/, and the StyleOptions panel
used by StyleMoreOptions to its parent folder. Then delete the rest.

Net: -33 typecheck errors (the dead tree had its own type problems).
2026-04-29 09:41:21 -07:00
Tanish Manku 48ab5f73ee Phase 7: rename Editor/ -> engine/ and drop dead useSignals() calls
Editor/ -> engine/:
- Rename pages/PageScene/Editor/ -> pages/PageScene/engine/
- Update every "./Editor/...", "../Editor/...", "../../Editor/...",
  "~/pages/PageScene/Editor/..." import path. The class name `Editor`,
  `EditorState` types, and `editorState` field are unchanged -- only
  the directory is renamed
- ImageTo3DExperience also gets the new path

useSignals() drops where the component reads zero .value signals:
- comps/GenerateModals/Activity.tsx
- comps/SidePanelTabs/hooks/useFeatureFlags.tsx
- comps/SidePanelTabs/sharedComps/ItemElement.tsx
- comps/SidePanelTabs/tabComps/StylizeTab/PageStyleSelection.tsx
- comps/StyleMoreOptions/StyleMoreOptions.tsx

The remaining useSignals() callers still subscribe to genuine signals
(pageHeight/pageWidth from ~/signals, outlinerIsShowing,
sceneContainerSignal, etc.) and need to stay until those signals migrate.

Final grep audit confirms no residue:
- "PageEnigma" -- 0 hits
- "from \"...Editor/...\"" import paths -- 0 hits
- Queue.publish / Queue.subscribe / fromEngineActions /
  toEngineActions / toTimelineActions -- 0 hits
- TimeLine, AudioEngine, TransformEngine, EmotionEngine, LipSyncEngine,
  CharacterAnimationEngine, ClipUI, TimelineState -- 0 hits

Typecheck unchanged at 144 errors in PageScene; zero new errors.
2026-04-28 23:47:34 -07:00
Tanish Manku 76add858c9 PageEnigma -> PageScene: rename to match the route param and the page title
The directory was named after a stale codename. The route is
/$sceneToken, the top bar reads "Edit Scene", and the page is the main
scene-editing workspace. Renaming the directory, top-level component,
and Zustand store accordingly:

- pages/PageEnigma/ -> pages/PageScene/
- pages/PageScene/PageEnigma.tsx -> PageScene.tsx
- pages/PageScene/PageEnigmaStore.ts -> PageSceneStore.ts
- usePageEnigmaStore -> usePageSceneStore (function rename across the
  whole codebase via sed)
- All ~/pages/PageEnigma/* import paths -> ~/pages/PageScene/*
- Component name PageEnigma -> PageScene in routes/_index.tsx and
  routes/$sceneToken.tsx

234 files touched, all mechanical. Typecheck error count holds at 144;
zero new errors introduced by the rename.
2026-04-28 23:44:26 -07:00
Tanish Manku e39e67195e PageEnigma refactor Phase 5g: migrate stylizeTab signal slice to the store
stylizeTab.ts (deleted). 11 consumers migrated:

UI components:
- StyleOptions: cinematic / upscale / faceDetail / lipSync /
  enginePreProcessing toggles drive the matching store setters; the
  upscale<->cinematic mutual-exclusion logic preserved
- StyleStrength: slider + number input read styleStrength from store,
  call setStyleStrength
- Prompts: textBufferPositive / textBufferNegative now positivePrompt /
  negativePrompt on the store; isUserInputPositive /
  isUserInputNegative / showNegativePrompt as fields with setters
- IPAdapter: adapterImage + globalIPAMediaToken from store; useShallow
  subscribes to all four needed fields/setters in one selector
- StylizeTab: selectedArtStyle from store, setSelectedArtStyle replaces
  the old setArtStyleSelection helper
- StyleSelection: same pattern -- selectedArtStyle and
  setSelectedArtStyle
- GenerateMovieButton: snapshots the entire stylize bag from the store
  with usePageEnigmaStore.getState() inside the click handler

Engine + persistence:
- SceneMetadata.getSceneGenerationMetaData reads cameraAspectRatio,
  globalIPAMediaToken, upscale, faceDetail, styleStrength, lipSync,
  cinematic, enginePreProcessing all from the store in one getState()
- save_manager.loadFromJson restores all stylize state through the
  store's setters instead of the old restoreSceneGenerationMetadata
  signal helper. globalIpAdapterImageMediaToken restoration also goes
  through setGlobalIPAMediaToken
- video_generation: setGlobalIPAMediaToken on upload + reads
  globalIPAMediaToken from store for the gen request payload
- ControlsTopButtons: new-scene flow calls
  usePageEnigmaStore.getState().resetSceneGenerationMetadata()

Bonus cleanup:
- SidePanelTabs reverted to inlining pageHeight - 64 (the previous
  edit got lost in a stash round-trip)
- IPAdapter dropped useSignals and uses useShallow for its 4-field
  selector

Net effect on typecheck: 145 -> 144 errors in PageEnigma.
2026-04-28 23:28:55 -07:00
Tanish Manku ea49ddf60a PageEnigma refactor Phase 5f: migrate sizing slice to the store
sizing.ts (deleted). Was already minimal -- just sidePanelWidth, the
last survivor after the timeline-related sizing exports were stripped
in Phase 2. Three consumers migrated:

- PageEditor's mount-time setup: store.setSidePanelWidth(340)
- PreviewFrameImage: pulls sidePanelWidth off the store alongside
  sidePanelVisible
- SidePanel: same pattern (already reading sidePanelVisible /
  dndSidePanelWidth from the store; sidePanelWidth joins them)
- StyleSelection: subscribes to sidePanelWidth via the store

signals/ down to 10 files: camera, demoAssets, dragAndDrop, engine,
objectPanel, outliner, precisionSelectorMenu, promptBox, stylizeTab.
2026-04-28 23:22:57 -07:00
Tanish Manku e0c04efa8f PageEnigma refactor Phase 5e: migrate selectedMode + sidePanel slices
selectedMode.ts (deleted):
- PoseModeSelector reads poseMode + showPoseControls from store, calls
  setPoseMode to toggle
- keybinds_controls migrates the t/r/g hotkeys (mode change),
  x (transform space toggle), 'Escape' (exits pose mode), object
  selection (sets showPoseControls), and FK toggle to the store
- editor.change_mode + toggleTransformSpace read/write transformSpace
  through usePageEnigmaStore.getState()
- Controls3D's mode dropdown + world/local pill read selectedMode and
  transformSpace from the store
- actions/setTransformMode no longer dual-writes the legacy signal
- store's PoseMode union changed from "select" | "edit" to
  "select" | "pose" to match what the engine actually checks

sidePanel.ts (deleted):
- SidePanel reads sidePanelVisible + dndSidePanelWidth from store
- TabTitle's close button calls setSidePanelVisible
- SidePanelMenu pulls sidePanelVisible + setSidePanelVisible
- PreviewFrameImage reads sidePanelVisible (was a signal, now store)
- sidePanelHeight (was a computed signal of pageHeight - 64) inlined
  at its three call sites: PreviewEngineCamera, SidePanelTabs,
  StyleSelection. cameraFilter has no consumers; it lives on the store
  for the next round.

signals/ down to 11 leaves: camera, demoAssets, dragAndDrop, engine,
objectPanel, outliner, precisionSelectorMenu, promptBox, sizing,
stylizeTab.
2026-04-28 23:21:13 -07:00
Tanish Manku dc140c0bc1 PageEnigma refactor Phase 5d: migrate hotkeys + selectedItem signal slices
hotkeys.ts (deleted):
- keybinds_controls reads usePageEnigmaStore.getState().hotkeyStatus.disabled
- ControlPanelSceneObject pulls disableHotkeyInput / enableHotkeyInput
  off the store and passes them to InputVector
- PageEditor pulls the same and passes them to LoginModal +
  PromptBox3D. DomLevels imported from PageEnigmaStore (replacing the
  signals/hotkeys re-export)

selectedItem.ts (deleted):
- No external consumers (dead since the timeline removal).
  selectedObject lives on the store; the legacy signal had no readers
  outside its own file.

signals/index.ts no longer re-exports either slice. Down to 13 files
in signals/.
2026-04-28 23:15:45 -07:00
Tanish Manku 21d3617970 PageEnigma refactor Phase 5c: migrate assetModal signal slice to the store
assetModal.ts (deleted):
- AssetMenu: setAssetModalVisibleDuringDrag(true) on open
- AssetModal: reads assetModalVisible / assetModalVisibleDuringDrag /
  reopenAfterDrag from the store, writes via the matching setters.
  Modal close drives setAssetModalVisible(false); the reopen-after-add
  checkbox writes through setReopenAfterDrag (note: store field is
  reopenAfterDrag, not the old reopenAfterDragSignal name)
- Controls3D: handleOpenModal flips both setters from the store; the
  empty-scene tooltip useEffect reads assetModalVisible from the store
- keybinds_controls: 'b' shortcut opens the asset modal via the store
- DragAndDrop/DndAsset: pointerdown clears
  assetModalVisibleDuringDrag, pointerup restores it from
  reopenAfterDrag, click (no drag) restores it to true. All through
  the store now.

signals/index.ts no longer re-exports assetModal. Four signal files
gone so far in Phase 5; signals/ now down to 13 leaves (camera,
demoAssets, dragAndDrop, engine, hotkeys, objectPanel, outliner,
precisionSelectorMenu, promptBox, selectedItem, selectedMode,
sidePanel, sizing, stylizeTab).
2026-04-28 23:13:57 -07:00
Tanish Manku 11a0ac82b6 PageEnigma refactor Phase 5b: migrate error, myMovies, editorOverlays signal slices
Three more signal files retired in favour of the Zustand store.

error.ts (deleted):
- ErrorDialog (in ~/components/signaled): reads showErrorDialog /
  errorDialogTitle / errorDialogMessage from the store, calls
  setShowErrorDialog
- SceneTitleInput (in TopBar): handleShowErrorDialog uses the store's
  setErrorDialog action

myMovies.ts (deleted):
- MyMoviesButton (in TopBar): writes via setGenerateMovieId /
  setViewMyMovies
- GenerateModals: useEffect-on-store replaces useSignalEffect; reads
  generateMovieId from store
- MyMovies and Sharing: read viewMyMovies from store, close via
  setViewMyMovies
- Sharing also reads generateMovieId from store

editorOverlays.ts (deleted):
- PageEditor's LoadingDots: reads editorLoader from store
- SceneContainer: reads editorLetterBox from store and passes through
  to Letterbox
- PreviewEngineCamera: reads editorLetterBox + toggleEditorLetterBox
  from store
- save_manager: showEditorLoader/hideEditorLoader become thin wrappers
  over the store actions, keeping the same call sites

signals/index.ts no longer re-exports any of the three deleted files.
2026-04-28 23:11:52 -07:00
Tanish Manku 4b0618e50b PageEnigma refactor Phase 5a: migrate engine.ts signal slice to the store
Migrates components and engine code from reading signals/engine.ts
(cameraAspectRatio, editorState, gridVisibility, previewSrc) to reading
the Zustand store.

Components migrated to usePageEnigmaStore reads:
- AspectRatioMenu (also dropped useSignals + collapsed two
  duplicated lookup ladders into ICON_BY_RATIO / LABEL_BY_RATIO)
- PreviewFrameImage
- Letterbox (no longer reactive via signals; reads camAspect from store)
- StyleButtons (editorState, previewSrc, setEditorState)
- Outliner (cameraAspectRatio)
- SidePanelMenu (editorState + setEditorState)
- ControlPanelSceneObject (editorState)
- PreviewEngineCamera (cameraAspectRatio + editorState)
- PageEditor (cameraAspectRatio for getScale; the signal version still
  flows into PromptBox3D as a prop)

Engine code migrated to usePageEnigmaStore.getState():
- editor.ts: snapShotOfCurrentFrame reads cameraAspectRatio + grid;
  switchPreview / switchEdit set editorState
- helper.ts: switchCameraView writes editorState
- video_generation.ts: generateFrame writes previewSrc + editorState
- SceneMetadata.ts: cameraAspectRatio for snapshot metadata
- scene.ts: subscribes to store gridVisible (not the signal)

signals/engine.ts becomes a thin compatibility layer for PromptBox3D's
signal-typed props. Its setters dual-write into the store so any
PromptBox3D-driven changes (grid toggle, aspect ratio dropdown) keep
the store authoritative. editorState / previewSrc / setEditorState
removed entirely -- nothing outside the store needs them anymore.

Net effect on typecheck: 148 -> 147 errors in PageEnigma. Zero new
errors introduced.
2026-04-28 23:05:54 -07:00
Tanish Manku 35aae54d49 fix: let UploadSuccess actually render in upload modals
The auto-success useEffect in UploadModalSplat / UploadModalImage /
UploadModal3D was firing on UploaderStates.success and immediately
calling onClose(), which short-circuited the UploaderModalContent
switch before the UploaderStates.success branch (UploadSuccess) could
render. The OK button on UploadSuccess was effectively dead code.

Move the success-side actions (open the gallery, fire onSuccess, close
the modal) into the UploadSuccess onOk handler. The user now sees the
"upload successful" dialog and can dismiss it intentionally; the
gallery opens at dismissal so they can drag the freshly uploaded asset
onto the canvas.
2026-04-28 22:56:38 -07:00
Tanish Manku 088edd270f UploadModalSplat: open gallery on success to match image / 3D-model upload
Pre-refactor the splat upload modal had two paths: the (developer-only)
local-bytes path that handed the in-memory buffer to a now-deleted
editor.timeline.addLocalSplat, and a real upload path that registered
the file in Storyteller's media library but did nothing else with it.

The local-bytes branch was the only thing letting a freshly uploaded
splat reach the canvas. With it gone, splat upload was a dead end --
the file landed in the user's media library but they had no path back
to the 3D editor.

Align with the existing image / 3D-model upload convention: on
UploaderStates.success, set galleryModalVisibleViewMode and call
onSuccess(selectedCategory). The user can then drag the freshly
uploaded splat from the auto-opened gallery onto the canvas, which
flows through PageEditor's onImageDrop handler and into
actions/addObject.

Other changes:
- onSuccess signature: (ArrayBuffer, boolean) => (FilterEngineCategories)
  to match UploadModalImage / UploadModal3D
- Drop the unused onLocalBytes prop and plumbing in UploadFilesSplat;
  no callsite invoked it. The two existing consumers
  (PageEnigma/Controls3D, GlobalFileDropHandler) ignore the category
  argument, which is fine -- () => void is assignable.
2026-04-28 22:50:17 -07:00
Tanish Manku 61311028e2 PageEnigma refactor Phase 4: build actions/ and rewire add/select/transform flows
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.
2026-04-28 22:37:30 -07:00
Tanish Manku 6d22d0d3c3 PageEnigma refactor: strip Queue, TimeLine, and timeline engines from editor.ts
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.
2026-04-28 22:23:11 -07:00
Tanish Manku 50e68b0721 PageEnigma refactor: strip Queue and timeline-only signals from comps and engine helpers
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.
2026-04-28 22:07:31 -07:00