event bus start: code, PRs e specs
This commit is contained in:
parent
a16d63cfb5
commit
e7670b5474
@ -0,0 +1,124 @@
|
|||||||
|
# PR-07a Assets Event Topology and Lifecycle Foundation
|
||||||
|
|
||||||
|
Domain owner: `docs/studio`
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
Refactor the `Assets` workspace foundation away from a monolithic `AssetWorkspace` that redraws major regions after local changes.
|
||||||
|
|
||||||
|
This slice establishes the corrective architectural direction for the Studio as a whole.
|
||||||
|
|
||||||
|
`Assets` is the first consumer and proving ground, but the target is not an `Assets`-only pattern.
|
||||||
|
|
||||||
|
The Studio-standard direction is:
|
||||||
|
|
||||||
|
- lifecycle-aware UI components;
|
||||||
|
- typed workspace events as the update mechanism;
|
||||||
|
- component-scoped subscriptions;
|
||||||
|
- explicit separation between structural workspace sync and local UI projection updates;
|
||||||
|
- reusable workspace framework pieces that other Studio workspaces must consume instead of inventing their own refresh-heavy flow.
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Create the event-driven workspace foundation required for all later `Assets` refactor slices and establish it as the correct Studio-wide pattern for workspace implementation.
|
||||||
|
|
||||||
|
After this PR:
|
||||||
|
|
||||||
|
- the workspace root coordinates composition instead of owning every region render path;
|
||||||
|
- navigator, row, details, and details-internal controls can subscribe independently;
|
||||||
|
- the workspace event bus becomes the primary update path for UI change propagation;
|
||||||
|
- `refresh()` is no longer the default answer for local state changes.
|
||||||
|
- the extracted lifecycle/event-driven primitives are designed for reuse by non-`Assets` workspaces.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- [`../specs/4. Assets Workspace Specification.md`](../specs/4.%20Assets%20Workspace%20Specification.md)
|
||||||
|
- [`./PR-05a-assets-workspace-foundation-and-service-state.md`](./PR-05a-assets-workspace-foundation-and-service-state.md)
|
||||||
|
- existing `StudioWorkspaceEventBus`
|
||||||
|
- existing `StudioControlLifecycle` support
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- define the target event topology for the `Assets` workspace
|
||||||
|
- define the target event topology as the canonical Studio workspace model
|
||||||
|
- split workspace responsibilities between composition, state coordination, and component rendering
|
||||||
|
- extract reusable workspace framework primitives where the abstraction is already justified by the `Assets` refactor
|
||||||
|
- introduce lifecycle-aware component boundaries for:
|
||||||
|
- navigator host
|
||||||
|
- asset-list host
|
||||||
|
- asset-row item
|
||||||
|
- details host
|
||||||
|
- details-local sections/forms
|
||||||
|
- introduce reusable patterns or base components for:
|
||||||
|
- workspace composition roots
|
||||||
|
- lifecycle-managed event subscribers
|
||||||
|
- projection host controls
|
||||||
|
- structural-sync versus local-patch orchestration
|
||||||
|
- define typed events for:
|
||||||
|
- structural snapshot changes
|
||||||
|
- projection changes
|
||||||
|
- selection changes
|
||||||
|
- selected-asset details lifecycle
|
||||||
|
- local patch propagation
|
||||||
|
- demote global redraw events to transitional or removable status
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- no final navigator visuals redesign in this slice
|
||||||
|
- no final details-panel redesign in this slice
|
||||||
|
- no mutation-semantics redesign beyond event routing needs
|
||||||
|
- no broad shell-level event-system rewrite outside `Assets`
|
||||||
|
- no fake generalization disconnected from concrete `Assets` usage
|
||||||
|
|
||||||
|
## Execution Method
|
||||||
|
|
||||||
|
1. Define the component tree and ownership model.
|
||||||
|
The workspace root should compose controls and services, not render every region inline.
|
||||||
|
|
||||||
|
2. Introduce event contracts for the asset workspace projection model.
|
||||||
|
The baseline contract should distinguish:
|
||||||
|
- structural workspace sync events
|
||||||
|
- navigator projection events
|
||||||
|
- selected-asset details events
|
||||||
|
- per-asset patch events
|
||||||
|
|
||||||
|
3. Convert `AssetWorkspace` into a composition root plus orchestration layer.
|
||||||
|
It may still coordinate service calls, but rendering responsibilities should move into dedicated controls.
|
||||||
|
|
||||||
|
4. Extract reusable workspace-framework pieces while doing the refactor.
|
||||||
|
`Assets` should consume the same primitives that future Studio workspaces are expected to consume.
|
||||||
|
|
||||||
|
5. Require lifecycle installation for event-consuming controls.
|
||||||
|
Every component that subscribes to workspace events must implement `StudioControlLifecycle` and subscribe only while attached to the scene.
|
||||||
|
|
||||||
|
6. Mark the existing redraw-request pattern as transitional.
|
||||||
|
`StudioAssetsNavigatorRedrawRequestedEvent` and `StudioAssetsDetailsRedrawRequestedEvent` should no longer be the target architecture.
|
||||||
|
|
||||||
|
7. Propagate the rule to Studio documentation.
|
||||||
|
The resulting plans/spec updates should make clear that future workspaces are expected to build on this framework instead of introducing workspace-local refresh architecture.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- the refactor has a clear component topology instead of one render-heavy workspace class
|
||||||
|
- lifecycle-aware controls own their own subscriptions
|
||||||
|
- the workspace event bus carries typed update events that components can consume independently
|
||||||
|
- local UI changes can be expressed without a full workspace refresh path
|
||||||
|
- the old redraw-request events are either removed or isolated behind temporary compatibility adapters
|
||||||
|
- the extracted primitives are reusable by other Studio workspaces
|
||||||
|
- the plan explicitly establishes this direction as the Studio-standard workspace architecture
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- unit tests for event routing between composition root and child controls
|
||||||
|
- unit tests for lifecycle subscribe/unsubscribe behavior on workspace controls
|
||||||
|
- smoke validation that mounting and unmounting the workspace does not leak subscriptions
|
||||||
|
|
||||||
|
## Affected Artifacts
|
||||||
|
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetWorkspace.java`
|
||||||
|
- new reusable workspace-framework classes under `prometeu-studio/src/main/java/p/studio/...`
|
||||||
|
- new `Assets` workspace controls under `prometeu-studio/src/main/java/p/studio/workspaces/assets/...`
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/events/...`
|
||||||
|
- `docs/studio/specs/1. Studio Shell and Workspace Layout Specification.md`
|
||||||
|
- `docs/studio/specs/3. Studio Components Module Specification.md`
|
||||||
|
- tests for workspace event topology and lifecycle behavior
|
||||||
@ -0,0 +1,75 @@
|
|||||||
|
# PR-07b Asset Navigator and Row Subscriptions
|
||||||
|
|
||||||
|
Domain owner: `docs/studio`
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
Refactor the navigator side of the `Assets` workspace so the list and each asset row update through event subscriptions instead of workspace-wide rerendering.
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Make navigator behavior event-directed and component-local.
|
||||||
|
|
||||||
|
After this PR:
|
||||||
|
|
||||||
|
- the navigator list subscribes to projection changes it actually needs;
|
||||||
|
- each asset row subscribes to row-scoped summary/selection updates;
|
||||||
|
- search and filters change the navigator projection without rebuilding the whole workspace;
|
||||||
|
- selection styling and row patching happen without forcing a full list refresh from the workspace root.
|
||||||
|
- the navigator and row patterns are implemented in a way that other Studio workspaces can reuse for list/detail navigation surfaces.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- [`./PR-07a-assets-event-topology-and-lifecycle-foundation.md`](./PR-07a-assets-event-topology-and-lifecycle-foundation.md)
|
||||||
|
- [`../specs/4. Assets Workspace Specification.md`](../specs/4.%20Assets%20Workspace%20Specification.md)
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- extract the navigator into dedicated controls
|
||||||
|
- separate navigator projection calculation from visual control ownership
|
||||||
|
- introduce row-scoped subscriptions and row identity handling
|
||||||
|
- keep reusable list/projection primitives outside `Assets`-only naming where they are genuinely cross-workspace
|
||||||
|
- publish projection updates for:
|
||||||
|
- search changes
|
||||||
|
- filter changes
|
||||||
|
- structural asset collection changes
|
||||||
|
- per-asset summary patches
|
||||||
|
- selection changes
|
||||||
|
- remove direct row bookkeeping from the workspace root where possible
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- no details-panel refactor in this slice
|
||||||
|
- no final mutation confirmation flow refactor in this slice
|
||||||
|
- no broad service-layer redesign beyond what navigator subscriptions require
|
||||||
|
|
||||||
|
## Execution Method
|
||||||
|
|
||||||
|
1. Introduce an `AssetNavigatorControl` or equivalent host with lifecycle-managed subscriptions.
|
||||||
|
2. Extract row rendering into an `AssetRowControl` or equivalent lifecycle-managed component.
|
||||||
|
3. Move search/filter handling to event publication plus projection recalculation.
|
||||||
|
4. Publish selection updates as typed events that row controls can consume directly.
|
||||||
|
5. Replace root-owned row maps and manual selection restyling with row-scoped update flow.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- asset rows are no longer rebuilt by default on every local navigator change
|
||||||
|
- search and filter changes update the navigator projection only
|
||||||
|
- selecting an asset updates only the controls that depend on selection
|
||||||
|
- patching one asset summary updates the affected row without requiring a full workspace reload
|
||||||
|
- navigator controls subscribe and unsubscribe through the lifecycle support
|
||||||
|
- reusable navigator/list subscription patterns are left available for future Studio workspaces
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- unit tests for navigator projection event flow
|
||||||
|
- unit tests for row identity stability across patches
|
||||||
|
- unit tests for selection update behavior without full projection rebuild
|
||||||
|
- UI smoke validation for search, filters, and selection transitions
|
||||||
|
|
||||||
|
## Affected Artifacts
|
||||||
|
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetWorkspace.java`
|
||||||
|
- new navigator and row controls under `prometeu-studio/src/main/java/p/studio/workspaces/assets/...`
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/events/...`
|
||||||
|
- tests for navigator projection and row update behavior
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
# PR-07c Asset Details and Form Lifecycle
|
||||||
|
|
||||||
|
Domain owner: `docs/studio`
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
Refactor the selected-asset details side so the details host and its internal sections/forms subscribe only to the state they need.
|
||||||
|
|
||||||
|
This slice explicitly covers the problem called out in the current direction:
|
||||||
|
|
||||||
|
- the selected-asset details host must not own all redraws;
|
||||||
|
- each internal section/form must update from events instead of full details rebuilds.
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Make the details area componentized, lifecycle-aware, and event-driven.
|
||||||
|
|
||||||
|
After this PR:
|
||||||
|
|
||||||
|
- summary/actions, runtime contract, inputs/preview, diagnostics, and mutation-preview sections can update independently;
|
||||||
|
- details load state, ready state, and error state are event-driven;
|
||||||
|
- local form interactions such as preload toggles or preview selection do not rebuild unrelated details content;
|
||||||
|
- internal controls subscribe only while mounted.
|
||||||
|
- the details-section and form-subscription patterns become reusable Studio workspace primitives where appropriate.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- [`./PR-07a-assets-event-topology-and-lifecycle-foundation.md`](./PR-07a-assets-event-topology-and-lifecycle-foundation.md)
|
||||||
|
- [`./PR-05c-selected-asset-details-contract-and-preview.md`](./PR-05c-selected-asset-details-contract-and-preview.md)
|
||||||
|
- [`./PR-05e-assets-staged-mutations-preview-and-apply.md`](./PR-05e-assets-staged-mutations-preview-and-apply.md)
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- extract the details host into dedicated controls
|
||||||
|
- split details content into lifecycle-aware sections
|
||||||
|
- extract reusable section-host and form-event patterns when they are not asset-specific
|
||||||
|
- define details events for:
|
||||||
|
- details loading started
|
||||||
|
- details ready
|
||||||
|
- details failed
|
||||||
|
- local summary patch applied
|
||||||
|
- preview input changed
|
||||||
|
- preview zoom changed
|
||||||
|
- mutation preview state changed
|
||||||
|
- route details-local form actions through the workspace bus instead of root-owned imperative redraw
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- no navigator refactor in this slice
|
||||||
|
- no shell activity redesign in this slice
|
||||||
|
- no cross-workspace form framework extraction unless needed by the `Assets` details controls
|
||||||
|
|
||||||
|
## Execution Method
|
||||||
|
|
||||||
|
1. Introduce a dedicated details host control with lifecycle-managed subscriptions.
|
||||||
|
2. Extract stable details sections into separate controls.
|
||||||
|
3. Move details load and error transitions to typed events.
|
||||||
|
4. Route details-local interactions through narrow events and local state holders.
|
||||||
|
5. Remove root-level details reconstruction for interactions that affect only one section.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- the selected-asset details view is no longer rebuilt as one large region for section-local changes
|
||||||
|
- forms and preview controls update independently
|
||||||
|
- details-ready and details-error transitions are observable through typed events
|
||||||
|
- details-local subscriptions are owned by the mounted controls, not the workspace root
|
||||||
|
- changing one local control does not force unrelated details sections to rerender
|
||||||
|
- reusable details/form lifecycle patterns are available for future Studio workspaces
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- unit tests for details lifecycle event flow
|
||||||
|
- unit tests for section-local updates
|
||||||
|
- unit tests for preload-toggle or equivalent form patch behavior
|
||||||
|
- UI smoke validation for selection, details loading, preview interaction, and diagnostics visibility
|
||||||
|
|
||||||
|
## Affected Artifacts
|
||||||
|
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetWorkspace.java`
|
||||||
|
- new details controls under `prometeu-studio/src/main/java/p/studio/workspaces/assets/...`
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/events/...`
|
||||||
|
- tests for details load state and section-local updates
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
# PR-07d Asset Mutation and Structural Sync Orchestration
|
||||||
|
|
||||||
|
Domain owner: `docs/studio`
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
Replace the current mutation flow that falls back to `refresh()` after many operations with an event-directed orchestration model.
|
||||||
|
|
||||||
|
The key rule is:
|
||||||
|
|
||||||
|
- local patches stay local;
|
||||||
|
- only structural workspace changes trigger structural sync;
|
||||||
|
- structural sync is explicit and typed, not an incidental rerender path.
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Make asset operations compatible with the event-driven component model.
|
||||||
|
|
||||||
|
After this PR:
|
||||||
|
|
||||||
|
- direct mutations publish patch or structural-sync events according to their actual impact;
|
||||||
|
- row and details controls react to targeted operation results when possible;
|
||||||
|
- create/remove/relocate or other structural operations request a workspace sync explicitly;
|
||||||
|
- the workspace no longer treats every successful operation as a reason to reload everything.
|
||||||
|
- the local-patch versus structural-sync rule is available as a reusable Studio workspace orchestration rule.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- [`./PR-07a-assets-event-topology-and-lifecycle-foundation.md`](./PR-07a-assets-event-topology-and-lifecycle-foundation.md)
|
||||||
|
- [`./PR-07b-asset-navigator-and-row-subscriptions.md`](./PR-07b-asset-navigator-and-row-subscriptions.md)
|
||||||
|
- [`./PR-07c-asset-details-and-form-lifecycle.md`](./PR-07c-asset-details-and-form-lifecycle.md)
|
||||||
|
- [`./PR-05d-assets-activity-progress-and-logs-integration.md`](./PR-05d-assets-activity-progress-and-logs-integration.md)
|
||||||
|
- [`./PR-05e-assets-staged-mutations-preview-and-apply.md`](./PR-05e-assets-staged-mutations-preview-and-apply.md)
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- classify operations by update impact:
|
||||||
|
- local summary patch
|
||||||
|
- details patch
|
||||||
|
- structural workspace sync
|
||||||
|
- failure retention/reporting
|
||||||
|
- extract reusable orchestration helpers or contracts where the distinction is cross-workspace rather than asset-specific
|
||||||
|
- route mutation preview/apply results through typed events
|
||||||
|
- replace generic `refresh()` fallbacks for non-structural success paths
|
||||||
|
- keep progress/log/activity integration aligned with the new orchestration path
|
||||||
|
- make structural sync requests explicit and testable
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- no redesign of packer service semantics
|
||||||
|
- no generic Studio-wide command bus abstraction in this slice
|
||||||
|
- no persistence work unrelated to asset-workspace orchestration
|
||||||
|
|
||||||
|
## Execution Method
|
||||||
|
|
||||||
|
1. Define operation-result events and structural-sync request events.
|
||||||
|
2. Map each mutation action to its update strategy.
|
||||||
|
3. Update mutation flows to publish events instead of directly forcing global workspace refresh.
|
||||||
|
4. Keep structural reload only for actions that truly change collection shape or asset identity.
|
||||||
|
5. Wire progress, logs, and activity to the new operation flow without reintroducing redraw coupling.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- include/exclude, preload, and similar local operations can update via targeted events
|
||||||
|
- create/register/remove/relocate use explicit structural-sync flow only when required
|
||||||
|
- mutation preview and apply flows do not directly rebuild navigator and details by default
|
||||||
|
- activity/log/progress signals remain correct under the new orchestration
|
||||||
|
- the remaining structural sync path is narrow, explicit, and justified by actual data-shape changes
|
||||||
|
- the orchestration rule is documented as reusable Studio behavior, not an `Assets` exception
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- unit tests for operation-to-update-strategy mapping
|
||||||
|
- unit tests for structural-sync event publication
|
||||||
|
- unit tests for targeted patch propagation after successful operations
|
||||||
|
- UI smoke validation for register/include/exclude/relocate/remove flows
|
||||||
|
|
||||||
|
## Affected Artifacts
|
||||||
|
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetWorkspace.java`
|
||||||
|
- mutation and orchestration classes under `prometeu-studio/src/main/java/p/studio/workspaces/assets/...`
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/events/...`
|
||||||
|
- tests for mutation orchestration and structural sync behavior
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
# PR-07e Assets Refactor Cleanup and Regression Coverage
|
||||||
|
|
||||||
|
Domain owner: `docs/studio`
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
Consolidate the refactor by removing obsolete redraw-heavy code paths, tightening naming, and locking the event-driven behavior with tests.
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Finish the corrective refactor so the codebase does not drift back toward monolithic render ownership.
|
||||||
|
|
||||||
|
After this PR:
|
||||||
|
|
||||||
|
- obsolete redraw-request and monolithic render helpers are removed;
|
||||||
|
- remaining component boundaries are clearer and easier to maintain;
|
||||||
|
- tests guard lifecycle, event routing, and selective-update behavior;
|
||||||
|
- the workspace code is organized around durable responsibilities rather than incremental leftovers.
|
||||||
|
- the resulting framework is left in a shape that other Studio workspaces can adopt directly.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- [`./PR-07a-assets-event-topology-and-lifecycle-foundation.md`](./PR-07a-assets-event-topology-and-lifecycle-foundation.md)
|
||||||
|
- [`./PR-07b-asset-navigator-and-row-subscriptions.md`](./PR-07b-asset-navigator-and-row-subscriptions.md)
|
||||||
|
- [`./PR-07c-asset-details-and-form-lifecycle.md`](./PR-07c-asset-details-and-form-lifecycle.md)
|
||||||
|
- [`./PR-07d-asset-mutation-and-structural-sync-orchestration.md`](./PR-07d-asset-mutation-and-structural-sync-orchestration.md)
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- remove dead or transitional redraw-oriented code
|
||||||
|
- rename classes/events where the old naming reflects the wrong direction
|
||||||
|
- tighten package structure for navigator, details, and orchestration code
|
||||||
|
- make reusable framework pieces visible and discoverable to future workspace implementations
|
||||||
|
- add regression coverage for:
|
||||||
|
- lifecycle subscription hygiene
|
||||||
|
- selection propagation
|
||||||
|
- row patch propagation
|
||||||
|
- details-local updates
|
||||||
|
- structural sync boundaries
|
||||||
|
- update Studio learn/spec material if the refactor exposes terminology drift
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- no new user-facing asset features in this slice
|
||||||
|
- no speculative refactor outside the `Assets` workspace and its direct event contracts
|
||||||
|
|
||||||
|
## Execution Method
|
||||||
|
|
||||||
|
1. Remove compatibility layers that existed only to bridge from the old refresh-heavy implementation.
|
||||||
|
2. Normalize class and event naming around lifecycle and event-driven ownership.
|
||||||
|
3. Reorganize tests to reflect component boundaries instead of one giant workspace class.
|
||||||
|
4. Update documentation to reflect the stabilized architecture.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- the refactored `Assets` workspace no longer depends on redraw-request events as a primary mechanism
|
||||||
|
- code ownership is split along navigator, details, and orchestration boundaries
|
||||||
|
- lifecycle and event-driven behavior are covered by focused tests
|
||||||
|
- the remaining `AssetWorkspace` root is materially smaller and primarily compositional
|
||||||
|
- the reusable framework surface is clear enough for other workspaces to consume instead of cloning `Assets` internals
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- full unit-test pass for asset-workspace packages
|
||||||
|
- targeted regression tests for mount/unmount lifecycle safety
|
||||||
|
- targeted regression tests proving that local updates stay local unless structural sync is requested
|
||||||
|
|
||||||
|
## Affected Artifacts
|
||||||
|
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/workspaces/assets/...`
|
||||||
|
- `prometeu-studio/src/main/java/p/studio/events/...`
|
||||||
|
- `prometeu-studio/src/test/java/p/studio/workspaces/assets/...`
|
||||||
|
- `docs/studio/specs/4. Assets Workspace Specification.md`
|
||||||
|
- `docs/studio/learn/mental-model-assets-workspace.md`
|
||||||
@ -43,8 +43,17 @@ The current Studio execution queue is:
|
|||||||
3. [`PR-05c-selected-asset-details-contract-and-preview.md`](./PR-05c-selected-asset-details-contract-and-preview.md)
|
3. [`PR-05c-selected-asset-details-contract-and-preview.md`](./PR-05c-selected-asset-details-contract-and-preview.md)
|
||||||
4. [`PR-05d-assets-activity-progress-and-logs-integration.md`](./PR-05d-assets-activity-progress-and-logs-integration.md)
|
4. [`PR-05d-assets-activity-progress-and-logs-integration.md`](./PR-05d-assets-activity-progress-and-logs-integration.md)
|
||||||
5. [`PR-05e-assets-staged-mutations-preview-and-apply.md`](./PR-05e-assets-staged-mutations-preview-and-apply.md)
|
5. [`PR-05e-assets-staged-mutations-preview-and-apply.md`](./PR-05e-assets-staged-mutations-preview-and-apply.md)
|
||||||
6. [`PR-06-project-scoped-studio-state-and-activity-persistence.md`](./PR-06-project-scoped-studio-state-and-activity-persistence.md)
|
6. [`PR-07a-assets-event-topology-and-lifecycle-foundation.md`](./PR-07a-assets-event-topology-and-lifecycle-foundation.md)
|
||||||
|
7. [`PR-07b-asset-navigator-and-row-subscriptions.md`](./PR-07b-asset-navigator-and-row-subscriptions.md)
|
||||||
|
8. [`PR-07c-asset-details-and-form-lifecycle.md`](./PR-07c-asset-details-and-form-lifecycle.md)
|
||||||
|
9. [`PR-07d-asset-mutation-and-structural-sync-orchestration.md`](./PR-07d-asset-mutation-and-structural-sync-orchestration.md)
|
||||||
|
10. [`PR-07e-assets-refactor-cleanup-and-regression-coverage.md`](./PR-07e-assets-refactor-cleanup-and-regression-coverage.md)
|
||||||
|
11. [`PR-06-project-scoped-studio-state-and-activity-persistence.md`](./PR-06-project-scoped-studio-state-and-activity-persistence.md)
|
||||||
|
|
||||||
|
The `PR-07` family is a corrective refactor pass for the current `Assets` implementation.
|
||||||
|
It exists to replace the refresh-heavy direction with lifecycle-managed, event-driven ownership.
|
||||||
|
It also establishes the intended Studio-wide workspace framework, with `Assets` as the first consumer and proof point.
|
||||||
|
|
||||||
Recommended execution order:
|
Recommended execution order:
|
||||||
|
|
||||||
`PR-05a -> PR-05b -> PR-05c -> PR-05d -> PR-05e -> PR-06`
|
`PR-05a -> PR-05b -> PR-05c -> PR-05d -> PR-05e -> PR-07a -> PR-07b -> PR-07c -> PR-07d -> PR-07e -> PR-06`
|
||||||
|
|||||||
@ -23,6 +23,8 @@ This specification consolidates the accepted Studio shell decision into normativ
|
|||||||
4. The main shell must expose a center workspace host.
|
4. The main shell must expose a center workspace host.
|
||||||
5. The main shell must expose a right global utility panel.
|
5. The main shell must expose a right global utility panel.
|
||||||
6. The main shell must expose an always-visible run surface in the top-right area.
|
6. The main shell must expose an always-visible run surface in the top-right area.
|
||||||
|
7. Workspaces mounted in the shell must follow the Studio event-driven workspace framework.
|
||||||
|
8. The shell must preserve a clear path for reusable workspace framework primitives shared across workspaces.
|
||||||
|
|
||||||
## Project Entry
|
## Project Entry
|
||||||
|
|
||||||
@ -48,6 +50,20 @@ The baseline workspace set includes:
|
|||||||
|
|
||||||
`BuilderWorkspace` is transitional and must not be treated as a long-term architectural reference for the final shipping surface.
|
`BuilderWorkspace` is transitional and must not be treated as a long-term architectural reference for the final shipping surface.
|
||||||
|
|
||||||
|
## Workspace Architecture Model
|
||||||
|
|
||||||
|
The shell-level workspace host exists to mount independently authored workspaces under one Studio contract.
|
||||||
|
|
||||||
|
Baseline workspace architecture rules are:
|
||||||
|
|
||||||
|
- each workspace should have a composition root that mounts the workspace into the shell;
|
||||||
|
- a workspace composition root should coordinate workspace services, state orchestration, and structural sync, but it should not own every local render path directly;
|
||||||
|
- event-observing workspace controls must be lifecycle-managed;
|
||||||
|
- workspace-local updates should flow through the Studio event system rather than ad hoc imperative redraw chains;
|
||||||
|
- future workspaces should consume shared Studio workspace framework primitives where those primitives already cover the needed behavior.
|
||||||
|
|
||||||
|
The shell must not encourage a workspace model where every interaction falls back to whole-workspace refresh.
|
||||||
|
|
||||||
## Right Utility Panel
|
## Right Utility Panel
|
||||||
|
|
||||||
The right-side global utility surface is tab-based.
|
The right-side global utility surface is tab-based.
|
||||||
@ -95,6 +111,7 @@ Rules:
|
|||||||
- defining the full internal layout of every workspace
|
- defining the full internal layout of every workspace
|
||||||
- defining the final future tab set of the right utility panel
|
- defining the final future tab set of the right utility panel
|
||||||
- defining the detailed activity rendering model
|
- defining the detailed activity rendering model
|
||||||
|
- defining every reusable workspace primitive in this document
|
||||||
|
|
||||||
## Exit Criteria
|
## Exit Criteria
|
||||||
|
|
||||||
@ -102,4 +119,5 @@ This specification is complete enough when:
|
|||||||
|
|
||||||
- project entry and shell regions are unambiguous;
|
- project entry and shell regions are unambiguous;
|
||||||
- workspace and shell responsibilities are clearly separated;
|
- workspace and shell responsibilities are clearly separated;
|
||||||
|
- the shell-level expectations for workspace architecture are explicit;
|
||||||
- the baseline Studio frame is stable enough for implementation planning.
|
- the baseline Studio frame is stable enough for implementation planning.
|
||||||
|
|||||||
@ -23,6 +23,9 @@ This specification consolidates the accepted Studio UI foundations decision into
|
|||||||
3. The Studio event system must include one global bus and one bus per workspace.
|
3. The Studio event system must include one global bus and one bus per workspace.
|
||||||
4. Every event published on a workspace bus must also be published on the global bus automatically.
|
4. Every event published on a workspace bus must also be published on the global bus automatically.
|
||||||
5. Studio UI must preserve theme and i18n compatibility by construction.
|
5. Studio UI must preserve theme and i18n compatibility by construction.
|
||||||
|
6. Lifecycle-managed event subscription is the canonical way Studio controls observe external state.
|
||||||
|
7. The Studio must provide a reusable workspace framework for event-driven workspace composition.
|
||||||
|
8. Studio workspaces must consume that framework rather than invent refresh-heavy workspace-local patterns.
|
||||||
|
|
||||||
## Naming by Role
|
## Naming by Role
|
||||||
|
|
||||||
@ -118,6 +121,7 @@ Shared Studio foundations include:
|
|||||||
|
|
||||||
- shell-level UI conventions;
|
- shell-level UI conventions;
|
||||||
- event publication and observation infrastructure;
|
- event publication and observation infrastructure;
|
||||||
|
- reusable workspace framework primitives;
|
||||||
- reusable Studio control conventions.
|
- reusable Studio control conventions.
|
||||||
|
|
||||||
## Subscription Lifecycle
|
## Subscription Lifecycle
|
||||||
@ -135,6 +139,27 @@ This exists to make event wiring visible, testable, and safe under UI lifecycle
|
|||||||
|
|
||||||
The exact base class or interface shape is an implementation concern, but the lifecycle itself is normative.
|
The exact base class or interface shape is an implementation concern, but the lifecycle itself is normative.
|
||||||
|
|
||||||
|
## Canonical Workspace Framework
|
||||||
|
|
||||||
|
The Studio workspace model is not just a collection of unrelated screens.
|
||||||
|
|
||||||
|
Baseline workspace-framework rules are:
|
||||||
|
|
||||||
|
- a workspace should be built as a composition root plus lifecycle-managed child controls;
|
||||||
|
- child controls should subscribe only to the workspace events or external state they actually consume;
|
||||||
|
- local UI changes should be propagated through typed events and targeted patches where identity is preserved;
|
||||||
|
- structural synchronization should be explicit and separate from local patch propagation;
|
||||||
|
- whole-workspace refresh is a structural recovery path, not the default routine interaction path.
|
||||||
|
|
||||||
|
The baseline reusable workspace framework should cover at least:
|
||||||
|
|
||||||
|
- composition-root conventions;
|
||||||
|
- lifecycle-managed event-subscriber controls;
|
||||||
|
- projection-host patterns for list/tree/detail surfaces;
|
||||||
|
- local-patch versus structural-sync orchestration rules.
|
||||||
|
|
||||||
|
The first concrete proving ground for this framework may be a single workspace, but the resulting framework is normative for future Studio workspace implementation.
|
||||||
|
|
||||||
Shared Studio foundations do not include:
|
Shared Studio foundations do not include:
|
||||||
|
|
||||||
- domain-specific workspace business logic;
|
- domain-specific workspace business logic;
|
||||||
@ -146,6 +171,7 @@ Shared Studio foundations do not include:
|
|||||||
- defining the full event catalog in advance
|
- defining the full event catalog in advance
|
||||||
- defining every future workspace interaction
|
- defining every future workspace interaction
|
||||||
- defining the first concrete control implementation wave in detail
|
- defining the first concrete control implementation wave in detail
|
||||||
|
- allowing each workspace to define its own incompatible event/lifecycle model
|
||||||
|
|
||||||
## Exit Criteria
|
## Exit Criteria
|
||||||
|
|
||||||
@ -153,4 +179,5 @@ This specification is complete enough when:
|
|||||||
|
|
||||||
- naming rules are stable enough to guide new Studio code;
|
- naming rules are stable enough to guide new Studio code;
|
||||||
- the event topology is explicit;
|
- the event topology is explicit;
|
||||||
|
- the lifecycle and workspace-framework rules are explicit;
|
||||||
- theme and i18n constraints are unambiguous for shared Studio UI work.
|
- theme and i18n constraints are unambiguous for shared Studio UI work.
|
||||||
|
|||||||
@ -21,6 +21,7 @@ This specification consolidates the accepted Studio components module decision i
|
|||||||
3. A control enters the module when it is needed by the current Studio UI wave.
|
3. A control enters the module when it is needed by the current Studio UI wave.
|
||||||
4. Controls in the module wrap and shape the JavaFX controls the Studio actively uses, but do not clone the full JavaFX API.
|
4. Controls in the module wrap and shape the JavaFX controls the Studio actively uses, but do not clone the full JavaFX API.
|
||||||
5. Every admitted control must preserve theme and i18n compatibility.
|
5. Every admitted control must preserve theme and i18n compatibility.
|
||||||
|
6. Reusable event-driven workspace controls should be admitted when they are part of the active Studio workspace framework.
|
||||||
|
|
||||||
## Module Role
|
## Module Role
|
||||||
|
|
||||||
@ -28,7 +29,8 @@ This specification consolidates the accepted Studio components module decision i
|
|||||||
|
|
||||||
- the authoritative Studio control layer;
|
- the authoritative Studio control layer;
|
||||||
- the home of the Studio visual dialect;
|
- the home of the Studio visual dialect;
|
||||||
- the place where Studio-facing controls are shaped for Studio use.
|
- the place where Studio-facing controls are shaped for Studio use;
|
||||||
|
- the preferred home for reusable lifecycle-aware workspace controls when those controls are part of the visible Studio UI dialect.
|
||||||
|
|
||||||
`prometeu-studio-components` is not:
|
`prometeu-studio-components` is not:
|
||||||
|
|
||||||
@ -45,6 +47,7 @@ Rules:
|
|||||||
- immediate current Studio use is sufficient justification;
|
- immediate current Studio use is sufficient justification;
|
||||||
- a second use is not required when the control is clearly part of the shell or the current workspace surface;
|
- a second use is not required when the control is clearly part of the shell or the current workspace surface;
|
||||||
- no control should be added without an immediate Studio consumer.
|
- no control should be added without an immediate Studio consumer.
|
||||||
|
- a control needed first by one workspace may still be admitted when it is clearly intended as a reusable Studio workspace primitive.
|
||||||
|
|
||||||
## Studio Usage Rule
|
## Studio Usage Rule
|
||||||
|
|
||||||
@ -81,6 +84,25 @@ Rules:
|
|||||||
|
|
||||||
These hooks exist so event-driven controls remain predictable and safe to embed in shell and workspace UI.
|
These hooks exist so event-driven controls remain predictable and safe to embed in shell and workspace UI.
|
||||||
|
|
||||||
|
## Reusable Workspace Primitives
|
||||||
|
|
||||||
|
Some reusable controls are not shell-only and are not domain-specific either.
|
||||||
|
|
||||||
|
They may still belong in `prometeu-studio-components` when they are part of the visible Studio workspace framework.
|
||||||
|
|
||||||
|
Illustrative admissible examples include:
|
||||||
|
|
||||||
|
- projection hosts for list/tree/detail surfaces;
|
||||||
|
- lifecycle-managed row or item controls;
|
||||||
|
- reusable section or inspector surfaces;
|
||||||
|
- visible controls that encode local-patch versus structural-sync interaction patterns.
|
||||||
|
|
||||||
|
What does not belong here:
|
||||||
|
|
||||||
|
- workspace-specific orchestration logic;
|
||||||
|
- domain-specific service calls;
|
||||||
|
- hidden coordination code with no visible control responsibility.
|
||||||
|
|
||||||
## Typical Control Scope
|
## Typical Control Scope
|
||||||
|
|
||||||
Typical controls that belong in the module include:
|
Typical controls that belong in the module include:
|
||||||
@ -135,6 +157,7 @@ The module must remain:
|
|||||||
- defining the first concrete component set in full detail
|
- defining the first concrete component set in full detail
|
||||||
- defining final package layout exhaustively
|
- defining final package layout exhaustively
|
||||||
- mirroring the entire JavaFX control hierarchy
|
- mirroring the entire JavaFX control hierarchy
|
||||||
|
- storing domain-specific workspace orchestration code in the components module
|
||||||
|
|
||||||
## Exit Criteria
|
## Exit Criteria
|
||||||
|
|
||||||
@ -142,4 +165,5 @@ This specification is complete enough when:
|
|||||||
|
|
||||||
- the module role is unambiguous;
|
- the module role is unambiguous;
|
||||||
- admission rules are stable enough to guide implementation;
|
- admission rules are stable enough to guide implementation;
|
||||||
|
- reusable workspace-control admission is clear enough to prevent local reimplementation drift;
|
||||||
- the JavaFX wrapping boundary is clear enough to prevent drift.
|
- the JavaFX wrapping boundary is clear enough to prevent drift.
|
||||||
|
|||||||
@ -68,6 +68,14 @@ The workspace must help the user understand:
|
|||||||
- what the asset declares toward the runtime-facing contract;
|
- what the asset declares toward the runtime-facing contract;
|
||||||
- which operations are safe, staged, blocked, or destructive.
|
- which operations are safe, staged, blocked, or destructive.
|
||||||
|
|
||||||
|
The `Assets` workspace is also the first concrete consumer of the Studio event-driven workspace framework.
|
||||||
|
|
||||||
|
That means:
|
||||||
|
|
||||||
|
- `Assets` must prove the canonical Studio workspace architecture in real use;
|
||||||
|
- reusable lifecycle and projection patterns extracted here should be consumable by future Studio workspaces;
|
||||||
|
- `Assets` must not rely on a monolithic workspace root that redraws most of the UI after local interactions.
|
||||||
|
|
||||||
## Baseline Layout
|
## Baseline Layout
|
||||||
|
|
||||||
The baseline workspace layout is:
|
The baseline workspace layout is:
|
||||||
@ -80,6 +88,18 @@ The baseline workspace layout is:
|
|||||||
|
|
||||||
Filesystem structure may be visible and actionable as supporting context, but it is not the primary identity model of the workspace.
|
Filesystem structure may be visible and actionable as supporting context, but it is not the primary identity model of the workspace.
|
||||||
|
|
||||||
|
## Component Ownership Rules
|
||||||
|
|
||||||
|
The `Assets` workspace must be implemented as a composition root plus lifecycle-managed child controls.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- the workspace root should coordinate service calls, workspace state, and structural synchronization;
|
||||||
|
- the workspace root should not own the render and update path of every navigator row, details section, and form control directly;
|
||||||
|
- the navigator host, asset rows, selected-asset details host, and details-internal sections/forms should subscribe only to the events and state they consume;
|
||||||
|
- event-consuming controls must subscribe only while mounted in the UI tree;
|
||||||
|
- reusable framework primitives should be consumed where the Studio already provides them instead of duplicating local refresh-oriented mechanisms.
|
||||||
|
|
||||||
## Asset Navigator Rules
|
## Asset Navigator Rules
|
||||||
|
|
||||||
### Primary Navigation Unit
|
### Primary Navigation Unit
|
||||||
@ -133,6 +153,17 @@ Filesystem structure may be visible and actionable as supporting context, but it
|
|||||||
- If the selected asset is removed from the navigator, selection must clear explicitly.
|
- If the selected asset is removed from the navigator, selection must clear explicitly.
|
||||||
- Selection must never silently drift to another asset due to refresh ordering.
|
- Selection must never silently drift to another asset due to refresh ordering.
|
||||||
|
|
||||||
|
### Local Rendering Rules
|
||||||
|
|
||||||
|
- The workspace must use event-directed local redraws for routine interactions.
|
||||||
|
- The navigator host must react to projection-level events rather than whole-workspace rerender requests.
|
||||||
|
- Asset row items should react to row-scoped patch and selection events when identity is preserved.
|
||||||
|
- Selecting an asset must update navigator selection styling and the selected-asset details surface without rebuilding the entire workspace shell.
|
||||||
|
- Search and filter changes must redraw the navigator projection only.
|
||||||
|
- Details-local interactions such as preview input selection, preview zoom, or runtime-contract edits must redraw the selected-asset details surface only.
|
||||||
|
- Structural operations such as asset creation, removal, relocation, or full workspace reload may rebuild both navigator and details projections.
|
||||||
|
- Workspace-scoped progress and logging updates must not force navigator or selected-asset details redraw unless the underlying projection actually changed.
|
||||||
|
|
||||||
### State Rules
|
### State Rules
|
||||||
|
|
||||||
- The navigator must define explicit `loading assets` state.
|
- The navigator must define explicit `loading assets` state.
|
||||||
@ -158,6 +189,14 @@ The selected asset view must use this stable composition:
|
|||||||
|
|
||||||
This composition is stable across asset families.
|
This composition is stable across asset families.
|
||||||
|
|
||||||
|
The selected-asset details area must follow component-local ownership.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- the details host should react to details lifecycle events such as loading, ready, and error;
|
||||||
|
- details sections should update independently when only one section's projection changed;
|
||||||
|
- forms and preview-local controls should publish and consume narrow typed events rather than trigger full details rebuild by default.
|
||||||
|
|
||||||
### Summary
|
### Summary
|
||||||
|
|
||||||
- `Summary` must always be present for a selected asset.
|
- `Summary` must always be present for a selected asset.
|
||||||
@ -253,6 +292,14 @@ Inline staged panels are allowed for non-destructive blocked or inspect-only flo
|
|||||||
|
|
||||||
Modal review and confirmation is required for `Exclude From Build`, `Remove`, and relocation commits.
|
Modal review and confirmation is required for `Exclude From Build`, `Remove`, and relocation commits.
|
||||||
|
|
||||||
|
Routine state patches such as `preload`, `Include In Build`, and `Exclude From Build` must patch the affected asset projection locally when identity is preserved. Full workspace refresh is reserved for operations that change workspace structure or identity resolution.
|
||||||
|
|
||||||
|
Operation orchestration must follow this rule:
|
||||||
|
|
||||||
|
- local patch when stable identity and local projection are preserved;
|
||||||
|
- explicit structural sync when collection shape, identity, or projection membership changes;
|
||||||
|
- no routine mutation path should fall back to whole-workspace refresh merely for implementation convenience.
|
||||||
|
|
||||||
### Mutations Requiring Preview
|
### Mutations Requiring Preview
|
||||||
|
|
||||||
- `Exclude From Build`
|
- `Exclude From Build`
|
||||||
@ -295,7 +342,6 @@ This specification does not define:
|
|||||||
|
|
||||||
- the exact JavaFX component tree or control class structure of the `Assets` workspace;
|
- the exact JavaFX component tree or control class structure of the `Assets` workspace;
|
||||||
- final reusable component boundaries for all preview surfaces;
|
- final reusable component boundaries for all preview surfaces;
|
||||||
- cross-workspace reuse beyond the `Assets` workspace;
|
|
||||||
- future multi-select or bulk-edit UX beyond the currently defined staged batch summary rules.
|
- future multi-select or bulk-edit UX beyond the currently defined staged batch summary rules.
|
||||||
|
|
||||||
## Exit Criteria
|
## Exit Criteria
|
||||||
@ -307,4 +353,5 @@ This specification is satisfied when the Studio `Assets` workspace:
|
|||||||
- explains the selected asset through summary, contract, inputs, diagnostics, and actions;
|
- explains the selected asset through summary, contract, inputs, diagnostics, and actions;
|
||||||
- keeps activity, progress, and logs clearly separated;
|
- keeps activity, progress, and logs clearly separated;
|
||||||
- stages sensitive mutations through preview-first flows;
|
- stages sensitive mutations through preview-first flows;
|
||||||
|
- proves the Studio event-driven workspace framework through lifecycle-managed local ownership;
|
||||||
- and behaves as a didactic helper for the packer model.
|
- and behaves as a didactic helper for the packer model.
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
package p.studio.events;
|
||||||
|
|
||||||
|
import p.studio.projects.ProjectReference;
|
||||||
|
import p.studio.workspaces.assets.AssetWorkspaceAssetSummary;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record StudioAssetsAssetSummaryPatchedEvent(
|
||||||
|
ProjectReference project,
|
||||||
|
AssetWorkspaceAssetSummary summary) implements StudioEvent {
|
||||||
|
public StudioAssetsAssetSummaryPatchedEvent {
|
||||||
|
Objects.requireNonNull(project, "project");
|
||||||
|
Objects.requireNonNull(summary, "summary");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package p.studio.events;
|
||||||
|
|
||||||
|
import p.studio.projects.ProjectReference;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record StudioAssetsDetailsRedrawRequestedEvent(ProjectReference project) implements StudioEvent {
|
||||||
|
public StudioAssetsDetailsRedrawRequestedEvent {
|
||||||
|
Objects.requireNonNull(project, "project");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package p.studio.events;
|
||||||
|
|
||||||
|
import p.studio.projects.ProjectReference;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record StudioAssetsNavigatorRedrawRequestedEvent(ProjectReference project) implements StudioEvent {
|
||||||
|
public StudioAssetsNavigatorRedrawRequestedEvent {
|
||||||
|
Objects.requireNonNull(project, "project");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package p.studio.events;
|
||||||
|
|
||||||
|
import p.studio.projects.ProjectReference;
|
||||||
|
import p.studio.workspaces.assets.AssetWorkspaceSelectionKey;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record StudioAssetsWorkspaceSelectionRequestedEvent(
|
||||||
|
ProjectReference project,
|
||||||
|
AssetWorkspaceSelectionKey selectionKey) implements StudioEvent {
|
||||||
|
public StudioAssetsWorkspaceSelectionRequestedEvent {
|
||||||
|
Objects.requireNonNull(project, "project");
|
||||||
|
Objects.requireNonNull(selectionKey, "selectionKey");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -86,13 +86,6 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AssetWorkspace(
|
|
||||||
ProjectReference projectReference,
|
|
||||||
AssetWorkspaceService assetWorkspaceService,
|
|
||||||
AssetWorkspaceMutationService mutationService) {
|
|
||||||
this(projectReference, assetWorkspaceService, defaultWorkspaceBus(), mutationService, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AssetWorkspace(
|
private AssetWorkspace(
|
||||||
ProjectReference projectReference,
|
ProjectReference projectReference,
|
||||||
AssetWorkspaceService assetWorkspaceService,
|
AssetWorkspaceService assetWorkspaceService,
|
||||||
@ -123,6 +116,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
packerEventAdapter);
|
packerEventAdapter);
|
||||||
this.packService = new FileSystemPackerBuildService(new p.packer.building.PackerBuildPlanner(), packerEventAdapter);
|
this.packService = new FileSystemPackerBuildService(new p.packer.building.PackerBuildPlanner(), packerEventAdapter);
|
||||||
|
|
||||||
|
subscribeLocalEvents();
|
||||||
root.getStyleClass().add("assets-workspace");
|
root.getStyleClass().add("assets-workspace");
|
||||||
root.setCenter(buildLayout());
|
root.setCenter(buildLayout());
|
||||||
root.setBottom(buildLogsPane());
|
root.setBottom(buildLogsPane());
|
||||||
@ -157,6 +151,33 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
return new StudioWorkspaceEventBus(WorkspaceId.ASSETS, Container.events());
|
return new StudioWorkspaceEventBus(WorkspaceId.ASSETS, Container.events());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void subscribeLocalEvents() {
|
||||||
|
workspaceBus.subscribe(StudioAssetsWorkspaceSelectionRequestedEvent.class, event -> {
|
||||||
|
if (projectMatches(event.project())) {
|
||||||
|
applySelectionRequest(event.selectionKey());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
workspaceBus.subscribe(StudioAssetsNavigatorRedrawRequestedEvent.class, event -> {
|
||||||
|
if (projectMatches(event.project())) {
|
||||||
|
renderNavigator();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
workspaceBus.subscribe(StudioAssetsDetailsRedrawRequestedEvent.class, event -> {
|
||||||
|
if (projectMatches(event.project())) {
|
||||||
|
renderDetails();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
workspaceBus.subscribe(StudioAssetsAssetSummaryPatchedEvent.class, event -> {
|
||||||
|
if (projectMatches(event.project())) {
|
||||||
|
applyAssetSummaryPatch(event.summary());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean projectMatches(ProjectReference project) {
|
||||||
|
return projectReference.equals(project);
|
||||||
|
}
|
||||||
|
|
||||||
private VBox buildLayout() {
|
private VBox buildLayout() {
|
||||||
inlineProgressLabel.getStyleClass().add("assets-workspace-inline-progress-label");
|
inlineProgressLabel.getStyleClass().add("assets-workspace-inline-progress-label");
|
||||||
inlineProgressLabel.setText(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE));
|
inlineProgressLabel.setText(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE));
|
||||||
@ -179,7 +200,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
final String current = newValue == null ? "" : newValue;
|
final String current = newValue == null ? "" : newValue;
|
||||||
if (!previous.equals(current)) {
|
if (!previous.equals(current)) {
|
||||||
searchQuery = current;
|
searchQuery = current;
|
||||||
renderState();
|
requestNavigatorRedraw();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -269,7 +290,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
} else {
|
} else {
|
||||||
activeFilters.remove(filter);
|
activeFilters.remove(filter);
|
||||||
}
|
}
|
||||||
renderState();
|
requestNavigatorRedraw();
|
||||||
});
|
});
|
||||||
filterButtons.put(filter, button);
|
filterButtons.put(filter, button);
|
||||||
filterBar.getChildren().add(button);
|
filterBar.getChildren().add(button);
|
||||||
@ -285,7 +306,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
stagedMutationPreview = null;
|
stagedMutationPreview = null;
|
||||||
selectedPreviewInput = null;
|
selectedPreviewInput = null;
|
||||||
selectedPreviewZoom = 1;
|
selectedPreviewZoom = 1;
|
||||||
renderState();
|
requestRedraw();
|
||||||
}
|
}
|
||||||
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_REFRESHING), ProgressBar.INDETERMINATE_PROGRESS, true);
|
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_REFRESHING), ProgressBar.INDETERMINATE_PROGRESS, true);
|
||||||
appendLog("Assets refresh started.");
|
appendLog("Assets refresh started.");
|
||||||
@ -303,7 +324,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
}
|
}
|
||||||
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false);
|
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false);
|
||||||
appendLog("Assets refresh failed: " + rootCauseMessage(throwable));
|
appendLog("Assets refresh failed: " + rootCauseMessage(throwable));
|
||||||
renderState();
|
requestRedraw();
|
||||||
workspaceBus.publish(new StudioAssetsWorkspaceRefreshFailedEvent(projectReference, rootCauseMessage(throwable)));
|
workspaceBus.publish(new StudioAssetsWorkspaceRefreshFailedEvent(projectReference, rootCauseMessage(throwable)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -315,7 +336,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
pendingSelectionKey = null;
|
pendingSelectionKey = null;
|
||||||
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false);
|
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false);
|
||||||
appendLog("Assets refresh completed: " + state.assets().size() + " assets.");
|
appendLog("Assets refresh completed: " + state.assets().size() + " assets.");
|
||||||
renderState();
|
requestRedraw();
|
||||||
workspaceBus.publish(new StudioAssetsWorkspaceRefreshedEvent(projectReference, state.assets().size()));
|
workspaceBus.publish(new StudioAssetsWorkspaceRefreshedEvent(projectReference, state.assets().size()));
|
||||||
state.selectedAsset().ifPresent(asset -> {
|
state.selectedAsset().ifPresent(asset -> {
|
||||||
workspaceBus.publish(new StudioAssetsWorkspaceSelectionChangedEvent(projectReference, asset.selectionKey()));
|
workspaceBus.publish(new StudioAssetsWorkspaceSelectionChangedEvent(projectReference, asset.selectionKey()));
|
||||||
@ -339,7 +360,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
stagedMutationPreview = null;
|
stagedMutationPreview = null;
|
||||||
selectedPreviewInput = null;
|
selectedPreviewInput = null;
|
||||||
selectedPreviewZoom = 1;
|
selectedPreviewZoom = 1;
|
||||||
renderDetails();
|
requestDetailsRedraw();
|
||||||
}
|
}
|
||||||
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_LOADING_DETAILS), ProgressBar.INDETERMINATE_PROGRESS, true);
|
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_LOADING_DETAILS), ProgressBar.INDETERMINATE_PROGRESS, true);
|
||||||
appendLog("Loading details for " + selectionKey.stableKey() + ".");
|
appendLog("Loading details for " + selectionKey.stableKey() + ".");
|
||||||
@ -359,7 +380,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
}
|
}
|
||||||
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false);
|
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false);
|
||||||
appendLog("Asset details failed: " + message);
|
appendLog("Asset details failed: " + message);
|
||||||
renderDetails();
|
requestDetailsRedraw();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +390,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
selectedPreviewZoom = 1;
|
selectedPreviewZoom = 1;
|
||||||
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false);
|
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false);
|
||||||
appendLog("Asset details ready for " + details.summary().assetName() + ".");
|
appendLog("Asset details ready for " + details.summary().assetName() + ".");
|
||||||
renderDetails();
|
requestDetailsRedraw();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,6 +415,19 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
renderDetails();
|
renderDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void requestRedraw() {
|
||||||
|
requestNavigatorRedraw();
|
||||||
|
requestDetailsRedraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestNavigatorRedraw() {
|
||||||
|
workspaceBus.publish(new StudioAssetsNavigatorRedrawRequestedEvent(projectReference));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestDetailsRedraw() {
|
||||||
|
workspaceBus.publish(new StudioAssetsDetailsRedrawRequestedEvent(projectReference));
|
||||||
|
}
|
||||||
|
|
||||||
private void renderNavigator() {
|
private void renderNavigator() {
|
||||||
assetRowsBySelectionKey.clear();
|
assetRowsBySelectionKey.clear();
|
||||||
switch (state.status()) {
|
switch (state.status()) {
|
||||||
@ -556,7 +590,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
inputButton.setOnAction(event -> {
|
inputButton.setOnAction(event -> {
|
||||||
selectedPreviewInput = input;
|
selectedPreviewInput = input;
|
||||||
selectedPreviewZoom = 1;
|
selectedPreviewZoom = 1;
|
||||||
renderState();
|
requestDetailsRedraw();
|
||||||
});
|
});
|
||||||
roleBox.getChildren().add(inputButton);
|
roleBox.getChildren().add(inputButton);
|
||||||
}
|
}
|
||||||
@ -718,7 +752,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
button.setDisable(zoom > maxZoom);
|
button.setDisable(zoom > maxZoom);
|
||||||
button.setOnAction(event -> {
|
button.setOnAction(event -> {
|
||||||
selectedPreviewZoom = zoom;
|
selectedPreviewZoom = zoom;
|
||||||
renderState();
|
requestDetailsRedraw();
|
||||||
});
|
});
|
||||||
zoomBar.getChildren().add(button);
|
zoomBar.getChildren().add(button);
|
||||||
}
|
}
|
||||||
@ -824,17 +858,9 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final AssetWorkspaceAssetSummary updatedSummary = withPreload(details.summary(), preloadEnabled);
|
final AssetWorkspaceAssetSummary updatedSummary = withPreload(details.summary(), preloadEnabled);
|
||||||
selectedAssetDetails = new AssetWorkspaceAssetDetails(
|
|
||||||
updatedSummary,
|
|
||||||
details.outputFormat(),
|
|
||||||
details.outputCodec(),
|
|
||||||
details.inputsByRole(),
|
|
||||||
details.diagnostics());
|
|
||||||
state = AssetWorkspaceState.ready(replaceAssetSummary(updatedSummary), updatedSummary.selectionKey());
|
|
||||||
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false);
|
setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false);
|
||||||
appendLog("Preload updated for " + updatedSummary.assetName() + ".");
|
appendLog("Preload updated for " + updatedSummary.assetName() + ".");
|
||||||
renderNavigator();
|
workspaceBus.publish(new StudioAssetsAssetSummaryPatchedEvent(projectReference, updatedSummary));
|
||||||
renderDetails();
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -869,12 +895,40 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
summary.hasDiagnostics());
|
summary.hasDiagnostics());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AssetWorkspaceAssetSummary withBuildParticipation(
|
||||||
|
AssetWorkspaceAssetSummary summary,
|
||||||
|
AssetWorkspaceBuildParticipation buildParticipation) {
|
||||||
|
return new AssetWorkspaceAssetSummary(
|
||||||
|
summary.selectionKey(),
|
||||||
|
summary.assetName(),
|
||||||
|
summary.state(),
|
||||||
|
buildParticipation,
|
||||||
|
summary.assetId(),
|
||||||
|
summary.assetFamily(),
|
||||||
|
summary.assetRoot(),
|
||||||
|
summary.preload(),
|
||||||
|
summary.hasDiagnostics());
|
||||||
|
}
|
||||||
|
|
||||||
private List<AssetWorkspaceAssetSummary> replaceAssetSummary(AssetWorkspaceAssetSummary updatedSummary) {
|
private List<AssetWorkspaceAssetSummary> replaceAssetSummary(AssetWorkspaceAssetSummary updatedSummary) {
|
||||||
return state.assets().stream()
|
return state.assets().stream()
|
||||||
.map(asset -> asset.selectionKey().equals(updatedSummary.selectionKey()) ? updatedSummary : asset)
|
.map(asset -> asset.selectionKey().equals(updatedSummary.selectionKey()) ? updatedSummary : asset)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyAssetSummaryPatch(AssetWorkspaceAssetSummary updatedSummary) {
|
||||||
|
state = AssetWorkspaceState.ready(replaceAssetSummary(updatedSummary), state.selectedKey());
|
||||||
|
if (selectedAssetDetails != null && selectedAssetDetails.summary().selectionKey().equals(updatedSummary.selectionKey())) {
|
||||||
|
selectedAssetDetails = new AssetWorkspaceAssetDetails(
|
||||||
|
updatedSummary,
|
||||||
|
selectedAssetDetails.outputFormat(),
|
||||||
|
selectedAssetDetails.outputCodec(),
|
||||||
|
selectedAssetDetails.inputsByRole(),
|
||||||
|
selectedAssetDetails.diagnostics());
|
||||||
|
}
|
||||||
|
requestRedraw();
|
||||||
|
}
|
||||||
|
|
||||||
private void renderNavigatorProjection(AssetNavigatorProjection projection) {
|
private void renderNavigatorProjection(AssetNavigatorProjection projection) {
|
||||||
navigatorContent.getChildren().clear();
|
navigatorContent.getChildren().clear();
|
||||||
for (AssetNavigatorGroup group : projection.groups()) {
|
for (AssetNavigatorGroup group : projection.groups()) {
|
||||||
@ -983,11 +1037,15 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void selectAsset(AssetWorkspaceSelectionKey selectionKey) {
|
private void selectAsset(AssetWorkspaceSelectionKey selectionKey) {
|
||||||
|
workspaceBus.publish(new StudioAssetsWorkspaceSelectionRequestedEvent(projectReference, selectionKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySelectionRequest(AssetWorkspaceSelectionKey selectionKey) {
|
||||||
state = state.withSelection(selectionKey);
|
state = state.withSelection(selectionKey);
|
||||||
stagedMutationPreview = null;
|
stagedMutationPreview = null;
|
||||||
appendLog("Selected asset " + selectionKey.stableKey() + ".");
|
appendLog("Selected asset " + selectionKey.stableKey() + ".");
|
||||||
updateNavigatorSelection();
|
updateNavigatorSelection();
|
||||||
renderDetails();
|
requestDetailsRedraw();
|
||||||
workspaceBus.publish(new StudioAssetsWorkspaceSelectionChangedEvent(projectReference, selectionKey));
|
workspaceBus.publish(new StudioAssetsWorkspaceSelectionChangedEvent(projectReference, selectionKey));
|
||||||
loadSelectedAssetDetails(selectionKey);
|
loadSelectedAssetDetails(selectionKey);
|
||||||
}
|
}
|
||||||
@ -1043,7 +1101,6 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
if (!result.diagnostics().isEmpty()) {
|
if (!result.diagnostics().isEmpty()) {
|
||||||
appendLog("Doctor diagnostics: " + result.diagnostics().size() + ".");
|
appendLog("Doctor diagnostics: " + result.diagnostics().size() + ".");
|
||||||
}
|
}
|
||||||
refresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePackResult(PackerBuildResult result) {
|
private void handlePackResult(PackerBuildResult result) {
|
||||||
@ -1089,14 +1146,14 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
try {
|
try {
|
||||||
stagedMutationPreview = mutationService.preview(projectReference, selectedAsset, action, null);
|
stagedMutationPreview = mutationService.preview(projectReference, selectedAsset, action, null);
|
||||||
appendLog("Preview ready for " + actionLabel(action) + ".");
|
appendLog("Preview ready for " + actionLabel(action) + ".");
|
||||||
renderState();
|
requestDetailsRedraw();
|
||||||
Platform.runLater(() -> detailsScroll.setVvalue(0.0d));
|
Platform.runLater(() -> detailsScroll.setVvalue(0.0d));
|
||||||
} catch (RuntimeException runtimeException) {
|
} catch (RuntimeException runtimeException) {
|
||||||
final String message = rootCauseMessage(runtimeException);
|
final String message = rootCauseMessage(runtimeException);
|
||||||
appendLog("Preview failed: " + message);
|
appendLog("Preview failed: " + message);
|
||||||
workspaceBus.publish(new StudioAssetsMutationFailedEvent(projectReference, action, message));
|
workspaceBus.publish(new StudioAssetsMutationFailedEvent(projectReference, action, message));
|
||||||
stagedMutationPreview = null;
|
stagedMutationPreview = null;
|
||||||
renderState();
|
requestDetailsRedraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1111,19 +1168,21 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
if (!preview.canApply()) {
|
if (!preview.canApply()) {
|
||||||
stagedMutationPreview = preview;
|
stagedMutationPreview = preview;
|
||||||
appendLog(actionLabel(action) + " blocked.");
|
appendLog(actionLabel(action) + " blocked.");
|
||||||
renderState();
|
requestDetailsRedraw();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mutationService.apply(projectReference, preview);
|
mutationService.apply(projectReference, preview);
|
||||||
appendLog("Applied " + actionLabel(preview.action()) + ".");
|
appendLog("Applied " + actionLabel(preview.action()) + ".");
|
||||||
stagedMutationPreview = null;
|
stagedMutationPreview = null;
|
||||||
refresh();
|
if (!applyMutationSummaryPatch(preview)) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
} catch (RuntimeException runtimeException) {
|
} catch (RuntimeException runtimeException) {
|
||||||
final String message = rootCauseMessage(runtimeException);
|
final String message = rootCauseMessage(runtimeException);
|
||||||
appendLog("Mutation failed: " + message);
|
appendLog("Mutation failed: " + message);
|
||||||
workspaceBus.publish(new StudioAssetsMutationFailedEvent(projectReference, action, message));
|
workspaceBus.publish(new StudioAssetsMutationFailedEvent(projectReference, action, message));
|
||||||
stagedMutationPreview = null;
|
stagedMutationPreview = null;
|
||||||
renderState();
|
requestDetailsRedraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1148,16 +1207,31 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
mutationService.apply(projectReference, preview);
|
mutationService.apply(projectReference, preview);
|
||||||
appendLog("Applied " + actionLabel(preview.action()) + ".");
|
appendLog("Applied " + actionLabel(preview.action()) + ".");
|
||||||
stagedMutationPreview = null;
|
stagedMutationPreview = null;
|
||||||
refresh();
|
if (!applyMutationSummaryPatch(preview)) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
} catch (RuntimeException runtimeException) {
|
} catch (RuntimeException runtimeException) {
|
||||||
final String message = rootCauseMessage(runtimeException);
|
final String message = rootCauseMessage(runtimeException);
|
||||||
appendLog("Mutation failed: " + message);
|
appendLog("Mutation failed: " + message);
|
||||||
workspaceBus.publish(new StudioAssetsMutationFailedEvent(projectReference, action, message));
|
workspaceBus.publish(new StudioAssetsMutationFailedEvent(projectReference, action, message));
|
||||||
stagedMutationPreview = null;
|
stagedMutationPreview = null;
|
||||||
renderState();
|
requestDetailsRedraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean applyMutationSummaryPatch(AssetWorkspaceMutationPreview preview) {
|
||||||
|
final AssetWorkspaceAssetSummary updatedSummary = switch (preview.action()) {
|
||||||
|
case INCLUDE_IN_BUILD -> withBuildParticipation(preview.asset(), AssetWorkspaceBuildParticipation.INCLUDED);
|
||||||
|
case EXCLUDE_FROM_BUILD -> withBuildParticipation(preview.asset(), AssetWorkspaceBuildParticipation.EXCLUDED);
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
if (updatedSummary == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
workspaceBus.publish(new StudioAssetsAssetSummaryPatchedEvent(projectReference, updatedSummary));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private Node createStagedMutationPanel(AssetWorkspaceMutationPreview preview) {
|
private Node createStagedMutationPanel(AssetWorkspaceMutationPreview preview) {
|
||||||
final VBox panel = new VBox(10);
|
final VBox panel = new VBox(10);
|
||||||
panel.getStyleClass().add("assets-mutation-panel");
|
panel.getStyleClass().add("assets-mutation-panel");
|
||||||
@ -1191,7 +1265,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
cancel.getStyleClass().addAll("studio-button", "studio-button-cancel");
|
cancel.getStyleClass().addAll("studio-button", "studio-button-cancel");
|
||||||
cancel.setOnAction(event -> {
|
cancel.setOnAction(event -> {
|
||||||
stagedMutationPreview = null;
|
stagedMutationPreview = null;
|
||||||
renderState();
|
requestDetailsRedraw();
|
||||||
});
|
});
|
||||||
final Button apply = new Button(Container.i18n().text(I18n.ASSETS_MUTATION_APPLY));
|
final Button apply = new Button(Container.i18n().text(I18n.ASSETS_MUTATION_APPLY));
|
||||||
apply.getStyleClass().addAll("studio-button", "studio-button-primary");
|
apply.getStyleClass().addAll("studio-button", "studio-button-primary");
|
||||||
@ -1310,7 +1384,7 @@ public final class AssetWorkspace implements Workspace {
|
|||||||
final String message = rootCauseMessage(runtimeException);
|
final String message = rootCauseMessage(runtimeException);
|
||||||
appendLog("Mutation failed: " + message);
|
appendLog("Mutation failed: " + message);
|
||||||
stagedMutationPreview = preview;
|
stagedMutationPreview = preview;
|
||||||
renderState();
|
requestDetailsRedraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,6 @@
|
|||||||
"asset_id" : 8,
|
"asset_id" : 8,
|
||||||
"asset_uuid" : "9a7386e7-6f0e-4e4c-9919-0de71e0b7031",
|
"asset_uuid" : "9a7386e7-6f0e-4e4c-9919-0de71e0b7031",
|
||||||
"root" : "sound",
|
"root" : "sound",
|
||||||
"included_in_build" : true
|
"included_in_build" : false
|
||||||
} ]
|
} ]
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user