Skip to content

feat(studio): canvas context menu and z-order actions (unwired)#2281

Open
miguel-heygen wants to merge 3 commits into
studio-dnd/pr10-gesture-characterization-testsfrom
studio-dnd/pr11-canvas-zorder-menu
Open

feat(studio): canvas context menu and z-order actions (unwired)#2281
miguel-heygen wants to merge 3 commits into
studio-dnd/pr10-gesture-characterization-testsfrom
studio-dnd/pr11-canvas-zorder-menu

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What

Brief description of the change.

Why

Why is this change needed?

How

How was this implemented? Any notable design decisions?

Test plan

How was this tested?

  • Unit tests added/updated
  • Manual testing performed
  • Documentation updated (if applicable)

What: CanvasContextMenu (right-click menu for canvas selections) and
canvasContextMenuZOrder (tie-aware bring-forward/send-backward z-order patch
computation) with its test suite. Shipped unwired.

Why: the z-order rules are the substance; mounting is one line in the later
overlay swap.

How: new files, compiled against current main. Nothing mounts the menu yet,
so .fallowrc.jsonc gains TEMP(studio-dnd) entries (entry registration +
ignoreExports) — removed by the app-shell swap PR that wires everything.

Test plan: bunx vitest run canvasContextMenuZOrder.test.ts; tsc --noEmit;
fallow audit clean.

miguel-heygen commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

miguel-heygen added a commit that referenced this pull request Jul 12, 2026
* feat(studio): timeline collision and placement model

What: new pure module timelineCollision — zone-aware drop placement
(clampTrackToZone, resolveZoneDropPlacement, resolveInsertRow,
resolvePlacement, lane/overlap predicates) with its full test suite.

Why: the no-overlap core of the NLE clip-drag engine; plain functions, no
DOM, no React, no store writes.

How: new files only; type-only imports from the existing playerStore.
First runtime consumer arrives with the drag-engine PRs.

Test plan: bunx vitest run timelineCollision.test.ts; tsc --noEmit; fallow
audit clean (all exports test-consumed).

* feat(studio): timeline magnetic snapping

What: new pure module timelineSnapping — snap-target collection and
pixel-threshold time snapping (collectTimelineSnapTargets, snapTimelineTime,
snapMoveToTargets) with tests.

Why: the magnet math for clip drags/trims, reviewable standalone.

How: new files only; type-only playerStore imports; consumers land with the
drag engine.

Test plan: bunx vitest run timelineSnapping.test.ts; tsc --noEmit; fallow
audit clean.

* feat(studio): multi-clip drag preview math

What: new pure module timelineMultiDragPreview — group-drag passenger
offsets and clamped group deltas (isMultiDragActive, multiDragDeltaSeconds,
multiDragPassengerOffsetPx, clampGroupMoveDelta) with tests.

Why: the group-drag math, standalone and DOM-free.

How: new files only; consumed later by TimelineLanes.

Test plan: bunx vitest run timelineMultiDragPreview.test.ts; tsc --noEmit;
fallow audit clean.

* feat(studio): timeline z-stacking sync model

What: new pure module timelineStackingSync — lane order ↔ z-index
reconciliation (laneIsAbove, computeStackingPatches) with tests.

Why: the single source of truth for how timeline lane order maps to canvas
stacking; the ordering rules and tie-breaks live here.

How: new files only; consumed later by timelineZones and the stacking-sync
hook.

Test plan: bunx vitest run timelineStackingSync.test.ts; tsc --noEmit;
fallow audit clean.

* feat(studio): timeline lane-zone model

What: new pure module timelineZones — visual/audio track-zone
classification (classifyZone) and normalizeToZones, which re-packs lanes
into zone-consistent rows; tests cover the stacking/zones interaction.

Why: completes the z-model started in the stacking-sync PR.

How: new files; consumes isAudioTimelineElement (leaf-helpers PR) and
computeStackingPatches (stacking-sync PR); type-only playerStore imports.

Test plan: bunx vitest run timelineZones.test.ts; tsc --noEmit; fallow
audit clean.

* feat(studio): asset click policy and canvas nudge gate

What: two small pure modules with tests — assetClickBehavior (click vs
double-click policy for sidebar assets) and canvasNudgeGate (debounce gate
for arrow-key canvas nudges).

Why: policy dependencies of the upcoming asset card and nudge hook,
reviewable as plain decision tables.

How: new files only.

Test plan: bunx vitest run on both test files; tsc --noEmit; fallow audit
clean.

* test(studio): characterization suites for resize commit and razor history

What: two test-only suites pinning CURRENT behavior before the NLE swap:
anchoredResizeReleaseShift.test.ts (manual-offset resize release commits)
and useRazorSplit.history.test.tsx (razor split undo/redo history).

Why: regression tripwires — the later glue-swap PRs must keep these green.

How: test files only; they import existing main modules unchanged and pass
against them as-is.

Test plan: bunx vitest run on both suites; fallow audit clean.

* feat(studio): canvas context menu and z-order actions (unwired)

What: CanvasContextMenu (right-click menu for canvas selections) and
canvasContextMenuZOrder (tie-aware bring-forward/send-backward z-order patch
computation) with its test suite. Shipped unwired.

Why: the z-order rules are the substance; mounting is one line in the later
overlay swap.

How: new files, compiled against current main. Nothing mounts the menu yet,
so .fallowrc.jsonc gains TEMP(studio-dnd) entries (entry registration +
ignoreExports) — removed by the app-shell swap PR that wires everything.

Test plan: bunx vitest run canvasContextMenuZOrder.test.ts; tsc --noEmit;
fallow audit clean.

---------

Co-authored-by: ukimsanov <ular.kimsanov@heygen.com>
ukimsanov and others added 2 commits July 12, 2026 00:20
What: domEditNudge — pure nudge-step math (direction × modifier → delta,
commit debounce constant) with tests. Shipped unwired.

Why: small standalone concept consumed by the canvas nudge hook in the
chrome-components PR.

How: new files; one TEMP(studio-dnd) ignoreExports entry until the consumer
lands (removed by the app-shell swap PR).

Test plan: bunx vitest run domEditNudge.test.ts; tsc --noEmit; fallow audit
clean.
…s (unwired) (#2282)

* feat(studio): arrow-key nudge step math (unwired)

What: domEditNudge — pure nudge-step math (direction × modifier → delta,
commit debounce constant) with tests. Shipped unwired.

Why: small standalone concept consumed by the canvas nudge hook in the
chrome-components PR.

How: new files; one TEMP(studio-dnd) ignoreExports entry until the consumer
lands (removed by the app-shell swap PR).

Test plan: bunx vitest run domEditNudge.test.ts; tsc --noEmit; fallow audit
clean.

* feat(studio): NLE shell components — context provider, panes, overlays (unwired)

What: the NLE shell layer, unwired: NLEContext (provider extracted from
NLELayout), PreviewPane, AssetPreviewOverlay + assetPreviewStore,
TimelineOverlays (menus/modal/hint extracted from Timeline),
timelineClipChildren, timelineClipDragTypes, useTimelinePlayerLoop
(playback loop extracted from useTimelinePlayer).

Why: the extracted replacements for NLELayout/StudioPreviewArea and the
oversized Timeline/useTimelinePlayer internals, reviewable standalone.

How: new files only, tsc-clean against main. They intentionally duplicate
blocks of their still-alive originals (duplication is warn-level in fallow;
the swap PRs delete the originals). Dead-file findings covered by
TEMP(studio-dnd) entry registrations, removed at the app-shell swap.

Test plan: tsc --noEmit; bunx vitest run (suite unchanged); fallow audit
clean.

---------

Co-authored-by: ukimsanov <ular.kimsanov@heygen.com>
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.

2 participants