Take ktfmt's defaults for the Kotlin half
The server half went to rustfmt's defaults earlier today; this is the same move for the app, and the same reasoning. Kotlin ships no formatter with the Gradle build, so the question was which to adopt: ktfmt is Kotlin-org owned now (it moved from facebook/ktfmt), is a formatter rather than a configurable linter, and has essentially nothing to tune -- which is what rule 27 is asking for. ktlint's .editorconfig surface is the thing that rule warns against, and detekt is static analysis, whose job Android Lint already does here. One setting, and it is a choice between the tool's own two styles rather than a tuning: kotlinLangStyle() is the 4-space one, which is what this code already was. The 2-space default would have reindented every file to say nothing. ./gradlew :androidApp:ktfmtFormat to apply ./gradlew :androidApp:ktfmtCheck to verify Formatting only. The one thing worth checking by hand was the generated PEM constant, since a leading newline there costs Android's CertificateFactory its preamble sniff and fails at runtime nowhere near the cause: ktfmt moved `.trimMargin()` onto its own line and left the template alone, and the regenerated constant still starts at the opening quotes. Verified after: ktfmtCheck, compileDebugKotlin and lintDebug all pass, and the APK builds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw
This commit is contained in:
1 parent
dde5042b12
commit
0c13090d70
16 files changed
+585
-496
No files matched your search
@@ -44,9 +44,8 @@ private const val LOCAL_HOST_LABEL = "backend"
|
||||
/**
|
||||
* The spawn screen: what to run, where to run it, and the per-kind fields.
|
||||
*
|
||||
* Providers and hosts both come from the server, so adding either to its
|
||||
* config.ron shows up here with no app rebuild -- and because they are
|
||||
* independent, any provider can be sent to any host.
|
||||
* Providers and hosts both come from the server, so adding either to its config.ron shows up here
|
||||
* with no app rebuild -- and because they are independent, any provider can be sent to any host.
|
||||
*/
|
||||
@Composable
|
||||
fun SpawnScreen(
|
||||
@@ -73,15 +72,17 @@ fun SpawnScreen(
|
||||
var spawnError by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
options = try {
|
||||
val fetched = withContext(Dispatchers.IO) {
|
||||
SpawnOptions(fetchProviders(settings), fetchHosts(settings))
|
||||
options =
|
||||
try {
|
||||
val fetched =
|
||||
withContext(Dispatchers.IO) {
|
||||
SpawnOptions(fetchProviders(settings), fetchHosts(settings))
|
||||
}
|
||||
provider = fetched.providers.firstOrNull()
|
||||
LoadState.Loaded(fetched)
|
||||
} catch (e: ApiException) {
|
||||
LoadState.failed(e)
|
||||
}
|
||||
provider = fetched.providers.firstOrNull()
|
||||
LoadState.Loaded(fetched)
|
||||
} catch (e: ApiException) {
|
||||
LoadState.failed(e)
|
||||
}
|
||||
}
|
||||
|
||||
val current = provider
|
||||
@@ -105,17 +106,18 @@ fun SpawnScreen(
|
||||
// failure to fetch them leaves no form worth showing -- so this
|
||||
// reports and stops, rather than offering empty pickers under an
|
||||
// error message.
|
||||
val (providers, hosts) = when (val state = options) {
|
||||
is LoadState.Loading -> {
|
||||
CircularProgressIndicator()
|
||||
return@Column
|
||||
val (providers, hosts) =
|
||||
when (val state = options) {
|
||||
is LoadState.Loading -> {
|
||||
CircularProgressIndicator()
|
||||
return@Column
|
||||
}
|
||||
is LoadState.Error -> {
|
||||
Text(state.message, color = MaterialTheme.colorScheme.error)
|
||||
return@Column
|
||||
}
|
||||
is LoadState.Loaded -> state.value
|
||||
}
|
||||
is LoadState.Error -> {
|
||||
Text(state.message, color = MaterialTheme.colorScheme.error)
|
||||
return@Column
|
||||
}
|
||||
is LoadState.Loaded -> state.value
|
||||
}
|
||||
|
||||
ChipGroup(
|
||||
label = "Provider",
|
||||
@@ -133,13 +135,15 @@ fun SpawnScreen(
|
||||
onSelect = { name -> host = name.takeIf { it != LOCAL_HOST_LABEL } },
|
||||
)
|
||||
host?.let { chosen ->
|
||||
hosts.firstOrNull { it.name == chosen }?.let {
|
||||
Text(
|
||||
it.address,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
hosts
|
||||
.firstOrNull { it.name == chosen }
|
||||
?.let {
|
||||
Text(
|
||||
it.address,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(16.dp))
|
||||
|
||||
@@ -202,17 +206,18 @@ fun SpawnScreen(
|
||||
busy = true
|
||||
scope.launch {
|
||||
try {
|
||||
val spawned = withContext(Dispatchers.IO) {
|
||||
spawnSession(
|
||||
settings,
|
||||
provider = chosen.name,
|
||||
title = title.trim(),
|
||||
host = host,
|
||||
model = model.trim().takeIf { isClaude },
|
||||
cwd = cwd.trim().takeIf { isClaude },
|
||||
permissionMode = permissionMode.takeIf { isClaude },
|
||||
)
|
||||
}
|
||||
val spawned =
|
||||
withContext(Dispatchers.IO) {
|
||||
spawnSession(
|
||||
settings,
|
||||
provider = chosen.name,
|
||||
title = title.trim(),
|
||||
host = host,
|
||||
model = model.trim().takeIf { isClaude },
|
||||
cwd = cwd.trim().takeIf { isClaude },
|
||||
permissionMode = permissionMode.takeIf { isClaude },
|
||||
)
|
||||
}
|
||||
onSpawned(spawned)
|
||||
} catch (e: ApiException) {
|
||||
spawnError = e.message
|
||||
@@ -221,7 +226,9 @@ fun SpawnScreen(
|
||||
}
|
||||
},
|
||||
enabled = !busy && current != null,
|
||||
) { Text(if (busy) "Spawning..." else "Spawn") }
|
||||
) {
|
||||
Text(if (busy) "Spawning..." else "Spawn")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,9 +238,9 @@ private data class SpawnOptions(val providers: List<Provider>, val hosts: List<R
|
||||
/**
|
||||
* A labeled row of choices that wraps onto as many lines as it needs.
|
||||
*
|
||||
* FlowRow rather than Row: a plain Row gives every chip an equal share of
|
||||
* a single line, so once the options don't fit, the text inside each one
|
||||
* wraps to one character per line instead of the row wrapping.
|
||||
* FlowRow rather than Row: a plain Row gives every chip an equal share of a single line, so once
|
||||
* the options don't fit, the text inside each one wraps to one character per line instead of the
|
||||
* row wrapping.
|
||||
*/
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
|
||||
Reference in new issue
Block a user