Auto-commit after successful build: update app gameplay/UI, improve build setup

Changed files:\napp/build.gradle.kts
app/src/main/java/com/kawomi/githugandroid/GitHugApp.kt
This commit is contained in:
Joe Tretter
2026-04-22 13:13:38 -05:00
parent c93c37da09
commit 75462e784b
2 changed files with 56 additions and 26 deletions

View File

@@ -11,8 +11,8 @@ android {
applicationId = "com.kawomi.githugandroid"
minSdk = 26
targetSdk = 34
versionCode = 5
versionName = "0.1.4"
versionCode = 6
versionName = "0.1.5"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -70,7 +70,6 @@ dependencies {
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material:material-icons-extended")
implementation("androidx.compose.material3:material3:1.2.1")
implementation("com.google.android.material:material:1.12.0")

View File

@@ -8,19 +8,12 @@ import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material.icons.filled.KeyboardTab
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
@@ -39,6 +32,8 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
@@ -47,6 +42,8 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
private val AppBackground = Color(0xFF000000)
private val PanelPrimary = Color(0xFF121212)
@@ -83,6 +80,7 @@ fun GitHugApp() {
var repo by remember { mutableStateOf(levels.first().setup()) }
var commandInput by remember { mutableStateOf(TextFieldValue("")) }
var inputFieldVersion by remember { mutableStateOf(0) }
var suppressedImeEcho by remember { mutableStateOf<String?>(null) }
var output by remember { mutableStateOf(listOf("Welcome to GitHug Android.")) }
var hintIndex by remember { mutableStateOf(0) }
var completedLevels by remember { mutableStateOf(setOf<String>()) }
@@ -91,20 +89,21 @@ fun GitHugApp() {
var historyDraft by remember { mutableStateOf("") }
val currentLevel = levels[currentLevelIndex]
val solved = currentLevel.validator(repo)
LaunchedEffect(currentLevelIndex) {
screenScrollState.scrollTo(0)
}
fun clearCommandInput() {
fun clearCommandInput(recreateField: Boolean = false) {
commandInput = TextFieldValue(text = "", selection = TextRange.Zero)
inputFieldVersion += 1
if (recreateField) {
inputFieldVersion += 1
}
}
fun resetCurrentLevel(message: String = "Level reset.") {
repo = currentLevel.setup()
clearCommandInput()
suppressedImeEcho = null
output = listOf(message)
hintIndex = 0
historyIndex = -1
@@ -164,10 +163,12 @@ fun GitHugApp() {
}
fun runCommand() {
val raw = commandInput.text.trim()
val submittedText = commandInput.text
val raw = submittedText.trim()
if (raw.isBlank()) return
clearCommandInput()
suppressedImeEcho = submittedText
clearCommandInput(recreateField = true)
if (commandHistory.lastOrNull() != raw) {
commandHistory = commandHistory + raw
@@ -196,11 +197,13 @@ fun GitHugApp() {
repo = nextLevel.setup()
output = listOf("Loaded level: ${nextLevel.title}")
clearCommandInput()
suppressedImeEcho = null
hintIndex = 0
} else {
repo = newRepo
output = listOf("🏁 All available MVP levels completed.")
clearCommandInput()
suppressedImeEcho = null
}
} else {
repo = newRepo
@@ -227,6 +230,7 @@ fun GitHugApp() {
repo = levels[index].setup()
output = listOf("Loaded level: ${levels[index].title}")
clearCommandInput()
suppressedImeEcho = null
hintIndex = 0
historyIndex = -1
historyDraft = ""
@@ -248,7 +252,17 @@ fun GitHugApp() {
output = output,
inputFieldVersion = inputFieldVersion,
commandInput = commandInput,
onValueChange = { commandInput = it },
onValueChange = {
val blockedEcho = suppressedImeEcho
if (blockedEcho != null && commandInput.text.isEmpty()) {
val blockedTrimmed = blockedEcho.trim()
if (it.text == blockedEcho || (blockedTrimmed.isNotEmpty() && it.text == blockedTrimmed)) {
return@TerminalPanel
}
}
suppressedImeEcho = null
commandInput = it
},
onRun = { runCommand() },
onTab = { tabComplete() },
onCursorLeft = { moveCursor(-1) },
@@ -376,9 +390,20 @@ private fun TerminalPanel(
modifier: Modifier = Modifier,
) {
val focusRequester = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
val keyboardController = LocalSoftwareKeyboardController.current
LaunchedEffect(inputFieldVersion) {
if (inputFieldVersion == 0) return@LaunchedEffect
delay(75)
focusRequester.requestFocus()
keyboardController?.show()
}
fun submitCommand() {
focusManager.clearFocus(force = true)
keyboardController?.hide()
onRun()
}
Card(modifier = modifier, colors = CardDefaults.cardColors(containerColor = TerminalBackground)) {
@@ -427,7 +452,7 @@ private fun TerminalPanel(
keyboardType = KeyboardType.Ascii,
imeAction = ImeAction.Done,
),
keyboardActions = KeyboardActions(onDone = { onRun() })
keyboardActions = KeyboardActions(onDone = { submitCommand() })
)
}
}
@@ -448,29 +473,35 @@ private fun SpecialKeyBar(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
TerminalKeyButton(icon = { Icon(Icons.Filled.KeyboardTab, contentDescription = "Tab") }, onClick = onTab, modifier = Modifier.weight(1.3f))
TerminalKeyButton(icon = { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Move cursor left") }, onClick = onCursorLeft)
TerminalKeyButton(icon = { Icon(Icons.AutoMirrored.Filled.ArrowForward, contentDescription = "Move cursor right") }, onClick = onCursorRight)
TerminalKeyButton(icon = { Icon(Icons.Filled.KeyboardArrowUp, contentDescription = "Previous command") }, onClick = onHistoryUp)
TerminalKeyButton(icon = { Icon(Icons.Filled.KeyboardArrowDown, contentDescription = "Next command") }, onClick = onHistoryDown)
TerminalKeyButton(label = "TAB", onClick = onTab, modifier = Modifier.width(68.dp))
TerminalKeyButton(label = "", onClick = onCursorLeft, modifier = Modifier.width(48.dp))
TerminalKeyButton(label = "", onClick = onCursorRight, modifier = Modifier.width(48.dp))
TerminalKeyButton(label = "", onClick = onHistoryUp, modifier = Modifier.width(48.dp))
TerminalKeyButton(label = "", onClick = onHistoryDown, modifier = Modifier.width(48.dp))
}
}
@Composable
private fun TerminalKeyButton(
icon: @Composable () -> Unit,
label: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Button(
onClick = onClick,
modifier = modifier,
modifier = modifier.height(34.dp),
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 4.dp),
colors = ButtonDefaults.buttonColors(
containerColor = PanelSecondary,
contentColor = TextPrimary
)
) {
icon()
Text(
text = label,
fontFamily = FontFamily.Monospace,
fontWeight = FontWeight.Bold,
fontSize = 13.sp
)
}
}