Skip to content

feat(editor): Open in New Tab is a menu item, double-click frames the camera - #49

Closed
pythonlearner1025 wants to merge 1 commit into
mainfrom
feat/open-in-new-tab
Closed

pythonlearner1025 wants to merge 1 commit into
mainfrom
feat/open-in-new-tab

Conversation

@pythonlearner1025

Copy link
Copy Markdown
Member

What part this touches

The editor's left panel holds the Objects tree: one row per object in the open document. The bottom
slot holds the Files panel, a file browser over the project folder. The centre holds the Viewport
with a strip of document tabs. DocumentStore.open(path) is the one call that puts a file on the
Viewport as a tab.

Three files carry the gestures. BPHierarchyComponent.tsx is the Objects tree: it handles a click, a
double click and a right click on a row. ObjectHierarchyComponent.tsx wraps that tree and owns the
menu actions. FilesPanel.tsx draws the file grid and its menus.

The problem

What a double click opened was arbitrary. On a placed asset's root it was meant to open that asset's
file as a tab. On any other row it framed the camera. In the Files panel it opened whatever the
extension said. One gesture, three meanings.

The asset open never ran. The wrapper resolved a row to a path like this:

const rootPath = (obj as {_tpRootPath?: string})._tpRootPath ? null : obj.userData?.rootPath

_tpRootPath names the file an object was cloned out of. Every object in an open document carries
it, the scene's own children included. Measured on the terminator project, every top level row
answered _tpRootPath of http://127.0.0.1:4477/files/assets/main.scene.gltf?v=add4cd60..., so the
resolver answered false for every row and the double click framed the camera instead.

So the Objects tree had no way to open a placed asset's file. The Inspector's Edit Asset button is
absent on those rows too, for the same reason: I counted [title="Edit Asset"] on a selected
Unit_HK-Tank row and got 0.

openFile in the Files panel also held a dead branch: a commented window.open and a bare return
behind a newTab flag. Cmd double click hit it and did nothing.

The fix

Double click in the Objects tree frames the camera on the row's object, on every row.
Nothing in the Objects tree opens a tab by double click.
The right click menu holds "Open in New Tab" first, above Isolate, on a row that resolves to a file.
The item is absent, never greyed out, on a row that resolves to nothing.
The Files panel's file menu holds the same item first, above the file items.
The Files panel keeps double click as its open, because a file list has no camera to frame.
The tree prop onOpenAsset becomes documentPath, which answers a path or null and opens nothing.
The wrapper's openDocument action calls store.open(path), the call the Inspector's Edit Asset makes.
The resolver reads userData.rootPath alone, so a placed asset's root answers its file again.
An object inside an asset has no userData.rootPath, so it answers null and gets no item.
The dead newTab branch in openFile goes, with its parameter and the two e.metaKey reads.

The risk trade

Dropping the _tpRootPath test widens what the item appears on. That is the point: with the test in
place the item appears on nothing. The cost is a row that names a file the project no longer has.
The item still shows there, because the path reads as openable, and the open fails with a red toast
and a cold tab. I drove that path: the toast says "Unable to open
assets/models/units/endo/missing.gltf" and the editor keeps running. The Files panel has done the
same for any unreadable file since before this change, so the failure is the store's, not the menu's.

The rejected alternative was to keep the resolver byte for byte and demonstrate the item somewhere
else. There is nowhere else: no row in a document loaded from a file passes that test, so the item
would have shipped dead, the way the double click open shipped dead.

I left the Inspector's Edit Asset button alone. It carries the same dead test and it is a separate
fix.

Tests

Headless, Playwright with the installed Google Chrome at 1512x982, against my build of the editor
served by kite3d dev with CI=1 on port 4477 over /Users/minjunes/games/terminator-next. The
served bundle is my build: assets/index-Bj8x1xNg.js matched packages/editor/dist by sha256.

Screenshots I looked at, all under
/private/tmp/claude-501/-Users-minjunes-blitz/278f5f97-7e16-4722-b1bc-c061014966db/scratchpad/evidence/open-in-tab/:

  • 00-objects-menu-asset-row-before.png and 00-objects-menu-asset-row-after.png, the same row and
    the same viewport on both builds. The only difference is the new first item.
  • 01-objects-menu-asset-row-after.png, the item above Isolate.
  • 02-objects-doubleclick-frames-camera.png, the camera framed on the unit.
  • 03-endo-document-on-viewport.png, endo.gltf on the Viewport after the item.
  • 04-objects-menu-plain-node.png, Player_Start with no such item.
  • 08-files-menu-openable-file.png, the Files panel menu with the item above Import in Scene.
  • 11-edge-missing-file.png, the red toast on the worst path.
  • 12-reference-row-reveals.png, a reference row still revealing in Resources.

What I drove and read, from walkthrough.log, compare.log and edges.log in the same folder:

  • Double click on Unit_T-800_Endo: camera pos [62,49,60] target [0,0,0] became
    pos [49.21,33.11,40.22] target [23.35,0.99,0.23]. Tabs stayed ["main.scene.gltf"].
  • Right click the same row: ["Open in New Tab","Isolate","Move Up","Move Down","Scale to Radius", "Auto Center","Pivot to Node Center","Duplicate Object","Delete Object"]. Clicking the item made
    tabs ["main.scene.gltf","endo.gltf"] with endo.gltf active and the endo model on the Viewport.
  • Back to the scene tab and the same item again: tabs stayed at two and endo.gltf was active. The
    second open focuses the tab it already made.
  • Player_Start and Game_Manager: no such item, and the rest of both menus is character for
    character what the baseline build shows. Default Camera still offers Make Asset.
  • Double click Player_Start: camera target became [0,0,9], the node's position. Double click
    Scene: camera framed the whole model root. Neither added a tab.
  • Files panel: main.js and the assets folder open no menu at all, as before.
    assets/models/units/scout/scout.gltf opens ["Open in New Tab","Import in Scene"]. Double click
    on it still opened it, tabs went from two to three with scout.gltf active. The item on a file
    already open focused its tab.
  • Reference rows under a mesh: right click opens no menu, double click still switches the left panel
    to Resources.

Edges, driven through the real menu after setting userData.rootPath on a live row:

  • An asset id the manifest does not hold: no item.
  • A file the Viewport has no view for, AGENTS.md: no item.
  • A path whose file is gone: the item shows, the open fails, a red toast names the path and the
    editor keeps running with a cold tab.

Suites, all green: npm run typecheck exit 0, npm run lint exit 0, npm test -w packages/kite3d
10 tests in 5 files, npm run test:scripts 2 tests. The kite3d test that opens a document from the
Files panel by double click passes untouched.

What fails without the fix: on origin/main the same right click on Unit_T-800_Endo gives
["Isolate","Move Up","Move Down","Scale to Radius","Auto Center","Pivot to Node Center", "Duplicate Object","Delete Object"], with no way to open that unit's file from the Objects tree.

No test was added. Every check here is a manual headless run, and the repository bans proactive
tests.

Deploy

The editor bundle ships it, through the next kite3d release on npm. Build with
npm ci && npm run build, then release with npm run release. Rollback is the previous version on
npm: npm i -D kite3d@0.21.0-alpha.7.

… camera

A double click in the Objects tree frames the camera on the row's object
again, on every row. Nothing opens a tab by double click.

Opening a document is now the explicit item "Open in New Tab", first on
the right click menu, in the Objects tree and in the Files panel. The
Files panel keeps its double click open, because a file list has no
camera to frame.

The prop that resolved a row to an asset answers a path or null now, and
the tree builds the item from that. Its old rule rejected any object that
carries _tpRootPath. Every object in an open document carries it, the
scene's own children included, so that rule answered a path for no row
and the double click open it guarded never ran.

The dead newTab branch in the Files panel's openFile, a second browser
window that was never built, goes with its parameter.
@pythonlearner1025
pythonlearner1025 deleted the feat/open-in-new-tab branch September 17, 2026 03:31
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