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.
Three SectionView overloads were each computing the same cardColor inline:
if (CurrentColors.value.base == DefaultTheme.LIGHT) Color.White
else MaterialTheme.colors.background.mixWith(...). DRY violation paired with
the canvasColorForCurrentTheme() helper that already covers the canvas side
of the same theme split. Add a sectionCardColor() function in Theme.kt and
collapse the 3 inline formulas to one call.
Restore UserPicker.kt and UserPicker.android.kt to their state at 43855ae07
(before commit 3a7118235 introduced SectionView wraps). The founder asked in
chat to keep UserPicker out of the cards facelift — undo all of our changes
to it, including the followup tweaks (avatar padding, divider above grid,
active-profile wrap, etc.) and the founder's own followup cleanup 23b0e41d8
which only existed to refactor our wraps.
Card was flush against the sheet's top edge; add DEFAULT_PADDING spacer above. Left padding inside the card was 16dp while the avatar (60dp) sat in an 80dp minHeight row so visual top/bottom were ~10dp — bring start down to 10dp so the photo sits equidistant from card top, bottom and left.
26 section header strings used as SectionView titles (SETTINGS, CHAT DATABASE, HELP, SERVERS, etc.) were stored ALL CAPS in source. The .uppercase() removal commit did nothing for them. Convert the source values to sentence case with proper-noun preservation (SimpleX, SOCKS). LIVE and OK stay all-caps (status badge and button).
Commit 3a7118235 extracted the profile out of its original SectionView when wrapping the menu in its own card. Without the card chrome the profile shifted to the screen edge instead of sitting at CARD_PADDING like the menu below. Wrap it back in SectionView and add SectionDividerSpaced before the menu card.
Commit 633e0f414 made SectionDivider() a no-op outside SectionView card, which removed the desktop chat list dividers (the list is not wrapped in a SectionView). Mirror the Android conditional: SectionDivider in-card, padded Divider otherwise.
Source string resources are already in sentence case (e.g. "Profile images", "Message reception"). The .uppercase() calls forced them to ALL CAPS, which is the Android settings convention but conflicts with the iOS-style facelift. Remove the call everywhere so headers render as in the source.
* desktop: prevent duplicate launches
Acquires a file lock and listens on a loopback ServerSocket in dataDir.
A second launch signals the running instance to restore its window and
exits silently. See plans/2026-05-13-desktop-single-instance.md.
* desktop: un-minimize window in showWindow
toFront() does not un-minimize a JFrame on any AWT platform. Clear the
ICONIFIED bit so a minimized window restores; preserves MAXIMIZED_BOTH.
Also fixes the same case when restoring from the tray icon.
* desktop: move showWindow from DesktopTray to DesktopApp
It has callers outside the tray (single-instance signal) and belongs
next to simplexWindowState, which it operates on.
* simplify
* refactor
* desktop: start show-file watcher when choosing minimize from first-close dialog
The handleCloseRequest path already starts the watcher when minimizing to
tray; the Ask-dialog path did not, so the first-time user who picks
"Minimize to tray" got a hidden window with no signal handling — a
duplicate launch would not restore it.
* desktop: always watch for duplicate-launch signal, drop hung-instance alert
The watcher now runs for the JVM lifetime once the lock is acquired,
not only when minimized to tray. Duplicate launches always restore the
primary's window (un-minimize, un-tray-hide, toFront) instead of being
silently dropped when the primary is not minimized.
Drops the "may be hung, start anyway?" popup and the two strings — that
fallback was needed only because the watcher could miss signals. With
the always-on watcher there is no scenario where the primary fails to
consume simplex.show, so the escape hatch becomes dead code.
* desktop: alert when primary's watcher doesn't consume the show file
Restores the "another instance may be running" alert. Every duplicate
launch waits up to 1s for the primary's watcher to delete the show file
it just created. If the file is consumed within the window, the
duplicate exits silently. If still there after 1s the primary is hung
and the alert fires.
---------
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
ChatInfoView (Contact prefs/Send receipts/Chat theme → Delete messages) and GroupChatInfoView (Member reports → Edit group profile) both used SectionDividerSpaced(maxBottomPadding = false) = 10dp between two cards that have neither header nor footer touching the gap, so the tight variant wasn't justified. Switch to the default 20dp.
Three places had adjacent SectionView cards with no spacer (GroupLinkView QR + actions, WelcomeMessageView non-owner preview + copy), or used a one-off Spacer(8.dp) instead of the conventional helper (owner mode-button card). Replace with SectionDividerSpaced() so all between-card gaps live behind one helper.
When both chatSubStatus and cStats are null, the SectionView body rendered as empty (zero-height card) but the SERVERS title still appeared, leaving an orphan header between E2E encryption and Clear chat. Gate the whole section on having at least one of the two.
The mixWith canvas-darkening formula only lands well in LIGHT. For DARK / BLACK / SIMPLEX, fall back to secondaryVariant, which UserPicker already uses for the active profile avatar — keeps placeholder avatars consistent across the app on dark themes.
Both rows used Modifier.padding(top = 10.dp) so the tile hugged the bottom of the SectionView card. Switch to padding(vertical = 10.dp) to match the symmetric padding used by ProfileImageSection.
secondary (#8B8786) was too dark. Use background mixed with onBackground at 0.88 — same darkening recipe as canvasColorForCurrentTheme uses with 0.94, applied a step further. On LIGHT this lands near #E1E1E1: 15 units darker than the canvas, matching how the canvas sits 15 darker than white.
UserPickerOptionRow no longer applies extraPadding on desktop, and the Settings row uses default SectionItemView padding instead of its own. Both now match the CARD_ITEM_PADDING used in Settings screens. After the inactive-users avatar grid in the unified card, paint a SectionDivider so it visually separates from Your chat profiles.
secondaryVariant is near-white (#F1F2F6) and disappears against the gray canvas. Use the visible secondary tone instead so default avatars without a photo are legible on both card and canvas backgrounds.
Both Android and desktop portrait now show address, preferences, (desktop: inactive-users grid), profiles, link mobile / use from desktop, and settings inside a single SectionView card. Desktop landscape keeps the side-by-side two-card layout.
LocalInSectionCard is declared in Section.kt which has no package (root package), so it must be imported as 'import LocalInSectionCard', not as 'chat.simplex.common.views.helpers.LocalInSectionCard'.
Desktop background was MaterialTheme.colors.surface (white) so the SectionView cards introduced earlier were invisible. Switch to canvasColorForCurrentTheme() to match Android.
Drop the explicit Divider above the inactive-users grid: split SecondSection into two SectionView cards with the avatar grid between them so the section dividers come from the cards themselves.
Desktop UserPicker doesn't have a canvas background, so white cards on the white surface were invisible and the existing desktop divider above inactive users looked stray next to the SectionItemView mini-dividers.
The summary layouts wrapped Stats / Subscriptions / Sessions inside the outer Server address SectionView, which produced nested cards and pushed the Statistics 'Starting from...' footer inside a card. Unnest them so each section is its own card with proper spacing and the footer renders outside.
InfoRow defaulted to DEFAULT_PADDING (20dp), but card chrome adopted CARD_ITEM_PADDING (15dp) for SectionItemView and InfoRowTwoValues. Inside a card, rows of different kinds visibly jumped left/right. Bring InfoRow and its IndentedInfoRow variant onto the same baseline.
Two changes:
1) Theme.kt LIGHT canvas: 0.97f → 0.94f (#F0F0F0). User wants more
contrast against cards. With Material's default 0.04-alpha hover
(#F5F5F5) this puts hover LIGHTER than canvas by 5 units — unusual
direction but it's the user's call; they'll evaluate visually.
2) ChatListNavLinkView.android: when rendered inside a SectionView card
(e.g. contact list inside NewChatSheet after the forEach-into-card
refactor), use SectionDivider() — same 2dp full-width canvas-color
divider as desktop. Outside a card (main chat list), fall back to
the original Material `Divider(Modifier.padding(horizontal = 8.dp))`
so unchanged for that context.
3) LocalInSectionCard made `internal` so the android-specific file can
read it. Same pattern as LocalAppColors etc.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes from the user's verified list of misplaced footers/spacers:
- ChatInfoView: SimpleX address footer ("You can share this address
with your contacts to let them connect with you.") moved out of the
address SectionView lambda.
- GroupMemberInfoView: same string for member address.
- Appearance: SectionSpacer in the Image-wallpaper branch (after
"Remove image" button) removed — it created 30dp empty padding
inside the THEMES card only when a custom image was selected.
- NotificationsSettingsView: Xiaomi battery-optimization footer
("Xiaomi devices: please enable Autostart...") moved out of the
notifications SectionView lambda (visible only on Xiaomi devices
in Periodic/Service notification mode).
- ConnectMobileView: dropped the 20dp Spacer that sat inside the QR
SectionView after the developer-tools "Share link" row — visible
as extra padding below Share link inside the card.
Same pre-card-chrome pattern as other moves: helpers placed inside
SectionView lambdas before PR #6777 rendered fine when SectionView was
a plain Column; after card chrome they render inside the white card.
Moved them outside so footers read as captions and spacers actually
separate cards.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User asked for taller settings rows. Bump the default minHeight in
all four SectionItemView family functions from DEFAULT_MIN_SECTION_ITEM_HEIGHT
(50dp) to DEFAULT_MIN_SECTION_ITEM_HEIGHT + 6.dp (56dp).
Scoped to SectionItemView callers only — does not touch the global
DEFAULT_MIN_SECTION_ITEM_HEIGHT constant, so non-section callers
(ChatItemInfoView, ComposeContextProfilePicker, TagListView,
UserPicker) keep the 50dp baseline.
Callers that pass explicit minHeight (e.g. 54dp in GroupChatInfoView
members) are unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User feedback: the off-white canvas at 0.95 (#F2F2F2) read as too
dark. Two coordinated changes:
- canvasColorForCurrentTheme LIGHT branch: 0.95f → 0.97f. Canvas now
#F7F7F7 (3% darker than white, was 5%). Still distinct from pure
white card but lighter.
- Drop the custom sectionItemHover Modifier helper (and its hoverable
+ InteractionSource + background machinery). The reason for the
custom hover was that the default Material 0.04-alpha ripple hover
(#F5F5F5 on white card) blended with the old #F2F2F2 canvas. With
the lighter canvas at #F7F7F7 the default hover #F5F5F5 is now
visibly darker than canvas (2 units delta) — visible enough at
Material default without our custom override.
Removed unused MutableInteractionSource and collectIsHoveredAsState
imports.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>