2026 04 19_graph_canvas_overlays_and_camera_relands_plan - mark-ik/graphshell GitHub Wiki
Status: Archived 2026-04-20 โ every section landed. ยง2, ยง2.1
(Miro-style after Mark picked the convention), ยง3, ยง4 at the
graph-canvas layer + ยง4 host wiring (canvas_bridge + egui painter)
all shipped. Navigation-policy tuning that grew out of ยง2 was
lifted into its own plan:
2026-04-20_navigation_policy_plan.md (sibling in this checkpoint).
Scope: Re-land the five orphaned-but-doable features identified during
the egui_graphs retirement work. These were pre-existing features that were
silently disabled when the graph-canvas live path landed and egui_graphs
was retired. This plan re-lands them on the CanvasCamera + ProjectedScene
seam so they work uniformly across egui and (future) iced hosts.
Parent: ../../../archive_docs/checkpoint_2026-04-19/graphshell_docs/implementation_strategy/shell/2026-04-18_egui_graphs_retirement_plan.md ยง5.6 (archived 2026-04-19).
Standards bar: Firefox-consistent infinite-canvas navigation (plain
wheel pans, Ctrl/Cmd+wheel zooms, middle-click pans); accessible via
keyboard where applicable; overlays emitted as packet draw items so all
hosts paint them the same way.
Five plumbing-only features to re-land, each a thin translation on top of already-landed infrastructure:
| Feature | Lands on | Approximate size |
|---|---|---|
| Background pan (primary drag) |
CanvasCamera::pan + input translation |
~30 LOC |
| Plain wheel โ pan | canvas_bridge::collect_canvas_events |
~20 LOC |
Ctrl/Cmd+wheel โ zoom (also trackpad pinch) |
CanvasCamera::zoom via new event variant |
~40 LOC |
| Middle-click drag โ free pan |
canvas_bridge::collect_canvas_events + engine gesture |
~50 LOC |
| Pan inertia on release | New CanvasCamera sidecar state |
~80 LOC |
| Fit / FitSelection / FitGraphlet |
CanvasCamera::zoom + pan target |
~120 LOC |
| Frame affinity backdrops |
ProjectedScene overlay items |
~100 LOC |
| Scene region backdrops (Arrange/Simulate) | Same packet-overlay path | ~80 LOC |
| Highlighted edge overlay | Same packet-overlay path | ~60 LOC |
Total: ~580 LOC. No new dependencies. All additive on top of the landed
graph-canvas::{camera, packet, engine} modules and canvas_bridge.
When the user presses the primary mouse button on an empty area of the graph (not a node, not a lasso gesture) and drags, pan the camera.
- Add
CanvasInputEvent::PointerDrag { delta: Vector2D<f32>, button: PointerButton, modifiers: Modifiers }or reuse the existingPointerMoved+downstate. -
InteractionEnginerecognizes drag-on-background and emitsCanvasAction::PanCamera(delta). (The action already exists;canvas_bridge::apply_panalready applies it.) - Host translation: in
canvas_bridge::collect_canvas_events, emit the portable drag events when egui reports a pointer drag and no node is hovered.
Infinite-canvas convention: scrolling the wheel pans the view. See feedback_graph_canvas_navigation_defaults.md.
-
CanvasInputEvent::Scrollalready carriesdelta,position,modifiers. - Engine: when
modifiers.ctrlis false, translate scroll delta toCanvasAction::PanCamera. When true, it stays asZoomCamera(below).
- Most trackpads synthesize
ctrlKey = trueon pinch gestures, so this binding covers both physical key+wheel and native pinch. - The existing
CanvasAction::ZoomCamera { factor, focus }already exists. Engine routesScrollwithmodifiers.ctrlto it. - Default zoom step:
1.1^(delta / 120.0)โ matches Figma / typical infinite-canvas feel.
- New or repurposed
PointerButton::Middlehandling. - Engine recognizes middle-button-drag and always emits
PanCamera, regardless of what's under the cursor (unlike primary drag which pans only on background).
- When primary drag ends or middle-click drag ends with a non-trivial velocity, start a per-frame decay.
- New
CanvasCamerasidecar:pan_velocity: Vector2D<f32>. - Each frame:
pan += pan_velocity * dt; pan_velocity *= damping. Stop when|pan_velocity| < min. - Keep on desktop per memory โ reinforces the spatial/physical feel of
the force-directed graph.
dampingdefault 0.88.
- Drag on empty area: camera pans; nodes visually stay in world.
- Scroll with no modifier: camera pans vertically.
- Scroll with
Ctrl: camera zooms centered on cursor. - Middle-drag: free-pan regardless of hover target.
- Release after drag: camera continues briefly, decays smoothly.
Three commands that compute a bounding box of some node set and position the camera to frame it:
- Fit โ all visible nodes.
- FitSelection โ currently selected nodes.
- FitGraphlet โ members of the active graphlet projection.
Each is a one-shot camera move.
Route the existing CameraCommand enum through a new canvas_bridge helper:
pub fn apply_fit_to_bounds(
camera: &mut CanvasCamera,
bounds: Rect<f32>,
viewport: &CanvasViewport,
padding_ratio: f32, // e.g. 1.08 for 8% margin
) -> Option<()>Compute:
fit_zoom = min(viewport.w / bounds.w, viewport.h / bounds.h) / padding_ratiopan = viewport_center โ bounds_center ร fit_zoom- Clamp
fit_zoomto the view'scamera.zoom_min / zoom_max.
The three Fit flavors differ only in which bounds they pass in. Bounds
come from petgraph node_projected_position โ no dependency on the
retired egui_state.
- Fit on an empty graph is a no-op.
- Fit with one node centers that node at the current zoom.
- FitSelection with a pin-locked camera respects the lock (no-op or emits the existing diagnostic channel).
- FitGraphlet falls back to Fit when no graphlet is active.
Three overlay kinds that today have zero presence on the live path:
- Frame affinity backdrops โ one colored rounded rect per frame region, behind the nodes, labeled with the frame name.
- Scene region backdrops โ one shape (rect or circle) per scene region, styled by effect kind (attractor, repulsor, dampener, wall).
- Highlighted edge overlay โ when the user focuses an edge, a thicker stroke with optional tooltip endpoint markers.
Graph-canvas's ProjectedScene already has a overlays: Vec<SceneDrawItem>
slot that gets painted between world and hit-proxy layers. Each overlay
kind becomes an SceneDrawItem emitted during derive_scene.
Concretely, extend the derive module:
// crates/graph-canvas/src/derive.rs
pub struct DeriveConfig {
// ...existing fields...
pub frame_regions: Vec<FrameAffinityBackdrop<N>>,
pub scene_regions: Vec<SceneRegionBackdrop>,
pub highlighted_edge: Option<HighlightedEdge<N>>,
}Each backdrop carries: shape, fill color, stroke color, optional label.
derive_scene projects them through the camera and emits
SceneDrawItem::{Rect, Circle, Text} entries into overlays.
canvas_egui_painter::paint_projected_scene already iterates
scene.overlays and paints each SceneDrawItem. Adding new draw-item
shapes requires the painter to cover them; if Rect, Circle, and Text
are already handled, this is zero host work.
- Frame backdrops appear behind nodes, not in front. Z-order via overlay layer index.
- Backdrops survive camera pan/zoom with no jitter (projected through
CanvasCamera, not layered on top of the egui UI separately). - iced host inherits backdrops for free when M5 lands, since they're emitted as packet items not egui painter calls.
- Camera input re-lands (ยง2) โ lowest risk; all five features share a shape. One PR.
- Fit commands (ยง3) โ one PR; builds on ยง2's
CanvasCameratargeting. - Overlays (ยง4) โ separate PR; touches
derive_sceneand potentiallycanvas_egui_painter. Each overlay kind (frame, scene, edge) can be its own commit.
Each step is self-contained and leaves the tree green.
-
Rebuilding the old MetadataFrame lane. The old camera authority is
retired; this plan strictly uses
CanvasCamera. - Interactive region editing โ just rendering the regions. Interactive drawing/resizing of scene regions is a separate lane covered by ../graph/2026-04-03_physics_region_plan.md.
- Hover tooltips โ covered by the input/accessibility follow-on (2026-04-19_graph_canvas_input_accessibility_followon_plan.md).
-
Plan created after egui_graphs retirement and Step 2โ6 layout landings. Ready to execute; no design discussions pending.
-
Navigation-defaults slice landed later the same day. Delivers the wheel/zoom/inertia behavior pinned in feedback_graph_canvas_navigation_defaults.md. Scope covered from this plan: ยง2.2 (plain wheel โ pan), ยง2.3 (
Ctrl/Cmd+wheel โ zoom, including trackpad pinch via synthesizedctrlKey), ยง2.4 (middle-click free pan โ was already working in the engine's drag state machine), and ยง2.5 (pan inertia on release).Specific changes:
-
crates/graph-canvas/src/camera.rs:
added
CanvasCamera::pan_velocity: Vector2D<f32>with#[serde(default)]for backward-compatible deserialize; newtick_inertia(dt, damping_per_second)method applies velocity, decays exponentially, snaps to zero atPAN_VELOCITY_EPSILON; exportedDEFAULT_PAN_DAMPING_PER_SECOND = 0.003(โ500ms settle at 60fps). -
crates/graph-canvas/src/engine.rs:
Scrollhandler now branches onmodifiers.ctrlโ plain scroll emitsPanCamerascaled byscroll_pan_pixels_per_unit(default 50 px/unit), Ctrl-scroll continues to emitZoomCamera.DragState::Panninggained alast_world_deltafield; release emits a newCanvasAction::SetPanInertia(velocity_per_second)whenInteractionConfig::pan_inertia_enabled(defaulttrue) and the terminal delta is abovePAN_VELOCITY_EPSILON. Velocity estimate islast_delta * 60.0โ the engine has no real frame clock, so assumes 60 Hz tick cadence, which is adequate for feel. -
crates/graph-canvas/src/interaction.rs:
new
CanvasAction::SetPanInertia(Vector2D<f32>)variant. -
render/canvas_bridge.rs:
wires
SetPanInertiaintocamera.pan_velocity; tickscamera.tick_inertia(1/60, DEFAULT_PAN_DAMPING_PER_SECOND)once per frame so idle frames keep decaying. Updatedrun_graph_canvas_frame_updates_camera_from_scroll_eventsto exercise both scroll legs (plain scroll โ no zoom; Ctrl-scroll โ zoom > 1.0). Picked up the newpan_velocityfield incamera_from_view_frame(starts zero โ inertia only accumulates from live drags, never persists across view-frame snapshots).
Seven new tests across camera and engine cover: serde-back-compat for cameras without
pan_velocity,tick_inertiaapplies-and-decays, tick snaps to zero under epsilon, plain scroll emits pan (never zoom), Ctrl-scroll emits zoom, pan-release seeds inertia when the feature is on, pan-release skips inertia when disabled. -
crates/graph-canvas/src/camera.rs:
added
-
Still pending in this plan:
-
ยง2.1 Background-pan vs lasso UX. Today primary-drag on empty
background starts a lasso (when
lasso_enabled) โ the plan wants it to pan. Resolving this needs a modifier convention (e.g.,Shift+drag for lasso) or a host-level toggle. Punted: the current defaults preserve existing lasso behavior and the plan's stated "background pan" is covered by middle-click and secondary-drag today, both of which already work. - ยง3 Fit / FitSelection / FitGraphlet โ LANDED (2026-04-19). See the dedicated progress entry below.
-
ยง4 Overlay backdrops.
ProjectedScene.overlaysslot and the egui painter both exist;derive_scenedoesn't populateframe_regions,scene_regions, orhighlighted_edgeyet.DeriveConfigneeds the three new input slots andderive_sceneneeds to project their shapes through the camera. Not landed.
-
ยง2.1 Background-pan vs lasso UX. Today primary-drag on empty
background starts a lasso (when
-
Receipts:
cargo test -p graph-canvas --libcamera+engine 24 passed;cargo test -p graph-canvas --features simulate --lib234 passed;cargo test -p graphshell --lib2144 passed / 0 failed / 3 ignored;cargo check --workspace --exclude servoshell --exclude webdriver_serverclean.
Second /loop iteration on this plan landed ยง3's three Fit variants:
Fit, FitSelection, FitGraphlet โ the CameraCommand enum at
app/graph_app_types.rs is no
longer dormant, and request_camera_command emissions from keyboard
handlers (graph_app.rs:1427/1435) now actually move the camera.
Portable layer โ CanvasCamera::fit_to_bounds added to
crates/graph-canvas/src/camera.rs.
Pure math over a world-space Rect, a viewport, a padding ratio, and
zoom bounds. Fits bounds.center() to the viewport center, picks a
zoom that matches the tighter axis, clamps to [zoom_min, zoom_max],
clears pan_velocity so inertia can't keep coasting past the target,
returns false on zero-area viewport so the host can leave the
pending request in place. Zero-area bounds use the fallback_zoom
argument and still center. Seven new tests cover:
- centering on bounds.center
- padding ratio reduces zoom
- zoom clamped to max when bounds are tiny
- zero-area bounds use fallback + center
- zero-area viewport โ no-op
- inertia cleared on fit
Host layer โ apply_fit_camera_command + bounds_of_nodes
helpers added to render/canvas_bridge.rs.
Computes per-variant bounds against the current CanvasSceneInput:
-
Fitโ bounds of every node in scene, expanded by radius. -
FitSelectionโ bounds ofapp.focused_selection(); falls back toFitwhen selection is empty. -
FitGraphletโ bounds ofview.graphlet_node_mask; falls back toFitSelectionโFitwhen mask is missing or empty. -
SetZoom(factor)โ snaps zoom into the same[0.1, 10.0]range as drag-zoom; preserves pan; clearspan_velocity.
The dispatcher runs once per run_graph_canvas_frame after the
inertia tick (so a Fit always overrides residual coast) and only when
pending_camera_command_target() == view_id. The pending command is
cleared once consumed โ including when the scene is empty, so a
Fit-on-empty-graph doesn't busy-loop. Fit padding ratio is 1.08
(~4 % margin per edge), matching the retired egui_graphs feel.
Host-layer tests โ five targeted tests in
render::canvas_bridge::scene_input_tests:
-
run_graph_canvas_frame_consumes_pending_fit_over_populated_graphโ populated scene +Fitclears the command and moves the zoom away from identity. -
run_graph_canvas_frame_consumes_pending_fit_on_empty_graphโ empty scene still clears the pending command (no busy-loop). -
run_graph_canvas_frame_fit_selection_frames_only_selected_nodesโ two nodes far apart, only one selected;FitSelectionpans to the selected node's neighborhood, not the midpoint. -
run_graph_canvas_frame_fit_selection_falls_back_to_fit_when_no_selectionโ empty selection falls through toFit; zoom moves away from identity on a populated graph. -
run_graph_canvas_frame_set_zoom_clamps_and_clears_pendingโ aSetZoom(50.0)request clamps to the[0.1, 10.0]range and clears the pending command.
Receipts:
-
cargo test -p graph-canvas --lib camera::โ 13/13 pass (was 6/6 before ยง3; 7 newfit_to_bounds_*tests added). -
cargo test -p graphshell --lib render::canvas_bridgeโ 16/16 pass (was 11/11 before ยง3; 5 new Fit dispatch tests added). -
cargo test -p graphshell --libโ 2149/2149 pass (up from 2144, no regressions). -
cargo check -p graphshell --libclean.
Still deferred from this plan:
- ยง2.1 background-pan-vs-lasso UX (needs a modifier convention discussion before landing).
- ยง4 overlay backdrops โ next /loop iteration on this plan will
tackle them: add
DeriveConfigslots forframe_regions,scene_regions,highlighted_edge; populateProjectedScene.overlaysinderive_scene; verify egui painter covers the shape set.
Third /loop iteration on this plan landed ยง4. ProjectedScene.background
and ProjectedScene.overlays layers are now populated with frame-region
discs, scene-region shapes, and a highlighted-edge stroke respectively
โ previously both layers were empty on the live graph path, and all
three overlay kinds had zero presence.
Minor departures from the plan text: the richer inputs live alongside
DeriveConfig rather than inside it. DeriveConfig is non-generic
config (LOD, colors, projection tuning) and the per-frame overlay
inputs need the host's N node-id type. Keeping them separate avoids
making DeriveConfig generic and keeps the shared config cheap to
reuse across frames.
New types, added to crates/graph-canvas/src/derive.rs:
-
OverlayInputs<'a, N>โ borrowed slices for per-frame hints:frame_regions: &'a [FrameRegion<N>],scene_regions: &'a [SceneRegion],highlighted_edge: Option<(N, N)>.Defaultyields empty slices /Noneso hosts that don't care about any overlay kind pay nothing. -
OverlayStyleโ visual tuning (fills, strokes, padding, label font size) that hosts can theme without reimplementing the emitter. Defaults match the feel of the retired egui_graphs backdrops.
New entry point, derive_scene_with_overlays. Takes the usual
derive arguments plus &OverlayInputs<'_, N> + &OverlayStyle and
emits:
- Frame-affinity backdrops โ
ProjectedScene.background(enclosing disc = member centroid + max-distance radius +frame_region_padding, projected to screen viacamera.world_to_screenand scaled bycamera.zoom). Regions with no members in the current scene are dropped โ no empty backdrops. - Scene-region backdrops โ
ProjectedScene.background(CircleorRoundedRect, colored by effect kind, label when present). Regions withvisible: falseare skipped. All geometry projected through the camera so backdrops pan and zoom with the scene. - Highlighted edge โ
ProjectedScene.overlays(oneLinestroke athighlighted_edge_width, colored byhighlighted_edge_color). No-op when either endpoint is missing from the scene โ stale highlights from removed nodes are dropped silently.
The existing derive_scene becomes a thin wrapper around
derive_scene_with_overlays with OverlayInputs::default() and
OverlayStyle::default(). Every existing call site continues to work
unchanged; the host opts in to overlays by switching to the richer
entry point.
Layering rationale. The plan's ยง4.2 text said overlays emit to
ProjectedScene.overlays, but frame and scene backdrops visually
belong behind nodes. Emitting them to background (which paints
before world) matches the visual intent while still flowing through
the same draw-item pipeline the painter already handles. The
highlighted edge stays on overlays so it sits atop regular edges
for emphasis.
Tests โ seven new tests in derive::tests:
-
derive_scene_with_overlays_empty_matches_derive_sceneโ empty overlay inputs produce identical output to the legacy entry point. -
derive_scene_emits_frame_region_backdrop_in_background_layerโ one region with three members โ one disc on the background layer. -
derive_scene_skips_frame_region_with_no_members_in_sceneโ members referencing missing ids emit nothing. -
derive_scene_emits_scene_region_backdrop_circle_and_rectโ mixed shapes plus a hidden region: one circle + one rect + one label (label only on the labeled region), hidden region skipped. -
derive_scene_emits_highlighted_edge_on_overlay_layerโ Some pair โ one line on the overlay layer. -
derive_scene_skips_highlighted_edge_when_endpoints_missingโ dangling id โ no line, no panic. -
derive_scene_projects_backdrops_through_cameraโ world radius 50 at zoom 2.0 produces screen radius 100, confirming the projection math actually runs in the emitters.
Still pending for ยง4 host wiring:
-
render/canvas_bridge.rsdoes not yet compute the overlay inputs from the app'sArrangementRelation(FrameMember)edges, scene regions registry, and hovered-edge state; it still calls the legacyderive_scene. The pipe is ready โ hosts just need to pick upderive_scene_with_overlaysand wire their inputs. Deferred here because the app-side region store is being shaped by a concurrent plan; the portable derivation layer is the load-bearing gap and that's what landed. -
canvas_egui_painter::paint_projected_scenealready iteratesscene.overlaysper the Explore agent's pre-land survey. It does not currently iteratescene.backgroundโ host integration needs to extend the painter to paint the background layer before world items. Trivial (mirror the overlays loop, pre-world), but best-grouped with the host wiring pass above.
Receipts:
-
cargo test -p graph-canvas --lib derive::testsโ 21/21 pass (was 14/14 before ยง4; 7 new overlay tests). -
cargo test -p graph-canvas --features simulate --libโ 247/247 pass (up from 234 before ยง4). -
cargo check --workspace --exclude servoshell --exclude webdriver_serverclean.
Plan scope complete at the graph-canvas layer. ยง2 / ยง3 / ยง4 all landed; the only residual work is ยง2.1 (UX discussion) and the host- side wiring above (separate concern).
Decision: Miro/tldraw-style โ plain primary-drag on empty
background pans the camera; Shift+primary-drag on empty background
starts a lasso marquee. Matches the infinite-canvas framing pinned in
feedback_graph_canvas_navigation_defaults.md
("wheel=pan, middle=pan, treat graph canvas as infinite-canvas doc
not webpage") and keeps Shift as the universal multi-select modifier
consistent with Ctrl-click-toggle on nodes. Right-click stays
unbound at the engine level โ reserved for the context-menu lane the
radial-menu plan wants later.
Implementation, in crates/graph-canvas/src/engine.rs:
-
DragState::Pendinggained amodifiers: Modifiersfield, captured atPointerPressedtime. The threshold-crossing handler inhandle_pointer_movereads it when deciding which gesture to start. - The
(HitTestResult::None, PointerButton::Primary)arm now splits into two sub-arms: withmodifiers.shift && lasso_enabledit launches the lasso (the previous behavior, now gated on Shift); without Shift it falls through to the same pan path that middle- drag and secondary-drag already use. - Middle-drag and secondary-drag on background still pan (unchanged). Node-drag on a node-hit target still drags the node (unchanged).
Tests โ one existing engine test replaced with two clearer ones:
-
plain_primary_drag_on_background_pans(new): press+move without Shift emitsPanCameraand neverLassoBegin;state.lassostaysNone. -
shift_primary_drag_on_background_lassos(replaces the formerlasso_on_background_drag): press+move with Shift emitsLassoBegin, then release emitsLassoCompleteโ same shape as the old test, just gated on the modifier.
Receipts:
-
cargo test -p graph-canvas --lib engine::โ 18/18 pass (was 16/16 pre-ยง2.1; two lasso-on-background tests replaced by the two clearer pairings above). -
cargo test -p graph-canvas --features simulate --libโ 248/248 pass. -
cargo test -p graphshell --libโ 2149/2149 pass (no host-side regressions; the change is purely inside the engine's gesture router).
Plan closed โ ยง2 / ยง2.1 / ยง3 / ยง4 all landed at the graph-canvas
layer. Host-side wiring for ยง4 backdrops (compute overlay inputs in
render/canvas_bridge.rs, extend the egui painter to paint
scene.background) remains as the single follow-on item and is
tracked inline in the ยง4 entry.
After stopping, Mark asked whether the host-side wiring was worth doing given the iced migration; I argued yes for the overlay inputs (host-neutral, iced will reuse them) and yes-and-cheap for the egui painter (3-line mirror of the overlays loop). Landed:
-
Painter was already done. The egui
canvas_egui_painter::paint_projected_scene
already iterates
scene.backgroundbeforescene.world(line 27). The earlier ยง4 entry's claim that the painter needed extending was based on a stale Explore-agent snapshot. No edit was required. -
Host-side overlay input builders added in
render/canvas_bridge.rs:
-
build_portable_frame_regions(app)calls the existingcrate::graph::frame_affinity::derive_frame_affinity_regionsand maps eachFrameAffinityRegionto a portablegraph_canvas::layout::extras::FrameRegion<NodeKey>. Rendered frames now track the same membership the physics pass uses. -
build_portable_scene_regions(app, view_id)walksapp.graph_view_scene_runtime(view_id)?.regionsand converts each app-sideSceneRegionRuntime(egui types,uuid::Uuidids) into a portablegraph_canvas::scene_region::SceneRegion(euclid types,u64ids). Shape, effect, label, and visibility round-trip 1:1.
-
-
ID bridging helper added on the app-side
graph/scene_runtime.rs:
SceneRegionId::as_u64_low()returns the lower 64 bits of the underlying UUID as a stableu64. That's the projection the portableSceneRegionId(pub u64)needs. Collision risk is negligible at the scale of per-view region sets. -
run_graph_canvas_frameswitched fromderive_scenetoderive_scene_with_overlays. Overlay inputs are built once per frame (highlighted edge comes straight fromapp.workspace.graph_runtime.highlighted_graph_edge: Option<(NodeKey, NodeKey)>). DefaultOverlayStyleis used โ hosts can theme later by passing their own.
Upstream blocker โ cargo build -p graphshell --lib currently
fails because the path-dep ../webrender-wgpu/webrender has a
webrender_build/lib.rs that calls shaderc::Compiler::new().ok_or_else(...)
and shaderc::CompileOptions::new().ok_or_else(...). In the current
shaderc release those constructors return Option, not Result, so
the method doesn't exist. This is Mark's in-flight SPIR-V shader
pipeline work (see webrender-wgpu/wr-wgpu-notes/2026-04-18_spirv_shader_pipeline_plan.md),
unrelated to the overlays work here. graph-canvas itself builds
cleanly (cargo check -p graph-canvas --lib and cargo test -p graph-canvas --features simulate --lib both green, 248/248), and
cargo check -p graphshell --lib emits zero errors in any
graphshell-side source โ every reported error is in
webrender_build. Running the full graphshell test suite has to
wait until the upstream path-dep resolves; the host-wiring changes
are in place and syntactically clean pending that verification.
Receipts (partial):
-
cargo check -p graph-canvas --libโ clean. -
cargo test -p graph-canvas --features simulate --libโ 248/248. -
cargo check -p graphshell --libโ zero graphshell-side errors; build blocked by webrender_build (pre-existing, unrelated). - Full
cargo test -p graphshell --libโ deferred until the webrender_build/shaderc API mismatch upstream is resolved.