Perform rudimentary ansi color level detection (#711)

There is a lot to add here, but this gets us off the ground.
This commit is contained in:
Jake Wharton
2025-02-19 00:51:01 -05:00
committed by GitHub
parent de15cc8699
commit d601be5e6a
5 changed files with 20 additions and 27 deletions

View File

@ -2,7 +2,6 @@
jetbrains-compose = "1.7.3"
kotlin = "2.1.10"
kotlinx-coroutines = "1.10.1"
mordant = "3.0.2"
[libraries]
kotlin-plugin-core = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
@ -29,9 +28,6 @@ spotless-gradlePlugin = "com.diffplug.spotless:spotless-plugin-gradle:7.0.2"
ktlint-core = "com.pinterest.ktlint:ktlint-cli:1.3.1"
ktlint-composeRules = "io.nlopez.compose.rules:ktlint:0.4.22"
mordant-core = { module = "com.github.ajalt.mordant:mordant-core", version.ref = "mordant" }
mordant-jvmJna = { module = "com.github.ajalt.mordant:mordant-jvm-jna", version.ref = "mordant" }
clikt = "com.github.ajalt.clikt:clikt:5.0.3"
codepoints = "de.cketti.unicode:kotlin-codepoints:0.9.0"
finalizationHook = "com.jakewharton.finalization:finalization-hook:0.1.0"

View File

@ -22,7 +22,6 @@ kotlin {
implementation projects.mosaicTerminal
implementation libs.androidx.collection
implementation libs.finalizationHook
implementation libs.mordant.core
implementation libs.codepoints
}
}
@ -34,12 +33,6 @@ kotlin {
implementation libs.assertk
}
}
jvmMain {
dependencies {
implementation libs.mordant.jvmJna
}
}
}
compilerOptions.freeCompilerArgs.add('-Xexpect-actual-classes')

View File

@ -1,7 +1,5 @@
package com.jakewharton.mosaic
import com.github.ajalt.mordant.rendering.AnsiLevel as MordantAnsiLevel
import com.jakewharton.mosaic.ui.AnsiLevel
import com.jakewharton.mosaic.ui.Color
import kotlin.math.roundToInt
@ -49,15 +47,6 @@ internal const val ansiBgColorOffset = 10
internal const val ansiSelectorColor256 = 5
internal const val ansiSelectorColorRgb = 2
internal fun MordantAnsiLevel.toMosaicAnsiLevel(): AnsiLevel {
return when (this) {
MordantAnsiLevel.NONE -> AnsiLevel.NONE
MordantAnsiLevel.ANSI16 -> AnsiLevel.ANSI16
MordantAnsiLevel.ANSI256 -> AnsiLevel.ANSI256
MordantAnsiLevel.TRUECOLOR -> AnsiLevel.TRUECOLOR
}
}
// simpler version without full conversion to HSV
// https://github.com/ajalt/colormath/blob/4a0cc9796c743cb4965407204ee63b40aaf22fca/colormath/src/commonMain/kotlin/com/github/ajalt/colormath/model/RGB.kt#L301
internal fun Color.toAnsi16Code(): Int {

View File

@ -21,7 +21,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.github.ajalt.mordant.terminal.Terminal as MordantTerminal
import com.jakewharton.finalization.withFinalizationHook
import com.jakewharton.mosaic.layout.KeyEvent
import com.jakewharton.mosaic.layout.MosaicNode
@ -92,8 +91,6 @@ private const val StageDefaultQueries = 1
private const val StageNormalOperation = 0
internal suspend fun runMosaic(isTest: Boolean, content: @Composable () -> Unit) {
val mordantTerminal = MordantTerminal()
// Entering raw mode can fail, so perform it before any additional control sequences which change
// settings. We also need to be in character mode to query capabilities with control sequences.
val rawMode = if (!isTest && env("MOSAIC_RAW_MODE") != "false") {
@ -291,7 +288,7 @@ internal suspend fun runMosaic(isTest: Boolean, content: @Composable () -> Unit)
}
val rendering = createRendering(
ansiLevel = mordantTerminal.terminalInfo.ansiLevel.toMosaicAnsiLevel(),
ansiLevel = detectAnsiLevel(),
synchronizedRendering = supportsSynchronizedRendering,
)
@ -299,7 +296,7 @@ internal suspend fun runMosaic(isTest: Boolean, content: @Composable () -> Unit)
val mosaicComposition = MosaicComposition(
coroutineContext = coroutineContext + clock,
onDraw = { rootNode ->
mordantTerminal.rawPrint(rendering.render(rootNode).toString())
print(rendering.render(rootNode).toString())
},
keyEvents = keyEvents,
terminalState = terminalState,

View File

@ -3,6 +3,7 @@ package com.jakewharton.mosaic
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.compositionLocalOf
import com.jakewharton.mosaic.ui.AnsiLevel
import com.jakewharton.mosaic.ui.unit.IntSize
import dev.drewhamilton.poko.Poko
@ -32,3 +33,20 @@ internal inline fun Terminal.copy(
darkTheme: Boolean = this.darkTheme,
size: IntSize = this.size,
) = Terminal(focused, darkTheme, size)
internal fun detectAnsiLevel(): AnsiLevel {
if (env("NO_COLOR").orEmpty().isNotEmpty()) {
return AnsiLevel.NONE
}
val term = env("COLORTERM") ?: env("TERM") ?: "dumb"
if (term.contains("24bit", ignoreCase = true) || term.contains("truecolor", ignoreCase = true)) {
return AnsiLevel.TRUECOLOR
}
if (term.contains("256")) {
return AnsiLevel.ANSI256
}
if (term != "dumb") {
return AnsiLevel.ANSI16
}
return AnsiLevel.NONE
}