Files
XCEngine/editor/AGENTS.md

315 lines
14 KiB
Markdown

# XCUI Editor Agent Guide
This file documents the current editor architecture for agents working under
`editor/`. It describes the code that exists in this checkout, not a desired
future shape.
If this file conflicts with the current code, `editor/CMakeLists.txt`, or the
real directory tree, trust the code and update this file in the same change.
## Build Shape
The important production targets are:
- `XCUIEditorLib`: reusable XCEditor framework code from `editor/src` and
`editor/include/XCEditor`, including the reusable window authority,
synchronization planner, and presentation projection under
`XCEditor/Windowing`.
- `XCUIEditorAppWindowing`: app-internal static library for the current
host contracts, content controllers, coordinators, frame transfer flow,
runtime controllers, and window manager.
- `XCUIEditorApp`: concrete editor executable. Its output name is `XCEngine`.
Do not invent target boundaries that are not present in `editor/CMakeLists.txt`.
Names such as `XCUIEditorAppCore`, `XCUIEditorAppLib`, and `XCUIEditorHost`
are not current production library boundaries in this checkout.
There is now a narrow public `editor/include/XCEditor/Windowing` layer for the
generic window authority model, synchronization plan/planner, and presentation
projection. App-specific window orchestration remains under
`editor/app/Windowing`, and the concrete Win32 host remains under
`editor/app/Platform/Win32`.
## Layering
Use these ownership boundaries when changing code:
- `editor/include/XCEditor` and `editor/src`: reusable editor framework.
Keep this layer independent from app state, Win32, D3D12 host code, and
`App::*` types.
- `editor/include/XCEditor/Windowing` and `editor/src/Windowing`: reusable
window authority, validation, synchronization planning, commit, and
presentation projection. Keep this layer generic and free of app content,
host runtime, native platform, and rendering details.
- `editor/app/Composition`, `Commands`, `Features`, `Project`, `Scene`,
`State`, `System`, `UtilityWindows`: editor product semantics.
- `editor/app/Windowing/Content`, `Coordinator`, `Frame`, `Host`, `Runtime`:
app window orchestration, content ownership, frame driving, frame transfer,
and per-window runtime state.
- `editor/app/Platform/Win32`: native window, message dispatch, input,
lifecycle, chrome, HWND ownership, and native host adapter behavior.
- `editor/app/Rendering`: editor rendering host and D3D12 integration.
The semantic dependency direction should remain:
```text
XCEditor framework
<- editor app semantics
<- app windowing runtime
<- Win32 host / rendering host
```
## Startup Flow
The application starts through:
```text
app/main.cpp
-> RunXCUIEditorApp
-> Application::Run
-> Application::Initialize
```
The primary workspace window is initialized through:
```text
EditorContext::BuildWorkspaceController()
-> EditorWindowSystem::BootstrapPrimaryWindow(...)
-> EditorWindowManager::CreateWorkspaceWindow(...)
-> EditorWindowContentFactory::CreateWorkspaceContentController(...)
-> EditorWindowRuntimeController(EditorContext, contentController)
-> EditorWindowHostRuntime::CreateHostWindow(runtimeController, ...)
```
Keep authoritative window bootstrap in `EditorWindowSystem`; do not move it
back into the Win32 host.
## Frame Runtime Ownership
`EditorWindowManager` owns steady-state frame iteration and the immediate-frame
callbacks used by native paint, resize, maximize/restore, and chrome actions.
The Win32 host may request a frame through `EditorWindowHostCoordinator`, but it
must not own the editor frame loop or fetch `EditorContext`.
`EditorWindowRuntimeController` lives under `editor/app/Windowing/Runtime`.
App windowing creates it from `EditorContext` plus a workspace or utility
content controller, then passes it to the native host. The Win32 host stores and
calls the runtime controller, but it does not create content controllers and it
does not receive `EditorContext`.
The host contract direction is:
```text
EditorWindowManager / coordinators
-> create EditorWindowRuntimeController
-> EditorWindowHostRuntime::CreateHostWindow(runtimeController, ...)
-> Win32 EditorWindow owns HWND and delegates frame work to the runtime
```
## Window Authority Model
`EditorWindowSystem` owns the authoritative `UIEditorWindowWorkspaceSet` through
`EditorWindowWorkspaceStore`.
Normal per-frame workspace edits now use direct writes into that authoritative
state:
```text
EditorWorkspaceWindowContentController::UpdateAndAppend(...)
-> EditorWindowSystem::TryBuildLiveWindowWorkspaceController(windowId, ...)
-> UIEditorWorkspaceController::BindToState(...)
-> workspace operations mutate the bound authoritative workspace/session
```
The old normal path is obsolete:
```text
snapshot diff
-> workspaceMutation frame request
-> coordinator
-> synchronization plan
-> commit
```
Do not reintroduce `workspaceMutation` as the steady-state route for ordinary
layout, tab, visibility, or active-panel edits inside an existing workspace
window.
Cross-window create, update, close, and destroyed-window reconciliation still
use the planner/coordinator flow:
```text
target UIEditorWindowWorkspaceSet
-> EditorWindowSystem::BuildPlanForWindowSet(...)
-> EditorWindowWorkspaceCoordinator::ApplySynchronizationPlan(...)
-> EditorWindowSystem::CommitSynchronizationPlan(...)
```
`EditorWindowWorkspaceCoordinator::RefreshWindowPresentation(...)` refreshes
each workspace window projection from authoritative state. Presentation should
follow authority; it should not become a second source of truth.
## Frame Transfer Requests
`EditorWindowFrameTransferRequests` is only for host-side or cross-window side
effects that cannot be represented as direct in-window workspace edits.
Current request fields are:
- `workspace.beginGlobalTabDrag`
- `workspace.detachPanel`
- `utility.openUtilityWindow`
There is no `workspace.workspaceMutation` field in the current frame transfer
contract. Treat any doc, comment, or test name that says otherwise as stale.
## Window Categories
Workspace and utility windows share the native host path but use distinct
content controllers.
- Workspace windows use `EditorWorkspaceWindowContentController`.
- Utility windows use `EditorUtilityWindowContentController`.
- App windowing creates content through `EditorWindowContentFactory`.
- App windowing wraps content in `EditorWindowRuntimeController`.
- The concrete host validates `EditorWindowContentCapabilities` through the
runtime controller when a native host window is created.
Utility windows are descriptor driven through `EditorUtilityWindowDescriptor`,
`EditorUtilityWindowRegistry`, and `CreateEditorUtilityWindowPanel(...)`.
Register new utility windows there rather than hard-coding them in Win32 host
logic.
## Modification Rules
- First decide whether the change belongs to XCEditor framework, app semantics,
app windowing, Win32 host, or rendering host.
- Keep public framework headers free of `App::*`, Win32, and D3D12 host types.
- Do not add `editor/app` as an include directory for `XCUIEditorLib`.
App-only services must cross into framework code through framework-owned
interfaces and app-side adapters.
- Do not compile `${XCUI_EDITOR_SHARED_SOURCES}` directly into `XCUIEditorApp`;
the executable should consume shared framework code through `XCUIEditorLib`.
- Use `editor/include/XCEditor/Windowing` and `editor/src/Windowing` for
generic window authority, synchronization, validation, and presentation
projection.
- Use `editor/app/Windowing` for app-specific window runtime semantics,
content, frame transfer, host contracts, runtime controllers, and
coordinators.
- Use `editor/app/Platform/Win32` only for native host behavior and message
integration. Win32 code may request frames through the host coordinator; it
must not own the editor frame loop or expose `EditorContext`.
- Do not let `editor/app/Windowing` include `Platform/Win32` headers.
- Keep concrete rendering details inside `editor/app/Windowing/Runtime` or
`editor/app/Rendering`; do not spread D3D12 host types into content,
coordinator, frame-transfer, or public host-contract headers.
- Do not let Win32 host code create workspace or utility content directly.
It should receive an `EditorWindowRuntimeController` created by app windowing.
- Use direct authoritative workspace binding for ordinary in-window workspace
mutations.
- Use synchronization plans for operations that create, close, replace, or
reconcile workspace windows.
- Use frame transfer requests only for host-side or cross-window effects.
## Current Architecture Debt
The highest-value windowing boundary has been hardened: `XCUIEditorLib` now
owns the reusable window authority, synchronization planner, workspace store,
and presentation projection under `XCEditor/Windowing`. `XCUIEditorAppWindowing`
owns the app-internal content controller factory, runtime controllers,
coordinators, frame transfer flow, host contracts, and manager. The Win32 host
is the concrete native adapter and creates native host windows from runtime
controllers supplied by app windowing.
The framework/app compile boundary is also enforced. `XCUIEditorLib` does not
receive `editor/app` includes, and `XCUIEditorApp` does not directly compile
the shared framework sources. When framework UI code needs app-provided data
such as loaded icons, expose a framework-owned interface and adapt it in app
composition code.
The main windowing debt that was previously in the Win32 host has been cut:
frame iteration, immediate frame driving, content-controller construction, and
`EditorContext` ownership now live in app windowing. `EditorWindowHostCoordinator`
does not expose `GetEditorContext()`, and the native host receives an
`EditorWindowRuntimeController` rather than raw workspace/utility content.
The remaining promotion debt is the app runtime surface itself.
`XCUIEditorAppWindowing` is still app-internal and may depend on app semantics
such as `EditorContext`, `EditorShellRuntime`, utility window descriptors,
product-specific content, and the current D3D12 window render loop. Do not
promote host interfaces, frame transfer, runtime controllers, or content
controllers to `XCEditor` until they are generic enough to expose. Do not move
frame ownership back into `editor/app/Platform/Win32`.
## Validation
Default editor validation is only the app build plus the 12-second smoke run:
```powershell
cmake --build build --config Debug --target editor_ui_smoke_targets
build\tests\UI\Editor\smoke\Debug\editor_ui_smoke_runner.exe build\editor\Debug\XCEngine.exe
```
Do not run `editor_windowing_phase1_tests`, `editor_ui_tests`, or broader test
targets by default. Run them only when the user explicitly asks for them or a
separate targeted change makes them necessary.
The runner sets `XCUIEDITOR_SMOKE_TEST_DURATION_SECONDS=12`, waits for the
editor to launch, lets the app auto-exit, and treats a clean editor exit as
success.
## Recommended Reading
Start with these files for editor/windowing work:
- `editor/CMakeLists.txt`
- `editor/app/Bootstrap/Application.*`
- `editor/app/Composition/EditorContext.*`
- `editor/app/Composition/EditorShellRuntime.*`
- `editor/include/XCEditor/Workspace/UIEditorWorkspaceController.h`
- `editor/src/Workspace/UIEditorWorkspaceController.cpp`
- `editor/include/XCEditor/Windowing/System/EditorWindowSystem.h`
- `editor/include/XCEditor/Windowing/System/EditorWindowSynchronizationPlan.h`
- `editor/include/XCEditor/Windowing/System/EditorWindowSynchronizationPlanner.h`
- `editor/include/XCEditor/Windowing/Presentation/EditorWorkspaceWindowProjection.h`
- `editor/include/XCEditor/Windowing/Presentation/EditorWindowPresentationPolicy.h`
- `editor/src/Windowing/System/EditorWindowSystem.cpp`
- `editor/src/Windowing/System/EditorWindowWorkspaceStore.*`
- `editor/src/Windowing/System/EditorWindowSynchronizationPlanner.cpp`
- `editor/src/Windowing/Presentation/EditorWindowPresentationPolicy.cpp`
- `editor/app/Windowing/Host/EditorWindowHostInterfaces.h`
- `editor/app/Windowing/Content/EditorWindowContentController.h`
- `editor/app/Windowing/Content/EditorWindowContentFactory.*`
- `editor/app/Windowing/Content/EditorWorkspaceWindowContentController.*`
- `editor/app/Windowing/Frame/EditorWindowFrameOrchestrator.*`
- `editor/app/Windowing/Frame/EditorWindowTransferRequests.h`
- `editor/app/Windowing/Runtime/EditorWindowRuntimeController.*`
- `editor/app/Windowing/Runtime/EditorWindowScreenshotController.*`
- `editor/app/Windowing/Coordinator/EditorWindowWorkspaceCoordinator.*`
- `editor/app/Windowing/Coordinator/EditorUtilityWindowCoordinator.*`
- `editor/app/Windowing/EditorWindowManager.*`
- `editor/app/Platform/Win32/Windowing/EditorWindow.*`
- `editor/app/Platform/Win32/Windowing/EditorWindowHostRuntime.*`
- `editor/app/Platform/Win32/Windowing/EditorWindowMessageDispatcher.*`
- `tests/UI/Editor/smoke/CMakeLists.txt`
## Recent Cuts
- The framework-to-app icon dependency is sealed: `UIEditorShellCompose` uses
the framework-owned `UIEditorShellIconResolver`, and the app adapts
`BuiltInIcons` in `EditorShellDrawComposer`.
- The framework/app compile boundary is sealed: `XCUIEditorLib` does not receive
`editor/app` includes, `XCUIEditorApp` does not directly compile
`${XCUI_EDITOR_SHARED_SOURCES}`, and the app consumes framework code through
`XCUIEditorLib`.
- The reusable window authority core lives in the framework:
`EditorWindowSystem`, synchronization planning, workspace store, and
presentation projection are under `editor/include/XCEditor/Windowing` and
`editor/src/Windowing`.
- App windowing owns the runtime cut: content controllers,
`EditorWindowRuntimeController`, frame driving, frame transfer, host
contracts, and `EditorWindowManager` are under `editor/app/Windowing`; Win32
remains the native adapter and no longer exposes `EditorContext`.
- Default validation remains the editor app build plus the 12-second smoke run;
run broader windowing/unit targets only for targeted coverage.