Editor redesign: Monolith theme + undo/redo, command palette, Profiler, Beam, prefabs & scripting QoL - #30
Conversation
Replaces the generic Hazel-derived dark-ImGui look with a distinct identity across three areas: - Theme (Colors.h, ImGuiLayer.cpp): warm-graphite neutrals and a single lime accent (#C8FF4D) replace the cool-graphite/indigo palette; the unused secondary indigo hues are dropped, tab/separator hover overlays become named accent-tinted constants, and control rounding drops from soft (4-9px) to sharp (2-3px) with the existing 1px hairline borders. - Default panel layout (EditorLayer.cpp/.h): a new DockBuilder-based ResetDefaultDockLayout() docks Scene Hierarchy left, Viewport/Text Editor center-top, Content Browser/Log center-bottom, and Properties right. It runs on first launch (empty imgui.ini) and from a new View > Reset Layout menu item. Scene Renderer and Light Settings are now closed by default (still available from the View menu). imgui.ini is reset to empty so DockBuilder is the single source of truth for the default layout. - Viewport overlays (EditorLayer.cpp/.h): a display-only 3-axis orientation gizmo (top-right, oriented from the camera view matrix) and a lime "<NAME> SELECTED" badge (top-left, shown when an entity is selected). Both follow the existing viewport-overlay window pattern. Verified: Release build of Core + Editor is clean; the editor launches without crashes or asserts and the re-saved imgui.ini confirms the new dock tree.
Ubuntu's ld defaults to --as-needed, which drops -latomic before LTO's deferred codegen inserts __atomic_store/__atomic_load calls for non-lock-free atomics (e.g. RenderCommandBuffer's std::atomic<PipelineStatistics>), causing undefined references at final link. Force it to stay linked with --no-as-needed/--as-needed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A54GbKmpSf6NCXDjx9y8bL
ImPlot was already vendored under Core/vendor/imgui/implot and listed in the ImGui static-lib premake project, but never compiled in, initialised, or used. Create the ImPlot context immediately after the ImGui context and destroy it immediately before, in both ImGui layer implementations, so any ImPlot:: call has a valid context regardless of which layer is live. Rebuilding the vendored ImGui lib now compiles implot.cpp / implot_items.cpp (a clean checkout picks this up on first build).
Simple/Advanced layout modes (adapted from the LuckyEngine editor's
two-layout idea, game-editor only): a persisted Editor.SimpleLayout pref
drives two DockBuilder default layouts — Simple is the minimal arrangement,
Advanced additionally docks Scene Renderer, Light Settings, Statistics, and
the Renderer Debugger. SetEditorLayoutMode opens/closes the advanced-only
panels and defers the dock rebuild to the next frame. Toggle from
Settings > Editor > "Simple UX" and from the viewport gear's "Switch to
Simple Mode" (shown only in Advanced).
New Statistics panel: a lightweight, always-available performance overview
with a Tracy-style frame-time timeline drawn with ImPlot (shaded line,
budget-bucket colouring, a 16.67 ms reference line, built-in hover), plus
headline timing / thread / scene / memory metric tables — all from existing
data sources, no new instrumentation. Kept separate from the deep per-pass
Renderer Debugger.
Adds source files (StatisticsPanel.{h,cpp}) — regenerate the project.
Group the panel's sections into an Overview/Profiling/Memory/Render Graph/GPU Scene/Shaders tab bar instead of one long scroll. Replace the old ImGui::PlotLines frame/pass history with ImPlot: a dual CPU/GPU frame-history line plot (shaded GPU series, 60 FPS budget line) and a per-pass GPU-time horizontal bar chart sorted heaviest-first. Drop the per-pass PassHistory ring map (and its unordered_map include); reuse pre-sized scratch vectors for the chart so it doesn't reallocate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Rebuild the panel presentation from scratch: a header of status chips (Ready/View/Output/Scale), a segmented quality-preset selector, and a live settings-search box, over six richly-styled collapsible cards (Debug Views, Quality & Performance, Screen-Space Effects, Shadows, Post FX, Color Grading) with FontAwesome accent icons. Searching filters rows by label and force-opens cards that still have a match. Boolean settings use a custom drawn toggle switch (no undo entry, fine for renderer settings); sliders/dropdowns/color keep ImGuiEx::Property*. All ~60 settings, their conditional sub-settings, and every projectSettingsChanged/screenSpaceResourcesChanged accumulator, clamp, refresh and callback are preserved verbatim. The Fixed-Resolution preset block closes and reopens the property grid in a balanced End/Begin pair so it no longer leaks ImGui style vars and an ID entry each frame. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Whitelist .idea in .gitignore so only the portable project config is tracked (vcs.xml, JetBrains' own .idea/.gitignore, and the codeStyles/inspectionProfiles/runConfigurations dirs); per-user and CLion-regenerated files (workspace.xml, editor.xml, *.iml, modules.xml) stay ignored, as does any future IDE noise. Add Shell Script run configurations so the Linux fetch/build/run scripts are one click from CLion's run dropdown: Fetch, Build (Release|Debug), Run (Release|Debug). Build/Run pass the config non-interactively and execute in a terminal so logs are visible. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
…g-only Linux-Build.sh now downloads a pinned, isolated-extraction Vulkan SDK (1.4.335.0, override via VULKAN_VERSION) into the vendored path when it's missing, so a clean checkout needs no separate fetch step; an explicitly set but missing VULKAN_SDK stays a hard error. Linux-Run.sh runs the build first (LUX_SKIP_BUILD=1 to skip) so a forgotten recompile can't run a stale binary, and resolves LUX_DIR from its own location. Linux-Fetch.sh bumped to the same SDK version with the same safe tmp extraction. Drop JPH_FLOATING_POINT_EXCEPTIONS_ENABLED from Jolt's Release defines so degenerate-but-harmless physics state no longer hard-crashes release builds; it stays on in Debug. Building.md documents all of the above. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
No renderer feature currently depends on ray queries, so a GPU/driver that lacks them should log a warning and continue rather than erroring out of device setup. Threading.md gains a note on why Linux executables must link libatomic for atomics wider than 16 bytes (e.g. PipelineStatistics), which fall back to libatomic's generic CAS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Move the SkyDiver scene onto the scene-owned PostProcess block and drop the obsolete Post Process Volume entity / RenderVolumeComponent left over from the per-volume system. Also refresh a stale StaticMesh AssetID and the registry's FileLastWriteTime churn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Replace Roboto with Archivo for all ImGui UI fonts, matching the Monolith
mock's body face. The swap is in place — font add-order (Fonts[0]="Bold",
Fonts[1]="Large") and the name keys ("Default"/"Medium"/...) are
preserved, so every index and named lookup still resolves and FontAwesome
still merges into Default. Both the live ImGuiLayer and the (currently
dormant) VulkanImGuiLayer are kept in lockstep so they can't diverge.
Register two new named faces appended after the UI fonts (indices
unchanged): "Mono" (JetBrains Mono) for numeric readouts and "Display"
(Bricolage Grotesque) for wordmarks/headers. Only the referenced weights
are vendored (Archivo Regular/Medium/SemiBold/Bold, JetBrains Mono Bold,
Bricolage Bold) with their OFL licenses; the unused Roboto family is
removed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Merge the gizmo tools and the play/simulate/stop transport into one centred bar drawn with flat vector glyphs (stroked outlines for the gizmo tools, filled shapes for the transport) following the theme accent: a bordered select/move/rotate/scale cluster, a divider, then a lime Play triangle, fast-forward Simulate, and Stop square. The standalone top-left gizmo overlay is gone (UI_GizmosToolbar removed), which frees the corner for the selection badge (moved up from y+48 to y+12). The viewport performance HUD now renders in the "Mono" (JetBrains Mono) face. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
On Wayland, GLFW prefers libdecor for client-side decorations whenever it is installed. libdecor draws its own titlebar and does not honour set_visibility(false) for our undecorated window on KWin, so its header rendered on top of the editor's custom titlebar. Disable libdecor via glfwInitHint before glfwInit so GLFW uses the native xdg-decoration protocol instead: the undecorated window requests CLIENT_SIDE and KWin draws no server titlebar of its own. The hint is Wayland-only and ignored on Windows/X11. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Replace the Hazel logo with a vector LUX mark and a Display-font wordmark. Swap the centered scene name and right-side project box for a left breadcrumb (Project / Scene) drawn in the mono face, clipped to the window controls so long names can't overrun them. Centre the menu bar vertically in the tall titlebar so it lines up with the logo, breadcrumb, and window buttons instead of hugging the top. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Give the hierarchy the Monolith sidebar look. Each row now draws a per-type FontAwesome icon (cube/light/camera/audio/script/...) with a category tint, over an empty-label tree node so the icon keeps its own colour and the name turns accent-lime when selected, on a subtle lime selection fill and hover highlight. Add an uppercase "HIERARCHY - N" header with a live entity count, and a hover-revealed visibility eye on mesh rows that toggles StaticMeshComponent.Visible (handled through the node's own click, guarded by the eye rect, so it doesn't disturb the drag-drop binding). Search, multi-select, drag-drop reparent/prefab drop, and the context menu are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Adopt the concept's inspector look. Add ImGuiEx::ToggleSwitch (rounded track + accent knob) and route the shared Property(bool) through it, so every boolean in the editor becomes a lime switch instead of a checkbox. Flatten the component-section header: an uppercase label + chevron drawn over an empty-label node with a faint hover wash, dropping the framed Hazel header and texture icon while keeping the collapse and the gear Reset/Remove menu (via AllowOverlap). Replace the entity header's pencil icon with an accent status dot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Viewport navigation hides/locks the cursor via DisableMouse(). When the camera went inactive (viewport lost focus/hover) while still captured, OnUpdate's early return re-enabled ImGui input but never restored the GLFW cursor mode, leaving the mouse hidden and unusable until the window lost focus. Restore the cursor to Normal in that path when it is found non-normal. The new viewport overlays make the mid-capture hover flip easy to hit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Give every inspector input the concept's bordered, slightly-inset "field box" look via a scoped RAII frame style (border + rounding + inset FrameBg), applied once so it unwinds cleanly across DrawComponents' returns. Restyle the Add Component button as a lime call-to-action with a plus glyph. Per-component field logic is untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
Add the JetBrains Mono Regular/Medium weights and switch the "Mono" UI face from Bold to Medium (crisp, not heavy) in both ImGui layers. Push the mono face around the ImPlot plots so the axis/tick labels render monospaced, Tracy-style: the Statistics frame-time plot and both Renderer Debugger charts (frame history + per-pass GPU). Each PushFont/PopFont brackets the whole plot so the font stack stays balanced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHCXdL86aTQMgQv2D3Tw7E
The X/Y/Z fields used ImGuiEx::Property with hidden "##X" labels, which rendered the label literally as text and pushed their own item width, ignoring PushMultiItemsWidths and breaking the row. Switch to raw ImGui::DragFloat, add per-axis SameLine gaps and a trailing Dummy for row spacing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
Timestamp + message render in the Mono font with the timestamp dimmed; INFO/WARNING/ERROR tags retuned to the Monolith blue/amber/red. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
Rebrand the old text editor to Beam: per-file Document tabs, Ctrl+N/S/W/F/G shortcuts, Ctrl+PageUp/Down tab cycling, a mono status bar, and a Display-font wordmark. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
Draw the gizmo tools + play/simulate/stop transport centred in the titlebar (rect excluded from the window drag zone) instead of over the viewport, so it stays usable when Beam covers the viewport. Viewport gains IsVisible(); the settings/orientation/selection/perf overlays only draw when the viewport is the active tab. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
Full presentation remake: mono breadcrumb + right-aligned New/view/sort/options controls, asset-type filter chips, grid/list view toggle, Name/Type/Modified sorting, pinned favourite folders, and a selected-asset details footer. View mode, sort, filter and favourites persist via Application settings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
Website-ready reference for the editor: architecture, panels, viewport/camera, content browser, theme/fonts/icons, keybindings, and extension recipes. Each subject grounded in source (path:line) with how-it-was-made and how-to-modify sections. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
Add the LaptopStart sample scene, refresh the asset registry and project file to reference it, and save the redesigned editor dock layout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
WalkthroughThe change updates Linux build and run workflows, adds Vulkan SDK setup, refreshes the editor theme and panels, introduces Simple and Advanced layouts, adds Statistics and Beam features, updates sample scenes, and adds editor documentation. ChangesLinux workflow
Editor shell and visual foundation
Editor panels
Sample content
Documentation
Estimated code review effort: 5 (Critical) | ~120 minutes Merge Risk: 🟠 High · up to This redesign currently risks data loss in Beam, undefined behavior in Content Browser operations, and a non-rendering default sample scene; its build changes can also terminate Release physics workers or leave Vulkan SDK setup unusable. Merge should be blocked until these concrete issues are fixed or explicitly accepted. Sequence Diagram(s)sequenceDiagram
participant EditorLayer
participant PanelManager
participant ContentBrowserPanel
participant StatisticsPanel
participant SceneRenderer
EditorLayer->>PanelManager: register editor panels
EditorLayer->>ContentBrowserPanel: render persisted browser state
EditorLayer->>StatisticsPanel: set SceneRenderer context
StatisticsPanel->>SceneRenderer: read frame and scene metrics
ContentBrowserPanel-->>EditorLayer: report selection or rename action
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 12.70% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 126 functions across 29 files. (26 skipped: 26 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Phase 4 — after an undo/redo, select the entities that step touched (that still exist), so the user sees what changed; handles multi-select and structural edits (undo a delete re-selects the recreated entity). RestoreSelection derives the set from the command's EntityDeltas rather than storing a separate selection snapshot. Phase 5 — UndoHistoryPanel (View > History): a header-only EditorPanel that reads the stack through function bindings and renders it Photoshop-style (past states top, current middle, redoable below); click a row to jump to that state. Transaction grouping proved unnecessary — the whole-scene diff at commit already makes one action one step. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
UndoCommand gains optional CustomUndo/CustomRedo closures; when set, undo/redo call them instead of applying a scene diff, so one stack (and one Ctrl+Z / menu / History panel) holds both scene and non-scene commands. EditorLayer::PushUndoCommand is the entry point. Renderer/project settings are captured as a ProjectSceneRendererSettings (via SceneRenderer::WriteProjectSettings, which excludes the transient debug-view toggles), diffed against a baseline, and restored via ApplyProjectSettings (the canonical apply+refresh used on project load). Shows as "Undo Renderer Settings". Scene post-processing and material assignment were already covered by the scene diff. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
Play/Simulate get their own transient undo stack (m_Play*), baselined on Play/ Simulate and cleared on Stop, so a play session never touches the edit-mode history. Undo/redo route through ActiveUndoStack() (edit stack in Edit mode, play stack otherwise), so the same Ctrl+Z, Edit menu, and History panel drive both; edit-mode behaviour is unchanged. A play step is a closure command whose restore rebuilds the runtime scene from the snapshot and restarts its runtime (OnRuntimeStop -> DeserializeFromSnapshots -> OnRuntimeStart, via AdoptRuntimeScene) -- so undoing during Play resets physics/scripts to that point. A headless self-test confirmed the round-trip including the mid-play restart. CaptureSceneEntities is now parameterized by scene so the play path reuses it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
Each UndoCommand records its heap payload size (ApproxBytes = entity/meta YAML); TrimUndoStack evicts the oldest steps until under both a step cap (s_MaxUndoDepth, 64 -> 256) and a byte budget (s_MaxUndoBytes = 128 MB), always keeping at least one. Applies to the edit and play stacks. Replaces the fixed 64-step cap at all commit sites, so a few huge diffs can't blow up memory while small edits keep a long history. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
SceneSerializer::RunRoundTripSelfTests (following the RunValidationSelfTests pattern) builds a scene with a hierarchy + components, does split -> reassemble -> re-split, and confirms every entity's YAML and the scene metadata are reproduced exactly. Surfaced as a button in the Renderer Debugger next to the render-graph self-tests, so a regression in the snapshot path the undo system depends on is one click from being caught. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
Asset rename/move/delete are now reversible closure commands pushed onto the editor
undo stack via ContentBrowserPanel::SetUndoPush -> EditorLayer::PushUndoCommand.
Reversible primitives are static RawRenameAsset/RawMoveAsset/TrashAsset/RestoreAsset;
the public ops call the primitive then push {undo,redo} closures that reverse it and
Refresh the browser.
Delete is fail-safe: it never calls FileSystem::DeleteFile — it moves the file to
<project>/.trash/<handle>__<name> (outside the scanned asset dir so ProcessDirectory
won't re-import it), deregisters the handle, and undo moves it back and re-registers the
SAME handle so scene references still resolve. Trash is never auto-purged. Directory ops
keep their existing permanent-delete path (not covered). The trash file-move round-trip
was verified; the full delete->undo flow needs interactive testing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
After an undo or redo, EditorLayer::UI_UndoToast fades a short "Undo: <label>" / "Redo: <label>" pill in at the bottom of the window (~1.6s + 0.5s fade) so the action registers even when the change is subtle or off-screen. Other Phase 9 items (held-Ctrl+Z repeat, per-widget coalescing, extra history-panel a11y) deliberately skipped -- see docs/Editor/Undo-Redo.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
…f registry On exit (File > Exit / SIGTERM), the editor segfaulted while destroying GPU resources. Root cause was a static-destruction-order fiasco: Project::s_AssetManager is an inline static Ref that outlives Application shutdown, so its cached scenes' Vulkan resources (IndexBuffer, etc.) were freed at program exit — after the device was already gone. Two fixes: - EditorLayer::OnDetach now calls Project::SetActive(nullptr), which runs the asset manager's Shutdown() and drops the static Ref while the Vulkan device is still alive. - RefUtils' live-reference registry (Ref.cpp) is now an intentionally-leaked, construct- on-first-use singleton, so it outlives every static Ref and RemoveFromLiveReferences() can never erase() into a freed hash table (the deeper latent cause of the same crash). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
- New-tab '+': drop ImGuiTabItemFlags_Trailing and render it after the tab loop so it sits inline with the tabs; hide the tab bar entirely when no file is open (the lone '+' looked orphaned below the toolbar). - Replace the toolbar separator (it collided with the tab strip) with a definite gap. - Editor/Diff toggle now pins flush-right (width was overstated at 120px). - Status bar moved below the editor (was between the tabs and the text). - 'BEAM' wordmark: push the display font before AlignTextToFramePadding so it lines up with the toolbar buttons. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011rEemocEgDwnS1Lm7W9TDQ
Add a fuzzy command palette (Ctrl+Shift+P) over every menu action and panel toggle, and a consolidated Profiler panel — frame-time graph, CPU zone breakdown, GPU pass timings, and scene/memory stats — built purely over existing Application/SceneRenderer telemetry (no new instrumentation). The Profiler supersedes the lightweight Statistics panel; its unique draw-call / visible-instance / VRAM metrics are folded into the new Scene & Memory section, and StatisticsPanel is removed. Also: neutralize Beam's tab-strip styling (kills the stray lime tab underline) and fix the content-browser top bar so the breadcrumb no longer overlaps the search box. The palette shortcut reads the chord through ImGui input so it fires regardless of which panel holds focus; results auto-scroll only on keyboard nav, leaving wheel scrolling free. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
Correctness fixes: - Content browser: descending sort no longer returns !less (that broke strict weak ordering — UB in std::sort); reverse the operands instead. - Content browser: defer ToggleFavorite out of the range-for so it can't invalidate the iterator / dangle the favorite path. - Content browser: unique per-directory context-menu id (was shared). - Beam: keep a document dirty and log when the write fails (was marking it clean on failure — silent data loss); refuse Save As onto a path a another tab already owns; prompt Save/Discard/Cancel before closing a dirty tab. - ImGuiEx::ToggleSwitch: InvisibleButton with EnableNav + its return value so keyboard/gamepad activation works (IsItemClicked is mouse-only). Build/docs: - premake: JPH_FLOATING_POINT_EXCEPTIONS_ENABLED is Debug/Debug-AS only now (was defined in Release, which can hard-crash on degenerate physics). - Default dock layout: dock "Beam"/"Profiler" (were the dead names "Text Editor"/"Statistics"). - Strip stray </content>/</invoke> artifacts from the editor docs. - Threading.md: correct the libatomic/LTO linker guidance. - Linux-Fetch.sh: guard the Vulkan SDK swap on a successful download. - Linux-Run.sh: honor an explicit VULKAN_SDK; fail loudly on a missing SDK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
Extend TagComponent with editor-only Locked and LabelColor (packed RGBA), serialized with the scene (emitted only when set, so existing scenes stay byte-identical) and read with safe defaults for older scenes. Hierarchy: a colour-label stripe on the row's left edge; a lock badge and dimmed name when locked; a right-click menu for Lock/Unlock and a label palette (6 swatches + None). Every change raises an undo step. Locked = protected: no drag-reparent, no delete (also filtered out of a multi-selection delete), and the Inspector is read-only with an Unlock button. Search/filter and the mesh-visibility eye already existed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
Add EditorCamera::SetOrbitState to restore a full orbit pose (focal point, distance, pitch, yaw), and wire nine session-local viewport bookmarks in EditorLayer: Ctrl+<1-9> captures the current view, <1-9> jumps to it (gated on the viewport being hovered and no text field focused). A View -> Camera Bookmarks submenu exposes Set/Jump per slot. Persistence across sessions is a follow-up. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
Pin the current selection with Ctrl+B (also a palette command and a View -> Entity Bookmarks submenu). Selecting a bookmark selects the entity and frames the editor camera on its world position via EditorCamera::Focus. Bookmarks are per-scene UUIDs: cleared on scene load, and stale handles (e.g. deleted entities) are skipped in the menu. Session-only for now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
Prefabs had no disk persistence: AssetImporter had no serializer for AssetType::Prefab, so a .lprefab hit the "no loader" fallback and GetAsset<Prefab> failed. Add PrefabSerializer (mirrors SceneAssetSerializer): Serialize writes the prefab's sub-hierarchy as a scene via SceneSerializer; TryLoadData deserializes it and re-identifies the single parentless root. Registered in AssetImporter; PrefabSerializer befriended by Prefab. Editor: "Create Prefab from Selection" (Edit menu + command palette) builds a Prefab from the selected entity, writes the .lprefab, and imports it. This is the foundation focus-mode save-back needs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
A panel to inspect the SceneRenderer's intermediate targets: final composite, HDR scene colour, and the G-buffer attachments (base colour, normals, metal/rough, material ID, object ID, velocity), with fit/zoom and a resolution readout. It only reads the renderer's existing stable image accessors and draws them via ImGuiEx::Image on the main-thread ImGui path the viewport already uses — no render-graph, shader, or pass changes, so it can't perturb the frame it is viewing. Unavailable targets (feature off / not yet produced) show a note instead of asserting. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
TagComponent is excluded from AllComponents/DuplicateComponents (its name is copied specially at entity creation), so the editor-only Locked and LabelColor fields were dropped by Scene::Copy and DuplicateEntity — Ctrl+D on a locked or colour-labelled entity produced a bare duplicate. Carry both fields explicitly in both paths, alongside the name copy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
Drop FrameDebuggerPanel and its EditorLayer wiring. Its render-target views largely duplicated what the existing debug-view-mode switch already surfaces, so it wasn't worth keeping as a separate panel. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
LoadProjectAssembly now records a ScriptEngine::ReloadStatus (success + script count, or a failure reason) exposed via GetLastReloadStatus(). EditorLayer routes all three reload entry points (Ctrl+R, Edit menu, command palette) through ReloadScriptsWithFeedback(), which pops a green "C# reloaded" / red "C# reload failed" toast; detailed errors still go to the Log. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
Live script-field editing already worked: during Play the hierarchy context is the runtime scene and FieldStorage reads/writes the live Coral instance, so the inspector controls reflect and edit the running object. Add a green "● Live" marker to the Script section while the scene runs so that's obvious rather than looking like the stored snapshot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NZz1zmqCagPaqQwAu6Ci9x
Add C# editor-hint attributes read by ScriptEngine::BuildAssemblyCache via Coral field reflection and applied in the inspector's Script section: - [Range(min,max)] draws a slider for Float/Int fields - [Header(text)] draws a bold grouping label above a field - [Tooltip(text)] shows help text on hover FieldMetadata carries the hints; DrawScriptFieldControl takes an optional range. No new files, no serialization or system-boundary change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LS429QSrsmSZ7NZBQ44tsk
Add a FolderComponent marker so an entity can act as a pure grouping node in the hierarchy. Folders reuse the existing entity parent/child tree, so drag-drop, nesting, and serialization work unchanged; their identity TransformComponent is kept (world-space math untouched) but not editable, so dropping entities into a folder never moves them. - Components.h: FolderComponent (non-empty for entt's empty-type opt), added to AllComponents / DuplicateComponents / PrefabInstantiationComponents - Scene.cpp: OnComponentAdded<FolderComponent> specialization - SceneSerializer: Folder marker written and read - SceneHierarchyPanel: Create Folder menu, folder icon (open/closed by expand state), hide Transform section + Add Component for folders - EditorLayer: skip the gizmo when the selected entity is a folder Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LS429QSrsmSZ7NZBQ44tsk
Make prefab instances first-class in the editor:
- Create Prefab from Selection now links the source hierarchy as a live
instance (each entity carries PrefabComponent{PrefabID, EntityID}),
instead of leaving plain entities. Prefab::Create optionally returns the
source-to-prefab UUID map used to stamp the link.
- Scene::CopyPrefabInstanceComponents(dst, src) syncs the prefab-tracked
components across scenes.
- The Prefab inspector section replaces the debug PrefabID/EntityID inputs
with the source name + Revert to Prefab (prefab -> instance) and Apply to
Prefab (instance -> asset, re-serialized, behind a confirm modal).
Value-sync only for now; per-component add/remove and variant assets come
in later phases. Architecture doc updated with the prefab-instance model.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LS429QSrsmSZ7NZBQ44tsk
Surface and manage prefab instance overrides at component granularity: - SceneSerializer::GetOverriddenComponentKeys diffs an instance against its prefab source (changed / instance-only / prefab-only components) by comparing their serialized blocks; YAML stays inside the serializer. - Scene::ReconcilePrefabComponents replaces the value-only copy with exact add/replace/remove reconciliation, so revert/apply are exact (kills the Phase 1 limitation). - Prefab inspector: an Overrides list with per-component Revert/Apply, plus Revert All / Apply All (apply behind a confirm modal). 'No overrides' when the instance matches its prefab. Architecture doc updated. Detection/row/reconcile sets are kept in lockstep with what SerializeEntity emits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LS429QSrsmSZ7NZBQ44tsk
Edit a prefab in isolation and push changes back to its instances: - Double-click a .lprefab (Content Browser) to enter edit mode: the editing context swaps to a copy of the prefab's scene (ApplyEditorScene, factored out of OpenScene). A banner shows 'Editing Prefab: <name>' with Save & Return / Discard & Return; Ctrl+S saves the prefab; Play/Simulate are blocked while focused. - SavePrefabEdits writes the asset, reloads the cache, and propagates. - Scene::PropagatePrefabEdits refreshes un-overridden instances in the returned scene to the edited prefab. Transform-aware: a placed root keeps its position while still adopting other changes; overridden instances are left for per-component revert. Covers component-value changes on existing prefab entities (structural add/delete of entities doesn't propagate). Completes prefab variants Phases 1-3. Architecture doc updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LS429QSrsmSZ7NZBQ44tsk
A variant is a self-contained prefab that remembers a base prefab: - Prefab carries a BasePrefab handle; PrefabSerializer::WritePrefabFile is the single prefab-write path, writing an optional top-level BasePrefab key (backward-compatible). All prefab-write sites route through it so a variant's base link survives every save. - Content Browser: right-click a .lprefab -> Create Variant (a copy of the base scene + base link). It instantiates and edits like any prefab; the edit banner shows '(variant of X)'. - Base inheritance: saving a base prefab calls EditorLayer::PropagateToVariants, which refreshes each derived variant asset via Scene::AdoptPrefabBaseEdits (UUID-matched; un-overridden entities adopt the base edit, overridden kept). Value-level inheritance on existing entities; structural and nested-variant cascades are out of scope. Completes prefab variants Phases 1-4. Arch doc updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LS429QSrsmSZ7NZBQ44tsk
…yout Populate the LaptopStart sample scene with a hierarchy folder grouping a camera, plus a sphere mesh — a small demonstration of the new folders feature. Register the Cylinder material and update the editor dock layout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LS429QSrsmSZ7NZBQ44tsk
Redesigns the LuxEngine editor into the warm-graphite "Monolith, warmed" aesthetic (single acid-lime accent), de-Hazel-ifies the UI, reworks the panels, and adds several substantial systems on top: a full undo/redo stack, a command palette, an in-editor Profiler, a complete prefab system (serialization → instances → per-component overrides → edit mode → variant assets), hierarchy folders, viewport/entity bookmarks, and a scripting quality-of-life pass. Targets
dev.Added
Ctrl+Shift+P, also under Tools) — fuzzy search over every menu action and panel toggle; keyboard-driven, disabled-aware..lprefab(delegates toSceneSerializerover the prefab's scene; root is the single parentless entity), registered withAssetImporter, plus Create Prefab from a selected hierarchy.PrefabComponent{PrefabID, EntityID}); the inspector's Prefab section replaces the debug IDs with Revert / Apply.SceneSerializer::GetOverriddenComponentKeysdiffs an instance against its source; the inspector lists changed / added / removed components with per-component Revert / Apply plus Revert-All / Apply-All.Scene::ReconcilePrefabComponentsdoes exact add/replace/remove..lprefabto edit it in isolation (context swaps to a copy of its scene; Play/Simulate blocked; Ctrl+S saves). Saving propagates to un-overridden instances (transform-aware, so placed instances keep their position).BasePrefab(right-click → Create Variant); editing a base propagates to derived variants' un-overridden entities.FolderComponentmarks a purely organizational grouping node: folder icon, no editable transform / gizmo, children grouped without being moved. Create Folder in the hierarchy menu.Ctrl+1–9set,1–9jump; full orbit state restored viaEditorCamera::SetOrbitState) and pinned entity bookmarks (Ctrl+B; jump selects and frames the entity).TagComponentcarries a lock flag and a label colour, serialized (only when non-default) and carried on copy / duplicate / prefab.[Range(min,max)]→ slider,[Header(text)]→ grouping label,[Tooltip(text)]→ hover help.Ctrl+N/S/W/F/Gkeybinds, find/replace, mono status bar, unsaved-changes prompt on close.Reworked / changed
Colors::Theme) — warm-graphite surfaces, one lime accent used sparingly.Fixed
std::sort);ToggleFavoriteinvalidated the iterator mid-loop; shared directory context-menu id.ImGuiEx::ToggleSwitchignored keyboard activation (mouse-onlyIsItemClicked) — now nav-enabled.Text Editor,Statistics) →Beam/Profiler.Ctrl+Shift+Pwas swallowed by panel focus (now anIsKeyChordPressedglobal) and its list auto-scrolled to the selection every frame (now gated to keyboard navigation).##Xand broke widths; editor cursor stuck hidden after the camera went inactive mid-capture; double titlebar on Wayland (disable libdecor); Linux libatomic dropped under LTO/Dist; unsupported ray-query downgraded to a warning.Build / tooling
VULKAN_SDKand fails loudly on a missing one; Run rebuilds first..idearun configs.Docs
docs/Editor/documentation set (architecture, panels — incl. Profiler & Command Palette, viewport/camera, content browser, theme/fonts/icons, keybindings, undo/redo, extension recipes) — website-ready, grounded in source..claude/docscorrected: libatomic/LTO linker guidance and the Debug-only FP-exception note.Sample
Notes for review
Editor/Source/CommandPalette.{h,cpp},Editor/Source/Panels/ProfilerPanel.{h,cpp}, andCore/Source/Lux/Asset/PrefabSerializer.{h,cpp}(andStatisticsPanel.{h,cpp}was removed). Runscripts\Win-GenProjects.batbefore building or the link fails (Premake doesn't self-regenerate). The Jolt FP-exception premake change also needs a regen to take effect.ScriptCore(Lux.RangeAttribute/HeaderAttribute/TooltipAttribute); rebuildScriptCoreso the editor'sResources/Scripts/ScriptCore.dllcarries them.0c38215(correctness fixes above); two findings were skipped with reason (an already-guarded null check, and a sample-scene content choice).Box2D,msdf-atlas-gen) are intentionally not touched.