Skip to content

Commit

Permalink
Scroll local video rendering to follow currently executing command (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Leland-Takamine authored Jan 14, 2025
1 parent 183c126 commit 84624cf
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 18 deletions.
38 changes: 20 additions & 18 deletions maestro-cli/src/main/java/maestro/cli/graphics/SkiaFrameRenderer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,9 @@ package maestro.cli.graphics
import org.jetbrains.skia.Canvas
import org.jetbrains.skia.Color
import org.jetbrains.skia.Font
import org.jetbrains.skia.FontMgr
import org.jetbrains.skia.Paint
import org.jetbrains.skia.Rect
import org.jetbrains.skia.Surface
import org.jetbrains.skia.paragraph.FontCollection
import org.jetbrains.skia.paragraph.ParagraphBuilder
import org.jetbrains.skia.paragraph.ParagraphStyle
import org.jetbrains.skia.paragraph.TextStyle
import org.jetbrains.skiko.toImage
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
Expand Down Expand Up @@ -42,13 +37,10 @@ class SkiaFrameRenderer : FrameRenderer {
private val footerText = "maestro.mobile.dev"

private val terminalBgColor = Color.makeARGB(220, 0, 0, 0)
private val terminalTextStyle = TextStyle().apply {
fontFamilies = SkiaFonts.MONOSPACE_FONT_FAMILIES.toTypedArray()
fontSize = 24f
color = Color.WHITE
}
private val terminalContentPadding = 40f

private val textClipper = SkiaTextClipper()

override fun render(
outputWidthPx: Int,
outputHeightPx: Int,
Expand Down Expand Up @@ -154,14 +146,24 @@ class SkiaFrameRenderer : FrameRenderer {
val contentRect = Rect.makeLTRB(terminalRect.left, headerRect.bottom, terminalRect.right, footerRect.top)
canvas.drawRect(contentRect, Paint().apply { color = terminalBgColor })

val paddedContentRect = contentRect.inflate(-terminalContentPadding)
val paddedContentRect = Rect.makeLTRB(
l = contentRect.left + terminalContentPadding,
t = contentRect.top + terminalContentPadding,
r = contentRect.right - terminalContentPadding,
b = contentRect.bottom - terminalContentPadding / 4f,
)

val focusedLineIndex = getFocusedLineIndex(string)
val focusedLinePadding = 5
textClipper.renderClippedText(canvas, paddedContentRect, string, focusedLineIndex + focusedLinePadding)
}

val fontCollection = FontCollection().setDefaultFontManager(FontMgr.default)
val p = ParagraphBuilder(ParagraphStyle(), fontCollection)
.pushStyle(terminalTextStyle)
.addText(string)
.build()
p.layout(paddedContentRect.width)
p.paint(canvas, paddedContentRect.left, paddedContentRect.top)
private fun getFocusedLineIndex(text: String): Int {
val lines = text.lines()
val indexOfFirstPendingLine = lines.indexOfFirst { it.contains("\uD83D\uDD32") }
if (indexOfFirstPendingLine != -1) return indexOfFirstPendingLine
val indexOfLastCheck = lines.indexOfLast { it.contains("") }
if (indexOfLastCheck != -1) return indexOfLastCheck
return 0
}
}
64 changes: 64 additions & 0 deletions maestro-cli/src/main/java/maestro/cli/graphics/SkiaTextClipper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package maestro.cli.graphics

import org.jetbrains.skia.Canvas
import org.jetbrains.skia.Color
import org.jetbrains.skia.FontMgr
import org.jetbrains.skia.Rect
import org.jetbrains.skia.paragraph.FontCollection
import org.jetbrains.skia.paragraph.Paragraph
import org.jetbrains.skia.paragraph.ParagraphBuilder
import org.jetbrains.skia.paragraph.ParagraphStyle
import org.jetbrains.skia.paragraph.RectHeightMode
import org.jetbrains.skia.paragraph.RectWidthMode
import org.jetbrains.skia.paragraph.TextStyle
import kotlin.math.min

class SkiaTextClipper {

private val terminalTextStyle = TextStyle().apply {
fontFamilies = SkiaFonts.MONOSPACE_FONT_FAMILIES.toTypedArray()
fontSize = 24f
color = Color.WHITE
}

fun renderClippedText(canvas: Canvas, rect: Rect, text: String, focusedLine: Int) {
val p = createParagraph(text, rect.width)
val focusedLineRange = getRangeForLine(text, focusedLine)
val focusedLineBottom = p.getRectsForRange(
start = focusedLineRange.first,
end = focusedLineRange.second,
rectHeightMode = RectHeightMode.MAX,
rectWidthMode = RectWidthMode.MAX
).maxOf { it.rect.bottom }
val offsetY = min(0f, rect.height - focusedLineBottom)
canvas.save()
canvas.clipRect(rect)
p.paint(canvas, rect.left, rect.top + offsetY)
canvas.restore()
}

private fun getRangeForLine(text: String, lineIndex: Int): Pair<Int, Int> {
var start = 0
var end = 0
var currentLine = 0
while (currentLine <= lineIndex) {
start = end
end = text.indexOf('\n', start + 1)
if (end == -1) {
end = text.length
break
}
currentLine++
}
return Pair(start, end)
}

private fun createParagraph(text: String, width: Float): Paragraph {
val fontCollection = FontCollection().setDefaultFontManager(FontMgr.default)
return ParagraphBuilder(ParagraphStyle(), fontCollection)
.pushStyle(terminalTextStyle)
.addText(text)
.build()
.apply { layout(width) }
}
}

0 comments on commit 84624cf

Please sign in to comment.