2026-06-17 11:24:36 +01:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
2026-06-21 09:54:15 +01:00
namespace GameCult.Aetheria.State.Verse
2026-06-17 11:24:36 +01:00
{
public static class AetheriaRuntimeClientTargetSurfaceBuilder
{
2026-06-29 05:18:50 +01:00
public static AetheriaRuntimeSurfaceDocument Build (
AetheriaRuntimeStateBootReport stateBoot ,
AetheriaRuntimeVerseHostSettingsDocument verseHost ,
string updatedAtUtc ,
long version = 1 )
{
2026-06-29 08:30:47 +01:00
var targetKind = stateBoot . TargetKind ? ? AetheriaRuntimeClientTargetKinds . StateFile ;
var targetTitle = stateBoot . Title ? ? "" ;
var targetVerseId = stateBoot . VerseId ? ? "" ;
2026-07-05 20:57:22 +01:00
var targetRuntimeId = string . IsNullOrWhiteSpace ( stateBoot . RuntimeId ) ? AetheriaRuntimeStateBoundary . DefaultClientRuntimeId : stateBoot . RuntimeId ;
2026-06-29 08:30:47 +01:00
var targetCultMeshAddress = stateBoot . CultMeshAddress ? ? "" ;
var targetStateFilePath = stateBoot . StateFilePath ? ? "" ;
var targetReplicaStateFilePath = stateBoot . ReplicaStateFilePath ? ? "" ;
var discoveryEndpointsText = string . Join ( ", " , stateBoot . DiscoveryEndpoints ? ? Array . Empty < string > ( ) ) ;
var discoveredVerses = stateBoot . DiscoveredVerses ? ? Array . Empty < AetheriaRuntimeDiscoveredVerse > ( ) ;
var targetSource = stateBoot . TargetSource ? ? "" ;
var bootFailureMessage = stateBoot . FailureMessage ? ? "" ;
var hostTitle = verseHost ? . Title ? ? targetTitle ;
var hostVerseId = verseHost ? . VerseId ? ? targetVerseId ;
var hostVisibility = verseHost ? . Visibility ? ? "unknown" ;
var hostCultMeshAddress = verseHost ? . CultMeshAddress ? ? targetCultMeshAddress ;
updatedAtUtc ? ? = "" ;
2026-06-17 11:24:36 +01:00
2026-06-29 08:30:47 +01:00
var targetKindLabel = string . Equals ( targetKind , AetheriaRuntimeClientTargetKinds . CultMeshVerse , StringComparison . Ordinal )
2026-06-17 11:24:36 +01:00
? "CultMesh Verse"
: "Local State File" ;
2026-06-29 08:30:47 +01:00
var visibilityLabel = string . IsNullOrWhiteSpace ( hostVisibility ) ? "unknown" : hostVisibility ;
2026-06-17 11:24:36 +01:00
var visibilityActionLabel = string . Equals ( visibilityLabel , "public" , StringComparison . OrdinalIgnoreCase )
? "Make Private"
: "Make Public" ;
2026-06-29 08:30:47 +01:00
var lastDiscoveryLabel = string . IsNullOrWhiteSpace ( stateBoot . LastDiscoveryAtUtc ) ? "never" : stateBoot . LastDiscoveryAtUtc ;
var lastReplicaSyncLabel = string . IsNullOrWhiteSpace ( stateBoot . LastReplicaSyncAtUtc ) ? "never" : stateBoot . LastReplicaSyncAtUtc ;
var summaryNote = ! stateBoot . SupportsLocalStateFileRead & & ! string . IsNullOrWhiteSpace ( bootFailureMessage )
? bootFailureMessage
: string . Equals ( targetSource , "state-path-override" , StringComparison . Ordinal )
2026-06-17 11:24:36 +01:00
? "AETHERIA_STATE_PATH is overriding the persisted client target. Update the environment if you want boot to follow the saved target again."
2026-06-21 13:07:38 +01:00
: "Client target edits persist in aetheria-client.cc. Verse discovery and selection mutate the same typed owner. Verse visibility changes append provider-owned Eve requests for the daemon bridge. Remote Verse targets hydrate a cache-only local replica before observers read them." ;
2026-06-17 12:14:16 +01:00
var discoveryChildren = new List < AetheriaRuntimeSurfaceComponent >
{
TextInput (
"aetheria.clientTarget.discovery.endpoints" ,
"Discovery Endpoints" ,
2026-06-29 08:30:47 +01:00
discoveryEndpointsText ,
2026-06-17 12:14:16 +01:00
AetheriaRuntimeClientTargetCommands . SetDiscoveryEndpoints ) ,
Metric ( "aetheria.clientTarget.discovery.lastScan" , "Last Scan" , lastDiscoveryLabel )
} ;
2026-06-29 08:30:47 +01:00
if ( ! string . IsNullOrWhiteSpace ( stateBoot . LastDiscoveryError ) )
2026-06-17 12:14:16 +01:00
{
discoveryChildren . Add ( Text (
"aetheria.clientTarget.discovery.error" ,
2026-06-29 08:30:47 +01:00
stateBoot . LastDiscoveryError ) ) ;
2026-06-17 12:14:16 +01:00
}
2026-06-29 08:30:47 +01:00
if ( discoveredVerses . Length = = 0 )
2026-06-17 12:14:16 +01:00
{
discoveryChildren . Add ( Text (
"aetheria.clientTarget.discovery.empty" ,
2026-06-29 08:30:47 +01:00
string . IsNullOrWhiteSpace ( discoveryEndpointsText )
2026-06-17 12:14:16 +01:00
? "Add one or more cultnet:// discovery endpoints to scan for public or federated Aetheria Verses."
: "No Verse descriptors are cached yet for the configured discovery endpoints." ) ) ;
}
else
{
discoveryChildren . Add ( Metric (
"aetheria.clientTarget.discovery.count" ,
"Known Verses" ,
2026-06-29 08:30:47 +01:00
discoveredVerses . Length . ToString ( ) ) ) ;
2026-06-17 12:14:16 +01:00
discoveryChildren . Add ( Row (
"aetheria.clientTarget.discovery.list" ,
2026-06-29 08:30:47 +01:00
discoveredVerses
. Select ( ( verse , index ) = > BuildDiscoveredVerseNode ( targetKind , targetVerseId , verse , index ) )
2026-06-17 12:14:16 +01:00
. ToArray ( ) ) ) ;
}
discoveryChildren . Add ( ButtonRow (
"aetheria.clientTarget.discovery.actions" ,
Button (
"aetheria.clientTarget.discovery.scan" ,
"Discover" ,
AetheriaRuntimeClientTargetCommands . DiscoverVerses ) ,
Button (
"aetheria.clientTarget.discovery.refresh" ,
2026-07-05 16:59:06 +01:00
"Refresh" ,
2026-06-17 12:14:16 +01:00
AetheriaRuntimeClientTargetCommands . Refresh ) ) ) ;
2026-06-17 11:24:36 +01:00
return new AetheriaRuntimeSurfaceDocument (
providerId : "aetheria" ,
providerKind : "game.menu" ,
title : "Aetheria Verse Settings" ,
version : version ,
2026-06-29 08:30:47 +01:00
updatedAtUtc : updatedAtUtc ,
2026-06-17 11:24:36 +01:00
surface : new AetheriaRuntimeSurfaceTree (
AetheriaRuntimeClientTargetCommands . SurfaceId ,
Node (
"aetheria.clientTarget.root" ,
"surface" ,
Array . Empty < ( string Key , string Value ) > ( ) ,
Node (
"aetheria.clientTarget.summary" ,
"card" ,
new [ ] { ( "title" , "Client Target" ) } ,
2026-06-29 08:30:47 +01:00
Metric ( "aetheria.clientTarget.summary.target" , "Target" , stateBoot . TargetLabel ) ,
2026-06-17 11:24:36 +01:00
Metric ( "aetheria.clientTarget.summary.transport" , "Transport" , targetKindLabel ) ,
2026-06-29 08:30:47 +01:00
Metric ( "aetheria.clientTarget.summary.source" , "Target Source" , targetSource ) ,
2026-06-17 11:24:36 +01:00
Text ( "aetheria.clientTarget.summary.note" , summaryNote ) ) ,
Node (
"aetheria.clientTarget.target" ,
"card" ,
new [ ] { ( "title" , "Target Fields" ) } ,
TextInput (
"aetheria.clientTarget.target.title" ,
"Title" ,
2026-06-29 08:30:47 +01:00
targetTitle ,
2026-06-17 11:24:36 +01:00
AetheriaRuntimeClientTargetCommands . SetTitle ) ,
TextInput (
"aetheria.clientTarget.target.verseId" ,
"Verse Id" ,
2026-06-29 08:30:47 +01:00
targetVerseId ,
2026-06-17 11:24:36 +01:00
AetheriaRuntimeClientTargetCommands . SetVerseId ) ,
2026-06-25 18:09:33 +01:00
TextInput (
"aetheria.clientTarget.target.runtimeId" ,
"Runtime Id" ,
2026-06-29 08:30:47 +01:00
targetRuntimeId ,
2026-06-25 18:09:33 +01:00
AetheriaRuntimeClientTargetCommands . SetRuntimeId ) ,
2026-06-17 11:24:36 +01:00
TextInput (
"aetheria.clientTarget.target.cultMeshAddress" ,
"CultMesh Address" ,
2026-06-29 08:30:47 +01:00
targetCultMeshAddress ,
2026-06-17 11:24:36 +01:00
AetheriaRuntimeClientTargetCommands . SetCultMeshAddress ) ,
TextInput (
"aetheria.clientTarget.target.stateFilePath" ,
"State File Path" ,
2026-06-29 08:30:47 +01:00
targetStateFilePath ,
2026-06-17 11:24:36 +01:00
AetheriaRuntimeClientTargetCommands . SetStateFilePath ) ,
2026-06-17 13:51:36 +01:00
Metric (
"aetheria.clientTarget.target.replicaStateFilePath" ,
"Replica State File" ,
2026-06-29 08:30:47 +01:00
targetReplicaStateFilePath ) ,
2026-06-17 14:29:41 +01:00
Metric (
"aetheria.clientTarget.target.replicaSyncAt" ,
"Replica Sync" ,
lastReplicaSyncLabel ) ,
Text (
"aetheria.clientTarget.target.replicaSyncError" ,
2026-06-29 08:30:47 +01:00
stateBoot . LastReplicaSyncError ) ,
2026-06-17 11:24:36 +01:00
ButtonRow (
"aetheria.clientTarget.target.actions" ,
Button (
"aetheria.clientTarget.target.cycleTransport" ,
2026-06-29 08:30:47 +01:00
string . Equals ( targetKind , AetheriaRuntimeClientTargetKinds . CultMeshVerse , StringComparison . Ordinal )
2026-06-17 11:24:36 +01:00
? "Use Local State File"
: "Use CultMesh Verse" ,
AetheriaRuntimeClientTargetCommands . CycleTargetKind ) ,
2026-06-17 14:29:41 +01:00
Button (
"aetheria.clientTarget.target.syncReplica" ,
"Sync Replica" ,
AetheriaRuntimeClientTargetCommands . SyncReplica ) ,
2026-06-17 11:24:36 +01:00
Button (
"aetheria.clientTarget.target.refresh" ,
"Refresh" ,
AetheriaRuntimeClientTargetCommands . Refresh ) ) ) ,
2026-06-17 12:14:16 +01:00
Node (
"aetheria.clientTarget.discovery" ,
"card" ,
new [ ] { ( "title" , "Verse Discovery" ) } ,
discoveryChildren . ToArray ( ) ) ,
2026-06-17 11:24:36 +01:00
Node (
"aetheria.clientTarget.host" ,
"card" ,
new [ ] { ( "title" , "Daemon Verse Host" ) } ,
2026-06-29 08:30:47 +01:00
Metric ( "aetheria.clientTarget.host.verse" , "Verse" , BuildVerseLabel ( hostTitle , hostVerseId ) ) ,
2026-06-17 11:24:36 +01:00
Metric ( "aetheria.clientTarget.host.visibility" , "Visibility" , visibilityLabel ) ,
2026-06-29 08:30:47 +01:00
Metric ( "aetheria.clientTarget.host.cultMesh" , "CultMesh" , hostCultMeshAddress ) ,
2026-06-17 11:24:36 +01:00
ButtonRow (
"aetheria.clientTarget.host.actions" ,
Button (
"aetheria.clientTarget.host.toggleVisibility" ,
visibilityActionLabel ,
AetheriaRuntimeVerseHostCommands . CycleVisibility ) ,
Button (
"aetheria.clientTarget.host.refresh" ,
"Refresh Host" ,
AetheriaRuntimeVerseHostCommands . Refresh ) ) ) ,
Node (
"aetheria.clientTarget.host.noteCard" ,
"card" ,
new [ ] { ( "title" , "Ownership" ) } ,
Text (
"aetheria.clientTarget.host.note" ,
2026-07-06 01:29:53 +01:00
"Client target edits persist through the managed target document. Visibility changes append provider-owned Eve requests against the selected Verse target." ) ) ) ,
2026-06-17 11:24:36 +01:00
Array . Empty < AetheriaRuntimeSurfaceStyleToken > ( ) ) ,
commands : new [ ]
{
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeClientTargetCommands . Refresh ,
"Refresh" ,
2026-06-21 13:00:42 +01:00
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-17 11:24:36 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeClientTargetCommands . CycleTargetKind ,
"Cycle Transport" ,
2026-06-21 13:00:42 +01:00
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-17 11:24:36 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeClientTargetCommands . SetTitle ,
"Set Title" ,
2026-06-21 13:00:42 +01:00
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-17 11:24:36 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeClientTargetCommands . SetVerseId ,
"Set Verse Id" ,
2026-06-21 13:00:42 +01:00
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-25 18:09:33 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeClientTargetCommands . SetRuntimeId ,
"Set Runtime Id" ,
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-17 11:24:36 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeClientTargetCommands . SetCultMeshAddress ,
"Set CultMesh Address" ,
2026-06-21 13:00:42 +01:00
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-17 11:24:36 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeClientTargetCommands . SetStateFilePath ,
"Set State File Path" ,
2026-06-21 13:00:42 +01:00
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-17 12:14:16 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeClientTargetCommands . SetDiscoveryEndpoints ,
"Set Discovery Endpoints" ,
2026-06-21 13:00:42 +01:00
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-17 12:14:16 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeClientTargetCommands . DiscoverVerses ,
"Discover Verses" ,
2026-06-21 13:00:42 +01:00
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-17 12:14:16 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeClientTargetCommands . SelectDiscoveredVerse ,
"Select Discovered Verse" ,
2026-06-21 13:00:42 +01:00
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-17 11:24:36 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
2026-06-17 14:29:41 +01:00
AetheriaRuntimeClientTargetCommands . SyncReplica ,
"Sync Replica" ,
2026-06-21 13:00:42 +01:00
AetheriaRuntimeSurfaceCommandTemplate . CultMeshTransport ) ,
2026-06-17 14:29:41 +01:00
new AetheriaRuntimeSurfaceCommandTemplate (
2026-06-17 11:24:36 +01:00
AetheriaRuntimeVerseHostCommands . CycleVisibility ,
"Toggle Visibility" ,
"cultmesh" ) ,
new AetheriaRuntimeSurfaceCommandTemplate (
AetheriaRuntimeVerseHostCommands . Refresh ,
"Refresh Host" ,
"cultmesh" )
} ) ;
}
private static AetheriaRuntimeSurfaceComponent Metric ( string id , string label , string value )
{
return Node ( id , "metric" , new [ ] { ( "label" , label ) , ( "value" , value ? ? "" ) } ) ;
}
private static AetheriaRuntimeSurfaceComponent Text ( string id , string value )
{
return Node ( id , "text" , new [ ] { ( "value" , value ? ? "" ) } ) ;
}
private static AetheriaRuntimeSurfaceComponent Button ( string id , string label , string command )
{
return Node ( id , "control.button" , new [ ] { ( "label" , label ? ? "" ) , ( "command" , command ? ? "" ) } ) ;
}
private static AetheriaRuntimeSurfaceComponent TextInput ( string id , string label , string value , string command )
{
return Node (
id ,
"control.text" ,
new [ ] { ( "label" , label ? ? "" ) , ( "value" , value ? ? "" ) , ( "command" , command ? ? "" ) } ) ;
}
private static AetheriaRuntimeSurfaceComponent ButtonRow (
string id ,
params AetheriaRuntimeSurfaceComponent [ ] children )
{
return Node ( id , "row" , Array . Empty < ( string Key , string Value ) > ( ) , children ) ;
}
2026-06-17 12:14:16 +01:00
private static AetheriaRuntimeSurfaceComponent Row (
string id ,
params AetheriaRuntimeSurfaceComponent [ ] children )
{
return Node ( id , "inspector.kv" , Array . Empty < ( string Key , string Value ) > ( ) , children ) ;
}
2026-06-17 11:24:36 +01:00
private static AetheriaRuntimeSurfaceComponent Node (
string id ,
string kind ,
IEnumerable < ( string Key , string Value ) > props ,
params AetheriaRuntimeSurfaceComponent [ ] children )
{
return new AetheriaRuntimeSurfaceComponent (
id ,
kind ,
props . ToDictionary ( prop = > prop . Key , prop = > prop . Value , StringComparer . Ordinal ) ,
children ? ? Array . Empty < AetheriaRuntimeSurfaceComponent > ( ) ) ;
}
2026-06-17 12:14:16 +01:00
private static AetheriaRuntimeSurfaceComponent BuildDiscoveredVerseNode (
2026-06-29 08:30:47 +01:00
string targetKind ,
string targetVerseId ,
2026-06-17 12:14:16 +01:00
AetheriaRuntimeDiscoveredVerse verse ,
int index )
{
var verseLabel = BuildVerseLabel ( verse ) ;
var address = verse . DiscoveryEndpoints . FirstOrDefault ( ) ? ? "" ;
2026-06-29 08:30:47 +01:00
var isSelected = string . Equals ( targetKind , AetheriaRuntimeClientTargetKinds . CultMeshVerse , StringComparison . Ordinal ) & &
string . Equals ( targetVerseId , verse . VerseId , StringComparison . Ordinal ) ;
2026-06-17 12:14:16 +01:00
var buttonLabel = isSelected ? "Selected" : "Select" ;
return Node (
$"aetheria.clientTarget.discovery.verse.{index}" ,
"inspector.kv" ,
Array . Empty < ( string Key , string Value ) > ( ) ,
Metric ( $"aetheria.clientTarget.discovery.verse.{index}.label" , "Verse" , verseLabel ) ,
Metric ( $"aetheria.clientTarget.discovery.verse.{index}.authority" , "Authority" , verse . AuthorityModel ) ,
Metric ( $"aetheria.clientTarget.discovery.verse.{index}.transport" , "Transport" , verse . TransportVersion ) ,
Metric ( $"aetheria.clientTarget.discovery.verse.{index}.endpoint" , "Endpoint" , address ) ,
Text (
$"aetheria.clientTarget.discovery.verse.{index}.description" ,
string . IsNullOrWhiteSpace ( verse . Description )
? "No public description."
: verse . Description ) ,
Node (
$"aetheria.clientTarget.discovery.verse.{index}.action" ,
"control.button" ,
new [ ]
{
( "label" , buttonLabel ) ,
( "command" , AetheriaRuntimeClientTargetCommands . SelectDiscoveredVerse ) ,
( "verseId" , verse . VerseId ? ? "" ) ,
( "title" , verse . DisplayName ? ? "" ) ,
( "cultMeshAddress" , address ) ,
( "discoveryEndpoints" , string . Join ( ", " , verse . DiscoveryEndpoints ? ? Array . Empty < string > ( ) ) )
} ) ) ;
}
private static string BuildVerseLabel ( AetheriaRuntimeDiscoveredVerse verse )
{
2026-06-29 08:30:47 +01:00
return verse = = null
? "Unknown Verse"
: BuildVerseLabel ( verse . DisplayName , verse . VerseId ) ;
}
private static string BuildVerseLabel ( string displayName , string verseId )
{
if ( string . IsNullOrWhiteSpace ( displayName ) )
return string . IsNullOrWhiteSpace ( verseId ) ? "Unknown Verse" : verseId ;
2026-06-17 12:14:16 +01:00
2026-06-29 08:30:47 +01:00
return string . IsNullOrWhiteSpace ( verseId ) | | string . Equals ( displayName , verseId , StringComparison . Ordinal )
? displayName
: $"{displayName} ({verseId})" ;
2026-06-17 12:14:16 +01:00
}
2026-06-17 11:24:36 +01:00
}
}