Expand manpage and terminal output sizing

This commit is contained in:
Joe Tretter
2026-05-19 13:58:15 -05:00
parent b48244d4c2
commit b5e9038c84
5 changed files with 45 additions and 26 deletions

View File

@@ -19,8 +19,8 @@ android {
applicationId = "solutions.tretter.githugandroid" applicationId = "solutions.tretter.githugandroid"
minSdk = 26 minSdk = 26
targetSdk = 35 targetSdk = 35
versionCode = 159 versionCode = 160
versionName = "0.1.158" versionName = "0.1.159"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true

View File

@@ -186,7 +186,10 @@ fun GitHugApp() {
} }
} }
fun applyRecommendedPaneWeights(persist: Boolean = true) { fun applyRecommendedPaneWeights(
persist: Boolean = true,
outputLineCount: Int = terminalOutputLineCount(output),
) {
updatePaneLayout( updatePaneLayout(
transform = { layout -> transform = { layout ->
layout.copy( layout.copy(
@@ -194,7 +197,7 @@ fun GitHugApp() {
heights = recommendedPaneHeights( heights = recommendedPaneHeights(
level = currentLevel, level = currentLevel,
levelCount = levels.size, levelCount = levels.size,
outputLineCount = output.size, outputLineCount = outputLineCount,
screenHeightDp = screenHeightDp, screenHeightDp = screenHeightDp,
suggestionsVisible = activeExerciseDetail == ExerciseDetailPanel.SUGGESTIONS, suggestionsVisible = activeExerciseDetail == ExerciseDetailPanel.SUGGESTIONS,
visibleHint = visibleHint, visibleHint = visibleHint,
@@ -223,7 +226,7 @@ fun GitHugApp() {
editorState = null editorState = null
gitMessageEditorState = null gitMessageEditorState = null
manPageState = null manPageState = null
applyRecommendedPaneWeights(persist = false) applyRecommendedPaneWeights(persist = false, outputLineCount = terminalOutputLineCount(listOf(message)))
} }
fun loadLevel( fun loadLevel(
@@ -307,7 +310,9 @@ fun GitHugApp() {
val replacement = if (matches.size == 1) matches.first() else commonPrefix(matches) val replacement = if (matches.size == 1) matches.first() else commonPrefix(matches)
if (replacement == token && matches.size > 1) { if (replacement == token && matches.size > 1) {
output = output + "completion> ${matches.joinToString(" ")}" val newOutput = output + "completion> ${matches.joinToString(" ")}"
output = newOutput
applyRecommendedPaneWeights(persist = false, outputLineCount = terminalOutputLineCount(newOutput))
return return
} }
@@ -353,15 +358,17 @@ fun GitHugApp() {
AppLog.d("GitHugApp", "All levels completed") AppLog.d("GitHugApp", "All levels completed")
scope.launch { persistProgress(updatedCompletedLevels, levels.lastOrNull()?.id) } scope.launch { persistProgress(updatedCompletedLevels, levels.lastOrNull()?.id) }
repo = newRepo repo = newRepo
output = newOutput + listOf("🏁 All Githug levels completed.") val completedOutput = newOutput + listOf("🏁 All Githug levels completed.")
output = completedOutput
clearCommandInput() clearCommandInput()
suppressedImeEcho = null suppressedImeEcho = null
applyRecommendedPaneWeights(persist = false, outputLineCount = terminalOutputLineCount(completedOutput))
} }
} else { } else {
AppLog.d("GitHugApp", "Staying on level=${levelForResult.id}") AppLog.d("GitHugApp", "Staying on level=${levelForResult.id}")
repo = newRepo repo = newRepo
output = newOutput output = newOutput
applyRecommendedPaneWeights(persist = false) applyRecommendedPaneWeights(persist = false, outputLineCount = terminalOutputLineCount(newOutput))
} }
} }
@@ -372,7 +379,7 @@ fun GitHugApp() {
} else { } else {
runtime.readEditorFile(currentLevel, repo, path) runtime.readEditorFile(currentLevel, repo, path)
} }
output = buildList { val newOutput = buildList {
addAll(output) addAll(output)
add("$ ${invocation.command}") add("$ ${invocation.command}")
addAll(lines) addAll(lines)
@@ -380,6 +387,7 @@ fun GitHugApp() {
add("Opened ${invocation.editor} editor${if (path.isBlank()) "" else " for $path"}") add("Opened ${invocation.editor} editor${if (path.isBlank()) "" else " for $path"}")
} }
} }
output = newOutput
if (lines.isEmpty()) { if (lines.isEmpty()) {
editorState = TextEditorState( editorState = TextEditorState(
editor = invocation.editor, editor = invocation.editor,
@@ -389,18 +397,19 @@ fun GitHugApp() {
saveAsPath = path, saveAsPath = path,
) )
} }
applyRecommendedPaneWeights(persist = false) applyRecommendedPaneWeights(persist = false, outputLineCount = terminalOutputLineCount(newOutput))
} }
fun openManPage(invocation: GitHelpInvocation) { fun openManPage(invocation: GitHelpInvocation) {
val content = runtime.gitManPage(currentLevel, repo, invocation.topic) val content = runtime.gitManPage(currentLevel, repo, invocation.topic)
output = buildList { val newOutput = buildList {
addAll(output) addAll(output)
add("$ ${invocation.command}") add("$ ${invocation.command}")
add("Opened git help viewer for ${invocation.topic}") add("Opened git help viewer for ${invocation.topic}")
} }
output = newOutput
manPageState = ManPageState(topic = invocation.topic, content = content) manPageState = ManPageState(topic = invocation.topic, content = content)
applyRecommendedPaneWeights(persist = false) applyRecommendedPaneWeights(persist = false, outputLineCount = terminalOutputLineCount(newOutput))
} }
fun openGitMessageEditor(invocation: GitEditorInvocation) { fun openGitMessageEditor(invocation: GitEditorInvocation) {
@@ -410,16 +419,17 @@ fun GitHugApp() {
GitEditorCommandKind.PATCH_HUNK -> "Opened Git patch editor" GitEditorCommandKind.PATCH_HUNK -> "Opened Git patch editor"
else -> "Opened Git message editor" else -> "Opened Git message editor"
} }
output = buildList { val newOutput = buildList {
addAll(output) addAll(output)
add("$ ${invocation.command}") add("$ ${invocation.command}")
add(openedMessage) add(openedMessage)
} }
output = newOutput
gitMessageEditorState = GitMessageEditorState( gitMessageEditorState = GitMessageEditorState(
invocation = invocation.copy(initialContent = initialContent), invocation = invocation.copy(initialContent = initialContent),
content = initialContent, content = initialContent,
) )
applyRecommendedPaneWeights(persist = false) applyRecommendedPaneWeights(persist = false, outputLineCount = terminalOutputLineCount(newOutput))
} }
fun saveEditor() { fun saveEditor() {
@@ -490,7 +500,9 @@ fun GitHugApp() {
} }
fun showCommandHelp() { fun showCommandHelp() {
output = output + runtime.commandReferenceLines() val newOutput = output + runtime.commandReferenceLines()
output = newOutput
applyRecommendedPaneWeights(persist = false, outputLineCount = terminalOutputLineCount(newOutput))
} }
fun registerHeaderTap() { fun registerHeaderTap() {

View File

@@ -39,6 +39,7 @@ import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
data class ManPageState( data class ManPageState(
val topic: String, val topic: String,
@@ -131,7 +132,10 @@ fun ManPageDialog(
horizontalScrollState.animateScrollTo(horizontalTarget) horizontalScrollState.animateScrollTo(horizontalTarget)
} }
Dialog(onDismissRequest = onClose) { Dialog(
onDismissRequest = onClose,
properties = DialogProperties(usePlatformDefaultWidth = false),
) {
Surface( Surface(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@@ -29,7 +29,7 @@ fun recommendedPaneHeights(
val suggestionsLines = if (suggestionsVisible) level.commandSuggestions.size + 1 else 0 val suggestionsLines = if (suggestionsVisible) level.commandSuggestions.size + 1 else 0
val hintLines = visibleHint?.let { (it.length / 42).coerceAtLeast(1) + 1 } ?: 0 val hintLines = visibleHint?.let { (it.length / 42).coerceAtLeast(1) + 1 } ?: 0
val exerciseHeight = (120 + descriptionLines * 24 + suggestionsLines * 22 + hintLines * 22).coerceAtLeast(180) val exerciseHeight = (120 + descriptionLines * 24 + suggestionsLines * 22 + hintLines * 22).coerceAtLeast(180)
val terminalHeight = (170 + outputLineCount.coerceAtLeast(1) * 20).coerceAtMost((screenHeight * 0.7f).toInt()) val terminalHeight = (170 + outputLineCount.coerceAtLeast(1) * 24).coerceAtMost((screenHeight * 0.8f).toInt() + 140)
val visualHeight = (screenHeight * 0.24f).toInt().coerceAtLeast(170) val visualHeight = (screenHeight * 0.24f).toInt().coerceAtLeast(170)
return mapOf( return mapOf(
@@ -69,4 +69,4 @@ fun commonPrefix(values: List<String>): String {
} }
} }
return prefix return prefix
} }

View File

@@ -55,6 +55,10 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
fun terminalOutputDisplayLines(output: List<String>): List<String> = output.flatMap { line -> line.lines() }
fun terminalOutputLineCount(output: List<String>): Int = terminalOutputDisplayLines(output).size.coerceAtLeast(1)
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun TerminalPane( fun TerminalPane(
@@ -78,12 +82,11 @@ fun TerminalPane(
val outputHorizontalScroll = rememberScrollState() val outputHorizontalScroll = rememberScrollState()
val outputVerticalScroll = rememberScrollState() val outputVerticalScroll = rememberScrollState()
val terminalMinWidth = 80.dp * 7.2f val terminalMinWidth = 80.dp * 7.2f
val terminalPaneMaxHeight = configuration.screenHeightDp.dp * 0.7f val displayOutput = remember(output) { terminalOutputDisplayLines(output) }
val reservedControlsHeight = 86.dp val maxOutputHeight = (configuration.screenHeightDp.dp * 0.8f).coerceAtLeast(120.dp)
val maxOutputHeight = (terminalPaneMaxHeight - reservedControlsHeight).coerceAtLeast(120.dp) val outputLineHeight = 24.dp
val outputLineHeight = 20.dp val hasOutput = displayOutput.isNotEmpty()
val hasOutput = output.isNotEmpty() val desiredOutputHeight = (16 + displayOutput.size.coerceAtLeast(1) * outputLineHeight.value)
val desiredOutputHeight = (output.size.coerceAtLeast(1) * outputLineHeight.value)
.dp .dp
.coerceAtLeast(48.dp) .coerceAtLeast(48.dp)
.coerceAtMost(maxOutputHeight) .coerceAtMost(maxOutputHeight)
@@ -93,7 +96,7 @@ fun TerminalPane(
focusRequester.requestFocus() focusRequester.requestFocus()
} }
LaunchedEffect(output.size) { LaunchedEffect(displayOutput.size) {
outputVerticalScroll.animateScrollTo(outputVerticalScroll.maxValue) outputVerticalScroll.animateScrollTo(outputVerticalScroll.maxValue)
} }
@@ -140,7 +143,7 @@ fun TerminalPane(
modifier = Modifier.widthIn(min = terminalMinWidth), modifier = Modifier.widthIn(min = terminalMinWidth),
verticalArrangement = Arrangement.spacedBy(4.dp), verticalArrangement = Arrangement.spacedBy(4.dp),
) { ) {
output.forEach { line -> displayOutput.forEach { line ->
Text( Text(
text = line, text = line,
color = if (line.startsWith("") || line.startsWith("🏁")) Success else TextSecondary, color = if (line.startsWith("") || line.startsWith("🏁")) Success else TextSecondary,