Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 24 additions & 49 deletions lib/src/main/java/org/connectbot/terminal/ColorCache.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.connectbot.terminal

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb

/**
* Efficient color cache to prevent allocating Color objects on every frame.
Expand Down Expand Up @@ -74,6 +75,11 @@ internal object ColorCache {
return newColor
}

fun ansiPaletteArgb(): IntArray =
IntArray(16) { index ->
paletteCache[index].toArgb()
}

private fun findPaletteIndex(rgb: Int): Int {
// Extract components once
val r = (rgb shr 16) and 0xFF
Expand Down Expand Up @@ -110,55 +116,24 @@ internal object ColorCache {
}

private fun standardAnsiColor(i: Int): Color = when (i) {
0 -> Color(0, 0, 0)

// Black
1 -> Color(205, 0, 0)

// Red
2 -> Color(0, 205, 0)

// Green
3 -> Color(205, 205, 0)

// Yellow
4 -> Color(0, 0, 238)

// Blue
5 -> Color(205, 0, 205)

// Magenta
6 -> Color(0, 205, 205)

// Cyan
7 -> Color(229, 229, 229)

// White
8 -> Color(127, 127, 127)

// Bright Black
9 -> Color(255, 0, 0)

// Bright Red
10 -> Color(0, 255, 0)

// Bright Green
11 -> Color(255, 255, 0)

// Bright Yellow
12 -> Color(92, 92, 255)

// Bright Blue
13 -> Color(255, 0, 255)

// Bright Magenta
14 -> Color(0, 255, 255)

// Bright Cyan
15 -> Color(255, 255, 255)

// Bright White
else -> Color.White
0 -> Color(24, 37, 58) // Black — Dearman surface
1 -> Color(255, 82, 72) // Red — aggressively not garnet
2 -> Color(142, 239, 247) // Green slot — Dearman cyan
3 -> Color(245, 102, 0) // Yellow — Clemson orange
4 -> Color(45, 125, 255) // Blue — Dearman deep blue
5 -> Color(82, 45, 128) // Magenta — Clemson purple
6 -> Color(66, 165, 255) // Cyan — Dearman blue
7 -> Color(142, 239, 247) // White — Dearman cyan
8 -> Color(113, 134, 159) // Bright black
9 -> Color(255, 145, 138) // Bright red — even less garnet
10 -> Color(173, 247, 251) // Bright green slot — bright Dearman cyan
11 -> Color(255, 140, 46) // Bright yellow — bright Clemson orange
12 -> Color(88, 180, 255) // Bright blue
13 -> Color(151, 105, 201) // Bright magenta — readable Clemson purple
14 -> Color(173, 247, 251) // Bright cyan
15 -> Color(247, 251, 255) // Bright white

else -> Color(247, 251, 255)
}

private fun rgb6Color(offset: Int): Color {
Expand Down
15 changes: 15 additions & 0 deletions lib/src/main/java/org/connectbot/terminal/SearchController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.connectbot.terminal

interface SearchController {
val query: String
val matchCount: Int
val activeMatchIndex: Int

fun find(query: String): Int

fun next(): Boolean

fun previous(): Boolean

fun clear()
}
67 changes: 67 additions & 0 deletions lib/src/main/java/org/connectbot/terminal/SearchManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.connectbot.terminal

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue

internal class SearchManager {
var query by mutableStateOf("")
private set

var matches by mutableStateOf<List<SearchMatch>>(emptyList())
private set

var activeMatchIndex by mutableIntStateOf(-1)
private set

val matchCount: Int
get() = matches.size

fun setMatches(
query: String,
matches: List<SearchMatch>,
) {
this.query = query
this.matches = matches
activeMatchIndex = if (matches.isNotEmpty()) 0 else -1
}

fun next(): SearchMatch? {
if (matches.isEmpty()) {
return null
}

activeMatchIndex = if (activeMatchIndex >= matches.lastIndex) {
0
} else {
activeMatchIndex + 1
}

return matches[activeMatchIndex]
}

fun previous(): SearchMatch? {
if (matches.isEmpty()) {
return null
}

activeMatchIndex = if (activeMatchIndex <= 0) {
matches.lastIndex
} else {
activeMatchIndex - 1
}

return matches[activeMatchIndex]
}

fun activeMatch(): SearchMatch? {
return matches.getOrNull(activeMatchIndex)
}

fun clear() {
query = ""
matches = emptyList()
activeMatchIndex = -1
}
}
6 changes: 6 additions & 0 deletions lib/src/main/java/org/connectbot/terminal/SearchMatch.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.connectbot.terminal

data class SearchMatch(
val range: SelectionRange,
val text: String,
)
40 changes: 30 additions & 10 deletions lib/src/main/java/org/connectbot/terminal/SelectionManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ sealed class SelectionMode {
data object LINE : SelectionMode()
}

internal data class SelectionRange(
data class SelectionRange(
val startRow: Int,
val startCol: Int,
val endRow: Int,
Expand Down Expand Up @@ -143,7 +143,6 @@ internal class SelectionManager {
private set

var selectionRange by mutableStateOf<SelectionRange?>(null)
private set

var isSelecting by mutableStateOf(false)
private set
Expand Down Expand Up @@ -239,6 +238,12 @@ internal class SelectionManager {
isSelecting = false
}

fun applySelectionRange(range: SelectionRange) {
mode = SelectionMode.CHARACTER
isSelecting = false
selectionRange = range
}

fun clearSelection() {
mode = SelectionMode.NONE
selectionRange = null
Expand Down Expand Up @@ -376,13 +381,16 @@ internal class SelectionManager {
return buildString {
for (row in minRow..maxRow) {
// Get line from the appropriate source based on scrollback position
val line = if (scrollbackPosition > 0) {
// Viewing scrollback: get from scrollback (stored newest-first, so reverse index)
val scrollbackIndex = snapshot.scrollback.size - scrollbackPosition + row
snapshot.scrollback.getOrNull(scrollbackIndex)
val lineIndex = if (scrollbackPosition > 0) {
snapshot.scrollback.size - scrollbackPosition + row
} else {
snapshot.scrollback.size + row
}

val line = if (lineIndex < snapshot.scrollback.size) {
snapshot.scrollback.getOrNull(lineIndex)
} else {
// Viewing current screen: get from visible lines
snapshot.lines.getOrNull(row)
snapshot.lines.getOrNull(lineIndex - snapshot.scrollback.size)
}

if (line == null) continue
Expand All @@ -397,7 +405,13 @@ internal class SelectionManager {
}
}.trimEnd()
append(lineText)
if (row < maxRow && !line.softWrapped) append('\n')
if (row < maxRow) {
if (line.softWrapped) {
append(' ')
} else {
append('\n')
}
}
}

SelectionMode.CHARACTER, SelectionMode.WORD -> {
Expand All @@ -419,7 +433,13 @@ internal class SelectionManager {
}
}.trimEnd()
append(lineText)
if (row < maxRow && !line.softWrapped) append('\n')
if (row < maxRow) {
if (line.softWrapped) {
append(' ')
} else {
append('\n')
}
}
}

SelectionMode.NONE -> {}
Expand Down
Loading