Improve upstream parity for level fixtures

- Add native per-level fixtures for complex histories, tags, branch graphs, rebases, resets, restores, stashes, and local remotes.
- Strengthen repository inspection so branch commit counts, remote-tracking branches, and detached tag checkouts reflect real Git state.
- Align Fetch, Pull, Push Branch, Branch At, and Rebase validators/tests with upstream-style repository state.
- Keep the prompt callout and native filesystem tab-completion fixes from the previous batch.
This commit is contained in:
Joe Tretter
2026-05-07 09:21:04 -05:00
parent 8e88b5e7c5
commit 91e2df3ffe
11 changed files with 526 additions and 17 deletions

View File

@@ -15,6 +15,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
@@ -39,15 +40,18 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.Alignment
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -91,6 +95,7 @@ fun GitHugApp() {
var showHelpOverlay by remember { mutableStateOf(false) }
var showHelpOnStart by remember { mutableStateOf(true) }
var helpPreferenceInitialized by remember { mutableStateOf(false) }
var promptBounds by remember { mutableStateOf<Rect?>(null) }
val currentLevel = levels[currentLevelIndex]
LaunchedEffect(solvedCelebrationTitle) {
@@ -293,7 +298,7 @@ fun GitHugApp() {
val isCdCompletion = leadingCommand == "cd"
if (token.isBlank() && !isCdCompletion) return
val candidates = if (isCdCompletion) directoryCompletionCandidates(repo) else fileCompletionCandidates(repo)
val candidates = runtime.completionCandidates(currentLevel, repo, directoriesOnly = isCdCompletion)
val matches = candidates.sorted().filter { it.startsWith(token) }
if (matches.isEmpty()) return
@@ -572,6 +577,7 @@ fun GitHugApp() {
onCursorRight = { moveCursor(1) },
onHistoryUp = { historyUp() },
onHistoryDown = { historyDown() },
onPromptBoundsChanged = { promptBounds = it },
)
},
)
@@ -585,6 +591,7 @@ fun GitHugApp() {
scope.launch { gameProgressStore.saveShowHelpOnStart(showHelpOnStart) }
},
onClose = { showHelpOverlay = false },
promptBounds = promptBounds,
)
}
}
@@ -663,7 +670,9 @@ private fun HelpCalloutOverlay(
onShowHelpOnStartChange: (Boolean) -> Unit,
onOk: () -> Unit,
onClose: () -> Unit,
promptBounds: Rect?,
) {
val density = LocalDensity.current
Box(
modifier = Modifier
.fillMaxSize()
@@ -674,9 +683,12 @@ private fun HelpCalloutOverlay(
modifier = Modifier.align(Alignment.TopStart),
)
HelpBubbleWithControls(
modifier = Modifier
modifier = promptBounds?.let { bounds ->
val calloutTop = with(density) { (bounds.top.toDp() - 148.dp).coerceAtLeast(12.dp) }
Modifier.offset { IntOffset(0, with(density) { calloutTop.roundToPx() }) }
} ?: Modifier
.align(Alignment.BottomStart)
.padding(bottom = 54.dp),
.padding(bottom = 96.dp),
text = "Tap the prompt to enter a command.",
showHelpOnStart = showHelpOnStart,
onShowHelpOnStartChange = onShowHelpOnStartChange,