diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 1339ca3..611ac80 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -274,12 +274,17 @@ Exit criteria: ## Active Work Queue -1. Add a Unity Norn graph plugin-host proof for `embed.norn`. -2. Move EveUnity toward split readiness with runtime-owned UPM release, +1. Move EveUnity toward split readiness with runtime-owned UPM release, Unity test, and capture lifecycle evidence. Recently cut: +- 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 diff --git a/docs/parity-testing-harness.md b/docs/parity-testing-harness.md index ba07355..82c79f7 100644 --- a/docs/parity-testing-harness.md +++ b/docs/parity-testing-harness.md @@ -26,8 +26,8 @@ transport schema, and incubation metadata. The central parity manifest may 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 first `sai.vn` plugin-host -proof while leaving `norn.graph` and `tex.math` as explicit unsupported plugin +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 semantics. ## Run diff --git a/packages/org.gamecult.eve.unity-uitoolkit/README.md b/packages/org.gamecult.eve.unity-uitoolkit/README.md index 76631f3..010b799 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/README.md +++ b/packages/org.gamecult.eve.unity-uitoolkit/README.md @@ -11,8 +11,10 @@ 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. -`norn.graph` and `tex.math` still pass through as generic Eve component -structure until they gain explicit Unity plugin hosts. +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. 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 diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceOptions.cs b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceOptions.cs index 4df4a70..9d512e1 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceOptions.cs +++ b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceOptions.cs @@ -18,7 +18,8 @@ namespace GameCult.Eve.UnityUIToolkit EmbeddedDocumentResolver = embeddedDocumentResolver; PluginHosts = pluginHosts ?? new IEveUiToolkitPluginHost[] { - new SaiVisualNovelUiToolkitPluginHost() + new SaiVisualNovelUiToolkitPluginHost(), + new NornGraphUiToolkitPluginHost() }; } diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs new file mode 100644 index 0000000..de915ce --- /dev/null +++ b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using GameCult.Eve.Surface; +using GameCult.Mesh; +using UnityEngine; +using UnityEngine.UIElements; + +#nullable enable + +namespace GameCult.Eve.UnityUIToolkit +{ + public sealed class NornGraphUiToolkitPluginHost : IEveUiToolkitPluginHost + { + public string PluginId => "norn.graph"; + + public IReadOnlyList Capabilities { get; } = new[] + { + "embed.norn" + }; + + public bool CanLower(EveSurfaceComponent component) + { + return string.Equals(component.Kind, "embed.norn", StringComparison.Ordinal); + } + + public VisualElement Lower( + EveSurfaceComponent component, + EveSurfaceDocument document, + Action? commandSink) + { + var graph = new VisualElement(); + graph.AddToClassList("eve-plugin-host"); + graph.AddToClassList("eve-plugin-norn-graph"); + graph.AddToClassList("norn-graph"); + graph.style.flexDirection = FlexDirection.Column; + + var title = component.GetProp("label", component.GetProp("title", "Norn graph")); + var titleLabel = new Label(title); + titleLabel.AddToClassList("norn-graph-title"); + titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold; + graph.Add(titleLabel); + + var mode = component.GetProp("mode", component.GetProp("layoutMode", "embedded")); + var modeLabel = new Label(mode); + modeLabel.AddToClassList("norn-graph-mode"); + graph.Add(modeLabel); + + var focusCommand = component.GetProp("command", component.GetProp("focusCommand", "graph.focus")); + graph.RegisterCallback(_ => EmitGraphCommand(document, component, focusCommand, commandSink)); + + return graph; + } + + private static void EmitGraphCommand( + EveSurfaceDocument document, + EveSurfaceComponent component, + string command, + Action? commandSink) + { + if (commandSink == null || string.IsNullOrWhiteSpace(command)) + return; + + commandSink(new EveSurfaceCommandRequest( + document.ProviderId, + document.Surface.Id, + ResolveOperation(document, command), + CultMesh.OperationPayload(component.Props), + DateTimeOffset.UtcNow, + "unity-uitoolkit")); + } + + private static CultMeshOperationInvocationDescriptor ResolveOperation( + EveSurfaceDocument document, + string command) + { + foreach (var template in document.Commands) + { + if (string.Equals(template.Command, command, StringComparison.Ordinal)) + return CultMesh.OperationInvocation(template.Operation); + } + + return CultMesh.OperationInvocation(command); + } + } +} diff --git a/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs.meta b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs.meta new file mode 100644 index 0000000..07345b2 --- /dev/null +++ b/packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 729936c97f3c45f7bba0fcd6de8b40f5 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 ac3299c..6f888e2 100644 --- a/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json +++ b/packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json @@ -22,13 +22,16 @@ "story.jump" ], "host": "SaiVisualNovelUiToolkitPluginHost" + }, + { + "pluginId": "norn.graph", + "capabilities": [ + "embed.norn" + ], + "host": "NornGraphUiToolkitPluginHost" } ], "unsupportedPlugins": [ - { - "pluginId": "norn.graph", - "reason": "Unity UI Toolkit currently preserves embed.norn as generic Eve structure; no Norn graph plugin host or layout adapter exists in this runtime." - }, { "pluginId": "tex.math", "reason": "Unity UI Toolkit currently preserves embed.tex as generic Eve structure; no TeX render plugin host exists in this runtime." diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index 0b985f3..4c7531a 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -428,8 +428,7 @@ ], "pendingProofs": [ "UPM package and release lifecycle are runtime-owned.", - "Unity test and capture lifecycle are runtime-owned.", - "Norn graph plugin host behavior declares reusable embed.norn capabilities." + "Unity test and capture lifecycle are runtime-owned." ] } ], @@ -689,13 +688,13 @@ { "pluginId": "sai.vn", "capabilities": ["vn.stage", "story.choose", "story.continue", "story.jump"] + }, + { + "pluginId": "norn.graph", + "capabilities": ["embed.norn"] } ], "unsupportedPlugins": [ - { - "pluginId": "norn.graph", - "reason": "Unity UI Toolkit currently preserves embed.norn as generic Eve structure; no Norn graph plugin host or layout adapter exists in this runtime." - }, { "pluginId": "tex.math", "reason": "Unity UI Toolkit currently preserves embed.tex as generic Eve structure; no TeX render plugin host exists in this runtime." @@ -704,7 +703,8 @@ "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/SaiVisualNovelUiToolkitPluginHost.cs", + "packages/org.gamecult.eve.unity-uitoolkit/Runtime/NornGraphUiToolkitPluginHost.cs" ], "expectedSourceSymbols": [ { @@ -718,6 +718,10 @@ { "path": "packages/org.gamecult.eve.unity-uitoolkit/Runtime/SaiVisualNovelUiToolkitPluginHost.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"] } ], "commandTransportSmoke": {