Six more screens get white surface background to match the form-screen visual:
- UsageConditionsView (Network & servers → Review conditions): root
ColumnWithScrollBar gets .background(surface).
- SingleOperatorUsageConditionsView (operator-conditions modal opened from
enabling an operator): same.
- HowItWorks (Settings → How to use it): root Column gets .background(surface).
- WhatsNewView (Settings → What's new): ModalView gets background = surface.
- SimpleXInfoLayout (Settings → About SimpleX Chat): conditional on
onboardingStage == null so the onboarding entry keeps its themedBackground
while the settings entry switches to surface.
- VersionInfoView (Settings → App version): root ColumnWithScrollBar gets
.background(surface).
Message info screen (right-click → Info on desktop) had off-white themedBackground
canvas with white SectionView cards inside. Switch to MaterialTheme.colors.surface
background and replace 7 SectionView wraps with plain Column (preserving the
contentPadding the SectionViews had) — content reads as a single sheet, no
ghost card edges on white-on-white.
Base file was converted in 8292a815f / de36f1f40 / 314384b69 but other
locales still rendered titles like SETTINGS, НАСТРОЙКИ, EINSTELLUNGEN,
PARAMÈTRES, USTAWIENIA, ÎMPOSTAZIONI in ALL CAPS. Bring every locale to
sentence case with a single sweep.
Implementation: Python script (/tmp/fix_uppercase_locales.py) walks every
non-base locale dir, finds the 29 key strings, and rewrites them when the
value is entirely uppercase (no lowercase letter). Placeholders like %1$s
are preserved as-is; SimpleX and SOCKS are kept as proper nouns after the
lowercase pass. Values already in sentence case, empty, or in scripts with
no case distinction are left alone.
540 string changes across 33 locales (ar, bg, ca, cs, da, de, el, es, fa,
fi, fr, hr, hu, in, it, iw, ja, ko, ku, lt, nb-rNO, nl, pl, pt, pt-rBR,
ro, ru, th, tr, uk, vi, zh-rCN, zh-rTW). Locales bn, hi, ml, sv, lv had
nothing to change.
WelcomeMessageView: drop SectionView wrap on SaveButton so all three
owner-mode action rows (Edit/Preview, Copy, Save) render uniformly as
loose rows on canvas, matching the post-revert direction of the
card-chrome cleanup.
GroupChatInfoView: restore per-item Divider() in the members lazy list
(lost during card-chrome experimentation). Owner row stays attached
to the "N members" card by design; divider appears between owner and
first lazy member, and between each subsequent member.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Your-chat-profiles screen is now on white surface bg; the SectionView
cards (founder's original from PR #6777) painted white-on-white and only
contributed padding. Unwrap the two SectionViews (hidden-profile reveal
button + main profiles list) so the rows render directly inside the
ColumnWithScrollBar without card chrome.
Form-only and link/QR screens have no card sections — the off-white canvas
under them just adds an extra visual layer with nothing to lift. Switch
their background to MaterialTheme.colors.surface (white on LIGHT, palette
surface on DARK/BLACK/SIMPLEX) so the screen reads as a single sheet.
Two patterns by container:
- 11 ModalView callsites get background = MaterialTheme.colors.surface.
- 4 screens rendered inside someone else's ModalView (GroupLinkView,
HiddenProfileView, TagListView, UserProfilesView) wrap their root
ColumnWithScrollBar in Box(Modifier.fillMaxSize().background(...))
so they own their background regardless of caller.
- 1 BottomSheet root (CreateProfile in WelcomeView) gets background on
the fillMaxSize Box.
Touched screens: Create profile, Create first profile (mobile/desktop),
Create group, Create channel (3 wizard steps), Edit group profile,
Group link, Add welcome message / Welcome message, Edit own profile,
Hide profile, Tag list editor, Your chat profiles, Add server,
Add chat relay (new variant only — Edit relay stays settings-style).
Old value (LightGray = #F1F2F6) was nearly invisible against the ~#F0F0F0
canvas — slightly bluish hue, ~1-2 units of contrast. The new #E0E0E0 sits
~16 units below canvas and ~31 below white card, visible on both. Affects
all LIGHT-theme avatar placeholders, UserPicker icons, DevicePill borders
and a handful of subtle UI surfaces using secondaryVariant.
Per review #1: card holds only Invite + (optional) search; the user-as-owner
row joins the same lazy column as the rest of the members, picking up the
canvas-variant avatar color through MemberProfileImage's updated default.
Revert defaultProfileIconColor back to 0.91 mix (~#E8) — that's the right
amount of contrast against a white SectionView card. Add a sibling helper
defaultProfileIconColorOnCanvas() at 0.85 mix (~#D9), which sits 23 units
below the ~#F0 canvas — same absolute contrast as the card variant achieves
on white.
Switch MemberProfileImage default from defaultProfileIconColor to the canvas
variant. Member avatars almost always render on canvas (chat list rows,
chat-bubble author avatar, group members list outside the card, channel
members, channel relays). Callers that need the card variant pass an
explicit color.
MemberProfileImage hard-coded color = MaterialTheme.colors.secondaryVariant
as default, which is LightGray (#F1F2F6) on LIGHT — slightly bluish and
nearly invisible against the ~#F0F0F0 canvas. Reuse the defaultProfileIconColor()
helper so the LIGHT default matches the rest of the app (midpoint between
canvas and white card), and DARK themes keep their palette secondaryVariant.
defaultProfileIconColor() in ChatInfoImage.kt promoted from private to file-
level visibility so it can be referenced from GroupMemberInfoView.
Was at 0.91 mix (~#E8) — designed to sit 'below' the white card, but on the
~#F0 canvas it nearly blended (delta ~8). Switch to 0.97 mix (~#F7), which
is the geometric midpoint between #FF (white card) and ~#F0 (canvas) and so
sits at equal absolute contrast against either background.
fa29bb7a7 put filteredMembers.value.forEach inside the same SectionView as
the Invite button and the owner row to get a unified card visual. That
sacrificed lazy rendering — all members composed at once, hurting big-group
scroll perf. Founder asked to bring lazy back.
Compromise: keep Invite + (search row) + owner row inside the SectionView
card (the 'hero' rows). Move the rest of the members out to a sibling
items(filteredMembers.value, key = { it.groupMemberId }) call in the
parent LazyColumn — bare SectionItemViewLongClickable rows below the card,
lazy-composed by LazyColumn.
Commit 3a9ece8d1 moved contacts forEach inside the if-branch and made the
else-branch fall back to NoFilteredContactsItem. That broke search: when
the user typed text and the filter returned non-empty results, the
if-condition (filtered.isNotEmpty() && searchText.isEmpty()) was false,
the else ran NoFilteredContactsItem, NoFilteredContactsItem's internal
guard saw a non-empty filter and rendered nothing — search results disappeared.
Restore three-way branching with when{}: header + contacts in card when
no search; contacts in plain card when search has matches; NoFilteredContactsItem
when filter is empty. Applied at both OneHandLazyColumn and the regular layout.
After founder simplified SectionDividerSpaced to one Spacer height (any flag
true -> DEFAULT_PADDING; both false -> DEFAULT_PADDING_HALF), many call sites
still pass combinations like (maxTopPadding = true) or
(maxTopPadding = true, maxBottomPadding = false) that all produce the same
20dp gap as the default. The flag names no longer match what they do —
reviewer flagged this as misleading.
Collapse all call sites to two canonical forms: SectionDividerSpaced() for
the 20dp gap, SectionDividerSpaced(maxBottomPadding = false) for the 10dp
tight gap. Behavior identical. Function signature kept (founder's API).
Reviewer asked why this helper uses CurrentColors.value.base instead of the
Compose MaterialTheme/CompositionLocal route. Reason is that the helper is
intentionally callable from both @Composable bodies and DrawScope (inside
sectionItemDivider's drawWithContent), and DrawScope can't invoke @Composable
getters. Add a paragraph to the doc-comment so future readers don't try to
'fix' it back to MaterialTheme.colors and break the divider draw path.