The AnimatedContent gate on the composer's send button required
inputFieldValue.text.isNotBlank() in addition to canSend, so attaching an
image or file with an empty input hid the button entirely — the attachment
could never be sent on its own. ChatInputPolicy.canSend already allowed
attachment-only sends and ChatViewModel.sendMessage already accepted blank
text with pending attachments, but the button never rendered to trigger it.
Drop the stale text gate so the send affordance tracks canSend (non-empty
text OR pending attachments, plus connection). The backend handles
image-only turns natively: prompt.submit with empty text + queued images
routes through build_native_content_parts, which wraps the pixels with an
[Image attached at: ...] hint.
Add a Compose UI regression test asserting the send button is displayed
with a pending image attachment and an empty input.
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
* feat(chat): compact sticky task progress chip (#942)
Re-add a glanceable progress strip above the chat timeline while work is
active. Distinct from the inline TodoTaskCard (permanent timeline record):
this chip is the temporary "what is happening right now" surface.
- Shows only while todos are incomplete or a subagent is running.
- Bounds to the same hydrated state.todos / subagentIndicators the card
uses -- NOT the old broken state.todos gate removed in #811.
- Label: "Tasks 2/5 · <in-progress task> · N agent" (agent count when relevant).
- Tap opens SubagentInspectionSheet + resumes bottom-follow.
- Auto-hides when all work completes/cancels; rebuilds on session resume.
- i18n strings added (en/zh/ko); ktlint clean; compileDebugKotlin + unit
tests green.
* refactor(chat): drop inline TodoTaskCard, keep single sticky progress chip
Per follow-up to #942: the inline todo card in the chat view is removed. The
sticky TaskProgressChip above the timeline is now the only glanceable surface;
the tool message itself remains the permanent record.
- Remove inline TodoTaskCard early-return in ToolBubble.kt (todo tool output
now renders like any other tool bubble).
- Delete TodoTaskCard.kt + TodoTaskCardTest.kt (SecurityRiskChip lives in
ToolBubble.kt, so risk display is unaffected).
- Redesign chip: replace the ElectricBolt icon with a real
CircularProgressIndicator (no emoji), keep ChevronRight affordance.
- Label handles the no-todos case (subagents only): 'N agent running'.
- Drop dead todo_task_count string; add task_progress_agent_running (en/zh/ko).
ktlint clean; compileDebugKotlin + chat unit tests green.
* chore: clear pre-existing compile warnings
- HermesWsClient.kt: drop unnecessary safe call on non-null ResponseBody
(resp.body.string()) and the now-dead 'body != null' guard it implied.
- SessionsScreen.kt: replace deprecated Icons.Filled.CallSplit with the
AutoMirrored variant (match existing Send usage in this file).
ktlint clean; compileDebugKotlin SUCCESSFUL with no warnings.
* chore(chat): replace emoji status markers with Material icons in inspection sheet
The sticky progress chip opens SubagentInspectionSheet on tap; that sheet used
emoji Text glyphs for status. Replaced with real Material icons:
- Todo rows: completed -> CheckCircle (success), in-progress -> Autorenew
(primary), cancelled -> Cancel (warning), pending -> RadioButtonUnchecked
(muted).
- Subagent rows: complete -> CheckCircle (success), failed -> Cancel (error),
running -> Autorenew (primary).
- Section headers: ElectricBolt -> FormatListBulleted (agent plan) + Groups
(subagents).
Icons tinted via LocalHermesStatusColors. No emoji remain in the chip's tap
surface. ktlint clean; compileDebugKotlin SUCCESSFUL.
* fix(lint): drop orphaned todo_task_count translation in ko
Removing the string from the default (en) locale left a stale Korean
translation, which tripped Lint's ExtraTranslation error and aborted
lintDebug. Removing the orphan so default + translations stay in sync.
android-lint now BUILD SUCCESSFUL (0 errors).
* fix(chat): address #943 review - progress count, resume hydration, chip tests, plurals
- TaskProgressChip: current-task number is now the first unfinished,
non-cancelled todo's 1-based position (active index + 1), so a completed
task 1 + in-progress task 2 reads as 2/5 instead of 1/5 (review point 2).
- Deterministic resume hydration: hydrate todos directly from cached Room
messages and from the merged REST transcript inside ChatViewModel load
paths, in addition to the WS reducer, so a resumed session rebuilds the
chip without waiting for a follow-up event (review point 3).
- Extracted computeChipDisplay / shouldShowProgressChip pure functions and
added TaskProgressChipTest covering active-task progress, completed/cancelled
hide, subagent-only state, and transcript-resume hydration (review point 4).
- agent count strings are now Android plurals (task_progress_agents /
task_progress_agent_running) in en/zh/ko to avoid '2 agent' (review point 5).
Co-Authored-By trailer intentionally omitted per repo convention.
* fix(chat): derive chip number and content from the same selected task
computeChipDisplay selected the task number from the first unfinished/
non-cancelled todo but the content independently from the first in_progress
todo. With a pending task 1 + in_progress task 2 this produced 'Tasks 1/N ·
<task2 content>'. Now both values come from one selected item (prefer
in_progress, fall back to first pending/non-terminal).
Adds two focused tests: same-item selection, and pending-only fallback.