From 9ac66173fd7ee209abca0e7a5509fdf34a31d300 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 28 Jul 2026 21:28:52 +0300 Subject: [PATCH 1/2] ui: ship opportunistic P2 polish batch (#501) Unify tree leaf tints/indent/icons, align CrossFadeStack and Stagger with motion levels, stop empty-hero Quick start flash, cross-fade update dialog phases, and share tooltip wait duration. --- lib/core/motion/querya_cross_fade_stack.dart | 20 ++++++--- lib/core/motion/querya_hover_surface.dart | 4 ++ lib/core/motion/querya_stagger.dart | 6 ++- lib/core/sdui/sdui_tree_builder.dart | 13 ++++-- lib/core/ui/querya_icons.dart | 11 ++++- lib/core/ui/querya_tooltip.dart | 2 + lib/core/ui/querya_tree_tokens.dart | 11 +++++ .../connections/connections_panel.dart | 2 + .../connections/connections_panel_mysql.dart | 2 +- .../connections_panel_pg_tree.dart | 14 +++--- .../connections/connections_panel_sqlite.dart | 2 +- .../main_screen/result_grid_view.dart | 3 +- .../main_screen/workspace_empty_hero.dart | 39 +++++++++------- lib/features/updater/update_dialog.dart | 21 ++++++++- .../motion/querya_cross_fade_stack_test.dart | 45 +++++++++++++++++++ test/core/motion/querya_stagger_test.dart | 18 ++++++++ .../workspace_empty_hero_test.dart | 24 ++++++++++ 17 files changed, 196 insertions(+), 41 deletions(-) create mode 100644 lib/core/ui/querya_tooltip.dart create mode 100644 lib/core/ui/querya_tree_tokens.dart diff --git a/lib/core/motion/querya_cross_fade_stack.dart b/lib/core/motion/querya_cross_fade_stack.dart index 4527cf85..9459d9cf 100644 --- a/lib/core/motion/querya_cross_fade_stack.dart +++ b/lib/core/motion/querya_cross_fade_stack.dart @@ -5,6 +5,9 @@ import 'querya_motion_context.dart'; /// Like [IndexedStack] but cross-fades the active child; off-screen children /// stay mounted (preserves SQL editor state, etc.). +/// +/// Enter/exit curves and index clamping match [QueryaSwitchingBody] (without +/// the optional slide). class QueryaCrossFadeStack extends StatelessWidget { const QueryaCrossFadeStack({ super.key, @@ -17,8 +20,11 @@ class QueryaCrossFadeStack extends StatelessWidget { @override Widget build(BuildContext context) { + assert(children.isNotEmpty, 'QueryaCrossFadeStack requires children'); + final safeIndex = index.clamp(0, children.length - 1); final duration = context.motionDuration(QueryaMotion.standard); - final curve = context.motionCurve(QueryaMotion.enter); + final inCurve = context.motionCurve(QueryaMotion.enter); + final outCurve = context.motionCurve(QueryaMotion.exit); return Stack( fit: StackFit.expand, @@ -26,17 +32,17 @@ class QueryaCrossFadeStack extends StatelessWidget { for (var i = 0; i < children.length; i++) Positioned.fill( child: IgnorePointer( - ignoring: index != i, + ignoring: i != safeIndex, child: ExcludeFocus( - excluding: index != i, + excluding: i != safeIndex, child: ExcludeSemantics( - excluding: index != i, + excluding: i != safeIndex, child: AnimatedOpacity( - opacity: index == i ? 1 : 0, + opacity: i == safeIndex ? 1 : 0, duration: duration, - curve: curve, + curve: i == safeIndex ? inCurve : outCurve, child: TickerMode( - enabled: index == i, + enabled: i == safeIndex, child: RepaintBoundary(child: children[i]), ), ), diff --git a/lib/core/motion/querya_hover_surface.dart b/lib/core/motion/querya_hover_surface.dart index 8db6875e..be95a5b1 100644 --- a/lib/core/motion/querya_hover_surface.dart +++ b/lib/core/motion/querya_hover_surface.dart @@ -4,6 +4,10 @@ import 'querya_motion.dart'; import 'querya_motion_context.dart'; /// Unified hover background / border using motion tokens (Responsive chrome). +/// +/// **Scope:** selection / picker cards (e.g. connection type tiles). Dense +/// trees and explorer rows keep lighter `InkWell` / `MouseRegion` hover — +/// do not broaden adoption without an explicit follow-up. class QueryaHoverSurface extends StatefulWidget { const QueryaHoverSurface({ super.key, diff --git a/lib/core/motion/querya_stagger.dart b/lib/core/motion/querya_stagger.dart index 654ceda7..18735fc8 100644 --- a/lib/core/motion/querya_stagger.dart +++ b/lib/core/motion/querya_stagger.dart @@ -31,6 +31,7 @@ class _QueryaStaggerState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller; bool _played = false; + Duration _effectiveStep = kQueryaStaggerStep; @override void initState() { @@ -47,12 +48,13 @@ class _QueryaStaggerState extends State if (n == 0) return; final base = context.motionDuration(QueryaMotion.fast); + _effectiveStep = context.motionDuration(widget.step); if (base == QueryaMotion.instant) { _controller.value = 1; return; } - final total = base + widget.step * n; + final total = base + _effectiveStep * n; _controller.duration = total; _controller.forward(); } @@ -96,7 +98,7 @@ class _QueryaStaggerState extends State final totalMs = _controller.duration!.inMilliseconds; if (totalMs <= 0) return 1; - final stepMs = widget.step.inMilliseconds; + final stepMs = _effectiveStep.inMilliseconds; final start = (stepMs * index) / totalMs; final end = (start + 0.35).clamp(0.0, 1.0); final t = _controller.value; diff --git a/lib/core/sdui/sdui_tree_builder.dart b/lib/core/sdui/sdui_tree_builder.dart index 5cb31f85..eccce458 100644 --- a/lib/core/sdui/sdui_tree_builder.dart +++ b/lib/core/sdui/sdui_tree_builder.dart @@ -4,6 +4,7 @@ import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/core/sdui/sdui_tree_schema.dart'; import 'package:querya_desktop/core/ui/querya_icon_sizes.dart'; import 'package:querya_desktop/core/ui/querya_icons.dart'; +import 'package:querya_desktop/core/ui/querya_tree_tokens.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; /// Renders a sidebar-style tree from an SDUI schema with lazy expansion. @@ -174,7 +175,9 @@ class SduiTreeBuilderState extends material.State { final row = rows[index]; if (row.isError) { return material.Padding( - padding: material.EdgeInsets.only(left: 36.0 + row.depth * 16.0), + padding: material.EdgeInsets.only( + left: 36.0 + row.depth * QueryaTreeTokens.indent, + ), child: material.Align( alignment: material.Alignment.centerLeft, child: Text(row.error!).muted().xSmall(), @@ -196,7 +199,6 @@ class SduiTreeBuilderState extends material.State { material.Widget _buildNodeRow(SduiTreeNode node, {required int depth}) { final theme = Theme.of(context); final muted = theme.colorScheme.mutedForeground; - final primary = theme.colorScheme.primary; final canExpand = node.expandable || node.hasChildren; final isExpanded = _expanded.contains(node.id); final isLoading = _loading.contains(node.id); @@ -205,8 +207,11 @@ class SduiTreeBuilderState extends material.State { // Same hierarchy as native trees (#476 / #497) — no separate sduiNode size. final iconSize = canExpand ? QueryaIconSizes.treeGroup : QueryaIconSizes.treeLeaf; - final iconColor = isBrowsable ? primary.withValues(alpha: 0.5) : muted; - final rowLeft = 8.0 + depth * 16.0 + (canExpand ? 0 : 4.0); + final iconColor = isBrowsable + ? QueryaTreeTokens.leafIconColor(theme.colorScheme) + : muted; + final rowLeft = + 8.0 + depth * QueryaTreeTokens.indent + (canExpand ? 0 : 4.0); return material.InkWell( onTap: () { diff --git a/lib/core/ui/querya_icons.dart b/lib/core/ui/querya_icons.dart index 297dcf4e..eaba0b47 100644 --- a/lib/core/ui/querya_icons.dart +++ b/lib/core/ui/querya_icons.dart @@ -14,7 +14,7 @@ abstract final class QueryaIcons { material.Icons.account_tree_rounded; static const material.IconData schema = material.Icons.diamond_rounded; static const material.IconData extension = material.Icons.extension_rounded; - static const material.IconData publicSchema = material.Icons.public_rounded; + static const material.IconData foreignData = material.Icons.hub_rounded; static const material.IconData tableGroup = material.Icons.table_chart_rounded; @@ -23,11 +23,18 @@ abstract final class QueryaIcons { static const material.IconData viewLeaf = material.Icons.view_week_rounded; static const material.IconData materializedViewGroup = material.Icons.dynamic_feed_rounded; + static const material.IconData materializedViewLeaf = + material.Icons.layers_rounded; static const material.IconData functionGroup = material.Icons.functions_rounded; static const material.IconData functionLeaf = material.Icons.code_rounded; - static const material.IconData sequence = + static const material.IconData sequenceGroup = material.Icons.format_list_numbered_rounded; + static const material.IconData sequenceLeaf = + material.Icons.looks_one_rounded; + + /// Alias kept for call sites that still say "sequence" as the group icon. + static const material.IconData sequence = sequenceGroup; static const material.IconData indexes = material.Icons.table_rows_rounded; static const material.IconData triggers = material.Icons.bolt_rounded; static const material.IconData types = material.Icons.category_rounded; diff --git a/lib/core/ui/querya_tooltip.dart b/lib/core/ui/querya_tooltip.dart new file mode 100644 index 00000000..264c8cf0 --- /dev/null +++ b/lib/core/ui/querya_tooltip.dart @@ -0,0 +1,2 @@ +/// Shared [Tooltip.waitDuration] for dense chrome (trees, grids, …). +const Duration kQueryaTooltipWait = Duration(milliseconds: 450); diff --git a/lib/core/ui/querya_tree_tokens.dart b/lib/core/ui/querya_tree_tokens.dart new file mode 100644 index 00000000..e9daae22 --- /dev/null +++ b/lib/core/ui/querya_tree_tokens.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +/// Shared connection-tree metrics and colors (PG / MySQL / SQLite / SDUI). +abstract final class QueryaTreeTokens { + /// Indent for schema rows and sibling object folders under a database. + static const double indent = 16; + + /// Leaf-row icon tint (tables, views, sequences, …). + static Color leafIconColor(ColorScheme scheme) => + scheme.primary.withValues(alpha: 0.5); +} diff --git a/lib/features/connections/connections_panel.dart b/lib/features/connections/connections_panel.dart index 958721a8..4c3cef41 100644 --- a/lib/features/connections/connections_panel.dart +++ b/lib/features/connections/connections_panel.dart @@ -68,6 +68,8 @@ import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/core/theme/querya_typography.dart'; import 'package:querya_desktop/core/ui/querya_icon_sizes.dart'; import 'package:querya_desktop/core/ui/querya_icons.dart'; +import 'package:querya_desktop/core/ui/querya_tooltip.dart'; +import 'package:querya_desktop/core/ui/querya_tree_tokens.dart'; import 'package:querya_desktop/core/motion/querya_animated_expand.dart'; import 'package:querya_desktop/core/motion/querya_motion.dart'; import 'package:querya_desktop/core/motion/querya_motion_context.dart'; diff --git a/lib/features/connections/connections_panel_mysql.dart b/lib/features/connections/connections_panel_mysql.dart index 41f5b5db..1af080a3 100644 --- a/lib/features/connections/connections_panel_mysql.dart +++ b/lib/features/connections/connections_panel_mysql.dart @@ -668,7 +668,7 @@ class _MysqlObjectGroupState extends State<_MysqlObjectGroup> { label: item, icon: widget.itemIcon, iconSize: QueryaIconSizes.treeLeaf, - iconColor: theme.colorScheme.mutedForeground, + iconColor: QueryaTreeTokens.leafIconColor(theme.colorScheme), textStyle: material.TextStyle( fontSize: 11, color: theme.colorScheme.foreground, diff --git a/lib/features/connections/connections_panel_pg_tree.dart b/lib/features/connections/connections_panel_pg_tree.dart index ad5f8385..f25bb71c 100644 --- a/lib/features/connections/connections_panel_pg_tree.dart +++ b/lib/features/connections/connections_panel_pg_tree.dart @@ -48,7 +48,7 @@ class _PgTreeRowLabel extends material.StatelessWidget { if (label.length < _tooltipMinLength) return text; return material.Tooltip( message: label, - waitDuration: const Duration(milliseconds: 450), + waitDuration: kQueryaTooltipWait, child: text, ); } @@ -381,7 +381,7 @@ class _PgDatabaseNodeState extends State<_PgDatabaseNode> { connection: widget.connection, databaseName: widget.databaseName, label: 'Foreign data', - icon: QueryaIcons.publicSchema, + icon: QueryaIcons.foreignData, kind: PostgresObjectKind.databaseForeignData, onPostgresObjectSelected: widget.onPostgresObjectSelected, onPostgresOpenSqlWorkspace: widget.onPostgresOpenSqlWorkspace, @@ -682,7 +682,7 @@ class _PgSchemaNodeState extends State<_PgSchemaNode> { Widget build(BuildContext context) { final theme = Theme.of(context); return material.Padding( - padding: const material.EdgeInsets.only(left: 12), + padding: const material.EdgeInsets.only(left: QueryaTreeTokens.indent), child: material.Column( crossAxisAlignment: material.CrossAxisAlignment.start, mainAxisSize: material.MainAxisSize.min, @@ -794,7 +794,7 @@ class _PgSchemaNodeState extends State<_PgSchemaNode> { onRefresh: _loadObjects, label: 'Materialized views', icon: QueryaIcons.materializedViewGroup, - itemIcon: QueryaIcons.materializedViewGroup, + itemIcon: QueryaIcons.materializedViewLeaf, items: _matviews, onItemTap: widget.onPostgresObjectSelected != null ? (name) => widget.onPostgresObjectSelected!( @@ -837,8 +837,8 @@ class _PgSchemaNodeState extends State<_PgSchemaNode> { widget.onPostgresOpenSqlWorkspace, onRefresh: _loadObjects, label: 'Sequences', - icon: QueryaIcons.sequence, - itemIcon: QueryaIcons.sequence, + icon: QueryaIcons.sequenceGroup, + itemIcon: QueryaIcons.sequenceLeaf, items: _sequences, onItemTap: widget.onPostgresObjectSelected != null ? (name) => widget.onPostgresObjectSelected!( @@ -1045,7 +1045,7 @@ class _PgObjectGroupState extends State<_PgObjectGroup> { label: item, icon: widget.itemIcon, iconSize: QueryaIconSizes.treeLeaf, - iconColor: theme.colorScheme.primary.withValues(alpha: 0.5), + iconColor: QueryaTreeTokens.leafIconColor(theme.colorScheme), textStyle: material.TextStyle( fontSize: 11, color: theme.colorScheme.foreground, diff --git a/lib/features/connections/connections_panel_sqlite.dart b/lib/features/connections/connections_panel_sqlite.dart index 50db8265..861e0cd6 100644 --- a/lib/features/connections/connections_panel_sqlite.dart +++ b/lib/features/connections/connections_panel_sqlite.dart @@ -398,7 +398,7 @@ class _SqliteObjectGroupState extends State<_SqliteObjectGroup> { label: item, icon: widget.itemIcon, iconSize: QueryaIconSizes.treeLeaf, - iconColor: theme.colorScheme.mutedForeground, + iconColor: QueryaTreeTokens.leafIconColor(theme.colorScheme), textStyle: material.TextStyle( fontSize: 11, color: theme.colorScheme.foreground, diff --git a/lib/features/main_screen/result_grid_view.dart b/lib/features/main_screen/result_grid_view.dart index 2ce785d1..9fc2becb 100644 --- a/lib/features/main_screen/result_grid_view.dart +++ b/lib/features/main_screen/result_grid_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart' as material; import 'package:flutter/services.dart' show Clipboard, ClipboardData; import 'package:querya_desktop/core/layout/ui_scale.dart'; +import 'package:querya_desktop/core/ui/querya_tooltip.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; /// Layout metrics for [VirtualResultGrid]. @@ -485,7 +486,7 @@ class _GridCell extends material.StatelessWidget { return material.Tooltip( message: text, - waitDuration: const Duration(milliseconds: 400), + waitDuration: kQueryaTooltipWait, child: interactiveCell, ); } diff --git a/lib/features/main_screen/workspace_empty_hero.dart b/lib/features/main_screen/workspace_empty_hero.dart index 4555d853..d254bd5e 100644 --- a/lib/features/main_screen/workspace_empty_hero.dart +++ b/lib/features/main_screen/workspace_empty_hero.dart @@ -182,22 +182,31 @@ class _WorkspaceEmptyHeroState extends State { QueryaFadeSlide( alignment: material.Alignment.topCenter, offset: const material.Offset(0, 0.03), - child: showRecent - ? _RecentConnectionsSection( - key: const material.ValueKey('empty_recent_section'), - connections: _recent, - onOpenConnection: widget.onOpenConnection, - compact: compact, + child: !_loaded + ? const material.SizedBox( + key: material.ValueKey('empty_section_loading'), + height: 120, ) - : _QuickStartSection( - key: const material.ValueKey('empty_quick_start'), - compact: compact, - surface: wb.surface, - borderColor: - wb.borderSubtle.withValues(alpha: 0.55), - foreground: cs.foreground, - primary: cs.primary, - ), + : showRecent + ? _RecentConnectionsSection( + key: const material.ValueKey( + 'empty_recent_section', + ), + connections: _recent, + onOpenConnection: widget.onOpenConnection, + compact: compact, + ) + : _QuickStartSection( + key: const material.ValueKey( + 'empty_quick_start', + ), + compact: compact, + surface: wb.surface, + borderColor: + wb.borderSubtle.withValues(alpha: 0.55), + foreground: cs.foreground, + primary: cs.primary, + ), ), ], ), diff --git a/lib/features/updater/update_dialog.dart b/lib/features/updater/update_dialog.dart index eebfb7f6..c9582d94 100644 --- a/lib/features/updater/update_dialog.dart +++ b/lib/features/updater/update_dialog.dart @@ -3,6 +3,8 @@ import 'dart:io'; import 'package:flutter/material.dart' as material; import 'package:querya_desktop/core/layout/window_layout.dart'; +import 'package:querya_desktop/core/motion/querya_motion.dart'; +import 'package:querya_desktop/core/motion/querya_motion_context.dart'; import 'package:querya_desktop/core/theme/querya_theme_scope.dart'; import 'package:querya_desktop/core/updater/app_updater_service.dart'; import 'package:querya_desktop/core/updater/update_manifest.dart'; @@ -273,7 +275,24 @@ class _UpdateDialogContentState extends material.State<_UpdateDialogContent> { horizontal: 24, vertical: 8, ), - child: _body(context), + child: material.AnimatedSwitcher( + duration: context.motionDuration(QueryaMotion.standard), + switchInCurve: context.motionCurve(QueryaMotion.enter), + switchOutCurve: context.motionCurve(QueryaMotion.exit), + layoutBuilder: (currentChild, previousChildren) { + return material.Stack( + alignment: material.Alignment.topCenter, + children: [ + ...previousChildren, + if (currentChild != null) currentChild, + ], + ); + }, + child: material.KeyedSubtree( + key: material.ValueKey(_phase), + child: _body(context), + ), + ), ), ), material.Container( diff --git a/test/core/motion/querya_cross_fade_stack_test.dart b/test/core/motion/querya_cross_fade_stack_test.dart index 61b0ff6e..28b8b9da 100644 --- a/test/core/motion/querya_cross_fade_stack_test.dart +++ b/test/core/motion/querya_cross_fade_stack_test.dart @@ -54,4 +54,49 @@ void main() { focusNode1.dispose(); focusNode2.dispose(); }); + + testWidgets('QueryaCrossFadeStack clamps out-of-range index', + (WidgetTester tester) async { + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: QueryaCrossFadeStack( + index: 99, + children: [ + Text('only', key: Key('only_child')), + ], + ), + ), + ), + ); + + final opacity = + tester.widget(find.byType(AnimatedOpacity)); + expect(opacity.opacity, 1.0); + expect(find.byKey(const Key('only_child')), findsOneWidget); + }); + + testWidgets('QueryaCrossFadeStack uses exit curve when fading out', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: QueryaCrossFadeStack( + index: 0, + children: const [ + Text('a', key: Key('a')), + Text('b', key: Key('b')), + ], + ), + ), + ), + ); + + final opacities = + tester.widgetList(find.byType(AnimatedOpacity)); + expect(opacities.elementAt(0).opacity, 1.0); + expect(opacities.elementAt(1).opacity, 0.0); + // Inactive layer uses exit curve; active uses enter. + expect(opacities.elementAt(0).curve, isNot(opacities.elementAt(1).curve)); + }); } diff --git a/test/core/motion/querya_stagger_test.dart b/test/core/motion/querya_stagger_test.dart index e21be0ed..820b992c 100644 --- a/test/core/motion/querya_stagger_test.dart +++ b/test/core/motion/querya_stagger_test.dart @@ -102,6 +102,24 @@ void main() { expect(opacityOf(tester, 'item-2'), 1.0); }); + testWidgets('Reduced motion halves stagger step timing', (tester) async { + await tester.pumpWidget( + wrap( + QueryaStagger( + step: const Duration(milliseconds: 80), + children: texts(3), + ), + level: QueryaMotionLevel.reduced, + ), + ); + // Full would still have item-2 at 0 after 30ms with 80ms step; Reduced + // halves step to 40ms so later items start earlier. + await tester.pump(const Duration(milliseconds: 30)); + expect(opacityOf(tester, 'item-0'), greaterThan(0)); + await tester.pumpAndSettle(); + expect(opacityOf(tester, 'item-2'), 1.0); + }); + testWidgets('OS disableAnimations skips stagger', (tester) async { await tester.pumpWidget( MaterialApp( diff --git a/test/features/main_screen/workspace_empty_hero_test.dart b/test/features/main_screen/workspace_empty_hero_test.dart index 99a90e56..af169d75 100644 --- a/test/features/main_screen/workspace_empty_hero_test.dart +++ b/test/features/main_screen/workspace_empty_hero_test.dart @@ -73,6 +73,30 @@ void main() { expect(sqliteTapped, isTrue); }); + testWidgets('does not flash Quick start before recent load completes', + (tester) async { + await tester.pumpWidget( + heroShell( + child: material.SizedBox( + width: 900, + height: 700, + child: WorkspaceEmptyHero( + onNewConnection: () {}, + ), + ), + ), + ); + // First frame before async recent load settles. + await tester.pump(); + + expect( + find.byKey(const material.ValueKey('empty_section_loading')), + findsOneWidget, + ); + expect(find.text('Quick start'), findsNothing); + expect(find.text('Recent connections'), findsNothing); + }); + testWidgets('WorkspaceEmptyHero opens a recent connection', (tester) async { ConnectionRow? opened; From c1cde76165d5d035d98792b7a6c8fb79f736a874 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Tue, 28 Jul 2026 21:31:40 +0300 Subject: [PATCH 2/2] fix(ui): take Color for tree leaf tint to avoid ColorScheme clash shadcn and Material each define ColorScheme; pass primary Color instead so analyze stays clean. --- lib/core/sdui/sdui_tree_builder.dart | 2 +- lib/core/ui/querya_tree_tokens.dart | 7 +++++-- lib/features/connections/connections_panel_mysql.dart | 4 +++- lib/features/connections/connections_panel_pg_tree.dart | 4 +++- lib/features/connections/connections_panel_sqlite.dart | 4 +++- test/core/motion/querya_cross_fade_stack_test.dart | 4 ++-- 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/core/sdui/sdui_tree_builder.dart b/lib/core/sdui/sdui_tree_builder.dart index eccce458..8dce2670 100644 --- a/lib/core/sdui/sdui_tree_builder.dart +++ b/lib/core/sdui/sdui_tree_builder.dart @@ -208,7 +208,7 @@ class SduiTreeBuilderState extends material.State { final iconSize = canExpand ? QueryaIconSizes.treeGroup : QueryaIconSizes.treeLeaf; final iconColor = isBrowsable - ? QueryaTreeTokens.leafIconColor(theme.colorScheme) + ? QueryaTreeTokens.leafIconColor(theme.colorScheme.primary) : muted; final rowLeft = 8.0 + depth * QueryaTreeTokens.indent + (canExpand ? 0 : 4.0); diff --git a/lib/core/ui/querya_tree_tokens.dart b/lib/core/ui/querya_tree_tokens.dart index e9daae22..20ef57ca 100644 --- a/lib/core/ui/querya_tree_tokens.dart +++ b/lib/core/ui/querya_tree_tokens.dart @@ -6,6 +6,9 @@ abstract final class QueryaTreeTokens { static const double indent = 16; /// Leaf-row icon tint (tables, views, sequences, …). - static Color leafIconColor(ColorScheme scheme) => - scheme.primary.withValues(alpha: 0.5); + /// + /// Takes [primary] (not [ColorScheme]) so both Material and shadcn schemes + /// can pass `.primary` without a type clash. + static Color leafIconColor(Color primary) => + primary.withValues(alpha: 0.5); } diff --git a/lib/features/connections/connections_panel_mysql.dart b/lib/features/connections/connections_panel_mysql.dart index 1af080a3..37a0edb3 100644 --- a/lib/features/connections/connections_panel_mysql.dart +++ b/lib/features/connections/connections_panel_mysql.dart @@ -668,7 +668,9 @@ class _MysqlObjectGroupState extends State<_MysqlObjectGroup> { label: item, icon: widget.itemIcon, iconSize: QueryaIconSizes.treeLeaf, - iconColor: QueryaTreeTokens.leafIconColor(theme.colorScheme), + iconColor: QueryaTreeTokens.leafIconColor( + theme.colorScheme.primary, + ), textStyle: material.TextStyle( fontSize: 11, color: theme.colorScheme.foreground, diff --git a/lib/features/connections/connections_panel_pg_tree.dart b/lib/features/connections/connections_panel_pg_tree.dart index f25bb71c..f0afecd2 100644 --- a/lib/features/connections/connections_panel_pg_tree.dart +++ b/lib/features/connections/connections_panel_pg_tree.dart @@ -1045,7 +1045,9 @@ class _PgObjectGroupState extends State<_PgObjectGroup> { label: item, icon: widget.itemIcon, iconSize: QueryaIconSizes.treeLeaf, - iconColor: QueryaTreeTokens.leafIconColor(theme.colorScheme), + iconColor: QueryaTreeTokens.leafIconColor( + theme.colorScheme.primary, + ), textStyle: material.TextStyle( fontSize: 11, color: theme.colorScheme.foreground, diff --git a/lib/features/connections/connections_panel_sqlite.dart b/lib/features/connections/connections_panel_sqlite.dart index 861e0cd6..0563f513 100644 --- a/lib/features/connections/connections_panel_sqlite.dart +++ b/lib/features/connections/connections_panel_sqlite.dart @@ -398,7 +398,9 @@ class _SqliteObjectGroupState extends State<_SqliteObjectGroup> { label: item, icon: widget.itemIcon, iconSize: QueryaIconSizes.treeLeaf, - iconColor: QueryaTreeTokens.leafIconColor(theme.colorScheme), + iconColor: QueryaTreeTokens.leafIconColor( + theme.colorScheme.primary, + ), textStyle: material.TextStyle( fontSize: 11, color: theme.colorScheme.foreground, diff --git a/test/core/motion/querya_cross_fade_stack_test.dart b/test/core/motion/querya_cross_fade_stack_test.dart index 28b8b9da..36e0b2ef 100644 --- a/test/core/motion/querya_cross_fade_stack_test.dart +++ b/test/core/motion/querya_cross_fade_stack_test.dart @@ -79,11 +79,11 @@ void main() { testWidgets('QueryaCrossFadeStack uses exit curve when fading out', (WidgetTester tester) async { await tester.pumpWidget( - MaterialApp( + const MaterialApp( home: Scaffold( body: QueryaCrossFadeStack( index: 0, - children: const [ + children: [ Text('a', key: Key('a')), Text('b', key: Key('b')), ],