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-23 13:09:29 -05:00
parent d4968183fc
commit c65f7d0547
2 changed files with 56 additions and 10 deletions

View File

@@ -11,8 +11,8 @@ android {
applicationId = "com.kawomi.githugandroid" applicationId = "com.kawomi.githugandroid"
minSdk = 26 minSdk = 26
targetSdk = 34 targetSdk = 34
versionCode = 28 versionCode = 29
versionName = "0.1.27" versionName = "0.1.28"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true

View File

@@ -123,6 +123,11 @@ private data class PaneLayout(
val weights: Map<PaneId, Float>, val weights: Map<PaneId, Float>,
) )
private enum class ExerciseDetailPanel {
HINT,
SUGGESTIONS,
}
private fun defaultPaneLayout(): PaneLayout = PaneLayout( private fun defaultPaneLayout(): PaneLayout = PaneLayout(
order = listOf(PaneId.EXERCISE, PaneId.TERMINAL, PaneId.VISUAL, PaneId.LEVELS), order = listOf(PaneId.EXERCISE, PaneId.TERMINAL, PaneId.VISUAL, PaneId.LEVELS),
collapsed = emptySet(), collapsed = emptySet(),
@@ -205,7 +210,6 @@ fun GitHugApp() {
val persistedPaneLayout by paneLayoutStore.layoutFlow.collectAsState(initial = defaultPaneLayout()) val persistedPaneLayout by paneLayoutStore.layoutFlow.collectAsState(initial = defaultPaneLayout())
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val screenScrollState = rememberScrollState() val screenScrollState = rememberScrollState()
val workspaceHeight = (configuration.screenHeightDp.dp * 1.05f).coerceAtLeast(560.dp)
val screenHeightDp = configuration.screenHeightDp val screenHeightDp = configuration.screenHeightDp
var paneLayout by remember { mutableStateOf(defaultPaneLayout()) } var paneLayout by remember { mutableStateOf(defaultPaneLayout()) }
@@ -216,7 +220,8 @@ fun GitHugApp() {
var suppressedImeEcho by remember { mutableStateOf<String?>(null) } var suppressedImeEcho by remember { mutableStateOf<String?>(null) }
var output by remember { mutableStateOf(listOf(runtime.startupBanner())) } var output by remember { mutableStateOf(listOf(runtime.startupBanner())) }
var hintIndex by remember { mutableStateOf(0) } var hintIndex by remember { mutableStateOf(0) }
var suggestionsExpanded by remember { mutableStateOf(false) } var activeExerciseDetail by remember { mutableStateOf<ExerciseDetailPanel?>(null) }
var visibleHint by remember { mutableStateOf<String?>(null) }
var completedLevels by remember { mutableStateOf(setOf<String>()) } var completedLevels by remember { mutableStateOf(setOf<String>()) }
var commandHistory by remember { mutableStateOf(listOf<String>()) } var commandHistory by remember { mutableStateOf(listOf<String>()) }
var historyIndex by remember { mutableStateOf(-1) } var historyIndex by remember { mutableStateOf(-1) }
@@ -258,13 +263,26 @@ fun GitHugApp() {
) )
} }
val recommendedWeights = recommendedPaneWeights(
level = currentLevel,
levelCount = levels.size,
outputLineCount = output.size,
screenHeightDp = screenHeightDp,
)
val workspaceHeight = recommendedWorkspaceHeight(
paneLayout = paneLayout,
recommendedWeights = recommendedWeights,
screenHeightDp = screenHeightDp,
)
fun resetCurrentLevel(message: String = "Level reset.") { fun resetCurrentLevel(message: String = "Level reset.") {
repo = runtime.prepareLevel(currentLevel) repo = runtime.prepareLevel(currentLevel)
clearCommandInput() clearCommandInput()
suppressedImeEcho = null suppressedImeEcho = null
output = listOf(message) output = listOf(message)
hintIndex = 0 hintIndex = 0
suggestionsExpanded = false activeExerciseDetail = null
visibleHint = null
historyIndex = -1 historyIndex = -1
historyDraft = "" historyDraft = ""
applyRecommendedPaneWeights(persist = false) applyRecommendedPaneWeights(persist = false)
@@ -277,7 +295,8 @@ fun GitHugApp() {
clearCommandInput() clearCommandInput()
suppressedImeEcho = null suppressedImeEcho = null
hintIndex = 0 hintIndex = 0
suggestionsExpanded = false activeExerciseDetail = null
visibleHint = null
historyIndex = -1 historyIndex = -1
historyDraft = "" historyDraft = ""
paneLayout = paneLayout.copy( paneLayout = paneLayout.copy(
@@ -434,11 +453,15 @@ fun GitHugApp() {
exerciseContent = { exerciseContent = {
ExercisePane( ExercisePane(
level = currentLevel, level = currentLevel,
suggestionsExpanded = suggestionsExpanded, visibleHint = if (activeExerciseDetail == ExerciseDetailPanel.HINT) visibleHint else null,
onToggleSuggestions = { suggestionsExpanded = !suggestionsExpanded }, suggestionsExpanded = activeExerciseDetail == ExerciseDetailPanel.SUGGESTIONS,
onToggleSuggestions = {
activeExerciseDetail = if (activeExerciseDetail == ExerciseDetailPanel.SUGGESTIONS) null else ExerciseDetailPanel.SUGGESTIONS
},
onHint = { onHint = {
val hint = currentLevel.hints.getOrNull(hintIndex) ?: "No more hints for this level." val hint = currentLevel.hints.getOrNull(hintIndex) ?: "No more hints for this level."
output = output + "hint> $hint" visibleHint = hint
activeExerciseDetail = ExerciseDetailPanel.HINT
hintIndex = (hintIndex + 1).coerceAtMost(currentLevel.hints.size) hintIndex = (hintIndex + 1).coerceAtMost(currentLevel.hints.size)
}, },
onReset = { resetCurrentLevel() }, onReset = { resetCurrentLevel() },
@@ -890,6 +913,7 @@ private fun VisualPane(repo: RepoState) {
@Composable @Composable
private fun ExercisePane( private fun ExercisePane(
level: Level, level: Level,
visibleHint: String?,
suggestionsExpanded: Boolean, suggestionsExpanded: Boolean,
onToggleSuggestions: () -> Unit, onToggleSuggestions: () -> Unit,
onHint: () -> Unit, onHint: () -> Unit,
@@ -930,6 +954,10 @@ private fun ExercisePane(
Text(suggestion, color = TextSecondary, fontFamily = FontFamily.Monospace) Text(suggestion, color = TextSecondary, fontFamily = FontFamily.Monospace)
} }
} }
if (visibleHint != null) {
Text("Hint", color = TextPrimary, fontWeight = FontWeight.Bold)
Text(visibleHint, color = TextSecondary)
}
} }
} }
@@ -983,7 +1011,9 @@ private fun TerminalPane(
val horizontalScroll = rememberScrollState() val horizontalScroll = rememberScrollState()
val outputVerticalScroll = rememberScrollState() val outputVerticalScroll = rememberScrollState()
val terminalMinWidth = 80.dp * 7.2f val terminalMinWidth = 80.dp * 7.2f
val maxOutputHeight = configuration.screenHeightDp.dp * 0.7f val terminalPaneMaxHeight = configuration.screenHeightDp.dp * 0.7f
val reservedControlsHeight = 86.dp
val maxOutputHeight = (terminalPaneMaxHeight - reservedControlsHeight).coerceAtLeast(120.dp)
val outputLineHeight = 20.dp val outputLineHeight = 20.dp
val desiredOutputHeight = (output.size.coerceAtLeast(6) * outputLineHeight.value).dp.coerceAtMost(maxOutputHeight) val desiredOutputHeight = (output.size.coerceAtLeast(6) * outputLineHeight.value).dp.coerceAtMost(maxOutputHeight)
@@ -1187,6 +1217,22 @@ private fun recommendedPaneWeights(
) )
} }
private fun recommendedWorkspaceHeight(
paneLayout: PaneLayout,
recommendedWeights: Map<PaneId, Float>,
screenHeightDp: Int,
): androidx.compose.ui.unit.Dp {
val screenHeight = screenHeightDp.coerceAtLeast(560)
val dividerHeight = 18 * (paneLayout.order.size - 1)
val collapsedPaneHeight = 44 * paneLayout.collapsed.size
val expandedHeight = paneLayout.order
.filterNot { it in paneLayout.collapsed }
.sumOf { pane ->
(recommendedWeights[pane] ?: 1f * screenHeight).toInt()
}
return (expandedHeight + dividerHeight + collapsedPaneHeight).coerceAtLeast(screenHeight / 2).dp
}
private fun commonPrefix(values: List<String>): String { private fun commonPrefix(values: List<String>): String {
if (values.isEmpty()) return "" if (values.isEmpty()) return ""
var prefix = values.first() var prefix = values.first()