feat(chat): message-list rework, customization surface, smooth scroll

Chat tab overhaul:
- Header/layout: single-row header (name + key-lock + compact click-to-copy address +
  right-aligned icon toolbar with a settings "notch"), composer moved below the message
  box (emoji toggle left of a bottom-flush input), tightened list-pane controls.
- Message list: per-day date separators (Today/Yesterday/date), time-only group headers,
  tight same-sender grouping with iMessage-style merged corners, per-run peer avatar,
  delivery status (clock -> check), hover-reveal per-message time. Message base ~18px.
- Customization: a settings "notch" gear opens a modal (also under Settings ->
  Chat & Contacts) with segmented controls for emoji style / bubble style / density /
  timestamps, sliders for poll rate + text size, a bubble-accent dropdown, Enter-to-send.
  Bubble style/accent/density/text-size/timestamps all applied live in the message loop.
- Settings: app-wide clock-format control lives in Settings -> General; chat keeps a
  per-tab override. Both chat panes now use the app's smooth wheel-scroll.
- i18n: new strings across all 8 languages; CJK subset rebuilt for the added glyphs.

Inline contact rename, hide/mute, 0-conf fast-scan and the export path are carried through.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-17 18:45:19 -05:00
parent 3b5db02e09
commit 2c909d35ea
13 changed files with 1060 additions and 212 deletions

View File

@@ -12,6 +12,7 @@
#include <sodium.h>
#include "../../util/logger.h"
#include "../windows/balance_tab.h"
#include "../windows/chat_tab.h" // RenderChatSettingsControls (shared Chat & Contacts controls)
#include "../windows/console_tab.h"
#include "../../util/i18n.h"
#include "../../util/platform.h"
@@ -821,6 +822,25 @@ void RenderSettingsPage(App* app) {
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
// --- Clock format (app-wide 24h / 12h — the Chat tab can override it in chat settings) ---
{
ImGui::PushFont(body2);
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted(TR("clock_format"));
ImGui::SameLine(0, Layout::spacingMd());
int cf = app->settings() ? app->settings()->getTimeFormat() : 0;
const char* cfItems[] = { TR("chat_ts_24h"), TR("chat_ts_12h") };
ImGui::SetNextItemWidth(160.0f * Layout::dpiScale());
if (ImGui::Combo("##ClockFormat", &cf, cfItems, 2) && app->settings()) {
app->settings()->setTimeFormat(cf);
app->settings()->save();
}
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_clock_format"));
ImGui::PopFont();
}
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
// --- Font Scale slider (always visible) ---
{
ImGui::PushFont(body2);
@@ -1086,6 +1106,25 @@ void RenderSettingsPage(App* app) {
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
// --- Clock format (app-wide 24h / 12h — the Chat tab can override it in chat settings) ---
{
ImGui::PushFont(body2);
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted(TR("clock_format"));
ImGui::SameLine(0, Layout::spacingMd());
int cf = app->settings() ? app->settings()->getTimeFormat() : 0;
const char* cfItems[] = { TR("chat_ts_24h"), TR("chat_ts_12h") };
ImGui::SetNextItemWidth(160.0f * Layout::dpiScale());
if (ImGui::Combo("##ClockFormat", &cf, cfItems, 2) && app->settings()) {
app->settings()->setTimeFormat(cf);
app->settings()->save();
}
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_clock_format"));
ImGui::PopFont();
}
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
// --- Font Scale slider (always visible) ---
{
ImGui::PushFont(body2);
@@ -2445,6 +2484,21 @@ void RenderSettingsPage(App* app) {
ImGui::Dummy(ImVec2(0, gap));
// ====================================================================
// CHAT & CONTACTS — card (same controls as the Chat tab's settings notch)
// ====================================================================
{
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("chat_settings_section"));
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
material::GlassCardScope card(dl, availWidth, pad, bottomPad, glassSpec);
ImGui::PushFont(body2);
RenderChatSettingsControls(app, availWidth - pad * 2.0f); // card inner width (GlassCard doesn't narrow it)
ImGui::PopFont();
}
ImGui::Dummy(ImVec2(0, gap));
// ====================================================================
// ABOUT — card
// ====================================================================