Add Unity Norn plugin host proof

This commit is contained in:
Meta Vens 2026-07-09 00:36:48 +01:00
commit 3d8ca4676f
8 changed files with 120 additions and 18 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -18,7 +18,8 @@ namespace GameCult.Eve.UnityUIToolkit
EmbeddedDocumentResolver = embeddedDocumentResolver;
PluginHosts = pluginHosts ?? new IEveUiToolkitPluginHost[]
{
new SaiVisualNovelUiToolkitPluginHost()
new SaiVisualNovelUiToolkitPluginHost(),
new NornGraphUiToolkitPluginHost()
};
}

View file

@ -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<string> 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<EveSurfaceCommandRequest>? 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<ClickEvent>(_ => EmitGraphCommand(document, component, focusCommand, commandSink));
return graph;
}
private static void EmitGraphCommand(
EveSurfaceDocument document,
EveSurfaceComponent component,
string command,
Action<EveSurfaceCommandRequest>? 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);
}
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 729936c97f3c45f7bba0fcd6de8b40f5

View file

@ -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."

View file

@ -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": {