@fps-games/editor 0.2.0-beta.3 → 0.2.0-beta.4

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.
Files changed (116) hide show
  1. package/dist/agent-bridge/capabilities/editor-state.d.ts.map +1 -1
  2. package/dist/agent-bridge/capabilities/editor-state.js +26 -17
  3. package/dist/agent-bridge/capabilities/editor-state.js.map +1 -1
  4. package/dist/agent-bridge/capabilities/prefabs.d.ts.map +1 -1
  5. package/dist/agent-bridge/capabilities/prefabs.js +61 -13
  6. package/dist/agent-bridge/capabilities/prefabs.js.map +1 -1
  7. package/dist/agent-bridge/capabilities/scene-objects.d.ts +1 -1
  8. package/dist/agent-bridge/capabilities/scene-objects.d.ts.map +1 -1
  9. package/dist/agent-bridge/capabilities/scene-objects.js +110 -25
  10. package/dist/agent-bridge/capabilities/scene-objects.js.map +1 -1
  11. package/dist/agent-bridge/capabilities/selection.d.ts.map +1 -1
  12. package/dist/agent-bridge/capabilities/selection.js +8 -5
  13. package/dist/agent-bridge/capabilities/selection.js.map +1 -1
  14. package/dist/agent-bridge/capabilities/shadows.d.ts.map +1 -1
  15. package/dist/agent-bridge/capabilities/shadows.js +11 -2
  16. package/dist/agent-bridge/capabilities/shadows.js.map +1 -1
  17. package/dist/agent-bridge/capabilities/transform.d.ts.map +1 -1
  18. package/dist/agent-bridge/capabilities/transform.js +6 -4
  19. package/dist/agent-bridge/capabilities/transform.js.map +1 -1
  20. package/dist/agent-bridge/capabilities/viewport.d.ts.map +1 -1
  21. package/dist/agent-bridge/capabilities/viewport.js +79 -47
  22. package/dist/agent-bridge/capabilities/viewport.js.map +1 -1
  23. package/dist/agent-bridge/shared.d.ts +8 -3
  24. package/dist/agent-bridge/shared.d.ts.map +1 -1
  25. package/dist/agent-bridge/shared.js +32 -0
  26. package/dist/agent-bridge/shared.js.map +1 -1
  27. package/dist/build-info.json +34 -34
  28. package/dist/local-editor-agent-actions.d.ts.map +1 -1
  29. package/dist/local-editor-agent-actions.js +9 -5
  30. package/dist/local-editor-agent-actions.js.map +1 -1
  31. package/dist/local-editor-harness.d.ts.map +1 -1
  32. package/dist/local-editor-harness.js +264 -41
  33. package/dist/local-editor-harness.js.map +1 -1
  34. package/node_modules/@fps-games/babylon-renderer/package.json +7 -7
  35. package/node_modules/@fps-games/editor-babylon/package.json +5 -5
  36. package/node_modules/@fps-games/editor-browser/dist/index.d.ts +3 -1
  37. package/node_modules/@fps-games/editor-browser/dist/index.d.ts.map +1 -1
  38. package/node_modules/@fps-games/editor-browser/dist/index.js +2 -0
  39. package/node_modules/@fps-games/editor-browser/dist/index.js.map +1 -1
  40. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts +2 -1
  41. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts.map +1 -1
  42. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js +260 -94
  43. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js.map +1 -1
  44. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-projection.d.ts +7 -0
  45. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-projection.d.ts.map +1 -0
  46. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-projection.js +186 -0
  47. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-projection.js.map +1 -0
  48. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-scope.d.ts +2 -2
  49. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-scope.d.ts.map +1 -1
  50. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-scope.js +14 -8
  51. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-scope.js.map +1 -1
  52. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.d.ts.map +1 -1
  53. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.js +23 -7
  54. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.js.map +1 -1
  55. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts +1 -1
  56. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.d.ts.map +1 -1
  57. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js +6 -0
  58. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-icons.js.map +1 -1
  59. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.d.ts +3 -0
  60. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.d.ts.map +1 -1
  61. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.js +60 -13
  62. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.js.map +1 -1
  63. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-status-diagnostics.d.ts +24 -0
  64. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-status-diagnostics.d.ts.map +1 -0
  65. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-status-diagnostics.js +69 -0
  66. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-status-diagnostics.js.map +1 -0
  67. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts +58 -14
  68. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts.map +1 -1
  69. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.js.map +1 -1
  70. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts +1 -1
  71. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts.map +1 -1
  72. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js +196 -11
  73. package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
  74. package/node_modules/@fps-games/editor-browser/package.json +4 -4
  75. package/node_modules/@fps-games/editor-core/dist/editor-session.d.ts +3 -0
  76. package/node_modules/@fps-games/editor-core/dist/editor-session.d.ts.map +1 -1
  77. package/node_modules/@fps-games/editor-core/dist/editor-session.js +9 -0
  78. package/node_modules/@fps-games/editor-core/dist/editor-session.js.map +1 -1
  79. package/node_modules/@fps-games/editor-core/package.json +6 -6
  80. package/node_modules/@fps-games/editor-forge-play/package.json +2 -2
  81. package/node_modules/@fps-games/editor-playable-sdk/package.json +6 -6
  82. package/node_modules/@fps-games/editor-protocol/package.json +2 -2
  83. package/node_modules/@fps-games/editor-tooling/package.json +3 -3
  84. package/node_modules/@fps-games/playable-assets/package.json +1 -1
  85. package/node_modules/@fps-games/playable-contracts/package.json +1 -1
  86. package/node_modules/@fps-games/playable-rendering/package.json +2 -2
  87. package/node_modules/@fps-games/playable-runtime/package.json +12 -12
  88. package/node_modules/@fps-games/playable-runtime-data/package.json +4 -4
  89. package/node_modules/@fps-games/playable-scene/package.json +2 -2
  90. package/node_modules/@fps-games/plugin-api/package.json +1 -1
  91. package/node_modules/@fps-games/plugin-assets/dist/version.d.ts +1 -1
  92. package/node_modules/@fps-games/plugin-assets/dist/version.js +1 -1
  93. package/node_modules/@fps-games/plugin-assets/dist/version.js.map +1 -1
  94. package/node_modules/@fps-games/plugin-assets/package.json +5 -5
  95. package/node_modules/@fps-games/plugin-host/package.json +2 -2
  96. package/node_modules/@fps-games/plugin-markers/dist/version.d.ts +1 -1
  97. package/node_modules/@fps-games/plugin-markers/dist/version.js +1 -1
  98. package/node_modules/@fps-games/plugin-markers/dist/version.js.map +1 -1
  99. package/node_modules/@fps-games/plugin-markers/package.json +5 -5
  100. package/node_modules/@fps-games/plugin-materials/dist/version.d.ts +1 -1
  101. package/node_modules/@fps-games/plugin-materials/dist/version.js +1 -1
  102. package/node_modules/@fps-games/plugin-materials/dist/version.js.map +1 -1
  103. package/node_modules/@fps-games/plugin-materials/package.json +5 -5
  104. package/node_modules/@fps-games/plugin-rendering/dist/version.d.ts +1 -1
  105. package/node_modules/@fps-games/plugin-rendering/dist/version.js +1 -1
  106. package/node_modules/@fps-games/plugin-rendering/dist/version.js.map +1 -1
  107. package/node_modules/@fps-games/plugin-rendering/package.json +5 -5
  108. package/node_modules/@fps-games/plugin-scene/dist/version.d.ts +1 -1
  109. package/node_modules/@fps-games/plugin-scene/dist/version.js +1 -1
  110. package/node_modules/@fps-games/plugin-scene/dist/version.js.map +1 -1
  111. package/node_modules/@fps-games/plugin-scene/package.json +5 -5
  112. package/node_modules/@fps-games/plugin-shadows/dist/version.d.ts +1 -1
  113. package/node_modules/@fps-games/plugin-shadows/dist/version.js +1 -1
  114. package/node_modules/@fps-games/plugin-shadows/dist/version.js.map +1 -1
  115. package/node_modules/@fps-games/plugin-shadows/package.json +7 -7
  116. package/package.json +19 -19
@@ -11,7 +11,7 @@ import { LOCAL_EDITOR_BROWSER_INSPECTOR_VEC3_AXES, resolveLocalEditorBrowserInsp
11
11
  import { DEFAULT_EDITOR_TRANSFORM_OPERATION_SETTINGS, DEFAULT_EDITOR_TRANSFORM_TOOL_DESCRIPTORS, } from '@fps-games/editor-core';
12
12
  export { createLocalEditorViewportRevealController, isLocalEditorElementVisibleInViewport, resolveLocalEditorViewportRevealScrollTop, revealLocalEditorElementInViewport, } from './local-editor-ui-dock-panel.js';
13
13
  export { createLocalEditorHierarchyBlankMenu, createLocalEditorHierarchyCopyShortcutAction, createLocalEditorHierarchyDeleteShortcutAction, createLocalEditorHierarchyDuplicateShortcutAction, createLocalEditorHierarchyNodeMenu, createLocalEditorHierarchyPasteShortcutAction, createLocalEditorHierarchySelectAllShortcutAction, } from './local-editor-ui-hierarchy-actions.js';
14
- export { createLocalEditorHierarchyController, resolveLocalEditorHierarchyCreateMarkerParentId, } from './local-editor-ui-hierarchy-controller.js';
14
+ export { createLocalEditorHierarchyController, resolveLocalEditorHierarchyCreateMarkerParentId, resolveLocalEditorHierarchyToolbarActiveId, } from './local-editor-ui-hierarchy-controller.js';
15
15
  export { resolveLocalEditorHierarchySearchRows, } from './local-editor-ui-hierarchy-view.js';
16
16
  export { createLocalEditorHierarchyAttachDialog, resolveLocalEditorHierarchyAttachTargets, } from './local-editor-ui-hierarchy-attach-dialog.js';
17
17
  export { canLocalEditorHierarchyNodeHaveChildren, canLocalEditorHierarchyNodeDelete, canLocalEditorHierarchyNodeRename, canLocalEditorHierarchyNodeToggleEnabled, createLocalEditorHierarchyTreeModel, isLocalEditorHierarchyNodeSelectable, isLocalEditorHierarchyNodeMovable, } from './local-editor-ui-hierarchy-tree.js';
@@ -25,6 +25,8 @@ import { createShortcutHelpPanel as createLocalEditorShortcutHelpPanel } from '.
25
25
  import { applyLocalEditorTheme, ensureLocalEditorTheme, LOCAL_EDITOR_THEME_CLASS, normalizeLocalEditorThemeName, } from './local-editor-ui-theme.js';
26
26
  import { createDefaultLocalEditorWorkbenchLayout, createLocalEditorWorkbenchLayoutController, createLocalEditorWorkbench, createSceneHeaderToolbar, createWorkbenchPanelContent, } from './local-editor-ui-workbench.js';
27
27
  import { createLocalEditorIcon, isLocalEditorIconName } from './local-editor-ui-icons.js';
28
+ import { createLocalEditorStatusCenterViewModel, resolveLocalEditorStatusDiagnostics, shouldCloseLocalEditorStatusCenter, } from './local-editor-ui-status-diagnostics.js';
29
+ import { formatLocalEditorStatusDiagnosticDetails } from './local-editor-ui-hierarchy-projection.js';
28
30
  import { resolveLocalEditorBrowserAssetKind, } from './local-editor-ui-asset-kind.js';
29
31
  import { createLocalEditorPrefabStageTitle, findLocalEditorPrefabStageStructureItem, formatLocalEditorPrefabStageComparisonPreviewStatus, LOCAL_EDITOR_PREFAB_COMPARISON_COLUMN_WIDTH_PX, resolveLocalEditorRightDockVisibility, resolveLocalEditorPrefabStageComparisonPreviewTone, resolveLocalEditorPrefabStageWorkbenchLayout, resolveLocalEditorPreviewToolSupport, shouldLocalEditorClosePerformanceMonitor, shouldLocalEditorPrefabStageBlockHierarchyShortcut, shouldShowLocalEditorPrefabComparisonColumn, shouldToggleLocalEditorPrefabComparisonShortcut, } from './local-editor-ui-prefab-stage.js';
30
32
  export { applyLocalEditorBrowserInspectorControlBinding, createLocalEditorBrowserInspectorControlRegistry, formatLocalEditorBrowserInspectorNumberValue, formatLocalEditorBrowserInspectorValue, parseLocalEditorBrowserInspectorNumberValue, resolveLocalEditorBrowserInspectorSectionStatus, resolveLocalEditorBrowserInspectorControlRegistration, } from './local-editor-ui-panels.js';
@@ -2653,6 +2655,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
2653
2655
  const undoButton = createToolbarIconButton(doc, '撤销', 'undo', '撤销');
2654
2656
  const redoButton = createToolbarIconButton(doc, '重做', 'redo', '重做');
2655
2657
  let helpOpen = false;
2658
+ let editorStatusCenterOpen = false;
2656
2659
  let performanceMonitorOpen = false;
2657
2660
  let prefabComparisonPreviewOpen = true;
2658
2661
  let assetPreviewOpen = false;
@@ -2666,7 +2669,46 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
2666
2669
  let prefabStageMainWorkbenchLayoutSnapshot = null;
2667
2670
  const editorStatusButton = createToolbarIconButton(doc, '编辑器状态', 'status', '编辑器状态');
2668
2671
  editorStatusButton.dataset.editorStatusButton = 'true';
2669
- editorStatusButton.style.cursor = 'help';
2672
+ editorStatusButton.dataset.editorStatusToggle = 'true';
2673
+ editorStatusButton.setAttribute('aria-expanded', 'false');
2674
+ const editorStatusCenter = doc.createElement('div');
2675
+ editorStatusCenter.classList.add(LOCAL_EDITOR_THEME_CLASS);
2676
+ editorStatusCenter.dataset.editorStatusCenter = 'true';
2677
+ editorStatusCenter.style.cssText = [
2678
+ 'position:fixed',
2679
+ 'z-index:2147483642',
2680
+ 'display:none',
2681
+ 'flex-direction:column',
2682
+ 'gap:8px',
2683
+ 'width:min(440px,calc(100vw - 16px))',
2684
+ 'max-height:min(520px,calc(100vh - 56px))',
2685
+ 'padding:10px',
2686
+ 'border:1px solid var(--fps-editor-border)',
2687
+ 'border-radius:4px',
2688
+ 'background:var(--fps-editor-panel)',
2689
+ 'box-shadow:var(--fps-editor-shadow-popover)',
2690
+ 'overflow:hidden',
2691
+ 'pointer-events:auto',
2692
+ ].join(';');
2693
+ const editorStatusCenterHeader = doc.createElement('div');
2694
+ editorStatusCenterHeader.style.cssText = 'display:flex;align-items:center;justify-content:space-between;gap:12px';
2695
+ const editorStatusCenterTitle = doc.createElement('div');
2696
+ editorStatusCenterTitle.textContent = '编辑器状态';
2697
+ editorStatusCenterTitle.style.cssText = 'font-size:13px;font-weight:900;color:var(--fps-editor-text-strong)';
2698
+ const editorStatusCopyAllButton = LocalEditorShared.createButton(doc, '复制全部', { icon: 'copy' });
2699
+ editorStatusCopyAllButton.dataset.editorStatusCopyAll = 'true';
2700
+ editorStatusCopyAllButton.style.cssText += ';min-height:26px;padding:3px 8px;font-size:11px';
2701
+ editorStatusCenterHeader.appendChild(editorStatusCenterTitle);
2702
+ editorStatusCenterHeader.appendChild(editorStatusCopyAllButton);
2703
+ const editorStatusCenterSummary = doc.createElement('div');
2704
+ editorStatusCenterSummary.dataset.editorStatusSummary = 'true';
2705
+ editorStatusCenterSummary.style.cssText = 'font-size:11px;line-height:1.45;color:var(--fps-editor-muted-strong)';
2706
+ const editorStatusCenterList = doc.createElement('div');
2707
+ editorStatusCenterList.dataset.editorStatusList = 'true';
2708
+ editorStatusCenterList.style.cssText = 'display:flex;flex-direction:column;gap:7px;overflow:auto;min-height:0';
2709
+ editorStatusCenter.appendChild(editorStatusCenterHeader);
2710
+ editorStatusCenter.appendChild(editorStatusCenterSummary);
2711
+ editorStatusCenter.appendChild(editorStatusCenterList);
2670
2712
  const agentBridgeStatusButton = createToolbarIconButton(doc, 'Agent Bridge 状态', 'status', 'Agent Bridge 状态');
2671
2713
  agentBridgeStatusButton.dataset.editorAgentBridgeStatusButton = 'true';
2672
2714
  agentBridgeStatusButton.style.cursor = 'help';
@@ -3322,6 +3364,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
3322
3364
  workbench.sceneFrame.appendChild(prefabStagePreviewLayout.root);
3323
3365
  workbench.sceneFrame.appendChild(coordinateAxesOverlay.root);
3324
3366
  construction.appendRootChild(localTestMenu);
3367
+ construction.appendRootChild(editorStatusCenter);
3325
3368
  construction.appendRootChild(toolbarOverflowMenu);
3326
3369
  construction.appendRootChild(staticShadowBakePopover);
3327
3370
  construction.appendRootChild(snapSettingsPopover);
@@ -3329,6 +3372,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
3329
3372
  construction.appendRootChild(transformActionPopover);
3330
3373
  construction.appendRootChild(overlaySettingsPopover);
3331
3374
  tooltipSurfaces.add(localTestMenu);
3375
+ tooltipSurfaces.add(editorStatusCenter);
3332
3376
  tooltipSurfaces.add(toolbarOverflowMenu);
3333
3377
  tooltipSurfaces.add(staticShadowBakePopover);
3334
3378
  tooltipSurfaces.add(snapSettingsPopover);
@@ -3525,6 +3569,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
3525
3569
  applyLocalEditorTheme(assetPickerModal, activeTheme);
3526
3570
  applyLocalEditorTheme(boxSelectionOverlay, activeTheme);
3527
3571
  applyLocalEditorTheme(localTestMenu, activeTheme);
3572
+ applyLocalEditorTheme(editorStatusCenter, activeTheme);
3528
3573
  applyLocalEditorTheme(toolbarOverflowMenu, activeTheme);
3529
3574
  applyLocalEditorTheme(staticShadowBakePopover, activeTheme);
3530
3575
  applyLocalEditorTheme(snapSettingsPopover, activeTheme);
@@ -3556,6 +3601,101 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
3556
3601
  localTestMenu.style.display = 'none';
3557
3602
  LocalEditorShared.applyButtonActiveState(localTestButton, false);
3558
3603
  }
3604
+ function readEditorStatusDiagnostics(state) {
3605
+ return resolveLocalEditorStatusDiagnostics(state);
3606
+ }
3607
+ function renderEditorStatusCenter(state) {
3608
+ const viewModel = createLocalEditorStatusCenterViewModel(state);
3609
+ editorStatusCenterSummary.textContent = viewModel.summary;
3610
+ editorStatusCopyAllButton.style.display = viewModel.diagnostics.length > 0 ? 'inline-flex' : 'none';
3611
+ LocalEditorShared.clearElement(editorStatusCenterList);
3612
+ if (viewModel.diagnostics.length === 0) {
3613
+ const empty = doc.createElement('div');
3614
+ empty.dataset.editorStatusEmpty = 'true';
3615
+ empty.textContent = '没有需要处理的错误或警告。';
3616
+ empty.style.cssText = 'padding:14px 8px;text-align:center;color:var(--fps-editor-muted);font-size:12px';
3617
+ editorStatusCenterList.appendChild(empty);
3618
+ return;
3619
+ }
3620
+ viewModel.entries.forEach(({ diagnostic, copyText }, index) => {
3621
+ const item = doc.createElement('div');
3622
+ item.dataset.editorStatusDiagnostic = diagnostic.code;
3623
+ item.style.cssText = [
3624
+ 'display:grid',
3625
+ 'grid-template-columns:minmax(0,1fr) auto',
3626
+ 'gap:8px',
3627
+ 'padding:8px',
3628
+ `border:1px solid ${diagnostic.severity === 'error' ? 'var(--fps-editor-danger-border)' : 'var(--fps-editor-warn-border)'}`,
3629
+ 'border-radius:3px',
3630
+ `background:${diagnostic.severity === 'error' ? 'var(--fps-editor-danger-soft)' : 'var(--fps-editor-warn-soft)'}`,
3631
+ ].join(';');
3632
+ const content = doc.createElement('div');
3633
+ content.style.cssText = 'min-width:0;display:flex;flex-direction:column;gap:4px';
3634
+ const code = doc.createElement('div');
3635
+ code.textContent = `${diagnostic.severity === 'error' ? '错误' : '警告'} · ${diagnostic.code}`;
3636
+ code.style.cssText = `font-size:11px;font-weight:900;color:${diagnostic.severity === 'error' ? 'var(--fps-editor-danger-strong)' : 'var(--fps-editor-warn)'}`;
3637
+ const message = doc.createElement('div');
3638
+ message.textContent = diagnostic.message;
3639
+ message.style.cssText = 'font-size:12px;line-height:1.45;color:var(--fps-editor-text-strong);overflow-wrap:anywhere';
3640
+ const meta = doc.createElement('div');
3641
+ const diagnosticLocation = [diagnostic.scopeId, diagnostic.rowId, diagnostic.targetId]
3642
+ .filter(value => value !== undefined && value !== null && value !== '')
3643
+ .join(' / ');
3644
+ meta.textContent = [
3645
+ `来源: ${diagnostic.source}`,
3646
+ diagnosticLocation ? `位置: ${diagnosticLocation}` : '',
3647
+ diagnostic.documentRevision != null ? `document revision: ${diagnostic.documentRevision}` : '',
3648
+ diagnostic.projectionRevision != null ? `projection revision: ${diagnostic.projectionRevision}` : '',
3649
+ diagnostic.details ? `details: ${formatLocalEditorStatusDiagnosticDetails(diagnostic.details)}` : '',
3650
+ ].filter(Boolean).join(' · ');
3651
+ meta.style.cssText = 'font-size:10px;line-height:1.35;color:var(--fps-editor-muted);overflow-wrap:anywhere';
3652
+ meta.style.display = meta.textContent ? 'block' : 'none';
3653
+ content.appendChild(code);
3654
+ content.appendChild(message);
3655
+ content.appendChild(meta);
3656
+ const copy = createToolbarIconButton(doc, `复制诊断 ${index + 1}`, 'copy', '复制该诊断');
3657
+ copy.dataset.editorStatusCopy = String(index);
3658
+ copy.addEventListener('click', event => {
3659
+ event.stopPropagation();
3660
+ copyTextToClipboard(doc.defaultView, copyText);
3661
+ });
3662
+ item.appendChild(content);
3663
+ item.appendChild(copy);
3664
+ editorStatusCenterList.appendChild(item);
3665
+ });
3666
+ }
3667
+ function closeEditorStatusCenter() {
3668
+ editorStatusCenterOpen = false;
3669
+ editorStatusCenter.style.display = 'none';
3670
+ editorStatusButton.setAttribute('aria-expanded', 'false');
3671
+ LocalEditorShared.applyButtonActiveState(editorStatusButton, false);
3672
+ }
3673
+ function openEditorStatusCenter() {
3674
+ closeLocalTestMenu();
3675
+ closeToolbarOverflowMenu();
3676
+ closeSnapSettingsPopover();
3677
+ closePlacementSettingsPopover();
3678
+ closeTransformActionPopover();
3679
+ closeOverlaySettingsPopover();
3680
+ renderEditorStatusCenter(currentState);
3681
+ const rect = editorStatusButton.getBoundingClientRect();
3682
+ const win = doc.defaultView;
3683
+ const viewportWidth = win?.innerWidth ?? doc.documentElement.clientWidth;
3684
+ const width = Math.min(440, Math.max(280, viewportWidth - 16));
3685
+ editorStatusCenter.style.width = `${width}px`;
3686
+ editorStatusCenter.style.left = `${Math.max(8, Math.min(rect.right - width, viewportWidth - width - 8))}px`;
3687
+ editorStatusCenter.style.top = `${Math.max(8, rect.bottom + 5)}px`;
3688
+ editorStatusCenter.style.display = 'flex';
3689
+ editorStatusCenterOpen = true;
3690
+ editorStatusButton.setAttribute('aria-expanded', 'true');
3691
+ LocalEditorShared.applyButtonActiveState(editorStatusButton, true);
3692
+ }
3693
+ function toggleEditorStatusCenter() {
3694
+ if (editorStatusCenterOpen)
3695
+ closeEditorStatusCenter();
3696
+ else
3697
+ openEditorStatusCenter();
3698
+ }
3559
3699
  function closeSnapSettingsPopover() {
3560
3700
  snapSettingsOpen = false;
3561
3701
  snapSettingsPopover.style.display = 'none';
@@ -3991,12 +4131,22 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
3991
4131
  else
3992
4132
  openToolbarOverflowMenu();
3993
4133
  };
4134
+ const onEditorStatusButtonClick = (event) => {
4135
+ event.stopPropagation();
4136
+ toggleEditorStatusCenter();
4137
+ };
4138
+ const onEditorStatusCopyAllClick = (event) => {
4139
+ event.stopPropagation();
4140
+ copyTextToClipboard(doc.defaultView, createLocalEditorStatusCenterViewModel(currentState).copyAllText);
4141
+ };
3994
4142
  const onToolbarOverflowPointerDown = (event) => {
3995
4143
  const target = event.target instanceof HTMLElement ? event.target : null;
3996
4144
  if (!target)
3997
4145
  return;
3998
4146
  if (target.closest('[data-editor-local-test-menu]') || target.closest('[data-editor-local-test-toggle]'))
3999
4147
  return;
4148
+ if (target.closest('[data-editor-status-center]') || target.closest('[data-editor-status-toggle]'))
4149
+ return;
4000
4150
  if (target.closest('[data-editor-toolbar-overflow-menu]') || target.closest('[data-editor-toolbar-overflow-toggle]'))
4001
4151
  return;
4002
4152
  if (target.closest('[data-transform-snap-settings-popover]') || target.closest('[data-transform-snap-toggle]'))
@@ -4008,6 +4158,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
4008
4158
  if (target.closest('[data-viewport-overlay-settings-popover]') || target.closest('[data-viewport-overlay-settings-toggle]'))
4009
4159
  return;
4010
4160
  closeLocalTestMenu();
4161
+ closeEditorStatusCenter();
4011
4162
  closeToolbarOverflowMenu();
4012
4163
  if (!target.closest('[data-editor-toolbar-static-shadow-bake-popover]'))
4013
4164
  closeStaticShadowBakePopover();
@@ -4023,6 +4174,8 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
4023
4174
  construction.addCleanup('toolbar-resize-observer', () => toolbarResizeObserver?.disconnect());
4024
4175
  toolbarOverflowButton.addEventListener('click', onToolbarOverflowButtonClick);
4025
4176
  localTestButton.addEventListener('click', onLocalTestButtonClick);
4177
+ editorStatusButton.addEventListener('click', onEditorStatusButtonClick);
4178
+ editorStatusCopyAllButton.addEventListener('click', onEditorStatusCopyAllClick);
4026
4179
  doc.addEventListener('pointerdown', onToolbarOverflowPointerDown);
4027
4180
  construction.addCleanup('toolbar-overflow-pointerdown', () => {
4028
4181
  doc.removeEventListener('pointerdown', onToolbarOverflowPointerDown);
@@ -4926,7 +5079,11 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
4926
5079
  callbacks.onAssetAction?.(createAssetBrowserActionInput(actionId, asset, createCurrentAssetBrowserActionContext()));
4927
5080
  });
4928
5081
  assetPanel.addEventListener('contextmenu', (event) => {
4929
- const target = event.target instanceof HTMLElement ? event.target : null;
5082
+ if (!inputRouter.shouldPreventBrowserContextMenu(event))
5083
+ return;
5084
+ const target = inputRouter.getEventTarget(event);
5085
+ if (!target)
5086
+ return;
4930
5087
  const assetButton = target?.closest('[data-editor-asset-id]');
4931
5088
  const browserAssetId = assetButton?.dataset.editorAssetId;
4932
5089
  if (!browserAssetId || !currentState)
@@ -4934,8 +5091,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
4934
5091
  const asset = currentState.assets.find(candidate => candidate.id === browserAssetId);
4935
5092
  if (!asset)
4936
5093
  return;
4937
- event.preventDefault();
4938
- event.stopPropagation();
5094
+ inputRouter.claimContextMenu(event);
4939
5095
  suppressAssetBrowserRevealIfVisible(assetButton);
4940
5096
  callbacks.onSelectAsset?.(browserAssetId);
4941
5097
  contextMenu.open({
@@ -5138,11 +5294,14 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
5138
5294
  const materialSlotSelectTarget = target?.closest('[data-editor-inspector-material-slot-select]');
5139
5295
  const materialSlotSelectionId = materialSlotSelectTarget?.dataset.editorInspectorMaterialSlotSelect;
5140
5296
  if (materialSlotSelectionId) {
5297
+ const precondition = createCurrentHierarchyCommandPrecondition();
5298
+ if (!precondition)
5299
+ return;
5141
5300
  callbacks.onSelectHierarchyItem?.({
5142
5301
  id: materialSlotSelectionId,
5143
5302
  additive: event.shiftKey && !event.metaKey && !event.ctrlKey,
5144
5303
  toggle: event.metaKey || event.ctrlKey,
5145
- });
5304
+ }, precondition);
5146
5305
  return;
5147
5306
  }
5148
5307
  const copyButton = target?.closest('[data-editor-inspector-asset-copy]');
@@ -5458,6 +5617,16 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
5458
5617
  : snap.scaleStep;
5459
5618
  return Number.isFinite(value) ? String(value) : '';
5460
5619
  }
5620
+ function createCurrentHierarchyCommandPrecondition() {
5621
+ if (!currentState)
5622
+ return null;
5623
+ const documentRevision = currentState.documentRevision ?? 0;
5624
+ return {
5625
+ scopeId: currentState.prefabStage ? `prefab-stage:${currentState.prefabStage.assetId}` : 'scene',
5626
+ documentRevision,
5627
+ projectionRevision: currentState.hierarchyProjectionRevision ?? documentRevision,
5628
+ };
5629
+ }
5461
5630
  function isTransformActionEnabled(action, operationState) {
5462
5631
  return action.startsWith('align-')
5463
5632
  ? operationState.canAlign
@@ -5673,23 +5842,32 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
5673
5842
  const transformStatusText = transformTool?.dragPhase === 'dragging'
5674
5843
  ? `正在拖拽 ${transformTool.draggingNodeId ?? '选择对象'}`
5675
5844
  : LocalEditorShared.toTransformToolStatusLabel(transformTool?.activeTool ?? 'select', transformTool?.activeSpace ?? 'world');
5676
- const statusToneColor = state.statusTone === 'error'
5845
+ const statusViewModel = createLocalEditorStatusCenterViewModel(state);
5846
+ const statusDiagnostics = statusViewModel.diagnostics;
5847
+ const resolvedDiagnosticTone = statusViewModel.severity;
5848
+ const diagnosticTone = resolvedDiagnosticTone === 'default' ? state.statusTone : resolvedDiagnosticTone;
5849
+ const statusToneColor = diagnosticTone === 'error'
5677
5850
  ? 'var(--fps-editor-danger-strong)'
5678
- : state.statusTone === 'warning'
5851
+ : diagnosticTone === 'warning'
5679
5852
  ? 'var(--fps-editor-warn)'
5680
- : state.statusTone === 'success'
5853
+ : diagnosticTone === 'success'
5681
5854
  ? 'var(--fps-editor-success)'
5682
5855
  : 'var(--fps-editor-muted)';
5856
+ setToolbarButtonIcon(doc, editorStatusButton, statusViewModel.icon);
5683
5857
  setToolbarButtonTooltip(editorStatusButton, [
5684
5858
  `编辑器状态:${editorStatusText}`,
5685
5859
  state.statusDetails && state.statusDetails !== state.status ? state.statusDetails : '',
5686
5860
  transformStatusText,
5687
5861
  LocalEditorShared.toTransformOperationStatusLabel(transformOperations.settings),
5862
+ statusDiagnostics.length > 0 ? `${statusDiagnostics.length} 条诊断,点击查看并复制` : '',
5688
5863
  ].filter(Boolean).join(' · '));
5689
5864
  editorStatusButton.style.color = statusToneColor;
5690
- editorStatusButton.style.borderColor = state.statusTone == null || state.statusTone === 'default'
5865
+ editorStatusButton.style.borderColor = diagnosticTone == null || diagnosticTone === 'default'
5691
5866
  ? 'var(--fps-editor-border)'
5692
5867
  : statusToneColor;
5868
+ editorStatusButton.dataset.editorStatusSeverity = diagnosticTone ?? 'default';
5869
+ if (editorStatusCenterOpen)
5870
+ renderEditorStatusCenter(state);
5693
5871
  const agentBridgeStatusColor = resolveAgentBridgeStatusToneColor(state.agentBridge);
5694
5872
  agentBridgeStatusButton.style.display = inEditor ? 'inline-flex' : 'none';
5695
5873
  agentBridgeStatusButton.style.color = agentBridgeStatusColor;
@@ -5771,8 +5949,12 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
5771
5949
  localTestButton.disabled = !isPreviewToolEnabled('local-test') || !localTestActionsEnabled;
5772
5950
  if (!inEditor || !localTestActionsEnabled || localTestGroup.style.display === 'none')
5773
5951
  closeLocalTestMenu();
5774
- if (!inEditor)
5952
+ if (!inEditor) {
5775
5953
  helpOpen = false;
5954
+ if (shouldCloseLocalEditorStatusCenter({ inEditor, open: editorStatusCenterOpen })) {
5955
+ closeEditorStatusCenter();
5956
+ }
5957
+ }
5776
5958
  if (shouldLocalEditorClosePerformanceMonitor({ inEditor, inPrefabStage, performanceMonitorOpen })) {
5777
5959
  setPerformanceMonitorOpen(false);
5778
5960
  }
@@ -6125,6 +6307,8 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
6125
6307
  toolbarOverflowButton.removeEventListener('click', onToolbarOverflowButtonClick);
6126
6308
  });
6127
6309
  run('local-test-click', () => localTestButton.removeEventListener('click', onLocalTestButtonClick));
6310
+ run('editor-status-click', () => editorStatusButton.removeEventListener('click', onEditorStatusButtonClick));
6311
+ run('editor-status-copy-all-click', () => editorStatusCopyAllButton.removeEventListener('click', onEditorStatusCopyAllClick));
6128
6312
  run('toolbar-overflow-pointerdown', () => {
6129
6313
  doc.removeEventListener('pointerdown', onToolbarOverflowPointerDown);
6130
6314
  });
@@ -6134,6 +6318,7 @@ function createLocalEditorBrowserUiImplementation(options, construction) {
6134
6318
  run('host-chrome', () => hostChrome.remove());
6135
6319
  run('workbench-root', () => workbench.root.remove());
6136
6320
  run('local-test-menu', () => localTestMenu.remove());
6321
+ run('editor-status-center', () => editorStatusCenter.remove());
6137
6322
  run('toolbar-overflow-menu', () => toolbarOverflowMenu.remove());
6138
6323
  run('static-shadow-bake-popover', () => staticShadowBakePopover.remove());
6139
6324
  run('snap-settings-popover', () => snapSettingsPopover.remove());