Gestures (Stage + Timeline):
- Pinch-zoom via zoom_delta() (touch pinch + Ctrl+wheel), unified with the raw-wheel
path so Ctrl+wheel zooms exactly once; plain wheel zoom + trackpad pan preserved.
- Double-tap empty → zoom-to-fit (Stage: artboard via zoom_to_fit; Timeline: new
fit_to_project over the full duration).
- Double-tap-drag on empty → transient marquee regardless of the active tool.
Long-press context menu: a shared SharedPaneState.mobile_context_menu that panes
populate with (label, MenuAction) items on secondary_clicked(); the mobile shell
renders one persistent popup (styled like the timeline menu) and dispatches via
pending_menu_actions. Stage offers Cut/Copy/Duplicate/Delete for clip instances and
Cut/Copy/Convert-to-movie-clip/Delete for geometry.
Fixes surfaced along the way:
- Geometry delete now frees selected fills (free_fill) and GCs isolated vertices
(new VectorGraph::gc_isolated_vertices) — no more orphaned fills / phantom snap
points; applies to the Delete key too.
- clipboard_delete_selection clears focus so deleting dismisses the mobile inspector.
- Mobile inspector: appears on pointer release (not press) so drags aren't
interrupted; reflows only when the tapped point is actually behind the sheet; taps
outside the sheet dismiss it; rounded-corner border no longer detaches.
Add Theme::var(name, ctx) (CSS custom-property getter) and Theme::apply_to_egui(ctx)
which maps the palette onto egui's global Visuals so standard widgets share the
theme colors. Add a mobile::palette::Palette built from the theme each frame and
replace the duplicated per-file C_* color constants across all mobile modules
(omni, topbar, stack, inspector, transport, intent, shell) with it. Add --scrim
and --accent-* category vars to styles.css. Apply the theme visuals every frame in
update(). Mobile and the main UI now share one CSS-variable-driven palette that
responds to light/dark/user themes.
- Add `is_mobile` to SharedPaneState (set in build_frame). On mobile the timeline
layer-header column collapses to a minimal type-colored swatch (1:2 w:h, ~30px),
brighter/outlined when active/selected; tapping it selects the layer (handled by
the existing header-column input path, which now uses the narrower width).
Desktop headers unchanged.
- Inspector reflow: the sheet overlaps the stack by default, but when the selected
pane would be covered by the sheet, the stack reflows (renders above the sheet)
so it stays visible. Restore on dismiss is automatic since reflow only changes
the render rect; manual divider moves while the sheet is up persist. Adds
stack::pane_bottom_in and inspector::target_slot.
Rework 3-pane divider behavior and animate every layout change:
- All transitions now ease over ~100ms via a LayoutAnim between two full stack
configs (resize, slide, collapse, fullscreen) instead of jumping; edge
transitions continue from the drag's progress.
- 3-pane dividers are linked along a single path parameter through the three
presets ([.5,.25,.25] / even / [.25,.25,.5]) so both dividers move together
and reach their snaps simultaneously. Over-grow past the extreme preset is
anchored to the path endpoint so the non-dragged divider stays continuous.
- Growing a pane past ~66% collapses the window to 2 panes (dropping the
opposite-end pane); the surviving divider snaps to the nearest 2-pane preset
(1/4, 1/2, 3/4) based on the drop position. Dragging to the slide extreme
still slides the window.
- 2-pane snaps to 1/4, 1/2, 3/4.
After releasing a divider, the boundary now eases (cubic ease-out, ~100ms) from
where the finger lifted to the snapped fraction instead of jumping. Starting a
new drag cancels an in-flight ease; membership changes still apply instantly.
Dragging a divider now resizes its two adjacent panes live and, on release,
snaps the boundary to the nearest of 1/4, 1/3, 1/2, 2/3, 3/4 — so one pane can
be made dominant within a 2- or 3-pane window. Dragging far enough to squeeze a
pane past the collapse threshold still triggers the membership transition
(slide/shrink per R1-R6), after which sizing resets to even.
MobileState gains a `weights: [f32;3]` (normalized on use); config_rects is
weight-aware; edge drags keep animating toward the even-split target.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bundle Lucide (ISC license) at assets/fonts/lucide.ttf and register it as the
egui font family "lucide" via mobile/icons.rs::install (called in EditorApp::new).
Icons are drawn as text with icons::font(size) + PUA codepoint constants.
Switch the stack headers (grip, maximize/minimize, node⇄instrument), the footer
chevron, and the transport play/pause to Lucide glyphs instead of relying on
egui's limited built-in emoji-icon-font.
- Double header height (52px) so the drag targets are easy to grab; footer
stays compact (28px).
- Add a fullscreen button on the right of each header that collapses the
window to that single pane; dragging the sole pane's header down or the
footer up splits it with the adjacent pane (count can now be 1). The button
toggles back to a 2-pane split.
- Use egui's bundled emoji-icon-font glyphs (⛶ fullscreen / ▣ restore) instead
of hand-drawn primitives.
- Cap the drag-to-commit distance (TRIGGER_MAX) so transitions don't require
dragging half the screen.
- Fix panic "attempt to subtract with overflow" when sliding to the end:
the R-ops used then_some(), which eagerly evaluates top-1 even when the
guard is false. Switch to lazy .then(|| ...).
- Replace the thin divider/edge grab bars with full-width header bars: each
band has a labeled header (top of band) that is its drag handle, plus a
reserved bottom footer for the BottomEdge "pull up for more" handle. Band 0's
header = TopEdge, band i's header = the divider above it, footer = BottomEdge.
The Node/Instrument ⇄ toggle lives in that band's header.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per design feedback, drop the top-tabs + single-ribbon model. The mobile UI
is now one vertical stack of all panes in a fixed order (Outliner, Asset
Library, Stage, Timeline, Piano Roll, Virtual Piano, Node/Instrument, Script
editor) with a window of 2-3 consecutive panes visible at a time.
Dragging the dividers between panes and the top/bottom screen edges grows,
shrinks, or slides the window via a small state machine (R1-R6 in stack.rs),
with continuous interpolation during the drag and snap-on-release. The
Node/Instrument band toggles between the node editor and the preset browser.
The transport stays pinned as the bottom floor.
Each visible band reuses PaneInstance::render_content full-bleed (surface.rs).
Removes topbar.rs and ribbon.rs; rewrites mod.rs state (window_top,
window_count, show_instruments, drag); adds stack.rs.