Skip to content

feat(ui): refresh app UI and add SillyTavern push flow#32

Merged
mxnix merged 9 commits into
mainfrom
improve-ui
May 9, 2026
Merged

feat(ui): refresh app UI and add SillyTavern push flow#32
mxnix merged 9 commits into
mainfrom
improve-ui

Conversation

@mxnix

@mxnix mxnix commented May 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • Refreshes Home, Accounts, Logs, Settings/About, shell, and update banner UI with shared Kick action buttons, loading indicators, smooth scrolling, haptics, KickIcons, and responsive layouts.
  • Adds experimental SillyTavern “Push to ST” flow from Home, including CSRF/cookie handling, secret write, profile upsert/select, logo asset, and localized errors.
  • Improves accounts UX with compact search/sort controls, metrics, avatar preview/selection/custom file support, and DiceBear fallbacks.
  • Improves logs UX with request grouping, expandable status timelines, animated new entries, clearable filters, and restyled export/share/clear/load-more actions.
  • Updates EN/RU localizations, generated l10n, goldens, dependencies, and tests for the new UI states and log refresh behavior.

Testing

  • flutter analyze
  • flutter test
  • Manual verification on Windows
  • Manual verification on Linux
  • Manual verification on Android

Copilot AI review requested due to automatic review settings May 9, 2026 18:59
@mxnix mxnix self-assigned this May 9, 2026
@mxnix mxnix added the enhancement New feature or request label May 9, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a major UI/UX overhaul, integrating Material 3 Expressive components and a centralized icon system. Key updates include a new floating bottom navigation bar, a service for pushing profiles to SillyTavern, and a redesigned logs page with request grouping and reveal animations. The accounts page now features a custom avatar picker and enhanced filtering. Feedback focuses on performance bottlenecks in the logs page caused by expensive JSON parsing during UI rebuilds and suggests moving grouping logic to a controller. Additionally, the SillyTavern settings patching logic should be refined to avoid potential schema redundancies.

Comment thread lib/features/logs/logs_page.dart Outdated
Comment on lines +1066 to +1089
List<_LogDisplayItem> _buildLogDisplayItems(List<AppLogEntry> entries) {
final items = <_LogDisplayItem>[];
final requestGroups = <String, _RequestLogDisplayItem>{};
var requestNumber = 0;

for (final entry in entries) {
final requestId = _requestIdForEntry(entry);
if (requestId == null) {
items.add(_SingleLogDisplayItem(entry));
continue;
}

var group = requestGroups[requestId];
if (group == null) {
requestNumber += 1;
group = _RequestLogDisplayItem(requestId: requestId, requestNumber: requestNumber);
requestGroups[requestId] = group;
items.add(group);
}
group._entries.add(entry);
}

return items;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The _buildLogDisplayItems function is called directly within the build method of _LogsPageState. This function iterates through the entire list of log entries and performs grouping logic, which includes multiple calls to _requestIdForEntry. Since _requestIdForEntry performs expensive jsonDecode operations on each entry's payload, this can lead to significant performance degradation and UI jank, especially as the log history grows.

Consider moving this grouping logic into the LogsController or a dedicated provider, so that the display items are computed only when the underlying log entries change, rather than on every rebuild.

Comment thread lib/features/logs/logs_page.dart Outdated
Comment on lines +1092 to +1101
final payloadRequestId = _payloadString(_decodeLogPayload(entry.maskedPayload), 'request_id');
if (payloadRequestId?.isNotEmpty == true) {
return payloadRequestId;
}
final rawRequestId = _payloadString(_decodeLogPayload(entry.rawPayload), 'request_id');
if (rawRequestId?.isNotEmpty == true) {
return rawRequestId;
}
return null;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The _requestIdForEntry function calls _decodeLogPayload twice per entry (once for maskedPayload and once for rawPayload). Each call to _decodeLogPayload invokes jsonDecode. For a large list of logs, this results in thousands of redundant JSON parsing operations during every build cycle.

To improve performance, consider caching the extracted requestId within the AppLogEntry model itself or using a memoization strategy to avoid re-parsing the same JSON strings repeatedly.

Comment on lines +140 to +162
final candidates = <Map<String, Object?>>[
settings,
if (settings['oai_settings'] is Map)
(settings['oai_settings'] as Map).map((key, value) => MapEntry(key.toString(), value)),
if (settings['openai_settings'] is Map)
(settings['openai_settings'] as Map).map((key, value) => MapEntry(key.toString(), value)),
];

for (final target in candidates) {
target['chat_completion_source'] = 'custom';
target['custom_url'] = proxyEndpoint;
target['custom_model'] = model;
target['bypass_status_check'] = true;
target['custom_prompt_post_processing'] ??= 'merge';
}

if (settings['oai_settings'] is Map) {
settings['oai_settings'] = candidates[1];
}
if (settings['openai_settings'] is Map) {
settings['openai_settings'] = candidates.last;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The _patchOpenAiSettings function modifies the root settings map by adding keys like chat_completion_source, custom_url, and custom_model directly to it (via candidates[0]). While this might be intended for compatibility, SillyTavern typically expects these specific chat completion settings to reside within the oai_settings or openai_settings sub-maps. Adding them to the root map might be redundant or could potentially conflict with future SillyTavern schema updates.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes KiCk’s UI with new shared UI primitives (actions, haptics, smooth scrolling, Material Symbols/M3E components), adds an experimental “Push to SillyTavern” flow from Home (CSRF/cookie/secret/profile handling), and upgrades Logs/Accounts UX with richer controls and visual states. It also updates localizations, assets, dependencies, and tests to cover the new behaviors (notably log refresh/animation and SillyTavern cookie handling).

Changes:

  • Introduce shared UI building blocks (KickActions, KickHaptics, KickSmoothScroll*, KickIcons) and adopt them across Home/Accounts/Logs/Settings/About/update banner.
  • Add SillyTavern push service + Home dialog/CTA and tests for folded Set-Cookie handling.
  • Enhance logs with request grouping + “newly visible entries” animation and add/adjust widget/unit tests and l10n strings.

Reviewed changes

Copilot reviewed 30 out of 37 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/silly_tavern_push_service_test.dart Adds coverage for folded Set-Cookie parsing and cookie persistence across SillyTavern push calls.
test/proxy_configuration_sync_test.dart Adds widget test ensuring logs refresh correctly on repeated proxy activity events.
test/logs_page_test.dart Updates logs page expectations for refreshed UI (e.g., refresh icon removal).
test/logs_controller_test.dart Adds test for “appearing entries” behavior after refreshing logs state.
test/kick_theme_test.dart Adds tests for Kick color scheme hue range and M3E/Kick theme extensions.
test/home_page_test.dart Adds/updates tests for new Home UI states and components (loading indicator, responsiveness, actions).
test/home_page_golden_test.dart Adjusts golden locale (ru → en) for updated Home visuals.
test/flutter_test_config.dart Ensures additional icon fonts are loaded in tests for Material Symbols usage.
test/accounts_page_test.dart Updates tests for new accounts metrics/sort UI affordances.
pubspec.yaml Adds dependencies (m3e_collection, material_symbols_icons) and registers SillyTavern logo asset.
pubspec.lock Locks new direct/transitive dependencies pulled in by M3E + Material Symbols.
lib/l10n/generated/app_localizations.dart Regenerates localization API with new strings (avatars, logs grouping, ST push, About sections).
lib/l10n/generated/app_localizations_ru.dart Adds RU translations for new UI strings and updates Project ID labels.
lib/l10n/generated/app_localizations_en.dart Adds EN strings for new UI and updates Project ID labels.
lib/l10n/app_ru.arb Adds RU source strings for avatars, logs grouping, ST push, About sections, and Project ID text updates.
lib/l10n/app_en.arb Adds EN source strings for avatars, logs grouping, ST push, About sections, and Project ID text updates.
lib/features/shared/kick_scroll.dart Introduces smooth scroll controller + wrappers for common scroll views with scrollbar integration.
lib/features/shared/kick_haptics.dart Adds centralized haptics helpers used by new action components.
lib/features/shared/kick_actions.dart Adds shared primary/secondary/icon actions, loading indicator wrapper, and expressive refresh wrapper.
lib/features/shared/app_update_banner.dart Updates update banner UI to use shared actions/icons and M3E progress indicator.
lib/features/settings/settings_sections.dart Refreshes settings section layout, adds haptics, and adopts shared actions/icons.
lib/features/settings/settings_page.dart Switches to smooth scrolling, uses shared loading indicator and KickIcons.
lib/features/settings/about_page.dart Refreshes About UI (analytics card + info rows with external links) and adopts shared components.
lib/features/logs/logs_page.dart Major logs UI refresh: request grouping/timeline, filter/search UX, appearing-entry animation, shared actions/icons.
lib/features/logs/log_message_localizer.dart Minor formatting cleanup for RegExp declaration.
lib/features/home/silly_tavern_push_service.dart Adds SillyTavern push implementation (CSRF fetch, cookie jar, secret write, settings upsert/save).
lib/features/home/home_page.dart Refreshes Home layout and adds “Push to ST” dialog/CTA wired to the push service.
lib/features/app_state/providers.dart Adds SillyTavern push service provider and extends LogsViewState with appearingEntryIds.
lib/features/app_shell/app_shell.dart Refreshes shell navigation with floating bottom nav, M3E rail, transitions, and clearance propagation.
lib/features/accounts/accounts_page.dart Refreshes accounts UX (controls/metrics) and adds avatar preview/picker with DiceBear + custom file support.
lib/features/accounts/account_usage_page.dart Updates usage page to use shared actions/icons, smooth scrolling, and KickRefresh.
lib/core/theme/kick_theme.dart Updates theme tokens/typography and installs M3E theme extension.
lib/core/theme/kick_icons.dart Adds KickIcons wrapper over Material Symbols icons.
lib/core/platform/android_foreground_runtime.dart Ensures plugin registrant initialization for Android foreground runtime entrypoint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +548 to +557

@override
void initState() {
super.initState();
_urlController = TextEditingController(text: 'http://127.0.0.1:8000');
_profileNameController = TextEditingController(text: 'KiCk');
_modelController = TextEditingController(text: _defaultSillyTavernModel(widget.settings));
}

@override
Comment thread lib/features/settings/about_page.dart Outdated
if (uri == null) {
return;
}
await launchUrl(uri, mode: LaunchMode.externalApplication);
Comment on lines +1163 to +1180
} else if (_isFileAvatarUrl(selectedUrl!)) {
image = ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: SizedBox(
width: size,
height: size,
child: Image.file(File.fromUri(Uri.parse(selectedUrl!)), fit: BoxFit.cover),
),
);
} else {
image = ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: SizedBox(
width: size,
height: size,
child: Image.network(selectedUrl!, fit: BoxFit.cover),
),
);
padding: const EdgeInsets.all(6),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(url, fit: BoxFit.cover),
@mxnix mxnix merged commit 745cca6 into main May 9, 2026
6 checks passed
@mxnix mxnix deleted the improve-ui branch May 14, 2026 23:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants