The title SelectionContainer covered only the text, so a tap on the empty bar area or the article body never cleared the highlight. A tap anywhere on the article screen now recreates the container and drops the selection.
Colors set by dictionaries such as navy, dark red and brown are nearly invisible on the dark background. Swap the dark named colors for lighter tones when rendering in the dark theme; the light theme keeps the original colors.
Link-heavy articles split every line into its own list item, so a
fast fling composed a dozen items in one frame and froze. Merge small
blocks into multi-paragraph items: indent via em space and a uniform
line pitch via LineHeightStyle(Proportional, Trim.Both).
Keep showing the last results while a new search generation loads,
and guard list access: the paging snapshot can shrink mid-layout,
which threw and crashed on an empty result set.
Blocks stayed open until the next character arrived, so following text
merged into them and trailing escapes dropped. Close each block right
at its tag and append escaped characters unconditionally.
Style and grammar labels printed upright while sources mark the same
words in different ways, so identical labels rendered inconsistently.
Every [p] block now uses the label color with italic posture, and the
text scan for specific label words is removed.
A literal tab advanced text to the next platform tab stop, which pushed
the rest of the line far enough to wrap it mid-phrase.
Replace each tab with an en space so the separator keeps a fixed, modest
width and the line breaks like ordinary text.
Dictionaries use a space right after an opening tag as a visible
separator, and dropping it glued words together.
Drop only space runs at the start of a block and collapse runs to one
space everywhere else; spaces after opening tags are now kept.
In landscape the detail pane rendered the title inside a fixed 56.dp
bar and centered the text in it, so the headword sat about 14 dp lower
than the first search result next to it. Add a compact variant of the
title bar without the fixed height; the title line now shares the top
offset of the first list row.
Every block of the dictionary article was wrapped in its own
SelectionContainer, so a tap in one block never cleared a highlight
held in another. A single container now wraps the article list — a tap
anywhere clears the selection and selection can span adjacent blocks.
The progress notification was detached from the foreground
service on pipeline finish, so a process killed mid-import left an
ongoing notification orphaned in the shade until the next app launch.
Terminal notifications now use their own id, and finishing the
pipeline removes the bound progress notification; swiping the app
from recents cleans it up as well.
A bare .dz file (DictZip without the .dsl prefix) is treated by the
pipeline as an orphaned file and deleted right after the download, so
a .dz source URL passed validation but silently produced no
dictionary. Accept only DictZip-compressed DSL (.dsl.dz; .gz already
covers .dsl.gz) and compare extensions case-insensitively.
A source URL without the {DATE} placeholder is date-invariant, so its
HEAD probe always answers 200. Including such sources in the freshness
check made the checker report "server has a newer version" regardless
of the dated sources' state.
URL validation rejected any host containing a colon, so sources on
non-standard ports could not be added. Strip the optional :port from
the host before applying the domain checks and validate the port
range (1-65535); the port is kept intact through template
normalization and URL building.
Split lines on LF, CRLF and lone CR like the indexer, strip leading whitespace from body lines, break the paragraph after a closing margin tag, and apply named colors from [c] tags.
ClearArticle nulls state before exit transition ends, so the outgoing screen briefly rendered the null-branch fallback in the title bar. Render empty title/content for null state; remove article_no_selected.
Re-evaluate after indexing completes, persist the last status with its check date, and restore it on startup. When the saved date is older than today the status is re-evaluated once, so new dictionary updates stay visible without a server request on every launch.
Treat inactive download contexts as cancellation, delete the partial file, and
rethrow the cancellation so the pipeline does not retain an incomplete payload.
Also remove orphaned .dsl/.dz files left behind by cancelled downloads.
Run the partial/orphan deletions on NonCancellable: the cleanup is invoked from
already-cancelled coroutines, where withContext(Dispatchers.IO) aborts before
executing the block and silently leaves the files behind.
perFileProgress was never populated and its progress branch was
unreachable; currentFile, currentIndex, currentFileProgress and label
were write-only (no consumer read them); progressPercent had no
callers; the -1f aggregate sentinel is gone along with the fallback
branches that only fired when the tracker was absent.
A late ImportState Success snapshot collected after the success
notification overwrote it with an ongoing progress record. Post
progress on Main behind a volatile terminal latch; notifications go
through NotificationPort, the pipeline lives in the coordinator, and
the service finish is injected.
Errors and skips are collected while the copy loop runs and reported
once it ends. The final import state is exhaustive - success, already
added, invalid name, or nothing supported found - so every import run
reaches a terminal state and the pipeline notification always resolves
instead of lingering as an indeterminate progress bar.
Paging result rows had no keys and were matched by position: appended
pages and query diffs could not reuse row state. Keys derived from
dictionaryPath+offset+word are stable per row, and the paging source
collapses duplicate entries for one dictionary card: case and diacritic
alias variants of a headword share the normalized word, so entries are
deduplicated by dictionaryPath+word+offset before emitting.
Bump AboutLibraries plugin to 15.0.4 -> 15.2.0; parse
aboutlibraries.json on Dispatchers.IO via produceState (header renders
immediately, rows fill in); drop animateContentSize from rows.
IndexSearcher.compareEntryGroups / IndexComparisonSummary /
IndexComparisonException / readEntryOrNull and AboutLibrariesData.decode
have no call sites left in the app.
monotonicTargetProgress is held in remember and only ever grows via
maxOf, so when a new indexing starts after a previous one reached a
higher value the bar is left at the old level while the percent label
starts at 0. Snap it to the current target when the section becomes
visible so the bar follows the new operation from its first frame.