Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,13 @@ plugins {
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.jetbrainsCompose) apply false
}

allprojects {
configurations.all {
resolutionStrategy {
force("org.jetbrains.kotlin:kotlin-test:2.2.21")
force("org.jetbrains.kotlin:kotlin-test-common:2.2.21")
force("org.jetbrains.kotlin:kotlin-test-annotations-common:2.2.21")
}
}
}
1 change: 1 addition & 0 deletions compose-web/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ kotlin {
}



149 changes: 75 additions & 74 deletions compose-web/src/wasmJsMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.CanvasBasedWindow
import androidx.compose.ui.window.ComposeViewport
import coil3.compose.AsyncImage
import dev.johnoreilly.common.di.initKoin
import dev.johnoreilly.common.remote.Assignment
Expand All @@ -59,96 +60,96 @@ fun main() {

val peopleInSpaceRepository = koin.get<PeopleInSpaceRepository>()

CanvasBasedWindow("PeopleInSpace", canvasElementId = "peopleInSpaceCanvas") {
ComposeViewport(content = {

val people by peopleInSpaceRepository.fetchPeopleAsFlow().collectAsState(emptyList())
var selectedPerson by remember { mutableStateOf<Assignment?>(null) }
val people by peopleInSpaceRepository.fetchPeopleAsFlow().collectAsState(emptyList())
var selectedPerson by remember { mutableStateOf<Assignment?>(null) }

Surface(
modifier = Modifier.fillMaxSize(),
color = backgroundColor
) {
Column(Modifier.fillMaxSize()) {
// Header
Box(
modifier = Modifier
.fillMaxWidth()
.background(primaryColor)
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Text(
"People In Space",
color = Color.White,
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
}

// Content
Row(Modifier.fillMaxSize().padding(16.dp)) {
// Left panel with list of people
Card(
modifier = Modifier.width(280.dp).fillMaxHeight(),
colors = CardDefaults.cardColors(containerColor = cardColor),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
shape = RoundedCornerShape(8.dp)
Surface(
modifier = Modifier.fillMaxSize(),
color = backgroundColor
) {
Column(Modifier.fillMaxSize()) {
// Header
Box(
modifier = Modifier
.fillMaxWidth()
.background(primaryColor)
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Column(Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(primaryColor.copy(alpha = 0.8f))
.padding(12.dp),
contentAlignment = Alignment.Center
) {
Text(
"Astronauts",
color = Color.White,
fontSize = 18.sp,
fontWeight = FontWeight.Medium
)
}
PersonList(people, selectedPerson) {
selectedPerson = it
}
}
Text(
"People In Space",
color = Color.White,
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
}

Spacer(modifier = Modifier.width(16.dp))

// Right panel with person details
Card(
modifier = Modifier.fillMaxSize(),
colors = CardDefaults.cardColors(containerColor = cardColor),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
shape = RoundedCornerShape(8.dp)
) {
Box(Modifier.fillMaxSize()) {
selectedPerson?.let {
PersonDetailsView(it)
} ?: run {
// Show a message when no person is selected
// Content
Row(Modifier.fillMaxSize().padding(16.dp)) {
// Left panel with list of people
Card(
modifier = Modifier.width(280.dp).fillMaxHeight(),
colors = CardDefaults.cardColors(containerColor = cardColor),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
shape = RoundedCornerShape(8.dp)
) {
Column(Modifier.fillMaxSize()) {
Box(
modifier = Modifier.fillMaxSize(),
modifier = Modifier
.fillMaxWidth()
.background(primaryColor.copy(alpha = 0.8f))
.padding(12.dp),
contentAlignment = Alignment.Center
) {
Text(
"Select an astronaut to view details",
style = TextStyle(
color = Color.Gray,
fontSize = 18.sp,
textAlign = TextAlign.Center
)
"Astronauts",
color = Color.White,
fontSize = 18.sp,
fontWeight = FontWeight.Medium
)
}
PersonList(people, selectedPerson) {
selectedPerson = it
}
}
}

Spacer(modifier = Modifier.width(16.dp))

// Right panel with person details
Card(
modifier = Modifier.fillMaxSize(),
colors = CardDefaults.cardColors(containerColor = cardColor),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
shape = RoundedCornerShape(8.dp)
) {
Box(Modifier.fillMaxSize()) {
selectedPerson?.let {
PersonDetailsView(it)
} ?: run {
// Show a message when no person is selected
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
"Select an astronaut to view details",
style = TextStyle(
color = Color.Gray,
fontSize = 18.sp,
textAlign = TextAlign.Center
)
)
}
}
}
}
}
}
}
}
}
})
}

@Composable
Expand Down
14 changes: 14 additions & 0 deletions compose-web/src/wasmJsMain/resources/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@
<meta charset="UTF-8">

<title>PeopleInSpace with Kotlin/Wasm</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#peopleInSpaceCanvas {
width: 100%;
height: 100%;
display: block;
}
</style>
<script type="application/javascript" src="skiko.js"></script>
<script type="application/javascript" src="peopleinspace.js"></script>
</head>
Expand Down
38 changes: 19 additions & 19 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[versions]
kotlin = "2.2.10"
ksp = "2.2.10-2.0.2"
kotlin = "2.2.21"
ksp = "2.3.0"

compose-multiplatform = "1.8.2"
compose-multiplatform = "1.9.3"
composeUiTooling = "1.5.4"
coroutines = "1.10.2"
kotlinxSerialization = "1.9.0"
androidGradlePlugin = "8.12.2"
androidGradlePlugin = "8.13.1"
koin = "4.1.1"
koin-annotations = "2.2.0"
koin-annotations = "2.3.1"
ktor = "3.2.3"
osmdroidAndroid = "6.1.20"
osmAndroidCompose = "0.0.5"
Expand All @@ -21,29 +21,29 @@ webPackPlugin = "9.1.0"
remoteCompose = "1.0.0-SNAPSHOT"


androidxActivity = "1.10.1"
androidxComposeBom = "2025.10.01"
material3-adaptive = "1.2.0"
material3-adaptive-navigation-suite = "1.4.0"
androidxNavigationCompose = "2.9.5"
uiToolingPreview = "1.9.4"
androidxActivity = "1.12.0-rc01"
androidxComposeBom = "2025.11.00"
material3-adaptive = "1.3.0-alpha03"
material3-adaptive-navigation-suite = "1.5.0-alpha08"
androidxNavigationCompose = "2.9.6"
uiToolingPreview = "1.10.0-beta02"
wearCompose = "1.5.4"
androidxLifecycle = "2.9.4"
androidxLifecycle = "2.10.0-rc01"
androidxLifecycleKMP = "2.9.5"

coilCompose = "2.7.0"
coilCompose3 = "3.3.0"

horologist = "0.8.2-alpha"
glanceAppWidget = "1.1.1"
okhttp = "5.2.1"
glanceAppWidget = "1.2.0-beta01"
okhttp = "5.3.0"
kermit = "2.0.8"

gradleVersionsPlugin = "0.52.0"
shadowPlugin = "9.1.0"
skie = "0.10.6"
gradleVersionsPlugin = "0.53.0"
shadowPlugin = "9.2.2"
skie = "0.10.8"

mcp = "0.6.0"
mcp = "0.7.7"

minSdk = "24"
compileSdk = "36"
Expand Down Expand Up @@ -92,7 +92,7 @@ androidx-lifecycle-compose-kmp = { module = "org.jetbrains.androidx.lifecycle:li

androidx-tracing = "androidx.tracing:tracing:1.3.0"

splash-screen = "androidx.core:core-splashscreen:1.0.1"
splash-screen = "androidx.core:core-splashscreen:1.2.0"
metrics = "androidx.metrics:metrics-performance:1.0.0-beta02"

osmdroidAndroid = { module = "org.osmdroid:osmdroid-android", version.ref = "osmdroidAndroid" }
Expand Down
6 changes: 6 additions & 0 deletions mcp-server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ plugins {
}

dependencies {
implementation(platform("io.ktor:ktor-bom:3.2.3"))
implementation(libs.mcp.kotlin)
implementation(projects.common)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.java)
implementation("io.ktor:ktor-server-core")
implementation("io.ktor:ktor-server-cio")
implementation("io.ktor:ktor-server-sse")
}

java {
Expand Down
3 changes: 2 additions & 1 deletion mcp-server/src/main/kotlin/server.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import io.modelcontextprotocol.kotlin.sdk.server.StdioServerTransport
import io.modelcontextprotocol.kotlin.sdk.server.mcp
import kotlinx.coroutines.Job
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.flow.first
import kotlinx.io.asSink
import kotlinx.io.buffered

Expand Down Expand Up @@ -38,7 +39,7 @@ fun configureServer(): Server {
name = "get-people-in-space",
description = "The list of people in space endpoint returns the list of people in space right now"
) {
val people = peopleInSpaceRepository.fetchPeople()
val people = peopleInSpaceRepository.fetchPeopleAsFlow().first()
CallToolResult(
content =
people.map { TextContent(it.name) }
Expand Down