package com.shire.mobile import android.content.Intent import android.os.Bundle import android.provider.Settings import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.RepeatMode import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.infiniteRepeatable import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.tween import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.RadioButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.darkColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.coroutines.delay private val Background = Color(0xFF090B10) private val Surface = Color(0xFF11151D) private val Raised = Color(0xFF181D28) private val RaisedSoft = Color(0xFF202634) private val Purple = Color(0xFFA987FF) private val Blue = Color(0xFF73D7FF) private val Green = Color(0xFF75E6A4) private val Amber = Color(0xFFFFC766) private val Red = Color(0xFFFF7D86) private val TextPrimary = Color(0xFFF2F3F7) private val TextMuted = Color(0xFF9EA6B5) private enum class Screen { Home, Academy } private enum class ShireExpression { Normal, Happy, Thinking, Sentinel, Concerned } private enum class ShireMode { Calm, Academy, Sentinel, Forge } private data class Skill( val id: String, val name: String, val category: String, val stage: String, val progress: Float, val state: String, val evidence: Int, val result: String, val forge: String ) private data class ApprovalItem( val id: Int, val title: String, val stage: String, val purpose: String, val affects: String, val risk: String, val estimate: String, val rollback: String, val status: String = "Pending" ) class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // SHIRE-MOBILE-APPROVAL-INBOX-0001 ApprovalNotifications.ensureChannel(this) ApprovalPollJobService.schedule(this) ApprovalNotifications.checkNow(this) setContent { MaterialTheme( colorScheme = darkColorScheme( primary = Purple, secondary = Blue, background = Background, surface = Surface, onPrimary = Color.Black, onBackground = TextPrimary, onSurface = TextPrimary ) ) { ShireMobileApp() } } } } @Composable private fun ShireMobileApp() { val context = LocalContext.current val systemAnimationsEnabled = remember { Settings.Global.getFloat( context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f ) > 0f } var personalityAnimations by remember { mutableStateOf(systemAnimationsEnabled) } var screen by remember { mutableStateOf(Screen.Home) } var mode by remember { mutableStateOf(ShireMode.Calm) } var expression by remember { mutableStateOf(ShireExpression.Normal) } var showLearnPrint by remember { mutableStateOf(false) } var showApprovals by remember { mutableStateOf(false) } var message by remember { mutableStateOf("Systems ready, Chief. What are we building today?") } val handleModeChange: (ShireMode) -> Unit = { selected -> mode = selected when (selected) { ShireMode.Calm -> { screen = Screen.Home expression = ShireExpression.Normal message = "Calm command mode ready, Chief." } ShireMode.Academy -> { screen = Screen.Academy expression = ShireExpression.Thinking message = "Academy mode ready. Let us review what I have learned." } ShireMode.Sentinel -> { screen = Screen.Home expression = ShireExpression.Sentinel message = "Sentinel mode active. Security systems have priority." } ShireMode.Forge -> { screen = Screen.Home expression = ShireExpression.Happy message = "Forge mode ready. Let us build something remarkable." } } } var approvals by remember { mutableStateOf(emptyList()) } BoxWithConstraints( modifier = Modifier .fillMaxSize() .background(Background) ) { val expanded = maxWidth >= 700.dp Scaffold(containerColor = Background) { paddingValues -> if (expanded) { FoldCommandCentre( modifier = Modifier.padding(paddingValues), screen = screen, mode = mode, expression = expression, message = message, approvals = approvals, personalityAnimations = personalityAnimations, onPersonalityAnimationsChange = { personalityAnimations = it }, onScreenChange = { screen = it }, onModeChange = handleModeChange, onExpressionChange = { expression = it }, onMessageChange = { message = it }, onLearnPrint = { expression = ShireExpression.Thinking showLearnPrint = true }, onApprovals = { expression = ShireExpression.Thinking context.startActivity( Intent( context, ApprovalInboxActivity::class.java ) ) }, onAddRecommendation = { approvals = approvals + recommendedApproval(approvals) expression = ShireExpression.Happy message = "Learning recommendation added for approval." } ) } else { FoldedCompanion( modifier = Modifier.padding(paddingValues), screen = screen, mode = mode, expression = expression, message = message, approvals = approvals, personalityAnimations = personalityAnimations, onScreenChange = { screen = it }, onModeChange = handleModeChange, onExpressionChange = { expression = it }, onMessageChange = { message = it }, onLearnPrint = { expression = ShireExpression.Thinking showLearnPrint = true }, onApprovals = { expression = ShireExpression.Thinking context.startActivity( Intent( context, ApprovalInboxActivity::class.java ) ) }, onAddRecommendation = { approvals = approvals + recommendedApproval(approvals) expression = ShireExpression.Happy message = "Learning recommendation added for approval." } ) } } } if (showLearnPrint) { LearnAndPrintDialog( onDismiss = { showLearnPrint = false expression = ShireExpression.Normal }, onCreatePlan = { request, mode -> approvals = approvals + ApprovalItem( id = (approvals.maxOfOrNull { it.id } ?: 0) + 1, title = request.take(55), stage = "Approve Learning", purpose = request, affects = "Creates a Safe Plan only. No learning, design, slicing or printing starts.", risk = "Awaiting safety, licensing and feasibility analysis.", estimate = mode, rollback = "Fully cancellable before staged approval." ) showLearnPrint = false expression = ShireExpression.Happy message = "Safe Plan added to the Approval Centre." } ) } if (showApprovals) { ApprovalCentreDialog( approvals = approvals, onDismiss = { showApprovals = false expression = ShireExpression.Normal }, onDecision = { id, decision -> approvals = approvals.map { if (it.id == id) it.copy(status = decision) else it } expression = when (decision) { "Approved" -> ShireExpression.Happy "Rejected" -> ShireExpression.Concerned "Changes Requested" -> ShireExpression.Thinking else -> ShireExpression.Normal } message = when (decision) { "Approved" -> "Only that exact stage was approved." "Rejected" -> "Plan rejected. No action will be taken." "Deferred" -> "Plan deferred and remains inactive." "Changes Requested" -> "A revised plan is now required." else -> "Approval updated." } } ) } } private fun recommendedApproval( approvals: List ): ApprovalItem { return ApprovalItem( id = (approvals.maxOfOrNull { it.id } ?: 0) + 1, title = "Parametric phone support geometry", stage = "Approve Learning", purpose = "Learn reusable phone-fit and support geometry for DockForge and Forge.", affects = "Adds an Academy Safe Plan only.", risk = "Low. No certification or Forge-permission changes.", estimate = "45–90 minutes.", rollback = "Can be rejected, deferred or cancelled." ) } @Composable private fun FoldedCompanion( modifier: Modifier, screen: Screen, mode: ShireMode, expression: ShireExpression, message: String, approvals: List, personalityAnimations: Boolean, onScreenChange: (Screen) -> Unit, onModeChange: (ShireMode) -> Unit, onExpressionChange: (ShireExpression) -> Unit, onMessageChange: (String) -> Unit, onLearnPrint: () -> Unit, onApprovals: () -> Unit, onAddRecommendation: () -> Unit ) { Column( modifier = modifier .fillMaxSize() .padding(horizontal = 17.dp, vertical = 13.dp), horizontalAlignment = Alignment.CenterHorizontally ) { AppHeader(compact = true) Spacer(modifier = Modifier.height(9.dp)) ModeSelector( selected = mode, onSelect = onModeChange ) Spacer(modifier = Modifier.height(10.dp)) Box( modifier = Modifier .fillMaxWidth() .weight(1f), contentAlignment = Alignment.TopCenter ) { when (screen) { Screen.Home -> { Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally ) { RealShireFace( expression = expression, expanded = false, personalityAnimations = personalityAnimations ) Spacer(modifier = Modifier.height(11.dp)) Text( text = message, textAlign = TextAlign.Center, fontSize = 16.sp, lineHeight = 22.sp ) Spacer(modifier = Modifier.height(14.dp)) CompactStatus( pendingApprovals = approvals.count { it.status == "Pending" } ) Spacer(modifier = Modifier.height(12.dp)) MainActionPanel( pendingApprovals = approvals.count { it.status == "Pending" }, onLearnPrint = onLearnPrint, onApprovals = onApprovals, onAcademy = { onScreenChange(Screen.Academy) }, onShire = { onExpressionChange( ShireExpression.Normal ) onMessageChange( "Local SHiRE mode selected." ) }, onChatGpt = { onExpressionChange( ShireExpression.Thinking ) onMessageChange( "ChatGPT mode selected." ) }, onBoth = { onExpressionChange( ShireExpression.Happy ) onMessageChange( "SHiRE and ChatGPT collaboration selected." ) } ) Spacer(modifier = Modifier.height(14.dp)) CurrentLearningCard() Spacer(modifier = Modifier.height(12.dp)) } } Screen.Academy -> { MiniAcademy( expanded = false, onBack = { onScreenChange(Screen.Home) }, onApprovals = onApprovals, onAddRecommendation = onAddRecommendation ) } } } } } @Composable private fun FoldCommandCentre( modifier: Modifier, screen: Screen, mode: ShireMode, expression: ShireExpression, message: String, approvals: List, personalityAnimations: Boolean, onPersonalityAnimationsChange: (Boolean) -> Unit, onScreenChange: (Screen) -> Unit, onModeChange: (ShireMode) -> Unit, onExpressionChange: (ShireExpression) -> Unit, onMessageChange: (String) -> Unit, onLearnPrint: () -> Unit, onApprovals: () -> Unit, onAddRecommendation: () -> Unit ) { Row( modifier = modifier .fillMaxSize() .padding(18.dp), horizontalArrangement = Arrangement.spacedBy(16.dp) ) { Column( modifier = Modifier .weight(0.92f) .fillMaxHeight() .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally ) { AppHeader(compact = false) Spacer(modifier = Modifier.height(9.dp)) ModeSelector( selected = mode, onSelect = onModeChange ) Spacer(modifier = Modifier.height(8.dp)) RealShireFace( expression = expression, expanded = true, personalityAnimations = personalityAnimations ) Spacer(modifier = Modifier.height(10.dp)) Text( text = message, modifier = Modifier.widthIn(max = 460.dp), textAlign = TextAlign.Center, fontSize = 18.sp, lineHeight = 24.sp ) Spacer(modifier = Modifier.height(13.dp)) CurrentMissionCard() Spacer(modifier = Modifier.height(12.dp)) MainActionPanel( pendingApprovals = approvals.count { it.status == "Pending" }, onLearnPrint = onLearnPrint, onApprovals = onApprovals, onAcademy = { onScreenChange(Screen.Academy) }, onShire = { onExpressionChange(ShireExpression.Normal) onMessageChange("Local SHiRE mode selected.") }, onChatGpt = { onExpressionChange(ShireExpression.Thinking) onMessageChange("ChatGPT mode selected.") }, onBoth = { onExpressionChange(ShireExpression.Happy) onMessageChange("SHiRE and ChatGPT collaboration selected.") } ) Spacer(modifier = Modifier.height(12.dp)) PersonalityAnimationControl( enabled = personalityAnimations, onChange = onPersonalityAnimationsChange ) } Card( modifier = Modifier .weight(1.35f) .fillMaxHeight(), colors = CardDefaults.cardColors(containerColor = Surface), shape = RoundedCornerShape(24.dp) ) { when (screen) { Screen.Home -> { CommandCentreOverview( mode = mode, approvals = approvals, onAcademy = { onScreenChange(Screen.Academy) }, onApprovals = onApprovals, onAddRecommendation = onAddRecommendation ) } Screen.Academy -> { MiniAcademy( expanded = true, onBack = { onScreenChange(Screen.Home) }, onApprovals = onApprovals, onAddRecommendation = onAddRecommendation ) } } } } } @Composable private fun ModeSelector( selected: ShireMode, onSelect: (ShireMode) -> Unit ) { Card( modifier = Modifier .fillMaxWidth() .widthIn(max = 650.dp), colors = CardDefaults.cardColors(containerColor = Surface), shape = RoundedCornerShape(18.dp) ) { FlowRow( modifier = Modifier.padding(9.dp), horizontalArrangement = Arrangement.spacedBy(7.dp), verticalArrangement = Arrangement.spacedBy(7.dp) ) { ShireMode.entries.forEach { mode -> FilterChip( selected = selected == mode, onClick = { onSelect(mode) }, label = { Text( when (mode) { ShireMode.Calm -> "Calm" ShireMode.Academy -> "Academy" ShireMode.Sentinel -> "Sentinel" ShireMode.Forge -> "Forge" } ) } ) } } } } @Composable private fun ModeWorkspace(mode: ShireMode) { val title: String val subtitle: String val status: String val accent: Color val details: List> when (mode) { ShireMode.Calm -> { title = "Calm Command" subtitle = "Daily assistance, conversation and mission control." status = "Ready" accent = Green details = listOf( "Conversation" to "Local SHiRE ready", "Current mission" to "Mobile command expansion", "Private routing" to "Tailscale only" ) } ShireMode.Academy -> { title = "Academy" subtitle = "Learning, evidence, curriculum and recommendations." status = "Learning" accent = Blue details = listOf( "Current skill" to "Boolean Operations", "Progress" to "72%", "Protected skills" to "371" ) } ShireMode.Sentinel -> { title = "Sentinel" subtitle = "Security posture, network alerts and protected actions." status = "Protected" accent = Red details = listOf( "Threat posture" to "Monitoring", "Network control" to "Approval required", "Public ports" to "None" ) } ShireMode.Forge -> { title = "Forge" subtitle = "Design, modelling, slicing and printer readiness." status = "Ready" accent = Purple details = listOf( "Design engine" to "CadQuery available", "Print actions" to "Approval locked", "Default material" to "PLA" ) } } Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(20.dp) ) { Column(modifier = Modifier.padding(17.dp)) { Row(verticalAlignment = Alignment.CenterVertically) { Column(modifier = Modifier.weight(1f)) { SectionLabel("ACTIVE SHIRE MODE") Text( text = title, fontSize = 21.sp, fontWeight = FontWeight.Black ) } Text( text = status, color = accent, fontWeight = FontWeight.Bold ) } Text( text = subtitle, color = TextMuted, lineHeight = 19.sp ) Spacer(modifier = Modifier.height(11.dp)) details.forEach { (label, value) -> Row(modifier = Modifier.padding(vertical = 3.dp)) { Text( text = label, modifier = Modifier.weight(1f), color = TextMuted ) Text( text = value, color = accent, fontWeight = FontWeight.SemiBold ) } } } } } @Composable private fun AppHeader(compact: Boolean) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Text( text = "SHiRE", fontSize = if (compact) 27.sp else 31.sp, fontWeight = FontWeight.Black, letterSpacing = 5.sp ) Text( text = if (compact) "MOBILE COMMAND" else "FOLD COMMAND CENTRE", color = TextMuted, fontSize = 10.sp, letterSpacing = 2.sp ) } } @Composable private fun RealShireFace( expression: ShireExpression, expanded: Boolean, personalityAnimations: Boolean ) { val transition = rememberInfiniteTransition(label = "shire-idle") val breathing by transition.animateFloat( initialValue = if (personalityAnimations) 0.988f else 1f, targetValue = if (personalityAnimations) 1.014f else 1f, animationSpec = infiniteRepeatable( animation = tween(2400), repeatMode = RepeatMode.Reverse ), label = "breathing" ) val floating by transition.animateFloat( initialValue = if (personalityAnimations) -2f else 0f, targetValue = if (personalityAnimations) 3f else 0f, animationSpec = infiniteRepeatable( animation = tween(3000), repeatMode = RepeatMode.Reverse ), label = "floating" ) val stretchX = remember { Animatable(1f) } val stretchY = remember { Animatable(1f) } var greetingVisible by remember { mutableStateOf(false) } LaunchedEffect(expanded, personalityAnimations) { if (expanded && personalityAnimations) { greetingVisible = true stretchX.snapTo(0.92f) stretchY.snapTo(1.05f) stretchX.animateTo( targetValue = 1.10f, animationSpec = tween(320) ) stretchY.animateTo( targetValue = 0.94f, animationSpec = tween(320) ) stretchX.animateTo( targetValue = 1f, animationSpec = tween(350) ) stretchY.animateTo( targetValue = 1f, animationSpec = tween(350) ) delay(1100) greetingVisible = false } else { stretchX.snapTo(1f) stretchY.snapTo(1f) greetingVisible = false } } val drawable = when (expression) { ShireExpression.Normal -> R.drawable.shire_face_main ShireExpression.Happy -> R.drawable.shire_face_happy ShireExpression.Thinking -> R.drawable.shire_face_thinking ShireExpression.Sentinel -> R.drawable.shire_face_sentinel ShireExpression.Concerned -> R.drawable.shire_face_scared } Column(horizontalAlignment = Alignment.CenterHorizontally) { Card( modifier = Modifier .size(if (expanded) 330.dp else 238.dp) .offset(y = floating.dp) .graphicsLayer { scaleX = breathing * stretchX.value scaleY = breathing * stretchY.value }, shape = CircleShape, colors = CardDefaults.cardColors(containerColor = Raised), elevation = CardDefaults.cardElevation(defaultElevation = 12.dp) ) { AnimatedContent( targetState = drawable, label = "shire-expression" ) { face -> Image( painter = painterResource(face), contentDescription = "SHiRE", modifier = Modifier .fillMaxSize() .padding(4.dp) .clip(CircleShape), contentScale = ContentScale.Crop ) } } AnimatedVisibility(visible = greetingVisible) { Text( text = "Ahh… much better, Chief.", modifier = Modifier.padding(top = 8.dp), color = Green, fontWeight = FontWeight.SemiBold ) } } } @Composable private fun CompactStatus(pendingApprovals: Int) { FlowRow( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(7.dp), verticalArrangement = Arrangement.spacedBy(7.dp) ) { StatusChip("ACADEMY", "Running", Green) StatusChip("APPROVALS", "Open inbox", Amber) StatusChip("SENTINEL", "Protected", Green) } } @Composable private fun StatusChip( title: String, value: String, valueColor: Color ) { Card( colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(13.dp) ) { Column( modifier = Modifier.padding( horizontal = 12.dp, vertical = 9.dp ) ) { Text( text = title, color = TextMuted, fontSize = 9.sp, fontWeight = FontWeight.Bold ) Text( text = value, color = valueColor, fontSize = 13.sp, fontWeight = FontWeight.SemiBold ) } } } @Composable private fun MainActionPanel( pendingApprovals: Int, onLearnPrint: () -> Unit, onApprovals: () -> Unit, onAcademy: () -> Unit, onShire: () -> Unit, onChatGpt: () -> Unit, onBoth: () -> Unit ) { Card( modifier = Modifier .fillMaxWidth() .widthIn(max = 650.dp), colors = CardDefaults.cardColors(containerColor = Surface), shape = RoundedCornerShape(20.dp) ) { Column( modifier = Modifier.padding(14.dp), verticalArrangement = Arrangement.spacedBy(9.dp) ) { ShireConversationCard() Button( onClick = onLearnPrint, modifier = Modifier .fillMaxWidth() .height(52.dp), colors = ButtonDefaults.buttonColors( containerColor = Purple, contentColor = Color.Black ) ) { Text("LEARN & PRINT", fontWeight = FontWeight.Black) } Row( horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Button( onClick = onAcademy, modifier = Modifier.weight(1f), colors = ButtonDefaults.buttonColors( containerColor = Blue, contentColor = Color.Black ) ) { Text("ACADEMY", fontWeight = FontWeight.Bold) } Button( onClick = onApprovals, modifier = Modifier.weight(1f), colors = ButtonDefaults.buttonColors( containerColor = Amber, contentColor = Color.Black ) ) { Text("APPROVALS") } } Row(horizontalArrangement = Arrangement.spacedBy(7.dp)) { val context = LocalContext.current OutlinedButton( onClick = { context.startActivity( Intent(context, SentinelActivity::class.java) ) }, modifier = Modifier.weight(1f) ) { Text("SENTINEL") } OutlinedButton( onClick = { context.startActivity( Intent(context, NfcGuardActivity::class.java) ) }, modifier = Modifier.weight(1f) ) { Text("NFC") } OutlinedButton( onClick = { context.startActivity( Intent(context, VoiceLabActivity::class.java) ) }, modifier = Modifier.weight(1f) ) { Text("VOICE") } } Row( horizontalArrangement = Arrangement.spacedBy(8.dp) ) { val context = LocalContext.current Button( onClick = { context.startActivity( Intent( context, LimbForgeMeasurementActivity::class.java ) ) }, modifier = Modifier.weight(1f), colors = ButtonDefaults.buttonColors( containerColor = Green, contentColor = Color.Black ) ) { Text( "LIMBFORGE", fontWeight = FontWeight.Black ) } Button( onClick = { context.startActivity( Intent( context, AgentPortalActivity::class.java ).apply { putExtra( AgentPortalActivity.EXTRA_AGENT_TITLE, "CoverCanvas" ) putExtra( AgentPortalActivity.EXTRA_AGENT_URL, BuildConfig.COVERCANVAS_BASE_URL ) } ) }, modifier = Modifier.weight(1f), colors = ButtonDefaults.buttonColors( containerColor = Blue, contentColor = Color.Black ) ) { Text( "COVER CANVAS", fontWeight = FontWeight.Black ) } } Row(horizontalArrangement = Arrangement.spacedBy(7.dp)) { OutlinedButton( onClick = onShire, modifier = Modifier.weight(1f) ) { Text("SHiRE") } OutlinedButton( onClick = onChatGpt, modifier = Modifier.weight(1f) ) { Text("ChatGPT") } OutlinedButton( onClick = onBoth, modifier = Modifier.weight(1f) ) { Text("Both") } } } } } @Composable private fun CurrentMissionCard() { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = Surface), shape = RoundedCornerShape(18.dp) ) { Column(modifier = Modifier.padding(14.dp)) { SectionLabel("CURRENT MISSION") Text( text = "Expand SHiRE Mobile capability", fontWeight = FontWeight.Bold, fontSize = 17.sp ) Text( text = "Mini Academy and adaptive Fold command centre", color = TextMuted, fontSize = 13.sp ) Spacer(modifier = Modifier.height(8.dp)) LinearProgressIndicator( progress = { 0.72f }, modifier = Modifier.fillMaxWidth() ) } } } @Composable private fun CurrentLearningCard() { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = Surface), shape = RoundedCornerShape(18.dp) ) { Column(modifier = Modifier.padding(15.dp)) { SectionLabel("CURRENT LEARNING") Text( text = "Boolean Operations", fontWeight = FontWeight.Bold, fontSize = 17.sp ) Text( text = "Geometry Foundations • Stage 3", color = TextMuted ) Spacer(modifier = Modifier.height(8.dp)) LinearProgressIndicator( progress = { 0.72f }, modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(6.dp)) Text( text = "72% • Read-only mobile view", color = Green, fontSize = 12.sp ) } } } @Composable private fun PersonalityAnimationControl( enabled: Boolean, onChange: (Boolean) -> Unit ) { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = Surface), shape = RoundedCornerShape(17.dp) ) { Row( modifier = Modifier.padding(13.dp), verticalAlignment = Alignment.CenterVertically ) { Column(modifier = Modifier.weight(1f)) { Text( text = "Personality animations", fontWeight = FontWeight.SemiBold ) Text( text = "Breathing, floating and unfolding stretch", color = TextMuted, fontSize = 12.sp ) } Switch( checked = enabled, onCheckedChange = onChange ) } } } @Composable private fun CommandCentreOverview( mode: ShireMode, approvals: List, onAcademy: () -> Unit, onApprovals: () -> Unit, onAddRecommendation: () -> Unit ) { Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) .padding(18.dp) ) { Text( text = "COMMAND OVERVIEW", fontSize = 24.sp, fontWeight = FontWeight.Black ) Text( text = "SHiRE Mini • private Tailscale command workspace", color = TextMuted ) Spacer(modifier = Modifier.height(15.dp)) ModeWorkspace(mode) Spacer(modifier = Modifier.height(14.dp)) OverviewMetricRow( approvals = approvals.count { it.status == "Pending" } ) Spacer(modifier = Modifier.height(14.dp)) AcademySummaryCard(onOpen = onAcademy) Spacer(modifier = Modifier.height(12.dp)) ApprovalSummaryCard( approvals = approvals, onOpen = onApprovals ) Spacer(modifier = Modifier.height(12.dp)) RecommendationCard(onAdd = onAddRecommendation) Spacer(modifier = Modifier.height(12.dp)) SystemGuardrailsCard() } } @Composable private fun OverviewMetricRow(approvals: Int) { FlowRow( horizontalArrangement = Arrangement.spacedBy(9.dp), verticalArrangement = Arrangement.spacedBy(9.dp) ) { MetricCard("371", "Skills") MetricCard("72%", "Current skill") MetricCard("$approvals", "Approvals") MetricCard("SAFE", "Guardrails") } } @Composable private fun MetricCard(value: String, label: String) { Card( colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(15.dp) ) { Column( modifier = Modifier.padding( horizontal = 17.dp, vertical = 12.dp ) ) { Text( text = value, fontSize = 20.sp, fontWeight = FontWeight.Black, color = Purple ) Text( text = label, color = TextMuted, fontSize = 11.sp ) } } } @Composable private fun AcademySummaryCard(onOpen: () -> Unit) { Card( modifier = Modifier .fillMaxWidth() .clickable(onClick = onOpen), colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(18.dp) ) { Column(modifier = Modifier.padding(16.dp)) { SectionLabel("MINI ACADEMY") Text( text = "Boolean Operations", fontSize = 19.sp, fontWeight = FontWeight.Bold ) Text( text = "Current learning • Stage 3 • Running", color = Green ) Spacer(modifier = Modifier.height(9.dp)) LinearProgressIndicator( progress = { 0.72f }, modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(7.dp)) Text( text = "Tap to inspect curriculum, queue, evidence and guardrails.", color = TextMuted, fontSize = 12.sp ) } } } @Composable private fun ApprovalSummaryCard( approvals: List, onOpen: () -> Unit ) { Card( modifier = Modifier .fillMaxWidth() .clickable(onClick = onOpen), colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(18.dp) ) { Column(modifier = Modifier.padding(16.dp)) { SectionLabel("APPROVAL CENTRE") Text( text = "${approvals.count { it.status == "Pending" }} actions waiting", fontSize = 18.sp, fontWeight = FontWeight.Bold, color = Amber ) approvals .filter { it.status == "Pending" } .take(2) .forEach { Text( text = "• ${it.stage}: ${it.title}", color = TextMuted, fontSize = 13.sp ) } } } } @Composable private fun MiniAcademy( expanded: Boolean, onBack: () -> Unit, onApprovals: () -> Unit, onAddRecommendation: () -> Unit ) { val skills = remember { listOf( Skill( id = "boolean", name = "Boolean Operations", category = "Geometry Foundations", stage = "Stage 3", progress = 0.72f, state = "Learning", evidence = 22, result = "Practice active", forge = "Locked" ), Skill( id = "parameters", name = "Parameter Architecture", category = "Parametric CAD", stage = "Review", progress = 1f, state = "Learned", evidence = 18, result = "Forge allowed", forge = "Allowed" ), Skill( id = "supports", name = "Printable Support Geometry", category = "Manufacturing", stage = "Queued", progress = 0.16f, state = "Queued", evidence = 3, result = "Awaiting plan", forge = "Locked" ) ) } var selectedSkillId by remember { mutableStateOf(skills.first().id) } val selectedSkill = skills.first { it.id == selectedSkillId } Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) .padding(if (expanded) 19.dp else 0.dp) ) { Row(verticalAlignment = Alignment.CenterVertically) { if (!expanded) { TextButton(onClick = onBack) { Text("Back") } } Column(modifier = Modifier.weight(1f)) { Text( text = "MINI ACADEMY", fontSize = 25.sp, fontWeight = FontWeight.Black ) Text( text = "Running • protected read-only view", color = Green, fontSize = 12.sp ) } OutlinedButton(onClick = onApprovals) { Text("Approvals") } } Spacer(modifier = Modifier.height(14.dp)) AcademyHero() Spacer(modifier = Modifier.height(13.dp)) if (expanded) { Row( horizontalArrangement = Arrangement.spacedBy(12.dp) ) { Column(modifier = Modifier.weight(0.92f)) { SkillList( skills = skills, selectedSkillId = selectedSkillId, onSelect = { selectedSkillId = it } ) Spacer(modifier = Modifier.height(12.dp)) LearningQueueCard() } Column(modifier = Modifier.weight(1.08f)) { SkillDetail(selectedSkill) Spacer(modifier = Modifier.height(12.dp)) RecommendationCard(onAdd = onAddRecommendation) Spacer(modifier = Modifier.height(12.dp)) SystemGuardrailsCard() } } } else { SkillList( skills = skills, selectedSkillId = selectedSkillId, onSelect = { selectedSkillId = it } ) Spacer(modifier = Modifier.height(12.dp)) SkillDetail(selectedSkill) Spacer(modifier = Modifier.height(12.dp)) LearningQueueCard() Spacer(modifier = Modifier.height(12.dp)) RecommendationCard(onAdd = onAddRecommendation) Spacer(modifier = Modifier.height(12.dp)) SystemGuardrailsCard() } } } @Composable private fun AcademyHero() { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(20.dp) ) { Column(modifier = Modifier.padding(17.dp)) { SectionLabel("CURRENT LEARNING") Text( text = "Boolean Operations", fontSize = 21.sp, fontWeight = FontWeight.Black ) Text( text = "Geometry Foundations • Stage 3", color = TextMuted ) Spacer(modifier = Modifier.height(10.dp)) LinearProgressIndicator( progress = { 0.72f }, modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(7.dp)) Row { Text( text = "72% knowledge progress", color = Blue, modifier = Modifier.weight(1f) ) Text( text = "22 successful runs", color = Green ) } Spacer(modifier = Modifier.height(8.dp)) Text( text = "The protected 371-skill state remains unchanged. " + "No certification or Forge permissions can be altered here.", color = TextMuted, fontSize = 12.sp, lineHeight = 17.sp ) } } } @Composable private fun SkillList( skills: List, selectedSkillId: String, onSelect: (String) -> Unit ) { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(19.dp) ) { Column(modifier = Modifier.padding(14.dp)) { SectionLabel("SKILLS") skills.forEach { skill -> Card( modifier = Modifier .fillMaxWidth() .padding(vertical = 4.dp) .clickable { onSelect(skill.id) }, colors = CardDefaults.cardColors( containerColor = if (skill.id == selectedSkillId) { RaisedSoft } else { Surface } ), shape = RoundedCornerShape(14.dp) ) { Column(modifier = Modifier.padding(12.dp)) { Row { Column(modifier = Modifier.weight(1f)) { Text( text = skill.name, fontWeight = FontWeight.Bold ) Text( text = "${skill.category} • ${skill.stage}", color = TextMuted, fontSize = 11.sp ) } Text( text = skill.state, color = when (skill.state) { "Learned" -> Green "Learning" -> Blue else -> Amber }, fontSize = 12.sp ) } Spacer(modifier = Modifier.height(6.dp)) LinearProgressIndicator( progress = { skill.progress }, modifier = Modifier.fillMaxWidth() ) } } } } } } @Composable private fun SkillDetail(skill: Skill) { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(19.dp) ) { Column(modifier = Modifier.padding(16.dp)) { SectionLabel("SKILL DETAIL") Text( text = skill.name, fontSize = 20.sp, fontWeight = FontWeight.Black ) Text( text = "${skill.category} • ${skill.stage}", color = TextMuted ) Spacer(modifier = Modifier.height(11.dp)) DetailLine("State", skill.state) DetailLine("Progress", "${(skill.progress * 100).toInt()}%") DetailLine("Evidence records", skill.evidence.toString()) DetailLine("Current result", skill.result) DetailLine("Forge permission", skill.forge) Spacer(modifier = Modifier.height(10.dp)) Text( text = "Mobile Academy is read-only. Any change requires a Safe Plan and explicit approval.", color = Green, fontSize = 12.sp, lineHeight = 17.sp ) } } } @Composable private fun LearningQueueCard() { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(19.dp) ) { Column(modifier = Modifier.padding(16.dp)) { SectionLabel("LEARNING QUEUE") QueueLine("Printable Support Geometry", "Next") QueueLine("Hard-Surface Modelling", "Planned") QueueLine("Blender Cloth Fundamentals", "Recommended") } } } @Composable private fun QueueLine(name: String, state: String) { Row( modifier = Modifier.padding(vertical = 5.dp) ) { Text( text = name, modifier = Modifier.weight(1f), fontWeight = FontWeight.SemiBold ) Text( text = state, color = Amber, fontSize = 12.sp ) } } @Composable private fun RecommendationCard(onAdd: () -> Unit) { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(19.dp) ) { Column(modifier = Modifier.padding(16.dp)) { SectionLabel("RECOMMENDED NEXT") Text( text = "Parametric phone support geometry", fontSize = 18.sp, fontWeight = FontWeight.Bold ) Text( text = "Supports DockForge, Forge accuracy and sellable products.", color = TextMuted, lineHeight = 19.sp ) Spacer(modifier = Modifier.height(7.dp)) Text( text = "Estimated effort: 45–90 minutes", color = Blue ) Text( text = "Benefit: Forge + income", color = Green ) Spacer(modifier = Modifier.height(10.dp)) Button(onClick = onAdd) { Text("Add for Approval") } } } } @Composable private fun SystemGuardrailsCard() { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = Raised), shape = RoundedCornerShape(19.dp) ) { Column(modifier = Modifier.padding(16.dp)) { SectionLabel("GUARDRAILS") GuardrailLine("Existing 371-skill state", "Protected") GuardrailLine("Counted attempts", "Unchanged") GuardrailLine("Certification", "Approval required") GuardrailLine("Forge permissions", "Locked by default") GuardrailLine("Automatic printing", "Disabled") } } } @Composable private fun GuardrailLine(name: String, state: String) { Row( modifier = Modifier.padding(vertical = 4.dp) ) { Text( text = name, modifier = Modifier.weight(1f), color = TextMuted ) Text( text = state, color = Green, fontWeight = FontWeight.SemiBold, fontSize = 12.sp ) } } @Composable private fun DetailLine(label: String, value: String) { Row( modifier = Modifier.padding(vertical = 4.dp) ) { Text( text = label, modifier = Modifier.weight(1f), color = TextMuted ) Text( text = value, fontWeight = FontWeight.SemiBold ) } } @Composable private fun SectionLabel(text: String) { Text( text = text, color = TextMuted, fontSize = 10.sp, fontWeight = FontWeight.Bold, letterSpacing = 1.4.sp ) Spacer(modifier = Modifier.height(6.dp)) } @OptIn(ExperimentalMaterial3Api::class) @Composable private fun LearnAndPrintDialog( onDismiss: () -> Unit, onCreatePlan: (String, String) -> Unit ) { val modes = listOf( "Learn Only", "Design Only", "Prepare to Print", "Learn + Design + Print" ) var request by remember { mutableStateOf("") } var selectedMode by remember { mutableStateOf("Learn + Design + Print") } var printer by remember { mutableStateOf("Bambu P1S #1") } var material by remember { mutableStateOf("PLA") } AlertDialog( onDismissRequest = onDismiss, title = { Column { Text( text = "Learn & Print", fontSize = 24.sp, fontWeight = FontWeight.Black ) Text( text = "Create an approval-gated SHiRE mission.", color = TextMuted, fontSize = 13.sp ) } }, text = { Column( modifier = Modifier .heightIn(max = 580.dp) .verticalScroll(rememberScrollState()) ) { OutlinedTextField( value = request, onValueChange = { if (it.length <= 1000) request = it }, modifier = Modifier .fillMaxWidth() .heightIn(min = 155.dp), label = { Text("Describe the mission") }, supportingText = { Text( text = "${request.length} / 1000", modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.End ) }, placeholder = { Text( "Learn how to design a gothic controller holder, " + "show me the model and prepare it for black PLA." ) }, maxLines = 12 ) Spacer(modifier = Modifier.height(10.dp)) SectionLabel("MISSION TYPE") modes.forEach { mode -> Row(verticalAlignment = Alignment.CenterVertically) { RadioButton( selected = selectedMode == mode, onClick = { selectedMode = mode } ) Text(mode) } } SectionLabel("PRINTER") FlowRow( horizontalArrangement = Arrangement.spacedBy(7.dp) ) { listOf( "Bambu P1S #1", "Bambu P1S #2", "Creality K2 Plus", "Decide Later" ).forEach { FilterChip( selected = printer == it, onClick = { printer = it }, label = { Text(it) } ) } } Spacer(modifier = Modifier.height(8.dp)) SectionLabel("MATERIAL") FlowRow( horizontalArrangement = Arrangement.spacedBy(7.dp) ) { listOf( "PLA", "PETG", "TPU", "Decide Later" ).forEach { FilterChip( selected = material == it, onClick = { material = it }, label = { Text(it) } ) } } Spacer(modifier = Modifier.height(10.dp)) Card( colors = CardDefaults.cardColors(containerColor = Raised) ) { Text( text = "Submitting creates a Safe Plan only. Learning, design, " + "slicing and printing remain separately locked.", modifier = Modifier.padding(12.dp), color = Green, fontSize = 12.sp ) } } }, confirmButton = { Button( enabled = request.isNotBlank(), onClick = { onCreatePlan( request.trim(), "$selectedMode • $printer • $material" ) } ) { Text("Create Safe Plan") } }, dismissButton = { TextButton(onClick = onDismiss) { Text("Cancel") } }, containerColor = Surface ) } @Composable private fun ApprovalCentreDialog( approvals: List, onDismiss: () -> Unit, onDecision: (Int, String) -> Unit ) { var selectedId by remember { mutableIntStateOf( approvals.firstOrNull { it.status == "Pending" }?.id ?: approvals.firstOrNull()?.id ?: -1 ) } val selected = approvals.firstOrNull { it.id == selectedId } AlertDialog( onDismissRequest = onDismiss, title = { Column { Text( text = "Approval Centre", fontSize = 24.sp, fontWeight = FontWeight.Black ) Text( text = "${approvals.count { it.status == "Pending" }} pending", color = Amber ) } }, text = { Column( modifier = Modifier .heightIn(max = 610.dp) .verticalScroll(rememberScrollState()) ) { approvals.forEach { item -> FilterChip( selected = selectedId == item.id, onClick = { selectedId = item.id }, label = { Text("${item.stage}: ${item.title} — ${item.status}") }, modifier = Modifier.fillMaxWidth() ) } selected?.let { item -> Spacer(modifier = Modifier.height(12.dp)) ApprovalDetail("STAGE", item.stage, Amber) ApprovalDetail("PURPOSE", item.purpose, TextPrimary) ApprovalDetail("AFFECTS", item.affects, Blue) ApprovalDetail("RISK", item.risk, Red) ApprovalDetail("ESTIMATE", item.estimate, Green) ApprovalDetail("ROLLBACK", item.rollback, TextMuted) ApprovalDetail("STATUS", item.status, Amber) if (item.status == "Pending") { Text( text = "Approval applies only to this exact stage.", color = Green, fontSize = 12.sp ) Spacer(modifier = Modifier.height(10.dp)) FlowRow( horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { Button( onClick = { onDecision(item.id, "Approved") } ) { Text("Approve Stage") } OutlinedButton( onClick = { onDecision(item.id, "Changes Requested") } ) { Text("Request Changes") } OutlinedButton( onClick = { onDecision(item.id, "Deferred") } ) { Text("Defer") } Button( onClick = { onDecision(item.id, "Rejected") }, colors = ButtonDefaults.buttonColors( containerColor = Red, contentColor = Color.Black ) ) { Text("Reject") } } } } } }, confirmButton = { TextButton(onClick = onDismiss) { Text("Close") } }, containerColor = Surface ) } @Composable private fun ApprovalDetail( label: String, value: String, valueColor: Color ) { SectionLabel(label) Text( text = value, color = valueColor, lineHeight = 19.sp ) Spacer(modifier = Modifier.height(8.dp)) }