From 22281891cbf9258db80c15a9fd465e6fa251e7dd Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 16 Aug 2026 23:03:30 +0200 Subject: [PATCH] Complete Persona context and gestalt individuation --- crates/ghostlight-dungeon/src/domain.rs | 14 +++ crates/ghostlight-dungeon/src/gestalt.rs | 23 +++- crates/ghostlight-dungeon/src/kernel.rs | 114 +++++++++++++----- crates/ghostlight-dungeon/src/main.rs | 47 +++----- crates/ghostlight-dungeon/src/mesh.rs | 97 ++++++++++++--- crates/ghostlight-dungeon/src/persona.rs | 58 ++++++++- crates/ghostlight-dungeon/src/surface.rs | 39 +++++- crates/ghostlight-dungeon/src/turn.rs | 9 ++ .../ghostlight-persona-projection/src/lib.rs | 4 +- notes/ghostlight-dungeon.md | 8 +- .../{index-DDHZWg6R.js => index-DSqcLvft.js} | 12 +- web/dist/index.html | 4 +- web/index.html | 2 +- web/src/main.ts | 2 +- 14 files changed, 341 insertions(+), 92 deletions(-) rename web/dist/assets/{index-DDHZWg6R.js => index-DSqcLvft.js} (84%) diff --git a/crates/ghostlight-dungeon/src/domain.rs b/crates/ghostlight-dungeon/src/domain.rs index 60c5cd1..c214f86 100644 --- a/crates/ghostlight-dungeon/src/domain.rs +++ b/crates/ghostlight-dungeon/src/domain.rs @@ -149,6 +149,14 @@ pub struct GestaltPromotion { pub location_id: String, } +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] +pub struct GestaltIndividuation { + pub gestalt_id: String, + pub expected_gestalt_version: u64, + pub member: GestaltMemberDelta, + pub location_id: String, +} + #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)] pub struct GestaltDemotion { pub actor_id: String, @@ -158,6 +166,8 @@ pub struct GestaltDemotion { #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Default)] pub struct GestaltPresencePlan { + #[serde(default)] + pub individuations: Vec, pub promotions: Vec, pub demotions: Vec, } @@ -422,6 +432,10 @@ pub enum WorldCommand { expected_member_version: u64, location_id: String, }, + IndividuateGestaltMember { + expected_revision: u64, + individuation: GestaltIndividuation, + }, DematerializeGestaltMember { expected_revision: u64, actor_id: String, diff --git a/crates/ghostlight-dungeon/src/gestalt.rs b/crates/ghostlight-dungeon/src/gestalt.rs index b4dc268..df8c180 100644 --- a/crates/ghostlight-dungeon/src/gestalt.rs +++ b/crates/ghostlight-dungeon/src/gestalt.rs @@ -34,7 +34,7 @@ impl GestaltPresencePlanner { model: self.model_name.clone(), snapshot_binding: format!("campaign:{}:revision:{}", campaign.id, campaign.revision), lived_stream: format!( - "Choose reversible Persona population presence after this event. Promote a member only when individualized interaction, perception, custody, promise, injury, possession, or relationship now matters. Demote a materialized member when they are no longer scene-relevant. Use only the supplied IDs and exact versions. Never place a promoted member outside the player location. Aggregate deltas must remain empty; population learning requires separate review. Emit the exact JSON schema.\nSCHEMA:\n{}\nCANDIDATES:\n{}\nEVENT:\n{}", + "Choose reversible Persona population presence after this event. Promote an existing member when they become individually relevant. If the event makes an anonymous population member individually relevant and no supplied member fits, individuate exactly one durable member delta from the gestalt baseline; use a new stable lowercase id, version 0, the exact gestalt id/version, no materialized actor id, and record only personal departures from the shared baseline. Demote a materialized member when they are no longer scene-relevant. Never place a promoted or individuated member outside the player location. Aggregate deltas must remain empty; population learning requires separate review. Emit the exact JSON schema.\nSCHEMA:\n{}\nCANDIDATES:\n{}\nEVENT:\n{}", serde_json::to_string_pretty(&schema)?, candidates, event_summary ), output_schema: Some(schema), @@ -58,6 +58,27 @@ fn validate_plan( player_location: &str, ) -> Result<()> { let mut members = BTreeSet::new(); + for individuation in &plan.individuations { + let member = &individuation.member; + let gestalt = campaign + .gestalts + .get(&individuation.gestalt_id) + .ok_or_else(|| anyhow!("presence plan invented a gestalt"))?; + if individuation.expected_gestalt_version != gestalt.version + || individuation.location_id != player_location + || member.gestalt_id != individuation.gestalt_id + || member.version != 0 + || member.materialized_actor_id.is_some() + || member.id.trim().is_empty() + || member.name.trim().is_empty() + || campaign.gestalt_members.contains_key(&member.id) + || !members.insert(member.id.clone()) + { + return Err(anyhow!( + "presence individuation does not match its snapshot" + )); + } + } for promotion in &plan.promotions { if !members.insert(promotion.member_id.clone()) { return Err(anyhow!("presence plan promotes one member twice")); diff --git a/crates/ghostlight-dungeon/src/kernel.rs b/crates/ghostlight-dungeon/src/kernel.rs index a859a82..73e4c33 100644 --- a/crates/ghostlight-dungeon/src/kernel.rs +++ b/crates/ghostlight-dungeon/src/kernel.rs @@ -348,6 +348,22 @@ fn execute( "direct materialization command", ) } + WorldCommand::IndividuateGestaltMember { + expected_revision, + individuation, + } => { + require_revision(&campaign, expected_revision)?; + let before = campaign.clone(); + apply_individuation(&mut campaign, &individuation)?; + commit_gestalt_presence( + store, + row, + before, + campaign, + "individuate_gestalt_member", + "direct individuation command", + ) + } WorldCommand::DematerializeGestaltMember { expected_revision, actor_id, @@ -385,6 +401,9 @@ fn execute( for demotion in &plan.demotions { apply_demotion(&mut candidate, demotion)?; } + for individuation in &plan.individuations { + apply_individuation(&mut candidate, individuation)?; + } for promotion in &plan.promotions { apply_promotion(&mut candidate, promotion)?; } @@ -505,6 +524,42 @@ fn execute( } } +fn apply_individuation( + campaign: &mut Campaign, + individuation: &GestaltIndividuation, +) -> Result<(), KernelError> { + let member = &individuation.member; + let gestalt = campaign + .gestalts + .get(&individuation.gestalt_id) + .ok_or_else(|| KernelError::Invalid("gestalt is unknown".into()))?; + if gestalt.version != individuation.expected_gestalt_version + || member.gestalt_id != individuation.gestalt_id + || member.version != 0 + || member.materialized_actor_id.is_some() + || member.id.trim().is_empty() + || member.name.trim().is_empty() + || campaign.gestalt_members.contains_key(&member.id) + { + return Err(KernelError::Invalid( + "gestalt individuation is stale or malformed".into(), + )); + } + campaign + .gestalt_members + .insert(member.id.clone(), member.clone()); + apply_promotion( + campaign, + &GestaltPromotion { + gestalt_id: individuation.gestalt_id.clone(), + expected_gestalt_version: individuation.expected_gestalt_version, + member_id: member.id.clone(), + expected_member_version: 0, + location_id: individuation.location_id.clone(), + }, + ) +} + fn apply_promotion( campaign: &mut Campaign, promotion: &GestaltPromotion, @@ -1664,30 +1719,27 @@ mod tests { pressures: vec![], }, ); - seed.gestalt_members.insert( - "john".into(), - GestaltMemberDelta { - schema: "ghostlight.gestalt_member_delta.v1".into(), - id: "john".into(), - gestalt_id: "village".into(), - version: 0, - name: "John".into(), - capability_additions: BTreeSet::from(["master blacksmith".into()]), - capability_removals: BTreeSet::new(), - knowledge_additions: BTreeSet::new(), - knowledge_removals: BTreeSet::new(), - equipment: BTreeSet::from(["John's hammer".into()]), - conditions: BTreeSet::new(), - obligations: BTreeSet::new(), - relationships: BTreeMap::from([("player".into(), "new acquaintance".into())]), - goals: vec![], - memories: vec!["met the player".into()], - last_location_id: None, - materialized_actor_id: None, - last_relevant_revision: 0, - relevance_lease_until_revision: 0, - }, - ); + let john = GestaltMemberDelta { + schema: "ghostlight.gestalt_member_delta.v1".into(), + id: "john".into(), + gestalt_id: "village".into(), + version: 0, + name: "John".into(), + capability_additions: BTreeSet::from(["master blacksmith".into()]), + capability_removals: BTreeSet::new(), + knowledge_additions: BTreeSet::new(), + knowledge_removals: BTreeSet::new(), + equipment: BTreeSet::from(["John's hammer".into()]), + conditions: BTreeSet::new(), + obligations: BTreeSet::new(), + relationships: BTreeMap::from([("player".into(), "new acquaintance".into())]), + goals: vec![], + memories: vec!["met the player".into()], + last_location_id: None, + materialized_actor_id: None, + last_relevant_revision: 0, + relevance_lease_until_revision: 0, + }; kernel .command(WorldCommand::CreateCampaign { campaign: seed, @@ -1697,13 +1749,14 @@ mod tests { .await .unwrap(); let first = kernel - .command(WorldCommand::MaterializeGestaltMember { + .command(WorldCommand::IndividuateGestaltMember { expected_revision: 0, - gestalt_id: "village".into(), - expected_gestalt_version: 0, - member_id: "john".into(), - expected_member_version: 0, - location_id: "room".into(), + individuation: GestaltIndividuation { + gestalt_id: "village".into(), + expected_gestalt_version: 0, + member: john, + location_id: "room".into(), + }, }) .await .unwrap(); @@ -1788,6 +1841,7 @@ mod tests { ); let bad_plan = GestaltPresencePlan { + individuations: vec![], demotions: vec![GestaltDemotion { actor_id: "member:john".into(), aggregate_delta: GestaltAggregateDelta::default(), diff --git a/crates/ghostlight-dungeon/src/main.rs b/crates/ghostlight-dungeon/src/main.rs index d4cb053..34ba20f 100644 --- a/crates/ghostlight-dungeon/src/main.rs +++ b/crates/ghostlight-dungeon/src/main.rs @@ -16,7 +16,7 @@ use ghostlight_dungeon::{ }, gestalt::GestaltPresencePlanner, kernel::CommandResult, - mesh::MeshPublisher, + mesh::{CampaignMeshSnapshot, MeshPublisher}, model::{DeepSeekPort, ModelPort, ModelStageRequest, run_validated_stage}, narrator::Narrator, persistence::CampaignStore, @@ -1052,6 +1052,7 @@ fn player_http_command_allowed(command: &WorldCommand, player_actor_id: &str) -> | WorldCommand::ExpandRegion { .. } | WorldCommand::MaterializeGestaltMember { .. } | WorldCommand::DematerializeGestaltMember { .. } + | WorldCommand::IndividuateGestaltMember { .. } | WorldCommand::ReconcileGestaltPresence { .. } | WorldCommand::ResolveReactionWave { .. } | WorldCommand::BeginNpcAction { .. } => false, @@ -1112,7 +1113,18 @@ async fn refresh_mesh(state: &AppState) -> anyhow::Result { .filter(|value| value.campaign_id == campaign.id) .collect::>(); narrations.sort_by_key(|value| value.source_revision); - snapshots.push((campaign, narrations)); + snapshots.push(CampaignMeshSnapshot { + campaign, + narrations, + evidence: runtime.store.load_all("vault_evidence_receipt.v1")?, + commits: runtime.store.load_all("world_commit_receipt.v1")?, + stages: runtime.store.load_all("persona_stage_receipt.v1")?, + strategic_ticks: runtime.store.load_all("strategic_tick.v1")?, + gestalt_receipts: runtime + .store + .load_all("gestalt_materialization_receipt.v1")?, + rejected: runtime.store.load_all("rejected_proposal_receipt.v1")?, + }); } let publisher = state.mesh.clone(); let deepseek = state.deepseek_status.clone(); @@ -1341,33 +1353,10 @@ async fn operator_inspector(headers: HeaderMap, State(state): State) - return (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(); } }; - let evidence = runtime - .store - .load_all::("vault_evidence_receipt.v1") - .unwrap_or_default(); - let commits = runtime - .store - .load_all::("world_commit_receipt.v1") - .unwrap_or_default(); - let stages = runtime - .store - .load_all::("persona_stage_receipt.v1") - .unwrap_or_default(); - let rejected = runtime - .store - .load_all::("rejected_proposal_receipt.v1") - .unwrap_or_default(); - let strategic_ticks = runtime - .store - .load_all::("strategic_tick.v1") - .unwrap_or_default(); - let gestalt_receipts = runtime - .store - .load_all::( - "gestalt_materialization_receipt.v1", - ) - .unwrap_or_default(); - Json(serde_json::json!({"schema":"ghostlight.operator_inspector.v1","campaign":campaign,"evidence":evidence,"commit_receipts":commits,"model_stage_receipts":stages,"strategic_ticks":strategic_ticks,"gestalt_materialization_receipts":gestalt_receipts,"rejected_proposals":rejected,"scheduler":{"live_turn_pressure":state.live_turns.load(Ordering::SeqCst)}})).into_response() + match state.mesh.operator_surface(campaign.id) { + Ok(surface) => Json(surface).into_response(), + Err(error) => (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response(), + } } async fn process_due_ticks( diff --git a/crates/ghostlight-dungeon/src/mesh.rs b/crates/ghostlight-dungeon/src/mesh.rs index 3cff106..73a66a5 100644 --- a/crates/ghostlight-dungeon/src/mesh.rs +++ b/crates/ghostlight-dungeon/src/mesh.rs @@ -8,7 +8,7 @@ use crate::{ WorldActionProposal, WorldClock, WorldCommitReceipt, WorldCompilePreview, WorldFact, }, model::ModelStageReceipt, - surface::player_surface, + surface::{operator_surface, player_surface}, }; use anyhow::{Context, Result}; use chrono::Utc; @@ -27,6 +27,18 @@ pub const PROVIDER_ID: &str = "gamecult.ghostlight.dungeon"; pub const HEALTH_KEY: &str = "ghostlight:dungeon:health"; pub const ADVERTISEMENT_KEY: &str = "eve:provider:gamecult.ghostlight.dungeon"; +#[derive(Clone)] +pub struct CampaignMeshSnapshot { + pub campaign: Campaign, + pub narrations: Vec, + pub evidence: Vec, + pub commits: Vec, + pub stages: Vec, + pub strategic_ticks: Vec, + pub gestalt_receipts: Vec, + pub rejected: Vec, +} + #[derive(Clone, Debug, PartialEq, DatabaseEntry)] #[cultcache(type = "gamecult.eve.surface", schema = "gamecult.eve.surface.v1")] pub struct EveSurfaceRecord { @@ -117,7 +129,7 @@ impl MeshPublisher { pub fn publish_snapshot( &self, - campaigns: &[(Campaign, Vec)], + campaigns: &[CampaignMeshSnapshot], deepseek_status: &str, live_turn_pressure: usize, ) -> Result { @@ -157,18 +169,32 @@ impl MeshPublisher { }); let surfaces = campaigns .iter() - .map(|(campaign, _)| { - json!({ - "schema":"gamecult.eve.surface.v1", - "surfaceId":format!("ghostlight.campaign.{}",campaign.id), - "key":format!("eve:surface:ghostlight.campaign.{}",campaign.id), - "transport":"cultmesh-record", - "status":"available", - "audience":"authenticated-player", - "mode":"interactive", - "surfaceKind":"interactive-world", - "interactionModel":"provider-command-receipts" - }) + .flat_map(|snapshot| { + let campaign = &snapshot.campaign; + [ + json!({ + "schema":"gamecult.eve.surface.v1", + "surfaceId":format!("ghostlight.campaign.{}",campaign.id), + "key":format!("eve:surface:ghostlight.campaign.{}",campaign.id), + "transport":"cultmesh-record", + "status":"available", + "audience":"authenticated-player", + "mode":"interactive", + "surfaceKind":"interactive-world", + "interactionModel":"provider-command-receipts" + }), + json!({ + "schema":"gamecult.eve.surface.v1", + "surfaceId":format!("ghostlight.operator.{}",campaign.id), + "key":format!("eve:operator:ghostlight.campaign.{}",campaign.id), + "transport":"cultmesh-record", + "status":"available", + "audience":"authenticated-operator", + "mode":"inspect", + "surfaceKind":"operator-inspector", + "interactionModel":"read-only-receipt-inspection" + }), + ] }) .collect::>(); let advertisement = json!({ @@ -212,8 +238,9 @@ impl MeshPublisher { value: advertisement, }, )?; - for (campaign, narrations) in campaigns { - let surface = player_surface(campaign, narrations); + for snapshot in campaigns { + let campaign = &snapshot.campaign; + let surface = player_surface(campaign, &snapshot.narrations); let key = format!("eve:surface:ghostlight.campaign.{}", campaign.id); self.put_and_publish( &mut node, @@ -222,6 +249,34 @@ impl MeshPublisher { value: surface.clone(), }, )?; + let operator = operator_surface( + campaign, + &snapshot.evidence, + &snapshot.commits, + &snapshot.stages, + &snapshot.strategic_ticks, + &snapshot.gestalt_receipts, + &snapshot.rejected, + live_turn_pressure, + ); + self.put_and_publish( + &mut node, + format!("eve:operator:ghostlight.campaign.{}", campaign.id), + &EveSurfaceRecord { + value: operator.clone(), + }, + )?; + self.put_and_publish( + &mut node, + format!("eve:operator-state:ghostlight.campaign.{}", campaign.id), + &EveSurfaceStateRecord { + provider_id: PROVIDER_ID.into(), + title: format!("{} operator", campaign.name), + version: campaign.revision as i64, + updated_at: updated_at.clone(), + surface: operator, + }, + )?; self.put_and_publish( &mut node, format!("eve:surface-state:ghostlight.campaign.{}", campaign.id), @@ -246,6 +301,16 @@ impl MeshPublisher { .map(|record| record.value) } + pub fn operator_surface(&self, campaign_id: uuid::Uuid) -> Result { + self.node + .lock() + .map_err(|_| anyhow::anyhow!("mesh lock poisoned"))? + .get_required::(&format!( + "eve:operator:ghostlight.campaign.{campaign_id}" + )) + .map(|record| record.value) + } + fn put_and_publish( &self, node: &mut CultMeshNode, diff --git a/crates/ghostlight-dungeon/src/persona.rs b/crates/ghostlight-dungeon/src/persona.rs index a374b09..9706db7 100644 --- a/crates/ghostlight-dungeon/src/persona.rs +++ b/crates/ghostlight-dungeon/src/persona.rs @@ -12,10 +12,12 @@ use std::sync::Arc; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct PermittedActorSlice { pub actor_id: String, + pub location_id: String, pub snapshot_binding: String, pub identity_experience: Vec, pub memories: Vec, pub perceived_events: Vec, + pub perceived_actors: std::collections::BTreeMap, pub relationships: Vec, pub goals: Vec, pub knowledge: Vec, @@ -129,7 +131,8 @@ impl PersonaProjectionEngine { self.permit .require(&slice.actor_id, &slice.snapshot_binding, "interpreter") .await?; - let schema = serde_json::to_value(schema_for!(PersonaProposalBundle))?; + let mut schema = serde_json::to_value(schema_for!(PersonaProposalBundle))?; + constrain_interpreter_schema(&mut schema, &slice)?; let interpreted = run_validated_stage( self.model.as_ref(), &ModelStageRequest { @@ -138,10 +141,11 @@ impl PersonaProjectionEngine { snapshot_binding: slice.snapshot_binding.clone(), lived_stream: build_interpreter_prompt(&InterpreterPrompt { identity: &slice.actor_id, + typed_context: &typed_context, lived_stream: &lived.text, persona_output: &persona.narrative, output_schema: &serde_json::to_string_pretty(&schema)?, - domain_guidance: "Record only private changes supported by the lived stream. World actions are attempts, not completed effects. Every world action actor_id must match the identity.", + domain_guidance: "Record only private changes supported by the lived stream and typed context. World actions are attempts, not completed effects. Use only exact actor ids and state-reference tokens admitted by the output schema.", }), output_schema: Some(schema), source_receipt_ids: slice.source_receipt_ids, @@ -166,6 +170,51 @@ impl PersonaProjectionEngine { } } +fn constrain_interpreter_schema( + schema: &mut serde_json::Value, + slice: &PermittedActorSlice, +) -> Result<()> { + let allowed_references = slice + .capabilities + .iter() + .map(|value| format!("capability:{value}")) + .chain( + slice + .knowledge + .iter() + .map(|value| format!("knowledge:{value}")), + ) + .chain( + slice + .affordances + .iter() + .map(|value| format!("equipment:{value}")), + ) + .chain(std::iter::once(format!("location:{}", slice.location_id))) + .collect::>(); + let world_action = schema + .pointer_mut("/$defs/WorldActionProposal/properties") + .and_then(|value| value.as_object_mut()) + .ok_or_else(|| anyhow!("Persona proposal schema has no world action properties"))?; + world_action.insert( + "actor_id".into(), + serde_json::json!({"const":slice.actor_id}), + ); + world_action.insert( + "state_references".into(), + serde_json::json!({"type":"array","items":{"type":"string","enum":allowed_references}}), + ); + let relationship_updates = schema + .pointer_mut("/$defs/ActorStateDelta/properties/relationship_updates") + .and_then(|value| value.as_object_mut()) + .ok_or_else(|| anyhow!("Persona proposal schema has no relationship updates"))?; + relationship_updates.insert( + "propertyNames".into(), + serde_json::json!({"enum":slice.perceived_actors.keys().collect::>()}), + ); + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -181,10 +230,15 @@ mod tests { }; let slice = PermittedActorSlice { actor_id: "npc".into(), + location_id: "room".into(), snapshot_binding: "campaign:1".into(), identity_experience: vec!["A tired navigator".into()], memories: vec![], perceived_events: vec![], + perceived_actors: std::collections::BTreeMap::from([( + "player".into(), + "Player".into(), + )]), relationships: vec![], goals: vec![], knowledge: vec![], diff --git a/crates/ghostlight-dungeon/src/surface.rs b/crates/ghostlight-dungeon/src/surface.rs index dc68f41..c32293b 100644 --- a/crates/ghostlight-dungeon/src/surface.rs +++ b/crates/ghostlight-dungeon/src/surface.rs @@ -1,4 +1,10 @@ -use crate::domain::{Campaign, NarrationProjection}; +use crate::{ + domain::{ + Campaign, GestaltMaterializationReceipt, NarrationProjection, RejectedProposalReceipt, + StrategicTickReceipt, VaultEvidenceReceipt, WorldCommitReceipt, + }, + model::ModelStageReceipt, +}; use serde_json::{Value, json}; pub fn player_surface(campaign: &Campaign, narrations: &[NarrationProjection]) -> Value { @@ -47,6 +53,37 @@ pub fn player_surface(campaign: &Campaign, narrations: &[NarrationProjection]) - }) } +pub fn operator_surface( + campaign: &Campaign, + evidence: &[VaultEvidenceReceipt], + commits: &[WorldCommitReceipt], + stages: &[ModelStageReceipt], + strategic_ticks: &[StrategicTickReceipt], + gestalt_receipts: &[GestaltMaterializationReceipt], + rejected: &[RejectedProposalReceipt], + live_turn_pressure: usize, +) -> Value { + let typed = json!({ + "campaign": campaign, + "evidence": evidence, + "commit_receipts": commits, + "model_stage_receipts": stages, + "strategic_ticks": strategic_ticks, + "gestalt_materialization_receipts": gestalt_receipts, + "rejected_proposals": rejected, + "scheduler": {"live_turn_pressure": live_turn_pressure} + }); + json!({ + "type":"surface-state", "schema":"gamecult.eve.surface.v1", "providerId":"gamecult.ghostlight.dungeon", + "providerKind":"narrative.simulation.operator", "title":format!("{} operator",campaign.name), "version":campaign.revision, + "surface":{"id":format!("ghostlight.operator.{}",campaign.id),"root":{"id":"dungeon.operator.root","kind":"surface","props":{},"children":[ + {"id":"dungeon.operator.status","kind":"card","props":{"title":format!("Revision {} · {} model stages · {} rejected proposals",campaign.revision,stages.len(),rejected.len())},"children":[]}, + {"id":"dungeon.operator.typed","kind":"code","props":{"language":"json","value":serde_json::to_string_pretty(&typed).unwrap_or_else(|_| "operator projection failed".into())},"children":[]} + ]},"styles":{"tokens":{"colorBackground":"#0c1110","colorPanel":"#17201d","colorText":"#e8e1cf","colorMuted":"#9aa69f","colorAccent":"#d49b58"}}}, + "commands":[] + }) +} + fn join(values: &std::collections::BTreeSet) -> String { if values.is_empty() { "none".into() diff --git a/crates/ghostlight-dungeon/src/turn.rs b/crates/ghostlight-dungeon/src/turn.rs index 7687afd..b4e0ad7 100644 --- a/crates/ghostlight-dungeon/src/turn.rs +++ b/crates/ghostlight-dungeon/src/turn.rs @@ -58,19 +58,28 @@ pub async fn appraise_present( }) .cloned() .collect(); + let perceived_actors = campaign + .actors + .values() + .filter(|actor| actor.location_id == player.location_id) + .map(|actor| (actor.id.clone(), actor.name.clone())) + .collect::>(); let mut jobs = tokio::task::JoinSet::new(); for actor in actors { let engine = engine.clone(); let snapshot = format!("campaign:{}:revision:{}", campaign.id, campaign.revision); let event = event_summary.to_owned(); let receipts = campaign.branch_origin.evidence_receipt_ids.clone(); + let perceived_actors = perceived_actors.clone(); jobs.spawn(async move { let slice = PermittedActorSlice { actor_id: actor.id.clone(), + location_id: actor.location_id.clone(), snapshot_binding: snapshot, identity_experience: vec![format!("You are {}.", actor.name)], memories: actor.memories, perceived_events: vec![event], + perceived_actors, relationships: actor .relationships .into_iter() diff --git a/crates/ghostlight-persona-projection/src/lib.rs b/crates/ghostlight-persona-projection/src/lib.rs index 209a4fe..5b76dbf 100644 --- a/crates/ghostlight-persona-projection/src/lib.rs +++ b/crates/ghostlight-persona-projection/src/lib.rs @@ -21,6 +21,7 @@ pub struct PersonaPrompt<'a> { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct InterpreterPrompt<'a> { pub identity: &'a str, + pub typed_context: &'a str, pub lived_stream: &'a str, pub persona_output: &'a str, pub output_schema: &'a str, @@ -48,9 +49,10 @@ pub fn build_persona_prompt(input: &PersonaPrompt<'_>) -> String { pub fn build_interpreter_prompt(input: &InterpreterPrompt<'_>) -> String { format!( - "\nYou are the private Interpreter for {identity}. Convert the natural Persona turn into typed candidate effects supported by the lived stream. Candidates are proposals only; the owning runtime validates and commits them. Do not invent knowledge, capability, custody, perception, or completed consequences.\n\nDomain guidance:\n{guidance}\n\nLived stream:\n{stream}\n\nPersona turn:\n{output}\n\nReturn exactly one JSON object matching this schema:\n{schema}", + "\nYou are the private Interpreter for {identity}. Convert the natural Persona turn into typed candidate effects supported by the lived stream and permissioned typed context. Candidates are proposals only; the owning runtime validates and commits them. Do not invent knowledge, capability, custody, perception, identifiers, state references, or completed consequences.\n\nDomain guidance:\n{guidance}\n\nPermissioned typed context:\n{context}\n\nLived stream:\n{stream}\n\nPersona turn:\n{output}\n\nReturn exactly one JSON object matching this schema:\n{schema}", identity = input.identity, guidance = input.domain_guidance, + context = input.typed_context, stream = input.lived_stream, output = input.persona_output, schema = input.output_schema, diff --git a/notes/ghostlight-dungeon.md b/notes/ghostlight-dungeon.md index ef01d02..8da3f60 100644 --- a/notes/ghostlight-dungeon.md +++ b/notes/ghostlight-dungeon.md @@ -71,8 +71,12 @@ materialize newly relevant members and fold irrelevant members back into their persistent deltas. The planner may propose presence only; it cannot promote population-wide learning or mutate state directly. -World compilation seeds collective populations and a bounded roster of durable -member identities. Population-scale capabilities, knowledge, resources, goals, +World compilation may seed collective populations and a bounded roster of durable +member identities, but that roster is not exhaustive. When an anonymous member +first becomes identity-bearing in play, the relevance planner may propose a new +member delta and the WorldKernel atomically creates that durable identity and its +active actor slot. Existing deltas are always preferred when they fit, preventing +the same person from being invented twice. Population-scale capabilities, knowledge, resources, goals, and pressures belong to the gestalt. A member record owns identity, memories, relationships, possessions, injuries, obligations, last known location, and additions to or removals from the shared baseline. Materialization is a derived diff --git a/web/dist/assets/index-DDHZWg6R.js b/web/dist/assets/index-DSqcLvft.js similarity index 84% rename from web/dist/assets/index-DDHZWg6R.js rename to web/dist/assets/index-DSqcLvft.js index 420f26b..ede70c7 100644 --- a/web/dist/assets/index-DDHZWg6R.js +++ b/web/dist/assets/index-DSqcLvft.js @@ -1,4 +1,4 @@ -(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))i(o);new MutationObserver(o=>{for(const a of o)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&i(s)}).observe(document,{childList:!0,subtree:!0});function n(o){const a={};return o.integrity&&(a.integrity=o.integrity),o.referrerPolicy&&(a.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?a.credentials="include":o.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function i(o){if(o.ep)return;o.ep=!0;const a=n(o);fetch(o.href,a)}})();const j=new WeakMap,be=20;function rt(e,t,n,i,o,a,s,l){const f=st(e.canvas);if(!f)return!1;const c=f.gl;if(!lt(f,a,s))return j.set(e.canvas,null),!1;const r=ot(t,n,i,o,l);c.bindFramebuffer(c.FRAMEBUFFER,f.framebuffer),c.viewport(0,0,a,s),c.disable(c.DEPTH_TEST),c.disable(c.CULL_FACE),c.enable(c.BLEND),c.blendFunc(c.ONE,c.ONE),c.clearColor(0,0,0,1),c.clear(c.COLOR_BUFFER_BIT),r.gravity.length>0&&(c.useProgram(f.splatProgram),c.uniform4f(c.getUniformLocation(f.splatProgram,"u_viewport"),o.minX,o.minY,o.maxX,o.maxY),c.uniform1f(c.getUniformLocation(f.splatProgram,"u_time"),I(t.simulationTimeSeconds,0)),c.bindBuffer(c.ARRAY_BUFFER,f.splatBuffer),c.bufferData(c.ARRAY_BUFFER,r.gravity,c.DYNAMIC_DRAW),Me(c,f.splatProgram),c.drawArrays(c.TRIANGLES,0,r.gravity.length/be)),c.bindFramebuffer(c.FRAMEBUFFER,f.tintFramebuffer),c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT),r.tint.length>0&&(c.useProgram(f.splatProgram),c.uniform4f(c.getUniformLocation(f.splatProgram,"u_viewport"),o.minX,o.minY,o.maxX,o.maxY),c.uniform1f(c.getUniformLocation(f.splatProgram,"u_time"),I(t.simulationTimeSeconds,0)),c.bindBuffer(c.ARRAY_BUFFER,f.splatBuffer),c.bufferData(c.ARRAY_BUFFER,r.tint,c.DYNAMIC_DRAW),Me(c,f.splatProgram),c.drawArrays(c.TRIANGLES,0,r.tint.length/be)),c.disable(c.BLEND),c.bindFramebuffer(c.FRAMEBUFFER,null),c.viewport(0,0,a,s),c.useProgram(f.shadeProgram),c.activeTexture(c.TEXTURE0),c.bindTexture(c.TEXTURE_2D,f.fieldTexture),c.uniform1i(c.getUniformLocation(f.shadeProgram,"u_field"),0),c.activeTexture(c.TEXTURE1),c.bindTexture(c.TEXTURE_2D,f.tintTexture),c.uniform1i(c.getUniformLocation(f.shadeProgram,"u_tint"),1),c.uniform2f(c.getUniformLocation(f.shadeProgram,"u_resolution"),a,s),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_time"),I(n.simulationTimeSeconds,I(t.simulationTimeSeconds,0))),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_lineInterval"),I(t.lineInterval,I(t.scalarFieldLineInterval,I(t.depthRange,228.79605)/20))),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_lineOffset"),I(t.lineOffset,I(t.startDepth,2.310401))),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_lineWidth"),I(t.lineWidth,.3)),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_lineFade"),I(t.lineFade,5)),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_angleWidth"),I(t.angleWidth,.05)),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_angleFade"),I(t.angleFade,1.15)),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_dangerSteepness"),I(t.dangerSteepness,1.5)),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_scale"),I(t.isolineScale,2.6924083)),Q(c,f.shadeProgram,"u_baseColor",Z(t.scalarFieldBaseColor,[.002,.006,.012])),Q(c,f.shadeProgram,"u_fieldGlowColor",Z(t.scalarFieldGlowColor,[.018,.05,.075])),Q(c,f.shadeProgram,"u_lineLowColor",Z(t.scalarFieldLowLineColor,[0,.34,.52])),Q(c,f.shadeProgram,"u_lineHighColor",Z(t.scalarFieldHighLineColor,[1.45,.3,.05])),Q(c,f.shadeProgram,"u_angleLowColor",Z(t.scalarFieldLowAngleColor,[.06,.16,.24])),Q(c,f.shadeProgram,"u_angleHighColor",Z(t.scalarFieldHighAngleColor,[1.1,.24,.04])),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_tintScale"),I(t.vectorFieldTintScale,.45)),c.bindBuffer(c.ARRAY_BUFFER,f.quadBuffer);const d=c.getAttribLocation(f.shadeProgram,"a_position");return c.enableVertexAttribArray(d),c.vertexAttribPointer(d,2,c.FLOAT,!1,0,0),c.drawArrays(c.TRIANGLES,0,6),e.drawImage(f.canvas,0,0,a,s),!0}function ot(e,t,n,i,o){const a=[],s=[];return at(a,e,t,n),ct(s,e,o),{gravity:new Float32Array(a),tint:new Float32Array(s)}}function at(e,t,n,i,o){const a=I(n.terrainRadius,I(t.terrainRadius,1200)),s=I(n.terrainDepth,I(t.terrainDepth,-8));s!==0&&le(e,0,0,Math.max(1,a*2),Math.max(1,a*2),-s,0,0,1,Math.max(1e-4,I(n.terrainDepthExponent,I(t.terrainDepthExponent,1.2))),0,1,0,0);for(const l of i)le(e,l.x,l.y,Math.max(1,l.radius),Math.max(1,l.radius),-l.depth,0,0,1,Math.max(1e-4,l.exponent),0,1,0,0),l.waveRadius>0&&l.waveDepth!==0&&le(e,l.x,l.y,l.waveRadius,l.waveRadius,-l.waveDepth,0,0,1,8,4,I(n.terrainWaveFrequency,I(t.terrainWaveFrequency,.6)),l.waveSpeed,0)}function ct(e,t,n){const i=dt(n?.splats),o=Array.isArray(n?.layers)?n.layers:[],a=new Set,s=ft(t.vectorFieldLayer,t.tintFieldLayer,"fog.tint");o.forEach((_,J)=>{const ie=ut(_?.layerKey,"");I(_?.channel,-1)===4&&ie===s&&a.add(J)});const l=Math.max(0,mt(i.count,0));if(l<=0||a.size===0)return;const f=L(i.centerX),c=L(i.centerY),r=L(i.halfExtentX),d=L(i.halfExtentY),u=L(i.rotationCos),h=L(i.rotationSin),y=L(i.channel),b=L(i.falloff),v=L(i.valueR),C=L(i.valueG),A=L(i.valueB),N=L(i.valueA),Y=L(i.sourceKind),z=L(i.frequencyX),H=L(i.animationSpeed),V=L(i.sourceFlags),K=L(i.layerIndex);for(let _=0;_Number(i));if(n.length>=3&&n.slice(0,3).every(Number.isFinite))return[n[0],n[1],n[2]]}if(typeof e=="string"){const n=e.split(/[,\s]+/).map(i=>Number(i)).filter(Number.isFinite);if(n.length>=3)return[n[0],n[1],n[2]]}return t}function Q(e,t,n,i){e.uniform3f(e.getUniformLocation(t,n),i[0],i[1],i[2])}function L(e){return Array.isArray(e)?e.map(t=>I(t,0)):[]}function dt(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function ut(e,t){return e==null?t:String(e)}function ft(...e){for(const t of e){if(typeof t=="string"&&t.trim())return t;if(typeof t=="number"&&Number.isFinite(t))return String(t)}return""}function mt(e,t){const n=Number(e);return Number.isFinite(n)?Math.max(0,Math.floor(n)):t}const pt=`#version 300 es +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))i(o);new MutationObserver(o=>{for(const a of o)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&i(s)}).observe(document,{childList:!0,subtree:!0});function n(o){const a={};return o.integrity&&(a.integrity=o.integrity),o.referrerPolicy&&(a.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?a.credentials="include":o.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function i(o){if(o.ep)return;o.ep=!0;const a=n(o);fetch(o.href,a)}})();const j=new WeakMap,be=20;function ot(e,t,n,i,o,a,s,l){const f=lt(e.canvas);if(!f)return!1;const c=f.gl;if(!dt(f,a,s))return j.set(e.canvas,null),!1;const r=at(t,n,i,o,l);c.bindFramebuffer(c.FRAMEBUFFER,f.framebuffer),c.viewport(0,0,a,s),c.disable(c.DEPTH_TEST),c.disable(c.CULL_FACE),c.enable(c.BLEND),c.blendFunc(c.ONE,c.ONE),c.clearColor(0,0,0,1),c.clear(c.COLOR_BUFFER_BIT),r.gravity.length>0&&(c.useProgram(f.splatProgram),c.uniform4f(c.getUniformLocation(f.splatProgram,"u_viewport"),o.minX,o.minY,o.maxX,o.maxY),c.uniform1f(c.getUniformLocation(f.splatProgram,"u_time"),A(t.simulationTimeSeconds,0)),c.bindBuffer(c.ARRAY_BUFFER,f.splatBuffer),c.bufferData(c.ARRAY_BUFFER,r.gravity,c.DYNAMIC_DRAW),Me(c,f.splatProgram),c.drawArrays(c.TRIANGLES,0,r.gravity.length/be)),c.bindFramebuffer(c.FRAMEBUFFER,f.tintFramebuffer),c.clearColor(0,0,0,0),c.clear(c.COLOR_BUFFER_BIT),r.tint.length>0&&(c.useProgram(f.splatProgram),c.uniform4f(c.getUniformLocation(f.splatProgram,"u_viewport"),o.minX,o.minY,o.maxX,o.maxY),c.uniform1f(c.getUniformLocation(f.splatProgram,"u_time"),A(t.simulationTimeSeconds,0)),c.bindBuffer(c.ARRAY_BUFFER,f.splatBuffer),c.bufferData(c.ARRAY_BUFFER,r.tint,c.DYNAMIC_DRAW),Me(c,f.splatProgram),c.drawArrays(c.TRIANGLES,0,r.tint.length/be)),c.disable(c.BLEND),c.bindFramebuffer(c.FRAMEBUFFER,null),c.viewport(0,0,a,s),c.useProgram(f.shadeProgram),c.activeTexture(c.TEXTURE0),c.bindTexture(c.TEXTURE_2D,f.fieldTexture),c.uniform1i(c.getUniformLocation(f.shadeProgram,"u_field"),0),c.activeTexture(c.TEXTURE1),c.bindTexture(c.TEXTURE_2D,f.tintTexture),c.uniform1i(c.getUniformLocation(f.shadeProgram,"u_tint"),1),c.uniform2f(c.getUniformLocation(f.shadeProgram,"u_resolution"),a,s),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_time"),A(n.simulationTimeSeconds,A(t.simulationTimeSeconds,0))),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_lineInterval"),A(t.lineInterval,A(t.scalarFieldLineInterval,A(t.depthRange,228.79605)/20))),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_lineOffset"),A(t.lineOffset,A(t.startDepth,2.310401))),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_lineWidth"),A(t.lineWidth,.3)),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_lineFade"),A(t.lineFade,5)),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_angleWidth"),A(t.angleWidth,.05)),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_angleFade"),A(t.angleFade,1.15)),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_dangerSteepness"),A(t.dangerSteepness,1.5)),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_scale"),A(t.isolineScale,2.6924083)),Q(c,f.shadeProgram,"u_baseColor",Z(t.scalarFieldBaseColor,[.002,.006,.012])),Q(c,f.shadeProgram,"u_fieldGlowColor",Z(t.scalarFieldGlowColor,[.018,.05,.075])),Q(c,f.shadeProgram,"u_lineLowColor",Z(t.scalarFieldLowLineColor,[0,.34,.52])),Q(c,f.shadeProgram,"u_lineHighColor",Z(t.scalarFieldHighLineColor,[1.45,.3,.05])),Q(c,f.shadeProgram,"u_angleLowColor",Z(t.scalarFieldLowAngleColor,[.06,.16,.24])),Q(c,f.shadeProgram,"u_angleHighColor",Z(t.scalarFieldHighAngleColor,[1.1,.24,.04])),c.uniform1f(c.getUniformLocation(f.shadeProgram,"u_tintScale"),A(t.vectorFieldTintScale,.45)),c.bindBuffer(c.ARRAY_BUFFER,f.quadBuffer);const d=c.getAttribLocation(f.shadeProgram,"a_position");return c.enableVertexAttribArray(d),c.vertexAttribPointer(d,2,c.FLOAT,!1,0,0),c.drawArrays(c.TRIANGLES,0,6),e.drawImage(f.canvas,0,0,a,s),!0}function at(e,t,n,i,o){const a=[],s=[];return ct(a,e,t,n),st(s,e,o),{gravity:new Float32Array(a),tint:new Float32Array(s)}}function ct(e,t,n,i,o){const a=A(n.terrainRadius,A(t.terrainRadius,1200)),s=A(n.terrainDepth,A(t.terrainDepth,-8));s!==0&&le(e,0,0,Math.max(1,a*2),Math.max(1,a*2),-s,0,0,1,Math.max(1e-4,A(n.terrainDepthExponent,A(t.terrainDepthExponent,1.2))),0,1,0,0);for(const l of i)le(e,l.x,l.y,Math.max(1,l.radius),Math.max(1,l.radius),-l.depth,0,0,1,Math.max(1e-4,l.exponent),0,1,0,0),l.waveRadius>0&&l.waveDepth!==0&&le(e,l.x,l.y,l.waveRadius,l.waveRadius,-l.waveDepth,0,0,1,8,4,A(n.terrainWaveFrequency,A(t.terrainWaveFrequency,.6)),l.waveSpeed,0)}function st(e,t,n){const i=ut(n?.splats),o=Array.isArray(n?.layers)?n.layers:[],a=new Set,s=mt(t.vectorFieldLayer,t.tintFieldLayer,"fog.tint");o.forEach((_,J)=>{const ie=ft(_?.layerKey,"");A(_?.channel,-1)===4&&ie===s&&a.add(J)});const l=Math.max(0,pt(i.count,0));if(l<=0||a.size===0)return;const f=L(i.centerX),c=L(i.centerY),r=L(i.halfExtentX),d=L(i.halfExtentY),u=L(i.rotationCos),h=L(i.rotationSin),y=L(i.channel),b=L(i.falloff),v=L(i.valueR),C=L(i.valueG),I=L(i.valueB),N=L(i.valueA),Y=L(i.sourceKind),z=L(i.frequencyX),H=L(i.animationSpeed),V=L(i.sourceFlags),K=L(i.layerIndex);for(let _=0;_Number(i));if(n.length>=3&&n.slice(0,3).every(Number.isFinite))return[n[0],n[1],n[2]]}if(typeof e=="string"){const n=e.split(/[,\s]+/).map(i=>Number(i)).filter(Number.isFinite);if(n.length>=3)return[n[0],n[1],n[2]]}return t}function Q(e,t,n,i){e.uniform3f(e.getUniformLocation(t,n),i[0],i[1],i[2])}function L(e){return Array.isArray(e)?e.map(t=>A(t,0)):[]}function ut(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function ft(e,t){return e==null?t:String(e)}function mt(...e){for(const t of e){if(typeof t=="string"&&t.trim())return t;if(typeof t=="number"&&Number.isFinite(t))return String(t)}return""}function pt(e,t){const n=Number(e);return Number.isFinite(n)?Math.max(0,Math.floor(n)):t}const ht=`#version 300 es in vec2 a_corner; in vec2 a_center; in vec2 a_half; @@ -26,7 +26,7 @@ void main() { v_meta = a_meta; v_source = a_source; v_value = a_value; -}`,ht=`#version 300 es +}`,gt=`#version 300 es precision highp float; in vec2 v_local; in vec4 v_meta; @@ -67,13 +67,13 @@ void main() { source = cos(pow(d * 2.0, 1.25) * v_source.y + u_time * v_source.z); } outColor = v_value * alpha * source; -}`,gt=`#version 300 es +}`,yt=`#version 300 es in vec2 a_position; out vec2 v_uv; void main() { v_uv = a_position * 0.5 + 0.5; gl_Position = vec4(a_position, 0.0, 1.0); -}`,yt=`#version 300 es +}`,bt=`#version 300 es precision highp float; uniform sampler2D u_field; uniform sampler2D u_tint; @@ -131,7 +131,7 @@ void main() { color += clamp(lines, 0.0, 1.7); outColor = vec4(color, 1.0); -}`,qe=new Set(["gamecult.fields.splats.v1","gamecult.fields.gravity.v1","gamecult.fields.objects.v1"]),bt={pluginId:"fields.surface",capabilities:["field.surface2d","gravity.surface","field.scalar","field.vector","field.objects"],componentKinds:["field.surface2d","gravity.surface"],schemas:[...qe],normalizeDocument:ve,renderComponent:vt};function vt(e,t,n,i,o,a){const s=document.createElement("canvas");s.className="cultui-gravity-surface",e.id&&(s.id=e.id),s.setAttribute("role","img"),s.setAttribute("aria-label",Kt(t.label,"gravity surface")),s.style.display="block",s.style.width="100%",s.style.height="100%",s.style.pointerEvents="none",a.applyGeneratedLayout(s,n,i);let l=!1;const f={},c=()=>St(s,t,f,a.resolveAssetUrl),r=()=>{!s.isConnected||l||(l=!0,requestAnimationFrame(()=>{l=!1,s.isConnected&&c()}))};s.addEventListener("cultui:asset-loaded",r);let d=0,u;const h=()=>{d&&window.clearInterval(d),u?.disconnect(),s.removeEventListener("cultui:asset-loaded",r)},y=()=>{if(!s.isConnected){h();return}xt(e,t,o,a.providerId,f).then(v=>{v&&s.isConnected&&r()}).catch(v=>{f.lastError=v instanceof Error?v.message:String(v)})};typeof ResizeObserver<"u"&&(u=new ResizeObserver(r),u.observe(s)),r();const b=Math.max(33,Math.min(1e3,Gt(t.stateRefreshMs,100)));return d=window.setInterval(y,b),queueMicrotask(y),window.setTimeout(r,300),window.setTimeout(r,1e3),s}async function xt(e,t,n,i,o){if(o.loading)return!1;const a=[ge(e,t,"renderSplats","renderSplatsDocumentId","renderSplatsSchemaId"),ge(e,t,"gravity","gravityDocumentId","gravitySchemaId"),ge(e,t,"objects","objectsDocumentId","objectsSchemaId")].filter(s=>!!s?.documentId);if(a.length===0)return!1;o.loading=!0;try{let s=!1;for(const l of a){const f=await n.documentResolver?.(l,e);let c=me(ve(f?.schemaId||l.schemaId,f?.document));if(Object.keys(c).length===0&&(c=me(ve(l.schemaId,await wt(l,i||n.provider?.providerId)))),Object.keys(c).length===0)continue;const r=l.slotId==="renderSplats"?"renderSplats":l.slotId==="gravity"?"gravity":"objects",d=we(o[r]?.frameId,-1),u=we(c.frameId,d);o[r]=c,s||=u!==d}return o.lastLoadedAt=Date.now(),s}finally{o.loading=!1}}async function wt(e,t){if(typeof fetch!="function"||typeof window>"u"||!t)return;const n=new URLSearchParams({documentId:e.documentId});e.schemaId&&n.set("schemaId",e.schemaId),e.slotId&&n.set("slotId",e.slotId);const i=await fetch(`/eir/document/${encodeURIComponent(t)}?${n.toString()}`,{cache:"no-store",headers:{Accept:"application/json"}});if(i.ok)return me(await i.json()).document}function ge(e,t,n,i,o){const a=(e.embeddedDocuments||[]).find(l=>String(l?.slotId||"")===n),s=T(t[i],a?.documentId);if(s)return{documentId:s,schemaId:T(t[o],a?.schemaId),presentationKind:T(a?.presentationKind)||"data",slotId:n}}function ve(e,t){const n=Array.isArray(t)?t:void 0,i=n?n[0]:t?.schema,o=String(e||i||"");return!n||!qe.has(o)?t:o==="gamecult.fields.splats.v1"?Mt(n):o==="gamecult.fields.gravity.v1"?Nt(n):Bt(n)}function St(e,t,n,i){const o=e.getBoundingClientRect(),a=Math.max(1,Math.floor(o.width||e.clientWidth||1)),s=Math.max(1,Math.floor(o.height||e.clientHeight||1)),l=Math.max(1,Math.min(2,window.devicePixelRatio||1)),f=Math.max(1,Math.floor(a*l)),c=Math.max(1,Math.floor(s*l));(e.width!==f||e.height!==c)&&(e.width=f,e.height=c);const r=e.getContext("2d");if(!r)return;r.setTransform(l,0,0,l,0,0),r.clearRect(0,0,a,s);const d=Pt(Lt(t),a,s),u=Ve(n?.gravity),h=kt(n?.gravity)||Tt(T(t.bodies,"")),y=Ft(n?.objects)||Rt(T(t.objects,""));rt(r,t,u,h,d,a,s,n?.renderSplats)||Et(r,t,a,s,"field.surface2d requires WebGL2 float render targets"),At(r,h,t,d,a,s,i),_t(r,y,t,d,a,s,i)}function Et(e,t,n,i,o){e.save(),e.fillStyle=T(t.failureBackground,"rgba(0, 0, 0, 0.72)"),e.fillRect(0,0,n,i),e.font=T(t.failureFont,"700 13px Cascadia Mono, Consolas, monospace"),e.fillStyle=T(t.failureColor,"rgba(255, 184, 79, 0.95)"),e.fillText(o,16,28),e.restore()}function At(e,t,n,i,o,a,s){for(const l of t){const f=He(i,l.x,l.y,o,a),c=Math.max(18,Math.min(190,l.radius/Math.max(1,i.maxX-i.minX)*o*.32)),r=s(l.icon),d=l.kind.toLowerCase().includes("sun")?Math.max(E(n.sunIconMinPx,34),c*E(n.sunIconScale,.72)):Math.max(E(n.bodyIconMinPx,24),c*E(n.bodyIconScale,.48)),u=l.iconSize>0?l.iconSize:d;r&&It(e,r,f.x,f.y,u,.92),l.key&&Ye(e,n,l.key.replace(/^local\./,""),f.x+c*.44,f.y-c*.48,T(n.bodyLabelColor,"rgba(226, 244, 255, 0.82)"))}}const fe=new Map;function It(e,t,n,i,o,a){let s=fe.get(t);s||(s=new Image,s.decoding="async",s.onload=()=>requestAnimationFrame(()=>{e.canvas.dispatchEvent(new CustomEvent("cultui:asset-loaded"))}),s.src=t,fe.set(t,s)),!(!s.complete||s.naturalWidth===0)&&(e.save(),e.globalAlpha=a,e.drawImage(s,n-o/2,i-o/2,o,o),e.restore())}function _t(e,t,n,i,o,a,s){for(const l of t){const f=He(i,l.x,l.y,o,a);if(f.x<-40||f.x>o+40||f.y<-40||f.y>a+40)continue;const c=l.kind.toLowerCase().includes("station")?E(n.stationIconSizePx,34):l.controlled?E(n.shipIconSizePx,22):E(n.remoteShipIconSizePx,18),r=Ct(n,l.faction,l.controlled,l.visibility),d=s(l.icon);e.save(),e.globalAlpha=Math.max(.25,Math.min(1,l.visibility)),e.translate(f.x,f.y);const u=Math.atan2(l.directionY,l.directionX)+Math.PI/2;if(l.kind.toLowerCase().includes("ship")&&e.rotate(u),d){const h=fe.get(d);if(h)h.complete&&h.naturalWidth>0&&e.drawImage(h,-c/2,-c/2,c,c);else{const y=new Image;y.decoding="async",y.onload=()=>e.canvas.dispatchEvent(new CustomEvent("cultui:asset-loaded")),y.src=d,fe.set(d,y)}}e.restore(),Ye(e,n,l.label,f.x+c*.55,f.y-c*.45,r)}}function Ye(e,t,n,i,o,a){n&&(e.save(),e.font=T(t.objectLabelFont,"700 12px Ubuntu, system-ui, sans-serif"),e.textBaseline="middle",e.lineWidth=E(t.objectLabelStrokeWidth,3),e.strokeStyle=T(t.objectLabelStroke,"rgba(0, 0, 0, 0.72)"),e.fillStyle=a,e.strokeText(n,i,o),e.fillText(n,i,o),e.restore())}function Ct(e,t,n,i){const o=Math.max(.25,Math.min(1,i));if(n)return re(T(e.objectControlledColor,"rgba(122, 240, 255, {alpha})"),o);const a=t.toLowerCase();return a.includes("raider")?re(T(e.objectRaiderColor,"rgba(255, 143, 74, {alpha})"),o):a.includes("neutral")?re(T(e.objectNeutralColor,"rgba(232, 232, 224, {alpha})"),o):re(T(e.objectDefaultColor,"rgba(214, 244, 255, {alpha})"),o)}function re(e,t){return e.replaceAll("{alpha}",String(t))}function Tt(e){return e.split(";").map(t=>t.trim()).filter(Boolean).map(t=>{const n=t.split("|");return{key:n[0]??"",kind:n[1]??"",x:Number(n[2])||0,y:Number(n[3])||0,radius:Math.max(1,Number(n[4])||1),depth:Number(n[5])||0,exponent:Number(n[6])||1,waveRadius:Number(n[7])||0,waveDepth:Number(n[8])||0,waveSpeed:Number(n[9])||0,icon:n[10]??"",iconSize:Number(n[12])||0,tint:n[11]??""}})}function Rt(e){return e.split(";").map(t=>t.trim()).filter(Boolean).map(t=>{const n=t.split("|");return{key:n[0]??"",kind:n[1]??"",label:n[2]??"",x:Number(n[3])||0,y:Number(n[4])||0,directionX:Number(n[5])||0,directionY:Number(n[6])||1,faction:n[7]??"",controlled:n[8]==="1"||n[8]==="true",visibility:Number(n[9])||1,icon:n[10]??""}})}function kt(e){const t=Array.isArray(e?.gravityInfluences)?e.gravityInfluences:[],n=Array.isArray(e?.bodies)?e.bodies:[];if(t.length===0&&n.length===0)return;const i=new Map;for(const o of n){const a=T(o.bodyKey,o.name,"");a&&i.set(a,o)}return(t.length>0?t:n).map(o=>{const a=T(o.bodyKey,o.name,"body"),s=i.get(a),l=T(o.kind,"Body");return{key:a,kind:l,x:E(o.x,0),y:E(o.y,0),radius:Math.max(1,E(o.radius,80)),depth:E(o.gravityDepth,0),exponent:E(o.gravityDepthExponent,1),waveRadius:E(o.waveRadius,0),waveDepth:E(o.waveDepth,0),waveSpeed:E(o.waveSpeed,0),icon:xe(o.iconAsset)||xe(s?.iconAsset),iconSize:E(s?.iconSize,E(o.iconSize,0)),tint:""}})}function Ft(e){const t=Array.isArray(e?.objects)?e.objects:[];if(t.length!==0)return t.map(n=>{const i=T(n.kind,"object");return{key:T(n.entityKey,String(n.entityIndex??""),"object"),kind:i,label:T(n.displayName,n.entityKey,"object"),x:E(n.x,0),y:E(n.y,0),directionX:E(n.directionX,1),directionY:E(n.directionY,0),faction:T(n.factionKey,""),controlled:jt(n.controlled),visibility:E(n.visibility,1),icon:xe(n.iconAsset)}})}function xe(e){const t=Ve(e);return T(t.uri,"")}function Lt(e){const t=Math.max(1,E(e.viewRadius,Math.max(1200,E(e.terrainRadius,1200))));return{minX:E(e.minX,-t),minY:E(e.minY,-t),maxX:E(e.maxX,t),maxY:E(e.maxY,t)}}function Pt(e,t,n){const i=Math.max(1e-4,e.maxX-e.minX),o=Math.max(1e-4,e.maxY-e.minY),a=Math.max(1e-4,t/Math.max(1,n)),s=i/o,l=(e.minX+e.maxX)*.5,f=(e.minY+e.maxY)*.5;if(a>s){const r=o*a;return{minX:l-r*.5,maxX:l+r*.5,minY:e.minY,maxY:e.maxY}}const c=i/a;return{minX:e.minX,maxX:e.maxX,minY:f-c*.5,maxY:f+c*.5}}function He(e,t,n,i,o){return{x:(t-e.minX)/Math.max(1,e.maxX-e.minX)*i,y:(n-e.minY)/Math.max(1,e.maxY-e.minY)*o}}function Mt(e){const t=M(e[9]);return{schema:e[0],frameId:e[1],publishedAtUtc:e[2],simulationTimeSeconds:e[3],runId:e[4],zoneIndex:e[5],zoneName:e[6],viewport:_e(e[7]),layers:M(e[8]).map(Dt),splats:{count:t[0]??0,centerX:t[1]||[],centerY:t[2]||[],halfExtentX:t[3]||[],halfExtentY:t[4]||[],rotationCos:t[5]||[],rotationSin:t[6]||[],channel:t[7]||[],falloff:t[8]||[],valueR:t[9]||[],valueG:t[10]||[],valueB:t[11]||[],valueA:t[12]||[],sourceKey:t[13]||[],layerIndex:t[14]||[],sourceKind:t[15]||[],frequencyX:t[16]||[],frequencyY:t[17]||[],phaseX:t[18]||[],phaseY:t[19]||[],animationSpeed:t[20]||[],sourceFlags:t[21]||[]}}}function Nt(e){return{schema:e[0],frameId:e[1],publishedAtUtc:e[2],simulationTimeSeconds:e[3],runId:e[4],zoneIndex:e[5],zoneName:e[6],viewport:_e(e[7]),gravityInfluences:M(e[8]).map($t),bodies:M(e[9]).map(Ut),terrainRadius:e[10],terrainDepth:e[11],terrainDepthExponent:e[12],terrainWaveFrequency:e[13]}}function Bt(e){return{schema:e[0],frameId:e[1],publishedAtUtc:e[2],simulationTimeSeconds:e[3],runId:e[4],zoneIndex:e[5],zoneName:e[6],currentEntityKey:e[7],viewport:_e(e[8]),controlledEntityIndices:M(e[9]),objects:M(e[10]).map(Ot)}}function _e(e){const t=M(e);return t.length?{minX:t[0],minY:t[1],maxX:t[2],maxY:t[3]}:e}function Dt(e){const t=M(e);return t.length?{layerKey:t[0],displayName:t[1],channel:t[2],blendMode:t[3],graphicsFormat:t[4],clearBeforeDraw:t[5],clearR:t[6],clearG:t[7],clearB:t[8],clearA:t[9]}:e}function $t(e){const t=M(e);return t.length?{bodyKey:t[0],orbitKey:t[1],kind:t[2],x:t[3],y:t[4],radius:t[5],gravityDepth:t[6],gravityDepthExponent:t[7],waveRadius:t[8],waveDepth:t[9],waveSpeed:t[10]}:e}function Ut(e){const t=M(e);return t.length?{bodyKey:t[0],orbitKey:t[1],name:t[2],kind:t[3],x:t[4],y:t[5],radius:t[6],isAsteroidBelt:t[7],body:t[8],iconAsset:Ce(t[9])}:e}function Ot(e){const t=M(e);return t.length?{entityIndex:t[0],entityKey:t[1],displayName:t[2],kind:t[3],factionKey:t[4],x:t[5],y:t[6],z:t[7],directionX:t[8],directionY:t[9],velocityX:t[10],velocityY:t[11],controlled:t[12],targetEntityIndex:t[13],isActive:t[14],visibility:t[15],status:Wt(t[16]),inventory:M(t[17]).map(zt),iconAsset:Ce(t[18])}:e}function Wt(e){const t=M(e);return t.length?{hull:t[0],shield:t[1],heat:t[2]}:e||{}}function zt(e){const t=M(e);return t.length?{source:t[0],itemKey:t[1],quantity:t[2],quality:t[3],durability:t[4],enabled:t[5],sourceIndex:t[6],x:t[7],y:t[8],iconAsset:Ce(t[9])}:e}function Ce(e){const t=M(e);return t.length?{assetKey:t[0],kind:t[1],uri:t[2],transport:t[3],contentHash:t[4],mimeType:t[5],metadata:t[6]||{}}:e||{}}function M(e){return Array.isArray(e)?e:[]}function me(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function T(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function Kt(e,t){return typeof e=="string"&&e?e:t}function Gt(e,t){const n=Number(e);return Number.isFinite(n)&&n>0?Math.floor(n):t}function we(e,t){const n=Number(e);return Number.isFinite(n)?n:t}const Ve=me,E=we;function jt(e){return typeof e=="boolean"?e:typeof e=="number"?e!==0:typeof e=="string"&&["1","true","yes","on"].includes(e.trim().toLowerCase())}const Xt=[bt],qt={"--bg":"#020909","--bg-top":"#020909","--bg-mid":"#020909","--bg-bottom":"#020909","--shell-top":"#010707","--shell-bottom":"#010707","--panel":"#071918","--panel-2":"#0a2423","--line":"rgba(103, 240, 228, 0.28)","--line-deep":"rgba(103, 240, 228, 0.18)","--text":"#e7f1f1","--text-bright":"#e7f1f1","--quiet":"#8ba5a3","--accent":"#ffb84f","--accent-strong":"#ffb84f","--corner-accent":"#ffb84f","--cyan":"#8efcff","--shadow":"rgba(0, 0, 0, 0.35)","--pixel-grid":"rgba(142, 252, 255, 0.04)","--border-width":"1px","--border-bottom-width":"1px","--corner-accent-size":"0","--font-body":'"M PLUS 1", "Ubuntu Sans", Ubuntu, "Noto Sans JP", system-ui, sans-serif',"--font-title":'"Montserrat", "Zen Kaku Gothic New", "M PLUS 1", "Ubuntu Sans", system-ui, sans-serif',"--font-mono":'"Ubuntu Sans Mono", "M PLUS 1 Code", "Cascadia Mono", Consolas, monospace'};let oe,Je=Te(void 0),te,O={};const De=new WeakMap;function Yt(e,t,n={}){if(De.get(t)?.dispose(),te=e,O=n,Je=Te(e.surface?.styles),globalThis.__eveCurrentMesh=e.mesh,mn(e.surface?.styles,n.body??document.body),n.body&&(n.body.dataset.provider=e.providerId||n.provider?.providerId||""),n.statusElement&&(n.statusElement.textContent=`${e.title||e.surface?.title||"surface"}${n.source?` (${n.source})`:""}`),e.surface?.root){if(t.replaceChildren(F(e.surface.root,n)),n.stateBindingResolver){const i=new Vt(e,t,n);De.set(t,i),i.start()}}else t.replaceChildren(q("No surface root"));return t}function Ht(e,t,n){const i=t.targetProp.split(".").filter(Boolean);if(i.length===0)throw new Error("Eve state binding targetProp must not be empty.");e.props||={};let o=e.props;for(const a of i.slice(0,-1)){const s=o[a];(!s||typeof s!="object"||Array.isArray(s))&&(o[a]={}),o=o[a]}o[i[i.length-1]]=n}class Vt{surface;host;options;disposed=!1;unsubscribers=[];constructor(t,n,i){this.surface=t,this.host=n,this.options=i}async start(){const t=this.options.stateBindingResolver,n=this.surface.surface?.root;if(!(!t||!n))for(const i of Ze(n))for(const o of i.stateBindings||[]){if(this.disposed)return;const a=await t(o,i);if(!a||this.disposed)continue;const s=l=>{this.disposed||(Ht(i,o,l),this.renderCanonicalProjection())};if(s(await a.latest()),this.disposed)return;this.unsubscribers.push(a.watch(s))}}dispose(){this.disposed=!0;for(const t of this.unsubscribers.splice(0))t()}renderCanonicalProjection(){const t=this.surface.surface?.root;t&&this.host.replaceChildren(F(t,this.options))}}function*Ze(e){yield e;for(const t of e.children||[])yield*Ze(t)}function F(e,t=O){const n=e.kind||"panel",i=x(e.props),o=x(e.layout),a=x(e.style),s=e.children||[],l=e.embeddedDocuments||[];if(n==="surface"||n==="cockpit"||n==="dashboard"){const r=m("section","cultui-surface-root eve-surface");w(r,e),typeof o.direction=="string"?r.dataset.direction=o.direction:typeof i.layout=="object"&&i.layout&&typeof i.layout.direction=="string"&&(r.dataset.direction=String(i.layout.direction)),n!=="surface"&&(r.dataset.surfaceKind=n),S(r,o,a);for(const d of s)r.append(F(d,t));return r}if(n==="column"||n==="row"||n==="form"||n==="rail"){const r=m("div",`cultui-partition eve-${n}`);w(r,e),r.dataset.direction=n==="row"||n==="rail"?"horizontal":"vertical",n==="rail"&&(r.dataset.overflow="x"),ce(r,{...o,...i}),S(r,o,a);for(const d of s)r.append(F(d,t));return r}if(n==="grid"){const r=m("section","cultui-grid"),d=B(i.columns,0);d>0?r.style.gridTemplateColumns=`repeat(${d}, minmax(0, 1fr))`:typeof i.columns=="string"&&(r.style.gridTemplateColumns=i.columns);const u=B(i.rows,0);u>0&&(r.style.gridTemplateRows=`repeat(${u}, minmax(0, auto))`),S(r,o,a);for(const h of s)r.append(F(h,t));return r}if(n==="partition"){const r=m("section",`cultui-partition split-${g(i.split,"none")}`);w(r,e),ce(r,i),S(r,o,a),r.style.minWidth="0",g(i.split,"none")==="x"&&!p(i.flexWrap,o.flexWrap,"")&&(r.style.flexWrap="wrap"),typeof i.role=="string"&&(r.dataset.role=i.role);for(const d of s)r.append(F(d,t));return r}if(n==="input.binding-map")return ln(e,i,t);if(n==="inventory.drag_session")return rn(e,i,o,a);if(n==="inventory.grid")return on(e,i,o,a,t);if(n==="inventory.item")return an(e,i,o,a);const f=(t.pluginAdapters||Xt).find(r=>r.componentKinds.includes(n));if(f?.renderComponent)return f.renderComponent(e,i,o,a,t,{applyGeneratedLayout:S,providerId:te?.providerId||t.provider?.providerId,resolveAssetUrl:ee});if(n==="world.scene3d"||n==="world.scene2d")return Zt(e,i,o,a,t);if(n==="image.background"){const r=m("div","cultui-background"),d=ee(p(i.src,i.assetUri,i.assetRef,""));return d&&(r.style.backgroundImage=`url("${d}")`),r.setAttribute("aria-label",g(i.label,"background")),r}if(n==="image.sprite"){const r=m("figure",`cultui-sprite ${g(i.slot,"center")}`);w(r,e);const d=ee(p(i.src,i.assetUri,i.assetRef,""));if(d){const u=m("img");u.src=d,u.alt=g(i.alt,g(i.actor,g(i.label,""))),r.append(u)}return r}if(n==="avatar"){const r=m("figure","cultui-avatar-card");w(r,e);const d=ee(p(i.src,i.assetUri,i.assetRef,""));if(d){const b=m("img");b.src=d,b.alt=g(i.alt,g(i.text,g(i.label,""))),r.append(b)}else r.append(m("div","cultui-avatar-fallback",In(p(i.text,i.label,i.actor,"?"))));const u=p(i.text,i.label,i.actor,"");u&&r.append(m("figcaption","cultui-avatar-name",u));const h=p(i.status,"");h&&r.append(m("div","cultui-avatar-status",h));const y=p(i.detail,"");return y&&r.append(m("div","cultui-avatar-detail",y)),r}if(n==="pane"||n==="panel"){const r=m("section","pane cultui-pane");w(r,e);const d=g(i.span,"");d&&(r.dataset.span=d);const u=g(i.density,"");u&&(r.dataset.density=u);const h=g(i.title,e.text||"");h&&r.append(m("h2","",h));for(const y of s)r.append(F(y,t));return r}if(n==="list"||n.endsWith(".roster")){const r=m("section","cultui-list");w(r,e),r.dataset.componentKind=n;const d=g(i.title,"");d&&r.append(m("h3","cultui-list-title",d));for(const[u,h]of En(i.items).entries())r.append(F({id:`${e.id||"list"}-item-${u}`,kind:"list.item",props:{...h}},t));for(const u of s)r.append(F(u,t));return r}if(n==="list.item"||n.endsWith(".item")){const r=Qe(e),d=m("article","cultui-list-item");w(d,e),d.dataset.componentKind=n;const u=m("div","cultui-list-item-heading");if(u.append(m("strong","cultui-list-item-label",r.label)),r.status&&u.append(m("span","cultui-list-item-status",r.status)),d.append(u),r.detail&&d.append(m("div","cultui-list-item-detail",r.detail)),r.badges.length){const h=m("div","cultui-list-item-badges");for(const y of r.badges)h.append(m("span","cultui-list-item-badge",y));d.append(h)}for(const h of s)d.append(F(h,t));return d}if(n==="bar"){const r=m("div","cultui-meter");w(r,e);const d=g(i.label,""),u=g(i.value,"");if(d||u){const b=m("div","cultui-meter-head");b.append(m("span","",d)),b.append(m("strong","",u)),r.append(b)}const h=m("div","cultui-meter-track"),y=m("span","cultui-meter-fill");return y.style.width=`${Ke(i.percent)}%`,h.append(y),r.append(h),r}if(n==="card"){const r=We(i,e),d=r?m("button","card cultui-card cultui-card-button"):m("article","card cultui-card");w(d,e),S(d,o,a),d instanceof HTMLButtonElement&&(d.type="button");const u=g(i.title,e.text||"");u&&d.append(m("div","card-title",u));for(const h of s)d.append(F(h,t));return r&&ze(d,e,r,i,t),d}if(n==="control.button"){const r=m("button","cultui-button eve-control-button",g(i.label,"Action"));return w(r,e),r.type="button",S(r,o,a),ze(r,e,We(i,e),i,t),r}if(n==="control.popup"){const r=m("button","cultui-button eve-control-popup",g(i.label,"Open"));return w(r,e),r.type="button",S(r,o,a),r.addEventListener("click",d=>{dn(e,t,r,d.clientX,d.clientY)}),r}if(n==="text.dialogue"||n==="text"||n==="text.title"||n==="text.subtitle"||n==="label"){const r=m("div",bn(n,i),g(i.value,g(i.text,e.text||"")));return w(r,e),ce(r,i),S(r,o,a),r}if(n==="metric"){const r=m("dl","metric cultui-reactive-metric eve-metric");return w(r,e),S(r,o,a),r.append(m("dt","",g(i.label,"Metric"))),r.append(m("dd","metric-value",g(i.value,""))),r}if(n==="progress"){const r=m("div","cultui-meter eve-progress");w(r,e),S(r,o,a);const d=g(i.label,""),u=g(i.value,""),h=Number(i.min??0),y=Number(i.max??1),b=Number(i.value??0),v=Number.isFinite(b)&&Number.isFinite(h)&&Number.isFinite(y)&&y!==h?(b-h)/(y-h)*100:Ke(i.percent);if(d||u){const N=m("div","cultui-meter-head");N.append(m("span","",d)),N.append(m("strong","",u)),r.append(N)}const C=m("div","cultui-meter-track"),A=m("span","cultui-meter-fill");return A.style.width=`${Math.max(0,Math.min(100,v))}%`,C.append(A),r.append(C),r}if(n==="modal"){const r=m("section","cultui-modal-surface");w(r,e),S(r,o,a);const d=g(i.title,"");d&&r.append(m("h2","cultui-modal-surface-title",d));for(const u of s)r.append(F(u,t));return r}if(n==="options"){const r=m("section","cultui-options");w(r,e),S(r,o,a);const d=g(i.label,"");d&&r.append(m("h3","cultui-list-title",d));for(const u of s)r.append(F(u,t));return s.length===0&&r.append(q("waiting for options")),r}if(n==="image.preview"){const r=m("figure","cultui-image-preview");w(r,e);const d=m("div","cultui-image-frame"),u=ee(p(i.src,i.assetUri,i.assetRef,""));if(u){const h=m("img");h.src=u,h.alt=g(i.label,""),h.style.imageRendering=g(i.imageRendering,"auto"),i.zoom!==void 0&&(h.style.transform=`scale(${i.zoom})`,h.style.transformOrigin=g(i.crop,"50% 50%")),d.append(h)}else d.append(m("span","",g(i.label,"image")));return r.append(d),i.label&&r.append(m("figcaption","",String(i.label))),r}if(n==="canvas.preview"||n==="canvas.editor"){const r=m("div",`cultui-canvas-placeholder ${n==="canvas.editor"?"editor":"preview"}`);return w(r,e),r.append(m("div","viz-label",g(i.label,"Canvas"))),r.append(m("div","cultui-canvas-grid",i.state==="empty"?"waiting for pixels":g(i.state,""))),r}if(n==="surface.slot"){const r=m("section","cultui-embedded-slot");w(r,e),S(r,o,a),r.dataset.slotId=g(i.slotId,g(l[0]?.slotId,"")),r.dataset.documentId=g(i.documentId,g(l[0]?.documentId,"")),r.dataset.schemaId=g(i.schemaId,g(l[0]?.schemaId,"")),r.dataset.presentationKind=g(i.presentationKind,g(l[0]?.presentationKind,""));const d=l.map(h=>x(h.surface)).find(h=>x(h.root).kind||h.root),u=i.modal===!0||r.dataset.presentationKind.includes("modal");if((d||r.dataset.documentId)&&u){const h=m("button","cultui-button cultui-modal-trigger",g(i.label,r.dataset.slotId||"Configure"));h.type="button",h.addEventListener("click",()=>nt(Se(e,t),t)),r.append(h)}else if(d||r.dataset.documentId){const h=m("div","cultui-embedded-slot-body");h.append(m("div","detail","materializing surface")),r.append(h),Se(e,t).then(y=>{h.replaceChildren(y?.root?F(y.root,t):q("Embedded surface unavailable"))}).catch(y=>{h.replaceChildren(q(`Embedded surface failed: ${y instanceof Error?y.message:String(y)}`))})}else r.append(m("div","detail",r.dataset.presentationKind||r.dataset.slotId||"embedded surface"));return r}if(n==="control.slider")return hn(i,s,t);if(n==="control.toggle"){const r=m("label","toggle-row cultui-toggle");return w(r,e),r.append(m("span","field-control",i.value?"on":"off")),r.append(m("span","toggle-copy",g(i.label,"Toggle"))),r}if(n==="input.number"||n==="input.select"||n==="control.range"||n==="control.input.text"||n==="control.select"){const r=m("label","field cultui-field");return w(r,e),ce(r,i),S(r,o,a),r.append(m("span","field-label",g(i.label,""))),r.append(m("span","field-control cultui-field-value",i.value===void 0?"":String(i.value))),r}if(n==="color.swatch"){const r=m("div","paint-swatch cultui-swatch");return w(r,e),r.style.background=g(i.value,g(i.color,"currentColor")),r.title=g(i.label,"Color"),r}const c=m("section","pane");w(c,e),c.append(m("h2","",n));for(const r of s)c.append(F(r,t));return c}function Qe(e){const t=x(e.props);return{label:p(t.label,t.title,t.displayName,e.text,e.kind,"item"),status:p(t.status,t.state,""),detail:p(t.detail,t.phase,t.repoName,""),badges:Array.isArray(t.badges)?t.badges.map(n=>String(n).trim()).filter(Boolean):p(t.badges,"").split(",").map(n=>n.trim()).filter(Boolean)}}function S(e,t,n={}){const i=p(t.display,"");i&&(e.style.display=i);const o=p(t.direction,"");o&&(e.style.flexDirection=o==="horizontal"?"row":o==="vertical"?"column":o);const a=p(t.flexWrap,"");a&&(e.style.flexWrap=a);const s=p(t.gridTemplateColumns,"");s&&(e.style.gridTemplateColumns=s);const l=p(t.gridTemplateRows,"");l&&(e.style.gridTemplateRows=l);const f=p(t.gridTemplateAreas,"");f&&(e.style.gridTemplateAreas=f);const c=p(t.gridArea,"");c&&(e.style.gridArea=c);const r=p(t.placeItems,"");r&&(e.style.placeItems=r),P(e,"gap",p(t.gap,"")),P(e,"padding",p(t.padding,"")),P(e,"margin",p(t.margin,"")),P(e,"top",p(t.top,"")),P(e,"right",p(t.right,"")),P(e,"bottom",p(t.bottom,"")),P(e,"left",p(t.left,"")),P(e,"width",p(t.width,"")),P(e,"minWidth",p(t.minWidth,"")),P(e,"maxWidth",p(t.maxWidth,"")),P(e,"height",p(t.height,"")),P(e,"minHeight",p(t.minHeight,"")),P(e,"maxHeight",p(t.maxHeight,""));const d=p(t.alignItems,"");d&&(e.style.alignItems=d);const u=p(t.justifyContent,"");u&&(e.style.justifyContent=u);const h=p(t.alignSelf,"");h&&(e.style.alignSelf=h);const y=p(t.overflow,"");y&&(e.style.overflow=y);const b=p(t.overflowX,"");b&&(e.style.overflowX=b);const v=p(t.overflowY,"");v&&(e.style.overflowY=v);const C=p(t.position,"");C&&(e.style.position=C);const A=p(t.cursor,"");A&&(e.style.cursor=A);const N=p(n.background,"");N&&(e.style.background=de(N));const Y=p(n.color,"");Y&&(e.style.color=de(Y)),P(e,"borderWidth",p(n.borderWidth,""));const z=p(n.borderColor,"");z&&(e.style.borderColor=de(z));const H=p(n.borderStyle,"");H&&(e.style.borderStyle=H),P(e,"borderRadius",p(n.borderRadius,""));const V=p(n.boxShadow,"");V&&(e.style.boxShadow=V);const K=p(n.font,"");K&&(e.style.font=K),P(e,"fontSize",p(n.fontSize,""));const _=p(n.fontWeight,"");_&&(e.style.fontWeight=_);const J=p(n.textTransform,"");J&&(e.style.textTransform=J);const ie=p(n.textAlign,"");ie&&(e.style.textAlign=ie);const he=p(n.whiteSpace,"");he&&(e.style.whiteSpace=he);const Fe=p(n.overflow,"");Fe&&(e.style.overflow=Fe);const Le=p(n.textOverflow,"");Le&&(e.style.textOverflow=Le),P(e,"lineHeight",p(n.lineHeight,""))}function P(e,t,n){n&&(e.style[t]=R(n))}function k(e,t){const n={};for(const[i,o]of Object.entries(e))i.startsWith(t)&&(n[i.slice(t.length)]=o);return n}function Jt(e){const t=(e.children||[]).filter(u=>u.kind==="world.entity3d"),n=t.map(u=>nn(x(u.props).position,[0,0,0])),i=n.map(u=>u[0]),o=n.map(u=>u[2]),a=Math.min(...i,-1),s=Math.max(...i,1),l=Math.min(...o,-1),f=Math.max(...o,1),c=Math.max(1,s-a),r=Math.max(1,f-l),d=6;return t.map((u,h)=>{const y=x(u.props);return{entityId:p(y.entityId,u.id),faction:g(y.faction,"neutral"),kind:g(y.entityKind,"entity"),label:p(y.label,y.entityId,u.id,"Entity"),controlled:Re(y.controllable),xPercent:d+(n[h][0]-a)/c*(100-d*2),yPercent:d+(1-(n[h][2]-l)/r)*(100-d*2),source:u}})}function Zt(e,t,n,i,o){const a=m("section","cultui-world-scene");w(a,e),S(a,n,i),a.tabIndex=0,a.setAttribute("aria-label",g(t.label,"Interactive world"));const s=Jt(e),l=s.find(r=>r.controlled),f=m("div","cultui-world-heading");f.append(m("strong","",g(t.label,"World"))),f.append(m("span","",`${s.length} entities`)),a.append(f);const c=m("div","cultui-world-plane");for(const r of s){const d=m("button",`cultui-world-entity faction-${$e(r.faction)} kind-${$e(r.kind)}`);d.type="button",d.style.left=`${r.xPercent}%`,d.style.top=`${r.yPercent}%`,d.dataset.entityId=r.entityId,d.dataset.controlled=r.controlled?"true":"false",d.title=r.label,d.setAttribute("aria-label",`${r.label}, ${r.kind}, ${r.faction}`),d.append(m("span","cultui-world-entity-glyph",tn(r.kind))),d.append(m("span","cultui-world-entity-label",r.label));const u=x(r.source.props),h=p(u.targetCommand,t.targetCommand,u.focusCommand,t.focusCommand);h&&d.addEventListener("click",()=>ye(h,{actorEntityId:l?.entityId||p(t.playerEntityId),entityId:r.entityId,targetEntityId:r.entityId,targetEntityKey:r.entityId},r.source,o)),c.append(d)}return a.append(c),a.addEventListener("keydown",r=>{const d=en(r.key);if(d){r.preventDefault();const h=p(t.movementCommand,x(l?.source.props).moveCommand);h&&ye(h,{actorEntityId:l?.entityId||p(t.playerEntityId),actorEntityKey:l?.entityId||p(t.playerEntityId),directionX:d[0],directionY:d[1],scalar:1},e,o);return}const u=r.key===" "?p(t.actionCommand,x(l?.source.props).actionCommand):r.key.toLowerCase()==="f"?p(t.focusCommand,x(l?.source.props).focusCommand):"";u&&(r.preventDefault(),ye(u,{actionId:r.key===" "?"0":"focus",actorEntityId:l?.entityId||p(t.playerEntityId),actorEntityKey:l?.entityId||p(t.playerEntityId)},e,o))}),a}function ye(e,t,n,i){i.commandSink?.(Qt(e,t,i),n)}function Qt(e,t,n=O){return ne(e,{action:t},n)}function en(e){switch(e.toLowerCase()){case"w":case"arrowup":return[0,1];case"s":case"arrowdown":return[0,-1];case"a":case"arrowleft":return[-1,0];case"d":case"arrowright":return[1,0];default:return}}function tn(e){return e.includes("station")?"S":e.includes("projectile")?".":e.includes("ship")?"^":"+"}function $e(e){return e.toLowerCase().replace(/[^a-z0-9_-]+/g,"-")}function X(e,t){const n=Number(e);return Number.isFinite(n)?n:t}function nn(e,t){if(Array.isArray(e)){const n=e.map(i=>Number(i));if(n.length>=3&&n.slice(0,3).every(Number.isFinite))return[n[0],n[1],n[2]]}if(typeof e=="string"){const n=e.split(/[,\s]+/).map(i=>Number(i)).filter(Number.isFinite);if(n.length>=3)return[n[0],n[1],n[2]]}return t}function rn(e,t,n,i){const o=Re(t.active),a=m("section","cultui-inventory-drag");w(a,e),S(a,n,i),a.dataset.active=o?"true":"false";const s=m("div","cultui-inventory-section-label","Drag");if(S(s,k(n,"sectionLabel."),k(i,"sectionLabel.")),a.append(s),!o){const d=m("div","cultui-inventory-empty","None");return S(d,k(n,"empty."),k(i,"empty.")),a.append(d),a}const l=p(t.itemKey,t.item,"item"),f=m("div","cultui-inventory-drag-chip",l);S(f,k(n,"dragChip."),k(i,"dragChip.")),a.append(f);const c=[t.originOffsetX,t.originOffsetY].map(d=>Number(d)).map(d=>Number.isFinite(d)?d:0),r=m("div","cultui-inventory-meta",`origin ${c[0]}, ${c[1]}`);return S(r,k(n,"meta."),k(i,"meta.")),a.append(r),a}function on(e,t,n,i,o){const a=m("section","cultui-inventory-grid");w(a,e),S(a,n,i);const s=B(p(t.columns,t.gridColumns,t.widthCells,""),0),l=s>0?s:6,f=B(t.rows,0),c=f>0?f:Math.max(3,Math.ceil(Math.max((e.children||[]).length,1)/l));a.style.setProperty("--inventory-columns",String(l)),a.style.setProperty("--inventory-rows",String(c)),a.dataset.cellSpriteMode=g(t.cellSpriteMode,"");const r=p(t.title,t.label,"Inventory"),d=m("div","cultui-inventory-section-label",r);S(d,k(n,"sectionLabel."),k(i,"sectionLabel.")),a.append(d);const u=m("div","cultui-inventory-board");u.style.display="grid",u.style.gridTemplateColumns=`repeat(${l}, ${R(p(t.cellSize,"72"))})`,u.style.gridTemplateRows=`repeat(${c}, ${R(p(t.cellSize,"72"))})`,u.style.gap=R(p(t.cellGap,"4")),u.style.overflow=p(t.boardOverflow,"auto"),u.style.padding=R(p(t.boardPadding,"4")),S(u,k(n,"board."),k(i,"board.")),u.addEventListener("dragover",b=>{cn(t)&&(b.preventDefault(),b.dataTransfer&&(b.dataTransfer.dropEffect="move"))}),u.addEventListener("drop",b=>{const v=b.dataTransfer?.getData("application/vnd.gamecult.eve.inventory-item+json")||"";if(!v)return;let C;try{C=JSON.parse(v)}catch{return}const A=u.getBoundingClientRect(),N=Math.max(1,X(t.cellSize,72)),Y=Math.max(0,X(t.cellGap,4)),z=N+Y,H=Math.max(0,Math.min(l-1,Math.floor((b.clientX-A.left)/z))),V=Math.max(0,Math.min(c-1,Math.floor((b.clientY-A.top)/z))),K=sn(C,t,H,V,o);K&&(b.preventDefault(),o.commandSink?.(K,e))});const h=new Set;for(const b of e.children||[]){const v=x(b.props),C=B(v.x,0),A=B(v.y,0);h.add(`${C}:${A}`),u.append(F(b,o))}const y=Math.min(l*c,256);for(let b=0;b{!a.draggable||!y.dataTransfer||(y.dataTransfer.effectAllowed="move",y.dataTransfer.setData("application/vnd.gamecult.eve.inventory-item+json",JSON.stringify(t)))});let f=B(t.shapeWidth,1),c=B(t.shapeHeight,1);const r=p(t.rotation,"").toLowerCase();["clockwise","counterclockwise","right","left","rotate90","rotate270"].includes(r)&&([f,c]=[c,f]),a.style.gridColumn=`${s+1} / span ${f}`,a.style.gridRow=`${l+1} / span ${c}`;const d=m("span","cultui-inventory-item-icon");S(d,k(n,"icon."),k(i,"icon.")),d.textContent=An(p(t.iconAssetUri,t.iconAssetKey,t.itemKey,t.label,"")),a.append(d);const u=p(t.label,t.itemKey,"");if(u){const y=m("span","cultui-inventory-item-label",u);S(y,k(n,"label."),k(i,"label.")),a.append(y)}const h=Number(t.quantity);if(Number.isFinite(h)&&h>1){const y=m("span","cultui-inventory-item-quantity",String(h));S(y,k(n,"quantity."),k(i,"quantity.")),a.append(y)}return a.title=[u,p(t.source,""),p(t.quality,"")?`quality ${t.quality}`:"",p(t.durability,"")?`durability ${t.durability}`:""].filter(Boolean).join(" | "),a}function cn(e){return Object.entries(e).some(([t,n])=>(t==="dropCommand"||t.startsWith("dropCommand."))&&typeof n=="string"&&n.length>0)}function sn(e,t,n,i,o=O){const a=p(e.sourceKind,e.source,""),s=p(t[`dropCommand.${a}`],t.dropCommand,"");if(!a||!s)return;const l=X(e.sourceIndex,-1),f=X(t.targetIndex,-1),c=p(t.targetKind,""),r={sourceKind:a,originEntityKey:p(e.sourceEntityKey,e.entityKey,""),originIndex:l,originCargoIndex:a==="cargo"?l:-1,itemKey:p(e.itemKey,""),quantity:Math.max(1,X(e.quantity,1)),sourceX:X(e.x,-2147483648),sourceY:X(e.y,-2147483648),destinationKind:c,destinationEntityKey:p(t.targetEntityKey,t.entityKey,""),destinationIndex:f,destinationCargoIndex:c==="cargo"?f:-1,destinationX:Math.trunc(n),destinationY:Math.trunc(i),hasDestinationPosition:!0};return ne(s,{action:r},o)}function ln(e,t,n){const i=m("section","cultui-binding-map");w(i,e);const o=document.createElementNS("http://www.w3.org/2000/svg","svg");o.setAttribute("class","cultui-xbox-controller"),o.setAttribute("viewBox","0 0 780 420"),o.setAttribute("role","img"),o.setAttribute("aria-label",g(t.label,"Xbox controller remapping")),o.innerHTML=fn(),i.append(o);const a=m("div","cultui-binding-overlay");for(const s of e.children||[]){const l=x(s.props),f=g(l.target,s.id||""),c=m("button","cultui-binding-box");w(c,s),c.type="button",c.dataset.target=f,c.dataset.kind=g(l.bindingKind,"button"),c.style.left=R(l.x??"50%"),c.style.top=R(l.y??"50%"),c.append(m("span","cultui-binding-target",g(l.label,f))),c.append(m("span","cultui-binding-source",g(l.current,"unmapped"))),c.addEventListener("click",()=>{const d=tt(s);if(et(s)||d?.documentId)nt(Se(s,n),n);else{const u=g(l.commandId,"");u&&n.commandSink?.(ne(u,l,n),s)}}),a.append(c);const r=document.createElementNS("http://www.w3.org/2000/svg","line");r.setAttribute("class","cultui-binding-line"),r.setAttribute("x1",g(l.anchorX,"390")),r.setAttribute("y1",g(l.anchorY,"210")),r.setAttribute("x2",String(Oe(l.x,780))),r.setAttribute("y2",String(Oe(l.y,420))),o.append(r)}return i.append(a),i}function et(e){for(const t of e.embeddedDocuments||[]){const n=x(t.surface);if(n.root)return n}}function tt(e){const t=(e.embeddedDocuments||[])[0],n=x(e.props),i=p(n.documentId,t?.documentId,"");if(i)return{documentId:i,presentationKind:p(n.presentationKind,t?.presentationKind,""),schemaId:p(n.schemaId,t?.schemaId,""),slotId:p(n.slotId,t?.slotId,"")}}async function Se(e,t){const n=et(e);if(n)return n;const i=tt(e);if(!i?.documentId||!t.documentResolver)return;const o=await t.documentResolver(i,e);if(o)return x(o.surface).root?o.surface:o}function nt(e,t){const n=m("div","cultui-modal-backdrop"),i=m("section","cultui-modal");i.setAttribute("role","dialog"),i.setAttribute("aria-modal","true");const o=m("header","cultui-modal-header");o.append(m("h2","","Configure Binding"));const a=m("button","cultui-modal-close","Close");a.type="button",a.addEventListener("click",()=>n.remove()),o.append(a);const s=m("div","cultui-modal-body");i.append(o,s),n.append(i),n.addEventListener("click",l=>{l.target===n&&n.remove()}),document.body.append(n),s.append(m("div","detail","materializing document")),Promise.resolve(e).then(l=>{o.querySelector("h2").textContent=l?.title||"Configure Binding",s.replaceChildren(l?.root?F(l.root,t):q("Embedded document unavailable"))}).catch(l=>{s.replaceChildren(q(`Embedded document failed: ${l instanceof Error?l.message:String(l)}`))})}function dn(e,t,n,i,o){const a=x(e.props),s=(e.children||[])[0],l=x(s?.props),f=p(l.anchor,a.anchor,"trigger"),c=p(l.placement,a.placement,"below-start"),r=Number(p(l.offsetX,a.offsetX,"0"))||0,d=Number(p(l.offsetY,a.offsetY,"4"))||0,u=m("div","cultui-modal-backdrop");u.style.background="transparent",u.style.placeItems="start";const h=m("section","cultui-modal");h.setAttribute("role","dialog"),h.setAttribute("aria-modal","true"),h.style.position="fixed",h.style.width="min(420px, calc(100vw - 24px))",h.style.maxHeight="min(620px, calc(100vh - 24px))";const y=m("header","cultui-modal-header");y.append(m("h2","",p(l.title,a.title,a.label,"Options")));const b=m("button","cultui-modal-close","Close");b.type="button",b.addEventListener("click",()=>u.remove()),y.append(b);const v=m("div","cultui-modal-body");if(s){const A=s.children||[];A.length>0?v.replaceChildren(...A.map(N=>F(N,t))):v.append(F(s,t))}else v.append(q("Popup has no content"));h.append(y,v),u.append(h),u.addEventListener("click",A=>{A.target===u&&u.remove()}),document.body.append(u);const C=un(f,c,n,h,i,o,r,d);h.style.left=`${C.left}px`,h.style.top=`${C.top}px`}function un(e,t,n,i,o,a,s,l){const f=n.getBoundingClientRect(),c=i.getBoundingClientRect(),r=c.width||420,d=c.height||320,u=e==="cursor"?{left:o,right:o,top:a,bottom:a}:f;if(t==="right-down"||t==="dropdown")return ae(u,r,d,s,l,"right","down");if(t==="left-down")return ae(u,r,d,s,l,"left","down");if(t==="right-up")return ae(u,r,d,s,l,"right","up");if(t==="left-up")return ae(u,r,d,s,l,"left","up");switch(t){case"above-start":return U(u.left+s,u.top-d-l,r,d);case"above-end":return U(u.right-r+s,u.top-d-l,r,d);case"below-end":return U(u.right-r+s,u.bottom+l,r,d);case"right-start":return U(u.right+s,u.top+l,r,d);case"left-start":return U(u.left-r-s,u.top+l,r,d);case"center":return U((window.innerWidth-r)/2+s,(window.innerHeight-d)/2+l,r,d);default:return U(u.left+s,u.bottom+l,r,d)}}function ae(e,t,n,i,o,a,s){const f=window.innerWidth-e.right-i-12,c=e.left-i-12,r=window.innerHeight-e.top-o-12,d=e.bottom-o-12,u=a==="right"?f>=t||f>=c:!(c>=t||c>=f),h=s==="down"?r>=n||r>=d:!(d>=n||d>=r),y=u?e.right+i:e.left-t-i,b=h?e.top+o:e.bottom-n-o;return U(y,b,t,n)}function U(e,t,n,i){return{left:Ue(e,12,window.innerWidth-n-12),top:Ue(t,12,window.innerHeight-i-12)}}function Ue(e,t,n){return nEt(s,t,f,a.resolveAssetUrl),r=()=>{!s.isConnected||l||(l=!0,requestAnimationFrame(()=>{l=!1,s.isConnected&&c()}))};s.addEventListener("cultui:asset-loaded",r);let d=0,u;const h=()=>{d&&window.clearInterval(d),u?.disconnect(),s.removeEventListener("cultui:asset-loaded",r)},y=()=>{if(!s.isConnected){h();return}wt(e,t,o,a.providerId,f).then(v=>{v&&s.isConnected&&r()}).catch(v=>{f.lastError=v instanceof Error?v.message:String(v)})};typeof ResizeObserver<"u"&&(u=new ResizeObserver(r),u.observe(s)),r();const b=Math.max(33,Math.min(1e3,jt(t.stateRefreshMs,100)));return d=window.setInterval(y,b),queueMicrotask(y),window.setTimeout(r,300),window.setTimeout(r,1e3),s}async function wt(e,t,n,i,o){if(o.loading)return!1;const a=[ge(e,t,"renderSplats","renderSplatsDocumentId","renderSplatsSchemaId"),ge(e,t,"gravity","gravityDocumentId","gravitySchemaId"),ge(e,t,"objects","objectsDocumentId","objectsSchemaId")].filter(s=>!!s?.documentId);if(a.length===0)return!1;o.loading=!0;try{let s=!1;for(const l of a){const f=await n.documentResolver?.(l,e);let c=me(ve(f?.schemaId||l.schemaId,f?.document));if(Object.keys(c).length===0&&(c=me(ve(l.schemaId,await St(l,i||n.provider?.providerId)))),Object.keys(c).length===0)continue;const r=l.slotId==="renderSplats"?"renderSplats":l.slotId==="gravity"?"gravity":"objects",d=we(o[r]?.frameId,-1),u=we(c.frameId,d);o[r]=c,s||=u!==d}return o.lastLoadedAt=Date.now(),s}finally{o.loading=!1}}async function St(e,t){if(typeof fetch!="function"||typeof window>"u"||!t)return;const n=new URLSearchParams({documentId:e.documentId});e.schemaId&&n.set("schemaId",e.schemaId),e.slotId&&n.set("slotId",e.slotId);const i=await fetch(`/eir/document/${encodeURIComponent(t)}?${n.toString()}`,{cache:"no-store",headers:{Accept:"application/json"}});if(i.ok)return me(await i.json()).document}function ge(e,t,n,i,o){const a=(e.embeddedDocuments||[]).find(l=>String(l?.slotId||"")===n),s=T(t[i],a?.documentId);if(s)return{documentId:s,schemaId:T(t[o],a?.schemaId),presentationKind:T(a?.presentationKind)||"data",slotId:n}}function ve(e,t){const n=Array.isArray(t)?t:void 0,i=n?n[0]:t?.schema,o=String(e||i||"");return!n||!qe.has(o)?t:o==="gamecult.fields.splats.v1"?Nt(n):o==="gamecult.fields.gravity.v1"?Bt(n):Dt(n)}function Et(e,t,n,i){const o=e.getBoundingClientRect(),a=Math.max(1,Math.floor(o.width||e.clientWidth||1)),s=Math.max(1,Math.floor(o.height||e.clientHeight||1)),l=Math.max(1,Math.min(2,window.devicePixelRatio||1)),f=Math.max(1,Math.floor(a*l)),c=Math.max(1,Math.floor(s*l));(e.width!==f||e.height!==c)&&(e.width=f,e.height=c);const r=e.getContext("2d");if(!r)return;r.setTransform(l,0,0,l,0,0),r.clearRect(0,0,a,s);const d=Mt(Pt(t),a,s),u=Ve(n?.gravity),h=Ft(n?.gravity)||Rt(T(t.bodies,"")),y=Lt(n?.objects)||kt(T(t.objects,""));ot(r,t,u,h,d,a,s,n?.renderSplats)||It(r,t,a,s,"field.surface2d requires WebGL2 float render targets"),At(r,h,t,d,a,s,i),Ct(r,y,t,d,a,s,i)}function It(e,t,n,i,o){e.save(),e.fillStyle=T(t.failureBackground,"rgba(0, 0, 0, 0.72)"),e.fillRect(0,0,n,i),e.font=T(t.failureFont,"700 13px Cascadia Mono, Consolas, monospace"),e.fillStyle=T(t.failureColor,"rgba(255, 184, 79, 0.95)"),e.fillText(o,16,28),e.restore()}function At(e,t,n,i,o,a,s){for(const l of t){const f=He(i,l.x,l.y,o,a),c=Math.max(18,Math.min(190,l.radius/Math.max(1,i.maxX-i.minX)*o*.32)),r=s(l.icon),d=l.kind.toLowerCase().includes("sun")?Math.max(E(n.sunIconMinPx,34),c*E(n.sunIconScale,.72)):Math.max(E(n.bodyIconMinPx,24),c*E(n.bodyIconScale,.48)),u=l.iconSize>0?l.iconSize:d;r&&_t(e,r,f.x,f.y,u,.92),l.key&&Ye(e,n,l.key.replace(/^local\./,""),f.x+c*.44,f.y-c*.48,T(n.bodyLabelColor,"rgba(226, 244, 255, 0.82)"))}}const fe=new Map;function _t(e,t,n,i,o,a){let s=fe.get(t);s||(s=new Image,s.decoding="async",s.onload=()=>requestAnimationFrame(()=>{e.canvas.dispatchEvent(new CustomEvent("cultui:asset-loaded"))}),s.src=t,fe.set(t,s)),!(!s.complete||s.naturalWidth===0)&&(e.save(),e.globalAlpha=a,e.drawImage(s,n-o/2,i-o/2,o,o),e.restore())}function Ct(e,t,n,i,o,a,s){for(const l of t){const f=He(i,l.x,l.y,o,a);if(f.x<-40||f.x>o+40||f.y<-40||f.y>a+40)continue;const c=l.kind.toLowerCase().includes("station")?E(n.stationIconSizePx,34):l.controlled?E(n.shipIconSizePx,22):E(n.remoteShipIconSizePx,18),r=Tt(n,l.faction,l.controlled,l.visibility),d=s(l.icon);e.save(),e.globalAlpha=Math.max(.25,Math.min(1,l.visibility)),e.translate(f.x,f.y);const u=Math.atan2(l.directionY,l.directionX)+Math.PI/2;if(l.kind.toLowerCase().includes("ship")&&e.rotate(u),d){const h=fe.get(d);if(h)h.complete&&h.naturalWidth>0&&e.drawImage(h,-c/2,-c/2,c,c);else{const y=new Image;y.decoding="async",y.onload=()=>e.canvas.dispatchEvent(new CustomEvent("cultui:asset-loaded")),y.src=d,fe.set(d,y)}}e.restore(),Ye(e,n,l.label,f.x+c*.55,f.y-c*.45,r)}}function Ye(e,t,n,i,o,a){n&&(e.save(),e.font=T(t.objectLabelFont,"700 12px Ubuntu, system-ui, sans-serif"),e.textBaseline="middle",e.lineWidth=E(t.objectLabelStrokeWidth,3),e.strokeStyle=T(t.objectLabelStroke,"rgba(0, 0, 0, 0.72)"),e.fillStyle=a,e.strokeText(n,i,o),e.fillText(n,i,o),e.restore())}function Tt(e,t,n,i){const o=Math.max(.25,Math.min(1,i));if(n)return re(T(e.objectControlledColor,"rgba(122, 240, 255, {alpha})"),o);const a=t.toLowerCase();return a.includes("raider")?re(T(e.objectRaiderColor,"rgba(255, 143, 74, {alpha})"),o):a.includes("neutral")?re(T(e.objectNeutralColor,"rgba(232, 232, 224, {alpha})"),o):re(T(e.objectDefaultColor,"rgba(214, 244, 255, {alpha})"),o)}function re(e,t){return e.replaceAll("{alpha}",String(t))}function Rt(e){return e.split(";").map(t=>t.trim()).filter(Boolean).map(t=>{const n=t.split("|");return{key:n[0]??"",kind:n[1]??"",x:Number(n[2])||0,y:Number(n[3])||0,radius:Math.max(1,Number(n[4])||1),depth:Number(n[5])||0,exponent:Number(n[6])||1,waveRadius:Number(n[7])||0,waveDepth:Number(n[8])||0,waveSpeed:Number(n[9])||0,icon:n[10]??"",iconSize:Number(n[12])||0,tint:n[11]??""}})}function kt(e){return e.split(";").map(t=>t.trim()).filter(Boolean).map(t=>{const n=t.split("|");return{key:n[0]??"",kind:n[1]??"",label:n[2]??"",x:Number(n[3])||0,y:Number(n[4])||0,directionX:Number(n[5])||0,directionY:Number(n[6])||1,faction:n[7]??"",controlled:n[8]==="1"||n[8]==="true",visibility:Number(n[9])||1,icon:n[10]??""}})}function Ft(e){const t=Array.isArray(e?.gravityInfluences)?e.gravityInfluences:[],n=Array.isArray(e?.bodies)?e.bodies:[];if(t.length===0&&n.length===0)return;const i=new Map;for(const o of n){const a=T(o.bodyKey,o.name,"");a&&i.set(a,o)}return(t.length>0?t:n).map(o=>{const a=T(o.bodyKey,o.name,"body"),s=i.get(a),l=T(o.kind,"Body");return{key:a,kind:l,x:E(o.x,0),y:E(o.y,0),radius:Math.max(1,E(o.radius,80)),depth:E(o.gravityDepth,0),exponent:E(o.gravityDepthExponent,1),waveRadius:E(o.waveRadius,0),waveDepth:E(o.waveDepth,0),waveSpeed:E(o.waveSpeed,0),icon:xe(o.iconAsset)||xe(s?.iconAsset),iconSize:E(s?.iconSize,E(o.iconSize,0)),tint:""}})}function Lt(e){const t=Array.isArray(e?.objects)?e.objects:[];if(t.length!==0)return t.map(n=>{const i=T(n.kind,"object");return{key:T(n.entityKey,String(n.entityIndex??""),"object"),kind:i,label:T(n.displayName,n.entityKey,"object"),x:E(n.x,0),y:E(n.y,0),directionX:E(n.directionX,1),directionY:E(n.directionY,0),faction:T(n.factionKey,""),controlled:Xt(n.controlled),visibility:E(n.visibility,1),icon:xe(n.iconAsset)}})}function xe(e){const t=Ve(e);return T(t.uri,"")}function Pt(e){const t=Math.max(1,E(e.viewRadius,Math.max(1200,E(e.terrainRadius,1200))));return{minX:E(e.minX,-t),minY:E(e.minY,-t),maxX:E(e.maxX,t),maxY:E(e.maxY,t)}}function Mt(e,t,n){const i=Math.max(1e-4,e.maxX-e.minX),o=Math.max(1e-4,e.maxY-e.minY),a=Math.max(1e-4,t/Math.max(1,n)),s=i/o,l=(e.minX+e.maxX)*.5,f=(e.minY+e.maxY)*.5;if(a>s){const r=o*a;return{minX:l-r*.5,maxX:l+r*.5,minY:e.minY,maxY:e.maxY}}const c=i/a;return{minX:e.minX,maxX:e.maxX,minY:f-c*.5,maxY:f+c*.5}}function He(e,t,n,i,o){return{x:(t-e.minX)/Math.max(1,e.maxX-e.minX)*i,y:(n-e.minY)/Math.max(1,e.maxY-e.minY)*o}}function Nt(e){const t=M(e[9]);return{schema:e[0],frameId:e[1],publishedAtUtc:e[2],simulationTimeSeconds:e[3],runId:e[4],zoneIndex:e[5],zoneName:e[6],viewport:_e(e[7]),layers:M(e[8]).map($t),splats:{count:t[0]??0,centerX:t[1]||[],centerY:t[2]||[],halfExtentX:t[3]||[],halfExtentY:t[4]||[],rotationCos:t[5]||[],rotationSin:t[6]||[],channel:t[7]||[],falloff:t[8]||[],valueR:t[9]||[],valueG:t[10]||[],valueB:t[11]||[],valueA:t[12]||[],sourceKey:t[13]||[],layerIndex:t[14]||[],sourceKind:t[15]||[],frequencyX:t[16]||[],frequencyY:t[17]||[],phaseX:t[18]||[],phaseY:t[19]||[],animationSpeed:t[20]||[],sourceFlags:t[21]||[]}}}function Bt(e){return{schema:e[0],frameId:e[1],publishedAtUtc:e[2],simulationTimeSeconds:e[3],runId:e[4],zoneIndex:e[5],zoneName:e[6],viewport:_e(e[7]),gravityInfluences:M(e[8]).map(Ut),bodies:M(e[9]).map(Wt),terrainRadius:e[10],terrainDepth:e[11],terrainDepthExponent:e[12],terrainWaveFrequency:e[13]}}function Dt(e){return{schema:e[0],frameId:e[1],publishedAtUtc:e[2],simulationTimeSeconds:e[3],runId:e[4],zoneIndex:e[5],zoneName:e[6],currentEntityKey:e[7],viewport:_e(e[8]),controlledEntityIndices:M(e[9]),objects:M(e[10]).map(Ot)}}function _e(e){const t=M(e);return t.length?{minX:t[0],minY:t[1],maxX:t[2],maxY:t[3]}:e}function $t(e){const t=M(e);return t.length?{layerKey:t[0],displayName:t[1],channel:t[2],blendMode:t[3],graphicsFormat:t[4],clearBeforeDraw:t[5],clearR:t[6],clearG:t[7],clearB:t[8],clearA:t[9]}:e}function Ut(e){const t=M(e);return t.length?{bodyKey:t[0],orbitKey:t[1],kind:t[2],x:t[3],y:t[4],radius:t[5],gravityDepth:t[6],gravityDepthExponent:t[7],waveRadius:t[8],waveDepth:t[9],waveSpeed:t[10]}:e}function Wt(e){const t=M(e);return t.length?{bodyKey:t[0],orbitKey:t[1],name:t[2],kind:t[3],x:t[4],y:t[5],radius:t[6],isAsteroidBelt:t[7],body:t[8],iconAsset:Ce(t[9])}:e}function Ot(e){const t=M(e);return t.length?{entityIndex:t[0],entityKey:t[1],displayName:t[2],kind:t[3],factionKey:t[4],x:t[5],y:t[6],z:t[7],directionX:t[8],directionY:t[9],velocityX:t[10],velocityY:t[11],controlled:t[12],targetEntityIndex:t[13],isActive:t[14],visibility:t[15],status:zt(t[16]),inventory:M(t[17]).map(Kt),iconAsset:Ce(t[18])}:e}function zt(e){const t=M(e);return t.length?{hull:t[0],shield:t[1],heat:t[2]}:e||{}}function Kt(e){const t=M(e);return t.length?{source:t[0],itemKey:t[1],quantity:t[2],quality:t[3],durability:t[4],enabled:t[5],sourceIndex:t[6],x:t[7],y:t[8],iconAsset:Ce(t[9])}:e}function Ce(e){const t=M(e);return t.length?{assetKey:t[0],kind:t[1],uri:t[2],transport:t[3],contentHash:t[4],mimeType:t[5],metadata:t[6]||{}}:e||{}}function M(e){return Array.isArray(e)?e:[]}function me(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function T(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function Gt(e,t){return typeof e=="string"&&e?e:t}function jt(e,t){const n=Number(e);return Number.isFinite(n)&&n>0?Math.floor(n):t}function we(e,t){const n=Number(e);return Number.isFinite(n)?n:t}const Ve=me,E=we;function Xt(e){return typeof e=="boolean"?e:typeof e=="number"?e!==0:typeof e=="string"&&["1","true","yes","on"].includes(e.trim().toLowerCase())}const qt=[vt],Yt={"--bg":"#020909","--bg-top":"#020909","--bg-mid":"#020909","--bg-bottom":"#020909","--shell-top":"#010707","--shell-bottom":"#010707","--panel":"#071918","--panel-2":"#0a2423","--line":"rgba(103, 240, 228, 0.28)","--line-deep":"rgba(103, 240, 228, 0.18)","--text":"#e7f1f1","--text-bright":"#e7f1f1","--quiet":"#8ba5a3","--accent":"#ffb84f","--accent-strong":"#ffb84f","--corner-accent":"#ffb84f","--cyan":"#8efcff","--shadow":"rgba(0, 0, 0, 0.35)","--pixel-grid":"rgba(142, 252, 255, 0.04)","--border-width":"1px","--border-bottom-width":"1px","--corner-accent-size":"0","--font-body":'"M PLUS 1", "Ubuntu Sans", Ubuntu, "Noto Sans JP", system-ui, sans-serif',"--font-title":'"Montserrat", "Zen Kaku Gothic New", "M PLUS 1", "Ubuntu Sans", system-ui, sans-serif',"--font-mono":'"Ubuntu Sans Mono", "M PLUS 1 Code", "Cascadia Mono", Consolas, monospace'};let oe,Je=Te(void 0),te,W={};const De=new WeakMap;function Ze(e,t,n={}){if(De.get(t)?.dispose(),te=e,W=n,Je=Te(e.surface?.styles),globalThis.__eveCurrentMesh=e.mesh,mn(e.surface?.styles,n.body??document.body),n.body&&(n.body.dataset.provider=e.providerId||n.provider?.providerId||""),n.statusElement&&(n.statusElement.textContent=`${e.title||e.surface?.title||"surface"}${n.source?` (${n.source})`:""}`),e.surface?.root){if(t.replaceChildren(F(e.surface.root,n)),n.stateBindingResolver){const i=new Vt(e,t,n);De.set(t,i),i.start()}}else t.replaceChildren(q("No surface root"));return t}function Ht(e,t,n){const i=t.targetProp.split(".").filter(Boolean);if(i.length===0)throw new Error("Eve state binding targetProp must not be empty.");e.props||={};let o=e.props;for(const a of i.slice(0,-1)){const s=o[a];(!s||typeof s!="object"||Array.isArray(s))&&(o[a]={}),o=o[a]}o[i[i.length-1]]=n}class Vt{surface;host;options;disposed=!1;unsubscribers=[];constructor(t,n,i){this.surface=t,this.host=n,this.options=i}async start(){const t=this.options.stateBindingResolver,n=this.surface.surface?.root;if(!(!t||!n))for(const i of Qe(n))for(const o of i.stateBindings||[]){if(this.disposed)return;const a=await t(o,i);if(!a||this.disposed)continue;const s=l=>{this.disposed||(Ht(i,o,l),this.renderCanonicalProjection())};if(s(await a.latest()),this.disposed)return;this.unsubscribers.push(a.watch(s))}}dispose(){this.disposed=!0;for(const t of this.unsubscribers.splice(0))t()}renderCanonicalProjection(){const t=this.surface.surface?.root;t&&this.host.replaceChildren(F(t,this.options))}}function*Qe(e){yield e;for(const t of e.children||[])yield*Qe(t)}function F(e,t=W){const n=e.kind||"panel",i=x(e.props),o=x(e.layout),a=x(e.style),s=e.children||[],l=e.embeddedDocuments||[];if(n==="surface"||n==="cockpit"||n==="dashboard"){const r=m("section","cultui-surface-root eve-surface");w(r,e),typeof o.direction=="string"?r.dataset.direction=o.direction:typeof i.layout=="object"&&i.layout&&typeof i.layout.direction=="string"&&(r.dataset.direction=String(i.layout.direction)),n!=="surface"&&(r.dataset.surfaceKind=n),S(r,o,a);for(const d of s)r.append(F(d,t));return r}if(n==="column"||n==="row"||n==="form"||n==="rail"){const r=m("div",`cultui-partition eve-${n}`);w(r,e),r.dataset.direction=n==="row"||n==="rail"?"horizontal":"vertical",n==="rail"&&(r.dataset.overflow="x"),ce(r,{...o,...i}),S(r,o,a);for(const d of s)r.append(F(d,t));return r}if(n==="grid"){const r=m("section","cultui-grid"),d=D(i.columns,0);d>0?r.style.gridTemplateColumns=`repeat(${d}, minmax(0, 1fr))`:typeof i.columns=="string"&&(r.style.gridTemplateColumns=i.columns);const u=D(i.rows,0);u>0&&(r.style.gridTemplateRows=`repeat(${u}, minmax(0, auto))`),S(r,o,a);for(const h of s)r.append(F(h,t));return r}if(n==="partition"){const r=m("section",`cultui-partition split-${g(i.split,"none")}`);w(r,e),ce(r,i),S(r,o,a),r.style.minWidth="0",g(i.split,"none")==="x"&&!p(i.flexWrap,o.flexWrap,"")&&(r.style.flexWrap="wrap"),typeof i.role=="string"&&(r.dataset.role=i.role);for(const d of s)r.append(F(d,t));return r}if(n==="input.binding-map")return ln(e,i,t);if(n==="inventory.drag_session")return rn(e,i,o,a);if(n==="inventory.grid")return on(e,i,o,a,t);if(n==="inventory.item")return an(e,i,o,a);const f=(t.pluginAdapters||qt).find(r=>r.componentKinds.includes(n));if(f?.renderComponent)return f.renderComponent(e,i,o,a,t,{applyGeneratedLayout:S,providerId:te?.providerId||t.provider?.providerId,resolveAssetUrl:ee});if(n==="world.scene3d"||n==="world.scene2d")return Zt(e,i,o,a,t);if(n==="image.background"){const r=m("div","cultui-background"),d=ee(p(i.src,i.assetUri,i.assetRef,""));return d&&(r.style.backgroundImage=`url("${d}")`),r.setAttribute("aria-label",g(i.label,"background")),r}if(n==="image.sprite"){const r=m("figure",`cultui-sprite ${g(i.slot,"center")}`);w(r,e);const d=ee(p(i.src,i.assetUri,i.assetRef,""));if(d){const u=m("img");u.src=d,u.alt=g(i.alt,g(i.actor,g(i.label,""))),r.append(u)}return r}if(n==="avatar"){const r=m("figure","cultui-avatar-card");w(r,e);const d=ee(p(i.src,i.assetUri,i.assetRef,""));if(d){const b=m("img");b.src=d,b.alt=g(i.alt,g(i.text,g(i.label,""))),r.append(b)}else r.append(m("div","cultui-avatar-fallback",An(p(i.text,i.label,i.actor,"?"))));const u=p(i.text,i.label,i.actor,"");u&&r.append(m("figcaption","cultui-avatar-name",u));const h=p(i.status,"");h&&r.append(m("div","cultui-avatar-status",h));const y=p(i.detail,"");return y&&r.append(m("div","cultui-avatar-detail",y)),r}if(n==="pane"||n==="panel"){const r=m("section","pane cultui-pane");w(r,e);const d=g(i.span,"");d&&(r.dataset.span=d);const u=g(i.density,"");u&&(r.dataset.density=u);const h=g(i.title,e.text||"");h&&r.append(m("h2","",h));for(const y of s)r.append(F(y,t));return r}if(n==="list"||n.endsWith(".roster")){const r=m("section","cultui-list");w(r,e),r.dataset.componentKind=n;const d=g(i.title,"");d&&r.append(m("h3","cultui-list-title",d));for(const[u,h]of En(i.items).entries())r.append(F({id:`${e.id||"list"}-item-${u}`,kind:"list.item",props:{...h}},t));for(const u of s)r.append(F(u,t));return r}if(n==="list.item"||n.endsWith(".item")){const r=et(e),d=m("article","cultui-list-item");w(d,e),d.dataset.componentKind=n;const u=m("div","cultui-list-item-heading");if(u.append(m("strong","cultui-list-item-label",r.label)),r.status&&u.append(m("span","cultui-list-item-status",r.status)),d.append(u),r.detail&&d.append(m("div","cultui-list-item-detail",r.detail)),r.badges.length){const h=m("div","cultui-list-item-badges");for(const y of r.badges)h.append(m("span","cultui-list-item-badge",y));d.append(h)}for(const h of s)d.append(F(h,t));return d}if(n==="bar"){const r=m("div","cultui-meter");w(r,e);const d=g(i.label,""),u=g(i.value,"");if(d||u){const b=m("div","cultui-meter-head");b.append(m("span","",d)),b.append(m("strong","",u)),r.append(b)}const h=m("div","cultui-meter-track"),y=m("span","cultui-meter-fill");return y.style.width=`${Ke(i.percent)}%`,h.append(y),r.append(h),r}if(n==="card"){const r=Oe(i,e),d=r?m("button","card cultui-card cultui-card-button"):m("article","card cultui-card");w(d,e),S(d,o,a),d instanceof HTMLButtonElement&&(d.type="button");const u=g(i.title,e.text||"");u&&d.append(m("div","card-title",u));for(const h of s)d.append(F(h,t));return r&&ze(d,e,r,i,t),d}if(n==="control.button"){const r=m("button","cultui-button eve-control-button",g(i.label,"Action"));return w(r,e),r.type="button",S(r,o,a),ze(r,e,Oe(i,e),i,t),r}if(n==="control.popup"){const r=m("button","cultui-button eve-control-popup",g(i.label,"Open"));return w(r,e),r.type="button",S(r,o,a),r.addEventListener("click",d=>{dn(e,t,r,d.clientX,d.clientY)}),r}if(n==="text.dialogue"||n==="text"||n==="text.title"||n==="text.subtitle"||n==="label"){const r=m("div",bn(n,i),g(i.value,g(i.text,e.text||"")));return w(r,e),ce(r,i),S(r,o,a),r}if(n==="metric"){const r=m("dl","metric cultui-reactive-metric eve-metric");return w(r,e),S(r,o,a),r.append(m("dt","",g(i.label,"Metric"))),r.append(m("dd","metric-value",g(i.value,""))),r}if(n==="progress"){const r=m("div","cultui-meter eve-progress");w(r,e),S(r,o,a);const d=g(i.label,""),u=g(i.value,""),h=Number(i.min??0),y=Number(i.max??1),b=Number(i.value??0),v=Number.isFinite(b)&&Number.isFinite(h)&&Number.isFinite(y)&&y!==h?(b-h)/(y-h)*100:Ke(i.percent);if(d||u){const N=m("div","cultui-meter-head");N.append(m("span","",d)),N.append(m("strong","",u)),r.append(N)}const C=m("div","cultui-meter-track"),I=m("span","cultui-meter-fill");return I.style.width=`${Math.max(0,Math.min(100,v))}%`,C.append(I),r.append(C),r}if(n==="modal"){const r=m("section","cultui-modal-surface");w(r,e),S(r,o,a);const d=g(i.title,"");d&&r.append(m("h2","cultui-modal-surface-title",d));for(const u of s)r.append(F(u,t));return r}if(n==="options"){const r=m("section","cultui-options");w(r,e),S(r,o,a);const d=g(i.label,"");d&&r.append(m("h3","cultui-list-title",d));for(const u of s)r.append(F(u,t));return s.length===0&&r.append(q("waiting for options")),r}if(n==="image.preview"){const r=m("figure","cultui-image-preview");w(r,e);const d=m("div","cultui-image-frame"),u=ee(p(i.src,i.assetUri,i.assetRef,""));if(u){const h=m("img");h.src=u,h.alt=g(i.label,""),h.style.imageRendering=g(i.imageRendering,"auto"),i.zoom!==void 0&&(h.style.transform=`scale(${i.zoom})`,h.style.transformOrigin=g(i.crop,"50% 50%")),d.append(h)}else d.append(m("span","",g(i.label,"image")));return r.append(d),i.label&&r.append(m("figcaption","",String(i.label))),r}if(n==="canvas.preview"||n==="canvas.editor"){const r=m("div",`cultui-canvas-placeholder ${n==="canvas.editor"?"editor":"preview"}`);return w(r,e),r.append(m("div","viz-label",g(i.label,"Canvas"))),r.append(m("div","cultui-canvas-grid",i.state==="empty"?"waiting for pixels":g(i.state,""))),r}if(n==="surface.slot"){const r=m("section","cultui-embedded-slot");w(r,e),S(r,o,a),r.dataset.slotId=g(i.slotId,g(l[0]?.slotId,"")),r.dataset.documentId=g(i.documentId,g(l[0]?.documentId,"")),r.dataset.schemaId=g(i.schemaId,g(l[0]?.schemaId,"")),r.dataset.presentationKind=g(i.presentationKind,g(l[0]?.presentationKind,""));const d=l.map(h=>x(h.surface)).find(h=>x(h.root).kind||h.root),u=i.modal===!0||r.dataset.presentationKind.includes("modal");if((d||r.dataset.documentId)&&u){const h=m("button","cultui-button cultui-modal-trigger",g(i.label,r.dataset.slotId||"Configure"));h.type="button",h.addEventListener("click",()=>it(Se(e,t),t)),r.append(h)}else if(d||r.dataset.documentId){const h=m("div","cultui-embedded-slot-body");h.append(m("div","detail","materializing surface")),r.append(h),Se(e,t).then(y=>{h.replaceChildren(y?.root?F(y.root,t):q("Embedded surface unavailable"))}).catch(y=>{h.replaceChildren(q(`Embedded surface failed: ${y instanceof Error?y.message:String(y)}`))})}else r.append(m("div","detail",r.dataset.presentationKind||r.dataset.slotId||"embedded surface"));return r}if(n==="control.slider")return hn(i,s,t);if(n==="control.toggle"){const r=m("label","toggle-row cultui-toggle");return w(r,e),r.append(m("span","field-control",i.value?"on":"off")),r.append(m("span","toggle-copy",g(i.label,"Toggle"))),r}if(n==="input.number"||n==="input.select"||n==="control.range"||n==="control.input.text"||n==="control.select"){const r=m("label","field cultui-field");return w(r,e),ce(r,i),S(r,o,a),r.append(m("span","field-label",g(i.label,""))),r.append(m("span","field-control cultui-field-value",i.value===void 0?"":String(i.value))),r}if(n==="color.swatch"){const r=m("div","paint-swatch cultui-swatch");return w(r,e),r.style.background=g(i.value,g(i.color,"currentColor")),r.title=g(i.label,"Color"),r}const c=m("section","pane");w(c,e),c.append(m("h2","",n));for(const r of s)c.append(F(r,t));return c}function et(e){const t=x(e.props);return{label:p(t.label,t.title,t.displayName,e.text,e.kind,"item"),status:p(t.status,t.state,""),detail:p(t.detail,t.phase,t.repoName,""),badges:Array.isArray(t.badges)?t.badges.map(n=>String(n).trim()).filter(Boolean):p(t.badges,"").split(",").map(n=>n.trim()).filter(Boolean)}}function S(e,t,n={}){const i=p(t.display,"");i&&(e.style.display=i);const o=p(t.direction,"");o&&(e.style.flexDirection=o==="horizontal"?"row":o==="vertical"?"column":o);const a=p(t.flexWrap,"");a&&(e.style.flexWrap=a);const s=p(t.gridTemplateColumns,"");s&&(e.style.gridTemplateColumns=s);const l=p(t.gridTemplateRows,"");l&&(e.style.gridTemplateRows=l);const f=p(t.gridTemplateAreas,"");f&&(e.style.gridTemplateAreas=f);const c=p(t.gridArea,"");c&&(e.style.gridArea=c);const r=p(t.placeItems,"");r&&(e.style.placeItems=r),P(e,"gap",p(t.gap,"")),P(e,"padding",p(t.padding,"")),P(e,"margin",p(t.margin,"")),P(e,"top",p(t.top,"")),P(e,"right",p(t.right,"")),P(e,"bottom",p(t.bottom,"")),P(e,"left",p(t.left,"")),P(e,"width",p(t.width,"")),P(e,"minWidth",p(t.minWidth,"")),P(e,"maxWidth",p(t.maxWidth,"")),P(e,"height",p(t.height,"")),P(e,"minHeight",p(t.minHeight,"")),P(e,"maxHeight",p(t.maxHeight,""));const d=p(t.alignItems,"");d&&(e.style.alignItems=d);const u=p(t.justifyContent,"");u&&(e.style.justifyContent=u);const h=p(t.alignSelf,"");h&&(e.style.alignSelf=h);const y=p(t.overflow,"");y&&(e.style.overflow=y);const b=p(t.overflowX,"");b&&(e.style.overflowX=b);const v=p(t.overflowY,"");v&&(e.style.overflowY=v);const C=p(t.position,"");C&&(e.style.position=C);const I=p(t.cursor,"");I&&(e.style.cursor=I);const N=p(n.background,"");N&&(e.style.background=de(N));const Y=p(n.color,"");Y&&(e.style.color=de(Y)),P(e,"borderWidth",p(n.borderWidth,""));const z=p(n.borderColor,"");z&&(e.style.borderColor=de(z));const H=p(n.borderStyle,"");H&&(e.style.borderStyle=H),P(e,"borderRadius",p(n.borderRadius,""));const V=p(n.boxShadow,"");V&&(e.style.boxShadow=V);const K=p(n.font,"");K&&(e.style.font=K),P(e,"fontSize",p(n.fontSize,""));const _=p(n.fontWeight,"");_&&(e.style.fontWeight=_);const J=p(n.textTransform,"");J&&(e.style.textTransform=J);const ie=p(n.textAlign,"");ie&&(e.style.textAlign=ie);const he=p(n.whiteSpace,"");he&&(e.style.whiteSpace=he);const Fe=p(n.overflow,"");Fe&&(e.style.overflow=Fe);const Le=p(n.textOverflow,"");Le&&(e.style.textOverflow=Le),P(e,"lineHeight",p(n.lineHeight,""))}function P(e,t,n){n&&(e.style[t]=R(n))}function k(e,t){const n={};for(const[i,o]of Object.entries(e))i.startsWith(t)&&(n[i.slice(t.length)]=o);return n}function Jt(e){const t=(e.children||[]).filter(u=>u.kind==="world.entity3d"),n=t.map(u=>nn(x(u.props).position,[0,0,0])),i=n.map(u=>u[0]),o=n.map(u=>u[2]),a=Math.min(...i,-1),s=Math.max(...i,1),l=Math.min(...o,-1),f=Math.max(...o,1),c=Math.max(1,s-a),r=Math.max(1,f-l),d=6;return t.map((u,h)=>{const y=x(u.props);return{entityId:p(y.entityId,u.id),faction:g(y.faction,"neutral"),kind:g(y.entityKind,"entity"),label:p(y.label,y.entityId,u.id,"Entity"),controlled:Re(y.controllable),xPercent:d+(n[h][0]-a)/c*(100-d*2),yPercent:d+(1-(n[h][2]-l)/r)*(100-d*2),source:u}})}function Zt(e,t,n,i,o){const a=m("section","cultui-world-scene");w(a,e),S(a,n,i),a.tabIndex=0,a.setAttribute("aria-label",g(t.label,"Interactive world"));const s=Jt(e),l=s.find(r=>r.controlled),f=m("div","cultui-world-heading");f.append(m("strong","",g(t.label,"World"))),f.append(m("span","",`${s.length} entities`)),a.append(f);const c=m("div","cultui-world-plane");for(const r of s){const d=m("button",`cultui-world-entity faction-${$e(r.faction)} kind-${$e(r.kind)}`);d.type="button",d.style.left=`${r.xPercent}%`,d.style.top=`${r.yPercent}%`,d.dataset.entityId=r.entityId,d.dataset.controlled=r.controlled?"true":"false",d.title=r.label,d.setAttribute("aria-label",`${r.label}, ${r.kind}, ${r.faction}`),d.append(m("span","cultui-world-entity-glyph",tn(r.kind))),d.append(m("span","cultui-world-entity-label",r.label));const u=x(r.source.props),h=p(u.targetCommand,t.targetCommand,u.focusCommand,t.focusCommand);h&&d.addEventListener("click",()=>ye(h,{actorEntityId:l?.entityId||p(t.playerEntityId),entityId:r.entityId,targetEntityId:r.entityId,targetEntityKey:r.entityId},r.source,o)),c.append(d)}return a.append(c),a.addEventListener("keydown",r=>{const d=en(r.key);if(d){r.preventDefault();const h=p(t.movementCommand,x(l?.source.props).moveCommand);h&&ye(h,{actorEntityId:l?.entityId||p(t.playerEntityId),actorEntityKey:l?.entityId||p(t.playerEntityId),directionX:d[0],directionY:d[1],scalar:1},e,o);return}const u=r.key===" "?p(t.actionCommand,x(l?.source.props).actionCommand):r.key.toLowerCase()==="f"?p(t.focusCommand,x(l?.source.props).focusCommand):"";u&&(r.preventDefault(),ye(u,{actionId:r.key===" "?"0":"focus",actorEntityId:l?.entityId||p(t.playerEntityId),actorEntityKey:l?.entityId||p(t.playerEntityId)},e,o))}),a}function ye(e,t,n,i){i.commandSink?.(Qt(e,t,i),n)}function Qt(e,t,n=W){return ne(e,{action:t},n)}function en(e){switch(e.toLowerCase()){case"w":case"arrowup":return[0,1];case"s":case"arrowdown":return[0,-1];case"a":case"arrowleft":return[-1,0];case"d":case"arrowright":return[1,0];default:return}}function tn(e){return e.includes("station")?"S":e.includes("projectile")?".":e.includes("ship")?"^":"+"}function $e(e){return e.toLowerCase().replace(/[^a-z0-9_-]+/g,"-")}function X(e,t){const n=Number(e);return Number.isFinite(n)?n:t}function nn(e,t){if(Array.isArray(e)){const n=e.map(i=>Number(i));if(n.length>=3&&n.slice(0,3).every(Number.isFinite))return[n[0],n[1],n[2]]}if(typeof e=="string"){const n=e.split(/[,\s]+/).map(i=>Number(i)).filter(Number.isFinite);if(n.length>=3)return[n[0],n[1],n[2]]}return t}function rn(e,t,n,i){const o=Re(t.active),a=m("section","cultui-inventory-drag");w(a,e),S(a,n,i),a.dataset.active=o?"true":"false";const s=m("div","cultui-inventory-section-label","Drag");if(S(s,k(n,"sectionLabel."),k(i,"sectionLabel.")),a.append(s),!o){const d=m("div","cultui-inventory-empty","None");return S(d,k(n,"empty."),k(i,"empty.")),a.append(d),a}const l=p(t.itemKey,t.item,"item"),f=m("div","cultui-inventory-drag-chip",l);S(f,k(n,"dragChip."),k(i,"dragChip.")),a.append(f);const c=[t.originOffsetX,t.originOffsetY].map(d=>Number(d)).map(d=>Number.isFinite(d)?d:0),r=m("div","cultui-inventory-meta",`origin ${c[0]}, ${c[1]}`);return S(r,k(n,"meta."),k(i,"meta.")),a.append(r),a}function on(e,t,n,i,o){const a=m("section","cultui-inventory-grid");w(a,e),S(a,n,i);const s=D(p(t.columns,t.gridColumns,t.widthCells,""),0),l=s>0?s:6,f=D(t.rows,0),c=f>0?f:Math.max(3,Math.ceil(Math.max((e.children||[]).length,1)/l));a.style.setProperty("--inventory-columns",String(l)),a.style.setProperty("--inventory-rows",String(c)),a.dataset.cellSpriteMode=g(t.cellSpriteMode,"");const r=p(t.title,t.label,"Inventory"),d=m("div","cultui-inventory-section-label",r);S(d,k(n,"sectionLabel."),k(i,"sectionLabel.")),a.append(d);const u=m("div","cultui-inventory-board");u.style.display="grid",u.style.gridTemplateColumns=`repeat(${l}, ${R(p(t.cellSize,"72"))})`,u.style.gridTemplateRows=`repeat(${c}, ${R(p(t.cellSize,"72"))})`,u.style.gap=R(p(t.cellGap,"4")),u.style.overflow=p(t.boardOverflow,"auto"),u.style.padding=R(p(t.boardPadding,"4")),S(u,k(n,"board."),k(i,"board.")),u.addEventListener("dragover",b=>{cn(t)&&(b.preventDefault(),b.dataTransfer&&(b.dataTransfer.dropEffect="move"))}),u.addEventListener("drop",b=>{const v=b.dataTransfer?.getData("application/vnd.gamecult.eve.inventory-item+json")||"";if(!v)return;let C;try{C=JSON.parse(v)}catch{return}const I=u.getBoundingClientRect(),N=Math.max(1,X(t.cellSize,72)),Y=Math.max(0,X(t.cellGap,4)),z=N+Y,H=Math.max(0,Math.min(l-1,Math.floor((b.clientX-I.left)/z))),V=Math.max(0,Math.min(c-1,Math.floor((b.clientY-I.top)/z))),K=sn(C,t,H,V,o);K&&(b.preventDefault(),o.commandSink?.(K,e))});const h=new Set;for(const b of e.children||[]){const v=x(b.props),C=D(v.x,0),I=D(v.y,0);h.add(`${C}:${I}`),u.append(F(b,o))}const y=Math.min(l*c,256);for(let b=0;b{!a.draggable||!y.dataTransfer||(y.dataTransfer.effectAllowed="move",y.dataTransfer.setData("application/vnd.gamecult.eve.inventory-item+json",JSON.stringify(t)))});let f=D(t.shapeWidth,1),c=D(t.shapeHeight,1);const r=p(t.rotation,"").toLowerCase();["clockwise","counterclockwise","right","left","rotate90","rotate270"].includes(r)&&([f,c]=[c,f]),a.style.gridColumn=`${s+1} / span ${f}`,a.style.gridRow=`${l+1} / span ${c}`;const d=m("span","cultui-inventory-item-icon");S(d,k(n,"icon."),k(i,"icon.")),d.textContent=In(p(t.iconAssetUri,t.iconAssetKey,t.itemKey,t.label,"")),a.append(d);const u=p(t.label,t.itemKey,"");if(u){const y=m("span","cultui-inventory-item-label",u);S(y,k(n,"label."),k(i,"label.")),a.append(y)}const h=Number(t.quantity);if(Number.isFinite(h)&&h>1){const y=m("span","cultui-inventory-item-quantity",String(h));S(y,k(n,"quantity."),k(i,"quantity.")),a.append(y)}return a.title=[u,p(t.source,""),p(t.quality,"")?`quality ${t.quality}`:"",p(t.durability,"")?`durability ${t.durability}`:""].filter(Boolean).join(" | "),a}function cn(e){return Object.entries(e).some(([t,n])=>(t==="dropCommand"||t.startsWith("dropCommand."))&&typeof n=="string"&&n.length>0)}function sn(e,t,n,i,o=W){const a=p(e.sourceKind,e.source,""),s=p(t[`dropCommand.${a}`],t.dropCommand,"");if(!a||!s)return;const l=X(e.sourceIndex,-1),f=X(t.targetIndex,-1),c=p(t.targetKind,""),r={sourceKind:a,originEntityKey:p(e.sourceEntityKey,e.entityKey,""),originIndex:l,originCargoIndex:a==="cargo"?l:-1,itemKey:p(e.itemKey,""),quantity:Math.max(1,X(e.quantity,1)),sourceX:X(e.x,-2147483648),sourceY:X(e.y,-2147483648),destinationKind:c,destinationEntityKey:p(t.targetEntityKey,t.entityKey,""),destinationIndex:f,destinationCargoIndex:c==="cargo"?f:-1,destinationX:Math.trunc(n),destinationY:Math.trunc(i),hasDestinationPosition:!0};return ne(s,{action:r},o)}function ln(e,t,n){const i=m("section","cultui-binding-map");w(i,e);const o=document.createElementNS("http://www.w3.org/2000/svg","svg");o.setAttribute("class","cultui-xbox-controller"),o.setAttribute("viewBox","0 0 780 420"),o.setAttribute("role","img"),o.setAttribute("aria-label",g(t.label,"Xbox controller remapping")),o.innerHTML=fn(),i.append(o);const a=m("div","cultui-binding-overlay");for(const s of e.children||[]){const l=x(s.props),f=g(l.target,s.id||""),c=m("button","cultui-binding-box");w(c,s),c.type="button",c.dataset.target=f,c.dataset.kind=g(l.bindingKind,"button"),c.style.left=R(l.x??"50%"),c.style.top=R(l.y??"50%"),c.append(m("span","cultui-binding-target",g(l.label,f))),c.append(m("span","cultui-binding-source",g(l.current,"unmapped"))),c.addEventListener("click",()=>{const d=nt(s);if(tt(s)||d?.documentId)it(Se(s,n),n);else{const u=g(l.commandId,"");u&&n.commandSink?.(ne(u,l,n),s)}}),a.append(c);const r=document.createElementNS("http://www.w3.org/2000/svg","line");r.setAttribute("class","cultui-binding-line"),r.setAttribute("x1",g(l.anchorX,"390")),r.setAttribute("y1",g(l.anchorY,"210")),r.setAttribute("x2",String(We(l.x,780))),r.setAttribute("y2",String(We(l.y,420))),o.append(r)}return i.append(a),i}function tt(e){for(const t of e.embeddedDocuments||[]){const n=x(t.surface);if(n.root)return n}}function nt(e){const t=(e.embeddedDocuments||[])[0],n=x(e.props),i=p(n.documentId,t?.documentId,"");if(i)return{documentId:i,presentationKind:p(n.presentationKind,t?.presentationKind,""),schemaId:p(n.schemaId,t?.schemaId,""),slotId:p(n.slotId,t?.slotId,"")}}async function Se(e,t){const n=tt(e);if(n)return n;const i=nt(e);if(!i?.documentId||!t.documentResolver)return;const o=await t.documentResolver(i,e);if(o)return x(o.surface).root?o.surface:o}function it(e,t){const n=m("div","cultui-modal-backdrop"),i=m("section","cultui-modal");i.setAttribute("role","dialog"),i.setAttribute("aria-modal","true");const o=m("header","cultui-modal-header");o.append(m("h2","","Configure Binding"));const a=m("button","cultui-modal-close","Close");a.type="button",a.addEventListener("click",()=>n.remove()),o.append(a);const s=m("div","cultui-modal-body");i.append(o,s),n.append(i),n.addEventListener("click",l=>{l.target===n&&n.remove()}),document.body.append(n),s.append(m("div","detail","materializing document")),Promise.resolve(e).then(l=>{o.querySelector("h2").textContent=l?.title||"Configure Binding",s.replaceChildren(l?.root?F(l.root,t):q("Embedded document unavailable"))}).catch(l=>{s.replaceChildren(q(`Embedded document failed: ${l instanceof Error?l.message:String(l)}`))})}function dn(e,t,n,i,o){const a=x(e.props),s=(e.children||[])[0],l=x(s?.props),f=p(l.anchor,a.anchor,"trigger"),c=p(l.placement,a.placement,"below-start"),r=Number(p(l.offsetX,a.offsetX,"0"))||0,d=Number(p(l.offsetY,a.offsetY,"4"))||0,u=m("div","cultui-modal-backdrop");u.style.background="transparent",u.style.placeItems="start";const h=m("section","cultui-modal");h.setAttribute("role","dialog"),h.setAttribute("aria-modal","true"),h.style.position="fixed",h.style.width="min(420px, calc(100vw - 24px))",h.style.maxHeight="min(620px, calc(100vh - 24px))";const y=m("header","cultui-modal-header");y.append(m("h2","",p(l.title,a.title,a.label,"Options")));const b=m("button","cultui-modal-close","Close");b.type="button",b.addEventListener("click",()=>u.remove()),y.append(b);const v=m("div","cultui-modal-body");if(s){const I=s.children||[];I.length>0?v.replaceChildren(...I.map(N=>F(N,t))):v.append(F(s,t))}else v.append(q("Popup has no content"));h.append(y,v),u.append(h),u.addEventListener("click",I=>{I.target===u&&u.remove()}),document.body.append(u);const C=un(f,c,n,h,i,o,r,d);h.style.left=`${C.left}px`,h.style.top=`${C.top}px`}function un(e,t,n,i,o,a,s,l){const f=n.getBoundingClientRect(),c=i.getBoundingClientRect(),r=c.width||420,d=c.height||320,u=e==="cursor"?{left:o,right:o,top:a,bottom:a}:f;if(t==="right-down"||t==="dropdown")return ae(u,r,d,s,l,"right","down");if(t==="left-down")return ae(u,r,d,s,l,"left","down");if(t==="right-up")return ae(u,r,d,s,l,"right","up");if(t==="left-up")return ae(u,r,d,s,l,"left","up");switch(t){case"above-start":return U(u.left+s,u.top-d-l,r,d);case"above-end":return U(u.right-r+s,u.top-d-l,r,d);case"below-end":return U(u.right-r+s,u.bottom+l,r,d);case"right-start":return U(u.right+s,u.top+l,r,d);case"left-start":return U(u.left-r-s,u.top+l,r,d);case"center":return U((window.innerWidth-r)/2+s,(window.innerHeight-d)/2+l,r,d);default:return U(u.left+s,u.bottom+l,r,d)}}function ae(e,t,n,i,o,a,s){const f=window.innerWidth-e.right-i-12,c=e.left-i-12,r=window.innerHeight-e.top-o-12,d=e.bottom-o-12,u=a==="right"?f>=t||f>=c:!(c>=t||c>=f),h=s==="down"?r>=n||r>=d:!(d>=n||d>=r),y=u?e.right+i:e.left-t-i,b=h?e.top+o:e.bottom-n-o;return U(y,b,t,n)}function U(e,t,n,i){return{left:Ue(e,12,window.innerWidth-n-12),top:Ue(t,12,window.innerHeight-i-12)}}function Ue(e,t,n){return n @@ -150,4 +150,4 @@ void main() { YBXA - `}function mn(e,t=document.body){const n=Te(e),i=document.documentElement.style;yn(n.assets.fontCss);for(const[c,r]of Object.entries(qt))i.setProperty(c,r);const o={colorBackground:"--bg",colorBackgroundTop:"--bg-top",colorBackgroundMid:"--bg-mid",colorBackgroundBottom:"--bg-bottom",colorShellSurfaceTop:"--shell-top",colorShellSurfaceBottom:"--shell-bottom",colorPanel:"--panel",colorPanelAlt:"--panel-2",colorPanelBorder:"--line",colorPanelBorderDeep:"--line-deep",colorText:"--text",colorTextBright:"--text-bright",colorMuted:"--quiet",colorAccent:"--accent",colorAccentStrong:"--accent-strong",colorCornerAccent:"--corner-accent",colorLink:"--cyan",colorShadow:"--shadow",colorPixelGrid:"--pixel-grid"};for(const[c,r]of Object.entries(o)){const d=n.tokens[c];d!==void 0&&i.setProperty(r,String(d))}const a=n.tokens.fontBody,s=n.tokens.fontDisplay,l=n.tokens.fontTitle,f=n.tokens.fontMono;a&&i.setProperty("--font-body",se(String(a))),s&&i.setProperty("--font-title",se(String(s))),l&&i.setProperty("--font-title",se(String(l))),f&&i.setProperty("--font-mono",se(String(f))),n.tokens.borderWidthPx&&i.setProperty("--border-width",`${n.tokens.borderWidthPx}px`),n.tokens.borderBottomWidthPx&&i.setProperty("--border-bottom-width",`${n.tokens.borderBottomWidthPx}px`),n.tokens.cornerAccentPx&&i.setProperty("--corner-accent-size",`${n.tokens.cornerAccentPx}px`),t.dataset.pixelArt=n.tokens.pixelArt||n.tokens.imageRendering==="pixelated"?"true":"false",t.dataset.scanlines=n.tokens.scanlineOverlay?"true":"false"}function ne(e,t={},n=O){const i=x(t.action),o=n.provider?.providerId||te?.providerId||"surface unknown",a=n.activeSurfaceId||te?.surface?.id||n.provider?.surfaces?.[0]?.surfaceId||o,s=pn(n,a),l=p(s.commandBoundary,t.commandBoundary,i.commandBoundary,i.target),f=p(s.receiptSchema,t.receiptSchema,i.receiptSchema),c={type:"surface-command",schema:"gamecult.eve.command_invocation.v1",providerId:o,surfaceId:a,command:e||g(i.type,"invoke"),payload:{...i,transport:t.transport??null},issuedAt:new Date().toISOString(),clientId:n.clientId||"eve.browser"};return l&&(c.commandBoundary=l),f&&(c.receiptSchema=f),c}function pn(e,t){const n=[...e.provider?.surfaces||[],...e.provider?.localAdvertisement?.surfaces||[]];return n.find(i=>i.surfaceId===t&&i.worldInteraction)?.worldInteraction||n.find(i=>i.worldInteraction)?.worldInteraction||{}}function We(e,t){const n=x(e.action);return g(e.command,g(e.commandId,g(n.command,g(n.target,g(n.type,t?.commandId||"")))))}function q(e){const t=m("section","pane");return t.append(m("h2","","No Surface")),t.append(m("div","detail",e)),t}function ze(e,t,n,i,o){e.dataset.commandId=n,e.tabIndex=e instanceof HTMLButtonElement?e.tabIndex:0;const a=()=>{const s=ne(n,i,o);o.commandSink?.(s,t)};e.addEventListener("click",a),e.addEventListener("keydown",s=>{(s.key==="Enter"||s.key===" ")&&(s.preventDefault(),a())})}function hn(e,t,n){const i=gn(e,t),o=x(i.find(u=>u.kind==="control.box")?.props),a=i.filter(u=>u.kind==="control.part"),s=Number(e.min??0),l=Number(e.max??1),f=m("div","cultui-slider");f.dataset.skin=g(e.skin,"default"),f.dataset.bind=g(e.bind,""),vn(f,o);const c=m("div","cultui-slider-visual"),r=m("input","cultui-slider-input");r.type="range",r.min=String(s),r.max=String(l),r.step=String(e.step??.01),r.setAttribute("aria-label",g(e.bind,"slider"));for(const u of a){const h=x(u.props),y=m("span",`cultui-slider-part ${g(h.name,"part")}`);xn(y,h),c.append(y)}return a.some(u=>x(u.props).name==="track")||c.append(m("span","cultui-slider-part track")),a.some(u=>x(u.props).name==="fill")||c.append(m("span","cultui-slider-part fill")),a.some(u=>x(u.props).name==="thumb")||c.append(m("span","cultui-slider-part thumb")),f.append(c,r),(u=>{const h=Number(u??s),y=Math.max(s,Math.min(l,Number.isFinite(h)?h:s)),b=l===s?0:(y-s)/(l-s)*100;r.value=String(y),f.style.setProperty("--cultui-slider-value",`${b}%`),f.dataset.value=String(y)})(e.value??s),r.addEventListener("input",()=>{const u=ne(g(e.command,"control.slider.input"),{...e,action:{...x(e.action),value:Number(r.value)},value:Number(r.value)},n);n.commandSink?.(u,{kind:"control.slider",props:e})}),f}function gn(e,t){const n=typeof e.skin=="string"?e.skin:"",o=[...n?Je.controlSkins[n]?.children||[]:[],...t];return o.length?o:[{kind:"control.box",props:{height:18,overflow:"visible"}},{kind:"control.part",props:{name:"track",anchor:"center",size:["100%",6],radius:2,fill:"color.panelInset"}},{kind:"control.part",props:{name:"fill",anchor:["left","center"],size:["value%",6],radius:2,fill:"color.accent"}},{kind:"control.part",props:{name:"thumb",anchor:["value","center"],size:[12,12],bleed:3,radius:999,fill:"color.accent"}}]}function Te(e){if(Array.isArray(e)){const t={},n={};for(const i of e)i?.name&&(i.name==="font.web.google"&&typeof i.value=="string"?n.fontCss=i.value:t[i.name]=i.value);return{tokens:t,assets:n,controlSkins:{}}}return{tokens:x(e?.tokens),assets:x(e?.assets),controlSkins:x(e?.controlSkins)}}function ee(e){if(!e)return"";const t=O.assetUrlResolver;return t?t(e,te):/^(https?:|data:|blob:)/i.test(e)?e:O.assetBaseUrl&&e.startsWith("/")?`${O.assetBaseUrl.replace(/\/+$/,"")}${e}`:e}function yn(e){const t=typeof e=="string"?e.trim():"";if(oe?.dataset.href===t||(oe?.remove(),oe=void 0,!t))return;const n=document.createElement("link");n.rel="preconnect",n.href="https://fonts.gstatic.com",n.crossOrigin="anonymous",document.head.append(n);const i=document.createElement("link");i.rel="stylesheet",i.href=t,i.dataset.href=t,document.head.append(i),oe=i}function bn(e,t,n){const i=[];return(e==="text.title"||t.role==="title")&&i.push("cultui-title","eve-text-title"),e==="text.subtitle"&&i.push("eve-text-subtitle"),e==="label"&&i.push("cultui-label"),t.role==="mono"&&i.push("detail","mono"),i.length===0&&i.push("detail","eve-text"),i.join(" ")}function vn(e,t){t.height!==void 0&&(e.style.minHeight=R(t.height)),t.width!==void 0&&(e.style.width=R(t.width)),typeof t.overflow=="string"&&(e.style.overflow=t.overflow)}function xn(e,t){const n=Array.isArray(t.size)?t.size:[t.size,void 0];n[0]!==void 0&&n[0]!=="value%"&&(e.style.width=R(n[0])),n[1]!==void 0&&(e.style.height=R(n[1])),t.radius!==void 0&&(e.style.borderRadius=R(t.radius)),t.fill!==void 0&&(e.style.background=de(String(t.fill))),t.bleed!==void 0&&e.style.setProperty("--part-bleed",R(t.bleed)),(Array.isArray(t.anchor)?t.anchor:[t.anchor]).includes("value")&&(e.dataset.anchorValue="true")}function ce(e,t){t.gap!==void 0&&(e.style.gap=R(t.gap)),t.padding!==void 0&&(e.style.padding=R(t.padding)),t.size!==void 0&&(e.style.flex=wn(t.size)),t.min!==void 0&&(e.style.minWidth=R(t.min)),t.max!==void 0&&(e.style.maxWidth=R(t.max)),t.align!==void 0&&(e.style.alignItems=t.align==="end"?"flex-end":String(t.align)),(t.clip==="true"||t.clip===!0)&&(e.style.overflow="hidden"),t.scroll==="y"&&(e.style.overflowY="auto"),t.scroll==="x"&&(e.style.overflowX="auto")}function wn(e){return typeof e=="string"&&e.endsWith("fr")?`${Number(e.slice(0,-2))||1} 1 0`:e==="auto"||e==="content"?"0 0 auto":`0 0 ${R(e)}`}function R(e){return Array.isArray(e)?e.map(R).join(" "):typeof e=="number"?`${e}px`:typeof e=="string"&&/^-?\d+(\.\d+)?$/.test(e)?`${e}px`:String(e??"")}function de(e){return{"color.accent":"var(--accent)","color.panelInset":"rgba(0, 0, 0, 0.34)","color.panel":"var(--panel)","color.text":"var(--text)"}[e]||e}function se(e){return e.includes("Zen Kaku Gothic New")||e.includes("M PLUS 1")?e:e.includes("Montserrat")?`${e}, "Zen Kaku Gothic New", "M PLUS 1", "Noto Sans JP", "Hiragino Sans", "Yu Gothic", sans-serif`:e.includes("Ubuntu")?`${e}, "M PLUS 1", "Noto Sans JP", "Hiragino Sans", "Yu Gothic", sans-serif`:`${e}, "Noto Sans JP", "Hiragino Sans", "Yu Gothic", sans-serif`}function w(e,t){t.id&&(e.id=t.id),t.id&&(e.dataset.eveNodeId=t.id),t.kind&&(e.dataset.eveKind=t.kind)}function x(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function Sn(e){return Array.isArray(e)?e:[]}function En(e){return Sn(e).map((t,n)=>Qe({kind:"list.item",props:x(t)}))}function g(e,t){return e==null?t:String(e)}function p(...e){for(const t of e){if(typeof t=="string"&&t.trim())return t;if(typeof t=="number"&&Number.isFinite(t))return String(t)}return""}function Re(e){return typeof e=="boolean"?e:typeof e=="number"?e!==0:typeof e=="string"?["1","true","yes","on"].includes(e.trim().toLowerCase()):!1}function B(e,t){const n=Number(e);return Number.isFinite(n)?Math.max(0,Math.floor(n)):t}function An(e){const t=e.toLowerCase();return t.includes("sensor")?"S":t.includes("thermal")?"T":t.includes("fuel")?"F":t.includes("cargo")?"C":t.includes("weapon")?"W":e.trim().slice(0,1).toUpperCase()||"?"}function In(e){const t=e.trim().split(/\s+/).filter(Boolean);return t.length&&t.slice(0,2).map(n=>n[0]?.toUpperCase()||"").join("")||"?"}function Ke(e){const t=Number(e);return Number.isFinite(t)?Math.max(0,Math.min(100,t)):0}function m(e,t="",n=""){const i=document.createElement(e);return t&&(i.className=t),n&&(i.textContent=n),i}const Ge=document.querySelector("#surface"),D=document.querySelector("#status"),pe=document.querySelector("#receipt"),_n=document.querySelector("#compiler"),Ee=document.querySelector("#composer"),ue=document.querySelector("#destination-form"),Ae=document.querySelector("#compiler-results"),Cn=document.querySelector("#campaign-lab"),je=document.querySelector("#campaign-list");let ke=0;async function W(){const e=await fetch("/api/surface");if(e.status===401){D.textContent="This laboratory requires an unused invite link.";return}const t=await e.json(),n=t.surface_id==="ghostlight.compiler";if(_n.hidden=!n,Ee.hidden=n,ue.hidden=n,Ge.hidden=n,Cn.hidden=n,n){D.textContent="No campaign exists. Retrieve the Vault and approve a world seed.";return}ke=Number(t.version??0),Yt(t,Ge,{body:document.body,clientId:"ghostlight.browser",statusElement:D});const i=await fetch("/api/campaigns").then(o=>o.json());je.innerHTML=i.campaigns.map(o=>``).join(""),je.querySelectorAll("button:not(:disabled)").forEach(o=>o.addEventListener("click",async()=>{await $(`/api/campaigns/select/${o.dataset.campaignId}`,{}),await W()}))}async function $(e,t){D.textContent="Retrieving evidence and compiling…";const n=await fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)}),i=n.headers.get("content-type")?.includes("json")?await n.json():await n.text();if(!n.ok)throw D.textContent=typeof i=="string"?i:"Compilation failed",new Error(D.textContent);return i}function Xe(e,t,n){Ae.innerHTML=`
${e.map((i,o)=>`

${i.title??i.name}

${i.player_hook??i.premise}

${i.era?`${i.era} · ${i.place} · ${i.pressure}`:""}

`).join("")}
`,Ae.querySelectorAll("button").forEach(i=>i.addEventListener("click",()=>n(e[Number(i.dataset.index)])))}document.querySelector("#suggest-form").addEventListener("submit",async e=>{e.preventDefault();const t=await $("/api/compiler/openings",{setting:document.querySelector("#setting").value,constraints:[]});Xe(t.openings,"Choose opening",async n=>{const i=await $("/api/compiler/roles",n);Xe(i.roles,"Choose role",async o=>it(await $("/api/compiler/selected",{campaign_name:n.title,opening:n,role:o})))})});document.querySelector("#custom-form").addEventListener("submit",async e=>{e.preventDefault(),await it(await $("/api/compiler/custom",Object.fromEntries(new FormData(e.currentTarget))))});async function it(e){const t=e.preview;Ae.innerHTML=`

${t.title}

${t.campaign.transcript?.[0]?.text??""}

${Object.keys(t.campaign.locations).length} locations · ${Object.keys(t.campaign.actors).length} actors · ${Object.keys(t.campaign.institutions).length} institutions

${t.gaps.length?`Material gaps: ${t.gaps.join("; ")}`:"No declared material gaps."}

Branch assumptions: ${t.branch_assumptions.join("; ")||"none"}

`,document.querySelector("#approve-world").addEventListener("click",async()=>{await $(`/api/compiler/approve/${e.preview_id}`,{}),await W()}),D.textContent="Preview compiled. Nothing has entered world state yet."}async function Ie(e){D.textContent="The world is considering the command…";const t=await fetch("/api/command",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)}),n=await t.json();return pe.hidden=!1,pe.textContent=JSON.stringify(n,null,2),t.ok?(await W(),n):(D.textContent=n.error??"The command was refused.",await W(),n)}Ee.addEventListener("submit",async e=>{e.preventDefault();const t=document.querySelector("#attempt").value.trim();if(!t)return;const n=await Ie({type:"assess",expected_revision:ke,intent:{actor_id:"player",description:t,intended_effect:t}});if(n?.kind==="assessed"){const i=document.createElement("button");i.textContent=n.assessment.admissible?"Confirm server roll":"Revise attempt",i.addEventListener("click",async()=>{n.assessment.admissible?(await Ie({type:"attempt",assessment_digest:n.assessment.digest}),i.remove()):document.querySelector("#attempt").focus()}),Ee.append(i)}});document.querySelector("#wait").addEventListener("click",()=>{Ie({type:"wait",expected_revision:ke,minutes:60})});ue.addEventListener("submit",async e=>{e.preventDefault();const t=await $("/api/compiler/destination",Object.fromEntries(new FormData(ue))),n=t.preview;pe.hidden=!1,pe.textContent=JSON.stringify(n,null,2),D.textContent="Destination preview compiled; topology is unchanged until approval.";const i=document.createElement("button");i.textContent="Approve destination",i.addEventListener("click",async()=>{await $(`/api/compiler/destination/approve/${t.preview_id}`,{}),i.remove(),await W()}),ue.append(i)});document.querySelector("#fork-form").addEventListener("submit",async e=>{e.preventDefault(),await $("/api/campaigns/fork",Object.fromEntries(new FormData(e.currentTarget))),await W()});document.querySelector("#reset-form").addEventListener("submit",async e=>{e.preventDefault(),await $("/api/campaigns/reset",Object.fromEntries(new FormData(e.currentTarget))),await W()});document.querySelector("#load-operator").addEventListener("click",async()=>{const e=await fetch("/api/operator");document.querySelector("#operator-output").textContent=JSON.stringify(await e.json(),null,2)});W(); + `}function mn(e,t=document.body){const n=Te(e),i=document.documentElement.style;yn(n.assets.fontCss);for(const[c,r]of Object.entries(Yt))i.setProperty(c,r);const o={colorBackground:"--bg",colorBackgroundTop:"--bg-top",colorBackgroundMid:"--bg-mid",colorBackgroundBottom:"--bg-bottom",colorShellSurfaceTop:"--shell-top",colorShellSurfaceBottom:"--shell-bottom",colorPanel:"--panel",colorPanelAlt:"--panel-2",colorPanelBorder:"--line",colorPanelBorderDeep:"--line-deep",colorText:"--text",colorTextBright:"--text-bright",colorMuted:"--quiet",colorAccent:"--accent",colorAccentStrong:"--accent-strong",colorCornerAccent:"--corner-accent",colorLink:"--cyan",colorShadow:"--shadow",colorPixelGrid:"--pixel-grid"};for(const[c,r]of Object.entries(o)){const d=n.tokens[c];d!==void 0&&i.setProperty(r,String(d))}const a=n.tokens.fontBody,s=n.tokens.fontDisplay,l=n.tokens.fontTitle,f=n.tokens.fontMono;a&&i.setProperty("--font-body",se(String(a))),s&&i.setProperty("--font-title",se(String(s))),l&&i.setProperty("--font-title",se(String(l))),f&&i.setProperty("--font-mono",se(String(f))),n.tokens.borderWidthPx&&i.setProperty("--border-width",`${n.tokens.borderWidthPx}px`),n.tokens.borderBottomWidthPx&&i.setProperty("--border-bottom-width",`${n.tokens.borderBottomWidthPx}px`),n.tokens.cornerAccentPx&&i.setProperty("--corner-accent-size",`${n.tokens.cornerAccentPx}px`),t.dataset.pixelArt=n.tokens.pixelArt||n.tokens.imageRendering==="pixelated"?"true":"false",t.dataset.scanlines=n.tokens.scanlineOverlay?"true":"false"}function ne(e,t={},n=W){const i=x(t.action),o=n.provider?.providerId||te?.providerId||"surface unknown",a=n.activeSurfaceId||te?.surface?.id||n.provider?.surfaces?.[0]?.surfaceId||o,s=pn(n,a),l=p(s.commandBoundary,t.commandBoundary,i.commandBoundary,i.target),f=p(s.receiptSchema,t.receiptSchema,i.receiptSchema),c={type:"surface-command",schema:"gamecult.eve.command_invocation.v1",providerId:o,surfaceId:a,command:e||g(i.type,"invoke"),payload:{...i,transport:t.transport??null},issuedAt:new Date().toISOString(),clientId:n.clientId||"eve.browser"};return l&&(c.commandBoundary=l),f&&(c.receiptSchema=f),c}function pn(e,t){const n=[...e.provider?.surfaces||[],...e.provider?.localAdvertisement?.surfaces||[]];return n.find(i=>i.surfaceId===t&&i.worldInteraction)?.worldInteraction||n.find(i=>i.worldInteraction)?.worldInteraction||{}}function Oe(e,t){const n=x(e.action);return g(e.command,g(e.commandId,g(n.command,g(n.target,g(n.type,t?.commandId||"")))))}function q(e){const t=m("section","pane");return t.append(m("h2","","No Surface")),t.append(m("div","detail",e)),t}function ze(e,t,n,i,o){e.dataset.commandId=n,e.tabIndex=e instanceof HTMLButtonElement?e.tabIndex:0;const a=()=>{const s=ne(n,i,o);o.commandSink?.(s,t)};e.addEventListener("click",a),e.addEventListener("keydown",s=>{(s.key==="Enter"||s.key===" ")&&(s.preventDefault(),a())})}function hn(e,t,n){const i=gn(e,t),o=x(i.find(u=>u.kind==="control.box")?.props),a=i.filter(u=>u.kind==="control.part"),s=Number(e.min??0),l=Number(e.max??1),f=m("div","cultui-slider");f.dataset.skin=g(e.skin,"default"),f.dataset.bind=g(e.bind,""),vn(f,o);const c=m("div","cultui-slider-visual"),r=m("input","cultui-slider-input");r.type="range",r.min=String(s),r.max=String(l),r.step=String(e.step??.01),r.setAttribute("aria-label",g(e.bind,"slider"));for(const u of a){const h=x(u.props),y=m("span",`cultui-slider-part ${g(h.name,"part")}`);xn(y,h),c.append(y)}return a.some(u=>x(u.props).name==="track")||c.append(m("span","cultui-slider-part track")),a.some(u=>x(u.props).name==="fill")||c.append(m("span","cultui-slider-part fill")),a.some(u=>x(u.props).name==="thumb")||c.append(m("span","cultui-slider-part thumb")),f.append(c,r),(u=>{const h=Number(u??s),y=Math.max(s,Math.min(l,Number.isFinite(h)?h:s)),b=l===s?0:(y-s)/(l-s)*100;r.value=String(y),f.style.setProperty("--cultui-slider-value",`${b}%`),f.dataset.value=String(y)})(e.value??s),r.addEventListener("input",()=>{const u=ne(g(e.command,"control.slider.input"),{...e,action:{...x(e.action),value:Number(r.value)},value:Number(r.value)},n);n.commandSink?.(u,{kind:"control.slider",props:e})}),f}function gn(e,t){const n=typeof e.skin=="string"?e.skin:"",o=[...n?Je.controlSkins[n]?.children||[]:[],...t];return o.length?o:[{kind:"control.box",props:{height:18,overflow:"visible"}},{kind:"control.part",props:{name:"track",anchor:"center",size:["100%",6],radius:2,fill:"color.panelInset"}},{kind:"control.part",props:{name:"fill",anchor:["left","center"],size:["value%",6],radius:2,fill:"color.accent"}},{kind:"control.part",props:{name:"thumb",anchor:["value","center"],size:[12,12],bleed:3,radius:999,fill:"color.accent"}}]}function Te(e){if(Array.isArray(e)){const t={},n={};for(const i of e)i?.name&&(i.name==="font.web.google"&&typeof i.value=="string"?n.fontCss=i.value:t[i.name]=i.value);return{tokens:t,assets:n,controlSkins:{}}}return{tokens:x(e?.tokens),assets:x(e?.assets),controlSkins:x(e?.controlSkins)}}function ee(e){if(!e)return"";const t=W.assetUrlResolver;return t?t(e,te):/^(https?:|data:|blob:)/i.test(e)?e:W.assetBaseUrl&&e.startsWith("/")?`${W.assetBaseUrl.replace(/\/+$/,"")}${e}`:e}function yn(e){const t=typeof e=="string"?e.trim():"";if(oe?.dataset.href===t||(oe?.remove(),oe=void 0,!t))return;const n=document.createElement("link");n.rel="preconnect",n.href="https://fonts.gstatic.com",n.crossOrigin="anonymous",document.head.append(n);const i=document.createElement("link");i.rel="stylesheet",i.href=t,i.dataset.href=t,document.head.append(i),oe=i}function bn(e,t,n){const i=[];return(e==="text.title"||t.role==="title")&&i.push("cultui-title","eve-text-title"),e==="text.subtitle"&&i.push("eve-text-subtitle"),e==="label"&&i.push("cultui-label"),t.role==="mono"&&i.push("detail","mono"),i.length===0&&i.push("detail","eve-text"),i.join(" ")}function vn(e,t){t.height!==void 0&&(e.style.minHeight=R(t.height)),t.width!==void 0&&(e.style.width=R(t.width)),typeof t.overflow=="string"&&(e.style.overflow=t.overflow)}function xn(e,t){const n=Array.isArray(t.size)?t.size:[t.size,void 0];n[0]!==void 0&&n[0]!=="value%"&&(e.style.width=R(n[0])),n[1]!==void 0&&(e.style.height=R(n[1])),t.radius!==void 0&&(e.style.borderRadius=R(t.radius)),t.fill!==void 0&&(e.style.background=de(String(t.fill))),t.bleed!==void 0&&e.style.setProperty("--part-bleed",R(t.bleed)),(Array.isArray(t.anchor)?t.anchor:[t.anchor]).includes("value")&&(e.dataset.anchorValue="true")}function ce(e,t){t.gap!==void 0&&(e.style.gap=R(t.gap)),t.padding!==void 0&&(e.style.padding=R(t.padding)),t.size!==void 0&&(e.style.flex=wn(t.size)),t.min!==void 0&&(e.style.minWidth=R(t.min)),t.max!==void 0&&(e.style.maxWidth=R(t.max)),t.align!==void 0&&(e.style.alignItems=t.align==="end"?"flex-end":String(t.align)),(t.clip==="true"||t.clip===!0)&&(e.style.overflow="hidden"),t.scroll==="y"&&(e.style.overflowY="auto"),t.scroll==="x"&&(e.style.overflowX="auto")}function wn(e){return typeof e=="string"&&e.endsWith("fr")?`${Number(e.slice(0,-2))||1} 1 0`:e==="auto"||e==="content"?"0 0 auto":`0 0 ${R(e)}`}function R(e){return Array.isArray(e)?e.map(R).join(" "):typeof e=="number"?`${e}px`:typeof e=="string"&&/^-?\d+(\.\d+)?$/.test(e)?`${e}px`:String(e??"")}function de(e){return{"color.accent":"var(--accent)","color.panelInset":"rgba(0, 0, 0, 0.34)","color.panel":"var(--panel)","color.text":"var(--text)"}[e]||e}function se(e){return e.includes("Zen Kaku Gothic New")||e.includes("M PLUS 1")?e:e.includes("Montserrat")?`${e}, "Zen Kaku Gothic New", "M PLUS 1", "Noto Sans JP", "Hiragino Sans", "Yu Gothic", sans-serif`:e.includes("Ubuntu")?`${e}, "M PLUS 1", "Noto Sans JP", "Hiragino Sans", "Yu Gothic", sans-serif`:`${e}, "Noto Sans JP", "Hiragino Sans", "Yu Gothic", sans-serif`}function w(e,t){t.id&&(e.id=t.id),t.id&&(e.dataset.eveNodeId=t.id),t.kind&&(e.dataset.eveKind=t.kind)}function x(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}function Sn(e){return Array.isArray(e)?e:[]}function En(e){return Sn(e).map((t,n)=>et({kind:"list.item",props:x(t)}))}function g(e,t){return e==null?t:String(e)}function p(...e){for(const t of e){if(typeof t=="string"&&t.trim())return t;if(typeof t=="number"&&Number.isFinite(t))return String(t)}return""}function Re(e){return typeof e=="boolean"?e:typeof e=="number"?e!==0:typeof e=="string"?["1","true","yes","on"].includes(e.trim().toLowerCase()):!1}function D(e,t){const n=Number(e);return Number.isFinite(n)?Math.max(0,Math.floor(n)):t}function In(e){const t=e.toLowerCase();return t.includes("sensor")?"S":t.includes("thermal")?"T":t.includes("fuel")?"F":t.includes("cargo")?"C":t.includes("weapon")?"W":e.trim().slice(0,1).toUpperCase()||"?"}function An(e){const t=e.trim().split(/\s+/).filter(Boolean);return t.length&&t.slice(0,2).map(n=>n[0]?.toUpperCase()||"").join("")||"?"}function Ke(e){const t=Number(e);return Number.isFinite(t)?Math.max(0,Math.min(100,t)):0}function m(e,t="",n=""){const i=document.createElement(e);return t&&(i.className=t),n&&(i.textContent=n),i}const Ge=document.querySelector("#surface"),B=document.querySelector("#status"),pe=document.querySelector("#receipt"),_n=document.querySelector("#compiler"),Ee=document.querySelector("#composer"),ue=document.querySelector("#destination-form"),Ie=document.querySelector("#compiler-results"),Cn=document.querySelector("#campaign-lab"),je=document.querySelector("#campaign-list");let ke=0;async function O(){const e=await fetch("/api/surface");if(e.status===401){B.textContent="This laboratory requires an unused invite link.";return}const t=await e.json(),n=t.surface_id==="ghostlight.compiler";if(_n.hidden=!n,Ee.hidden=n,ue.hidden=n,Ge.hidden=n,Cn.hidden=n,n){B.textContent="No campaign exists. Retrieve the Vault and approve a world seed.";return}ke=Number(t.version??0),Ze(t,Ge,{body:document.body,clientId:"ghostlight.browser",statusElement:B});const i=await fetch("/api/campaigns").then(o=>o.json());je.innerHTML=i.campaigns.map(o=>``).join(""),je.querySelectorAll("button:not(:disabled)").forEach(o=>o.addEventListener("click",async()=>{await $(`/api/campaigns/select/${o.dataset.campaignId}`,{}),await O()}))}async function $(e,t){B.textContent="Retrieving evidence and compiling…";const n=await fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)}),i=n.headers.get("content-type")?.includes("json")?await n.json():await n.text();if(!n.ok)throw B.textContent=typeof i=="string"?i:"Compilation failed",new Error(B.textContent);return i}function Xe(e,t,n){Ie.innerHTML=`
${e.map((i,o)=>`

${i.title??i.name}

${i.player_hook??i.premise}

${i.era?`${i.era} · ${i.place} · ${i.pressure}`:""}

`).join("")}
`,Ie.querySelectorAll("button").forEach(i=>i.addEventListener("click",()=>n(e[Number(i.dataset.index)])))}document.querySelector("#suggest-form").addEventListener("submit",async e=>{e.preventDefault();const t=await $("/api/compiler/openings",{setting:document.querySelector("#setting").value,constraints:[]});Xe(t.openings,"Choose opening",async n=>{const i=await $("/api/compiler/roles",n);Xe(i.roles,"Choose role",async o=>rt(await $("/api/compiler/selected",{campaign_name:n.title,opening:n,role:o})))})});document.querySelector("#custom-form").addEventListener("submit",async e=>{e.preventDefault(),await rt(await $("/api/compiler/custom",Object.fromEntries(new FormData(e.currentTarget))))});async function rt(e){const t=e.preview;Ie.innerHTML=`

${t.title}

${t.campaign.transcript?.[0]?.text??""}

${Object.keys(t.campaign.locations).length} locations · ${Object.keys(t.campaign.actors).length} actors · ${Object.keys(t.campaign.institutions).length} institutions

${t.gaps.length?`Material gaps: ${t.gaps.join("; ")}`:"No declared material gaps."}

Branch assumptions: ${t.branch_assumptions.join("; ")||"none"}

`,document.querySelector("#approve-world").addEventListener("click",async()=>{await $(`/api/compiler/approve/${e.preview_id}`,{}),await O()}),B.textContent="Preview compiled. Nothing has entered world state yet."}async function Ae(e){B.textContent="The world is considering the command…";const t=await fetch("/api/command",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)}),n=await t.json();return pe.hidden=!1,pe.textContent=JSON.stringify(n,null,2),t.ok?(await O(),n):(B.textContent=n.error??"The command was refused.",await O(),n)}Ee.addEventListener("submit",async e=>{e.preventDefault();const t=document.querySelector("#attempt").value.trim();if(!t)return;const n=await Ae({type:"assess",expected_revision:ke,intent:{actor_id:"player",description:t,intended_effect:t}});if(n?.kind==="assessed"){const i=document.createElement("button");i.textContent=n.assessment.admissible?"Confirm server roll":"Revise attempt",i.addEventListener("click",async()=>{n.assessment.admissible?(await Ae({type:"attempt",assessment_digest:n.assessment.digest}),i.remove()):document.querySelector("#attempt").focus()}),Ee.append(i)}});document.querySelector("#wait").addEventListener("click",()=>{Ae({type:"wait",expected_revision:ke,minutes:60})});ue.addEventListener("submit",async e=>{e.preventDefault();const t=await $("/api/compiler/destination",Object.fromEntries(new FormData(ue))),n=t.preview;pe.hidden=!1,pe.textContent=JSON.stringify(n,null,2),B.textContent="Destination preview compiled; topology is unchanged until approval.";const i=document.createElement("button");i.textContent="Approve destination",i.addEventListener("click",async()=>{await $(`/api/compiler/destination/approve/${t.preview_id}`,{}),i.remove(),await O()}),ue.append(i)});document.querySelector("#fork-form").addEventListener("submit",async e=>{e.preventDefault(),await $("/api/campaigns/fork",Object.fromEntries(new FormData(e.currentTarget))),await O()});document.querySelector("#reset-form").addEventListener("submit",async e=>{e.preventDefault(),await $("/api/campaigns/reset",Object.fromEntries(new FormData(e.currentTarget))),await O()});document.querySelector("#load-operator").addEventListener("click",async()=>{const t=await(await fetch("/api/operator")).json();Ze(t,document.querySelector("#operator-output"),{body:document.body,clientId:"ghostlight.operator",statusElement:B})});O(); diff --git a/web/dist/index.html b/web/dist/index.html index e72c237..b07cec7 100644 --- a/web/dist/index.html +++ b/web/dist/index.html @@ -1,5 +1,5 @@ -GhostlightDungeon +GhostlightDungeon

STARFIRE / PRIVATE VERSE

GhostlightDungeon

Reading the world…

@@ -9,5 +9,5 @@
- +
diff --git a/web/index.html b/web/index.html index 82aaabb..c4dcfe0 100644 --- a/web/index.html +++ b/web/index.html @@ -7,5 +7,5 @@
- + diff --git a/web/src/main.ts b/web/src/main.ts index 16a10cb..b01078d 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -64,5 +64,5 @@ document.querySelector("#wait")!.addEventListener("click", () destinationForm.addEventListener("submit",async event=>{event.preventDefault();const result=await compilerPost("/api/compiler/destination",Object.fromEntries(new FormData(destinationForm)));const preview=result.preview;receipt.hidden=false;receipt.textContent=JSON.stringify(preview,null,2);status.textContent="Destination preview compiled; topology is unchanged until approval.";const button=document.createElement("button");button.textContent="Approve destination";button.addEventListener("click",async()=>{await compilerPost(`/api/compiler/destination/approve/${result.preview_id}`,{});button.remove();await refresh();});destinationForm.append(button);}); document.querySelector("#fork-form")!.addEventListener("submit",async event=>{event.preventDefault();await compilerPost("/api/campaigns/fork",Object.fromEntries(new FormData(event.currentTarget as HTMLFormElement)));await refresh();}); document.querySelector("#reset-form")!.addEventListener("submit",async event=>{event.preventDefault();await compilerPost("/api/campaigns/reset",Object.fromEntries(new FormData(event.currentTarget as HTMLFormElement)));await refresh();}); -document.querySelector("#load-operator")!.addEventListener("click",async()=>{const response=await fetch("/api/operator");document.querySelector("#operator-output")!.textContent=JSON.stringify(await response.json(),null,2);}); +document.querySelector("#load-operator")!.addEventListener("click",async()=>{const response=await fetch("/api/operator");const surface=await response.json();renderEveSurface(surface,document.querySelector("#operator-output")!,{body:document.body,clientId:"ghostlight.operator",statusElement:status});}); void refresh();