From 4712f7c7f8964eeb7690aea2cff3c6175a1ebf2c Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 9 Jul 2026 00:48:40 +0100 Subject: [PATCH] Reframe Unity plugins as projection adapters --- docs/eve-migration-roadmap.md | 53 ++++---- docs/parity-testing-harness.md | 8 +- docs/plugin-architecture.md | 9 +- docs/repo-strategy.md | 4 +- .../README.md | 20 +-- .../Runtime/EveUiToolkitSurfaceLowerer.cs | 6 +- .../Runtime/EveUiToolkitSurfaceOptions.cs | 14 +- ...> IEveUiToolkitPluginProjectionAdapter.cs} | 2 +- ...eUiToolkitPluginProjectionAdapter.cs.meta} | 0 ...=> NornGraphUiToolkitProjectionAdapter.cs} | 4 +- ...rnGraphUiToolkitProjectionAdapter.cs.meta} | 0 ...iVisualNovelUiToolkitProjectionAdapter.cs} | 12 +- ...alNovelUiToolkitProjectionAdapter.cs.meta} | 0 .../Tests.meta | 8 ++ .../Tests/Editor.meta | 8 ++ .../Editor/EveUiToolkitSurfaceLowererTests.cs | 122 ++++++++++++++++++ .../EveUiToolkitSurfaceLowererTests.cs.meta | 2 + .../GameCult.Eve.UnityUIToolkit.Tests.asmdef | 23 ++++ ...eCult.Eve.UnityUIToolkit.Tests.asmdef.meta | 7 + .../eve-runtime-capability.json | 14 +- scripts/run-aetheria-unity-package-smoke.ps1 | 6 +- tools/parity/parity-manifest.json | 38 +++--- 22 files changed, 274 insertions(+), 86 deletions(-) rename packages/org.gamecult.eve.unity-uitoolkit/Runtime/{IEveUiToolkitPluginHost.cs => IEveUiToolkitPluginProjectionAdapter.cs} (89%) rename packages/org.gamecult.eve.unity-uitoolkit/Runtime/{IEveUiToolkitPluginHost.cs.meta => IEveUiToolkitPluginProjectionAdapter.cs.meta} (100%) rename packages/org.gamecult.eve.unity-uitoolkit/Runtime/{NornGraphUiToolkitPluginHost.cs => NornGraphUiToolkitProjectionAdapter.cs} (94%) rename packages/org.gamecult.eve.unity-uitoolkit/Runtime/{NornGraphUiToolkitPluginHost.cs.meta => NornGraphUiToolkitProjectionAdapter.cs.meta} (100%) rename packages/org.gamecult.eve.unity-uitoolkit/Runtime/{SaiVisualNovelUiToolkitPluginHost.cs => SaiVisualNovelUiToolkitProjectionAdapter.cs} (94%) rename packages/org.gamecult.eve.unity-uitoolkit/Runtime/{SaiVisualNovelUiToolkitPluginHost.cs.meta => SaiVisualNovelUiToolkitProjectionAdapter.cs.meta} (100%) create mode 100644 packages/org.gamecult.eve.unity-uitoolkit/Tests.meta create mode 100644 packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor.meta create mode 100644 packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs create mode 100644 packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs.meta create mode 100644 packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef create mode 100644 packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef.meta diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 1d15fd3..3619b88 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -25,8 +25,8 @@ The target shape: - Owner repos: Sai owns VN/Ink, Norn owns graph semantics, providers such as Aetheria own live state and authored product surfaces. - Runtime repos: EveUnity, EveFlutter, EveAndroid, EveIOS, EveGodot, EveElectron, - Fensalir/Direct2D, and future bodies own projection, build glue, plugin host - adapters, capture, and runtime tests. + Fensalir/Direct2D, and future bodies own projection, build glue, plugin + projection adapters, capture, and runtime tests. - EveConformance: fixture corpus, parity runner, capability matrix, screenshot/report artifacts, plugin packs, and CI once the harness is stable enough to leave Eve core. @@ -208,10 +208,11 @@ Purpose: let runtime repos own native projection without losing conformance. Work: -- EveFlutter: provider picker, command transport, plugin host, Android/iOS/desktop - build flow, native capture, and parity reports. -- EveUnity: UPM package release, plugin host behavior, resolver-backed embedded - documents, Unity test/capture lifecycle, and Aetheria consumption. +- EveFlutter: provider picker, command transport, plugin projection adapters, + Android/iOS/desktop build flow, native capture, and parity reports. +- EveUnity: UPM package release, plugin projection adapter behavior, + resolver-backed embedded documents, Unity test/capture lifecycle, and Aetheria + consumption. - EveAndroid: Kotlin device edge, generic surface traversal, sensor publishing, Android-specific CI, and explicit discovery input. - EveIOS: UIKit renderer, sensor edge release flow, fixed-device and simulator @@ -284,28 +285,29 @@ Recently cut: parity harness validates release, test, and capture lifecycle claims against `tools/parity/parity-manifest.json`, checks declared evidence paths, and exports lifecycle status through the conformance index. Current proof covers - incubating UPM package identity and the Aetheria consumer-build smoke; tagged - EveUnity release, Unity EditMode/PlayMode tests, and editor or batchmode - capture remain explicit split blockers. + incubating UPM package identity, authored package EditMode tests, and the + Aetheria consumer-build smoke; tagged EveUnity release, runtime-owned + batchmode test execution, and editor or batchmode capture remain explicit + split blockers. - Aetheria now has repeatable Unity package consumer-build evidence for the Eve UI Toolkit runtime. `scripts/run-aetheria-unity-package-smoke.ps1` verifies Aetheria's Unity `Packages/manifest.json` consumes Eve's surface and UI Toolkit packages by file reference, checks the generated `GameCult.Eve.UnityUIToolkit.csproj` includes all current runtime files, and builds the package through Aetheria's Unity-generated project. -- Unity UI Toolkit now has a first-party `norn.graph` plugin-host proof for - `embed.norn`. `NornGraphUiToolkitPluginHost` owns the native embedded graph - shell and graph command emission path, while Norn keeps graph layout and graph - semantics. EveUnity split-readiness is now blocked on runtime-owned release, - test, and capture lifecycle evidence rather than missing Sai/Norn plugin - support. -- Unity UI Toolkit now has a first-party `sai.vn` plugin-host proof. The - runtime owns `IEveUiToolkitPluginHost`, registers - `SaiVisualNovelUiToolkitPluginHost` through `EveUiToolkitSurfaceOptions`, and - declares support for `vn.stage`, `story.choose`, `story.continue`, and - `story.jump` in its runtime capability manifest. Sai still owns story state - and command semantics; Unity only lowers the visual stage/dialogue/action - surface and emits Eve command requests. +- Unity UI Toolkit now has a first-party `norn.graph` projection-adapter proof + for `embed.norn`. `NornGraphUiToolkitProjectionAdapter` owns the native + embedded graph shell and graph command emission path, while the Norn sidecar + plugin keeps graph layout and graph semantics. EveUnity split-readiness is now + blocked on runtime-owned release, test, and capture lifecycle evidence rather + than missing Sai/Norn projection support. +- Unity UI Toolkit now has a first-party `sai.vn` projection-adapter proof. The + runtime owns `IEveUiToolkitPluginProjectionAdapter`, registers + `SaiVisualNovelUiToolkitProjectionAdapter` through + `EveUiToolkitSurfaceOptions`, and declares support for `vn.stage`, + `story.choose`, `story.continue`, and `story.jump` in its runtime capability + manifest. Sai still owns story state and command semantics; Unity only lowers + the visual stage/dialogue/action surface and emits Eve command requests. - Unity UI Toolkit now publishes a runtime-owned capability manifest at `packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json`. The parity harness validates that manifest against @@ -324,9 +326,10 @@ Recently cut: - The parity harness now emits an `EveConformance`-shaped export under `artifacts/conformance/latest`, with a top-level index and per-pack JSON files for core, plugin, provider, and runtime consumers. -- Unity's plugin-host capability gap is now explicitly demoted. The UI Toolkit - runtime owns generic projection and command requests, while Sai, Norn, and - TeX semantics remain unsupported until a real Unity plugin host exists. +- Unity's plugin projection capability gap is now explicitly demoted. The UI + Toolkit runtime owns generic projection and command requests, while Sai, Norn, + and TeX semantics remain sidecar-plugin responsibilities. Unity only adds + projection adapters for capabilities a plugin advertises through Eve. - Flutter and Unity now have runtime command-transport smoke evidence in the parity report. Flutter emits `gamecult.eve.command.v1` intents from lowered controls, and Unity command requests now carry the same command schema. diff --git a/docs/parity-testing-harness.md b/docs/parity-testing-harness.md index bca493a..a0f0b91 100644 --- a/docs/parity-testing-harness.md +++ b/docs/parity-testing-harness.md @@ -27,7 +27,7 @@ coordinate split-readiness, but the runtime body must publish the capability claim it expects other repos to consume. Unity UI Toolkit currently uses this path for its `sai.vn` and `norn.graph` -plugin-host proofs while leaving `tex.math` as explicit unsupported plugin +plugin projection adapter proofs while leaving `tex.math` as explicit unsupported plugin semantics. ## Run @@ -78,7 +78,8 @@ The semantic runner writes: The conformance export includes runtime `capabilityManifestPath` and `capabilityManifestErrors` fields so external consumers can distinguish a -generic projection runtime from a runtime that actually hosts plugin semantics. +generic projection runtime from a runtime that can project plugin-declared +capabilities. After generating the parity report, `run-parity-harness.ps1` copies `artifacts/conformance/latest` into @@ -121,7 +122,8 @@ The parity harness validates those lifecycle claims against exist. The current lifecycle proof is deliberately split: - release: incubating UPM package identity and import surface exist in Eve; -- test: Aetheria can build the package through Unity's generated project; +- test: package-owned EditMode tests are authored and Aetheria can build the + package through Unity's generated project; - capture: Unity editor or batchmode capture remains a split blocker. The smoke runner writes: diff --git a/docs/plugin-architecture.md b/docs/plugin-architecture.md index 0aa2bfc..477ee6e 100644 --- a/docs/plugin-architecture.md +++ b/docs/plugin-architecture.md @@ -45,8 +45,9 @@ Renderer clients own: - input capture and command emission; - local sensor/media capture when relevant; - runtime-specific capability gaps; -- runtime capability manifests that declare supported features, plugin-host - support, unsupported plugin semantics, command transport, and split metadata; +- runtime capability manifests that declare supported features, plugin projection + adapter support, unsupported plugin projections, command transport, and split + metadata; - screenshots, frame capture, and performance telemetry. Providers own: @@ -129,7 +130,7 @@ Allowed implementation forms: - process sidecar; - WASM module hosted by an Eve runtime; - native dynamic library behind the same ABI when performance demands it; -- in-process package only when wrapped by the same plugin host interface. +- in-process package only when wrapped by the same executable plugin ABI. Forbidden integration: @@ -196,7 +197,7 @@ Example ABI response: ``` If a provider and plugin are written in the same language, the provider may run -the plugin host in-process for performance. It still talks to the same ABI +the plugin runtime in-process for performance. It still talks to the same ABI object model. No special trusted back door. ### Example Plugin Manifest diff --git a/docs/repo-strategy.md b/docs/repo-strategy.md index 893defc..3aa75d2 100644 --- a/docs/repo-strategy.md +++ b/docs/repo-strategy.md @@ -22,7 +22,7 @@ Anything else is guilty until it can explain why it protects a core invariant. ## Graduation Rules - A runtime splits when it has its own build/deploy lifecycle, platform SDK pain, - plugin host behavior, capture pipeline, or release cadence. + plugin projection behavior, capture pipeline, or release cadence. - A plugin splits when it owns domain semantics, fixtures, and conformance cases. - A product demo splits when it carries product-specific state, assets, flows, receipts, or deployment authority. @@ -37,7 +37,7 @@ Anything else is guilty until it can explain why it protects a core invariant. - `Sai`: owner for the VN/Ink plugin once `sai.vn` graduates. - `Norn`: owner for graph plugin semantics once `norn.graph` graduates. - `EveUnity`, `EveGodot`, `EveFlutter`, `EveElectron`, and similar runtime - repos: native projection, platform build glue, plugin host, captures, and + repos: native projection, platform build glue, plugin projection adapters, captures, and runtime tests. - `EveConformance`: fixture corpus, parity runner, capability matrix, screenshot/report artifacts, plugin packs, and CI orchestration once the diff --git a/packages/org.gamecult.eve.unity-uitoolkit/README.md b/packages/org.gamecult.eve.unity-uitoolkit/README.md index 00a1e49..94c7dcb 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/README.md +++ b/packages/org.gamecult.eve.unity-uitoolkit/README.md @@ -7,14 +7,15 @@ It owns native projection only. Providers still own truth, accepted state, style token values, and command effects through CultMesh/CultNet. Unknown component kinds degrade to inert containers instead of gaining local semantics. -Plugin semantics enter through runtime plugin hosts. The package includes a -first-party `sai.vn` host proof for `vn.stage`, dialogue panels, action rails, -and story command projection. Sai still owns story state and command semantics; -Unity only owns native projection and emits `gamecult.eve.command.v1` requests. -The package also includes a `norn.graph` host proof for `embed.norn`; Norn still -owns graph layout and graph semantics, while Unity owns the embedded graph shell -and command emission. `tex.math` still passes through as generic Eve component -structure until it gains an explicit Unity plugin host. +Plugin semantics enter through plugin ABI sidecars and advertisements, not +through Unity. This package includes first-party projection adapters for the +`sai.vn` projection surface (`vn.stage`, dialogue panels, action rails, and +story command requests) and the `norn.graph` projection surface (`embed.norn`). +Sai still owns story state and command semantics. Norn still owns graph layout +and graph semantics. Unity only owns native projection of already-declared +plugin capabilities and emits `gamecult.eve.command.v1` requests. `tex.math` +still passes through as generic Eve component structure until it gains an +explicit Unity projection adapter. This package lives in the Eve repository as the shared Unity lowering target. Aetheria and other Unity consumers should import it from Eve instead of @@ -37,7 +38,8 @@ The runtime capability manifest is evidence: - release: incubating UPM package identity and import surface; -- test: Aetheria consumer-build smoke through Unity's generated project; +- test: package-owned EditMode tests plus Aetheria consumer-build smoke through + Unity's generated project; - capture: pending Unity editor or batchmode artifact. Those lifecycle claims are validated by the parity harness. A missing evidence diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceLowerer.cs b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceLowerer.cs index e1a267b..f989ccf 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceLowerer.cs +++ b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceLowerer.cs @@ -76,9 +76,9 @@ namespace GameCult.Eve.UnityUIToolkit EveSurfaceDocument document, Action? commandSink) { - var pluginHost = _options.FindPluginHost(component); - if (pluginHost != null) - return pluginHost.Lower(component, document, commandSink); + var projectionAdapter = _options.FindPluginProjectionAdapter(component); + if (projectionAdapter != null) + return projectionAdapter.Lower(component, document, commandSink); switch (NormalizeKind(component.Kind)) { diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceOptions.cs b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceOptions.cs index 9d512e1..e972f4e 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceOptions.cs +++ b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceOptions.cs @@ -13,23 +13,23 @@ namespace GameCult.Eve.UnityUIToolkit public EveUiToolkitSurfaceOptions( Func? embeddedDocumentResolver = null, - IReadOnlyList? pluginHosts = null) + IReadOnlyList? pluginProjectionAdapters = null) { EmbeddedDocumentResolver = embeddedDocumentResolver; - PluginHosts = pluginHosts ?? new IEveUiToolkitPluginHost[] + PluginProjectionAdapters = pluginProjectionAdapters ?? new IEveUiToolkitPluginProjectionAdapter[] { - new SaiVisualNovelUiToolkitPluginHost(), - new NornGraphUiToolkitPluginHost() + new SaiVisualNovelUiToolkitProjectionAdapter(), + new NornGraphUiToolkitProjectionAdapter() }; } public Func? EmbeddedDocumentResolver { get; } - public IReadOnlyList PluginHosts { get; } + public IReadOnlyList PluginProjectionAdapters { get; } - public IEveUiToolkitPluginHost? FindPluginHost(EveSurfaceComponent component) + public IEveUiToolkitPluginProjectionAdapter? FindPluginProjectionAdapter(EveSurfaceComponent component) { - return PluginHosts.FirstOrDefault(host => host.CanLower(component)); + return PluginProjectionAdapters.FirstOrDefault(adapter => adapter.CanLower(component)); } } } diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginHost.cs b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginProjectionAdapter.cs similarity index 89% rename from packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginHost.cs rename to packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginProjectionAdapter.cs index 77f04ee..8e99b61 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginHost.cs +++ b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginProjectionAdapter.cs @@ -7,7 +7,7 @@ using UnityEngine.UIElements; namespace GameCult.Eve.UnityUIToolkit { - public interface IEveUiToolkitPluginHost + public interface IEveUiToolkitPluginProjectionAdapter { string PluginId { get; } diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginHost.cs.meta b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginProjectionAdapter.cs.meta similarity index 100% rename from packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginHost.cs.meta rename to packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginProjectionAdapter.cs.meta diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitProjectionAdapter.cs similarity index 94% rename from packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs rename to packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitProjectionAdapter.cs index de915ce..d18d7cf 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs +++ b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitProjectionAdapter.cs @@ -9,7 +9,7 @@ using UnityEngine.UIElements; namespace GameCult.Eve.UnityUIToolkit { - public sealed class NornGraphUiToolkitPluginHost : IEveUiToolkitPluginHost + public sealed class NornGraphUiToolkitProjectionAdapter : IEveUiToolkitPluginProjectionAdapter { public string PluginId => "norn.graph"; @@ -29,7 +29,7 @@ namespace GameCult.Eve.UnityUIToolkit Action? commandSink) { var graph = new VisualElement(); - graph.AddToClassList("eve-plugin-host"); + graph.AddToClassList("eve-plugin-projection"); graph.AddToClassList("eve-plugin-norn-graph"); graph.AddToClassList("norn-graph"); graph.style.flexDirection = FlexDirection.Column; diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs.meta b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitProjectionAdapter.cs.meta similarity index 100% rename from packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs.meta rename to packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitProjectionAdapter.cs.meta diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitPluginHost.cs b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitProjectionAdapter.cs similarity index 94% rename from packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitPluginHost.cs rename to packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitProjectionAdapter.cs index 4e9273b..9bf0c19 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitPluginHost.cs +++ b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitProjectionAdapter.cs @@ -9,7 +9,7 @@ using UnityEngine.UIElements; namespace GameCult.Eve.UnityUIToolkit { - public sealed class SaiVisualNovelUiToolkitPluginHost : IEveUiToolkitPluginHost + public sealed class SaiVisualNovelUiToolkitProjectionAdapter : IEveUiToolkitPluginProjectionAdapter { public string PluginId => "sai.vn"; @@ -62,7 +62,7 @@ namespace GameCult.Eve.UnityUIToolkit private static VisualElement Stage(EveSurfaceComponent component) { var stage = new VisualElement(); - stage.AddToClassList("eve-plugin-host"); + stage.AddToClassList("eve-plugin-projection"); stage.AddToClassList("eve-plugin-sai-vn"); stage.AddToClassList("sai-vn-stage"); stage.style.flexGrow = 1; @@ -86,7 +86,7 @@ namespace GameCult.Eve.UnityUIToolkit private static VisualElement DialoguePanel(EveSurfaceComponent component) { var panel = new VisualElement(); - panel.AddToClassList("eve-plugin-host"); + panel.AddToClassList("eve-plugin-projection"); panel.AddToClassList("eve-plugin-sai-vn"); panel.AddToClassList("sai-vn-dialogue"); panel.style.flexDirection = FlexDirection.Column; @@ -114,7 +114,7 @@ namespace GameCult.Eve.UnityUIToolkit private static VisualElement DialogueText(EveSurfaceComponent component) { var text = new Label(component.GetProp("text")); - text.AddToClassList("eve-plugin-host"); + text.AddToClassList("eve-plugin-projection"); text.AddToClassList("eve-plugin-sai-vn"); text.AddToClassList("sai-vn-dialogue-text"); return text; @@ -123,7 +123,7 @@ namespace GameCult.Eve.UnityUIToolkit private static VisualElement ActionRail() { var rail = new VisualElement(); - rail.AddToClassList("eve-plugin-host"); + rail.AddToClassList("eve-plugin-projection"); rail.AddToClassList("eve-plugin-sai-vn"); rail.AddToClassList("sai-vn-actions"); rail.style.flexDirection = FlexDirection.Row; @@ -139,7 +139,7 @@ namespace GameCult.Eve.UnityUIToolkit var label = component.GetProp("label", component.GetProp("title", "Continue")); var command = StoryCommand(component); var button = new Button(() => EmitStoryCommand(document, component, command, commandSink)) { text = label }; - button.AddToClassList("eve-plugin-host"); + button.AddToClassList("eve-plugin-projection"); button.AddToClassList("eve-plugin-sai-vn"); button.AddToClassList("sai-vn-story-command"); button.AddToClassList($"sai-vn-command-{SafeClass(command)}"); diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitPluginHost.cs.meta b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitProjectionAdapter.cs.meta similarity index 100% rename from packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitPluginHost.cs.meta rename to packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitProjectionAdapter.cs.meta diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Tests.meta b/packages/org.gamecult.eve.unity-uitoolkit/Tests.meta new file mode 100644 index 0000000..0966739 --- /dev/null +++ b/packages/org.gamecult.eve.unity-uitoolkit/Tests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 694b4921723a4d56a9f0f72ad2240feb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor.meta b/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor.meta new file mode 100644 index 0000000..9ea2f73 --- /dev/null +++ b/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: acfcd889183e4bfbac82f8883deef300 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs b/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs new file mode 100644 index 0000000..1390f5e --- /dev/null +++ b/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using GameCult.Eve.Surface; +using GameCult.Mesh; +using NUnit.Framework; +using UnityEngine.UIElements; + +#nullable enable + +namespace GameCult.Eve.UnityUIToolkit.Tests +{ + public sealed class EveUiToolkitSurfaceLowererTests + { + [Test] + public void DefaultOptionsExposeSaiAndNornProjectionAdapters() + { + var options = EveUiToolkitSurfaceOptions.Default; + + Assert.That(options.FindPluginProjectionAdapter(Component("stage", "vn.stage"))?.PluginId, Is.EqualTo("sai.vn")); + Assert.That(options.FindPluginProjectionAdapter(Component("graph", "embed.norn"))?.PluginId, Is.EqualTo("norn.graph")); + } + + [Test] + public void SaiStageLowersThroughRuntimeProjectionAdapter() + { + var document = Document(Component( + "stage", + "vn.stage", + new Dictionary + { + ["storyId"] = "sai.fixture", + ["currentPath"] = "intro" + })); + + var root = new EveUiToolkitSurfaceLowerer().Lower(document); + + Assert.That(root.ClassListContains("eve-plugin-projection"), Is.True); + Assert.That(root.ClassListContains("eve-plugin-sai-vn"), Is.True); + Assert.That(root.ClassListContains("sai-vn-stage"), Is.True); + } + + [Test] + public void NornGraphLowersThroughRuntimeProjectionAdapter() + { + var document = Document(Component( + "graph", + "embed.norn", + new Dictionary + { + ["label"] = "World graph", + ["mode"] = "embedded" + })); + + var root = new EveUiToolkitSurfaceLowerer().Lower(document); + + Assert.That(root.ClassListContains("eve-plugin-projection"), Is.True); + Assert.That(root.ClassListContains("eve-plugin-norn-graph"), Is.True); + Assert.That(root.ClassListContains("norn-graph"), Is.True); + } + + [Test] + public void EmbeddedDocumentSlotMountsResolverBackedChildSurface() + { + var child = Document( + Component("child-root", "text", new Dictionary { ["text"] = "Nested surface" }), + surfaceId: "child-surface"); + var parentRoot = new EveSurfaceComponent( + "parent-root", + "surface", + EmptyProps(), + Array.Empty(), + Array.Empty(), + new[] + { + new EveEmbeddedDocumentSlot( + "child-slot", + "child-surface", + EveSurfaceDocument.SchemaId, + "inline") + }); + var lowerer = new EveUiToolkitSurfaceLowerer(new EveUiToolkitSurfaceOptions( + embeddedDocumentResolver: _ => child, + pluginProjectionAdapters: Array.Empty())); + + var root = lowerer.Lower(Document(parentRoot)); + var embedded = root.Query(className: "eve-embedded-document").First(); + + Assert.That(embedded, Is.Not.Null); + Assert.That(embedded.name, Is.EqualTo("child-slot")); + Assert.That(embedded.ClassListContains("eve-embedded-kind-inline"), Is.True); + } + + private static EveSurfaceDocument Document(EveSurfaceComponent root, string surfaceId = "test-surface") + { + return new EveSurfaceDocument( + "gamecult.eve.unity.tests", + "runtime.test", + "Unity UI Toolkit tests", + 1, + "2026-07-08T00:00:00Z", + new EveSurfaceTree(surfaceId, root, Array.Empty()), + Array.Empty()); + } + + private static EveSurfaceComponent Component( + string id, + string kind, + IReadOnlyDictionary? props = null) + { + return new EveSurfaceComponent( + id, + kind, + props ?? EmptyProps(), + Array.Empty()); + } + + private static IReadOnlyDictionary EmptyProps() + { + return new Dictionary(StringComparer.Ordinal); + } + } +} diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs.meta b/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs.meta new file mode 100644 index 0000000..c6725d8 --- /dev/null +++ b/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2921e30d39fe49098bc3763c538ed67c diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef b/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef new file mode 100644 index 0000000..8659c1c --- /dev/null +++ b/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef @@ -0,0 +1,23 @@ +{ + "name": "GameCult.Eve.UnityUIToolkit.Tests", + "rootNamespace": "GameCult.Eve.UnityUIToolkit.Tests", + "references": [ + "GameCult.Eve.UnityUIToolkit", + "GameCult.Eve.Surface", + "GameCult.Mesh" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [], + "optionalUnityReferences": [ + "TestAssemblies" + ], + "noEngineReferences": false +} diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef.meta b/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef.meta new file mode 100644 index 0000000..e9af2a3 --- /dev/null +++ b/packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 353e1aea044b46ac98835959a19fe0ac +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json b/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json index f89d622..46e9f1d 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json +++ b/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json @@ -7,7 +7,7 @@ "ownerRepo": "EveUnity", "currentHostRepo": "Eve", "splitTarget": "EveUnity", - "graduationTrigger": "Split when Unity package has independent UPM release, plugin host behavior, and Unity test/capture lifecycle." + "graduationTrigger": "Split when Unity package has independent UPM release, plugin projection adapter behavior, and Unity test/capture lifecycle." }, "supportedFeatures": [ "embeddedDocuments" @@ -21,20 +21,20 @@ "story.continue", "story.jump" ], - "host": "SaiVisualNovelUiToolkitPluginHost" + "projectionAdapter": "SaiVisualNovelUiToolkitProjectionAdapter" }, { "pluginId": "norn.graph", "capabilities": [ "embed.norn" ], - "host": "NornGraphUiToolkitPluginHost" + "projectionAdapter": "NornGraphUiToolkitProjectionAdapter" } ], "unsupportedPlugins": [ { "pluginId": "tex.math", - "reason": "Unity UI Toolkit currently preserves embed.tex as generic Eve structure; no TeX render plugin host exists in this runtime." + "reason": "Unity UI Toolkit currently preserves embed.tex as generic Eve structure; no TeX projection adapter exists in this runtime." } ], "commandTransport": { @@ -59,13 +59,15 @@ }, "test": { "ownerRepo": "EveUnity", - "status": "consumer-build-smoke", + "status": "authored-editmode-tests-and-consumer-build-smoke", "command": "powershell -ExecutionPolicy Bypass -File .\\scripts\\run-aetheria-unity-package-smoke.ps1", "evidencePaths": [ + "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef", + "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs", "scripts/run-aetheria-unity-package-smoke.ps1" ], "pendingProofs": [ - "Unity EditMode or PlayMode test assembly runs from the runtime repo." + "Unity EditMode or PlayMode test assembly runs from the runtime repo in batchmode." ] }, "capture": { diff --git a/scripts/run-aetheria-unity-package-smoke.ps1 b/scripts/run-aetheria-unity-package-smoke.ps1 index d0ea3fc..c8411d3 100644 --- a/scripts/run-aetheria-unity-package-smoke.ps1 +++ b/scripts/run-aetheria-unity-package-smoke.ps1 @@ -36,9 +36,9 @@ $project = Get-Content -Raw -LiteralPath $unityToolkitProject $requiredCompileItems = @( "Runtime\EveUiToolkitSurfaceOptions.cs", "Runtime\EveUiToolkitSurfaceLowerer.cs", - "Runtime\IEveUiToolkitPluginHost.cs", - "Runtime\SaiVisualNovelUiToolkitPluginHost.cs", - "Runtime\NornGraphUiToolkitPluginHost.cs" + "Runtime\IEveUiToolkitPluginProjectionAdapter.cs", + "Runtime\SaiVisualNovelUiToolkitProjectionAdapter.cs", + "Runtime\NornGraphUiToolkitProjectionAdapter.cs" ) foreach ($compileItem in $requiredCompileItems) { $expected = Join-Path $eveRoot "packages\org.gamecult.eve.unity-uitoolkit\$compileItem" diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index 2a1bcde..50e089e 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -428,7 +428,7 @@ ], "pendingProofs": [ "Tagged UPM release is cut from EveUnity rather than Eve incubation.", - "Unity EditMode or PlayMode test assembly runs from the runtime repo.", + "Unity EditMode or PlayMode test assembly runs from the runtime repo in batchmode.", "Unity batchmode or editor capture artifact is attached to the runtime conformance report." ] } @@ -478,7 +478,7 @@ "ownerRepo": "Eve", "repoRole": "runtime-incubating", "splitTarget": "EveFlutter", - "graduationTrigger": "Split when Flutter has provider picker, command transport, plugin host, and its own build/release lifecycle.", + "graduationTrigger": "Split when Flutter has provider picker, command transport, plugin projection adapters, and its own build/release lifecycle.", "requiredFixtures": ["embedded-surface"], "pluginFixtures": ["sai-vn"], "supportedFeatures": ["embeddedDocuments"], @@ -603,7 +603,7 @@ "ownerRepo": "Eve", "repoRole": "runtime-incubating", "splitTarget": "EveFlutter", - "graduationTrigger": "Split with EveFlutter when Android APK smoke, provider discovery, command transport, and plugin host are runtime-owned.", + "graduationTrigger": "Split with EveFlutter when Android APK smoke, provider discovery, command transport, and plugin projection adapters are runtime-owned.", "requiredFixtures": ["embedded-surface"], "pluginFixtures": ["sai-vn"], "supportedFeatures": ["embeddedDocuments"], @@ -677,7 +677,7 @@ "ownerRepo": "Eve", "repoRole": "runtime-incubating", "splitTarget": "EveUnity", - "graduationTrigger": "Split when Unity package has independent UPM release, plugin host behavior, and Unity test/capture lifecycle.", + "graduationTrigger": "Split when Unity package has independent UPM release, plugin projection adapter behavior, and Unity test/capture lifecycle.", "capabilityManifest": { "schemaPath": "schemas/gamecult.eve.runtime_capability.v1.schema.json", "manifestPath": "packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json" @@ -698,31 +698,37 @@ "unsupportedPlugins": [ { "pluginId": "tex.math", - "reason": "Unity UI Toolkit currently preserves embed.tex as generic Eve structure; no TeX render plugin host exists in this runtime." + "reason": "Unity UI Toolkit currently preserves embed.tex as generic Eve structure; no TeX projection adapter exists in this runtime." } ], "expectedPaths": [ "packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceLowerer.cs", - "packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginHost.cs", - "packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitPluginHost.cs", - "packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs" + "packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginProjectionAdapter.cs", + "packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitProjectionAdapter.cs", + "packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitProjectionAdapter.cs", + "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef", + "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs" ], "expectedSourceSymbols": [ { "path": "packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceLowerer.cs", - "contains": ["LowerEmbeddedDocument(", "EmbeddedDocuments", "eve-embedded-document", "FindPluginHost", "pluginHost.Lower"] + "contains": ["LowerEmbeddedDocument(", "EmbeddedDocuments", "eve-embedded-document", "FindPluginProjectionAdapter", "projectionAdapter.Lower"] }, { - "path": "packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginHost.cs", + "path": "packages/org.gamecult.eve.unity-uitoolkit/Runtime/IEveUiToolkitPluginProjectionAdapter.cs", "contains": ["PluginId", "Capabilities", "CanLower", "Lower("] }, { - "path": "packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitPluginHost.cs", + "path": "packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitProjectionAdapter.cs", "contains": ["sai.vn", "vn.stage", "story.choose", "story.continue", "story.jump", "StoryButton", "EmitStoryCommand", "EveSurfaceCommandRequest", "sai-vn-stage", "sai-vn-dialogue", "sai-vn-actions"] }, { - "path": "packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs", - "contains": ["norn.graph", "embed.norn", "NornGraphUiToolkitPluginHost", "norn-graph", "EmitGraphCommand", "EveSurfaceCommandRequest"] + "path": "packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitProjectionAdapter.cs", + "contains": ["norn.graph", "embed.norn", "NornGraphUiToolkitProjectionAdapter", "norn-graph", "EmitGraphCommand", "EveSurfaceCommandRequest"] + }, + { + "path": "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs", + "contains": ["FindPluginProjectionAdapter", "SaiStageLowersThroughRuntimeProjectionAdapter", "NornGraphLowersThroughRuntimeProjectionAdapter", "EmbeddedDocumentSlotMountsResolverBackedChildSurface", "eve-plugin-projection"] } ], "commandTransportSmoke": { @@ -757,12 +763,14 @@ }, "test": { "ownerRepo": "EveUnity", - "status": "consumer-build-smoke", + "status": "authored-editmode-tests-and-consumer-build-smoke", "evidencePaths": [ + "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef", + "packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs", "scripts/run-aetheria-unity-package-smoke.ps1" ], "pendingProofs": [ - "Unity EditMode or PlayMode test assembly runs from the runtime repo." + "Unity EditMode or PlayMode test assembly runs from the runtime repo in batchmode." ] }, "capture": {