Commit Graph
287 Commits
Author SHA1 Message Date
Hy4ri 1edd5449c9 refactor(chat): modularize update section and media launchers
Extract ChatAppUpdateSection and rememberChatMediaLaunchers to
components to decouple STT and update dialogs from ChatScreen.
2026-09-11 19:38:29 +03:00
Hy4ri 70f331b931 refactor(markdown): extract MarkdownModels and MarkdownParser
Split AST block models and zero-Compose Markdown block parser into
ui/chat/markdown/ package. Wire into MarkdownText.
2026-09-11 18:19:47 +03:00
Hy4ri a83f4725a2 refactor(chat): extract ChatTranscriptMerge helpers from ChatViewModel
Extract tool result canonicalization, logical message equality,
attachment ref stripping, and transcript merge logic from ChatViewModel.
2026-09-11 18:12:58 +03:00
Hy4ri 7b198fa6d6 fix(chat): restore pending approval guard, tighten encapsulation, drop dormant tracker 2026-09-11 00:21:06 +03:00
Hy4ri 0af4782da1 refactor(chat): wire extracted delegates into chat viewmodel 2026-09-11 00:04:09 +03:00
Hy4ri 3e012dbf31 refactor(chat): extract rpc request tracker with tests 2026-09-11 00:03:54 +03:00
Hy4ri 280a03ea23 refactor(chat): extract model switch delegate with tests 2026-09-11 00:03:46 +03:00
Hy4ri 9d35bf7cdc refactor(chat): extract credential prompts delegate with tests 2026-09-11 00:03:42 +03:00
Hy4ri 54dbb99a3d refactor(chat): extract approvals delegate with tests 2026-09-11 00:03:36 +03:00
Hy4ri 10f7e9fa3c fix(tool): satisfy ktlint formatting 2026-09-10 22:48:43 +03:00
Hy4ri 91566dfdd7 refactor(tool): split tool display engine 2026-09-10 22:23:41 +03:00
M57 850d01ca89 fix(update): bind live download state to dialog and show real-time progress 2026-09-06 20:59:39 +03:00
M57 ad34a51846 Merge branch 'dev' into fix/bounded-attachment-dispatch 2026-09-05 16:34:36 +03:00
M57 aff1b7ed3a fix(chat): use runtime session ID in approval RPCs to prevent 4001 session not found 2026-09-05 13:15:08 +03:00
AlexDev_ 89a14eb765 fix(chat): bound attachment encoding and recover rejected sends 2026-09-05 12:04:03 +02:00
M57 5ce01e429b fix(bidi): isolate leading and inline English words in RTL paragraphs
- In parseInlineSource, wrap consecutive Latin/LTR tokens in LTR isolates (LRI...PDI)
  when rendering inside an RTL block, preventing leading English terms like "Okay"
  from flipping paragraph layout direction and causing BiDi line shuffling.
- Switch textDirection from TextDirection.ContentOrRtl to explicit TextDirection.Rtl
  when isRtl is true across MarkdownText, ChatBubble, and GroupChatScreen, preventing
  first-strong character fallbacks from overriding resolved paragraph direction.
2026-09-04 23:22:22 +03:00
M57 bd0b4c2d55 feat(chat): full approval.request support (#1057) 2026-09-03 22:33:15 +03:00
M57 077ad1fa81 feat(chat): full sudo.request support with expire, cancel-unblock, secret fields (#1056) 2026-09-03 22:08:07 +03:00
M57 6d2362ba8a feat(chat): support multi-select, batch multi-question clarify prompts, and expiry (fixes #1045) (#1055) 2026-09-03 21:38:57 +03:00
M57 8c2d4216a7 feat(chat): display completion timestamp beside copy button on assistant message (#1053) 2026-09-03 20:21:43 +03:00
M57 5a79da96f7 fix(chat): restrict @ bot mention suggestions strictly to group chat (#1052) 2026-09-03 19:46:38 +03:00
M57 b3439d87c9 feat(chat): support /undo command with transcript reload and composer prefill (#1051) 2026-09-03 19:04:43 +03:00
M57 efe6fe81cb fix(chat): support confirmation dialog when switching to expensive or data-policy models (#1050) 2026-09-03 16:56:08 +03:00
M57 cd8c27e62b feat(chat): support dynamic BiDi Arabic input and isolated mixed text in Markdown (#1047) 2026-09-03 13:06:03 +03:00
M57 fa17c95296 fix(chat): support batch clarify.request payload and route question_id on respond (fixes #1045) (#1046) 2026-09-03 12:50:13 +03:00
M57 dbd7008ee3 feat(chat): support non-Google speech-to-text providers (fixes #1038) (#1042) 2026-09-02 12:31:42 +03:00
M57 2d0b6ae1a9 fix(history): resolve session resume profile scoping and history pagination (#1040) 2026-09-02 00:01:19 +03:00
M57andM57 11dd1eff92 feat(chat): live subagent steering and cancellation action sheet (closes #1030) (#1034)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-09-01 18:19:00 +03:00
M57andM57 ceb4813218 feat(chat): support hierarchical nested subtasks in Todos via parent property (#1018) (#1028)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-31 22:09:15 +03:00
M57andM57 53c8eca714 feat: adopt side-questions via prompt.btw and /btw command in Chat (fixes #1015) (#1026)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-31 15:54:09 +03:00
M57andM57 cfe3e736fd chore: upgrade ktlint to 1.8.0 and format codebase (#1024)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-31 12:27:40 +03:00
M57 cdcecb9734 feat(profiles): add local on-device profile hiding and filtering 2026-08-30 20:51:06 +03:00
M57andM57 5bc6e5f855 feat(settings): in-app chat font size setting (#1004) (#1005)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-29 11:36:56 +03:00
M57andM57 db3f8c736e feat(markdown): support variable-length code fences and nested codeblocks (#1003)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-28 20:27:17 +03:00
M57andM57 14fde03083 feat(image): migrate to Coil 3.6.0 with OkHttp network fetcher (#996)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-27 15:13:18 +03:00
AlexDev_ 0ae5ed4db2 feat(chat): stabilize multi-image attachment picker (#986) 2026-08-27 10:50:55 +03:00
AlexDev_ c7d7abf4a1 fix(chat): prevent timeline prefetch ANRs (#985) 2026-08-27 10:47:04 +03:00
AlexDev_andM57 ce8fdc45bd fix(notification): open tapped chat at bottom (#984)
Co-authored-by: M57 <41645758+Hy4ri@users.noreply.github.com>
2026-08-27 10:41:08 +03:00
M57 b422ead921 feat(chat): add @bot mention autocomplete to ChatComposer (#980) 2026-08-27 01:51:10 +03:00
M57 4e666b49a8 feat(chat): intercept /new in canonical Bot Chat sessions to /compact (#979) 2026-08-27 01:50:43 +03:00
M57andM57 1f1ebf3597 feat(update): redesign in-app updater with smart cooldown, release notes, and in-place dialog (#971)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-26 07:42:41 +03:00
M57andM57 460472f631 fix(chat): queue prompts while session creation is in flight (issue #969) (#970)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-26 06:35:44 +03:00
M57andM57 14b9c1d1d3 fix(chat): support multi-level nested bullet, task, and ordered markdown lists (#965) (#968)
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-25 17:41:47 +03:00
M57andM57 5fd732be9b fix(chat): preserve ordered list numbering in loose lists (#959) (#963)
ORDERED_RE only captured item text and discarded the source number, and the
parse loop broke on the first blank line — so loose numbered lists (blank lines
between items) re-indexed every item to 1.

- capture the prefix number and emit it as MdBlock.Ordered.index
- treat a blank line as list-internal when the next non-blank line is another
  ordered item, so loose lists stay one ordered list
- keep sequential numbering for tight lists (1,2,3) and honor custom starts

add regression tests in MarkdownTextFeatureTest (loose, custom-start, tight).

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-24 00:16:27 +03:00
M57andM57 7540f7228a fix(chat): show send button for attachment-only sends (#956) (#956)
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>
2026-08-22 21:58:53 +03:00
M57andM57 69578e7818 refactor: extract media + server-message-mapping from ChatViewModel (#955)
* refactor: extract ChatMediaDelegate from ChatViewModel (verbatim move)

* refactor: extract mapServerMessages to ChatServerMessageMapper (verbatim move)

* style: remove orphaned comments after media extraction, ktlint format

---------

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-22 20:44:43 +03:00
M57andM57 a11761f120 fix(chat): skip empty assistant tool-call placeholder rows in REST transcript (#953)
* fix(chat): skip empty assistant tool-call placeholder rows in REST transcript

Non-reasoning models (e.g. gemini-3.5-flash) store a tool-call turn as an
empty assistant row (content = "", no reasoning) with a tool_calls list.
The REST transcript returns these verbatim, and mapServerMessages was only
filtering empty assistant rows that carried reasoning text (the issue #771
thinking-model case). With no reasoning, the filter check failed and the
placeholder rows fell through — rendering as empty bubbles with Copy buttons
in the chat list.

Fix: skip ANY empty assistant row regardless of whether reasoning is present.
If it carries reasoning, stash it for folding (issue #771 path unchanged).
If it doesn't, it's a pure tool-call placeholder — silently drop it.
No user-visible content is lost: these rows never held displayable text.

Verified on device (hermes37 emulator): session 'Friendly greeting #9' had
10 ghost empty bubbles; after fix the transcript shows only real messages.

Issue: #956

* fix(chat): blank-content guard in FullBleedAgentMessage prose renderer

Defense-in-depth for the ghost-bubble fix (#953): never render an empty
prose block (blank text + lone Copy button) if a placeholder row ever
slips past upstream mapping. Streaming messages keep rendering so the
live cursor survives until the first delta lands.

Verified on device: cold start of 'Friendly greeting #9' renders only
real content — no empty bubbles.

---------

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-22 13:56:41 +03:00
M57 5153e1572f fix(chat): dedupe user bubbles enriched with attachment ref lines (#952)
Sending an image/file renders the user message twice: the optimistic
bubble carries the raw caption while the backend persists an enriched
copy with "@image:" / "@file:" ref lines and a "[screenshot]" marker
(_build_image_ref_message / run_agent multipart flattening). The trimmed
exact-content compare in sameLogicalMessage fails on that drift and the
prefix fallback gate (>=40 chars) rejects short captions, so the REST
transcript sync appends the REST copy as a second bubble.

Compare USER rows on the caption with attachment-injection lines
stripped (stripAttachmentRefLines). USER-only so assistant text that
legitimately mentions these tokens is never collapsed.

Device-verified on the hermes37 emulator: image send + transcript
resync renders a single user bubble, restart/reload stays clean.
Unit: ChatToolDedupeTest 32/32.
2026-08-22 12:33:48 +03:00
M57andM57 e9f2c726e4 feat(chat): surface per-model reasoning capabilities (#946) (#951)
Add ModelCapabilities (capabilities: Map<String, ModelCapabilities>?)
to ModelProvider matching backend /api/model/options shape
{model: {fast, reasoning, can_disable_reasoning}}. Nullable/defaulted
for backward compat with old backends (ignoreUnknownKeys+SnakeCase).

Chat composer: gate reasoning dropdown on can_disable_reasoning and
supportsReasoning — "None" disabled with "reasoning always on" hint
when can_disable==false, chip disabled with "No reasoning" when
reasoning==false. Missing keys offer full scale.

Sync currentModelCapabilities from cachedModelOptions on
SessionInfo, resume, preload/refresh, and optimistic /model swap;
clear on resetSessionState. Used by ComposerToolbar via ChatScreen.

Model picker: surface same hints in ModelPickerDialog (pinned+all)
and ModelScreen ProviderCard.

Verified: ktlintCheck OK, compileDebugKotlin OK (rerun-tasks).

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
2026-08-21 21:24:01 +03:00
M57 4549410a45 chore(ui): replace emoji with Material icons across chat UI (#944)
* chore(ui): replace emoji with Material icons across chat UI

Removes emoji glyphs and swaps them for real Material icons / plain text:

- ToolSchemaRegistry: iconEmoji String -> ImageVector icon for all ~35 tools;
  summaryPrefix emoji stripped (terminal keeps '$ '); consumer renders Icon().
- ToolBubble: collapsed summary now shows Icon(config.icon) instead of an emoji
  Text prefix; web-search url drops the link emoji; warning badge uses
  Icons.Filled.Warning instead of a '⚠' Text glyph.
- ChatBubble SelfImprovementReviewCard: 🛠/🧠 String -> Build/Psychology Icon.
- ChatViewModel / ChatWsEventReducer: drop ⚠️/✅ prefixes from user-facing
  status/error messages (severity already conveyed by color/styling).
- SubagentInspectionSheet: ✅/🔄/❌/⭕ status markers -> CheckCircle/Autorenew/
  Cancel/RadioButtonUnchecked (tinted); ElectricBolt section headers ->
  FormatListBulleted + Groups.
- strings.xml (en/zh/ko): drop '✓' from the Copied label.

ktlint clean; compileDebugKotlin SUCCESSFUL. Remaining chat files
(ToolViewBuilder, ChatComposer, MessageCards, ChatReactions, ComposerToolbar,
FullBleedAgentMessage) tracked in follow-up commits on this branch.

* test: align unit tests with emoji-free summary + status messages

composeToolSummaryLines dropped the emoji parameter and no longer prepends
an emoji glyph to the collapsed summary line, so update ToolSummaryLinesTest
call sites + expected strings accordingly.

SlashCommandDispatchRpcTest asserted the old '⚠️ /cmd: ...' error messages;
those prefixes were removed in the UI cleanup, so drop them from the
expected strings.

ChatWsEventReducerTest emoji usages are model-output fixtures, not assertions
on changed code paths — left untouched.
2026-08-21 00:07:37 +03:00