Created
September 24, 2025 20:16
-
-
Save iprashantpanwar/c4770640e88c7130afcb54251c353c39 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import androidx.compose.animation.AnimatedVisibility | |
| import androidx.compose.animation.core.* | |
| import androidx.compose.foundation.background | |
| import androidx.compose.foundation.layout.* | |
| import androidx.compose.foundation.shape.CircleShape | |
| import androidx.compose.foundation.shape.RoundedCornerShape | |
| import androidx.compose.material.* | |
| import androidx.compose.runtime.* | |
| import androidx.compose.ui.Alignment | |
| import androidx.compose.ui.ExperimentalComposeUiApi | |
| import androidx.compose.ui.Modifier | |
| import androidx.compose.ui.graphics.Color | |
| import androidx.compose.ui.graphics.graphicsLayer | |
| import androidx.compose.ui.unit.dp | |
| import androidx.compose.ui.unit.sp | |
| import androidx.compose.ui.window.ComposeViewport | |
| import kotlinx.coroutines.delay | |
| import kotlin.random.Random | |
| @OptIn(ExperimentalComposeUiApi::class) | |
| fun main() { | |
| ComposeViewport { App() } | |
| } | |
| @Composable | |
| fun App() { | |
| var darkTheme by remember { mutableStateOf(false) } | |
| var greetingText by remember { mutableStateOf("Hello World!") } | |
| var showPlugin by remember { mutableStateOf(false) } | |
| var counter by remember { mutableStateOf(0) } | |
| var name by remember { mutableStateOf("") } | |
| var selectedPlugin by remember { mutableStateOf(PluginType.HeartBeat) } | |
| var progress by remember { mutableStateOf(0.3f) } | |
| var snackbarVisible by remember { mutableStateOf(false) } | |
| val scaffoldState = rememberScaffoldState() | |
| PlaygroundTheme(darkTheme) { | |
| Scaffold( | |
| scaffoldState = scaffoldState, | |
| topBar = { | |
| TopAppBar( | |
| title = { Text("Compose WASM Playground") }, | |
| backgroundColor = MaterialTheme.colors.primary, | |
| contentColor = MaterialTheme.colors.onPrimary | |
| ) | |
| }, | |
| bottomBar = { | |
| BottomNavigation(backgroundColor = MaterialTheme.colors.primary) { | |
| for (p in PluginType.values()) { | |
| BottomNavigationItem( | |
| selected = selectedPlugin == p, | |
| onClick = { selectedPlugin = p }, | |
| label = { Text(p.label, color = MaterialTheme.colors.onPrimary) }, | |
| icon = {} | |
| ) | |
| } | |
| } | |
| }, | |
| floatingActionButton = { | |
| FloatingActionButton(onClick = { snackbarVisible = true }) { | |
| Text("+", color = MaterialTheme.colors.onPrimary) | |
| } | |
| } | |
| ) { paddingValues -> | |
| Column( | |
| modifier = Modifier | |
| .fillMaxWidth() | |
| .padding(16.dp) | |
| .padding(paddingValues), | |
| horizontalAlignment = Alignment.CenterHorizontally, | |
| verticalArrangement = Arrangement.spacedBy(12.dp) | |
| ) { | |
| // Theme switch | |
| Row(verticalAlignment = Alignment.CenterVertically) { | |
| Text(if (darkTheme) "Dark" else "Light") | |
| Switch(checked = darkTheme, onCheckedChange = { darkTheme = it }) | |
| } | |
| // Greeting | |
| Card(elevation = 4.dp, modifier = Modifier.fillMaxWidth()) { | |
| Column( | |
| modifier = Modifier.padding(12.dp), | |
| verticalArrangement = Arrangement.spacedBy(8.dp) | |
| ) { | |
| Text("Interactive Greeting", style = MaterialTheme.typography.subtitle1) | |
| TextField( | |
| value = name, | |
| onValueChange = { name = it }, | |
| label = { Text("Your name") }, | |
| modifier = Modifier.fillMaxWidth() | |
| ) | |
| Row(verticalAlignment = Alignment.CenterVertically) { | |
| Button(onClick = { | |
| counter++ | |
| greetingText = | |
| if (name.isBlank()) "Hello, Compose!" else "Hello, ${name.trim()}!" | |
| progress = Random.nextFloat() | |
| }) { | |
| Text(greetingText) | |
| } | |
| Spacer(modifier = Modifier.width(8.dp)) | |
| Text("Presses: $counter") | |
| } | |
| // Progress indicators | |
| LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) | |
| Spacer(modifier = Modifier.height(4.dp)) | |
| CircularProgressIndicator() | |
| } | |
| } | |
| // Plugin selector | |
| Card(elevation = 4.dp, modifier = Modifier.fillMaxWidth()) { | |
| Column(modifier = Modifier.padding(12.dp)) { | |
| Text("Plugin Host — pick a mini plugin") | |
| Spacer(modifier = Modifier.height(4.dp)) | |
| Row { | |
| for (p in PluginType.values()) { | |
| Button(onClick = { selectedPlugin = p }) { | |
| Text(p.label) | |
| } | |
| Spacer(modifier = Modifier.width(8.dp)) | |
| } | |
| } | |
| Spacer(modifier = Modifier.height(8.dp)) | |
| Row(verticalAlignment = Alignment.CenterVertically) { | |
| Button(onClick = { showPlugin = !showPlugin }) { | |
| Text(if (showPlugin) "Hide Plugin" else "Show Plugin") | |
| } | |
| Spacer(modifier = Modifier.width(8.dp)) | |
| Text("Active: ${selectedPlugin.label}") | |
| } | |
| } | |
| } | |
| // Plugin area | |
| AnimatedVisibility(visible = showPlugin) { | |
| Card(elevation = 4.dp, modifier = Modifier.fillMaxWidth()) { | |
| Box( | |
| modifier = Modifier | |
| .fillMaxWidth() | |
| .padding(12.dp), | |
| contentAlignment = Alignment.Center | |
| ) { | |
| PluginHost(selectedPlugin) | |
| } | |
| } | |
| } | |
| // Platform info | |
| Text( | |
| "Platform: ${getPlatform().name}", | |
| style = MaterialTheme.typography.caption, | |
| color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f) | |
| ) | |
| // Snackbar | |
| if (snackbarVisible) { | |
| LaunchedEffect(Unit) { | |
| delay(1500) | |
| snackbarVisible = false | |
| } | |
| Snackbar { Text("FAB Clicked!") } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // ---- Theme Setup ---- | |
| private val LightColors = lightColors( | |
| primary = Color(0xFF0066CC), | |
| primaryVariant = Color(0xFF004A99), | |
| secondary = Color(0xFFFFA000), | |
| background = Color(0xFFF2F2F2), | |
| surface = Color.White, | |
| onPrimary = Color.White, | |
| onSecondary = Color.Black, | |
| onBackground = Color.Black, | |
| onSurface = Color.Black | |
| ) | |
| private val DarkColors = darkColors( | |
| primary = Color(0xFF66B2FF), | |
| primaryVariant = Color(0xFF004A99), | |
| secondary = Color(0xFFFFB300), | |
| background = Color(0xFF121212), | |
| surface = Color(0xFF1E1E1E), | |
| onPrimary = Color.Black, | |
| onSecondary = Color.Black, | |
| onBackground = Color.White, | |
| onSurface = Color.White | |
| ) | |
| @Composable | |
| fun PlaygroundTheme(darkTheme: Boolean, content: @Composable () -> Unit) { | |
| MaterialTheme( | |
| colors = if (darkTheme) DarkColors else LightColors, | |
| typography = Typography(), | |
| shapes = Shapes(), | |
| content = content | |
| ) | |
| } | |
| // ---- Plugin Host and Types ---- | |
| enum class PluginType(val label: String) { | |
| HeartBeat("Beat"), | |
| ColorPulse("Pulse"), | |
| CounterTicker("Ticker"), | |
| BouncingBall("Ball"), | |
| RotatingSquare("Square"), | |
| ColorGrid("Grid") | |
| } | |
| @Composable | |
| fun PluginHost(type: PluginType) { | |
| when (type) { | |
| PluginType.HeartBeat -> HeartBeatPlugin() | |
| PluginType.ColorPulse -> ColorPulsePlugin() | |
| PluginType.CounterTicker -> CounterTickerPlugin() | |
| PluginType.BouncingBall -> BouncingBallPlugin() | |
| PluginType.RotatingSquare -> RotatingSquarePlugin() | |
| PluginType.ColorGrid -> ColorGridPlugin() | |
| } | |
| } | |
| // ---- Plugins ---- | |
| // HeartBeatPlugin, ColorPulsePlugin, CounterTickerPlugin, BouncingBallPlugin, RotatingSquarePlugin, ColorGridPlugin | |
| // Keep your previous plugin implementations here (unchanged) | |
| // ---- Plugins ---- | |
| @Composable | |
| fun HeartBeatPlugin() { | |
| val transition = rememberInfiniteTransition() | |
| val scale by transition.animateFloat( | |
| initialValue = 0.85f, | |
| targetValue = 1.15f, | |
| animationSpec = infiniteRepeatable( | |
| animation = tween(700, easing = FastOutSlowInEasing), | |
| repeatMode = RepeatMode.Reverse | |
| ) | |
| ) | |
| Box( | |
| modifier = Modifier | |
| .size(120.dp) | |
| .graphicsLayer( | |
| scaleX = scale, | |
| scaleY = scale | |
| ), | |
| contentAlignment = Alignment.Center | |
| ) { | |
| // Using Box as heart placeholder | |
| Box( | |
| modifier = Modifier | |
| .size(80.dp) | |
| .background(MaterialTheme.colors.error, shape = CircleShape) | |
| ) | |
| } | |
| } | |
| @Composable | |
| fun ColorPulsePlugin() { | |
| val transition = rememberInfiniteTransition() | |
| val fraction by transition.animateFloat( | |
| initialValue = 0f, | |
| targetValue = 1f, | |
| animationSpec = infiniteRepeatable( | |
| animation = tween(1600, easing = FastOutSlowInEasing), | |
| repeatMode = RepeatMode.Reverse | |
| ) | |
| ) | |
| val color = Color( | |
| red = 0.3f + 0.7f * fraction, | |
| green = 0.6f - 0.4f * fraction, | |
| blue = 0.9f - 0.5f * fraction | |
| ) | |
| Box( | |
| modifier = Modifier | |
| .size(120.dp) | |
| .background(color, CircleShape), | |
| contentAlignment = Alignment.Center | |
| ) { | |
| Text("Pulsing Box") | |
| } | |
| } | |
| @Composable | |
| fun CounterTickerPlugin() { | |
| var tick by remember { mutableStateOf(0) } | |
| LaunchedEffect(Unit) { | |
| while (true) { | |
| delay(700) | |
| tick++ | |
| } | |
| } | |
| Column(horizontalAlignment = Alignment.CenterHorizontally) { | |
| Text("Ticker") | |
| Spacer(Modifier.height(8.dp)) | |
| Text("Tick: $tick") | |
| Spacer(Modifier.height(8.dp)) | |
| Button(onClick = { tick = 0 }) { | |
| Text("Reset") | |
| } | |
| } | |
| } | |
| @Composable | |
| fun BouncingBallPlugin() { | |
| val transition = rememberInfiniteTransition() | |
| val offsetX by transition.animateFloat( | |
| initialValue = -100f, | |
| targetValue = 100f, | |
| animationSpec = infiniteRepeatable( | |
| animation = tween(1200, easing = FastOutSlowInEasing), | |
| repeatMode = RepeatMode.Reverse | |
| ) | |
| ) | |
| Box( | |
| modifier = Modifier.size(200.dp), | |
| contentAlignment = Alignment.Center | |
| ) { | |
| Box( | |
| modifier = Modifier | |
| .offset(x = offsetX.dp) | |
| .size(50.dp) | |
| .background(MaterialTheme.colors.secondary, CircleShape) | |
| ) | |
| } | |
| } | |
| @Composable | |
| fun RotatingSquarePlugin() { | |
| val transition = rememberInfiniteTransition() | |
| val angle by transition.animateFloat( | |
| initialValue = 0f, | |
| targetValue = 360f, | |
| animationSpec = infiniteRepeatable( | |
| animation = tween(2000, easing = LinearEasing), | |
| repeatMode = RepeatMode.Restart | |
| ) | |
| ) | |
| Box( | |
| modifier = Modifier | |
| .size(120.dp) | |
| .background(MaterialTheme.colors.primary, RoundedCornerShape(12.dp)) | |
| .graphicsLayer(rotationZ = angle), | |
| contentAlignment = Alignment.Center | |
| ) { | |
| Text("Rotating", color = MaterialTheme.colors.onPrimary) | |
| } | |
| } | |
| @Composable | |
| fun ColorGridPlugin() { | |
| var colors by remember { mutableStateOf(List(9) { randomColor() }) } | |
| LaunchedEffect(Unit) { | |
| while (true) { | |
| delay(1000) | |
| colors = List(9) { randomColor() } | |
| } | |
| } | |
| Column { | |
| repeat(3) { row -> | |
| Row { | |
| repeat(3) { col -> | |
| Box( | |
| modifier = Modifier | |
| .size(60.dp) | |
| .background(colors[row * 3 + col]) | |
| ) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| fun randomColor(): Color = | |
| Color(Random.nextFloat(), Random.nextFloat(), Random.nextFloat()) | |
| // ---- platform greeting ---- | |
| private val platform = object : Platform { | |
| override val name: String = "Web with Kotlin/Wasm" | |
| } | |
| fun getPlatform(): Platform = platform | |
| interface Platform { val name: String } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment