* ui: references of the flat layout names, italic and paragraphs
* ui: what's new references with the crowdfunding link
* core, ui: report reason with rule text, ios gaps as in plain text
* ui: references of report reason, ios gaps and right-to-left text
* ui: markdown messages show the empty lines that were typed
* ui: snapshot references of messages with empty lines
* ui: terminal layout spacing by one paragraph unit, names at text size
* ui: terminal gap below reactions, same empty lines in both apps
* ui: flat layout references with the paragraph unit
* core, ui: text with empty lines as paragraphs, ios bubbles fit text
* core, ui: move text with empty lines as paragraphs to a follow-up
* ui: ios references with bubbles that fit their text
* ui: other senders' names in the flat layout in the secondary colour
* core, ui: text with empty lines as paragraphs
* core, ui: single-star text italic, reports drawn without core prefix
* plan: UI improvements to improve LLM usage via SimpleX
* core, ui: types for chat layout
* core, ui: settings for alternative terminal-like chat layout for managing LLMs or other service bots
* plan: message priority
* fixes
* query plan
* update plans
* remove silent messages plan
* rename migrations
* implement layout
* amend
* fix
* fix
* fix
* improve
* time on the right, sender name in blue
* no avatar in direct chats
* better previews
* update, fixes
* update
* time gap
* changes
* ui: terminal layout line heights, gaps and time placement
* ui: simplify terminal meta layout, correct its docs and tests
* ui: right-to-left emoji edge in terminal rows, time placement tests
* android, desktop: raise Kotlin compiler memory to 4 GB
* ui: terminal captions as follow-up text, classic event separators
* ui: terminal media edge and overlay time, plan decisions
* ui: simplify terminal row separation, correct doc line references
* ui: keep the media time inside terminal rows, restore caption conditions
* ui: terminal heading capitals at large font scales, idle live message time
* ui: rename the classic voice-with-text flag, correct the meta copy docs
* ui: smaller terminal layout diff, sender gap and meta names
* ui: terminal row as tall as the media time
* docs: remove the chat layout section from the bots readme
* docs: shorten the terminal layout section of the chat view
* core: address settings API as on master, /auto_accept reads the stored layout
* ui: smaller terminal headings
* ui: classic sender rows and avatar rule as on master
* ui: classic reply quote text colour as on master
* ios: keep the chat layout setting with other UI settings
* ui: go-to button at the end of terminal rows, no gutter in direct chats
* ui: forwarded arrow on the header line of terminal rows, above the time
* ui: forwarded arrow centred above the time line of terminal rows
* ui: even insets, line heights and exact time space in classic bubbles
* ui: draw the time once in classic decryption errors
* tests: two-line text in the bubble inset test
* ui: exact time space in classic group invitations
* docs: classic bubbles plan: results and open questions
* ui: the time alone moves to the next line in classic bubbles
* ui: file icon on the size-line baseline in classic bubbles
* ui: calls in classic chats as on master
* ui: headings at 1.5625, 1.25 and 1 of the body size in both chat layouts
* ui: 11sp text inset and 14dp sides in classic bubbles
* ui: tail space in classic deleted and error items
* ui: events in classic chats grouped in runs
* ui: group sender name at the bubble edge in classic chats
* docs: classic bubbles plan: results of the 2026-10-26 decisions
* ui: events classified by the sender rule, one file icon size
* tests: no invisible character in the bubble copy test
* docs: classic bubbles plan: the shared sender rule
* docs: product concepts: the renamed meta copy
* ui: inline the copy of the time in decryption errors
* docs: chat view spec: line links after the classic bubbles changes
* ui: group invitation time on the last baseline, joined to the last word
* docs: classic bubbles: tail and time of a message above an event run
* ui: time on the last line of wrapped deleted and error texts
* ui: one layout switch in deleted and error items
* ui: one layout switch in message text padding
* ui: one name for the chat typography flag
* ui: one check whether a chat item tail is drawn
* ui: event run gaps in the chat list, row separation as on master
* ui: chat item previews draw bubbles as the chat does
* docs: classic bubbles: item cards, tail check, previews
* ui: deleted and error items without tails, insets fixed at large fonts
* ui, ci: references of classic bubbles, chat view previews in bubbles
* ci: desktop references of classic bubbles with message text at 95%
* ui: time on the drawn text baseline in desktop bubbles
* ci: desktop references with the time on the drawn text baseline
---------
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
* plan: UI improvements to improve LLM usage via SimpleX
* core, ui: types for chat layout
* core, ui: settings for alternative terminal-like chat layout for managing LLMs or other service bots
* plan: message priority
* fixes
* query plan
* update plans
* remove silent messages plan
* rename migrations
* implement layout
* amend
* fix
* fix
* fix
* improve
* time on the right, sender name in blue
* no avatar in direct chats
* better previews
* update, fixes
* update
* time gap
* changes
* ui: terminal layout line heights, gaps and time placement
* ui: simplify terminal meta layout, correct its docs and tests
* ui: right-to-left emoji edge in terminal rows, time placement tests
* android, desktop: raise Kotlin compiler memory to 4 GB
* ui: terminal captions as follow-up text, classic event separators
* ui: terminal media edge and overlay time, plan decisions
* ui: simplify terminal row separation, correct doc line references
* ui: keep the media time inside terminal rows, restore caption conditions
* ui: terminal heading capitals at large font scales, idle live message time
* ui: rename the classic voice-with-text flag, correct the meta copy docs
* ui: smaller terminal layout diff, sender gap and meta names
* ui: terminal row as tall as the media time
* docs: remove the chat layout section from the bots readme
* docs: shorten the terminal layout section of the chat view
* core: address settings API as on master, /auto_accept reads the stored layout
* ui: smaller terminal headings
* ui: classic sender rows and avatar rule as on master
* ui: classic reply quote text colour as on master
* ios: keep the chat layout setting with other UI settings
* ui: go-to button at the end of terminal rows, no gutter in direct chats
* ui: forwarded arrow on the header line of terminal rows, above the time
* ui: forwarded arrow centred above the time line of terminal rows
* ios: terminal chat layout, chosen per app, profile and chat
* ios: event time reserve of three spaces, app layout picker as Kotlin
* ios: spec and plan of the terminal chat layout
* ios: classic message text at 87% in dark themes, as terminal rows
* ios: terminal gaps scaled by the body size, 17/16 of Kotlin values
* ios: terminal quote sender at 13.5pt, thumbnail at the item end
* ios: product docs of the terminal layout, plans updated for iOS
* ios: chat scenes rendered to PNG by a unit test in the simulator CI
* ios: snapshots rendered by the smoke test script, workflow unchanged
* ios: terminal names at 13.5 x 17/16 pt, as the gaps
* ios: Markdown views at the 87% message colour in dark themes
* ios: terminal quote thumbnail 12pt after the quote text or the bar
* ios: terminal rule-only message widened by a wider quote or header
* ios: member avatars scaled with the text size, as in the chat list
* ios: snapshot rows at their own height, as list cells, so text wraps
* ios: member avatars back at 34pt, for the snapshot before scaling
* ios: member avatars scaled with the text size, as in the chat list
* ui: terminal quote thumbnail 12dp after the quote text or the bar
* ios: classic group bubble width follows the scaled member avatar
* ci: ios-sim README describes the snapshots rendered after the smoke test
* ios: scratch, padding variants for the I7 and I8 renders, to revert
* ios: revert the scratch padding variants after their renders
* ui: contrast of the voice message button in terminal rows
* ios: scratch, spacing variants and list margins to render, to revert
* ios: revert the scratch spacing variants after their renders
* ios: snapshots at a fixed date, in UTC and the en_US locale
* ios: snapshots drawn as 8-bit sRGB without alpha
* ci: compare iOS snapshots with the references in the repository
* ci: record and check iOS snapshot references on GitHub's runner
* ios: scratch, list margins on the remaining screens, to revert
* ios: revert the scratch list margins after their measurement
* ci: snapshot scripts executable, as the other CI scripts
* ios: scratch, voice circle variants in light themes, to revert
* ios: revert the scratch voice circle variants
* ios: one 12pt padding at the edges and after avatars in terminal rows
* ios: classic group sender name on the bubble edge
* ui: one 12dp padding at the edges and after avatars in terminal rows
* ios: one heading scale in both chat layouts
* ios: snapshot references of the chat scenes
* docs: leave the headings and classic lines of the plan to ep/classic-bubbles
* ios: declare terminal layout functions inside types
* ios: keep layout constants at file scope
* ios: keep file-scope functions, move only extension members into types
* ios: snapshot tests record row heights at first layout and the appearance layout switch
* ios: appearance preview follows the chat layout setting at once
* ios: snapshot test measures first-layout rows by SwiftUI and by cells
* ios: chat rows measured at the chat width when they first appear
* ios: snapshot test checks that chat rows keep their first height after a reload
* ios, ci: references of terminal and classic chat scenes, dark message text
* ios: message text at 95% in dark themes
* ios, ci: references with message text at 95% in dark themes
* ios: media in terminal rows end at the time's right inset
* ios, ci: references with terminal media at the time's right inset
---------
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
* plan: UI improvements to improve LLM usage via SimpleX
* core, ui: types for chat layout
* core, ui: settings for alternative terminal-like chat layout for managing LLMs or other service bots
* plan: message priority
* fixes
* query plan
* update plans
* remove silent messages plan
* rename migrations
* implement layout
* amend
* fix
* fix
* fix
* improve
* time on the right, sender name in blue
* no avatar in direct chats
* better previews
* update, fixes
* update
* time gap
* changes
* ui: terminal layout line heights, gaps and time placement
* ui: simplify terminal meta layout, correct its docs and tests
* ui: right-to-left emoji edge in terminal rows, time placement tests
* android, desktop: raise Kotlin compiler memory to 4 GB
* ui: terminal captions as follow-up text, classic event separators
* ui: terminal media edge and overlay time, plan decisions
* ui: simplify terminal row separation, correct doc line references
* ui: keep the media time inside terminal rows, restore caption conditions
* ui: terminal heading capitals at large font scales, idle live message time
* ui: rename the classic voice-with-text flag, correct the meta copy docs
* ui: smaller terminal layout diff, sender gap and meta names
* ui: terminal row as tall as the media time
* docs: remove the chat layout section from the bots readme
* docs: shorten the terminal layout section of the chat view
* core: address settings API as on master, /auto_accept reads the stored layout
* ui: smaller terminal headings
* ui: classic sender rows and avatar rule as on master
* ui: classic reply quote text colour as on master
* ios: keep the chat layout setting with other UI settings
* ui: go-to button at the end of terminal rows, no gutter in direct chats
* ui: forwarded arrow on the header line of terminal rows, above the time
* ui: forwarded arrow centred above the time line of terminal rows
---------
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
* desktop: add bounded animated image decoder
Skia's Codec is already on the desktop classpath through skiko and decodes
both GIF and animated WebP. The frames come from a file somebody else
composed, so the decoder is bounded before it allocates: the raster is
measured in bytes with the sides multiplied as Long, each side is capped
separately so an extreme aspect ratio cannot slip under the byte budget, and
the encoded size is checked before the bytes are copied into native memory.
Anything outside the bounds, or any failure, keeps the still image the chat
already renders.
Nothing calls this yet.
* desktop: animate GIFs in chat items and full screen
Both views drew the first frame only. The full screen view also decoded its
still on every recomposition, which an animation recomposes once per frame,
so that decode is remembered against the data it comes from.
The chat list preview stays a still image: it is a 36dp box that the desktop
layout keeps on screen the whole time, so animating it would hold a raster and
spend a frame of work per listed chat, without pause.
Removes the two markers left for this work.
* desktop: don't decode animation frames that cannot be seen
With media blur on, a blurred image is only revealed while the mouse is over
it, so every frame was decoded, uploaded and then blurred away again for
nobody - and the blur is a render effect re-run per frame. Frames now decode
only while the image can be seen, which also stops motion showing through a
blur that is there to hide it.
Passing the blur state to the view is why the shared signature changes; coil
drives its own animation on Android, so there is nothing to pause there.
* docs: move animated images plan to plans/
* docs: drop file path references from animated images plan
* docs: correct animated images plan against the code
* desktop: correct animated image comments
* desktop: reduce animated image comments
* desktop: correct and bound animated image decoding
* docs: correct animated images plan against measurements
* desktop: fuse the animation prior frame decision
* docs: cover desktop animated images in spec and product
* desktop: drop the unused animated image component
* desktop: return the animation frame instead of its state
* docs: correct the animated images documentation
* desktop: don't decode animations under the full screen viewer
* desktop: bound the frames an animation rebuilds
* desktop: pause animations under any full screen modal
* desktop: stop animations that alternate expensive frames
* desktop: read what playing a frame needs only once
* desktop: close the codec of an animation outside the bounds
* desktop: wait out what an animation frame cost to decode
* docs: correct animated images claims against the code
* desktop: bound the frame count where the others are bounded
* desktop: don't wait out a stall an animation frame did not spend
* desktop: say what the slow frame constants stand for
* desktop: don't decode animations behind a minimised window
* desktop: make the animation frame wait testable
* desktop: bound the file size where the others are bounded
* desktop: pin the frame wait clamp in its test
* desktop: keep the frame wait clamp private
* desktop: reduce animated image comments
---------
Co-authored-by: sh <github.shum@liber.li>