feat(editor): wave-7 polish -- undo UI + toolbar + asset browser (#20)#213
Merged
havokentity merged 1 commit intoMay 19, 2026
Merged
Conversation
Three editor-side features in one drop:
1. Global keyboard shortcuts (shared/src/keyboard.ts):
- Cmd/Ctrl+Z dispatches scene_undo
- Cmd/Ctrl+Shift+Z (and Ctrl+Y) dispatches scene_redo
- G / R / S flip gizmo_mode translate / rotate / scale
- Handler installed by <Shell>, opt-out via installShortcuts={false},
and skips dispatch when focus is inside <input>/<textarea>/
contenteditable so number-field typing still works
2. Toolbar panel rebuild (panels/toolbar):
- Thin header strip with three groups: gizmo mode, undo/redo,
snap toggle + transform-space selector
- Reads gizmo_mode / gizmo_snap / gizmo_space cvars at mount and
re-polls every 1s so external changes (GLFW G/R/S, console) stay
reflected
- Buttons highlight on matching shortcut press so the user gets
visual feedback when Cmd+Z fires
3. Asset Browser panel (panels/asset-browser):
- Four tabs (Scenes, HDRIs, glTF, Meshes) with file-path filter
- Click-to-load via engine console commands:
Scenes -> exec <path>
HDRIs -> r_env_map <path>
glTF -> mesh_load_gltf <path>
- Drag-start stuffs the dispatch line into
application/x-demont-asset + text/plain so a future native
viewport drop handler can read either MIME and run the line
Engine additions:
- new cvars gizmo_snap (default 0) + gizmo_space (default "world"),
archived so they survive across runs
- new command list_assets <subdir> -- directory_iterator over
tests/goldens/scenes (.cfg/.toml), assets/hdri (.hdr/.exr),
assets/gltf (.glb/.gltf), assets/meshes (.obj/.ply/.stl); emits one
workspace-relative path per line plus a trailing summary; React
client splits on '\n' and drops the parenthesised summary
Tests:
- shared/__tests__/keyboard.test.ts -- 11 tests for the shortcut
dispatcher (jsdom via happy-dom)
- panels/asset-browser/__tests__/helpers.test.ts -- 6 tests for the
list_assets output parser
Smoke-tested on Mac/Metal: the four panel HTMLs serve on
/editor/<name>, the WS protocol returns the new cvars correctly, and
list_assets enumerates the workspace asset tree as expected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wave-7 editor polish drop. Three sub-features in one PR:
1. Global keyboard shortcuts
Cmd/Ctrl+Zdispatchesscene_undoCmd/Ctrl+Shift+ZandCtrl+Ydispatchscene_redoG/R/Sflipgizmo_mode translate/rotate/scaleInstalled by
<Shell>so every editor panel gets them automatically. Listenergates on the focused element: a number-field that wants the user to type
sstill works because typing into
<input>/<textarea>/contenteditableskipsdispatch.
2. Toolbar panel rebuild (
panels/toolbar)Thin top-bar (~32 px) modeled after Blender / Unity headers:
gizmo_mode <mode>scene_undo/scene_redo(also flash when the matchingCmd+Z keyboard shortcut fires)
gizmo_snap, new) + Transform-space selector(cvar
gizmo_space, new;world/local)Toolbar polls the three cvars every 1 s so external changes (GLFW G/R/S
handler, console, other panels) stay reflected without a manual refresh.
3. Asset Browser panel (
panels/asset-browser)Four tabs, file-path filter, click-to-load:
tests/goldens/scenes/*.cfgexec <path>assets/hdri/*.hdrr_env_map <path>assets/gltf/*.gltf/*.glbmesh_load_gltf <path>assets/meshes/*.obj / .plymesh_load_gltf <path>(stub; no .obj importer yet)Rows are draggable: drag-start writes the dispatch line into both
application/x-demont-asset(custom MIME) andtext/plainso a futurenative-viewport drop handler can pick either off the DataTransfer.
Engine additions
gizmo_snap(default0) +gizmo_space(default"world"),both
CVAR_ARCHIVEso they survive across runslist_assets <subdir>: directory walk overtests/goldens/scenes(.cfg/.toml),assets/hdri(.hdr/.exr),assets/gltf(.glb/.gltf),assets/meshes(.obj/.ply/.stl). Emits oneworkspace-relative path per line plus a trailing
(list_assets: N entries under '<dir>')summary the React client splits on\nand drops.Existing engine commands consumed:
scene_undo/scene_redo/scene_undo_clear(from wave-6 fix(render): bloom now runs when denoiser is off (Vulkan) #18)gizmo_modecvar (from rotation-gizmo feat(editor): rotation gizmo dispatches prim_set_rotation (orientation for analytic prims) #207 wave)exec <cfg>/r_env_map <path>/mesh_load_gltf <path>(existing)Test plan
npm run typecheck-- clean (web/editor/)npm test-- 41 tests pass (24 hierarchy, 11 keyboard, 6 asset-browser)npm run build-- 18 embedded files emittedcmake --build build/mac-release --target demont -j 8-- clean Mac/Metal buildlist_assets scenes-- 29 entriesget_cvar gizmo_snapreturns{value: "0", default: "0"};gizmo_spacesame shapeGET /editor/{toolbar,asset-browser,inspector,scene-hierarchy}-- 200,<div id="app">presentexec scene_undoon a fresh engine returnsscene_undo: history is empty(round-trips properly)Notes for the integrator
happy-domfor the DOM-dependent keyboard handler tests; added as adevDependencyinweb/editor/package.jsonMeshestab is informational only; the engine currently only importsglTF. Empty-state hint says so
gizmo_space localis currently a metadata cvar -- the analytic-prim gizmodrag dispatch is world-space only. Wired now so the rotation-aware gizmo
follow-up (fix(shaders): per-ray underwater medium tracking (Phase 4 #134) #206 -- light / per-prim orientation) can read it without
another cvar-add roundtrip