11 files changed
+19
-190
No files matched your search
-2
@@ -133,8 +133,6 @@ class FullBleedChatAnchorTest {
|
|||||||
isLoadingOlder = loading.value,
|
isLoadingOlder = loading.value,
|
||||||
listState = listState,
|
listState = listState,
|
||||||
scrollController = controller,
|
scrollController = controller,
|
||||||
lastAnimatedMessageId = null,
|
|
||||||
onLastAnimatedMessageIdChange = {},
|
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
pagingSessionId = session.value,
|
pagingSessionId = session.value,
|
||||||
clarifyRequest = if (tailKey != null) ClarifyUi("Choose an option", listOf("Yes")) else null,
|
clarifyRequest = if (tailKey != null) ClarifyUi("Choose an option", listOf("Yes")) else null,
|
||||||
|
|||||||
-2
@@ -94,8 +94,6 @@ class FullBleedChatListTest {
|
|||||||
listState = listState,
|
listState = listState,
|
||||||
scope = CoroutineScope(Dispatchers.Main.immediate),
|
scope = CoroutineScope(Dispatchers.Main.immediate),
|
||||||
),
|
),
|
||||||
lastAnimatedMessageId = null,
|
|
||||||
onLastAnimatedMessageIdChange = {},
|
|
||||||
viewModel = mockk<ChatViewModel>(relaxed = true),
|
viewModel = mockk<ChatViewModel>(relaxed = true),
|
||||||
clarifyRequest =
|
clarifyRequest =
|
||||||
if (clarify) {
|
if (clarify) {
|
||||||
|
|||||||
@@ -272,15 +272,3 @@ class ChatMediaDelegate(
|
|||||||
uiState.update { it.copy(openError = null) }
|
uiState.update { it.copy(openError = null) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun sameMessages(
|
|
||||||
left: List<ChatMessage>,
|
|
||||||
right: List<ChatMessage>,
|
|
||||||
): Boolean =
|
|
||||||
left.size == right.size &&
|
|
||||||
left.zip(right).all { (a, b) ->
|
|
||||||
a.id == b.id &&
|
|
||||||
a.role == b.role &&
|
|
||||||
a.content == b.content &&
|
|
||||||
a.reasoningText == b.reasoningText
|
|
||||||
}
|
|
||||||
@@ -375,7 +375,6 @@ fun ChatScreen(
|
|||||||
viewModel.consumeComposerTextRestore()
|
viewModel.consumeComposerTextRestore()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var lastAnimatedMessageId by rememberSaveable { mutableStateOf<String?>(null) }
|
|
||||||
var showReloginDialog by rememberSaveable { mutableStateOf(false) }
|
var showReloginDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
var showSubagentInspectionSheet by rememberSaveable { mutableStateOf(false) }
|
var showSubagentInspectionSheet by rememberSaveable { mutableStateOf(false) }
|
||||||
LaunchedEffect(showSubagentInspectionSheet) {
|
LaunchedEffect(showSubagentInspectionSheet) {
|
||||||
@@ -783,8 +782,6 @@ fun ChatScreen(
|
|||||||
},
|
},
|
||||||
listState = listState,
|
listState = listState,
|
||||||
scrollController = scrollController,
|
scrollController = scrollController,
|
||||||
lastAnimatedMessageId = lastAnimatedMessageId,
|
|
||||||
onLastAnimatedMessageIdChange = { lastAnimatedMessageId = it },
|
|
||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
clarifyRequest = state.clarifyRequest.takeUnless { timelineState.isHistorical },
|
clarifyRequest = state.clarifyRequest.takeUnless { timelineState.isHistorical },
|
||||||
onRespondClarify = viewModel::respondToClarify,
|
onRespondClarify = viewModel::respondToClarify,
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ import com.m57.hermescontrol.ui.chat.markdown.splitInlineMath
|
|||||||
import com.m57.hermescontrol.util.BidiUtils
|
import com.m57.hermescontrol.util.BidiUtils
|
||||||
|
|
||||||
private val URL_PATTERN = Regex("""https?://[^\s)>\[\]"'‘’]+""")
|
private val URL_PATTERN = Regex("""https?://[^\s)>\[\]"'‘’]+""")
|
||||||
private val TABLE_COL_WIDTH = 160.dp
|
|
||||||
|
|
||||||
private fun bidiTextDirection(isRtl: Boolean): TextDirection = if (isRtl) TextDirection.Rtl else TextDirection.Ltr
|
private fun bidiTextDirection(isRtl: Boolean): TextDirection = if (isRtl) TextDirection.Rtl else TextDirection.Ltr
|
||||||
|
|
||||||
@@ -359,13 +358,13 @@ fun MarkdownText(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = 4.dp),
|
.padding(vertical = 4.dp),
|
||||||
) {
|
) {
|
||||||
com.m57.hermescontrol.ui.chat.components.InlineVideoPlayer(
|
com.m57.hermescontrol.ui.chat.components.InlineMediaPlayer(
|
||||||
videoUri = resolvedUri,
|
uri = resolvedUri,
|
||||||
onFullScreenClick = { showVideoDialog = true },
|
onFullScreenClick = { showVideoDialog = true },
|
||||||
)
|
)
|
||||||
if (showVideoDialog) {
|
if (showVideoDialog) {
|
||||||
com.m57.hermescontrol.ui.chat.components.VideoViewerDialog(
|
com.m57.hermescontrol.ui.chat.components.MediaViewerDialog(
|
||||||
videoUri = resolvedUri,
|
mediaUri = resolvedUri,
|
||||||
onDismissRequest = { showVideoDialog = false },
|
onDismissRequest = { showVideoDialog = false },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,10 +40,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||||||
*/
|
*/
|
||||||
data class ToolDisplayConfig(
|
data class ToolDisplayConfig(
|
||||||
val name: String,
|
val name: String,
|
||||||
/** Key into the `args` dict for the one-line summary (e.g. "command" for terminal). */
|
|
||||||
val summaryArgKey: String? = null,
|
|
||||||
/** Prefix for the summary line (e.g. "$ " for terminal). Emoji-free. */
|
|
||||||
val summaryPrefix: String = "",
|
|
||||||
/** Material icon for this tool type. */
|
/** Material icon for this tool type. */
|
||||||
val icon: ImageVector = Icons.Filled.Build,
|
val icon: ImageVector = Icons.Filled.Build,
|
||||||
)
|
)
|
||||||
@@ -55,285 +51,238 @@ object ToolSchemaRegistry {
|
|||||||
"terminal" to
|
"terminal" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "terminal",
|
name = "terminal",
|
||||||
summaryArgKey = "command",
|
|
||||||
summaryPrefix = "$ ",
|
|
||||||
icon = Icons.Filled.Terminal,
|
icon = Icons.Filled.Terminal,
|
||||||
),
|
),
|
||||||
"read_file" to
|
"read_file" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "read_file",
|
name = "read_file",
|
||||||
summaryArgKey = "path",
|
|
||||||
icon = Icons.Filled.Description,
|
icon = Icons.Filled.Description,
|
||||||
),
|
),
|
||||||
"write_file" to
|
"write_file" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "write_file",
|
name = "write_file",
|
||||||
summaryArgKey = "path",
|
|
||||||
icon = Icons.Filled.Edit,
|
icon = Icons.Filled.Edit,
|
||||||
),
|
),
|
||||||
"patch" to
|
"patch" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "patch",
|
name = "patch",
|
||||||
summaryArgKey = "path",
|
|
||||||
icon = Icons.Filled.Build,
|
icon = Icons.Filled.Build,
|
||||||
),
|
),
|
||||||
"search_files" to
|
"search_files" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "search_files",
|
name = "search_files",
|
||||||
summaryArgKey = "pattern",
|
|
||||||
icon = Icons.Filled.Search,
|
icon = Icons.Filled.Search,
|
||||||
),
|
),
|
||||||
"web_search" to
|
"web_search" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "web_search",
|
name = "web_search",
|
||||||
summaryArgKey = "query",
|
|
||||||
icon = Icons.Filled.Public,
|
icon = Icons.Filled.Public,
|
||||||
),
|
),
|
||||||
"browser_navigate" to
|
"browser_navigate" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_navigate",
|
name = "browser_navigate",
|
||||||
summaryArgKey = "url",
|
|
||||||
icon = Icons.Filled.Language,
|
icon = Icons.Filled.Language,
|
||||||
),
|
),
|
||||||
"browser_click" to
|
"browser_click" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_click",
|
name = "browser_click",
|
||||||
summaryArgKey = "ref",
|
|
||||||
icon = Icons.Filled.TouchApp,
|
icon = Icons.Filled.TouchApp,
|
||||||
),
|
),
|
||||||
"browser_snapshot" to
|
"browser_snapshot" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_snapshot",
|
name = "browser_snapshot",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Photo,
|
icon = Icons.Filled.Photo,
|
||||||
),
|
),
|
||||||
"clarify" to
|
"clarify" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "clarify",
|
name = "clarify",
|
||||||
summaryArgKey = "question",
|
|
||||||
icon = Icons.Filled.ChatBubble,
|
icon = Icons.Filled.ChatBubble,
|
||||||
),
|
),
|
||||||
"delegate_task" to
|
"delegate_task" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "delegate_task",
|
name = "delegate_task",
|
||||||
summaryArgKey = "goal",
|
|
||||||
icon = Icons.Filled.AccountTree,
|
icon = Icons.Filled.AccountTree,
|
||||||
),
|
),
|
||||||
"execute_code" to
|
"execute_code" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "execute_code",
|
name = "execute_code",
|
||||||
summaryArgKey = "code",
|
|
||||||
icon = Icons.Filled.PlayArrow,
|
icon = Icons.Filled.PlayArrow,
|
||||||
),
|
),
|
||||||
"todo" to
|
"todo" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "todo",
|
name = "todo",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Checklist,
|
icon = Icons.Filled.Checklist,
|
||||||
),
|
),
|
||||||
"todo_list" to
|
"todo_list" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "todo_list",
|
name = "todo_list",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Checklist,
|
icon = Icons.Filled.Checklist,
|
||||||
),
|
),
|
||||||
"fact_store" to
|
"fact_store" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "fact_store",
|
name = "fact_store",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Psychology,
|
icon = Icons.Filled.Psychology,
|
||||||
),
|
),
|
||||||
"session_search" to
|
"session_search" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "session_search",
|
name = "session_search",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Search,
|
icon = Icons.Filled.Search,
|
||||||
),
|
),
|
||||||
// ── Action-based tools ──
|
// ── Action-based tools ──
|
||||||
"cronjob" to
|
"cronjob" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "cronjob",
|
name = "cronjob",
|
||||||
summaryArgKey = "action",
|
|
||||||
icon = Icons.Filled.Schedule,
|
icon = Icons.Filled.Schedule,
|
||||||
),
|
),
|
||||||
"cronjob_manage" to
|
"cronjob_manage" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "cronjob_manage",
|
name = "cronjob_manage",
|
||||||
summaryArgKey = "action",
|
|
||||||
icon = Icons.Filled.Schedule,
|
icon = Icons.Filled.Schedule,
|
||||||
),
|
),
|
||||||
"memory" to
|
"memory" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "memory",
|
name = "memory",
|
||||||
summaryArgKey = "action",
|
|
||||||
icon = Icons.Filled.Memory,
|
icon = Icons.Filled.Memory,
|
||||||
),
|
),
|
||||||
"fact_feedback" to
|
"fact_feedback" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "fact_feedback",
|
name = "fact_feedback",
|
||||||
summaryArgKey = "action",
|
|
||||||
icon = Icons.Filled.ThumbUp,
|
icon = Icons.Filled.ThumbUp,
|
||||||
),
|
),
|
||||||
"process" to
|
"process" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "process",
|
name = "process",
|
||||||
summaryArgKey = "action",
|
|
||||||
icon = Icons.Filled.Settings,
|
icon = Icons.Filled.Settings,
|
||||||
),
|
),
|
||||||
"process_manage" to
|
"process_manage" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "process_manage",
|
name = "process_manage",
|
||||||
summaryArgKey = "action",
|
|
||||||
icon = Icons.Filled.Settings,
|
icon = Icons.Filled.Settings,
|
||||||
),
|
),
|
||||||
"skill_manage" to
|
"skill_manage" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "skill_manage",
|
name = "skill_manage",
|
||||||
summaryArgKey = "action",
|
|
||||||
icon = Icons.Filled.Build,
|
icon = Icons.Filled.Build,
|
||||||
),
|
),
|
||||||
// ── Web / Browser tools ──
|
// ── Web / Browser tools ──
|
||||||
"web_extract" to
|
"web_extract" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "web_extract",
|
name = "web_extract",
|
||||||
summaryArgKey = "urls",
|
|
||||||
icon = Icons.Filled.Language,
|
icon = Icons.Filled.Language,
|
||||||
),
|
),
|
||||||
"browser_type" to
|
"browser_type" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_type",
|
name = "browser_type",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Keyboard,
|
icon = Icons.Filled.Keyboard,
|
||||||
),
|
),
|
||||||
"browser_scroll" to
|
"browser_scroll" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_scroll",
|
name = "browser_scroll",
|
||||||
summaryArgKey = "direction",
|
|
||||||
icon = Icons.Filled.TouchApp,
|
icon = Icons.Filled.TouchApp,
|
||||||
),
|
),
|
||||||
"browser_back" to
|
"browser_back" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_back",
|
name = "browser_back",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Language,
|
icon = Icons.Filled.Language,
|
||||||
),
|
),
|
||||||
"browser_press" to
|
"browser_press" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_press",
|
name = "browser_press",
|
||||||
summaryArgKey = "key",
|
|
||||||
icon = Icons.Filled.Keyboard,
|
icon = Icons.Filled.Keyboard,
|
||||||
),
|
),
|
||||||
"browser_get_images" to
|
"browser_get_images" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_get_images",
|
name = "browser_get_images",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Image,
|
icon = Icons.Filled.Image,
|
||||||
),
|
),
|
||||||
"browser_vision" to
|
"browser_vision" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_vision",
|
name = "browser_vision",
|
||||||
summaryArgKey = "question",
|
|
||||||
icon = Icons.Filled.Visibility,
|
icon = Icons.Filled.Visibility,
|
||||||
),
|
),
|
||||||
"browser_console" to
|
"browser_console" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_console",
|
name = "browser_console",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Terminal,
|
icon = Icons.Filled.Terminal,
|
||||||
),
|
),
|
||||||
"browser_cdp" to
|
"browser_cdp" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_cdp",
|
name = "browser_cdp",
|
||||||
summaryArgKey = "action",
|
|
||||||
icon = Icons.Filled.Build,
|
icon = Icons.Filled.Build,
|
||||||
),
|
),
|
||||||
"browser_dialog" to
|
"browser_dialog" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "browser_dialog",
|
name = "browser_dialog",
|
||||||
summaryArgKey = "action",
|
|
||||||
icon = Icons.Filled.ChatBubble,
|
icon = Icons.Filled.ChatBubble,
|
||||||
),
|
),
|
||||||
// ── Media / Vision tools ──
|
// ── Media / Vision tools ──
|
||||||
"vision_analyze" to
|
"vision_analyze" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "vision_analyze",
|
name = "vision_analyze",
|
||||||
summaryArgKey = "image_url",
|
|
||||||
icon = Icons.Filled.Visibility,
|
icon = Icons.Filled.Visibility,
|
||||||
),
|
),
|
||||||
"text_to_speech" to
|
"text_to_speech" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "text_to_speech",
|
name = "text_to_speech",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.AutoMirrored.Filled.VolumeUp,
|
icon = Icons.AutoMirrored.Filled.VolumeUp,
|
||||||
),
|
),
|
||||||
"video_generate" to
|
"video_generate" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "video_generate",
|
name = "video_generate",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Movie,
|
icon = Icons.Filled.Movie,
|
||||||
),
|
),
|
||||||
"image_generate" to
|
"image_generate" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "image_generate",
|
name = "image_generate",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.Filled.Image,
|
icon = Icons.Filled.Image,
|
||||||
),
|
),
|
||||||
// ── Social / Messaging tools ──
|
// ── Social / Messaging tools ──
|
||||||
"x_search" to
|
"x_search" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "x_search",
|
name = "x_search",
|
||||||
summaryArgKey = "query",
|
|
||||||
icon = Icons.Filled.Forum,
|
icon = Icons.Filled.Forum,
|
||||||
),
|
),
|
||||||
"send_message" to
|
"send_message" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "send_message",
|
name = "send_message",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.AutoMirrored.Filled.Send,
|
icon = Icons.AutoMirrored.Filled.Send,
|
||||||
),
|
),
|
||||||
// ── Skills tools ──
|
// ── Skills tools ──
|
||||||
"skills_list" to
|
"skills_list" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "skills_list",
|
name = "skills_list",
|
||||||
summaryArgKey = null,
|
|
||||||
icon = Icons.AutoMirrored.Filled.MenuBook,
|
icon = Icons.AutoMirrored.Filled.MenuBook,
|
||||||
),
|
),
|
||||||
"skill_view" to
|
"skill_view" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "skill_view",
|
name = "skill_view",
|
||||||
summaryArgKey = "name",
|
|
||||||
icon = Icons.Filled.AutoStories,
|
icon = Icons.Filled.AutoStories,
|
||||||
),
|
),
|
||||||
// ── On-demand tools ──
|
// ── On-demand tools ──
|
||||||
"tool_search" to
|
"tool_search" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "tool_search",
|
name = "tool_search",
|
||||||
summaryArgKey = "query",
|
|
||||||
icon = Icons.Filled.Search,
|
icon = Icons.Filled.Search,
|
||||||
),
|
),
|
||||||
"tool_describe" to
|
"tool_describe" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "tool_describe",
|
name = "tool_describe",
|
||||||
summaryArgKey = "name",
|
|
||||||
icon = Icons.Filled.AutoStories,
|
icon = Icons.Filled.AutoStories,
|
||||||
),
|
),
|
||||||
"tool_call" to
|
"tool_call" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "tool_call",
|
name = "tool_call",
|
||||||
summaryArgKey = "name",
|
|
||||||
icon = Icons.Filled.Build,
|
icon = Icons.Filled.Build,
|
||||||
),
|
),
|
||||||
// ── Misc ──
|
// ── Misc ──
|
||||||
"read_terminal" to
|
"read_terminal" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "read_terminal",
|
name = "read_terminal",
|
||||||
summaryArgKey = "session_id",
|
|
||||||
icon = Icons.Filled.Computer,
|
icon = Icons.Filled.Computer,
|
||||||
),
|
),
|
||||||
"computer_use" to
|
"computer_use" to
|
||||||
ToolDisplayConfig(
|
ToolDisplayConfig(
|
||||||
name = "computer_use",
|
name = "computer_use",
|
||||||
summaryArgKey = "action",
|
|
||||||
icon = Icons.Filled.Computer,
|
icon = Icons.Filled.Computer,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -209,3 +209,18 @@ private fun InlineVideoPreview(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Formats milliseconds into `M:SS` or `H:MM:SS` */
|
||||||
|
fun formatMediaDuration(durationMs: Long): String {
|
||||||
|
if (durationMs <= 0) return "0:00"
|
||||||
|
val totalSeconds = durationMs / 1000
|
||||||
|
val seconds = totalSeconds % 60
|
||||||
|
val minutes = (totalSeconds / 60) % 60
|
||||||
|
val hours = totalSeconds / 3600
|
||||||
|
|
||||||
|
return if (hours > 0) {
|
||||||
|
"%d:%02d:%02d".format(hours, minutes, seconds)
|
||||||
|
} else {
|
||||||
|
"%d:%02d".format(minutes, seconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.m57.hermescontrol.ui.chat.components
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Backwards-compatible wrapper delegating to [InlineMediaPlayer].
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
fun InlineVideoPlayer(
|
|
||||||
videoUri: String,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
onFullScreenClick: () -> Unit = {},
|
|
||||||
) {
|
|
||||||
InlineMediaPlayer(
|
|
||||||
uri = videoUri,
|
|
||||||
modifier = modifier,
|
|
||||||
onFullScreenClick = onFullScreenClick,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats milliseconds into `M:SS` or `H:MM:SS` */
|
|
||||||
fun formatMediaDuration(durationMs: Long): String {
|
|
||||||
if (durationMs <= 0) return "0:00"
|
|
||||||
val totalSeconds = durationMs / 1000
|
|
||||||
val seconds = totalSeconds % 60
|
|
||||||
val minutes = (totalSeconds / 60) % 60
|
|
||||||
val hours = totalSeconds / 3600
|
|
||||||
|
|
||||||
return if (hours > 0) {
|
|
||||||
"%d:%02d:%02d".format(hours, minutes, seconds)
|
|
||||||
} else {
|
|
||||||
"%d:%02d".format(minutes, seconds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -90,7 +90,6 @@ import com.m57.hermescontrol.theme.CodeTerminalMuted
|
|||||||
import com.m57.hermescontrol.theme.CodeTerminalText
|
import com.m57.hermescontrol.theme.CodeTerminalText
|
||||||
import com.m57.hermescontrol.ui.chat.ClarifyUi
|
import com.m57.hermescontrol.ui.chat.ClarifyUi
|
||||||
import com.m57.hermescontrol.ui.chat.MarkdownText
|
import com.m57.hermescontrol.ui.chat.MarkdownText
|
||||||
import com.m57.hermescontrol.ui.chat.SubagentIndicator
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -400,14 +399,6 @@ private data class TokenPattern(
|
|||||||
val color: Color,
|
val color: Color,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun copyToClipboard(
|
|
||||||
context: Context,
|
|
||||||
text: String,
|
|
||||||
) {
|
|
||||||
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return
|
|
||||||
clipboard.setPrimaryClip(ClipData.newPlainText(null, text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── ClarifyBubble ─────────────────────────────────────────────────────────
|
// ── ClarifyBubble ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -670,58 +661,6 @@ fun ClarifyBubble(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── SubagentCard ──────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inline card showing subagent task progress.
|
|
||||||
*
|
|
||||||
* - While running: [CircularProgressIndicator] + goal text on [tertiaryContainer].
|
|
||||||
* - On completion: ✅ checkmark + summary text.
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
fun SubagentCard(
|
|
||||||
indicator: SubagentIndicator,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val isComplete = indicator.type == "subagent.complete"
|
|
||||||
Surface(
|
|
||||||
modifier = modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 2.dp).testTag("subagent_card"),
|
|
||||||
shape = RoundedCornerShape(12.dp),
|
|
||||||
color = MaterialTheme.colorScheme.tertiaryContainer,
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
if (isComplete) {
|
|
||||||
Text(text = "✅", fontSize = 14.sp)
|
|
||||||
} else {
|
|
||||||
CircularProgressIndicator(
|
|
||||||
modifier = Modifier.size(16.dp),
|
|
||||||
strokeWidth = 2.dp,
|
|
||||||
color = MaterialTheme.colorScheme.onTertiaryContainer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Spacer(Modifier.width(8.dp))
|
|
||||||
val displayText =
|
|
||||||
if (isComplete && !indicator.summary.isNullOrBlank()) {
|
|
||||||
indicator.summary
|
|
||||||
} else if (!indicator.goal.isNullOrBlank()) {
|
|
||||||
indicator.goal
|
|
||||||
} else {
|
|
||||||
"Subagent task"
|
|
||||||
}
|
|
||||||
Text(
|
|
||||||
text = displayText,
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.onTertiaryContainer,
|
|
||||||
maxLines = 2,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── TypingIndicator ───────────────────────────────────────────────────────
|
// ── TypingIndicator ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.m57.hermescontrol.ui.chat.components
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Backwards-compatible wrapper delegating to [MediaViewerDialog].
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
fun VideoViewerDialog(
|
|
||||||
videoUri: String,
|
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
) {
|
|
||||||
MediaViewerDialog(
|
|
||||||
mediaUri = videoUri,
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -93,8 +93,6 @@ fun FullBleedChatList(
|
|||||||
isLoadingOlder: Boolean,
|
isLoadingOlder: Boolean,
|
||||||
listState: androidx.compose.foundation.lazy.LazyListState,
|
listState: androidx.compose.foundation.lazy.LazyListState,
|
||||||
scrollController: ChatScrollController,
|
scrollController: ChatScrollController,
|
||||||
lastAnimatedMessageId: String?,
|
|
||||||
onLastAnimatedMessageIdChange: (String?) -> Unit,
|
|
||||||
viewModel: ChatViewModel,
|
viewModel: ChatViewModel,
|
||||||
clarifyRequest: ClarifyUi? = null,
|
clarifyRequest: ClarifyUi? = null,
|
||||||
onRespondClarify: ((String) -> Unit)? = null,
|
onRespondClarify: ((String) -> Unit)? = null,
|
||||||
|
|||||||
Reference in new issue
Block a user