Skip to content

Finish the editor: F2/F3/I/H/G, gizmos, streaming catalog, any-game CLI + production hardening#7

Merged
proggeramlug merged 9 commits into
mainfrom
feat/editor-completion
Jul 17, 2026
Merged

Finish the editor: F2/F3/I/H/G, gizmos, streaming catalog, any-game CLI + production hardening#7
proggeramlug merged 9 commits into
mainfrom
feat/editor-completion

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Replaces #6 (auto-closed when its base branch was deleted after #5 merged). Everything from #6 plus the fixes real use demanded since:

  • DPI/catalog-key/ray-miscompile fixes (placement now works; see commit messages for the full forensics)
  • Zoom-to-cursor, caret text editing, exit recovery
  • tools/ui-smoke.ps1 interaction test (PASS: 8→13 entities, save verified)
  • PLAN.md Parts 4–5 status truth

158 self-tests + ui-smoke green. Companion engine PRs #104/#120 already merged.

https://claude.ai/code/session_01PmL9WgNMkAgvpYSHEZga8K

Ralph Kuepper added 9 commits July 16, 2026 03:49
…ef, undoable environment (PLAN F2/F3-part/I)

- Outliner: the list is a scroll region (wheel + scrollbar) with a filter box
  pinned above it, matching name OR id across all four sections. A 500-entity
  world is now navigable; before, the panel could not scroll at all.
- Widgets: clip window (clipTop/clipBottom) + beginScrollRegion/endScrollRegion;
  clipped widgets skip draw/hit-test but still advance layout, so content
  height measures true.
- Inspector: entity name is editable (rename coalesces per entity), tint can
  be added/dragged/removed (add and remove stay DISCRETE undo entries — only
  value->value drags merge), tags have add/remove rows, and modelRef edits via
  a draft + Apply button (no per-keystroke rebuild into a magenta placeholder).
  Tint/tags/modelRef changes destroy+rebuild the scene node — sync only
  updates transform+tint on existing nodes, so a swap would not show otherwise.
- Environment panel: full schema coverage (sunColor, ambientColor, fogColor
  added) and every edit goes through SetEnvironmentCommand — the last mutation
  path that bypassed undo. Merge is scoped per FIELD, so Ctrl+Z steps back
  field by field, not the whole tweaking session.
- 148 self-tests pass (entity-edit + environment command tests added).

Claude-Session: https://claude.ai/code/session_01PmL9WgNMkAgvpYSHEZga8K
…set catalog (PLAN §C-tail/H/P5)

- point-gizmo.ts: axis-drag movement for the non-entity selectables the
  entity gizmos bail on. Water: move center, scale mode drags size out
  symmetrically. Rivers: per-control-point wire-sphere handles — click to
  pick the active point, drag axes to move it. Lights: move position.
  Commits through the existing Edit*Commands on release: one drag, one undo
  entry. A handle-pick click is consumed so it cannot fall through to
  handleSelectClick and deselect the river it just touched.
- Recent projects (H): toolbar Recent button opens the read UI that never
  existed for recent.json; clicking an entry switches project wholesale
  (openProject: project file -> catalog relist -> default world -> title).
- Asset catalog streams (P5): loadAssetCatalog only LISTS models now;
  pumpAssetCatalog loads one GLB per frame in the main loop. The ~20 s black
  window at startup is gone — the world appears immediately as placeholder
  boxes and meshes pop in as they arrive. Unreadable GLBs mark failed and
  are never retried. Catalog reset on reload makes project switching sound.
- --project/--world CLI args + dxcompiler.dll/dxil.dll beside the exe (see
  README): the editor now starts from ANY directory pointed at ANY world —
  it previously crashed at surface creation unless launched from a directory
  that happened to carry the DX12 compiler DLLs (the shooter root).
- 148 self-tests pass; 45 s live smoke against the shooter project.

Claude-Session: https://claude.ai/code/session_01PmL9WgNMkAgvpYSHEZga8K
pumpThumbnails renders each loaded GLB once into a 128x128 render texture —
one per frame, inside the normal frame, and only after the streaming catalog
finishes so the two pumps never fight. The Models tab is now a 64px thumbnail
grid (scroll region, hover/selection borders, name below each cell); cells
show a placeholder until their model streams in. Falls back to text-free
placeholder cells permanently if render targets are unavailable.

Requires the engine-side getRenderTextureTexture fix (returned { id } after
the v0.5 rename to Texture.handle — drawing any render texture fed undefined
into a native f64 param).

Prefab-tab thumbnails remain text rows — noted in PLAN.

Claude-Session: https://claude.ai/code/session_01PmL9WgNMkAgvpYSHEZga8K
…README truth pass

syncEnvironment now delegates to applyWorldEnvironment — the same call the
world-viewer example and any instantiateWorld consumer makes — keeping only
the shadow-pass toggle behind the explicit dirty flag. PLAN.md Part 4 marks
P2/P3a/P4/P5 done with what was learned; README rewritten to match reality
(feature-complete, streaming startup, game-agnostic CLI).

Claude-Session: https://claude.ai/code/session_01PmL9WgNMkAgvpYSHEZga8K
…project; visible asset cells

Screenshot-diagnosed (2026-07-16): with --project ../shooter/editor.project.json
the catalog was keyed by LOAD path ('../shooter/assets/models/x.glb') while
worlds reference 'assets/models/x.glb' — every lookup missed and the whole
arena rendered as placeholder boxes. The exact identity bug joinPath's comment
documents, reintroduced one level up by the --project flag. ModelEntry now
carries relPath (project-relative KEY, byte-equal to modelRef) and filePath
(resolved load path); texture listing and the playtest --world argument get
the same projectRelative treatment (the game's cwd is the project root).
Pinned by four new path-identity asserts (152 self-tests pass).

Also: the thumbnail render pass is DISABLED — the engine's begin_texture_mode
is a per-frame override consumed at end_frame, not the mid-frame raylib idiom,
so the thumbnails stayed empty and drew as invisible images (also screenshot-
verified). The grid now draws clearly visible category-colored cells with the
model's initial (dimmed while its GLB streams in); the drawTexturePro path
stays wired for the day the engine grows a real render-mesh-to-texture call.

Claude-Session: https://claude.ai/code/session_01PmL9WgNMkAgvpYSHEZga8K
…jection (load from absolute address 8)

Clicking to place (or grabbing a gizmo) died with 0xc0000005 at
mouseToWorldRay. Disassembly of the faulting instruction shows
'vmovq 0x8, %xmm6' — an element load whose BASE REGISTER was dropped,
i.e. a read from absolute address 8. Layout-sensitive: adding
console.error lines made it vanish, which is why every earlier build
and smoke test (none of which ever clicked in the viewport) was green
over a binary that crashed on the first placement click. Repro pair
kept: main-repro.exe + crash_main_10580_6a59eadc.dmp + main-repro.pdb.

Dodge, same family as shooter perry-quirks #8: hoist all 16 elements of
the inverse view-projection into named scalars first, then do only
scalar arithmetic — the idiom mat4Invert itself uses, which has always
compiled correctly. The inlined unprojectPoint helper (two 16-element
indexed mega-expressions) is gone.

Pinned by testMouseRay, which EXECUTES mouseToWorldRay headless — in
the broken binary it takes the --test run down with the same AV, which
is exactly the alarm we want. Verified live: 13 injected placement
clicks + camera drags + move/rotate/scale gizmo drags, no crash;
158 self-tests pass.

Claude-Session: https://claude.ai/code/session_01PmL9WgNMkAgvpYSHEZga8K
…ry, ui-smoke script (PLAN Part 5)

- Scroll-wheel zoom dollies toward the point under the CURSOR (min distance
  0.2, multi-notch aware) instead of forever approaching the world center —
  'get close to that house' no longer requires discovering middle-drag pan
  first. Gated on the viewport so scrolling a panel no longer also zooms.
  cameraEyePosition moved to editor-state to break the orbit-camera<->ray
  import cycle this created.
- Text fields have a real caret: click places it, Left/Right/Home/End move
  it, Backspace/Delete work on both sides, typing inserts in place. (Paste
  and selection remain engine-blocked: no clipboard FFI, no key repeat.)
- Closing the window with unsaved changes parks them in <world>.recover —
  never silently lost, never silently overwriting — and opening that world
  later announces the recovery file.
- tools/ui-smoke.ps1: the interaction test. Launches the real binary on a
  scratch fixture, injects placements/orbit/gizmo-drag/Ctrl+S, asserts
  alive + entity delta + safe-save siblings. First run: PASS (8 -> 13).
  --test cannot see input-path miscompiles; this can, and would have caught
  both of yesterday's shipped bugs.

158 self-tests pass. Companion engine PR: safe saves (engine#120).

Claude-Session: https://claude.ai/code/session_01PmL9WgNMkAgvpYSHEZga8K
@proggeramlug
proggeramlug merged commit cf9ce2a into main Jul 17, 2026
1 check passed
@proggeramlug
proggeramlug deleted the feat/editor-completion branch July 17, 2026 10:50
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@proggeramlug, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f71aa75-f0ed-420d-8dd4-b5653bd47f46

📥 Commits

Reviewing files that changed from the base of the PR and between a28f49a and 412e775.

📒 Files selected for processing (30)
  • .gitignore
  • PLAN.md
  • README.md
  • src/gizmos/point-gizmo.ts
  • src/io/asset-catalog.ts
  • src/io/open-project.ts
  • src/io/paths.ts
  • src/io/project.ts
  • src/io/world-io.ts
  • src/main.ts
  • src/playtest/launch.ts
  • src/playtest/playtest.ts
  • src/state/commands/edit-entity.ts
  • src/state/commands/set-environment.ts
  • src/state/editor-state.ts
  • src/tests/self-tests.ts
  • src/ui/layouts/asset-panel.ts
  • src/ui/layouts/environment-panel.ts
  • src/ui/layouts/inspector.ts
  • src/ui/layouts/outliner.ts
  • src/ui/layouts/recent-panel.ts
  • src/ui/layouts/toolbar.ts
  • src/ui/text-input.ts
  • src/ui/thumbnails.ts
  • src/ui/ui-context.ts
  • src/ui/widgets.ts
  • src/viewport/orbit-camera.ts
  • src/viewport/ray.ts
  • src/world-sync/sync.ts
  • tools/ui-smoke.ps1
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/editor-completion

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

1 participant