@fps-games/editor 0.2.1-beta.1 → 0.2.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-bridge/capabilities/ui-regions.d.ts.map +1 -1
- package/dist/agent-bridge/capabilities/ui-regions.js +16 -0
- package/dist/agent-bridge/capabilities/ui-regions.js.map +1 -1
- package/dist/build-info.json +43 -43
- package/dist/internal/material-preview.d.ts +22 -0
- package/dist/internal/material-preview.d.ts.map +1 -0
- package/dist/internal/material-preview.js +186 -0
- package/dist/internal/material-preview.js.map +1 -0
- package/dist/internal/project-model-preview.d.ts +4 -0
- package/dist/internal/project-model-preview.d.ts.map +1 -0
- package/dist/internal/project-model-preview.js +359 -0
- package/dist/internal/project-model-preview.js.map +1 -0
- package/dist/local-editor-agent-context.d.ts +12 -1
- package/dist/local-editor-agent-context.d.ts.map +1 -1
- package/dist/local-editor-agent-context.js +47 -0
- package/dist/local-editor-agent-context.js.map +1 -1
- package/dist/local-editor-harness.d.ts +15 -2
- package/dist/local-editor-harness.d.ts.map +1 -1
- package/dist/local-editor-harness.js +310 -21
- package/dist/local-editor-harness.js.map +1 -1
- package/dist/local-editor-tools.d.ts +1 -0
- package/dist/local-editor-tools.d.ts.map +1 -1
- package/dist/local-editor-tools.js +3 -0
- package/dist/local-editor-tools.js.map +1 -1
- package/dist/playable-local-editor-host.d.ts +4 -0
- package/dist/playable-local-editor-host.d.ts.map +1 -1
- package/dist/playable-local-editor-host.js +27 -0
- package/dist/playable-local-editor-host.js.map +1 -1
- package/dist/playable-product-scene-capabilities.d.ts.map +1 -1
- package/dist/playable-product-scene-capabilities.js +9 -3
- package/dist/playable-product-scene-capabilities.js.map +1 -1
- package/dist/playable-standard-project.d.ts.map +1 -1
- package/dist/playable-standard-project.js +5 -2
- package/dist/playable-standard-project.js.map +1 -1
- package/node_modules/@fps-games/babylon-renderer/package.json +7 -7
- package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.js +24 -0
- package/node_modules/@fps-games/editor-babylon/dist/marker-overlay.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts +10 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection.js +96 -1
- package/node_modules/@fps-games/editor-babylon/dist/projection.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-spatial-overlay-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-spatial-overlay-controller.js +85 -22
- package/node_modules/@fps-games/editor-babylon/dist/scene-view-spatial-overlay-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/selection-presentation-controller.d.ts +2 -0
- package/node_modules/@fps-games/editor-babylon/dist/selection-presentation-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/selection-presentation-controller.js +150 -4
- package/node_modules/@fps-games/editor-babylon/dist/selection-presentation-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.d.ts +1 -0
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.js +249 -15
- package/node_modules/@fps-games/editor-babylon/dist/transform-gizmo-controller.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/types.d.ts +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-babylon/dist/types.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/package.json +6 -6
- package/node_modules/@fps-games/editor-browser/dist/index.d.ts +5 -0
- package/node_modules/@fps-games/editor-browser/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/index.js +3 -0
- package/node_modules/@fps-games/editor-browser/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-asset-browser-continuity.d.ts +35 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-asset-browser-continuity.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-asset-browser-continuity.js +136 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-asset-browser-continuity.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-asset-filesystem.d.ts +46 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-asset-filesystem.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-asset-filesystem.js +283 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-asset-filesystem.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-preview-cleanup-registry.d.ts +12 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-preview-cleanup-registry.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-preview-cleanup-registry.js +67 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-preview-cleanup-registry.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-file-open.d.ts +6 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-file-open.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-file-open.js +9 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-file-open.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-file-preview.d.ts +7 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-file-preview.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-file-preview.js +20 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-file-preview.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-media-thumbnail-cache.d.ts +9 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-media-thumbnail-cache.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-media-thumbnail-cache.js +219 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-project-media-thumbnail-cache.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-tool-presentation.d.ts +14 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-tool-presentation.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-tool-presentation.js +42 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-tool-presentation.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.js +2 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-asset-kind.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-console.d.ts +27 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-console.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-console.js +288 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-console.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js +29 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts +20 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js +321 -60
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.d.ts +2 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.js +4 -2
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-prefab-stage.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts +63 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts +9 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js +88 -16
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-workbench.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts +2 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js +1463 -356
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-viewport-orientation.d.ts +13 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-viewport-orientation.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-viewport-orientation.js +15 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-viewport-orientation.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/package.json +7 -7
- package/node_modules/@fps-games/editor-core/dist/editor-tools.d.ts +1 -0
- package/node_modules/@fps-games/editor-core/dist/editor-tools.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/editor-tools.js +3 -1
- package/node_modules/@fps-games/editor-core/dist/editor-tools.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.d.ts +11 -2
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.js +12 -1
- package/node_modules/@fps-games/editor-core/dist/viewport-tools.js.map +1 -1
- package/node_modules/@fps-games/editor-core/package.json +6 -6
- package/node_modules/@fps-games/editor-forge-play/package.json +2 -2
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.d.ts +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.js +5 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/catalog.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/index.d.ts +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/index.js +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/asset-library/index.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/authoring-server/handler.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/authoring-server/handler.js +15 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/authoring-server/handler.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/authoring-server/routes.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/authoring-server/routes.js +3 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/authoring-server/routes.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/authoring-server/types.d.ts +6 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/authoring-server/types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/authoring-server/types.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/index.d.ts +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/index.js +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/product/scene-source.d.ts +4 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/product/scene-source.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/product/scene-source.js +10 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/product/scene-source.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/index.d.ts +4 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/index.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/index.js +4 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/index.js.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/protocol.d.ts +4 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/protocol.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/protocol.js +86 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/protocol.js.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/support.d.ts +9 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/support.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/support.js +76 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/support.js.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/types.d.ts +34 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/types.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/types.js +2 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/project-files/types.js.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/client.d.ts +4 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/client.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/client.js +40 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/scene-source/client.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.d.ts +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.js +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/index.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-authoring-api.d.ts +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-authoring-api.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-authoring-api.js +94 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/product-authoring-api.js.map +1 -1
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-file-handle.d.ts +14 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-file-handle.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-file-handle.js +101 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-file-handle.js.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-files.d.ts +23 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-files.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-files.js +180 -0
- package/node_modules/@fps-games/editor-playable-sdk/dist/vite/project-files.js.map +1 -0
- package/node_modules/@fps-games/editor-playable-sdk/package.json +6 -6
- package/node_modules/@fps-games/editor-protocol/dist/agent.d.ts +23 -3
- package/node_modules/@fps-games/editor-protocol/dist/agent.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-protocol/dist/agent.js.map +1 -1
- package/node_modules/@fps-games/editor-protocol/package.json +2 -2
- package/node_modules/@fps-games/editor-tooling/package.json +3 -3
- package/node_modules/@fps-games/editor-ui/dist/editor-ui-theme.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-ui/dist/editor-ui-theme.js +103 -10
- package/node_modules/@fps-games/editor-ui/dist/editor-ui-theme.js.map +1 -1
- package/node_modules/@fps-games/editor-ui/package.json +1 -1
- package/node_modules/@fps-games/playable-assets/package.json +1 -1
- package/node_modules/@fps-games/playable-contracts/package.json +1 -1
- package/node_modules/@fps-games/playable-rendering/package.json +2 -2
- package/node_modules/@fps-games/playable-runtime/package.json +12 -12
- package/node_modules/@fps-games/playable-runtime-data/package.json +4 -4
- package/node_modules/@fps-games/playable-scene/package.json +2 -2
- package/node_modules/@fps-games/plugin-api/package.json +1 -1
- package/node_modules/@fps-games/plugin-assets/dist/version.d.ts +1 -1
- package/node_modules/@fps-games/plugin-assets/dist/version.js +1 -1
- package/node_modules/@fps-games/plugin-assets/dist/version.js.map +1 -1
- package/node_modules/@fps-games/plugin-assets/package.json +5 -5
- package/node_modules/@fps-games/plugin-hierarchy/dist/version.d.ts +1 -1
- package/node_modules/@fps-games/plugin-hierarchy/dist/version.js +1 -1
- package/node_modules/@fps-games/plugin-hierarchy/dist/version.js.map +1 -1
- package/node_modules/@fps-games/plugin-hierarchy/package.json +5 -5
- package/node_modules/@fps-games/plugin-host/package.json +2 -2
- package/node_modules/@fps-games/plugin-markers/dist/version.d.ts +1 -1
- package/node_modules/@fps-games/plugin-markers/dist/version.js +1 -1
- package/node_modules/@fps-games/plugin-markers/dist/version.js.map +1 -1
- package/node_modules/@fps-games/plugin-markers/package.json +5 -5
- package/node_modules/@fps-games/plugin-materials/dist/version.d.ts +1 -1
- package/node_modules/@fps-games/plugin-materials/dist/version.js +1 -1
- package/node_modules/@fps-games/plugin-materials/dist/version.js.map +1 -1
- package/node_modules/@fps-games/plugin-materials/package.json +5 -5
- package/node_modules/@fps-games/plugin-rendering/dist/version.d.ts +1 -1
- package/node_modules/@fps-games/plugin-rendering/dist/version.js +1 -1
- package/node_modules/@fps-games/plugin-rendering/dist/version.js.map +1 -1
- package/node_modules/@fps-games/plugin-rendering/package.json +5 -5
- package/node_modules/@fps-games/plugin-scene/dist/version.d.ts +1 -1
- package/node_modules/@fps-games/plugin-scene/dist/version.js +1 -1
- package/node_modules/@fps-games/plugin-scene/dist/version.js.map +1 -1
- package/node_modules/@fps-games/plugin-scene/package.json +5 -5
- package/node_modules/@fps-games/plugin-shadow-map-experiment/dist/version.d.ts +1 -1
- package/node_modules/@fps-games/plugin-shadow-map-experiment/dist/version.js +1 -1
- package/node_modules/@fps-games/plugin-shadow-map-experiment/dist/version.js.map +1 -1
- package/node_modules/@fps-games/plugin-shadow-map-experiment/package.json +5 -5
- package/node_modules/@fps-games/plugin-shadows/dist/version.d.ts +1 -1
- package/node_modules/@fps-games/plugin-shadows/dist/version.js +1 -1
- package/node_modules/@fps-games/plugin-shadows/dist/version.js.map +1 -1
- package/node_modules/@fps-games/plugin-shadows/package.json +7 -7
- package/package.json +22 -22
|
@@ -8,15 +8,22 @@ import { createLocalEditorViewportRevealController } from './local-editor-ui-doc
|
|
|
8
8
|
import { createLocalEditorModalDialog } from './local-editor-ui-modal-dialog.js';
|
|
9
9
|
import { parseLocalEditorBrowserInspectorGroupValueClipboardPayload, } from './local-editor-inspector-clipboard.js';
|
|
10
10
|
import { writeLocalEditorClipboardText } from './local-editor-clipboard.js';
|
|
11
|
+
import { captureLocalEditorAssetBrowserReturnFocus, createLocalEditorAssetBrowserContinuityController, resolveLocalEditorAssetBrowserContentKey, restoreLocalEditorAssetBrowserReturnFocus, } from './local-editor-asset-browser-continuity.js';
|
|
12
|
+
import { canSkipLocalEditorPreviewCleanupPass, createLocalEditorPreviewCleanupRegistry, } from './local-editor-preview-cleanup-registry.js';
|
|
11
13
|
import { LOCAL_EDITOR_BROWSER_INSPECTOR_VEC3_AXES, resolveLocalEditorBrowserInspectorProportionalScaleValues, resolveLocalEditorBrowserInspectorVectorGroupAxisFromPropertyPath, resolveLocalEditorBrowserInspectorVectorGroupPathFromPropertyPath, } from './local-editor-inspector-groups.js';
|
|
12
14
|
import { DEFAULT_EDITOR_TRANSFORM_OPERATION_SETTINGS, } from '@fps-games/editor-core';
|
|
13
15
|
import { createEditorUiLayerHost } from '@fps-games/editor-ui';
|
|
16
|
+
import { isEditorProjectFilePreviewable, isEditorProjectMediaPreviewable, } from '@fps-games/editor-playable-sdk';
|
|
17
|
+
import { findLocalEditorProjectFileSearchMatches, toLocalEditorProjectRelativePath, } from './local-editor-project-file-preview.js';
|
|
18
|
+
import { resolveLocalEditorProjectFileOpenAction } from './local-editor-project-file-open.js';
|
|
14
19
|
export { createLocalEditorViewportRevealController, isLocalEditorElementVisibleInViewport, resolveLocalEditorViewportRevealScrollTop, revealLocalEditorElementInViewport, } from './local-editor-ui-dock-panel.js';
|
|
15
20
|
export { canUseHierarchyProviderReparent, createHierarchyEnabledOperationRequest, createLocalEditorHierarchyController, createHierarchyOperationMenuItems, createHierarchyToolbarCreateMenuItems, matchesHierarchyOperationShortcut, } from './local-editor-ui-hierarchy-controller.js';
|
|
16
21
|
export { createLocalEditorHierarchyViewAdapter, } from './local-editor-ui-hierarchy-view-adapter.js';
|
|
17
22
|
export { createLocalEditorContextMenuController, } from './local-editor-ui-context-menu.js';
|
|
18
23
|
export { createLocalEditorTooltipController, } from './local-editor-ui-tooltip.js';
|
|
19
24
|
import { createLocalEditorWorkbenchInputRouter } from './local-editor-ui-input-router.js';
|
|
25
|
+
import { createLocalEditorConsoleController } from './local-editor-ui-console.js';
|
|
26
|
+
import { synchronizeLocalEditorToolButtons } from './local-editor-tool-presentation.js';
|
|
20
27
|
import { createLocalEditorPanelRegistry } from './local-editor-ui-panel-registry.js';
|
|
21
28
|
import { createLocalEditorFormalSurfaceVisibilityController, disposeLocalEditorFormalSurfaces, shouldRenderLocalEditorInspectorPreview, } from './local-editor-ui-formal-surface-visibility.js';
|
|
22
29
|
import * as LocalEditorShared from './local-editor-ui-shared.js';
|
|
@@ -28,13 +35,22 @@ import { createLocalEditorIcon, isLocalEditorIconName } from './local-editor-ui-
|
|
|
28
35
|
import { createLocalEditorStatusCenterViewModel, resolveLocalEditorStatusDiagnosticLocatePlan, settleLocalEditorStatusDiagnosticRequest, resolveLocalEditorStatusDiagnostics, shouldCloseLocalEditorStatusCenter, } from './local-editor-ui-status-diagnostics.js';
|
|
29
36
|
import { formatLocalEditorStatusDiagnosticDetails } from './local-editor-ui-status-format.js';
|
|
30
37
|
import { resolveLocalEditorBrowserAssetKind, } from './local-editor-ui-asset-kind.js';
|
|
38
|
+
import { createLocalEditorAssetFilesystemSnapshot, resolveLocalEditorAssetFilesystemFolderPath, } from './local-editor-asset-filesystem.js';
|
|
39
|
+
import { createLocalEditorProjectModelThumbnailCache, } from './local-editor-project-media-thumbnail-cache.js';
|
|
31
40
|
import { createLocalEditorPrefabStageTitle, findLocalEditorPrefabStageStructureItem, resolveLocalEditorRightDockVisibility, resolveLocalEditorPrefabStageWorkbenchLayout, resolveLocalEditorPreviewToolSupport, shouldLocalEditorClosePerformanceMonitor, } from './local-editor-ui-prefab-stage.js';
|
|
41
|
+
import { resolveLocalEditorCoordinateAxisActiveDirection, resolveLocalEditorCoordinateAxisDirection, } from './local-editor-viewport-orientation.js';
|
|
32
42
|
export { applyLocalEditorBrowserInspectorControlBinding, createLocalEditorBrowserInspectorControlRegistry, formatLocalEditorBrowserInspectorNumberValue, formatLocalEditorBrowserInspectorValue, parseLocalEditorBrowserInspectorNumberValue, resolveLocalEditorBrowserInspectorSectionStatus, resolveLocalEditorBrowserInspectorControlRegistration, shouldShowPrefabModelAddAction, } from './local-editor-ui-panels.js';
|
|
33
43
|
export { createLocalEditorBrowserInspectorGroupValueClipboardPayload, createLocalEditorBrowserInspectorVectorCopyPayload, parseLocalEditorBrowserInspectorGroupValueClipboardPayload, } from './local-editor-inspector-clipboard.js';
|
|
34
44
|
export { LOCAL_EDITOR_BROWSER_INSPECTOR_VEC3_AXES, isLocalEditorBrowserInspectorTransformVectorGroupPath, resolveLocalEditorBrowserInspectorVectorGroupAxisFromPropertyPath, resolveLocalEditorBrowserInspectorVectorGroupPathFromPropertyPath, resolveLocalEditorBrowserInspectorVectorGroupPathFromProperties, } from './local-editor-inspector-groups.js';
|
|
45
|
+
export { findLocalEditorProjectFileSearchMatches, toLocalEditorProjectRelativePath, } from './local-editor-project-file-preview.js';
|
|
35
46
|
export { DEFAULT_LOCAL_EDITOR_THEME, LOCAL_EDITOR_THEME_CLASS, applyLocalEditorTheme, normalizeLocalEditorThemeName, } from './local-editor-ui-theme.js';
|
|
36
47
|
export { LOCAL_EDITOR_ICON_NAMES, createLocalEditorIcon, isLocalEditorIconName, resolveLocalEditorIconName, } from './local-editor-ui-icons.js';
|
|
37
48
|
const PENDING_NUMERIC_PROPERTY_INPUT = Symbol('pending-numeric-property-input');
|
|
49
|
+
function syncLocalEditorInspectorPendingState(panel, pending) {
|
|
50
|
+
panel.dataset.editorInspectorPending = String(pending);
|
|
51
|
+
panel.setAttribute('aria-busy', String(pending));
|
|
52
|
+
panel.toggleAttribute('inert', pending);
|
|
53
|
+
}
|
|
38
54
|
function readInspectorInputValue(input, source) {
|
|
39
55
|
const control = input.dataset.serializedControl;
|
|
40
56
|
const valueType = input.dataset.serializedValueType;
|
|
@@ -105,28 +121,6 @@ const PLACEMENT_MODE_DESCRIPTIONS = {
|
|
|
105
121
|
ground: '放置到 XZ 地面',
|
|
106
122
|
surface: '放置到场景表面',
|
|
107
123
|
};
|
|
108
|
-
const VIEW_PRESET_ICONS = {
|
|
109
|
-
perspective: 'view-perspective',
|
|
110
|
-
top: 'view-top',
|
|
111
|
-
bottom: 'view-top',
|
|
112
|
-
front: 'view-front',
|
|
113
|
-
back: 'view-front',
|
|
114
|
-
left: 'view-right',
|
|
115
|
-
right: 'view-right',
|
|
116
|
-
iso: 'view-perspective',
|
|
117
|
-
threeQuarterTop: 'view-perspective',
|
|
118
|
-
};
|
|
119
|
-
const VIEW_PRESET_TOOLTIPS = {
|
|
120
|
-
perspective: '透视视图',
|
|
121
|
-
top: '顶视图 · 自动切换正交',
|
|
122
|
-
bottom: '底视图 · 自动切换正交',
|
|
123
|
-
front: '前视图 · 自动切换正交',
|
|
124
|
-
back: '后视图 · 自动切换正交',
|
|
125
|
-
left: '左视图 · 自动切换正交',
|
|
126
|
-
right: '右视图 · 自动切换正交',
|
|
127
|
-
iso: '等距视图 · 自动切换正交',
|
|
128
|
-
threeQuarterTop: '斜 45° 俯视图',
|
|
129
|
-
};
|
|
130
124
|
const VIEWPORT_OVERLAY_TOGGLE_LABELS = {
|
|
131
125
|
bounds: '包围盒',
|
|
132
126
|
dimensions: '尺寸摘要',
|
|
@@ -137,6 +131,17 @@ const VIEWPORT_PROJECTION_LABELS = {
|
|
|
137
131
|
perspective: '透视',
|
|
138
132
|
orthographic: '正交',
|
|
139
133
|
};
|
|
134
|
+
const VIEWPORT_VIEW_LABELS = {
|
|
135
|
+
perspective: '透视',
|
|
136
|
+
top: '顶视图',
|
|
137
|
+
bottom: '底视图',
|
|
138
|
+
front: '前视图',
|
|
139
|
+
back: '后视图',
|
|
140
|
+
left: '左视图',
|
|
141
|
+
right: '右视图',
|
|
142
|
+
iso: '等距正交',
|
|
143
|
+
threeQuarterTop: '斜 45° 俯视',
|
|
144
|
+
};
|
|
140
145
|
const STATIC_SHADOW_BAKE_ACTION_ID = 'bake-static-shadows';
|
|
141
146
|
const TRANSFORM_ACTION_GROUPS = [
|
|
142
147
|
{
|
|
@@ -1223,96 +1228,6 @@ const PREFAB_IMPORT_STATS_METRICS = [
|
|
|
1223
1228
|
format: stats => formatPerformanceInteger(stats?.animationCount ?? null),
|
|
1224
1229
|
},
|
|
1225
1230
|
];
|
|
1226
|
-
function createSceneFrameRateOverlay(doc) {
|
|
1227
|
-
const root = doc.createElement('div');
|
|
1228
|
-
root.classList.add(LOCAL_EDITOR_THEME_CLASS, 'fps-editor-ui-overlay-card');
|
|
1229
|
-
root.dataset.editorSceneFrameRateOverlay = 'true';
|
|
1230
|
-
root.style.cssText = [
|
|
1231
|
-
'position:absolute',
|
|
1232
|
-
'right:14px',
|
|
1233
|
-
'bottom:134px',
|
|
1234
|
-
'z-index:3',
|
|
1235
|
-
'display:none',
|
|
1236
|
-
'align-items:center',
|
|
1237
|
-
'gap:6px',
|
|
1238
|
-
'width:112px',
|
|
1239
|
-
'height:30px',
|
|
1240
|
-
'padding:0 8px',
|
|
1241
|
-
'box-sizing:border-box',
|
|
1242
|
-
'border:1px solid color-mix(in srgb, var(--fps-editor-border) 78%, transparent)',
|
|
1243
|
-
'border-radius:6px',
|
|
1244
|
-
'background:color-mix(in srgb, var(--fps-editor-chrome) 76%, transparent)',
|
|
1245
|
-
'box-shadow:var(--fps-editor-shadow-panel)',
|
|
1246
|
-
'font-family:var(--fps-editor-font)',
|
|
1247
|
-
'font-size:11px',
|
|
1248
|
-
'font-weight:900',
|
|
1249
|
-
'letter-spacing:0',
|
|
1250
|
-
'color:var(--fps-editor-text-strong)',
|
|
1251
|
-
'pointer-events:none',
|
|
1252
|
-
'backdrop-filter:blur(5px)',
|
|
1253
|
-
'overflow:hidden',
|
|
1254
|
-
].join(';');
|
|
1255
|
-
const label = doc.createElement('span');
|
|
1256
|
-
label.textContent = 'FPS';
|
|
1257
|
-
label.style.cssText = [
|
|
1258
|
-
'color:var(--fps-editor-muted-strong)',
|
|
1259
|
-
'font-size:10px',
|
|
1260
|
-
'line-height:1',
|
|
1261
|
-
].join(';');
|
|
1262
|
-
const fpsValue = doc.createElement('span');
|
|
1263
|
-
fpsValue.textContent = '--';
|
|
1264
|
-
fpsValue.style.cssText = [
|
|
1265
|
-
'width:26px',
|
|
1266
|
-
'flex:0 0 26px',
|
|
1267
|
-
'font-variant-numeric:tabular-nums',
|
|
1268
|
-
'font-size:13px',
|
|
1269
|
-
'line-height:1',
|
|
1270
|
-
].join(';');
|
|
1271
|
-
const modeLabel = doc.createElement('span');
|
|
1272
|
-
modeLabel.textContent = 'IDLE';
|
|
1273
|
-
modeLabel.style.cssText = [
|
|
1274
|
-
'margin-left:auto',
|
|
1275
|
-
'padding:2px 4px',
|
|
1276
|
-
'flex:0 0 auto',
|
|
1277
|
-
'border:1px solid var(--fps-editor-border-soft)',
|
|
1278
|
-
'border-radius:4px',
|
|
1279
|
-
'background:color-mix(in srgb, var(--fps-editor-field) 72%, transparent)',
|
|
1280
|
-
'color:var(--fps-editor-muted)',
|
|
1281
|
-
'font-size:9px',
|
|
1282
|
-
'line-height:1',
|
|
1283
|
-
].join(';');
|
|
1284
|
-
root.appendChild(label);
|
|
1285
|
-
root.appendChild(fpsValue);
|
|
1286
|
-
root.appendChild(modeLabel);
|
|
1287
|
-
return { root, fpsValue, modeLabel };
|
|
1288
|
-
}
|
|
1289
|
-
function renderSceneFrameRateOverlay(overlay, stats) {
|
|
1290
|
-
if (!stats) {
|
|
1291
|
-
overlay.root.style.display = 'none';
|
|
1292
|
-
return;
|
|
1293
|
-
}
|
|
1294
|
-
overlay.root.style.display = 'flex';
|
|
1295
|
-
const fps = Number.isFinite(stats.fps) ? Math.round(stats.fps) : null;
|
|
1296
|
-
const displayFps = stats.mode === 'idle' ? 0 : fps;
|
|
1297
|
-
overlay.fpsValue.textContent = displayFps == null ? '--' : String(displayFps);
|
|
1298
|
-
overlay.modeLabel.textContent = stats.mode === 'continuous' ? 'LIVE' : 'IDLE';
|
|
1299
|
-
overlay.modeLabel.style.color = stats.mode === 'continuous'
|
|
1300
|
-
? 'var(--fps-editor-success)'
|
|
1301
|
-
: 'var(--fps-editor-muted)';
|
|
1302
|
-
overlay.fpsValue.style.color = displayFps == null || stats.mode === 'idle'
|
|
1303
|
-
? 'var(--fps-editor-muted)'
|
|
1304
|
-
: displayFps >= 50
|
|
1305
|
-
? 'var(--fps-editor-success)'
|
|
1306
|
-
: displayFps >= 30
|
|
1307
|
-
? 'var(--fps-editor-warn)'
|
|
1308
|
-
: 'var(--fps-editor-danger-strong)';
|
|
1309
|
-
overlay.root.title = [
|
|
1310
|
-
`Scene FPS: ${displayFps == null ? '--' : displayFps}`,
|
|
1311
|
-
`mode: ${stats.mode}`,
|
|
1312
|
-
stats.lastFrameMs == null ? '' : `frame: ${stats.lastFrameMs.toFixed(1)}ms`,
|
|
1313
|
-
stats.activeReasons.length ? `active: ${stats.activeReasons.join(', ')}` : '',
|
|
1314
|
-
].filter(Boolean).join(' | ');
|
|
1315
|
-
}
|
|
1316
1231
|
function createPerformanceMonitorOverlay(doc) {
|
|
1317
1232
|
const root = doc.createElement('section');
|
|
1318
1233
|
root.classList.add(LOCAL_EDITOR_THEME_CLASS, 'fps-editor-ui-overlay-card');
|
|
@@ -1612,154 +1527,211 @@ function isFinitePerformanceNumber(value) {
|
|
|
1612
1527
|
}
|
|
1613
1528
|
function createCoordinateAxesOverlay(doc) {
|
|
1614
1529
|
const root = doc.createElement('div');
|
|
1530
|
+
root.classList.add(LOCAL_EDITOR_THEME_CLASS);
|
|
1615
1531
|
root.dataset.editorCoordinateAxesOverlay = 'true';
|
|
1616
1532
|
root.style.cssText = [
|
|
1617
1533
|
'position:absolute',
|
|
1618
1534
|
'right:14px',
|
|
1619
1535
|
'bottom:14px',
|
|
1620
|
-
'width:
|
|
1621
|
-
'height:
|
|
1536
|
+
'width:124px',
|
|
1537
|
+
'height:124px',
|
|
1622
1538
|
'display:none',
|
|
1623
1539
|
'z-index:2',
|
|
1624
1540
|
'box-sizing:border-box',
|
|
1625
|
-
'
|
|
1626
|
-
'border-radius:8px',
|
|
1627
|
-
'background:color-mix(in srgb, var(--fps-editor-chrome) 58%, transparent)',
|
|
1628
|
-
'box-shadow:var(--fps-editor-shadow-panel)',
|
|
1541
|
+
'background:transparent',
|
|
1629
1542
|
'pointer-events:none',
|
|
1630
|
-
'overflow:
|
|
1543
|
+
'overflow:visible',
|
|
1631
1544
|
].join(';');
|
|
1632
1545
|
const svg = doc.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
1633
|
-
svg.setAttribute('viewBox', '0 0
|
|
1634
|
-
svg.setAttribute('
|
|
1546
|
+
svg.setAttribute('viewBox', '0 0 124 124');
|
|
1547
|
+
svg.setAttribute('role', 'group');
|
|
1548
|
+
svg.setAttribute('aria-label', '视口方向控制');
|
|
1635
1549
|
svg.style.cssText = 'display:block;width:100%;height:100%';
|
|
1636
1550
|
root.appendChild(svg);
|
|
1637
1551
|
const lineLayer = doc.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
1638
1552
|
const labelLayer = doc.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
1639
1553
|
svg.appendChild(lineLayer);
|
|
1640
1554
|
svg.appendChild(labelLayer);
|
|
1641
|
-
const
|
|
1555
|
+
const axisDirectionGroups = new Map();
|
|
1556
|
+
const axisDirectionVisuals = new Map();
|
|
1642
1557
|
const axisLines = new Map();
|
|
1643
|
-
const axisLabels = new Map();
|
|
1644
|
-
const axisDiscs = new Map();
|
|
1645
1558
|
for (const axis of ['x', 'y', 'z']) {
|
|
1646
|
-
const group = doc.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
1647
1559
|
const line = doc.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
1648
1560
|
line.setAttribute('stroke-width', '3.2');
|
|
1649
1561
|
line.setAttribute('stroke-linecap', 'round');
|
|
1650
|
-
line.setAttribute('x1', '
|
|
1651
|
-
line.setAttribute('y1', '
|
|
1652
|
-
|
|
1653
|
-
disc.setAttribute('r', '7');
|
|
1654
|
-
const label = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
1655
|
-
label.setAttribute('text-anchor', 'middle');
|
|
1656
|
-
label.setAttribute('dominant-baseline', 'middle');
|
|
1657
|
-
label.setAttribute('dy', '0.04em');
|
|
1658
|
-
label.style.cssText = [
|
|
1659
|
-
'fill:#172033',
|
|
1660
|
-
'font-size:10px',
|
|
1661
|
-
'font-weight:900',
|
|
1662
|
-
'font-family:Arial, Helvetica, sans-serif',
|
|
1663
|
-
'letter-spacing:0',
|
|
1664
|
-
].join(';');
|
|
1665
|
-
group.appendChild(disc);
|
|
1666
|
-
group.appendChild(label);
|
|
1562
|
+
line.setAttribute('x1', '62');
|
|
1563
|
+
line.setAttribute('y1', '62');
|
|
1564
|
+
line.style.filter = 'drop-shadow(0 1px 1.5px rgba(0,0,0,0.88))';
|
|
1667
1565
|
lineLayer.appendChild(line);
|
|
1668
|
-
labelLayer.appendChild(group);
|
|
1669
|
-
axisGroups.set(axis, group);
|
|
1670
1566
|
axisLines.set(axis, line);
|
|
1671
|
-
|
|
1672
|
-
|
|
1567
|
+
for (const sign of ['positive', 'negative']) {
|
|
1568
|
+
const directionId = `${axis}-${sign}`;
|
|
1569
|
+
const direction = resolveLocalEditorCoordinateAxisDirection(directionId);
|
|
1570
|
+
const group = doc.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
1571
|
+
group.setAttribute('role', 'button');
|
|
1572
|
+
group.setAttribute('tabindex', '0');
|
|
1573
|
+
group.dataset.coordinateAxisDirection = directionId;
|
|
1574
|
+
group.dataset.viewportViewPreset = direction.preset;
|
|
1575
|
+
group.style.cssText = 'pointer-events:auto;cursor:pointer;outline:none';
|
|
1576
|
+
const title = doc.createElementNS('http://www.w3.org/2000/svg', 'title');
|
|
1577
|
+
title.textContent = `${direction.label} · 点击切换正交视图`;
|
|
1578
|
+
const hit = doc.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
1579
|
+
hit.setAttribute('r', '14');
|
|
1580
|
+
hit.setAttribute('fill', 'transparent');
|
|
1581
|
+
const visual = doc.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
1582
|
+
visual.setAttribute('r', sign === 'positive' ? '9' : '8.5');
|
|
1583
|
+
visual.style.cssText = 'transition:filter 120ms ease,opacity 120ms ease;filter:drop-shadow(0 1px 2.5px rgba(0,0,0,0.78))';
|
|
1584
|
+
group.append(title, hit, visual);
|
|
1585
|
+
if (sign === 'positive') {
|
|
1586
|
+
const label = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
1587
|
+
label.setAttribute('text-anchor', 'middle');
|
|
1588
|
+
label.setAttribute('dominant-baseline', 'middle');
|
|
1589
|
+
label.setAttribute('dy', '0.04em');
|
|
1590
|
+
label.style.cssText = [
|
|
1591
|
+
'fill:#172033',
|
|
1592
|
+
'font-size:11px',
|
|
1593
|
+
'font-weight:900',
|
|
1594
|
+
'font-family:Arial, Helvetica, sans-serif',
|
|
1595
|
+
'letter-spacing:0',
|
|
1596
|
+
'pointer-events:none',
|
|
1597
|
+
].join(';');
|
|
1598
|
+
label.textContent = axis.toUpperCase();
|
|
1599
|
+
group.appendChild(label);
|
|
1600
|
+
}
|
|
1601
|
+
const emphasize = () => {
|
|
1602
|
+
visual.style.filter = 'drop-shadow(0 0 4px rgba(255,255,255,0.78))';
|
|
1603
|
+
};
|
|
1604
|
+
const restore = () => {
|
|
1605
|
+
visual.style.filter = 'drop-shadow(0 1px 2.5px rgba(0,0,0,0.78))';
|
|
1606
|
+
};
|
|
1607
|
+
group.addEventListener('pointerenter', emphasize);
|
|
1608
|
+
group.addEventListener('pointerleave', restore);
|
|
1609
|
+
group.addEventListener('focus', emphasize);
|
|
1610
|
+
group.addEventListener('blur', restore);
|
|
1611
|
+
labelLayer.appendChild(group);
|
|
1612
|
+
axisDirectionGroups.set(directionId, group);
|
|
1613
|
+
axisDirectionVisuals.set(directionId, visual);
|
|
1614
|
+
}
|
|
1673
1615
|
}
|
|
1674
1616
|
const centerDot = doc.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
1675
|
-
centerDot.setAttribute('cx', '
|
|
1676
|
-
centerDot.setAttribute('cy', '
|
|
1617
|
+
centerDot.setAttribute('cx', '62');
|
|
1618
|
+
centerDot.setAttribute('cy', '62');
|
|
1677
1619
|
centerDot.setAttribute('r', '3.2');
|
|
1678
|
-
centerDot.setAttribute('fill', '
|
|
1620
|
+
centerDot.setAttribute('fill', '#f4f7fb');
|
|
1621
|
+
centerDot.setAttribute('stroke', 'rgba(16,20,26,0.82)');
|
|
1622
|
+
centerDot.setAttribute('stroke-width', '1.5');
|
|
1679
1623
|
labelLayer.appendChild(centerDot);
|
|
1680
|
-
const
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
projectionIcon.appendChild(projectionIconShape);
|
|
1703
|
-
svg.appendChild(projectionIcon);
|
|
1624
|
+
const floatingButtonStyle = [
|
|
1625
|
+
'position:absolute',
|
|
1626
|
+
'top:4px',
|
|
1627
|
+
'width:26px',
|
|
1628
|
+
'height:26px',
|
|
1629
|
+
'min-width:26px',
|
|
1630
|
+
'padding:0',
|
|
1631
|
+
'border-color:color-mix(in srgb, var(--fps-editor-border) 72%, transparent)',
|
|
1632
|
+
'background:color-mix(in srgb, var(--fps-editor-chrome) 72%, transparent)',
|
|
1633
|
+
'box-shadow:0 2px 6px rgba(0,0,0,0.34)',
|
|
1634
|
+
'backdrop-filter:blur(5px)',
|
|
1635
|
+
'pointer-events:auto',
|
|
1636
|
+
].join(';');
|
|
1637
|
+
const projectionButton = createToolbarIconButton(doc, '投影模式', 'projection-perspective', '投影模式:透视', 'toggle');
|
|
1638
|
+
projectionButton.dataset.coordinateAxesProjectionToggle = 'true';
|
|
1639
|
+
projectionButton.style.cssText = `${floatingButtonStyle};left:4px`;
|
|
1640
|
+
root.appendChild(projectionButton);
|
|
1641
|
+
const viewMenuButton = createToolbarIconButton(doc, '更多视角', 'chevron-down', '更多视角');
|
|
1642
|
+
viewMenuButton.dataset.coordinateAxesViewMenu = 'true';
|
|
1643
|
+
viewMenuButton.setAttribute('aria-haspopup', 'menu');
|
|
1644
|
+
viewMenuButton.style.cssText = `${floatingButtonStyle};right:4px`;
|
|
1645
|
+
root.appendChild(viewMenuButton);
|
|
1704
1646
|
return {
|
|
1705
1647
|
root,
|
|
1706
1648
|
lineLayer,
|
|
1707
1649
|
labelLayer,
|
|
1708
|
-
|
|
1650
|
+
axisDirectionGroups,
|
|
1651
|
+
axisDirectionVisuals,
|
|
1709
1652
|
axisLines,
|
|
1710
|
-
axisLabels,
|
|
1711
|
-
axisDiscs,
|
|
1712
1653
|
centerDot,
|
|
1713
|
-
|
|
1714
|
-
|
|
1654
|
+
projectionButton,
|
|
1655
|
+
viewMenuButton,
|
|
1715
1656
|
};
|
|
1716
1657
|
}
|
|
1717
|
-
function renderCoordinateAxesOverlay(overlay, state) {
|
|
1658
|
+
function renderCoordinateAxesOverlay(overlay, state, viewPreset, busy) {
|
|
1718
1659
|
if (!state?.axes?.length) {
|
|
1719
1660
|
overlay.root.style.display = 'none';
|
|
1720
1661
|
return;
|
|
1721
1662
|
}
|
|
1722
1663
|
overlay.root.style.display = '';
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1664
|
+
const controlsDisabled = state.projectionToggleDisabled === true || busy;
|
|
1665
|
+
renderCoordinateAxesProjectionButton(overlay, state.projectionMode, controlsDisabled);
|
|
1666
|
+
overlay.viewMenuButton.disabled = controlsDisabled;
|
|
1667
|
+
overlay.viewMenuButton.setAttribute('aria-disabled', controlsDisabled ? 'true' : 'false');
|
|
1668
|
+
setToolbarButtonTooltip(overlay.viewMenuButton, controlsDisabled
|
|
1669
|
+
? `视角预设:${VIEWPORT_VIEW_LABELS[viewPreset]} · 当前不可切换`
|
|
1670
|
+
: `视角预设:${VIEWPORT_VIEW_LABELS[viewPreset]}`);
|
|
1671
|
+
const center = 62;
|
|
1672
|
+
const lineLength = 40;
|
|
1673
|
+
const activeDirectionId = resolveLocalEditorCoordinateAxisActiveDirection(viewPreset);
|
|
1726
1674
|
const sortedAxes = [...state.axes].sort((left, right) => right.depth - left.depth);
|
|
1727
1675
|
for (const axis of sortedAxes) {
|
|
1728
|
-
const group = overlay.axisGroups.get(axis.id);
|
|
1729
1676
|
const line = overlay.axisLines.get(axis.id);
|
|
1730
|
-
|
|
1731
|
-
const label = overlay.axisLabels.get(axis.id);
|
|
1732
|
-
if (!group || !line || !disc || !label)
|
|
1677
|
+
if (!line)
|
|
1733
1678
|
continue;
|
|
1734
1679
|
const length = lineLength * axis.scale;
|
|
1735
1680
|
const endX = center + axis.x * length;
|
|
1736
1681
|
const endY = center + axis.y * length;
|
|
1682
|
+
const oppositeX = center - axis.x * length;
|
|
1683
|
+
const oppositeY = center - axis.y * length;
|
|
1737
1684
|
line.setAttribute('x2', formatSvgNumber(endX));
|
|
1738
1685
|
line.setAttribute('y2', formatSvgNumber(endY));
|
|
1739
1686
|
line.setAttribute('stroke', axis.color);
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1687
|
+
const positiveId = `${axis.id}-positive`;
|
|
1688
|
+
const negativeId = `${axis.id}-negative`;
|
|
1689
|
+
const positiveDirection = resolveLocalEditorCoordinateAxisDirection(positiveId);
|
|
1690
|
+
const negativeDirection = resolveLocalEditorCoordinateAxisDirection(negativeId);
|
|
1691
|
+
const positiveGroup = overlay.axisDirectionGroups.get(positiveId);
|
|
1692
|
+
const negativeGroup = overlay.axisDirectionGroups.get(negativeId);
|
|
1693
|
+
const positiveVisual = overlay.axisDirectionVisuals.get(positiveId);
|
|
1694
|
+
const negativeVisual = overlay.axisDirectionVisuals.get(negativeId);
|
|
1695
|
+
if (!positiveGroup || !negativeGroup || !positiveVisual || !negativeVisual)
|
|
1696
|
+
continue;
|
|
1697
|
+
positiveGroup.setAttribute('transform', `translate(${formatSvgNumber(endX)} ${formatSvgNumber(endY)})`);
|
|
1698
|
+
negativeGroup.setAttribute('transform', `translate(${formatSvgNumber(oppositeX)} ${formatSvgNumber(oppositeY)})`);
|
|
1699
|
+
positiveGroup.setAttribute('aria-label', `${positiveDirection.label} · 点击切换正交视图`);
|
|
1700
|
+
negativeGroup.setAttribute('aria-label', `${negativeDirection.label} · 点击切换正交视图`);
|
|
1701
|
+
positiveGroup.setAttribute('aria-disabled', controlsDisabled ? 'true' : 'false');
|
|
1702
|
+
negativeGroup.setAttribute('aria-disabled', controlsDisabled ? 'true' : 'false');
|
|
1703
|
+
positiveGroup.style.cursor = controlsDisabled ? 'not-allowed' : 'pointer';
|
|
1704
|
+
negativeGroup.style.cursor = controlsDisabled ? 'not-allowed' : 'pointer';
|
|
1705
|
+
positiveGroup.style.opacity = String((controlsDisabled ? 0.42 : 0.72) + axis.scale * (controlsDisabled ? 0.12 : 0.28));
|
|
1706
|
+
negativeGroup.style.opacity = controlsDisabled ? '0.3' : String(0.5 + axis.scale * 0.18);
|
|
1707
|
+
const positiveActive = positiveId === activeDirectionId;
|
|
1708
|
+
const negativeActive = negativeId === activeDirectionId;
|
|
1709
|
+
positiveGroup.setAttribute('aria-pressed', String(positiveActive));
|
|
1710
|
+
negativeGroup.setAttribute('aria-pressed', String(negativeActive));
|
|
1711
|
+
positiveVisual.setAttribute('fill', axis.color);
|
|
1712
|
+
positiveVisual.setAttribute('stroke', positiveActive ? '#ffffff' : 'color-mix(in srgb, var(--fps-editor-chrome) 72%, transparent)');
|
|
1713
|
+
positiveVisual.setAttribute('stroke-width', positiveActive ? '2.4' : '1.2');
|
|
1714
|
+
negativeVisual.setAttribute('fill', negativeActive ? `color-mix(in srgb, ${axis.color} 26%, transparent)` : 'color-mix(in srgb, var(--fps-editor-chrome) 62%, transparent)');
|
|
1715
|
+
negativeVisual.setAttribute('stroke', axis.color);
|
|
1716
|
+
negativeVisual.setAttribute('stroke-width', negativeActive ? '3.8' : '3');
|
|
1747
1717
|
overlay.lineLayer.appendChild(line);
|
|
1748
|
-
|
|
1718
|
+
const farGroup = axis.depth >= 0 ? negativeGroup : positiveGroup;
|
|
1719
|
+
const nearGroup = axis.depth >= 0 ? positiveGroup : negativeGroup;
|
|
1720
|
+
overlay.labelLayer.appendChild(farGroup);
|
|
1721
|
+
overlay.labelLayer.appendChild(nearGroup);
|
|
1749
1722
|
}
|
|
1750
1723
|
overlay.labelLayer.appendChild(overlay.centerDot);
|
|
1751
1724
|
}
|
|
1752
|
-
function
|
|
1753
|
-
|
|
1754
|
-
overlay.
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
overlay.
|
|
1758
|
-
overlay.
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
: 'M4.5 14H13.5L10.5 4H7.5ZM7 10.5H11');
|
|
1725
|
+
function renderCoordinateAxesProjectionButton(overlay, mode, disabled) {
|
|
1726
|
+
const nextMode = mode === 'orthographic' ? 'perspective' : 'orthographic';
|
|
1727
|
+
overlay.projectionButton.dataset.viewportProjectionMode = mode;
|
|
1728
|
+
overlay.projectionButton.disabled = disabled;
|
|
1729
|
+
overlay.projectionButton.setAttribute('aria-pressed', mode === 'orthographic' ? 'true' : 'false');
|
|
1730
|
+
setToolbarButtonIcon(overlay.projectionButton.ownerDocument, overlay.projectionButton, mode === 'orthographic' ? 'projection-ortho' : 'projection-perspective');
|
|
1731
|
+
setToolbarButtonTooltip(overlay.projectionButton, disabled
|
|
1732
|
+
? `投影模式:${VIEWPORT_PROJECTION_LABELS[mode]} · 当前不可切换`
|
|
1733
|
+
: `投影模式:${VIEWPORT_PROJECTION_LABELS[mode]} · 点击切换为${VIEWPORT_PROJECTION_LABELS[nextMode]}`);
|
|
1734
|
+
LocalEditorShared.applyButtonActiveState(overlay.projectionButton, mode === 'orthographic');
|
|
1763
1735
|
}
|
|
1764
1736
|
function createSpatialOverlay(doc) {
|
|
1765
1737
|
const root = doc.createElement('div');
|
|
@@ -1876,8 +1848,8 @@ function ensureSpatialOverlayLine(overlay, id) {
|
|
|
1876
1848
|
if (existing)
|
|
1877
1849
|
return existing;
|
|
1878
1850
|
const line = overlay.root.ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
1879
|
-
line.setAttribute('stroke', 'color-mix(in srgb, var(--fps-editor-
|
|
1880
|
-
line.setAttribute('stroke-width', '1.
|
|
1851
|
+
line.setAttribute('stroke', 'color-mix(in srgb, var(--fps-editor-text) 82%, white)');
|
|
1852
|
+
line.setAttribute('stroke-width', '1.15');
|
|
1881
1853
|
line.setAttribute('stroke-linecap', 'round');
|
|
1882
1854
|
line.setAttribute('vector-effect', 'non-scaling-stroke');
|
|
1883
1855
|
line.style.filter = 'drop-shadow(0 1px 2px rgba(0,0,0,0.45))';
|
|
@@ -2021,7 +1993,7 @@ function formatSvgNumber(value) {
|
|
|
2021
1993
|
return Number.isFinite(value) ? value.toFixed(2) : '0';
|
|
2022
1994
|
}
|
|
2023
1995
|
function isLocalEditorAssetBrowserTab(value) {
|
|
2024
|
-
return value === 'all' || value === 'models' || value === 'prefabs' || value === 'materials' || value === 'textures';
|
|
1996
|
+
return value === 'all' || value === 'models' || value === 'prefabs' || value === 'materials' || value === 'textures' || value === 'audio';
|
|
2025
1997
|
}
|
|
2026
1998
|
export function createAssetBrowserMenuItems(state, asset) {
|
|
2027
1999
|
const assetKind = resolveLocalEditorBrowserAssetKind(asset);
|
|
@@ -2031,6 +2003,12 @@ export function createAssetBrowserMenuItems(state, asset) {
|
|
|
2031
2003
|
const items = [];
|
|
2032
2004
|
if (assetKind === 'material') {
|
|
2033
2005
|
const deleteDisabledReason = getMaterialAssetDeleteDisabledReason(asset);
|
|
2006
|
+
if (asset.preview) {
|
|
2007
|
+
items.push({
|
|
2008
|
+
id: 'asset.preview',
|
|
2009
|
+
label: '预览材质',
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2034
2012
|
items.push({
|
|
2035
2013
|
id: 'asset.edit-material',
|
|
2036
2014
|
label: '编辑材质球',
|
|
@@ -2226,6 +2204,13 @@ export function resolveAssetBrowserDoubleClickAction(state, asset) {
|
|
|
2226
2204
|
function copyTextToClipboard(win, text, onResult) {
|
|
2227
2205
|
void writeLocalEditorClipboardText(win, text).then(onResult ?? (() => undefined));
|
|
2228
2206
|
}
|
|
2207
|
+
function formatProjectFileByteSize(size) {
|
|
2208
|
+
if (size < 1024)
|
|
2209
|
+
return `${size} B`;
|
|
2210
|
+
if (size < 1024 * 1024)
|
|
2211
|
+
return `${(size / 1024).toFixed(size < 10 * 1024 ? 1 : 0)} KiB`;
|
|
2212
|
+
return `${(size / (1024 * 1024)).toFixed(1)} MiB`;
|
|
2213
|
+
}
|
|
2229
2214
|
function copyEditorStatusText(button, win, text) {
|
|
2230
2215
|
const idleLabel = button.getAttribute('aria-label') ?? '复制诊断';
|
|
2231
2216
|
copyTextToClipboard(win, text, copied => {
|
|
@@ -2420,8 +2405,21 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
2420
2405
|
let currentState = null;
|
|
2421
2406
|
let pendingStatusDiagnosticRequest = null;
|
|
2422
2407
|
let inspectorFilter = '';
|
|
2408
|
+
let lastRenderedInspectorContentKey = null;
|
|
2423
2409
|
const lockedInspectorVectorGroups = new Set();
|
|
2424
2410
|
let assetBrowserTab = 'all';
|
|
2411
|
+
let assetBrowserFolderPath = '/';
|
|
2412
|
+
const assetBrowserExpandedFolderPaths = new Set(['/']);
|
|
2413
|
+
let assetBrowserViewMode = 'grid';
|
|
2414
|
+
let selectedProjectEntryPath = null;
|
|
2415
|
+
const expandAssetBrowserFolderPath = (folderPath) => {
|
|
2416
|
+
assetBrowserExpandedFolderPaths.add('/');
|
|
2417
|
+
let currentPath = '';
|
|
2418
|
+
for (const segment of folderPath.split('/').filter(Boolean)) {
|
|
2419
|
+
currentPath = `${currentPath}/${segment}`;
|
|
2420
|
+
assetBrowserExpandedFolderPaths.add(currentPath);
|
|
2421
|
+
}
|
|
2422
|
+
};
|
|
2425
2423
|
const hierarchySurface = options.hierarchy?.surface;
|
|
2426
2424
|
if (!hierarchySurface)
|
|
2427
2425
|
throw new Error('Hierarchy surface is required by the Local Editor workbench.');
|
|
@@ -2501,8 +2499,42 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
2501
2499
|
let editorStatusCenterOpen = false;
|
|
2502
2500
|
let performanceMonitorOpen = false;
|
|
2503
2501
|
let assetPreviewOpen = false;
|
|
2502
|
+
let assetPreviewReturnFocus = null;
|
|
2504
2503
|
let previewedAsset = null;
|
|
2505
2504
|
let assetPreviewMode = 'preview';
|
|
2505
|
+
let projectMediaPreviewSource = null;
|
|
2506
|
+
let projectMediaPreviewRequestedPath = '';
|
|
2507
|
+
let projectMediaPreviewError = '';
|
|
2508
|
+
let projectMediaPreviewRequestId = 0;
|
|
2509
|
+
let uiDisposed = false;
|
|
2510
|
+
let projectModelPreviewCleanup = null;
|
|
2511
|
+
let projectModelPreviewAbortController = null;
|
|
2512
|
+
const projectModelPreviewCleanupRegistry = createLocalEditorPreviewCleanupRegistry({
|
|
2513
|
+
onTerminalFailure(error) {
|
|
2514
|
+
console.error('[fps-editor:model-preview] cleanup failed after bounded retries', error);
|
|
2515
|
+
},
|
|
2516
|
+
});
|
|
2517
|
+
const projectMediaThumbnailCache = new Map();
|
|
2518
|
+
const projectMediaThumbnailAbortControllers = new Set();
|
|
2519
|
+
const projectModelThumbnailCache = createLocalEditorProjectModelThumbnailCache(doc.defaultView);
|
|
2520
|
+
let projectMediaThumbnailGeneration = 0;
|
|
2521
|
+
let projectMediaThumbnailObserver = null;
|
|
2522
|
+
let materialPreviewRequestId = 0;
|
|
2523
|
+
let materialPreviewCleanup = null;
|
|
2524
|
+
const materialPreviewCleanupRegistry = createLocalEditorPreviewCleanupRegistry({
|
|
2525
|
+
onTerminalFailure(error) {
|
|
2526
|
+
console.error('[fps-editor:material-preview] cleanup failed after bounded retries', error);
|
|
2527
|
+
},
|
|
2528
|
+
});
|
|
2529
|
+
let projectFilePreviewOpen = false;
|
|
2530
|
+
let projectFilePreviewReturnFocus = null;
|
|
2531
|
+
let projectFilePreviewRequestId = 0;
|
|
2532
|
+
let projectFilePreviewContent = null;
|
|
2533
|
+
let projectFilePreviewRequestedPath = '';
|
|
2534
|
+
let projectFilePreviewError = '';
|
|
2535
|
+
let projectFilePreviewMatches = [];
|
|
2536
|
+
let projectFilePreviewActiveMatch = -1;
|
|
2537
|
+
let projectFilePreviewSearchRaf = null;
|
|
2506
2538
|
let assetPickerOpen = false;
|
|
2507
2539
|
let assetPickerFilter = '';
|
|
2508
2540
|
let assetPickerInput = null;
|
|
@@ -2593,6 +2625,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
2593
2625
|
const rightDockFormalSurfaceVisibility = createLocalEditorFormalSurfaceVisibilityController({
|
|
2594
2626
|
disposeHidden() {
|
|
2595
2627
|
disposeRightDockFormalSurfaces();
|
|
2628
|
+
lastRenderedInspectorContentKey = null;
|
|
2596
2629
|
},
|
|
2597
2630
|
rehydrateVisible() {
|
|
2598
2631
|
disposeRightDockFormalSurfaces();
|
|
@@ -2605,6 +2638,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
2605
2638
|
layerHost: editorUiLayerHost,
|
|
2606
2639
|
lockedVectorGroups: lockedInspectorVectorGroups,
|
|
2607
2640
|
});
|
|
2641
|
+
lastRenderedInspectorContentKey = resolveLocalEditorRightDockContentKey(currentState, 'inspector', inspectorFilter);
|
|
2608
2642
|
}
|
|
2609
2643
|
else if (activeRightTab === 'rendering') {
|
|
2610
2644
|
LocalEditorPanels.renderRenderingPanel(doc, renderingPanel, currentState, { layerHost: editorUiLayerHost });
|
|
@@ -2630,13 +2664,18 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
2630
2664
|
},
|
|
2631
2665
|
});
|
|
2632
2666
|
construction.addCleanup('workbench-layout-controller', () => workbenchLayoutController.dispose());
|
|
2667
|
+
const editorConsole = createLocalEditorConsoleController(doc, workbench.consoleDock, {
|
|
2668
|
+
isCollapsed: () => workbenchLayoutController.getLayout().consoleCollapsed,
|
|
2669
|
+
onCollapsedChange: collapsed => workbenchLayoutController.setConsoleCollapsed(collapsed, true),
|
|
2670
|
+
});
|
|
2671
|
+
construction.addCleanup('editor-console', () => editorConsole.dispose());
|
|
2633
2672
|
const findAssetBrowserScrollViewport = () => (assetPanel.querySelector('[data-editor-asset-browser-scroll-viewport]'));
|
|
2634
2673
|
const assetBrowserViewportReveal = createLocalEditorViewportRevealController({
|
|
2635
2674
|
getViewport: findAssetBrowserScrollViewport,
|
|
2636
2675
|
margin: 4,
|
|
2637
2676
|
});
|
|
2638
|
-
|
|
2639
|
-
let
|
|
2677
|
+
const assetBrowserContinuity = createLocalEditorAssetBrowserContinuityController(() => assetPanel);
|
|
2678
|
+
let lastRenderedAssetBrowserContentKey = null;
|
|
2640
2679
|
let lastRenderedSelectedAssetId;
|
|
2641
2680
|
const sceneToolOverlay = createSceneHeaderToolbar(doc);
|
|
2642
2681
|
const sceneTitle = doc.createElement('div');
|
|
@@ -2677,37 +2716,17 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
2677
2716
|
'border-left:1px solid var(--fps-editor-divider)',
|
|
2678
2717
|
].join(';');
|
|
2679
2718
|
const toolButtons = new Map();
|
|
2680
|
-
const
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
const tooltip = tool.shortcut ? `${tool.label}工具 (${tool.shortcut})` : `${tool.label}工具`;
|
|
2692
|
-
if (!button) {
|
|
2693
|
-
button = LocalEditorShared.createButton(doc, tool.label, {
|
|
2694
|
-
icon: tool.icon && isLocalEditorIconName(tool.icon) ? tool.icon : 'object',
|
|
2695
|
-
labelMode: 'icon-only',
|
|
2696
|
-
variant: 'toolbar-icon',
|
|
2697
|
-
tooltip,
|
|
2698
|
-
ariaLabel: tooltip,
|
|
2699
|
-
});
|
|
2700
|
-
button.dataset.editorToolId = tool.id;
|
|
2701
|
-
toolButtons.set(tool.id, button);
|
|
2702
|
-
}
|
|
2703
|
-
setToolbarButtonTooltip(button, tool.availability.state === 'enabled'
|
|
2704
|
-
? tooltip
|
|
2705
|
-
: `${tooltip} · ${tool.availability.reason}`);
|
|
2706
|
-
button.style.display = tool.availability.state === 'hidden' ? 'none' : 'inline-flex';
|
|
2707
|
-
button.disabled = tool.availability.state !== 'enabled';
|
|
2708
|
-
LocalEditorShared.applyButtonActiveState(button, tool.active);
|
|
2709
|
-
toolGroup.appendChild(button);
|
|
2710
|
-
}
|
|
2719
|
+
const renderToolPresentationState = (state) => {
|
|
2720
|
+
synchronizeLocalEditorToolButtons({
|
|
2721
|
+
document: doc,
|
|
2722
|
+
group: toolGroup,
|
|
2723
|
+
buttons: toolButtons,
|
|
2724
|
+
presentations: state.tools ?? [],
|
|
2725
|
+
});
|
|
2726
|
+
toolGroup.style.display = state.mode === 'editor'
|
|
2727
|
+
&& (state.tools ?? []).some(tool => tool.kind === 'primary' && tool.availability.state !== 'hidden')
|
|
2728
|
+
? 'flex'
|
|
2729
|
+
: 'none';
|
|
2711
2730
|
};
|
|
2712
2731
|
const spaceGroup = doc.createElement('div');
|
|
2713
2732
|
spaceGroup.className = 'fps-editor-ui-toolbar-group';
|
|
@@ -3065,16 +3084,6 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
3065
3084
|
'padding-left:8px',
|
|
3066
3085
|
'border-left:1px solid var(--fps-editor-divider)',
|
|
3067
3086
|
].join(';');
|
|
3068
|
-
const viewportPresetButtons = new Map();
|
|
3069
|
-
for (const preset of ['perspective', 'top', 'front', 'right']) {
|
|
3070
|
-
const button = createToolbarIconButton(doc, VIEW_PRESET_TOOLTIPS[preset], VIEW_PRESET_ICONS[preset], VIEW_PRESET_TOOLTIPS[preset]);
|
|
3071
|
-
button.dataset.viewportViewPreset = preset;
|
|
3072
|
-
viewportPresetButtons.set(preset, button);
|
|
3073
|
-
viewportToolsGroup.appendChild(button);
|
|
3074
|
-
}
|
|
3075
|
-
const projectionToggleButton = createToolbarIconButton(doc, '投影模式', 'projection-perspective', '切换投影模式');
|
|
3076
|
-
projectionToggleButton.dataset.viewportProjectionToggle = 'true';
|
|
3077
|
-
viewportToolsGroup.appendChild(projectionToggleButton);
|
|
3078
3087
|
const markerVisibilityButton = createToolbarIconButton(doc, '标记视图', 'view-overlay', '隐藏标记与关系线', 'toggle');
|
|
3079
3088
|
markerVisibilityButton.dataset.viewportMarkerVisibilityToggle = 'true';
|
|
3080
3089
|
viewportToolsGroup.appendChild(markerVisibilityButton);
|
|
@@ -3155,7 +3164,6 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
3155
3164
|
'pointer-events:auto',
|
|
3156
3165
|
].join(';');
|
|
3157
3166
|
const coordinateAxesOverlay = createCoordinateAxesOverlay(doc);
|
|
3158
|
-
const sceneFrameRateOverlay = createSceneFrameRateOverlay(doc);
|
|
3159
3167
|
const performanceMonitorOverlay = createPerformanceMonitorOverlay(doc);
|
|
3160
3168
|
const prefabImportStatsOverlay = createPrefabImportStatsOverlay(doc);
|
|
3161
3169
|
const prefabStagePreviewLayout = createPrefabStagePreviewLayout(doc);
|
|
@@ -3182,7 +3190,6 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
3182
3190
|
workbench.sceneHeader.appendChild(sceneToolOverlay);
|
|
3183
3191
|
workbench.sceneFrame.appendChild(spatialOverlay.root);
|
|
3184
3192
|
workbench.sceneFrame.appendChild(measurementOverlay.root);
|
|
3185
|
-
workbench.sceneFrame.appendChild(sceneFrameRateOverlay.root);
|
|
3186
3193
|
workbench.sceneFrame.appendChild(performanceMonitorOverlay.root);
|
|
3187
3194
|
workbench.sceneFrame.appendChild(prefabStagePreviewLayout.root);
|
|
3188
3195
|
workbench.sceneFrame.appendChild(coordinateAxesOverlay.root);
|
|
@@ -3299,6 +3306,112 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
3299
3306
|
assetPreviewModal.appendChild(assetPreviewDialog);
|
|
3300
3307
|
construction.appendRootChild(assetPreviewModal);
|
|
3301
3308
|
tooltipSurfaces.add(assetPreviewModal);
|
|
3309
|
+
const projectFilePreviewModal = doc.createElement('div');
|
|
3310
|
+
projectFilePreviewModal.classList.add(LOCAL_EDITOR_THEME_CLASS, 'fps-editor-ui-modal-root');
|
|
3311
|
+
projectFilePreviewModal.dataset.editorProjectFilePreviewModal = 'true';
|
|
3312
|
+
projectFilePreviewModal.dataset.editorWorkbenchRegion = 'modal';
|
|
3313
|
+
projectFilePreviewModal.style.cssText = [
|
|
3314
|
+
'position:fixed',
|
|
3315
|
+
'inset:0',
|
|
3316
|
+
'z-index:2147483642',
|
|
3317
|
+
'display:none',
|
|
3318
|
+
'align-items:center',
|
|
3319
|
+
'justify-content:center',
|
|
3320
|
+
'padding:24px',
|
|
3321
|
+
'background:rgba(0,0,0,0.5)',
|
|
3322
|
+
'font-family:var(--fps-editor-font)',
|
|
3323
|
+
'color:var(--fps-editor-text)',
|
|
3324
|
+
'pointer-events:auto',
|
|
3325
|
+
].join(';');
|
|
3326
|
+
const projectFilePreviewDialog = doc.createElement('div');
|
|
3327
|
+
projectFilePreviewDialog.className = 'fps-editor-ui-modal';
|
|
3328
|
+
projectFilePreviewDialog.setAttribute('role', 'dialog');
|
|
3329
|
+
projectFilePreviewDialog.setAttribute('aria-modal', 'true');
|
|
3330
|
+
projectFilePreviewDialog.setAttribute('aria-label', '只读文件预览');
|
|
3331
|
+
projectFilePreviewDialog.style.cssText = [
|
|
3332
|
+
'width:min(980px,calc(100vw - 48px))',
|
|
3333
|
+
'height:min(720px,calc(100vh - 48px))',
|
|
3334
|
+
'min-height:320px',
|
|
3335
|
+
'display:grid',
|
|
3336
|
+
'grid-template-rows:auto auto minmax(0,1fr) auto',
|
|
3337
|
+
'gap:8px',
|
|
3338
|
+
'padding:12px',
|
|
3339
|
+
'border:1px solid var(--fps-editor-border)',
|
|
3340
|
+
'border-radius:4px',
|
|
3341
|
+
'background:var(--fps-editor-panel)',
|
|
3342
|
+
'box-shadow:var(--fps-editor-shadow-modal)',
|
|
3343
|
+
'overflow:hidden',
|
|
3344
|
+
].join(';');
|
|
3345
|
+
const projectFilePreviewHeader = doc.createElement('div');
|
|
3346
|
+
projectFilePreviewHeader.className = 'fps-editor-ui-modal-header';
|
|
3347
|
+
projectFilePreviewHeader.style.cssText = 'display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0';
|
|
3348
|
+
const projectFilePreviewHeading = doc.createElement('div');
|
|
3349
|
+
projectFilePreviewHeading.style.cssText = 'display:flex;flex-direction:column;gap:2px;min-width:0';
|
|
3350
|
+
const projectFilePreviewTitle = doc.createElement('h2');
|
|
3351
|
+
projectFilePreviewTitle.className = 'fps-editor-ui-modal-title fps-editor-ui-ellipsis';
|
|
3352
|
+
projectFilePreviewTitle.style.cssText = 'margin:0;font-size:13px;font-weight:800;color:var(--fps-editor-text-strong)';
|
|
3353
|
+
const projectFilePreviewPath = doc.createElement('div');
|
|
3354
|
+
projectFilePreviewPath.className = 'fps-editor-ui-ellipsis';
|
|
3355
|
+
projectFilePreviewPath.style.cssText = 'font-size:11px;color:var(--fps-editor-muted)';
|
|
3356
|
+
projectFilePreviewHeading.append(projectFilePreviewTitle, projectFilePreviewPath);
|
|
3357
|
+
const projectFilePreviewCloseButton = createToolbarIconButton(doc, '关闭', 'close', '关闭文件预览');
|
|
3358
|
+
projectFilePreviewCloseButton.dataset.editorProjectFilePreviewClose = 'true';
|
|
3359
|
+
projectFilePreviewHeader.append(projectFilePreviewHeading, projectFilePreviewCloseButton);
|
|
3360
|
+
const projectFilePreviewSearchBar = doc.createElement('div');
|
|
3361
|
+
projectFilePreviewSearchBar.style.cssText = 'display:grid;grid-template-columns:minmax(160px,1fr) auto auto auto;align-items:center;gap:5px';
|
|
3362
|
+
const projectFilePreviewSearchShell = doc.createElement('div');
|
|
3363
|
+
projectFilePreviewSearchShell.className = 'fps-editor-ui-asset-search';
|
|
3364
|
+
const projectFilePreviewSearchIcon = createLocalEditorIcon(doc, 'search', { size: 15 });
|
|
3365
|
+
projectFilePreviewSearchIcon.classList.add('fps-editor-ui-asset-search-icon');
|
|
3366
|
+
const projectFilePreviewSearch = doc.createElement('input');
|
|
3367
|
+
projectFilePreviewSearch.className = 'fps-editor-ui-search';
|
|
3368
|
+
projectFilePreviewSearch.type = 'search';
|
|
3369
|
+
projectFilePreviewSearch.placeholder = '在文件中搜索';
|
|
3370
|
+
projectFilePreviewSearch.dataset.editorProjectFileSearch = 'true';
|
|
3371
|
+
projectFilePreviewSearch.style.paddingLeft = '28px';
|
|
3372
|
+
projectFilePreviewSearchShell.append(projectFilePreviewSearchIcon, projectFilePreviewSearch);
|
|
3373
|
+
const projectFilePreviewMatchCount = doc.createElement('span');
|
|
3374
|
+
projectFilePreviewMatchCount.dataset.editorProjectFileMatchCount = 'true';
|
|
3375
|
+
projectFilePreviewMatchCount.style.cssText = 'min-width:58px;text-align:center;font-size:11px;color:var(--fps-editor-muted-strong);font-variant-numeric:tabular-nums';
|
|
3376
|
+
const projectFilePreviewPrevious = createToolbarIconButton(doc, '上一个匹配项', 'arrow-up', '上一个匹配项');
|
|
3377
|
+
projectFilePreviewPrevious.dataset.editorProjectFileSearchPrevious = 'true';
|
|
3378
|
+
const projectFilePreviewNext = createToolbarIconButton(doc, '下一个匹配项', 'chevron-down', '下一个匹配项');
|
|
3379
|
+
projectFilePreviewNext.dataset.editorProjectFileSearchNext = 'true';
|
|
3380
|
+
projectFilePreviewSearchBar.append(projectFilePreviewSearchShell, projectFilePreviewMatchCount, projectFilePreviewPrevious, projectFilePreviewNext);
|
|
3381
|
+
const projectFilePreviewViewport = doc.createElement('div');
|
|
3382
|
+
projectFilePreviewViewport.dataset.editorProjectFilePreviewViewport = 'true';
|
|
3383
|
+
projectFilePreviewViewport.style.cssText = [
|
|
3384
|
+
'min-width:0',
|
|
3385
|
+
'min-height:0',
|
|
3386
|
+
'overflow:auto',
|
|
3387
|
+
'border:1px solid var(--fps-editor-divider)',
|
|
3388
|
+
'border-radius:3px',
|
|
3389
|
+
'background:var(--fps-editor-field)',
|
|
3390
|
+
].join(';');
|
|
3391
|
+
const projectFilePreviewCode = doc.createElement('pre');
|
|
3392
|
+
projectFilePreviewCode.dataset.editorProjectFilePreviewCode = 'true';
|
|
3393
|
+
projectFilePreviewCode.setAttribute('aria-label', '只读文件内容');
|
|
3394
|
+
projectFilePreviewCode.style.cssText = [
|
|
3395
|
+
'min-width:max-content',
|
|
3396
|
+
'min-height:100%',
|
|
3397
|
+
'margin:0',
|
|
3398
|
+
'padding:12px 14px',
|
|
3399
|
+
'box-sizing:border-box',
|
|
3400
|
+
'white-space:pre',
|
|
3401
|
+
'font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace',
|
|
3402
|
+
'font-size:12px',
|
|
3403
|
+
'line-height:1.55',
|
|
3404
|
+
'tab-size:2',
|
|
3405
|
+
'color:var(--fps-editor-text)',
|
|
3406
|
+
].join(';');
|
|
3407
|
+
projectFilePreviewViewport.appendChild(projectFilePreviewCode);
|
|
3408
|
+
const projectFilePreviewMeta = doc.createElement('div');
|
|
3409
|
+
projectFilePreviewMeta.dataset.editorProjectFilePreviewMeta = 'true';
|
|
3410
|
+
projectFilePreviewMeta.style.cssText = 'min-height:16px;font-size:10px;color:var(--fps-editor-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap';
|
|
3411
|
+
projectFilePreviewDialog.append(projectFilePreviewHeader, projectFilePreviewSearchBar, projectFilePreviewViewport, projectFilePreviewMeta);
|
|
3412
|
+
projectFilePreviewModal.appendChild(projectFilePreviewDialog);
|
|
3413
|
+
construction.appendRootChild(projectFilePreviewModal);
|
|
3414
|
+
tooltipSurfaces.add(projectFilePreviewModal);
|
|
3302
3415
|
const assetPickerModal = doc.createElement('div');
|
|
3303
3416
|
assetPickerModal.classList.add(LOCAL_EDITOR_THEME_CLASS, 'fps-editor-ui-modal-root', 'fps-editor-ui-asset-modal');
|
|
3304
3417
|
assetPickerModal.dataset.editorAssetPickerModal = 'true';
|
|
@@ -3390,12 +3503,12 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
3390
3503
|
applyLocalEditorTheme(workbench.root, activeTheme);
|
|
3391
3504
|
applyLocalEditorTheme(spatialOverlay.root, activeTheme);
|
|
3392
3505
|
applyLocalEditorTheme(measurementOverlay.root, activeTheme);
|
|
3393
|
-
applyLocalEditorTheme(sceneFrameRateOverlay.root, activeTheme);
|
|
3394
3506
|
applyLocalEditorTheme(performanceMonitorOverlay.root, activeTheme);
|
|
3395
3507
|
applyLocalEditorTheme(prefabStagePreviewLayout.root, activeTheme);
|
|
3396
3508
|
applyLocalEditorTheme(prefabImportStatsOverlay.root, activeTheme);
|
|
3397
3509
|
applyLocalEditorTheme(shortcutHelpPanel, activeTheme);
|
|
3398
3510
|
applyLocalEditorTheme(assetPreviewModal, activeTheme);
|
|
3511
|
+
applyLocalEditorTheme(projectFilePreviewModal, activeTheme);
|
|
3399
3512
|
applyLocalEditorTheme(assetPickerModal, activeTheme);
|
|
3400
3513
|
applyLocalEditorTheme(boxSelectionOverlay, activeTheme);
|
|
3401
3514
|
applyLocalEditorTheme(localTestMenu, activeTheme);
|
|
@@ -3504,6 +3617,11 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
3504
3617
|
pendingStatusDiagnosticRequest = navigationRequest;
|
|
3505
3618
|
if (locator.assetId) {
|
|
3506
3619
|
assetBrowserTab = 'all';
|
|
3620
|
+
const asset = currentState?.assets.find(candidate => (candidate.id === locator.assetId || candidate.assetId === locator.assetId));
|
|
3621
|
+
if (asset && currentState) {
|
|
3622
|
+
assetBrowserFolderPath = resolveLocalEditorAssetFilesystemFolderPath(createLocalEditorAssetFilesystemSnapshot(currentState.assets, currentState.projectFiles), asset.id);
|
|
3623
|
+
expandAssetBrowserFolderPath(assetBrowserFolderPath);
|
|
3624
|
+
}
|
|
3507
3625
|
panelRegistry.setBottomDockTab('assets');
|
|
3508
3626
|
workbenchLayoutController.setBottomCollapsed(false);
|
|
3509
3627
|
callbacks.onAssetFilterChange?.('');
|
|
@@ -4151,18 +4269,24 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4151
4269
|
enterEditorButton.addEventListener('click', () => callbacks.onEnterEditor?.());
|
|
4152
4270
|
saveButton.addEventListener('click', () => {
|
|
4153
4271
|
closeLocalTestMenu();
|
|
4154
|
-
if (localTestActionsEnabled)
|
|
4272
|
+
if (localTestActionsEnabled) {
|
|
4273
|
+
editorConsole.append({ severity: 'info', source: 'save', message: '请求保存场景' });
|
|
4155
4274
|
callbacks.onSaveScene?.();
|
|
4275
|
+
}
|
|
4156
4276
|
});
|
|
4157
4277
|
saveAndRunButton.addEventListener('click', () => {
|
|
4158
4278
|
closeLocalTestMenu();
|
|
4159
|
-
if (localTestActionsEnabled)
|
|
4279
|
+
if (localTestActionsEnabled) {
|
|
4280
|
+
editorConsole.append({ severity: 'info', source: 'run', message: '请求保存并运行' });
|
|
4160
4281
|
callbacks.onSaveAndRunGame?.();
|
|
4282
|
+
}
|
|
4161
4283
|
});
|
|
4162
4284
|
discardRunButton.addEventListener('click', () => {
|
|
4163
4285
|
closeLocalTestMenu();
|
|
4164
|
-
if (localTestActionsEnabled)
|
|
4286
|
+
if (localTestActionsEnabled) {
|
|
4287
|
+
editorConsole.append({ severity: 'warning', source: 'run', message: '请求放弃更改并运行' });
|
|
4165
4288
|
callbacks.onDiscardAndRunGame?.();
|
|
4289
|
+
}
|
|
4166
4290
|
});
|
|
4167
4291
|
undoButton.addEventListener('click', () => callbacks.onUndo?.());
|
|
4168
4292
|
redoButton.addEventListener('click', () => callbacks.onRedo?.());
|
|
@@ -4197,31 +4321,99 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4197
4321
|
const nextMode = currentMode === 'orthographic' ? 'perspective' : 'orthographic';
|
|
4198
4322
|
callbacks.onViewportProjectionModeChange?.(nextMode);
|
|
4199
4323
|
if (event.type === 'click')
|
|
4200
|
-
coordinateAxesOverlay.
|
|
4324
|
+
coordinateAxesOverlay.projectionButton.blur();
|
|
4201
4325
|
};
|
|
4202
|
-
coordinateAxesOverlay.
|
|
4326
|
+
coordinateAxesOverlay.projectionButton.addEventListener('pointerdown', (event) => {
|
|
4203
4327
|
event.preventDefault();
|
|
4204
4328
|
});
|
|
4205
|
-
coordinateAxesOverlay.
|
|
4206
|
-
coordinateAxesOverlay.
|
|
4329
|
+
coordinateAxesOverlay.projectionButton.addEventListener('click', toggleCoordinateAxesProjection);
|
|
4330
|
+
coordinateAxesOverlay.projectionButton.addEventListener('keydown', (event) => {
|
|
4207
4331
|
if (event.key !== 'Enter' && event.key !== ' ')
|
|
4208
4332
|
return;
|
|
4209
4333
|
toggleCoordinateAxesProjection(event);
|
|
4210
4334
|
});
|
|
4211
|
-
|
|
4212
|
-
const
|
|
4213
|
-
|
|
4214
|
-
if (preset) {
|
|
4215
|
-
callbacks.onViewportViewPresetChange?.(preset);
|
|
4335
|
+
const activateCoordinateAxesViewPreset = (preset) => {
|
|
4336
|
+
const axesState = currentState?.coordinateAxes ?? null;
|
|
4337
|
+
if (!axesState || axesState.projectionToggleDisabled || currentState?.busy)
|
|
4216
4338
|
return;
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
const nextMode = currentMode === 'orthographic' ? 'perspective' : 'orthographic';
|
|
4222
|
-
callbacks.onViewportProjectionModeChange?.(nextMode);
|
|
4339
|
+
callbacks.onViewportViewPresetChange?.(preset);
|
|
4340
|
+
};
|
|
4341
|
+
coordinateAxesOverlay.root.addEventListener('pointerdown', (event) => {
|
|
4342
|
+
if (!(event.target instanceof Element))
|
|
4223
4343
|
return;
|
|
4224
|
-
|
|
4344
|
+
if (!event.target.closest('[data-coordinate-axis-direction],[data-coordinate-axes-projection-toggle],[data-coordinate-axes-view-menu]'))
|
|
4345
|
+
return;
|
|
4346
|
+
event.preventDefault();
|
|
4347
|
+
event.stopPropagation();
|
|
4348
|
+
});
|
|
4349
|
+
coordinateAxesOverlay.root.addEventListener('click', (event) => {
|
|
4350
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
4351
|
+
const direction = target?.closest('[data-coordinate-axis-direction]');
|
|
4352
|
+
const preset = direction?.dataset.viewportViewPreset;
|
|
4353
|
+
if (!preset)
|
|
4354
|
+
return;
|
|
4355
|
+
event.preventDefault();
|
|
4356
|
+
event.stopPropagation();
|
|
4357
|
+
activateCoordinateAxesViewPreset(preset);
|
|
4358
|
+
});
|
|
4359
|
+
coordinateAxesOverlay.root.addEventListener('keydown', (event) => {
|
|
4360
|
+
if (event.key !== 'Enter' && event.key !== ' ')
|
|
4361
|
+
return;
|
|
4362
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
4363
|
+
const direction = target?.closest('[data-coordinate-axis-direction]');
|
|
4364
|
+
const preset = direction?.dataset.viewportViewPreset;
|
|
4365
|
+
if (!preset)
|
|
4366
|
+
return;
|
|
4367
|
+
event.preventDefault();
|
|
4368
|
+
event.stopPropagation();
|
|
4369
|
+
activateCoordinateAxesViewPreset(preset);
|
|
4370
|
+
});
|
|
4371
|
+
coordinateAxesOverlay.viewMenuButton.addEventListener('click', (event) => {
|
|
4372
|
+
event.preventDefault();
|
|
4373
|
+
event.stopPropagation();
|
|
4374
|
+
const axesState = currentState?.coordinateAxes ?? null;
|
|
4375
|
+
if (!axesState || axesState.projectionToggleDisabled || currentState?.busy)
|
|
4376
|
+
return;
|
|
4377
|
+
const rect = coordinateAxesOverlay.viewMenuButton.getBoundingClientRect();
|
|
4378
|
+
const activePreset = currentState?.viewportTools?.viewPreset ?? 'perspective';
|
|
4379
|
+
const viewItem = (preset, label) => ({
|
|
4380
|
+
id: `viewport-view:${preset}`,
|
|
4381
|
+
label,
|
|
4382
|
+
...(preset === activePreset ? { shortcut: '当前' } : {}),
|
|
4383
|
+
});
|
|
4384
|
+
contextMenu.open({
|
|
4385
|
+
x: rect.right,
|
|
4386
|
+
y: rect.bottom + 4,
|
|
4387
|
+
items: [
|
|
4388
|
+
viewItem('perspective', '透视视图'),
|
|
4389
|
+
viewItem('threeQuarterTop', '斜 45° 俯视'),
|
|
4390
|
+
viewItem('iso', '等距正交'),
|
|
4391
|
+
{
|
|
4392
|
+
id: 'viewport-view:fixed',
|
|
4393
|
+
label: '固定正交视图',
|
|
4394
|
+
separatorBefore: true,
|
|
4395
|
+
children: [
|
|
4396
|
+
viewItem('top', '顶视图 (+Y)'),
|
|
4397
|
+
viewItem('bottom', '底视图 (-Y)'),
|
|
4398
|
+
viewItem('front', '前视图 (-Z)'),
|
|
4399
|
+
viewItem('back', '后视图 (+Z)'),
|
|
4400
|
+
viewItem('left', '左视图 (-X)'),
|
|
4401
|
+
viewItem('right', '右视图 (+X)'),
|
|
4402
|
+
],
|
|
4403
|
+
},
|
|
4404
|
+
],
|
|
4405
|
+
onAction(item) {
|
|
4406
|
+
const presetId = item.id.startsWith('viewport-view:')
|
|
4407
|
+
? item.id.slice('viewport-view:'.length)
|
|
4408
|
+
: null;
|
|
4409
|
+
if (presetId && presetId !== 'fixed') {
|
|
4410
|
+
activateCoordinateAxesViewPreset(presetId);
|
|
4411
|
+
}
|
|
4412
|
+
},
|
|
4413
|
+
});
|
|
4414
|
+
});
|
|
4415
|
+
viewportToolsGroup.addEventListener('click', (event) => {
|
|
4416
|
+
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
4225
4417
|
const markerVisibilityToggle = target?.closest('[data-viewport-marker-visibility-toggle]');
|
|
4226
4418
|
if (markerVisibilityToggle) {
|
|
4227
4419
|
const markersVisible = currentState?.viewportTools?.markersVisible ?? true;
|
|
@@ -4271,10 +4463,15 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4271
4463
|
if (notify)
|
|
4272
4464
|
callbacks.onPerformanceMonitorOpenChange?.(performanceMonitorOpen);
|
|
4273
4465
|
};
|
|
4274
|
-
const
|
|
4466
|
+
const captureAssetBrowserReturnFocus = (trigger) => {
|
|
4467
|
+
const active = doc.activeElement instanceof HTMLElement ? doc.activeElement : null;
|
|
4468
|
+
return captureLocalEditorAssetBrowserReturnFocus(assetPanel, trigger ?? active);
|
|
4469
|
+
};
|
|
4470
|
+
const openAssetPreviewModal = (asset, trigger) => {
|
|
4275
4471
|
if (!asset.preview)
|
|
4276
4472
|
return;
|
|
4277
4473
|
contextMenu.close();
|
|
4474
|
+
assetPreviewReturnFocus = captureAssetBrowserReturnFocus(trigger);
|
|
4278
4475
|
previewedAsset = asset;
|
|
4279
4476
|
assetPreviewMode = 'preview';
|
|
4280
4477
|
assetPreviewDialog.setAttribute('aria-label', '资产预览');
|
|
@@ -4283,10 +4480,11 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4283
4480
|
syncInputModalOpen();
|
|
4284
4481
|
assetPreviewCloseButton.focus({ preventScroll: true });
|
|
4285
4482
|
};
|
|
4286
|
-
const openAssetMaterialEditorModal = (asset) => {
|
|
4483
|
+
const openAssetMaterialEditorModal = (asset, trigger) => {
|
|
4287
4484
|
if (!asset.material)
|
|
4288
4485
|
return;
|
|
4289
4486
|
contextMenu.close();
|
|
4487
|
+
assetPreviewReturnFocus = captureAssetBrowserReturnFocus(trigger);
|
|
4290
4488
|
previewedAsset = asset;
|
|
4291
4489
|
assetPreviewMode = 'material-editor';
|
|
4292
4490
|
assetPreviewDialog.setAttribute('aria-label', '编辑材质球');
|
|
@@ -4295,8 +4493,9 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4295
4493
|
syncInputModalOpen();
|
|
4296
4494
|
assetPreviewCloseButton.focus({ preventScroll: true });
|
|
4297
4495
|
};
|
|
4298
|
-
const openAssetRenameModal = (asset) => {
|
|
4496
|
+
const openAssetRenameModal = (asset, trigger) => {
|
|
4299
4497
|
contextMenu.close();
|
|
4498
|
+
assetPreviewReturnFocus = captureAssetBrowserReturnFocus(trigger);
|
|
4300
4499
|
previewedAsset = asset;
|
|
4301
4500
|
assetPreviewMode = 'rename';
|
|
4302
4501
|
assetPreviewDialog.setAttribute('aria-label', '重命名资产');
|
|
@@ -4309,14 +4508,451 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4309
4508
|
input?.select();
|
|
4310
4509
|
}, 0);
|
|
4311
4510
|
};
|
|
4511
|
+
const disposeProjectModelPreview = () => {
|
|
4512
|
+
projectModelPreviewAbortController?.abort();
|
|
4513
|
+
projectModelPreviewAbortController = null;
|
|
4514
|
+
const cleanup = projectModelPreviewCleanup;
|
|
4515
|
+
projectModelPreviewCleanup = null;
|
|
4516
|
+
projectModelPreviewCleanupRegistry.disposeOwned(cleanup);
|
|
4517
|
+
const failures = projectModelPreviewCleanupRegistry.takeFailures();
|
|
4518
|
+
if (failures.length > 0) {
|
|
4519
|
+
const error = new Error('Project model preview cleanup failed after bounded retries.');
|
|
4520
|
+
error.errors = failures;
|
|
4521
|
+
throw error;
|
|
4522
|
+
}
|
|
4523
|
+
};
|
|
4524
|
+
const resetProjectMediaThumbnailCache = () => {
|
|
4525
|
+
projectMediaThumbnailGeneration += 1;
|
|
4526
|
+
for (const controller of projectMediaThumbnailAbortControllers)
|
|
4527
|
+
controller.abort();
|
|
4528
|
+
projectMediaThumbnailAbortControllers.clear();
|
|
4529
|
+
projectMediaThumbnailCache.clear();
|
|
4530
|
+
projectMediaThumbnailObserver?.disconnect();
|
|
4531
|
+
projectMediaThumbnailObserver = null;
|
|
4532
|
+
};
|
|
4533
|
+
const resolveProjectMediaThumbnail = (browserPath, cacheKey) => {
|
|
4534
|
+
const requestGeneration = projectMediaThumbnailGeneration;
|
|
4535
|
+
const relativePath = toLocalEditorProjectRelativePath(browserPath);
|
|
4536
|
+
const memoryKey = cacheKey || relativePath;
|
|
4537
|
+
const cached = projectMediaThumbnailCache.get(memoryKey);
|
|
4538
|
+
if (cached)
|
|
4539
|
+
return cached;
|
|
4540
|
+
const pending = Promise.resolve(callbacks.onProjectMediaResolve?.(relativePath) ?? null)
|
|
4541
|
+
.then(async (source) => {
|
|
4542
|
+
if (!source)
|
|
4543
|
+
return null;
|
|
4544
|
+
if (source.kind === 'image') {
|
|
4545
|
+
return { alt: source.name, kind: 'image', url: source.url };
|
|
4546
|
+
}
|
|
4547
|
+
if (source.kind !== 'model' || !callbacks.onProjectModelThumbnailResolve)
|
|
4548
|
+
return null;
|
|
4549
|
+
if (requestGeneration !== projectMediaThumbnailGeneration)
|
|
4550
|
+
return null;
|
|
4551
|
+
if (cacheKey) {
|
|
4552
|
+
const cachedUrl = await projectModelThumbnailCache.get(cacheKey);
|
|
4553
|
+
if (cachedUrl) {
|
|
4554
|
+
return { alt: `${source.name} 模型缩略图`, kind: 'model', url: cachedUrl };
|
|
4555
|
+
}
|
|
4556
|
+
}
|
|
4557
|
+
const controller = new AbortController();
|
|
4558
|
+
projectMediaThumbnailAbortControllers.add(controller);
|
|
4559
|
+
let url;
|
|
4560
|
+
try {
|
|
4561
|
+
url = await callbacks.onProjectModelThumbnailResolve(source, controller.signal);
|
|
4562
|
+
}
|
|
4563
|
+
finally {
|
|
4564
|
+
projectMediaThumbnailAbortControllers.delete(controller);
|
|
4565
|
+
}
|
|
4566
|
+
if (controller.signal.aborted || requestGeneration !== projectMediaThumbnailGeneration)
|
|
4567
|
+
return null;
|
|
4568
|
+
if (url && cacheKey)
|
|
4569
|
+
void projectModelThumbnailCache.put(cacheKey, url);
|
|
4570
|
+
return url ? { alt: `${source.name} 模型缩略图`, kind: 'model', url } : null;
|
|
4571
|
+
})
|
|
4572
|
+
.catch(() => null);
|
|
4573
|
+
projectMediaThumbnailCache.set(memoryKey, pending);
|
|
4574
|
+
return pending;
|
|
4575
|
+
};
|
|
4576
|
+
const hydrateProjectMediaThumbnails = () => {
|
|
4577
|
+
if (!callbacks.onProjectMediaResolve)
|
|
4578
|
+
return;
|
|
4579
|
+
const generation = projectMediaThumbnailGeneration;
|
|
4580
|
+
projectMediaThumbnailObserver?.disconnect();
|
|
4581
|
+
projectMediaThumbnailObserver = null;
|
|
4582
|
+
const hydrate = (frame) => {
|
|
4583
|
+
const path = frame.dataset.editorProjectMediaThumbnailPath;
|
|
4584
|
+
if (!path || frame.dataset.thumbnailRequested === 'true')
|
|
4585
|
+
return;
|
|
4586
|
+
const cacheKey = frame.dataset.editorProjectMediaThumbnailCacheKey;
|
|
4587
|
+
frame.dataset.thumbnailRequested = 'true';
|
|
4588
|
+
void resolveProjectMediaThumbnail(path, cacheKey).then((result) => {
|
|
4589
|
+
if (!frame.isConnected || generation !== projectMediaThumbnailGeneration)
|
|
4590
|
+
return;
|
|
4591
|
+
if (!result) {
|
|
4592
|
+
frame.dataset.thumbnailState = 'failed';
|
|
4593
|
+
return;
|
|
4594
|
+
}
|
|
4595
|
+
const image = doc.createElement('img');
|
|
4596
|
+
image.alt = result.alt;
|
|
4597
|
+
image.decoding = 'async';
|
|
4598
|
+
image.draggable = false;
|
|
4599
|
+
if (result.kind === 'image')
|
|
4600
|
+
image.loading = 'lazy';
|
|
4601
|
+
image.addEventListener('load', () => {
|
|
4602
|
+
if (frame.isConnected)
|
|
4603
|
+
frame.dataset.thumbnailState = 'ready';
|
|
4604
|
+
}, { once: true });
|
|
4605
|
+
image.addEventListener('error', () => {
|
|
4606
|
+
image.remove();
|
|
4607
|
+
if (frame.isConnected)
|
|
4608
|
+
frame.dataset.thumbnailState = 'failed';
|
|
4609
|
+
}, { once: true });
|
|
4610
|
+
image.src = result.url;
|
|
4611
|
+
frame.appendChild(image);
|
|
4612
|
+
});
|
|
4613
|
+
};
|
|
4614
|
+
const frames = assetPanel.querySelectorAll('[data-editor-project-media-thumbnail-path]');
|
|
4615
|
+
const Observer = doc.defaultView?.IntersectionObserver;
|
|
4616
|
+
if (!Observer) {
|
|
4617
|
+
for (const frame of frames)
|
|
4618
|
+
hydrate(frame);
|
|
4619
|
+
return;
|
|
4620
|
+
}
|
|
4621
|
+
const viewport = assetPanel.querySelector('[data-editor-asset-browser-scroll-viewport]');
|
|
4622
|
+
projectMediaThumbnailObserver = new Observer((entries, observer) => {
|
|
4623
|
+
for (const entry of entries) {
|
|
4624
|
+
if (!entry.isIntersecting || !(entry.target instanceof HTMLElement))
|
|
4625
|
+
continue;
|
|
4626
|
+
observer.unobserve(entry.target);
|
|
4627
|
+
hydrate(entry.target);
|
|
4628
|
+
}
|
|
4629
|
+
}, { root: viewport, rootMargin: '120px 0px' });
|
|
4630
|
+
for (const frame of frames)
|
|
4631
|
+
projectMediaThumbnailObserver.observe(frame);
|
|
4632
|
+
};
|
|
4633
|
+
const disposeMaterialPreview = () => {
|
|
4634
|
+
materialPreviewRequestId += 1;
|
|
4635
|
+
const cleanup = materialPreviewCleanup;
|
|
4636
|
+
materialPreviewCleanup = null;
|
|
4637
|
+
materialPreviewCleanupRegistry.disposeOwned(cleanup);
|
|
4638
|
+
const failures = materialPreviewCleanupRegistry.takeFailures();
|
|
4639
|
+
if (failures.length > 0) {
|
|
4640
|
+
const error = new Error('Material preview cleanup failed after bounded retries.');
|
|
4641
|
+
error.errors = failures;
|
|
4642
|
+
throw error;
|
|
4643
|
+
}
|
|
4644
|
+
};
|
|
4645
|
+
const createMaterialPreviewStage = (asset) => {
|
|
4646
|
+
const preview = asset.preview;
|
|
4647
|
+
if (preview?.kind !== 'material-sphere' || !callbacks.onMaterialPreviewMount) {
|
|
4648
|
+
return LocalEditorPanels.createAssetPreviewElement(doc, asset, 'modal');
|
|
4649
|
+
}
|
|
4650
|
+
disposeMaterialPreview();
|
|
4651
|
+
const requestId = materialPreviewRequestId;
|
|
4652
|
+
const stage = doc.createElement('div');
|
|
4653
|
+
stage.dataset.editorMaterialPreviewStage = 'true';
|
|
4654
|
+
stage.dataset.previewState = 'loading';
|
|
4655
|
+
stage.style.cssText = [
|
|
4656
|
+
'position:relative',
|
|
4657
|
+
'width:100%',
|
|
4658
|
+
'aspect-ratio:16 / 10',
|
|
4659
|
+
'min-height:180px',
|
|
4660
|
+
'overflow:hidden',
|
|
4661
|
+
'border:1px solid var(--fps-editor-border)',
|
|
4662
|
+
'border-radius:4px',
|
|
4663
|
+
'background:var(--fps-editor-field)',
|
|
4664
|
+
'box-sizing:border-box',
|
|
4665
|
+
].join(';');
|
|
4666
|
+
const fallback = LocalEditorPanels.createAssetPreviewElement(doc, asset, 'modal');
|
|
4667
|
+
fallback.dataset.editorMaterialPreviewFallback = 'true';
|
|
4668
|
+
fallback.style.position = 'absolute';
|
|
4669
|
+
fallback.style.left = '50%';
|
|
4670
|
+
fallback.style.top = '50%';
|
|
4671
|
+
fallback.style.transform = 'translate(-50%,-50%)';
|
|
4672
|
+
fallback.style.border = '0';
|
|
4673
|
+
fallback.style.background = 'transparent';
|
|
4674
|
+
const canvas = doc.createElement('canvas');
|
|
4675
|
+
canvas.dataset.editorMaterialPreviewCanvas = 'true';
|
|
4676
|
+
canvas.setAttribute('aria-label', `${asset.label} 材质球三维预览`);
|
|
4677
|
+
canvas.title = '材质球预览';
|
|
4678
|
+
canvas.style.cssText = [
|
|
4679
|
+
'position:absolute',
|
|
4680
|
+
'inset:0',
|
|
4681
|
+
'display:block',
|
|
4682
|
+
'width:100%',
|
|
4683
|
+
'height:100%',
|
|
4684
|
+
'opacity:0',
|
|
4685
|
+
'cursor:grab',
|
|
4686
|
+
'touch-action:none',
|
|
4687
|
+
'outline:none',
|
|
4688
|
+
].join(';');
|
|
4689
|
+
stage.append(fallback, canvas);
|
|
4690
|
+
const source = {
|
|
4691
|
+
id: asset.material?.id ?? asset.assetId ?? asset.id,
|
|
4692
|
+
name: asset.material?.name || asset.label,
|
|
4693
|
+
materialKind: asset.material?.materialKind,
|
|
4694
|
+
preview,
|
|
4695
|
+
};
|
|
4696
|
+
void Promise.resolve().then(() => callbacks.onMaterialPreviewMount(source, canvas)).then((cleanup) => {
|
|
4697
|
+
if (!assetPreviewOpen || requestId !== materialPreviewRequestId || !stage.isConnected) {
|
|
4698
|
+
materialPreviewCleanupRegistry.disposeOwned(cleanup);
|
|
4699
|
+
return undefined;
|
|
4700
|
+
}
|
|
4701
|
+
materialPreviewCleanupRegistry.own(cleanup);
|
|
4702
|
+
materialPreviewCleanup = cleanup;
|
|
4703
|
+
stage.dataset.previewState = 'ready';
|
|
4704
|
+
fallback.style.display = 'none';
|
|
4705
|
+
canvas.style.opacity = '1';
|
|
4706
|
+
return undefined;
|
|
4707
|
+
}).catch(() => {
|
|
4708
|
+
if (requestId !== materialPreviewRequestId || !stage.isConnected)
|
|
4709
|
+
return;
|
|
4710
|
+
stage.dataset.previewState = 'fallback';
|
|
4711
|
+
canvas.style.display = 'none';
|
|
4712
|
+
fallback.style.display = 'flex';
|
|
4713
|
+
});
|
|
4714
|
+
return stage;
|
|
4715
|
+
};
|
|
4716
|
+
const openProjectMediaPreview = async (browserPath, trigger) => {
|
|
4717
|
+
const relativePath = toLocalEditorProjectRelativePath(browserPath);
|
|
4718
|
+
if (!callbacks.onProjectMediaResolve || !isEditorProjectMediaPreviewable(relativePath))
|
|
4719
|
+
return;
|
|
4720
|
+
contextMenu.close();
|
|
4721
|
+
closeProjectFilePreview();
|
|
4722
|
+
disposeProjectModelPreview();
|
|
4723
|
+
assetPreviewReturnFocus = captureAssetBrowserReturnFocus(trigger);
|
|
4724
|
+
const requestId = ++projectMediaPreviewRequestId;
|
|
4725
|
+
projectMediaPreviewSource = null;
|
|
4726
|
+
projectMediaPreviewRequestedPath = relativePath;
|
|
4727
|
+
projectMediaPreviewError = '';
|
|
4728
|
+
previewedAsset = null;
|
|
4729
|
+
assetPreviewMode = 'project-media';
|
|
4730
|
+
assetPreviewDialog.setAttribute('aria-label', '媒体文件预览');
|
|
4731
|
+
assetPreviewOpen = true;
|
|
4732
|
+
renderAssetPreviewModal();
|
|
4733
|
+
syncInputModalOpen();
|
|
4734
|
+
try {
|
|
4735
|
+
const source = await callbacks.onProjectMediaResolve(relativePath);
|
|
4736
|
+
if (!assetPreviewOpen || assetPreviewMode !== 'project-media' || requestId !== projectMediaPreviewRequestId)
|
|
4737
|
+
return;
|
|
4738
|
+
if (!source)
|
|
4739
|
+
throw new Error('project_file_media_unsupported');
|
|
4740
|
+
projectMediaPreviewSource = source;
|
|
4741
|
+
renderAssetPreviewModal();
|
|
4742
|
+
if (source.kind === 'model') {
|
|
4743
|
+
const canvas = assetPreviewBody.querySelector('[data-editor-project-model-preview]');
|
|
4744
|
+
if (!canvas || !callbacks.onProjectModelPreviewMount)
|
|
4745
|
+
throw new Error('project_model_preview_unavailable');
|
|
4746
|
+
const controller = new AbortController();
|
|
4747
|
+
projectModelPreviewAbortController = controller;
|
|
4748
|
+
const cleanup = await callbacks.onProjectModelPreviewMount(source, canvas, controller.signal);
|
|
4749
|
+
if (uiDisposed
|
|
4750
|
+
|| !assetPreviewDialog.isConnected
|
|
4751
|
+
|| !assetPreviewOpen
|
|
4752
|
+
|| assetPreviewMode !== 'project-media'
|
|
4753
|
+
|| requestId !== projectMediaPreviewRequestId) {
|
|
4754
|
+
controller.abort();
|
|
4755
|
+
if (projectModelPreviewAbortController === controller)
|
|
4756
|
+
projectModelPreviewAbortController = null;
|
|
4757
|
+
projectModelPreviewCleanupRegistry.disposeOwned(cleanup);
|
|
4758
|
+
return;
|
|
4759
|
+
}
|
|
4760
|
+
projectModelPreviewCleanupRegistry.own(cleanup);
|
|
4761
|
+
projectModelPreviewCleanup = cleanup;
|
|
4762
|
+
}
|
|
4763
|
+
assetPreviewCloseButton.focus({ preventScroll: true });
|
|
4764
|
+
}
|
|
4765
|
+
catch (error) {
|
|
4766
|
+
if (uiDisposed
|
|
4767
|
+
|| !assetPreviewDialog.isConnected
|
|
4768
|
+
|| !assetPreviewOpen
|
|
4769
|
+
|| assetPreviewMode !== 'project-media'
|
|
4770
|
+
|| requestId !== projectMediaPreviewRequestId)
|
|
4771
|
+
return;
|
|
4772
|
+
disposeProjectModelPreview();
|
|
4773
|
+
projectMediaPreviewError = error instanceof Error ? error.message : String(error);
|
|
4774
|
+
renderAssetPreviewModal();
|
|
4775
|
+
assetPreviewCloseButton.focus({ preventScroll: true });
|
|
4776
|
+
}
|
|
4777
|
+
};
|
|
4312
4778
|
const closeAssetPreviewModal = () => {
|
|
4313
4779
|
if (!assetPreviewOpen)
|
|
4314
4780
|
return;
|
|
4781
|
+
const returnFocus = assetPreviewReturnFocus;
|
|
4782
|
+
assetPreviewReturnFocus = null;
|
|
4783
|
+
projectMediaPreviewRequestId += 1;
|
|
4784
|
+
disposeProjectModelPreview();
|
|
4785
|
+
disposeMaterialPreview();
|
|
4315
4786
|
assetPreviewOpen = false;
|
|
4316
4787
|
previewedAsset = null;
|
|
4788
|
+
projectMediaPreviewSource = null;
|
|
4789
|
+
projectMediaPreviewRequestedPath = '';
|
|
4790
|
+
projectMediaPreviewError = '';
|
|
4317
4791
|
assetPreviewMode = 'preview';
|
|
4318
4792
|
renderAssetPreviewModal();
|
|
4319
4793
|
syncInputModalOpen();
|
|
4794
|
+
restoreLocalEditorAssetBrowserReturnFocus(assetPanel, returnFocus);
|
|
4795
|
+
};
|
|
4796
|
+
const renderProjectFilePreview = () => {
|
|
4797
|
+
if (projectFilePreviewSearchRaf != null) {
|
|
4798
|
+
doc.defaultView?.cancelAnimationFrame(projectFilePreviewSearchRaf);
|
|
4799
|
+
projectFilePreviewSearchRaf = null;
|
|
4800
|
+
}
|
|
4801
|
+
projectFilePreviewModal.style.display = projectFilePreviewOpen ? 'flex' : 'none';
|
|
4802
|
+
if (!projectFilePreviewOpen)
|
|
4803
|
+
return;
|
|
4804
|
+
const content = projectFilePreviewContent;
|
|
4805
|
+
const displayPath = content?.path ?? projectFilePreviewRequestedPath;
|
|
4806
|
+
const displayPathSegments = displayPath.split('/');
|
|
4807
|
+
projectFilePreviewTitle.textContent = content?.name
|
|
4808
|
+
?? displayPathSegments[displayPathSegments.length - 1]
|
|
4809
|
+
?? '文件预览';
|
|
4810
|
+
projectFilePreviewPath.textContent = displayPath;
|
|
4811
|
+
projectFilePreviewPath.title = displayPath;
|
|
4812
|
+
projectFilePreviewSearch.disabled = !content;
|
|
4813
|
+
projectFilePreviewPrevious.disabled = projectFilePreviewMatches.length === 0;
|
|
4814
|
+
projectFilePreviewNext.disabled = projectFilePreviewMatches.length === 0;
|
|
4815
|
+
projectFilePreviewMatchCount.textContent = projectFilePreviewMatches.length > 0
|
|
4816
|
+
? `${projectFilePreviewActiveMatch + 1} / ${projectFilePreviewMatches.length}`
|
|
4817
|
+
: projectFilePreviewSearch.value.trim() ? '0 / 0' : '0';
|
|
4818
|
+
projectFilePreviewMeta.textContent = projectFilePreviewError
|
|
4819
|
+
? projectFilePreviewError
|
|
4820
|
+
: content
|
|
4821
|
+
? [
|
|
4822
|
+
content.language,
|
|
4823
|
+
formatProjectFileByteSize(content.size),
|
|
4824
|
+
content.truncated ? '文件内容已截断' : '',
|
|
4825
|
+
'只读',
|
|
4826
|
+
].filter(Boolean).join(' · ')
|
|
4827
|
+
: '正在读取文件...';
|
|
4828
|
+
while (projectFilePreviewCode.firstChild)
|
|
4829
|
+
projectFilePreviewCode.removeChild(projectFilePreviewCode.firstChild);
|
|
4830
|
+
if (projectFilePreviewError) {
|
|
4831
|
+
projectFilePreviewCode.textContent = `无法查看文件:${projectFilePreviewError}`;
|
|
4832
|
+
return;
|
|
4833
|
+
}
|
|
4834
|
+
if (!content) {
|
|
4835
|
+
projectFilePreviewCode.textContent = '正在读取文件...';
|
|
4836
|
+
return;
|
|
4837
|
+
}
|
|
4838
|
+
if (projectFilePreviewMatches.length === 0) {
|
|
4839
|
+
projectFilePreviewCode.textContent = content.text;
|
|
4840
|
+
return;
|
|
4841
|
+
}
|
|
4842
|
+
let cursor = 0;
|
|
4843
|
+
for (const [index, match] of projectFilePreviewMatches.entries()) {
|
|
4844
|
+
if (match.start > cursor) {
|
|
4845
|
+
projectFilePreviewCode.appendChild(doc.createTextNode(content.text.slice(cursor, match.start)));
|
|
4846
|
+
}
|
|
4847
|
+
const mark = doc.createElement('mark');
|
|
4848
|
+
mark.dataset.editorProjectFileSearchMatch = String(index);
|
|
4849
|
+
mark.dataset.active = index === projectFilePreviewActiveMatch ? 'true' : 'false';
|
|
4850
|
+
mark.textContent = content.text.slice(match.start, match.end);
|
|
4851
|
+
mark.style.cssText = index === projectFilePreviewActiveMatch
|
|
4852
|
+
? 'background:var(--fps-editor-accent);color:var(--fps-editor-panel);outline:1px solid var(--fps-editor-accent-strong)'
|
|
4853
|
+
: 'background:var(--fps-editor-warn-soft);color:var(--fps-editor-text-strong);outline:1px solid var(--fps-editor-warn-border)';
|
|
4854
|
+
projectFilePreviewCode.appendChild(mark);
|
|
4855
|
+
cursor = match.end;
|
|
4856
|
+
}
|
|
4857
|
+
if (cursor < content.text.length) {
|
|
4858
|
+
projectFilePreviewCode.appendChild(doc.createTextNode(content.text.slice(cursor)));
|
|
4859
|
+
}
|
|
4860
|
+
const active = projectFilePreviewCode.querySelector('[data-editor-project-file-search-match][data-active="true"]');
|
|
4861
|
+
projectFilePreviewSearchRaf = doc.defaultView?.requestAnimationFrame(() => {
|
|
4862
|
+
projectFilePreviewSearchRaf = null;
|
|
4863
|
+
if (projectFilePreviewOpen && active?.isConnected) {
|
|
4864
|
+
active.scrollIntoView({ block: 'center', inline: 'nearest' });
|
|
4865
|
+
}
|
|
4866
|
+
}) ?? null;
|
|
4867
|
+
};
|
|
4868
|
+
const updateProjectFilePreviewSearch = (resetActive) => {
|
|
4869
|
+
projectFilePreviewMatches = projectFilePreviewContent
|
|
4870
|
+
? findLocalEditorProjectFileSearchMatches(projectFilePreviewContent.text, projectFilePreviewSearch.value)
|
|
4871
|
+
: [];
|
|
4872
|
+
if (resetActive)
|
|
4873
|
+
projectFilePreviewActiveMatch = projectFilePreviewMatches.length > 0 ? 0 : -1;
|
|
4874
|
+
else if (projectFilePreviewMatches.length === 0)
|
|
4875
|
+
projectFilePreviewActiveMatch = -1;
|
|
4876
|
+
else
|
|
4877
|
+
projectFilePreviewActiveMatch = Math.min(Math.max(0, projectFilePreviewActiveMatch), projectFilePreviewMatches.length - 1);
|
|
4878
|
+
renderProjectFilePreview();
|
|
4879
|
+
};
|
|
4880
|
+
const moveProjectFilePreviewMatch = (delta) => {
|
|
4881
|
+
if (projectFilePreviewMatches.length === 0)
|
|
4882
|
+
return;
|
|
4883
|
+
projectFilePreviewActiveMatch = (projectFilePreviewActiveMatch + delta + projectFilePreviewMatches.length) % projectFilePreviewMatches.length;
|
|
4884
|
+
renderProjectFilePreview();
|
|
4885
|
+
};
|
|
4886
|
+
const closeProjectFilePreview = () => {
|
|
4887
|
+
if (!projectFilePreviewOpen)
|
|
4888
|
+
return;
|
|
4889
|
+
if (projectFilePreviewSearchRaf != null) {
|
|
4890
|
+
doc.defaultView?.cancelAnimationFrame(projectFilePreviewSearchRaf);
|
|
4891
|
+
projectFilePreviewSearchRaf = null;
|
|
4892
|
+
}
|
|
4893
|
+
const returnFocus = projectFilePreviewReturnFocus;
|
|
4894
|
+
projectFilePreviewReturnFocus = null;
|
|
4895
|
+
projectFilePreviewRequestId += 1;
|
|
4896
|
+
projectFilePreviewOpen = false;
|
|
4897
|
+
projectFilePreviewContent = null;
|
|
4898
|
+
projectFilePreviewRequestedPath = '';
|
|
4899
|
+
projectFilePreviewError = '';
|
|
4900
|
+
projectFilePreviewMatches = [];
|
|
4901
|
+
projectFilePreviewActiveMatch = -1;
|
|
4902
|
+
projectFilePreviewSearch.value = '';
|
|
4903
|
+
renderProjectFilePreview();
|
|
4904
|
+
syncInputModalOpen();
|
|
4905
|
+
restoreLocalEditorAssetBrowserReturnFocus(assetPanel, returnFocus);
|
|
4906
|
+
};
|
|
4907
|
+
const openProjectFilePreview = async (browserPath, trigger) => {
|
|
4908
|
+
const relativePath = toLocalEditorProjectRelativePath(browserPath);
|
|
4909
|
+
if (!callbacks.onProjectFileRead || !isEditorProjectFilePreviewable(relativePath))
|
|
4910
|
+
return;
|
|
4911
|
+
contextMenu.close();
|
|
4912
|
+
if (assetPreviewOpen)
|
|
4913
|
+
closeAssetPreviewModal();
|
|
4914
|
+
projectFilePreviewReturnFocus = captureAssetBrowserReturnFocus(trigger);
|
|
4915
|
+
const requestId = ++projectFilePreviewRequestId;
|
|
4916
|
+
projectFilePreviewOpen = true;
|
|
4917
|
+
projectFilePreviewContent = null;
|
|
4918
|
+
projectFilePreviewRequestedPath = relativePath;
|
|
4919
|
+
projectFilePreviewError = '';
|
|
4920
|
+
projectFilePreviewMatches = [];
|
|
4921
|
+
projectFilePreviewActiveMatch = -1;
|
|
4922
|
+
projectFilePreviewSearch.value = '';
|
|
4923
|
+
renderProjectFilePreview();
|
|
4924
|
+
syncInputModalOpen();
|
|
4925
|
+
try {
|
|
4926
|
+
const content = await callbacks.onProjectFileRead(relativePath);
|
|
4927
|
+
if (!projectFilePreviewOpen || requestId !== projectFilePreviewRequestId)
|
|
4928
|
+
return;
|
|
4929
|
+
projectFilePreviewContent = content;
|
|
4930
|
+
updateProjectFilePreviewSearch(true);
|
|
4931
|
+
projectFilePreviewSearch.focus({ preventScroll: true });
|
|
4932
|
+
}
|
|
4933
|
+
catch (error) {
|
|
4934
|
+
if (!projectFilePreviewOpen || requestId !== projectFilePreviewRequestId)
|
|
4935
|
+
return;
|
|
4936
|
+
projectFilePreviewError = error instanceof Error ? error.message : String(error);
|
|
4937
|
+
renderProjectFilePreview();
|
|
4938
|
+
projectFilePreviewCloseButton.focus({ preventScroll: true });
|
|
4939
|
+
}
|
|
4940
|
+
};
|
|
4941
|
+
const openProjectBrowserFile = (browserPath, trigger) => {
|
|
4942
|
+
const relativePath = toLocalEditorProjectRelativePath(browserPath);
|
|
4943
|
+
const action = resolveLocalEditorProjectFileOpenAction(relativePath, {
|
|
4944
|
+
mediaPreview: !!callbacks.onProjectMediaResolve,
|
|
4945
|
+
textPreview: !!callbacks.onProjectFileRead,
|
|
4946
|
+
});
|
|
4947
|
+
if (action === 'media-preview') {
|
|
4948
|
+
void openProjectMediaPreview(browserPath, trigger);
|
|
4949
|
+
return true;
|
|
4950
|
+
}
|
|
4951
|
+
if (action === 'text-preview') {
|
|
4952
|
+
void openProjectFilePreview(browserPath, trigger);
|
|
4953
|
+
return true;
|
|
4954
|
+
}
|
|
4955
|
+
return false;
|
|
4320
4956
|
};
|
|
4321
4957
|
const openInspectorAssetPicker = (input, property) => {
|
|
4322
4958
|
contextMenu.close();
|
|
@@ -4328,6 +4964,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4328
4964
|
assetPickerFilter = '';
|
|
4329
4965
|
assetPickerOpen = true;
|
|
4330
4966
|
closeAssetPreviewModal();
|
|
4967
|
+
closeProjectFilePreview();
|
|
4331
4968
|
renderAssetPickerModal();
|
|
4332
4969
|
syncInputModalOpen();
|
|
4333
4970
|
assetPickerSearch.focus({ preventScroll: true });
|
|
@@ -4372,7 +5009,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4372
5009
|
syncInputModalOpen();
|
|
4373
5010
|
};
|
|
4374
5011
|
function syncInputModalOpen() {
|
|
4375
|
-
inputRouter.setModalOpen(currentState?.mode === 'editor' && (helpOpen || assetPreviewOpen || assetPickerOpen));
|
|
5012
|
+
inputRouter.setModalOpen(currentState?.mode === 'editor' && (helpOpen || assetPreviewOpen || projectFilePreviewOpen || assetPickerOpen));
|
|
4376
5013
|
}
|
|
4377
5014
|
function resolvePreviewedAssetFromState() {
|
|
4378
5015
|
if (!previewedAsset)
|
|
@@ -4385,11 +5022,17 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4385
5022
|
return previewedAsset;
|
|
4386
5023
|
}
|
|
4387
5024
|
function renderAssetPreviewModal() {
|
|
5025
|
+
if (assetPreviewMode === 'project-media') {
|
|
5026
|
+
disposeMaterialPreview();
|
|
5027
|
+
renderProjectMediaPreviewModal();
|
|
5028
|
+
return;
|
|
5029
|
+
}
|
|
4388
5030
|
const asset = resolvePreviewedAssetFromState();
|
|
4389
5031
|
if (!assetPreviewOpen
|
|
4390
5032
|
|| !asset
|
|
4391
5033
|
|| (assetPreviewMode === 'preview' && !asset.preview)
|
|
4392
5034
|
|| (assetPreviewMode === 'material-editor' && !asset.material)) {
|
|
5035
|
+
disposeMaterialPreview();
|
|
4393
5036
|
assetPreviewModal.style.display = 'none';
|
|
4394
5037
|
assetPreviewTitle.textContent = '';
|
|
4395
5038
|
assetPreviewMeta.textContent = '';
|
|
@@ -4403,6 +5046,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4403
5046
|
return;
|
|
4404
5047
|
}
|
|
4405
5048
|
if (assetPreviewMode === 'rename') {
|
|
5049
|
+
disposeMaterialPreview();
|
|
4406
5050
|
renderAssetRenameModal(asset);
|
|
4407
5051
|
assetPreviewModal.style.display = 'flex';
|
|
4408
5052
|
return;
|
|
@@ -4424,8 +5068,81 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4424
5068
|
].filter(Boolean).join(' · ');
|
|
4425
5069
|
while (assetPreviewBody.firstChild)
|
|
4426
5070
|
assetPreviewBody.removeChild(assetPreviewBody.firstChild);
|
|
4427
|
-
assetPreviewBody.appendChild(
|
|
5071
|
+
assetPreviewBody.appendChild(createMaterialPreviewStage(asset));
|
|
5072
|
+
assetPreviewModal.style.display = 'flex';
|
|
5073
|
+
}
|
|
5074
|
+
function renderProjectMediaPreviewModal() {
|
|
5075
|
+
if (!assetPreviewOpen) {
|
|
5076
|
+
assetPreviewModal.style.display = 'none';
|
|
5077
|
+
while (assetPreviewBody.firstChild)
|
|
5078
|
+
assetPreviewBody.removeChild(assetPreviewBody.firstChild);
|
|
5079
|
+
delete assetPreviewBody.dataset.editorProjectMediaPath;
|
|
5080
|
+
return;
|
|
5081
|
+
}
|
|
5082
|
+
const source = projectMediaPreviewSource;
|
|
5083
|
+
const path = source?.path ?? projectMediaPreviewRequestedPath;
|
|
5084
|
+
assetPreviewDialog.style.width = 'min(760px,calc(100vw - 64px))';
|
|
5085
|
+
const pathSegments = path.split('/');
|
|
5086
|
+
assetPreviewTitle.textContent = source?.name ?? pathSegments[pathSegments.length - 1] ?? '媒体预览';
|
|
5087
|
+
assetPreviewMeta.textContent = projectMediaPreviewError
|
|
5088
|
+
? `无法预览:${projectMediaPreviewError}`
|
|
5089
|
+
: source
|
|
5090
|
+
? [source.kind, source.extension.toUpperCase(), source.path, '只读'].join(' · ')
|
|
5091
|
+
: '正在准备预览...';
|
|
5092
|
+
assetPreviewBody.style.cssText = [
|
|
5093
|
+
'display:flex',
|
|
5094
|
+
'align-items:center',
|
|
5095
|
+
'justify-content:center',
|
|
5096
|
+
'min-height:360px',
|
|
5097
|
+
'max-height:min(620px,calc(100vh - 170px))',
|
|
5098
|
+
'overflow:hidden',
|
|
5099
|
+
'border:1px solid var(--fps-editor-divider)',
|
|
5100
|
+
'border-radius:4px',
|
|
5101
|
+
'background:var(--fps-editor-field)',
|
|
5102
|
+
].join(';');
|
|
4428
5103
|
assetPreviewModal.style.display = 'flex';
|
|
5104
|
+
const previewKey = projectMediaPreviewError ? `error:${path}:${projectMediaPreviewError}` : source?.url ?? `loading:${path}`;
|
|
5105
|
+
if (assetPreviewBody.dataset.editorProjectMediaPath === previewKey && assetPreviewBody.firstChild)
|
|
5106
|
+
return;
|
|
5107
|
+
while (assetPreviewBody.firstChild)
|
|
5108
|
+
assetPreviewBody.removeChild(assetPreviewBody.firstChild);
|
|
5109
|
+
assetPreviewBody.dataset.editorProjectMediaPath = previewKey;
|
|
5110
|
+
if (projectMediaPreviewError) {
|
|
5111
|
+
const error = doc.createElement('div');
|
|
5112
|
+
error.style.cssText = 'padding:24px;color:var(--fps-editor-danger);font-size:12px;text-align:center';
|
|
5113
|
+
error.textContent = projectMediaPreviewError;
|
|
5114
|
+
assetPreviewBody.appendChild(error);
|
|
5115
|
+
return;
|
|
5116
|
+
}
|
|
5117
|
+
if (!source) {
|
|
5118
|
+
const loading = doc.createElement('div');
|
|
5119
|
+
loading.style.cssText = 'color:var(--fps-editor-muted);font-size:12px';
|
|
5120
|
+
loading.textContent = '正在准备预览...';
|
|
5121
|
+
assetPreviewBody.appendChild(loading);
|
|
5122
|
+
return;
|
|
5123
|
+
}
|
|
5124
|
+
if (source.kind === 'image') {
|
|
5125
|
+
const image = doc.createElement('img');
|
|
5126
|
+
image.src = source.url;
|
|
5127
|
+
image.alt = source.name;
|
|
5128
|
+
image.style.cssText = 'display:block;width:100%;height:100%;max-height:min(620px,calc(100vh - 170px));object-fit:contain';
|
|
5129
|
+
assetPreviewBody.appendChild(image);
|
|
5130
|
+
return;
|
|
5131
|
+
}
|
|
5132
|
+
if (source.kind === 'audio') {
|
|
5133
|
+
const audio = doc.createElement('audio');
|
|
5134
|
+
audio.controls = true;
|
|
5135
|
+
audio.preload = 'metadata';
|
|
5136
|
+
audio.src = source.url;
|
|
5137
|
+
audio.style.cssText = 'width:min(560px,calc(100% - 32px))';
|
|
5138
|
+
assetPreviewBody.appendChild(audio);
|
|
5139
|
+
return;
|
|
5140
|
+
}
|
|
5141
|
+
const canvas = doc.createElement('canvas');
|
|
5142
|
+
canvas.dataset.editorProjectModelPreview = 'true';
|
|
5143
|
+
canvas.setAttribute('aria-label', `${source.name} 三维模型预览`);
|
|
5144
|
+
canvas.style.cssText = 'display:block;width:100%;height:min(560px,calc(100vh - 190px));min-height:360px;touch-action:none;outline:none';
|
|
5145
|
+
assetPreviewBody.appendChild(canvas);
|
|
4429
5146
|
}
|
|
4430
5147
|
function renderAssetRenameModal(asset) {
|
|
4431
5148
|
assetPreviewDialog.style.width = 'min(420px,calc(100vw - 64px))';
|
|
@@ -4477,7 +5194,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4477
5194
|
const emission = profile.emission ?? {};
|
|
4478
5195
|
const readonly = material.readonly === true;
|
|
4479
5196
|
const materialKind = material.materialKind === 'standard' ? 'standard' : 'pbr';
|
|
4480
|
-
assetPreviewDialog.style.width = 'min(
|
|
5197
|
+
assetPreviewDialog.style.width = 'min(840px,calc(100vw - 64px))';
|
|
4481
5198
|
assetPreviewTitle.textContent = '编辑材质球';
|
|
4482
5199
|
assetPreviewMeta.textContent = [
|
|
4483
5200
|
material.name || asset.label,
|
|
@@ -4490,7 +5207,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4490
5207
|
assetPreviewBody.removeChild(assetPreviewBody.firstChild);
|
|
4491
5208
|
assetPreviewBody.style.cssText = [
|
|
4492
5209
|
'display:grid',
|
|
4493
|
-
'grid-template-columns:
|
|
5210
|
+
'grid-template-columns:repeat(auto-fit,minmax(min(280px,100%),1fr))',
|
|
4494
5211
|
'gap:14px',
|
|
4495
5212
|
'align-items:start',
|
|
4496
5213
|
'min-height:360px',
|
|
@@ -4502,7 +5219,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4502
5219
|
].join(';');
|
|
4503
5220
|
const previewColumn = doc.createElement('div');
|
|
4504
5221
|
previewColumn.style.cssText = 'display:flex;flex-direction:column;gap:10px;min-width:0';
|
|
4505
|
-
previewColumn.appendChild(
|
|
5222
|
+
previewColumn.appendChild(createMaterialPreviewStage(asset));
|
|
4506
5223
|
const previewMeta = doc.createElement('div');
|
|
4507
5224
|
previewMeta.textContent = readonly
|
|
4508
5225
|
? '系统默认材质不可编辑。'
|
|
@@ -4881,6 +5598,32 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
4881
5598
|
if (target === assetPreviewModal)
|
|
4882
5599
|
closeAssetPreviewModal();
|
|
4883
5600
|
});
|
|
5601
|
+
projectFilePreviewModal.addEventListener('click', (event) => {
|
|
5602
|
+
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
5603
|
+
if (target?.closest('[data-editor-project-file-preview-close]')) {
|
|
5604
|
+
closeProjectFilePreview();
|
|
5605
|
+
return;
|
|
5606
|
+
}
|
|
5607
|
+
if (target?.closest('[data-editor-project-file-search-previous]')) {
|
|
5608
|
+
moveProjectFilePreviewMatch(-1);
|
|
5609
|
+
return;
|
|
5610
|
+
}
|
|
5611
|
+
if (target?.closest('[data-editor-project-file-search-next]')) {
|
|
5612
|
+
moveProjectFilePreviewMatch(1);
|
|
5613
|
+
return;
|
|
5614
|
+
}
|
|
5615
|
+
if (target === projectFilePreviewModal)
|
|
5616
|
+
closeProjectFilePreview();
|
|
5617
|
+
});
|
|
5618
|
+
projectFilePreviewSearch.addEventListener('input', () => {
|
|
5619
|
+
updateProjectFilePreviewSearch(true);
|
|
5620
|
+
});
|
|
5621
|
+
projectFilePreviewSearch.addEventListener('keydown', (event) => {
|
|
5622
|
+
if (event.key !== 'Enter')
|
|
5623
|
+
return;
|
|
5624
|
+
event.preventDefault();
|
|
5625
|
+
moveProjectFilePreviewMatch(event.shiftKey ? -1 : 1);
|
|
5626
|
+
});
|
|
4884
5627
|
assetPreviewBody.addEventListener('change', (event) => {
|
|
4885
5628
|
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
4886
5629
|
if (assetPreviewMode !== 'material-editor')
|
|
@@ -5085,8 +5828,21 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5085
5828
|
: {}),
|
|
5086
5829
|
};
|
|
5087
5830
|
}
|
|
5831
|
+
function navigateAssetBrowserFolder(folderPath) {
|
|
5832
|
+
assetBrowserFolderPath = folderPath;
|
|
5833
|
+
expandAssetBrowserFolderPath(folderPath);
|
|
5834
|
+
selectedProjectEntryPath = null;
|
|
5835
|
+
if (currentState)
|
|
5836
|
+
render(currentState);
|
|
5837
|
+
}
|
|
5838
|
+
function selectProjectBrowserEntry(path, button) {
|
|
5839
|
+
selectedProjectEntryPath = path;
|
|
5840
|
+
for (const candidate of assetPanel.querySelectorAll('[data-editor-project-directory-path],[data-editor-project-file-path]')) {
|
|
5841
|
+
candidate.setAttribute('aria-selected', candidate === button ? 'true' : 'false');
|
|
5842
|
+
}
|
|
5843
|
+
}
|
|
5088
5844
|
assetPanel.addEventListener('click', (event) => {
|
|
5089
|
-
const target = event.target instanceof
|
|
5845
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
5090
5846
|
const tabButton = target?.closest('[data-editor-dock-tab]');
|
|
5091
5847
|
const tab = tabButton?.dataset.editorDockTab;
|
|
5092
5848
|
if (tab === 'assets' || tab === 'history') {
|
|
@@ -5095,6 +5851,61 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5095
5851
|
render(currentState);
|
|
5096
5852
|
return;
|
|
5097
5853
|
}
|
|
5854
|
+
const clearAssetFilterButton = target?.closest('[data-editor-asset-filter-clear]');
|
|
5855
|
+
if (clearAssetFilterButton) {
|
|
5856
|
+
callbacks.onAssetFilterChange?.('');
|
|
5857
|
+
return;
|
|
5858
|
+
}
|
|
5859
|
+
const refreshAssetBrowserButton = target?.closest('[data-editor-asset-browser-refresh]');
|
|
5860
|
+
if (refreshAssetBrowserButton) {
|
|
5861
|
+
resetProjectMediaThumbnailCache();
|
|
5862
|
+
void projectModelThumbnailCache.clearPathEntries()
|
|
5863
|
+
.catch((error) => {
|
|
5864
|
+
console.error('[fps-editor:asset-browser] thumbnail cache refresh cleanup failed', error);
|
|
5865
|
+
})
|
|
5866
|
+
.finally(() => callbacks.onAssetBrowserRefresh?.());
|
|
5867
|
+
return;
|
|
5868
|
+
}
|
|
5869
|
+
const assetFolderToggle = target?.closest('[data-editor-asset-folder-toggle]');
|
|
5870
|
+
const toggledFolderPath = assetFolderToggle?.dataset.editorAssetFolderToggle;
|
|
5871
|
+
if (toggledFolderPath) {
|
|
5872
|
+
if (assetBrowserExpandedFolderPaths.has(toggledFolderPath)) {
|
|
5873
|
+
assetBrowserExpandedFolderPaths.delete(toggledFolderPath);
|
|
5874
|
+
}
|
|
5875
|
+
else {
|
|
5876
|
+
assetBrowserExpandedFolderPaths.add(toggledFolderPath);
|
|
5877
|
+
}
|
|
5878
|
+
if (currentState)
|
|
5879
|
+
render(currentState);
|
|
5880
|
+
return;
|
|
5881
|
+
}
|
|
5882
|
+
const assetFolderButton = target?.closest('[data-editor-asset-folder-path]');
|
|
5883
|
+
if (assetFolderButton?.dataset.editorAssetFolderPath) {
|
|
5884
|
+
const treeFolderPath = assetFolderButton.dataset.editorAssetFolderTreePath;
|
|
5885
|
+
if (treeFolderPath) {
|
|
5886
|
+
assetBrowserFolderPath = treeFolderPath;
|
|
5887
|
+
selectedProjectEntryPath = null;
|
|
5888
|
+
if (assetBrowserExpandedFolderPaths.has(treeFolderPath)) {
|
|
5889
|
+
assetBrowserExpandedFolderPaths.delete(treeFolderPath);
|
|
5890
|
+
}
|
|
5891
|
+
else {
|
|
5892
|
+
assetBrowserExpandedFolderPaths.add(treeFolderPath);
|
|
5893
|
+
}
|
|
5894
|
+
if (currentState)
|
|
5895
|
+
render(currentState);
|
|
5896
|
+
return;
|
|
5897
|
+
}
|
|
5898
|
+
navigateAssetBrowserFolder(assetFolderButton.dataset.editorAssetFolderPath);
|
|
5899
|
+
return;
|
|
5900
|
+
}
|
|
5901
|
+
const assetViewButton = target?.closest('[data-editor-asset-view-mode]');
|
|
5902
|
+
const nextAssetViewMode = assetViewButton?.dataset.editorAssetViewMode;
|
|
5903
|
+
if (nextAssetViewMode === 'grid' || nextAssetViewMode === 'list') {
|
|
5904
|
+
assetBrowserViewMode = nextAssetViewMode;
|
|
5905
|
+
if (currentState)
|
|
5906
|
+
render(currentState);
|
|
5907
|
+
return;
|
|
5908
|
+
}
|
|
5098
5909
|
const assetBrowserTabButton = target?.closest('[data-editor-asset-browser-tab]');
|
|
5099
5910
|
const nextAssetBrowserTab = assetBrowserTabButton?.dataset.editorAssetBrowserTab;
|
|
5100
5911
|
if (isLocalEditorAssetBrowserTab(nextAssetBrowserTab)) {
|
|
@@ -5125,26 +5936,93 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5125
5936
|
}
|
|
5126
5937
|
const assetButton = target?.closest('[data-editor-asset-id]');
|
|
5127
5938
|
if (assetButton?.dataset.editorAssetId) {
|
|
5939
|
+
selectedProjectEntryPath = null;
|
|
5128
5940
|
suppressAssetBrowserRevealIfVisible(assetButton);
|
|
5129
5941
|
callbacks.onSelectAsset?.(assetButton.dataset.editorAssetId);
|
|
5130
5942
|
return;
|
|
5131
5943
|
}
|
|
5944
|
+
const projectDirectoryButton = target?.closest('[data-editor-project-directory-path]');
|
|
5945
|
+
if (projectDirectoryButton?.dataset.editorProjectDirectoryPath) {
|
|
5946
|
+
selectProjectBrowserEntry(projectDirectoryButton.dataset.editorProjectDirectoryPath, projectDirectoryButton);
|
|
5947
|
+
return;
|
|
5948
|
+
}
|
|
5949
|
+
const projectFileButton = target?.closest('[data-editor-project-file-path]');
|
|
5950
|
+
if (projectFileButton?.dataset.editorProjectFilePath) {
|
|
5951
|
+
selectProjectBrowserEntry(projectFileButton.dataset.editorProjectFilePath, projectFileButton);
|
|
5952
|
+
return;
|
|
5953
|
+
}
|
|
5132
5954
|
});
|
|
5133
5955
|
assetPanel.addEventListener('dblclick', (event) => {
|
|
5134
|
-
const target = event.target instanceof
|
|
5956
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
5957
|
+
const projectDirectoryButton = target?.closest('[data-editor-project-directory-path]');
|
|
5958
|
+
const projectDirectoryPath = projectDirectoryButton?.dataset.editorProjectDirectoryPath;
|
|
5959
|
+
if (projectDirectoryPath) {
|
|
5960
|
+
event.preventDefault();
|
|
5961
|
+
event.stopPropagation();
|
|
5962
|
+
navigateAssetBrowserFolder(projectDirectoryPath);
|
|
5963
|
+
return;
|
|
5964
|
+
}
|
|
5135
5965
|
const assetButton = target?.closest('[data-editor-asset-id]');
|
|
5136
5966
|
const browserAssetId = assetButton?.dataset.editorAssetId;
|
|
5137
|
-
|
|
5967
|
+
const projectFileButton = target?.closest('[data-editor-project-file-path]');
|
|
5968
|
+
const browserProjectFilePath = projectFileButton?.dataset.editorProjectFilePath;
|
|
5969
|
+
if (!currentState)
|
|
5138
5970
|
return;
|
|
5139
|
-
const asset =
|
|
5971
|
+
const asset = browserAssetId
|
|
5972
|
+
? currentState.assets.find(candidate => candidate.id === browserAssetId)
|
|
5973
|
+
: undefined;
|
|
5140
5974
|
const actionId = asset ? resolveAssetBrowserDoubleClickAction(currentState, asset) : null;
|
|
5141
|
-
if (
|
|
5975
|
+
if (asset && actionId && browserAssetId) {
|
|
5976
|
+
event.preventDefault();
|
|
5977
|
+
event.stopPropagation();
|
|
5978
|
+
suppressAssetBrowserRevealIfVisible(assetButton);
|
|
5979
|
+
callbacks.onSelectAsset?.(browserAssetId);
|
|
5980
|
+
callbacks.onAssetAction?.(createAssetBrowserActionInput(actionId, asset, createCurrentAssetBrowserActionContext()));
|
|
5981
|
+
return;
|
|
5982
|
+
}
|
|
5983
|
+
if (browserProjectFilePath && openProjectBrowserFile(browserProjectFilePath, projectFileButton)) {
|
|
5984
|
+
event.preventDefault();
|
|
5985
|
+
event.stopPropagation();
|
|
5986
|
+
}
|
|
5987
|
+
});
|
|
5988
|
+
assetPanel.addEventListener('keydown', (event) => {
|
|
5989
|
+
if (event.key !== 'Enter')
|
|
5990
|
+
return;
|
|
5991
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
5992
|
+
const projectDirectoryButton = target?.closest('[data-editor-project-directory-path]');
|
|
5993
|
+
const projectDirectoryPath = projectDirectoryButton?.dataset.editorProjectDirectoryPath;
|
|
5994
|
+
if (projectDirectoryPath) {
|
|
5995
|
+
event.preventDefault();
|
|
5996
|
+
navigateAssetBrowserFolder(projectDirectoryPath);
|
|
5997
|
+
return;
|
|
5998
|
+
}
|
|
5999
|
+
const assetButton = target?.closest('[data-editor-asset-id]');
|
|
6000
|
+
const browserAssetId = assetButton?.dataset.editorAssetId;
|
|
6001
|
+
const asset = browserAssetId
|
|
6002
|
+
? currentState?.assets.find(candidate => candidate.id === browserAssetId)
|
|
6003
|
+
: undefined;
|
|
6004
|
+
const assetActionId = asset && currentState
|
|
6005
|
+
? resolveAssetBrowserDoubleClickAction(currentState, asset)
|
|
6006
|
+
: null;
|
|
6007
|
+
if (asset && browserAssetId && assetActionId) {
|
|
6008
|
+
event.preventDefault();
|
|
6009
|
+
suppressAssetBrowserRevealIfVisible(assetButton);
|
|
6010
|
+
callbacks.onSelectAsset?.(browserAssetId);
|
|
6011
|
+
callbacks.onAssetAction?.(createAssetBrowserActionInput(assetActionId, asset, createCurrentAssetBrowserActionContext()));
|
|
6012
|
+
return;
|
|
6013
|
+
}
|
|
6014
|
+
const projectFileButton = target?.closest('[data-editor-project-file-path]');
|
|
6015
|
+
const projectFilePath = projectFileButton?.dataset.editorProjectFilePath;
|
|
6016
|
+
if (projectFilePath && openProjectBrowserFile(projectFilePath, projectFileButton)) {
|
|
6017
|
+
event.preventDefault();
|
|
6018
|
+
return;
|
|
6019
|
+
}
|
|
6020
|
+
const treeFolderButton = target?.closest('[data-editor-asset-folder-tree-path]');
|
|
6021
|
+
const treeFolderPath = treeFolderButton?.dataset.editorAssetFolderTreePath;
|
|
6022
|
+
if (!treeFolderPath)
|
|
5142
6023
|
return;
|
|
5143
6024
|
event.preventDefault();
|
|
5144
|
-
|
|
5145
|
-
suppressAssetBrowserRevealIfVisible(assetButton);
|
|
5146
|
-
callbacks.onSelectAsset?.(browserAssetId);
|
|
5147
|
-
callbacks.onAssetAction?.(createAssetBrowserActionInput(actionId, asset, createCurrentAssetBrowserActionContext()));
|
|
6025
|
+
treeFolderButton.click();
|
|
5148
6026
|
});
|
|
5149
6027
|
assetPanel.addEventListener('contextmenu', (event) => {
|
|
5150
6028
|
if (!inputRouter.shouldPreventBrowserContextMenu(event))
|
|
@@ -5152,31 +6030,119 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5152
6030
|
const target = inputRouter.getEventTarget(event);
|
|
5153
6031
|
if (!target)
|
|
5154
6032
|
return;
|
|
5155
|
-
const
|
|
6033
|
+
const projectFileButton = target.closest('[data-editor-project-file-path]');
|
|
6034
|
+
const browserProjectFilePath = projectFileButton?.dataset.editorProjectFilePath;
|
|
6035
|
+
const projectDirectoryButton = target.closest('[data-editor-project-directory-path],[data-editor-asset-folder-tree-path]');
|
|
6036
|
+
const browserProjectDirectoryPath = projectDirectoryButton?.dataset.editorProjectDirectoryPath
|
|
6037
|
+
?? projectDirectoryButton?.dataset.editorAssetFolderTreePath;
|
|
6038
|
+
const assetButton = target.closest('[data-editor-asset-id]');
|
|
5156
6039
|
const browserAssetId = assetButton?.dataset.editorAssetId;
|
|
5157
|
-
if (!browserAssetId || !currentState)
|
|
6040
|
+
if ((!browserProjectFilePath && browserProjectDirectoryPath == null && !browserAssetId) || !currentState)
|
|
5158
6041
|
return;
|
|
5159
|
-
const asset =
|
|
5160
|
-
|
|
6042
|
+
const asset = browserAssetId
|
|
6043
|
+
? currentState.assets.find(candidate => candidate.id === browserAssetId) ?? null
|
|
6044
|
+
: null;
|
|
6045
|
+
if (browserAssetId && !asset)
|
|
5161
6046
|
return;
|
|
5162
6047
|
inputRouter.claimContextMenu(event);
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
6048
|
+
if (asset && assetButton) {
|
|
6049
|
+
suppressAssetBrowserRevealIfVisible(assetButton);
|
|
6050
|
+
callbacks.onSelectAsset?.(asset.id);
|
|
6051
|
+
}
|
|
6052
|
+
else if (browserProjectFilePath && projectFileButton) {
|
|
6053
|
+
selectProjectBrowserEntry(browserProjectFilePath, projectFileButton);
|
|
6054
|
+
}
|
|
6055
|
+
else if (browserProjectDirectoryPath != null && projectDirectoryButton) {
|
|
6056
|
+
selectProjectBrowserEntry(browserProjectDirectoryPath, projectDirectoryButton);
|
|
6057
|
+
}
|
|
6058
|
+
const relativeProjectFilePath = browserProjectFilePath
|
|
6059
|
+
? toLocalEditorProjectRelativePath(browserProjectFilePath)
|
|
6060
|
+
: null;
|
|
6061
|
+
const relativeProjectDirectoryPath = browserProjectDirectoryPath != null
|
|
6062
|
+
? toLocalEditorProjectRelativePath(browserProjectDirectoryPath) || '.'
|
|
6063
|
+
: null;
|
|
6064
|
+
const snapshot = createLocalEditorAssetFilesystemSnapshot(currentState.assets, currentState.projectFiles);
|
|
6065
|
+
const fileEntry = browserProjectFilePath
|
|
6066
|
+
? snapshot.entries.find(entry => entry.path === browserProjectFilePath) ?? null
|
|
6067
|
+
: null;
|
|
6068
|
+
const directoryEntry = browserProjectDirectoryPath != null
|
|
6069
|
+
? snapshot.entries.find(entry => entry.path === browserProjectDirectoryPath) ?? null
|
|
6070
|
+
: null;
|
|
6071
|
+
const directoryFolder = browserProjectDirectoryPath != null
|
|
6072
|
+
? snapshot.folders.find(folder => folder.path === browserProjectDirectoryPath) ?? null
|
|
6073
|
+
: null;
|
|
6074
|
+
const projectItems = relativeProjectFilePath
|
|
6075
|
+
? [
|
|
6076
|
+
...(callbacks.onProjectFileRead && isEditorProjectFilePreviewable(relativeProjectFilePath)
|
|
6077
|
+
? [{ id: 'project-file.preview', label: '查看文件' }]
|
|
6078
|
+
: []),
|
|
6079
|
+
...(callbacks.onProjectMediaResolve && isEditorProjectMediaPreviewable(relativeProjectFilePath)
|
|
6080
|
+
? [{ id: 'project-file.preview-media', label: '预览媒体' }]
|
|
6081
|
+
: []),
|
|
6082
|
+
...(callbacks.onProjectEntrySendToAgent
|
|
6083
|
+
? [{ id: 'project-entry.send-to-agent', label: '发送给 Agent' }]
|
|
6084
|
+
: []),
|
|
6085
|
+
{ id: 'project-file.copy-path', label: '复制路径' },
|
|
6086
|
+
]
|
|
6087
|
+
: relativeProjectDirectoryPath
|
|
6088
|
+
? [
|
|
6089
|
+
...(callbacks.onProjectEntrySendToAgent
|
|
6090
|
+
? [{ id: 'project-entry.send-to-agent', label: '发送给 Agent' }]
|
|
6091
|
+
: []),
|
|
6092
|
+
{ id: 'project-file.copy-path', label: '复制路径' },
|
|
6093
|
+
]
|
|
6094
|
+
: [];
|
|
6095
|
+
const assetItems = asset ? createAssetBrowserMenuItems(currentState, asset) : [];
|
|
6096
|
+
if (projectItems.length > 0 && assetItems.length > 0)
|
|
6097
|
+
assetItems[0] = { ...assetItems[0], separatorBefore: true };
|
|
6098
|
+
const openContextMenu = () => contextMenu.open({
|
|
5166
6099
|
x: event.clientX,
|
|
5167
6100
|
y: event.clientY,
|
|
5168
|
-
items:
|
|
6101
|
+
items: [...projectItems, ...assetItems],
|
|
5169
6102
|
onAction(item) {
|
|
6103
|
+
if (item.id === 'project-file.preview' && browserProjectFilePath) {
|
|
6104
|
+
void openProjectFilePreview(browserProjectFilePath, projectFileButton);
|
|
6105
|
+
return;
|
|
6106
|
+
}
|
|
6107
|
+
if (item.id === 'project-file.preview-media' && browserProjectFilePath) {
|
|
6108
|
+
void openProjectMediaPreview(browserProjectFilePath, projectFileButton);
|
|
6109
|
+
return;
|
|
6110
|
+
}
|
|
6111
|
+
if (item.id === 'project-entry.send-to-agent' && callbacks.onProjectEntrySendToAgent) {
|
|
6112
|
+
const path = relativeProjectFilePath ?? relativeProjectDirectoryPath;
|
|
6113
|
+
if (!path)
|
|
6114
|
+
return;
|
|
6115
|
+
const entryKind = relativeProjectFilePath ? 'file' : 'directory';
|
|
6116
|
+
const name = entryKind === 'file'
|
|
6117
|
+
? fileEntry?.label ?? path.split('/').slice(-1)[0] ?? path
|
|
6118
|
+
: directoryEntry?.label ?? directoryFolder?.label ?? currentState?.projectFiles?.rootName ?? path;
|
|
6119
|
+
void Promise.resolve(callbacks.onProjectEntrySendToAgent({
|
|
6120
|
+
path,
|
|
6121
|
+
name,
|
|
6122
|
+
entryKind,
|
|
6123
|
+
...(entryKind === 'file' && fileEntry?.extension ? { extension: fileEntry.extension } : {}),
|
|
6124
|
+
...(asset ? { assetId: asset.assetId ?? asset.id, assetKind: asset.kind } : {}),
|
|
6125
|
+
})).catch(() => undefined);
|
|
6126
|
+
return;
|
|
6127
|
+
}
|
|
6128
|
+
if (item.id === 'project-file.copy-path') {
|
|
6129
|
+
const path = relativeProjectFilePath ?? relativeProjectDirectoryPath;
|
|
6130
|
+
if (path)
|
|
6131
|
+
copyTextToClipboard(doc.defaultView, path);
|
|
6132
|
+
return;
|
|
6133
|
+
}
|
|
6134
|
+
if (!asset)
|
|
6135
|
+
return;
|
|
5170
6136
|
if (item.id === 'asset.preview') {
|
|
5171
|
-
openAssetPreviewModal(asset);
|
|
6137
|
+
openAssetPreviewModal(asset, assetButton);
|
|
5172
6138
|
return;
|
|
5173
6139
|
}
|
|
5174
6140
|
if (item.id === 'asset.edit-material') {
|
|
5175
|
-
openAssetMaterialEditorModal(asset);
|
|
6141
|
+
openAssetMaterialEditorModal(asset, assetButton);
|
|
5176
6142
|
return;
|
|
5177
6143
|
}
|
|
5178
6144
|
if (item.id === 'asset.rename') {
|
|
5179
|
-
openAssetRenameModal(asset);
|
|
6145
|
+
openAssetRenameModal(asset, assetButton);
|
|
5180
6146
|
return;
|
|
5181
6147
|
}
|
|
5182
6148
|
if (item.id === 'asset.edit-prefab' || item.id === 'asset.add-to-current-prefab') {
|
|
@@ -5190,6 +6156,15 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5190
6156
|
callbacks.onAssetAction?.(createAssetBrowserActionInput(item.id, asset, createCurrentAssetBrowserActionContext()));
|
|
5191
6157
|
},
|
|
5192
6158
|
});
|
|
6159
|
+
// Selecting a catalog asset rerenders the asset browser synchronously. Open
|
|
6160
|
+
// its menu on the next frame so the first right-click is not cleared by that
|
|
6161
|
+
// selection refresh.
|
|
6162
|
+
if (asset && assetButton && doc.defaultView?.requestAnimationFrame) {
|
|
6163
|
+
doc.defaultView.requestAnimationFrame(openContextMenu);
|
|
6164
|
+
}
|
|
6165
|
+
else {
|
|
6166
|
+
openContextMenu();
|
|
6167
|
+
}
|
|
5193
6168
|
});
|
|
5194
6169
|
workbench.rightDock.addEventListener('click', (event) => {
|
|
5195
6170
|
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
@@ -5236,6 +6211,8 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5236
6211
|
setShortcutHelpOpen(false);
|
|
5237
6212
|
if (assetPreviewOpen)
|
|
5238
6213
|
closeAssetPreviewModal();
|
|
6214
|
+
if (projectFilePreviewOpen)
|
|
6215
|
+
closeProjectFilePreview();
|
|
5239
6216
|
},
|
|
5240
6217
|
requestOperationInput: requestHierarchyOperationInput,
|
|
5241
6218
|
});
|
|
@@ -5246,6 +6223,15 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5246
6223
|
return;
|
|
5247
6224
|
callbacks.onAssetFilterChange?.(input.value);
|
|
5248
6225
|
});
|
|
6226
|
+
assetPanel.addEventListener('change', (event) => {
|
|
6227
|
+
const select = event.target instanceof HTMLSelectElement ? event.target : null;
|
|
6228
|
+
const nextAssetBrowserTab = select?.dataset.editorAssetBrowserTypeFilter ? select.value : null;
|
|
6229
|
+
if (!isLocalEditorAssetBrowserTab(nextAssetBrowserTab))
|
|
6230
|
+
return;
|
|
6231
|
+
assetBrowserTab = nextAssetBrowserTab;
|
|
6232
|
+
if (currentState)
|
|
6233
|
+
render(currentState);
|
|
6234
|
+
});
|
|
5249
6235
|
inspectorPanel.addEventListener('input', (event) => {
|
|
5250
6236
|
const input = event.target instanceof HTMLInputElement ? event.target : null;
|
|
5251
6237
|
if (input?.dataset.editorInspectorSearch != null) {
|
|
@@ -5254,6 +6240,8 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5254
6240
|
render(currentState);
|
|
5255
6241
|
return;
|
|
5256
6242
|
}
|
|
6243
|
+
if (currentState?.inspectorPending === true)
|
|
6244
|
+
return;
|
|
5257
6245
|
if (input?.type === 'checkbox' || input?.type === 'color')
|
|
5258
6246
|
return;
|
|
5259
6247
|
if (!input?.dataset.serializedPath || !input.dataset.serializedTargetId)
|
|
@@ -5268,6 +6256,11 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5268
6256
|
callbacks.onPropertyInput?.(propertyInput);
|
|
5269
6257
|
});
|
|
5270
6258
|
inspectorPanel.addEventListener('paste', (event) => {
|
|
6259
|
+
if (currentState?.inspectorPending === true) {
|
|
6260
|
+
event.preventDefault();
|
|
6261
|
+
event.stopPropagation();
|
|
6262
|
+
return;
|
|
6263
|
+
}
|
|
5271
6264
|
const input = event.target instanceof HTMLInputElement ? event.target : null;
|
|
5272
6265
|
const text = event.clipboardData?.getData('text/plain') ?? event.clipboardData?.getData('text') ?? '';
|
|
5273
6266
|
if (!input || !text)
|
|
@@ -5285,6 +6278,11 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5285
6278
|
callbacks.onPropertyInput?.(propertyInput);
|
|
5286
6279
|
});
|
|
5287
6280
|
inspectorPanel.addEventListener('click', (event) => {
|
|
6281
|
+
if (currentState?.inspectorPending === true) {
|
|
6282
|
+
event.preventDefault();
|
|
6283
|
+
event.stopPropagation();
|
|
6284
|
+
return;
|
|
6285
|
+
}
|
|
5288
6286
|
const target = event.target instanceof HTMLElement ? event.target : null;
|
|
5289
6287
|
const renderingActionButton = target?.closest('[data-editor-rendering-action]');
|
|
5290
6288
|
if (renderingActionButton) {
|
|
@@ -5397,6 +6395,8 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5397
6395
|
openInspectorAssetPicker(input, property);
|
|
5398
6396
|
});
|
|
5399
6397
|
inspectorPanel.addEventListener('change', (event) => {
|
|
6398
|
+
if (currentState?.inspectorPending === true)
|
|
6399
|
+
return;
|
|
5400
6400
|
const input = event.target instanceof HTMLInputElement || event.target instanceof HTMLSelectElement
|
|
5401
6401
|
? event.target
|
|
5402
6402
|
: null;
|
|
@@ -5418,6 +6418,11 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5418
6418
|
callbacks.onPropertyInput?.(propertyInput);
|
|
5419
6419
|
});
|
|
5420
6420
|
inspectorPanel.addEventListener('keydown', (event) => {
|
|
6421
|
+
if (currentState?.inspectorPending === true) {
|
|
6422
|
+
event.preventDefault();
|
|
6423
|
+
event.stopPropagation();
|
|
6424
|
+
return;
|
|
6425
|
+
}
|
|
5421
6426
|
const input = event.target instanceof HTMLInputElement ? event.target : null;
|
|
5422
6427
|
if (!input || !isLocalEditorKeyboardCommitEvent(event, input))
|
|
5423
6428
|
return;
|
|
@@ -5427,6 +6432,8 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5427
6432
|
event.stopPropagation();
|
|
5428
6433
|
});
|
|
5429
6434
|
inspectorPanel.addEventListener('focusout', (event) => {
|
|
6435
|
+
if (currentState?.inspectorPending === true)
|
|
6436
|
+
return;
|
|
5430
6437
|
const input = event.target instanceof HTMLInputElement ? event.target : null;
|
|
5431
6438
|
if (!input?.dataset.serializedPath || !input.dataset.serializedTargetId)
|
|
5432
6439
|
return;
|
|
@@ -5568,6 +6575,11 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5568
6575
|
closeAssetPreviewModal();
|
|
5569
6576
|
return;
|
|
5570
6577
|
}
|
|
6578
|
+
if (key === 'escape' && projectFilePreviewOpen) {
|
|
6579
|
+
event.preventDefault();
|
|
6580
|
+
closeProjectFilePreview();
|
|
6581
|
+
return;
|
|
6582
|
+
}
|
|
5571
6583
|
if (key === 'escape' && assetPickerOpen) {
|
|
5572
6584
|
event.preventDefault();
|
|
5573
6585
|
closeInspectorAssetPicker();
|
|
@@ -5621,6 +6633,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5621
6633
|
&& primaryModifier
|
|
5622
6634
|
&& key === 's') {
|
|
5623
6635
|
event.preventDefault();
|
|
6636
|
+
editorConsole.append({ severity: 'info', source: 'save', message: '请求保存场景' });
|
|
5624
6637
|
callbacks.onSaveScene?.();
|
|
5625
6638
|
return;
|
|
5626
6639
|
}
|
|
@@ -5646,6 +6659,17 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5646
6659
|
&& candidate.shortcut?.toLowerCase() === key));
|
|
5647
6660
|
if (tool) {
|
|
5648
6661
|
event.preventDefault();
|
|
6662
|
+
if (/(?:^|[?&])debugGizmo=(?:1|true)(?:&|$)/.test(doc.defaultView?.location?.search ?? '')) {
|
|
6663
|
+
console.info('[fps-editor:gizmo]', 'shortcut.dispatched', {
|
|
6664
|
+
timestamp: typeof performance !== 'undefined' ? Number(performance.now().toFixed(2)) : Date.now(),
|
|
6665
|
+
key,
|
|
6666
|
+
toolId: tool.id,
|
|
6667
|
+
availability: tool.availability.state,
|
|
6668
|
+
active: tool.active,
|
|
6669
|
+
eventTarget: event.target instanceof Element ? event.target.tagName : null,
|
|
6670
|
+
defaultPrevented: event.defaultPrevented,
|
|
6671
|
+
});
|
|
6672
|
+
}
|
|
5649
6673
|
if (tool.availability.state === 'enabled')
|
|
5650
6674
|
callbacks.onToolActivate?.(tool.id);
|
|
5651
6675
|
return;
|
|
@@ -5693,27 +6717,31 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5693
6717
|
&& getPreviewToolSupport(tool).support === 'enabled';
|
|
5694
6718
|
}
|
|
5695
6719
|
function applyPrefabStageWorkbenchLayoutOverride(layout = workbenchLayoutController.getLayout()) {
|
|
5696
|
-
const { leftWidth, rightWidth,
|
|
6720
|
+
const { leftWidth, rightWidth, consoleEdge, sceneBottomEdge, bottomVisible } = resolveLocalEditorPrefabStageWorkbenchLayout(layout, readLocalEditorWorkbenchViewport(doc));
|
|
5697
6721
|
workbench.leftDock.style.display = 'flex';
|
|
5698
6722
|
workbench.leftDock.style.width = `${leftWidth}px`;
|
|
5699
|
-
workbench.leftDock.style.bottom = `${
|
|
6723
|
+
workbench.leftDock.style.bottom = `${consoleEdge}px`;
|
|
5700
6724
|
workbench.leftDock.style.zIndex = '';
|
|
5701
6725
|
workbench.leftDock.style.boxShadow = '';
|
|
5702
6726
|
workbench.rightDock.style.display = 'flex';
|
|
5703
6727
|
workbench.rightDock.style.width = `${rightWidth}px`;
|
|
6728
|
+
workbench.rightDock.style.bottom = `${consoleEdge}px`;
|
|
5704
6729
|
workbench.rightDock.style.zIndex = '';
|
|
5705
6730
|
workbench.rightDock.style.boxShadow = '';
|
|
5706
6731
|
workbench.sceneFrame.style.left = `${leftWidth}px`;
|
|
5707
6732
|
workbench.sceneFrame.style.right = `${rightWidth}px`;
|
|
5708
|
-
workbench.sceneFrame.style.bottom = `${
|
|
6733
|
+
workbench.sceneFrame.style.bottom = `${sceneBottomEdge}px`;
|
|
5709
6734
|
if (bottomVisible) {
|
|
6735
|
+
workbench.bottomDock.style.left = `${leftWidth}px`;
|
|
5710
6736
|
workbench.bottomDock.style.right = `${rightWidth}px`;
|
|
6737
|
+
workbench.bottomDock.style.bottom = `${consoleEdge}px`;
|
|
6738
|
+
workbench.bottomResizer.style.left = `${leftWidth}px`;
|
|
5711
6739
|
workbench.bottomResizer.style.right = `${rightWidth}px`;
|
|
5712
6740
|
}
|
|
5713
6741
|
workbench.leftToggle.style.display = 'none';
|
|
5714
6742
|
workbench.rightToggle.style.display = 'none';
|
|
5715
|
-
workbench.leftResizer.style.bottom = `${
|
|
5716
|
-
workbench.rightResizer.style.bottom =
|
|
6743
|
+
workbench.leftResizer.style.bottom = `${consoleEdge}px`;
|
|
6744
|
+
workbench.rightResizer.style.bottom = `${consoleEdge}px`;
|
|
5717
6745
|
}
|
|
5718
6746
|
function readLocalEditorWorkbenchViewport(docRef) {
|
|
5719
6747
|
const win = docRef.defaultView;
|
|
@@ -5734,6 +6762,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5734
6762
|
emptyText: 'Select an item in Prefab Explorer.',
|
|
5735
6763
|
lockedVectorGroups: lockedInspectorVectorGroups,
|
|
5736
6764
|
});
|
|
6765
|
+
lastRenderedInspectorContentKey = resolveLocalEditorRightDockContentKey(currentState, 'inspector', inspectorFilter);
|
|
5737
6766
|
return;
|
|
5738
6767
|
}
|
|
5739
6768
|
LocalEditorPanels.disposeInspectorPanelUi(inspectorPanel);
|
|
@@ -5750,6 +6779,9 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5750
6779
|
appendPrefabStageReadonlyRow(grid, 'Mode', stage.readonly ? '只读' : '可编辑');
|
|
5751
6780
|
appendPrefabStageSelectedItemDetails(grid, selectedItem);
|
|
5752
6781
|
inspectorPanel.appendChild(grid);
|
|
6782
|
+
lastRenderedInspectorContentKey = currentState
|
|
6783
|
+
? resolveLocalEditorRightDockContentKey(currentState, 'inspector', inspectorFilter)
|
|
6784
|
+
: null;
|
|
5753
6785
|
}
|
|
5754
6786
|
function appendPrefabStageSelectedItemDetails(container, item) {
|
|
5755
6787
|
if (!item)
|
|
@@ -5914,8 +6946,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5914
6946
|
? 'var(--fps-editor-border)'
|
|
5915
6947
|
: agentBridgeStatusColor;
|
|
5916
6948
|
setToolbarButtonTooltip(agentBridgeStatusButton, createAgentBridgeStatusTooltip(state.agentBridge));
|
|
5917
|
-
renderCoordinateAxesOverlay(coordinateAxesOverlay, inEditor ? state.coordinateAxes ?? null : null);
|
|
5918
|
-
renderSceneFrameRateOverlay(sceneFrameRateOverlay, inEditor && !inPrefabStage ? state.sceneFrameStats ?? null : null);
|
|
6949
|
+
renderCoordinateAxesOverlay(coordinateAxesOverlay, inEditor ? state.coordinateAxes ?? null : null, state.viewportTools?.viewPreset ?? 'perspective', state.busy);
|
|
5919
6950
|
renderPerformanceMonitorOverlay(performanceMonitorOverlay, inEditor && !inPrefabStage ? state.performanceStats ?? null : null, inEditor && !inPrefabStage && performanceMonitorOpen);
|
|
5920
6951
|
prefabStagePreviewLayout.root.style.display = inEditor && inPrefabStage ? 'block' : 'none';
|
|
5921
6952
|
renderPrefabImportStatsOverlay(prefabImportStatsOverlay, inEditor && inPrefabStage ? state.prefabStage ?? null : null);
|
|
@@ -5938,12 +6969,48 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5938
6969
|
const updatePreview = (preview) => {
|
|
5939
6970
|
if (!currentState)
|
|
5940
6971
|
return;
|
|
6972
|
+
const toolPresentationStartedAt = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
6973
|
+
const shouldRenderToolPresentation = 'tools' in preview || 'activeToolId' in preview;
|
|
6974
|
+
const previousActiveToolId = currentState.activeToolId ?? null;
|
|
5941
6975
|
const shouldRenderInspectorPreview = 'inspectorObject' in preview || 'inspectorMultiObject' in preview;
|
|
5942
|
-
|
|
6976
|
+
const shouldRenderSelectionPresentation = 'selectedIds' in preview
|
|
6977
|
+
|| 'activeId' in preview
|
|
6978
|
+
|| 'selectionSummary' in preview;
|
|
6979
|
+
const nextState = {
|
|
5943
6980
|
...currentState,
|
|
5944
6981
|
...preview,
|
|
5945
6982
|
};
|
|
6983
|
+
const nextInspectorContentKey = resolveLocalEditorRightDockContentKey(nextState, 'inspector', inspectorFilter);
|
|
6984
|
+
const inspectorPreviewScrollSnapshot = shouldRenderInspectorPreview
|
|
6985
|
+
&& lastRenderedInspectorContentKey === nextInspectorContentKey
|
|
6986
|
+
? captureLocalEditorRightDockScrollSnapshot(currentState, 'inspector', inspectorFilter, inspectorPanel, renderingPanel, markersPanel, pluginsPanel)
|
|
6987
|
+
: null;
|
|
6988
|
+
currentState = nextState;
|
|
6989
|
+
if ('inspectorPending' in preview) {
|
|
6990
|
+
syncLocalEditorInspectorPendingState(inspectorPanel, currentState.inspectorPending === true);
|
|
6991
|
+
}
|
|
6992
|
+
if (shouldRenderToolPresentation) {
|
|
6993
|
+
renderToolPresentationState(currentState);
|
|
6994
|
+
scheduleToolbarOverflowLayout();
|
|
6995
|
+
if (/(?:^|[?&])debugGizmo=(?:1|true)(?:&|$)/.test(doc.defaultView?.location?.search ?? '')) {
|
|
6996
|
+
console.info('[fps-editor:gizmo]', 'ui.tool-presentation.updated', {
|
|
6997
|
+
timestamp: typeof performance !== 'undefined' ? Number(performance.now().toFixed(2)) : Date.now(),
|
|
6998
|
+
durationMs: Number(((typeof performance !== 'undefined' ? performance.now() : Date.now()) - toolPresentationStartedAt).toFixed(2)),
|
|
6999
|
+
previousActiveToolId,
|
|
7000
|
+
activeToolId: currentState.activeToolId ?? null,
|
|
7001
|
+
toolCount: currentState.tools?.length ?? 0,
|
|
7002
|
+
});
|
|
7003
|
+
}
|
|
7004
|
+
}
|
|
7005
|
+
if (shouldRenderSelectionPresentation)
|
|
7006
|
+
hierarchyController.render(currentState);
|
|
5946
7007
|
renderPreviewState(currentState);
|
|
7008
|
+
if (shouldRenderInspectorPreview
|
|
7009
|
+
&& currentState.mode === 'editor'
|
|
7010
|
+
&& !!currentState.prefabStage
|
|
7011
|
+
&& rightDockFormalSurfaceVisibility.isVisible()) {
|
|
7012
|
+
renderPrefabStagePropertiesPanel(currentState.prefabStage);
|
|
7013
|
+
}
|
|
5947
7014
|
if (shouldRenderLocalEditorInspectorPreview({
|
|
5948
7015
|
hasInspectorPreview: shouldRenderInspectorPreview,
|
|
5949
7016
|
mode: currentState.mode,
|
|
@@ -5951,20 +7018,33 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5951
7018
|
rightDockVisible: rightDockFormalSurfaceVisibility.isVisible(),
|
|
5952
7019
|
activeRightTab: panelRegistry.getRightDockTab(),
|
|
5953
7020
|
})) {
|
|
5954
|
-
const rightDockScrollSnapshot = captureLocalEditorRightDockScrollSnapshot(currentState, 'inspector', inspectorFilter, inspectorPanel, renderingPanel, markersPanel, pluginsPanel);
|
|
5955
7021
|
LocalEditorPanels.renderInspectorPanel(doc, inspectorPanel, currentState, inspectorFilter, {
|
|
5956
7022
|
...options.inspector,
|
|
5957
7023
|
layerHost: editorUiLayerHost,
|
|
5958
7024
|
lockedVectorGroups: lockedInspectorVectorGroups,
|
|
5959
7025
|
});
|
|
5960
|
-
|
|
7026
|
+
lastRenderedInspectorContentKey = nextInspectorContentKey;
|
|
7027
|
+
restoreLocalEditorRightDockScrollSnapshot(inspectorPreviewScrollSnapshot, currentState, 'inspector', inspectorFilter, inspectorPanel, renderingPanel, markersPanel, pluginsPanel);
|
|
7028
|
+
}
|
|
7029
|
+
if ((shouldRenderSelectionPresentation || shouldRenderInspectorPreview)
|
|
7030
|
+
&& /(?:^|[?&])debug(?:Gizmo|Selection)=(?:1|true)(?:&|$)/.test(doc.defaultView?.location?.search ?? '')) {
|
|
7031
|
+
console.info('[fps-editor:selection]', 'ui.selection-preview.updated', {
|
|
7032
|
+
timestamp: typeof performance !== 'undefined' ? Number(performance.now().toFixed(2)) : Date.now(),
|
|
7033
|
+
durationMs: Number(((typeof performance !== 'undefined' ? performance.now() : Date.now()) - toolPresentationStartedAt).toFixed(2)),
|
|
7034
|
+
phase: shouldRenderSelectionPresentation ? 'presentation' : 'inspector',
|
|
7035
|
+
activeId: currentState.activeId,
|
|
7036
|
+
selectedCount: currentState.selectedIds.length,
|
|
7037
|
+
});
|
|
5961
7038
|
}
|
|
5962
7039
|
};
|
|
5963
7040
|
const render = (state) => {
|
|
5964
7041
|
const previousState = currentState;
|
|
7042
|
+
const assetBrowserContinuitySnapshot = assetBrowserContinuity.capture(lastRenderedAssetBrowserContentKey);
|
|
5965
7043
|
const previousRightTab = panelRegistry.getRightDockTab();
|
|
5966
7044
|
const rightDockScrollSnapshot = captureLocalEditorRightDockScrollSnapshot(previousState, previousRightTab, inspectorFilter, inspectorPanel, renderingPanel, markersPanel, pluginsPanel);
|
|
5967
7045
|
currentState = state;
|
|
7046
|
+
syncLocalEditorInspectorPendingState(inspectorPanel, state.inspectorPending === true);
|
|
7047
|
+
editorConsole.syncState(state, previousState);
|
|
5968
7048
|
const focusSnapshot = captureLocalEditorEditableFocus(doc);
|
|
5969
7049
|
const inEditor = state.mode === 'editor';
|
|
5970
7050
|
const disabled = state.busy;
|
|
@@ -5993,14 +7073,29 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
5993
7073
|
setPerformanceMonitorOpen(false);
|
|
5994
7074
|
}
|
|
5995
7075
|
if (!inEditor || disabled) {
|
|
7076
|
+
projectMediaPreviewRequestId += 1;
|
|
7077
|
+
disposeProjectModelPreview();
|
|
5996
7078
|
assetPreviewOpen = false;
|
|
7079
|
+
assetPreviewReturnFocus = null;
|
|
5997
7080
|
previewedAsset = null;
|
|
5998
7081
|
assetPreviewMode = 'preview';
|
|
7082
|
+
projectMediaPreviewSource = null;
|
|
7083
|
+
projectMediaPreviewRequestedPath = '';
|
|
7084
|
+
projectMediaPreviewError = '';
|
|
7085
|
+
projectFilePreviewOpen = false;
|
|
7086
|
+
projectFilePreviewReturnFocus = null;
|
|
7087
|
+
projectFilePreviewRequestId += 1;
|
|
7088
|
+
projectFilePreviewContent = null;
|
|
7089
|
+
projectFilePreviewRequestedPath = '';
|
|
7090
|
+
projectFilePreviewError = '';
|
|
7091
|
+
projectFilePreviewMatches = [];
|
|
7092
|
+
projectFilePreviewActiveMatch = -1;
|
|
5999
7093
|
assetPickerOpen = false;
|
|
6000
7094
|
assetPickerInput = null;
|
|
6001
7095
|
assetPickerProperty = null;
|
|
6002
7096
|
settleHierarchyOperationInputRequest(null);
|
|
6003
7097
|
}
|
|
7098
|
+
renderProjectFilePreview();
|
|
6004
7099
|
syncInputModalOpen();
|
|
6005
7100
|
const transformTool = state.transformTool ?? null;
|
|
6006
7101
|
workbench.root.style.display = inEditor ? '' : 'none';
|
|
@@ -6066,21 +7161,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6066
7161
|
const viewportControlsTooltip = sceneCameraPreview.enabled
|
|
6067
7162
|
? '当前为 Main Camera 预览,请关闭后再使用视口工具'
|
|
6068
7163
|
: '';
|
|
6069
|
-
const viewportProjectionTooltip = sceneCameraPreview.enabled
|
|
6070
|
-
? '当前为 Main Camera 预览,投影由 Main Camera 决定;关闭预览后可切换编辑视口投影'
|
|
6071
|
-
: '';
|
|
6072
7164
|
viewportToolsGroup.style.display = isPreviewToolVisible('viewport-camera') ? 'flex' : 'none';
|
|
6073
|
-
for (const [preset, button] of viewportPresetButtons) {
|
|
6074
|
-
button.disabled = viewportControlsDisabled;
|
|
6075
|
-
setToolbarButtonTooltip(button, viewportControlsTooltip || VIEW_PRESET_TOOLTIPS[preset]);
|
|
6076
|
-
LocalEditorShared.applyButtonActiveState(button, viewportTools?.viewPreset === preset);
|
|
6077
|
-
}
|
|
6078
|
-
projectionToggleButton.disabled = viewportControlsDisabled;
|
|
6079
|
-
const projectionMode = viewportTools?.projectionMode ?? 'perspective';
|
|
6080
|
-
const nextProjectionMode = projectionMode === 'orthographic' ? 'perspective' : 'orthographic';
|
|
6081
|
-
setToolbarButtonIcon(doc, projectionToggleButton, projectionMode === 'orthographic' ? 'projection-ortho' : 'projection-perspective');
|
|
6082
|
-
setToolbarButtonTooltip(projectionToggleButton, viewportProjectionTooltip || `投影模式:${VIEWPORT_PROJECTION_LABELS[projectionMode]}。切换为${VIEWPORT_PROJECTION_LABELS[nextProjectionMode]}`);
|
|
6083
|
-
LocalEditorShared.applyButtonActiveState(projectionToggleButton, projectionMode === 'orthographic');
|
|
6084
7165
|
const markersVisible = viewportTools?.markersVisible ?? true;
|
|
6085
7166
|
markerVisibilityButton.disabled = disabled;
|
|
6086
7167
|
setToolbarButtonTooltip(markerVisibilityButton, markersVisible ? '隐藏标记与关系线' : '显示标记与关系线');
|
|
@@ -6120,11 +7201,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6120
7201
|
gridToggleButton.disabled = disabled || !gridState.available;
|
|
6121
7202
|
setToolbarButtonTooltip(gridToggleButton, gridState.visible ? '隐藏 Scene View 网格' : '显示 Scene View 网格');
|
|
6122
7203
|
LocalEditorShared.applyButtonActiveState(gridToggleButton, gridState.visible);
|
|
6123
|
-
|
|
6124
|
-
toolGroup.style.display = inEditor
|
|
6125
|
-
&& (state.tools ?? []).some(tool => tool.kind === 'primary' && tool.availability.state !== 'hidden')
|
|
6126
|
-
? 'flex'
|
|
6127
|
-
: 'none';
|
|
7204
|
+
renderToolPresentationState(state);
|
|
6128
7205
|
spaceGroup.style.display = isPreviewToolVisible('transform-space') ? 'flex' : 'none';
|
|
6129
7206
|
for (const [space, button] of Object.entries(spaceButtons)) {
|
|
6130
7207
|
button.disabled = !isPreviewToolEnabled('transform-space');
|
|
@@ -6199,12 +7276,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6199
7276
|
activeRightTab,
|
|
6200
7277
|
});
|
|
6201
7278
|
const activeBottomTab = panelRegistry.getBottomDockTab();
|
|
6202
|
-
const assetBrowserTabChanged = assetBrowserTab !== lastRenderedAssetBrowserTab;
|
|
6203
|
-
const bottomDockTabChanged = activeBottomTab !== lastRenderedBottomDockTab;
|
|
6204
7279
|
const selectedAssetChanged = state.selectedAssetId !== lastRenderedSelectedAssetId;
|
|
6205
|
-
const assetBrowserScrollSnapshot = activeBottomTab === 'assets'
|
|
6206
|
-
? assetBrowserViewportReveal.captureScrollSnapshot()
|
|
6207
|
-
: { scrollTop: null };
|
|
6208
7280
|
leftDockTabs.style.display = 'none';
|
|
6209
7281
|
rightDockTabs.style.display = rightDockVisibility.tabsDisplay;
|
|
6210
7282
|
inspectorPanel.style.display = rightDockVisibility.inspectorDisplay;
|
|
@@ -6213,10 +7285,18 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6213
7285
|
pluginsPanel.style.display = rightDockVisibility.pluginsDisplay;
|
|
6214
7286
|
hierarchyController.render(state);
|
|
6215
7287
|
renderPrefabStagePropertiesPanel(prefabStage);
|
|
6216
|
-
LocalEditorPanels.renderWorkbenchBottomDockPanel(doc, assetPanel, state, activeBottomTab, panelRegistry.getPanels('bottom'), assetBrowserTab);
|
|
6217
|
-
if (activeBottomTab === 'assets'
|
|
6218
|
-
|
|
7288
|
+
LocalEditorPanels.renderWorkbenchBottomDockPanel(doc, assetPanel, state, activeBottomTab, panelRegistry.getPanels('bottom'), assetBrowserTab, { expandedFolderPaths: assetBrowserExpandedFolderPaths, folderPath: assetBrowserFolderPath, viewMode: assetBrowserViewMode, selectedEntryPath: selectedProjectEntryPath });
|
|
7289
|
+
if (activeBottomTab === 'assets') {
|
|
7290
|
+
assetBrowserFolderPath = assetPanel.dataset.editorAssetBrowserFolderPath ?? assetBrowserFolderPath;
|
|
6219
7291
|
}
|
|
7292
|
+
const assetBrowserContentKey = resolveLocalEditorAssetBrowserContentKey({
|
|
7293
|
+
assetFilter: state.assetFilter,
|
|
7294
|
+
folderPath: assetBrowserFolderPath,
|
|
7295
|
+
tab: assetBrowserTab,
|
|
7296
|
+
viewMode: assetBrowserViewMode,
|
|
7297
|
+
});
|
|
7298
|
+
hydrateProjectMediaThumbnails();
|
|
7299
|
+
assetBrowserContinuity.restore(assetBrowserContinuitySnapshot, assetBrowserContentKey, activeBottomTab === 'assets');
|
|
6220
7300
|
const suppressAssetScroll = state.selectedAssetId
|
|
6221
7301
|
? assetBrowserViewportReveal.consumeRevealSuppression(state.selectedAssetId, { clearWhenDifferent: selectedAssetChanged })
|
|
6222
7302
|
: false;
|
|
@@ -6224,8 +7304,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6224
7304
|
revealSelectedAssetBrowserItem(state.selectedAssetId);
|
|
6225
7305
|
}
|
|
6226
7306
|
revealPendingStatusDiagnosticAsset(state);
|
|
6227
|
-
|
|
6228
|
-
lastRenderedBottomDockTab = activeBottomTab;
|
|
7307
|
+
lastRenderedAssetBrowserContentKey = activeBottomTab === 'assets' ? assetBrowserContentKey : null;
|
|
6229
7308
|
lastRenderedSelectedAssetId = state.selectedAssetId ?? null;
|
|
6230
7309
|
restoreLocalEditorEditableFocus(doc, focusSnapshot);
|
|
6231
7310
|
revealPendingStatusDiagnosticPath(state);
|
|
@@ -6240,12 +7319,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6240
7319
|
});
|
|
6241
7320
|
rightDockTabs.style.display = rightDockVisibility.tabsDisplay;
|
|
6242
7321
|
const activeBottomTab = panelRegistry.getBottomDockTab();
|
|
6243
|
-
const assetBrowserTabChanged = assetBrowserTab !== lastRenderedAssetBrowserTab;
|
|
6244
|
-
const bottomDockTabChanged = activeBottomTab !== lastRenderedBottomDockTab;
|
|
6245
7322
|
const selectedAssetChanged = state.selectedAssetId !== lastRenderedSelectedAssetId;
|
|
6246
|
-
const assetBrowserScrollSnapshot = activeBottomTab === 'assets'
|
|
6247
|
-
? assetBrowserViewportReveal.captureScrollSnapshot()
|
|
6248
|
-
: { scrollTop: null };
|
|
6249
7323
|
hierarchyPanel.dataset.editorPanelId = hierarchyDescriptor?.id ?? 'hierarchy';
|
|
6250
7324
|
inspectorPanel.dataset.editorPanelId = activeRightTab === 'inspector' ? rightDescriptor?.id ?? 'inspector' : 'inspector';
|
|
6251
7325
|
renderingPanel.dataset.editorPanelId = activeRightTab === 'rendering' ? rightDescriptor?.id ?? 'rendering' : 'rendering';
|
|
@@ -6257,10 +7331,18 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6257
7331
|
pluginsPanel.style.display = rightDockVisibility.pluginsDisplay;
|
|
6258
7332
|
LocalEditorPanels.renderWorkbenchLeftDockTabs(doc, leftDockTabs, 'hierarchy', panelRegistry.getPanels('left'));
|
|
6259
7333
|
hierarchyController.render(state);
|
|
6260
|
-
LocalEditorPanels.renderWorkbenchBottomDockPanel(doc, assetPanel, state, activeBottomTab, panelRegistry.getPanels('bottom'), assetBrowserTab);
|
|
6261
|
-
if (activeBottomTab === 'assets'
|
|
6262
|
-
|
|
6263
|
-
}
|
|
7334
|
+
LocalEditorPanels.renderWorkbenchBottomDockPanel(doc, assetPanel, state, activeBottomTab, panelRegistry.getPanels('bottom'), assetBrowserTab, { expandedFolderPaths: assetBrowserExpandedFolderPaths, folderPath: assetBrowserFolderPath, viewMode: assetBrowserViewMode, selectedEntryPath: selectedProjectEntryPath });
|
|
7335
|
+
if (activeBottomTab === 'assets') {
|
|
7336
|
+
assetBrowserFolderPath = assetPanel.dataset.editorAssetBrowserFolderPath ?? assetBrowserFolderPath;
|
|
7337
|
+
}
|
|
7338
|
+
const assetBrowserContentKey = resolveLocalEditorAssetBrowserContentKey({
|
|
7339
|
+
assetFilter: state.assetFilter,
|
|
7340
|
+
folderPath: assetBrowserFolderPath,
|
|
7341
|
+
tab: assetBrowserTab,
|
|
7342
|
+
viewMode: assetBrowserViewMode,
|
|
7343
|
+
});
|
|
7344
|
+
hydrateProjectMediaThumbnails();
|
|
7345
|
+
assetBrowserContinuity.restore(assetBrowserContinuitySnapshot, assetBrowserContentKey, activeBottomTab === 'assets');
|
|
6264
7346
|
const suppressAssetScroll = state.selectedAssetId
|
|
6265
7347
|
? assetBrowserViewportReveal.consumeRevealSuppression(state.selectedAssetId, { clearWhenDifferent: selectedAssetChanged })
|
|
6266
7348
|
: false;
|
|
@@ -6268,14 +7350,14 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6268
7350
|
revealSelectedAssetBrowserItem(state.selectedAssetId);
|
|
6269
7351
|
}
|
|
6270
7352
|
revealPendingStatusDiagnosticAsset(state);
|
|
6271
|
-
|
|
6272
|
-
lastRenderedBottomDockTab = activeBottomTab;
|
|
7353
|
+
lastRenderedAssetBrowserContentKey = activeBottomTab === 'assets' ? assetBrowserContentKey : null;
|
|
6273
7354
|
lastRenderedSelectedAssetId = state.selectedAssetId;
|
|
6274
7355
|
LocalEditorPanels.renderWorkbenchRightDockTabs(doc, rightDockTabs, activeRightTab, panelRegistry.getPanels('right'));
|
|
6275
7356
|
const rightDockVisible = rightDockFormalSurfaceVisibility.isVisible();
|
|
6276
7357
|
if (!rightDockVisible) {
|
|
6277
7358
|
LocalEditorPanels.disposeInspectorPanelUi(inspectorPanel);
|
|
6278
7359
|
LocalEditorPanels.disposeInspectorPanelUi(renderingPanel);
|
|
7360
|
+
lastRenderedInspectorContentKey = null;
|
|
6279
7361
|
}
|
|
6280
7362
|
else if (activeRightTab === 'inspector') {
|
|
6281
7363
|
LocalEditorPanels.renderInspectorPanel(doc, inspectorPanel, state, inspectorFilter, {
|
|
@@ -6283,6 +7365,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6283
7365
|
layerHost: editorUiLayerHost,
|
|
6284
7366
|
lockedVectorGroups: lockedInspectorVectorGroups,
|
|
6285
7367
|
});
|
|
7368
|
+
lastRenderedInspectorContentKey = resolveLocalEditorRightDockContentKey(state, 'inspector', inspectorFilter);
|
|
6286
7369
|
}
|
|
6287
7370
|
else if (activeRightTab === 'rendering') {
|
|
6288
7371
|
LocalEditorPanels.renderRenderingPanel(doc, renderingPanel, state, { layerHost: editorUiLayerHost });
|
|
@@ -6298,7 +7381,6 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6298
7381
|
revealPendingStatusDiagnosticPath(state);
|
|
6299
7382
|
};
|
|
6300
7383
|
const completedCleanup = new Set();
|
|
6301
|
-
let uiDisposed = false;
|
|
6302
7384
|
return {
|
|
6303
7385
|
update(state) {
|
|
6304
7386
|
render(state);
|
|
@@ -6307,7 +7389,6 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6307
7389
|
updateSceneFrameStats(stats) {
|
|
6308
7390
|
if (currentState)
|
|
6309
7391
|
currentState = { ...currentState, sceneFrameStats: stats };
|
|
6310
|
-
renderSceneFrameRateOverlay(sceneFrameRateOverlay, currentState?.mode === 'editor' && !currentState.prefabStage ? stats : null);
|
|
6311
7392
|
},
|
|
6312
7393
|
updatePerformanceStats(stats) {
|
|
6313
7394
|
if (currentState)
|
|
@@ -6330,7 +7411,10 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6330
7411
|
return activeTheme;
|
|
6331
7412
|
},
|
|
6332
7413
|
dispose() {
|
|
6333
|
-
if (uiDisposed
|
|
7414
|
+
if (canSkipLocalEditorPreviewCleanupPass(uiDisposed, [
|
|
7415
|
+
projectModelPreviewCleanupRegistry,
|
|
7416
|
+
materialPreviewCleanupRegistry,
|
|
7417
|
+
]))
|
|
6334
7418
|
return;
|
|
6335
7419
|
const errors = [];
|
|
6336
7420
|
const run = (key, operation) => {
|
|
@@ -6363,6 +7447,10 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6363
7447
|
run('toolbar-overflow-resize', () => {
|
|
6364
7448
|
doc.defaultView?.removeEventListener('resize', scheduleToolbarOverflowLayout);
|
|
6365
7449
|
});
|
|
7450
|
+
run('project-media-preview-request', () => {
|
|
7451
|
+
projectMediaPreviewRequestId += 1;
|
|
7452
|
+
assetPreviewOpen = false;
|
|
7453
|
+
});
|
|
6366
7454
|
run('host-chrome', () => hostChrome.remove());
|
|
6367
7455
|
run('inspector-ui', () => LocalEditorPanels.disposeInspectorPanelUi(inspectorPanel));
|
|
6368
7456
|
run('rendering-ui', () => LocalEditorPanels.disposeInspectorPanelUi(renderingPanel));
|
|
@@ -6378,14 +7466,25 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6378
7466
|
run('overlay-settings-popover', () => overlaySettingsPopover.remove());
|
|
6379
7467
|
run('spatial-overlay', () => spatialOverlay.root.remove());
|
|
6380
7468
|
run('measurement-overlay', () => measurementOverlay.root.remove());
|
|
6381
|
-
run('scene-frame-rate-overlay', () => sceneFrameRateOverlay.root.remove());
|
|
6382
7469
|
run('performance-monitor-overlay', () => performanceMonitorOverlay.root.remove());
|
|
6383
7470
|
run('prefab-stage-preview-layout', () => prefabStagePreviewLayout.root.remove());
|
|
6384
7471
|
run('box-selection-overlay', () => boxSelectionOverlay.remove());
|
|
6385
7472
|
run('shortcut-help-panel', () => shortcutHelpPanel.remove());
|
|
7473
|
+
run('project-model-preview', () => disposeProjectModelPreview());
|
|
7474
|
+
run('project-media-thumbnails', () => resetProjectMediaThumbnailCache());
|
|
7475
|
+
run('project-model-thumbnail-cache', () => projectModelThumbnailCache.dispose());
|
|
7476
|
+
run('material-preview', () => disposeMaterialPreview());
|
|
6386
7477
|
run('asset-preview-modal', () => assetPreviewModal.remove());
|
|
7478
|
+
run('project-file-preview-modal', () => projectFilePreviewModal.remove());
|
|
7479
|
+
run('project-file-preview-search-frame', () => {
|
|
7480
|
+
if (projectFilePreviewSearchRaf != null) {
|
|
7481
|
+
doc.defaultView?.cancelAnimationFrame(projectFilePreviewSearchRaf);
|
|
7482
|
+
projectFilePreviewSearchRaf = null;
|
|
7483
|
+
}
|
|
7484
|
+
});
|
|
6387
7485
|
run('asset-picker-modal', () => assetPickerModal.remove());
|
|
6388
7486
|
run('hierarchy-controller', () => hierarchyController.dispose());
|
|
7487
|
+
run('editor-console', () => editorConsole.dispose());
|
|
6389
7488
|
run('workbench-layout-controller', () => workbenchLayoutController.dispose());
|
|
6390
7489
|
run('tooltip-controller', () => tooltipController.dispose());
|
|
6391
7490
|
run('context-menu', () => contextMenu.dispose());
|
|
@@ -6393,12 +7492,20 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
|
|
|
6393
7492
|
run('input-router', () => inputRouter.dispose());
|
|
6394
7493
|
run('document-keydown', () => doc.removeEventListener('keydown', onKeyDown));
|
|
6395
7494
|
run('owned-root-children', () => construction.removeMountedRootChildren());
|
|
7495
|
+
projectModelPreviewCleanupRegistry.disposeOwned();
|
|
7496
|
+
materialPreviewCleanupRegistry.disposeOwned();
|
|
7497
|
+
const retainedPreviewCleanups = [
|
|
7498
|
+
...projectModelPreviewCleanupRegistry.takeFailures(),
|
|
7499
|
+
...materialPreviewCleanupRegistry.takeFailures(),
|
|
7500
|
+
];
|
|
7501
|
+
errors.push(...retainedPreviewCleanups);
|
|
6396
7502
|
if (errors.length > 0) {
|
|
6397
7503
|
const error = new Error('Local Editor browser UI cleanup failed.');
|
|
6398
7504
|
error.errors = errors;
|
|
6399
7505
|
throw error;
|
|
6400
7506
|
}
|
|
6401
|
-
uiDisposed =
|
|
7507
|
+
uiDisposed = !projectModelPreviewCleanupRegistry.hasOwned()
|
|
7508
|
+
&& !materialPreviewCleanupRegistry.hasOwned();
|
|
6402
7509
|
},
|
|
6403
7510
|
};
|
|
6404
7511
|
}
|