Take ktfmt's formatting on the files just added
Four files went in unformatted: I ran the formatter mid-change and then kept editing. ktfmtCheck is part of finishing, not part of starting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d580461bd5
commit
8fe13634cb
4 files changed
+39
-21
No files matched your search
@@ -29,8 +29,7 @@ sealed class SessionEvent {
|
|||||||
val options: List<String>,
|
val options: List<String>,
|
||||||
/** The tool call this is permission for, or null when it is not about one. */
|
/** The tool call this is permission for, or null when it is not about one. */
|
||||||
val about: String?,
|
val about: String?,
|
||||||
) :
|
) : SessionEvent()
|
||||||
SessionEvent()
|
|
||||||
|
|
||||||
data class Answered(val id: String, val answer: String) : SessionEvent()
|
data class Answered(val id: String, val answer: String) : SessionEvent()
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ import androidx.compose.ui.unit.dp
|
|||||||
/**
|
/**
|
||||||
* An assistant's reply, with anything it says it remembered drawn as a note rather than as markup.
|
* An assistant's reply, with anything it says it remembered drawn as a note rather than as markup.
|
||||||
*
|
*
|
||||||
* Claude Code marks a sentence that came from its stored memory by wrapping it in
|
* Claude Code marks a sentence that came from its stored memory by wrapping it in `<cc-memory
|
||||||
* `<cc-memory filenames="...">`. Markdown has nothing to say about that, so it arrived on screen as
|
* filenames="...">`. Markdown has nothing to say about that, so it arrived on screen as literal
|
||||||
* literal angle brackets in the middle of a sentence -- which reads as the model having emitted
|
* angle brackets in the middle of a sentence -- which reads as the model having emitted broken
|
||||||
* broken HTML. It is really the opposite: a claim about where something came from, which is worth
|
* HTML. It is really the opposite: a claim about where something came from, which is worth showing,
|
||||||
* showing, because "I was told this before" and "I worked this out just now" are different things
|
* because "I was told this before" and "I worked this out just now" are different things and the
|
||||||
* and the reader cannot otherwise tell them apart.
|
* reader cannot otherwise tell them apart.
|
||||||
*
|
*
|
||||||
* A tag that has not finished arriving is left alone. Streaming means the closing tag may be
|
* A tag that has not finished arriving is left alone. Streaming means the closing tag may be
|
||||||
* seconds away, and a half-written marker is not a marker yet.
|
* seconds away, and a half-written marker is not a marker yet.
|
||||||
@@ -81,8 +81,7 @@ fun splitMemoryNotes(text: String): List<MessagePart> {
|
|||||||
for (match in MEMORY_NOTE.findAll(text)) {
|
for (match in MEMORY_NOTE.findAll(text)) {
|
||||||
val before = text.substring(at, match.range.first)
|
val before = text.substring(at, match.range.first)
|
||||||
if (before.isNotBlank()) parts += MessagePart.Prose(before.trim())
|
if (before.isNotBlank()) parts += MessagePart.Prose(before.trim())
|
||||||
val files =
|
val files = match.groupValues[1].split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
||||||
match.groupValues[1].split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
|
||||||
parts += MessagePart.Remembered(match.groupValues[2].trim(), files)
|
parts += MessagePart.Remembered(match.groupValues[2].trim(), files)
|
||||||
at = match.range.last + 1
|
at = match.range.last + 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ sealed class TranscriptItem {
|
|||||||
*
|
*
|
||||||
* On the call's own row rather than beside it: the ask used to arrive as a second card
|
* On the call's own row rather than beside it: the ask used to arrive as a second card
|
||||||
* repeating the input verbatim, so the reader saw the same command twice and had to work
|
* repeating the input verbatim, so the reader saw the same command twice and had to work
|
||||||
* out that it was one event. The backend says which call a permission is about, so this
|
* out that it was one event. The backend says which call a permission is about, so this is
|
||||||
* is a fact rather than a match on the input.
|
* a fact rather than a match on the input.
|
||||||
*/
|
*/
|
||||||
val ask: QuestionCard? = null,
|
val ask: QuestionCard? = null,
|
||||||
) : TranscriptItem()
|
) : TranscriptItem()
|
||||||
@@ -138,7 +138,10 @@ fun foldEvent(items: List<TranscriptItem>, event: SessionEvent): List<Transcript
|
|||||||
// A question with no tool behind it -- AskUserQuestion, or an ask
|
// A question with no tool behind it -- AskUserQuestion, or an ask
|
||||||
// whose call fell outside the loaded window -- is a card of its
|
// whose call fell outside the loaded window -- is a card of its
|
||||||
// own, which is what every question was before this.
|
// own, which is what every question was before this.
|
||||||
if (event.about != null && items.any { it is TranscriptItem.ToolRun && it.id == event.about }) {
|
if (
|
||||||
|
event.about != null &&
|
||||||
|
items.any { it is TranscriptItem.ToolRun && it.id == event.about }
|
||||||
|
) {
|
||||||
updateTool(items, event.about) { it.copy(ask = card) }
|
updateTool(items, event.about) { it.copy(ask = card) }
|
||||||
} else {
|
} else {
|
||||||
items + card
|
items + card
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ import org.json.JSONObject
|
|||||||
* Every tool's input arrives as JSON, and showing it raw makes the reader parse `{"command":"…",
|
* Every tool's input arrives as JSON, and showing it raw makes the reader parse `{"command":"…",
|
||||||
* "timeout":120000}` themselves to find the one line they care about. So the fields that carry the
|
* "timeout":120000}` themselves to find the one line they care about. So the fields that carry the
|
||||||
* meaning are pulled out -- the command a shell will run, what it is for, how long it may take --
|
* meaning are pulled out -- the command a shell will run, what it is for, how long it may take --
|
||||||
* and anything left over is still shown, because dropping a field would be claiming the tool has
|
* and anything left over is still shown, because dropping a field would be claiming the tool has no
|
||||||
* no other input when it might.
|
* other input when it might.
|
||||||
*/
|
*/
|
||||||
data class ToolInput(
|
data class ToolInput(
|
||||||
/** The thing that will actually be run or read, if this tool has one. */
|
/** The thing that will actually be run or read, if this tool has one. */
|
||||||
@@ -70,13 +70,21 @@ fun parseToolInput(tool: String, input: String): ToolInput {
|
|||||||
} catch (_: org.json.JSONException) {
|
} catch (_: org.json.JSONException) {
|
||||||
// Not an object: older transcripts and some tools send a bare
|
// Not an object: older transcripts and some tools send a bare
|
||||||
// string. It is still the input, so it is still shown.
|
// string. It is still the input, so it is still shown.
|
||||||
return ToolInput(null, null, null, input.takeIf { it.isNotBlank() }?.let { listOf(it) }.orEmpty())
|
return ToolInput(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
input.takeIf { it.isNotBlank() }?.let { listOf(it) }.orEmpty(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
val (subjectKey, language) = SUBJECTS[tool] ?: (null to null)
|
val (subjectKey, language) = SUBJECTS[tool] ?: (null to null)
|
||||||
val subject = subjectKey?.let { json.optString(it) }?.takeIf { it.isNotBlank() }
|
val subject = subjectKey?.let { json.optString(it) }?.takeIf { it.isNotBlank() }
|
||||||
val description = DESCRIPTIONS.firstNotNullOfOrNull { json.optString(it).takeIf { v -> v.isNotBlank() } }
|
val description = DESCRIPTIONS.firstNotNullOfOrNull {
|
||||||
|
json.optString(it).takeIf { v -> v.isNotBlank() }
|
||||||
|
}
|
||||||
val rest =
|
val rest =
|
||||||
json.keys()
|
json
|
||||||
|
.keys()
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { it != subjectKey || subject == null }
|
.filter { it != subjectKey || subject == null }
|
||||||
.filter { it !in DESCRIPTIONS || description == null }
|
.filter { it !in DESCRIPTIONS || description == null }
|
||||||
@@ -107,7 +115,9 @@ fun ToolInputView(tool: String, input: String, modifier: Modifier = Modifier) {
|
|||||||
fontFamily = FontFamily.Monospace,
|
fontFamily = FontFamily.Monospace,
|
||||||
softWrap = false,
|
softWrap = false,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.padding(top = 4.dp).fillMaxWidth().horizontalScroll(rememberScrollState()),
|
Modifier.padding(top = 4.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.horizontalScroll(rememberScrollState()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
parsed.rest.forEach {
|
parsed.rest.forEach {
|
||||||
@@ -136,14 +146,21 @@ private fun highlighted(code: String, language: SyntaxLanguage?): AnnotatedStrin
|
|||||||
return remember(code, language, theme, plain) {
|
return remember(code, language, theme, plain) {
|
||||||
if (language == null) return@remember AnnotatedString(code)
|
if (language == null) return@remember AnnotatedString(code)
|
||||||
val marks =
|
val marks =
|
||||||
Highlights.Builder(code = code, language = language, theme = theme).build().getHighlights()
|
Highlights.Builder(code = code, language = language, theme = theme)
|
||||||
|
.build()
|
||||||
|
.getHighlights()
|
||||||
buildAnnotatedString {
|
buildAnnotatedString {
|
||||||
append(code)
|
append(code)
|
||||||
marks.forEach { mark ->
|
marks.forEach { mark ->
|
||||||
when (mark) {
|
when (mark) {
|
||||||
is ColorHighlight ->
|
is ColorHighlight ->
|
||||||
addStyle(
|
addStyle(
|
||||||
SpanStyle(color = androidx.compose.ui.graphics.Color(mark.rgb or 0xFF000000.toInt())),
|
SpanStyle(
|
||||||
|
color =
|
||||||
|
androidx.compose.ui.graphics.Color(
|
||||||
|
mark.rgb or 0xFF000000.toInt()
|
||||||
|
)
|
||||||
|
),
|
||||||
mark.location.start,
|
mark.location.start,
|
||||||
mark.location.end,
|
mark.location.end,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in new issue
Block a user