soften desktop chrome surfaces
This commit is contained in:
parent
0c463f4492
commit
9f53270746
@ -69,6 +69,7 @@ class _AppShellState extends State<AppShell> {
|
||||
builder: (context, constraints) {
|
||||
final palette = context.palette;
|
||||
final platform = Theme.of(context).platform;
|
||||
final brightness = Theme.of(context).brightness;
|
||||
final isCompactMobile =
|
||||
(platform == TargetPlatform.iOS ||
|
||||
platform == TargetPlatform.android) &&
|
||||
@ -272,10 +273,71 @@ class _AppShellState extends State<AppShell> {
|
||||
),
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: palette.canvas,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeBackground,
|
||||
palette.canvas,
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: -180,
|
||||
right: -80,
|
||||
child: IgnorePointer(
|
||||
child: Container(
|
||||
width: 420,
|
||||
height: 420,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: RadialGradient(
|
||||
colors: [
|
||||
palette.chromeHighlight
|
||||
.withValues(
|
||||
alpha:
|
||||
brightness ==
|
||||
Brightness.dark
|
||||
? 0.10
|
||||
: 0.58,
|
||||
),
|
||||
palette.chromeHighlight
|
||||
.withValues(alpha: 0),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: -220,
|
||||
left: -140,
|
||||
child: IgnorePointer(
|
||||
child: Container(
|
||||
width: 360,
|
||||
height: 360,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: RadialGradient(
|
||||
colors: [
|
||||
palette.chromeInset.withValues(
|
||||
alpha:
|
||||
brightness ==
|
||||
Brightness.dark
|
||||
? 0.10
|
||||
: 0.36,
|
||||
),
|
||||
palette.chromeInset.withValues(
|
||||
alpha: 0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildCurrentPage(controller.openDetail),
|
||||
],
|
||||
),
|
||||
@ -419,13 +481,24 @@ class _SidebarRevealRailState extends State<_SidebarRevealRail> {
|
||||
duration: const Duration(milliseconds: 180),
|
||||
width: _hovered ? 22 : 10,
|
||||
decoration: BoxDecoration(
|
||||
color: _hovered ? palette.surfaceSecondary : Colors.transparent,
|
||||
gradient: _hovered
|
||||
? LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(alpha: 0.92),
|
||||
palette.chromeSurface,
|
||||
],
|
||||
)
|
||||
: null,
|
||||
color: _hovered ? null : Colors.transparent,
|
||||
borderRadius: const BorderRadius.horizontal(
|
||||
right: Radius.circular(14),
|
||||
),
|
||||
border: Border.all(
|
||||
color: _hovered ? palette.strokeSoft : Colors.transparent,
|
||||
color: _hovered ? palette.chromeStroke : Colors.transparent,
|
||||
),
|
||||
boxShadow: _hovered ? [palette.chromeShadowLift] : const [],
|
||||
),
|
||||
child: _hovered
|
||||
? Icon(
|
||||
|
||||
@ -1086,9 +1086,17 @@ class _AssistantSideTabRail extends StatelessWidget {
|
||||
key: const Key('assistant-side-pane'),
|
||||
width: 46,
|
||||
decoration: BoxDecoration(
|
||||
color: palette.sidebar,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(alpha: 0.96),
|
||||
palette.chromeSurface,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: palette.sidebarBorder),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: [palette.chromeShadowAmbient],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
@ -1110,7 +1118,7 @@ class _AssistantSideTabRail extends StatelessWidget {
|
||||
),
|
||||
if (favoriteDestinations.isNotEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
Container(width: 24, height: 1, color: palette.strokeSoft),
|
||||
Container(width: 24, height: 1, color: palette.chromeStroke),
|
||||
const SizedBox(height: 4),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
@ -1146,6 +1154,14 @@ class _AssistantSideTabRail extends StatelessWidget {
|
||||
? appText('展开侧板', 'Expand side pane')
|
||||
: appText('收起侧板', 'Collapse side pane'),
|
||||
onPressed: onToggleCollapsed,
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: palette.chromeSurface,
|
||||
foregroundColor: palette.textSecondary,
|
||||
side: BorderSide(color: palette.chromeStroke),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
icon: Icon(
|
||||
collapsed
|
||||
? Icons.keyboard_double_arrow_right_rounded
|
||||
@ -1160,7 +1176,7 @@ class _AssistantSideTabRail extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _AssistantSideTabButton extends StatelessWidget {
|
||||
class _AssistantSideTabButton extends StatefulWidget {
|
||||
const _AssistantSideTabButton({
|
||||
super.key,
|
||||
required this.icon,
|
||||
@ -1174,31 +1190,64 @@ class _AssistantSideTabButton extends StatelessWidget {
|
||||
final String tooltip;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
State<_AssistantSideTabButton> createState() =>
|
||||
_AssistantSideTabButtonState();
|
||||
}
|
||||
|
||||
class _AssistantSideTabButtonState extends State<_AssistantSideTabButton> {
|
||||
bool _hovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final palette = context.palette;
|
||||
|
||||
return Tooltip(
|
||||
message: tooltip,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: selected ? palette.surfacePrimary : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: selected ? palette.strokeSoft : Colors.transparent,
|
||||
message: widget.tooltip,
|
||||
child: MouseRegion(
|
||||
onEnter: (_) => setState(() => _hovered = true),
|
||||
onExit: (_) => setState(() => _hovered = false),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
onTap: widget.onTap,
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
gradient: widget.selected || _hovered
|
||||
? LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(
|
||||
alpha: widget.selected ? 0.96 : 0.84,
|
||||
),
|
||||
widget.selected
|
||||
? palette.chromeSurface
|
||||
: palette.chromeSurfacePressed,
|
||||
],
|
||||
)
|
||||
: null,
|
||||
color: widget.selected || _hovered ? null : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: widget.selected || _hovered
|
||||
? palette.chromeStroke
|
||||
: Colors.transparent,
|
||||
),
|
||||
boxShadow: widget.selected
|
||||
? [palette.chromeShadowLift]
|
||||
: const [],
|
||||
),
|
||||
child: Icon(
|
||||
widget.icon,
|
||||
size: 18,
|
||||
color: widget.selected
|
||||
? palette.textPrimary
|
||||
: palette.textSecondary,
|
||||
),
|
||||
),
|
||||
child: Icon(
|
||||
icon,
|
||||
size: 18,
|
||||
color: selected ? palette.textPrimary : palette.textSecondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -1306,6 +1355,7 @@ class _ConversationArea extends StatelessWidget {
|
||||
return SurfaceCard(
|
||||
borderRadius: 0,
|
||||
padding: EdgeInsets.zero,
|
||||
tone: SurfaceCardTone.chrome,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
@ -1492,6 +1542,7 @@ class _AssistantTaskRail extends StatelessWidget {
|
||||
return SurfaceCard(
|
||||
borderRadius: 0,
|
||||
padding: EdgeInsets.zero,
|
||||
tone: SurfaceCardTone.chrome,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
@ -1970,6 +2021,7 @@ class _ComposerBar extends StatelessWidget {
|
||||
|
||||
return SurfaceCard(
|
||||
borderRadius: 10,
|
||||
tone: SurfaceCardTone.chrome,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -2060,11 +2112,11 @@ class _ComposerBar extends StatelessWidget {
|
||||
decoration: InputDecoration(
|
||||
isCollapsed: true,
|
||||
filled: true,
|
||||
fillColor: palette.surfacePrimary,
|
||||
fillColor: palette.chromeSurface,
|
||||
contentPadding: const EdgeInsets.fromLTRB(10, 8, 10, 8),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: palette.strokeSoft),
|
||||
borderSide: BorderSide(color: palette.chromeStroke),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
@ -2376,27 +2428,50 @@ class _ComposerBar extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ComposerIconButton extends StatelessWidget {
|
||||
class _ComposerIconButton extends StatefulWidget {
|
||||
const _ComposerIconButton({required this.icon});
|
||||
|
||||
final IconData icon;
|
||||
|
||||
@override
|
||||
State<_ComposerIconButton> createState() => _ComposerIconButtonState();
|
||||
}
|
||||
|
||||
class _ComposerIconButtonState extends State<_ComposerIconButton> {
|
||||
bool _hovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: context.palette.surfaceSecondary,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: context.palette.strokeSoft),
|
||||
final palette = context.palette;
|
||||
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(() => _hovered = true),
|
||||
onExit: (_) => setState(() => _hovered = false),
|
||||
child: Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(alpha: _hovered ? 0.94 : 0.88),
|
||||
_hovered ? palette.chromeSurfacePressed : palette.chromeSurface,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: [
|
||||
_hovered ? palette.chromeShadowLift : palette.chromeShadowAmbient,
|
||||
],
|
||||
),
|
||||
child: Icon(widget.icon, size: 18, color: palette.textMuted),
|
||||
),
|
||||
child: Icon(icon, size: 18, color: context.palette.textMuted),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ComposerToolbarChip extends StatelessWidget {
|
||||
class _ComposerToolbarChip extends StatefulWidget {
|
||||
const _ComposerToolbarChip({
|
||||
super.key,
|
||||
required this.icon,
|
||||
@ -2415,43 +2490,64 @@ class _ComposerToolbarChip extends StatelessWidget {
|
||||
final double maxLabelWidth;
|
||||
final EdgeInsetsGeometry padding;
|
||||
|
||||
@override
|
||||
State<_ComposerToolbarChip> createState() => _ComposerToolbarChipState();
|
||||
}
|
||||
|
||||
class _ComposerToolbarChipState extends State<_ComposerToolbarChip> {
|
||||
bool _hovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final palette = context.palette;
|
||||
|
||||
return Container(
|
||||
padding: padding,
|
||||
decoration: BoxDecoration(
|
||||
color: palette.surfaceSecondary,
|
||||
borderRadius: BorderRadius.circular(AppRadius.chip),
|
||||
border: Border.all(color: palette.strokeSoft),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 13, color: palette.textMuted),
|
||||
const SizedBox(width: 4),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: maxLabelWidth),
|
||||
child: Text(
|
||||
label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.labelLarge?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(() => _hovered = true),
|
||||
onExit: (_) => setState(() => _hovered = false),
|
||||
child: Container(
|
||||
padding: widget.padding,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(alpha: _hovered ? 0.94 : 0.88),
|
||||
_hovered ? palette.chromeSurfacePressed : palette.chromeSurface,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppRadius.chip),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: [
|
||||
_hovered ? palette.chromeShadowLift : palette.chromeShadowAmbient,
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(widget.icon, size: 13, color: palette.textMuted),
|
||||
const SizedBox(width: 4),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: widget.maxLabelWidth),
|
||||
child: Text(
|
||||
widget.label,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.labelLarge?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showChevron) ...[
|
||||
const SizedBox(width: 2),
|
||||
Icon(
|
||||
Icons.keyboard_arrow_down_rounded,
|
||||
size: 14,
|
||||
color: palette.textMuted,
|
||||
),
|
||||
if (widget.showChevron) ...[
|
||||
const SizedBox(width: 2),
|
||||
Icon(
|
||||
Icons.keyboard_arrow_down_rounded,
|
||||
size: 14,
|
||||
color: palette.textMuted,
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -6,6 +6,14 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
required this.canvas,
|
||||
required this.sidebar,
|
||||
required this.sidebarBorder,
|
||||
required this.chromeBackground,
|
||||
required this.chromeSurface,
|
||||
required this.chromeSurfacePressed,
|
||||
required this.chromeHighlight,
|
||||
required this.chromeStroke,
|
||||
required this.chromeInset,
|
||||
required this.chromeShadowAmbient,
|
||||
required this.chromeShadowLift,
|
||||
required this.surfacePrimary,
|
||||
required this.surfaceSecondary,
|
||||
required this.surfaceTertiary,
|
||||
@ -28,6 +36,14 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
final Color canvas;
|
||||
final Color sidebar;
|
||||
final Color sidebarBorder;
|
||||
final Color chromeBackground;
|
||||
final Color chromeSurface;
|
||||
final Color chromeSurfacePressed;
|
||||
final Color chromeHighlight;
|
||||
final Color chromeStroke;
|
||||
final Color chromeInset;
|
||||
final BoxShadow chromeShadowAmbient;
|
||||
final BoxShadow chromeShadowLift;
|
||||
final Color surfacePrimary;
|
||||
final Color surfaceSecondary;
|
||||
final Color surfaceTertiary;
|
||||
@ -50,6 +66,24 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
canvas: Color(0xFFF5F5F7),
|
||||
sidebar: Color(0xFFF1F1F3),
|
||||
sidebarBorder: Color(0xFFE5E5EA),
|
||||
chromeBackground: Color(0xFFF6F4F1),
|
||||
chromeSurface: Color(0xFFFCFBF9),
|
||||
chromeSurfacePressed: Color(0xFFF2EFEB),
|
||||
chromeHighlight: Color(0xFFFFFFFF),
|
||||
chromeStroke: Color(0xFFECE7E0),
|
||||
chromeInset: Color(0xFFF4F1EC),
|
||||
chromeShadowAmbient: BoxShadow(
|
||||
color: Color(0x12000000),
|
||||
blurRadius: 30,
|
||||
offset: Offset(0, 10),
|
||||
spreadRadius: -10,
|
||||
),
|
||||
chromeShadowLift: BoxShadow(
|
||||
color: Color(0x14000000),
|
||||
blurRadius: 14,
|
||||
offset: Offset(0, 5),
|
||||
spreadRadius: -6,
|
||||
),
|
||||
surfacePrimary: Color(0xFFFFFFFF),
|
||||
surfaceSecondary: Color(0xFFF1F1F3),
|
||||
surfaceTertiary: Color(0xFFECECEF),
|
||||
@ -73,6 +107,24 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
canvas: Color(0xFF0E0F12),
|
||||
sidebar: Color(0xFF15171C),
|
||||
sidebarBorder: Color(0xFF23262D),
|
||||
chromeBackground: Color(0xFF111318),
|
||||
chromeSurface: Color(0xFF1A1D23),
|
||||
chromeSurfacePressed: Color(0xFF16181D),
|
||||
chromeHighlight: Color(0xFF2A2E36),
|
||||
chromeStroke: Color(0xFF2A2F38),
|
||||
chromeInset: Color(0xFF1E2229),
|
||||
chromeShadowAmbient: BoxShadow(
|
||||
color: Color(0x52000000),
|
||||
blurRadius: 28,
|
||||
offset: Offset(0, 10),
|
||||
spreadRadius: -10,
|
||||
),
|
||||
chromeShadowLift: BoxShadow(
|
||||
color: Color(0x68000000),
|
||||
blurRadius: 12,
|
||||
offset: Offset(0, 4),
|
||||
spreadRadius: -6,
|
||||
),
|
||||
surfacePrimary: Color(0xFF15171C),
|
||||
surfaceSecondary: Color(0xFF1B1E24),
|
||||
surfaceTertiary: Color(0xFF22262E),
|
||||
@ -97,6 +149,14 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
Color? canvas,
|
||||
Color? sidebar,
|
||||
Color? sidebarBorder,
|
||||
Color? chromeBackground,
|
||||
Color? chromeSurface,
|
||||
Color? chromeSurfacePressed,
|
||||
Color? chromeHighlight,
|
||||
Color? chromeStroke,
|
||||
Color? chromeInset,
|
||||
BoxShadow? chromeShadowAmbient,
|
||||
BoxShadow? chromeShadowLift,
|
||||
Color? surfacePrimary,
|
||||
Color? surfaceSecondary,
|
||||
Color? surfaceTertiary,
|
||||
@ -119,6 +179,14 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
canvas: canvas ?? this.canvas,
|
||||
sidebar: sidebar ?? this.sidebar,
|
||||
sidebarBorder: sidebarBorder ?? this.sidebarBorder,
|
||||
chromeBackground: chromeBackground ?? this.chromeBackground,
|
||||
chromeSurface: chromeSurface ?? this.chromeSurface,
|
||||
chromeSurfacePressed: chromeSurfacePressed ?? this.chromeSurfacePressed,
|
||||
chromeHighlight: chromeHighlight ?? this.chromeHighlight,
|
||||
chromeStroke: chromeStroke ?? this.chromeStroke,
|
||||
chromeInset: chromeInset ?? this.chromeInset,
|
||||
chromeShadowAmbient: chromeShadowAmbient ?? this.chromeShadowAmbient,
|
||||
chromeShadowLift: chromeShadowLift ?? this.chromeShadowLift,
|
||||
surfacePrimary: surfacePrimary ?? this.surfacePrimary,
|
||||
surfaceSecondary: surfaceSecondary ?? this.surfaceSecondary,
|
||||
surfaceTertiary: surfaceTertiary ?? this.surfaceTertiary,
|
||||
@ -153,6 +221,26 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
sidebar: Color.lerp(sidebar, other.sidebar, t) ?? sidebar,
|
||||
sidebarBorder:
|
||||
Color.lerp(sidebarBorder, other.sidebarBorder, t) ?? sidebarBorder,
|
||||
chromeBackground:
|
||||
Color.lerp(chromeBackground, other.chromeBackground, t) ??
|
||||
chromeBackground,
|
||||
chromeSurface:
|
||||
Color.lerp(chromeSurface, other.chromeSurface, t) ?? chromeSurface,
|
||||
chromeSurfacePressed:
|
||||
Color.lerp(chromeSurfacePressed, other.chromeSurfacePressed, t) ??
|
||||
chromeSurfacePressed,
|
||||
chromeHighlight:
|
||||
Color.lerp(chromeHighlight, other.chromeHighlight, t) ??
|
||||
chromeHighlight,
|
||||
chromeStroke:
|
||||
Color.lerp(chromeStroke, other.chromeStroke, t) ?? chromeStroke,
|
||||
chromeInset: Color.lerp(chromeInset, other.chromeInset, t) ?? chromeInset,
|
||||
chromeShadowAmbient:
|
||||
BoxShadow.lerp(chromeShadowAmbient, other.chromeShadowAmbient, t) ??
|
||||
chromeShadowAmbient,
|
||||
chromeShadowLift:
|
||||
BoxShadow.lerp(chromeShadowLift, other.chromeShadowLift, t) ??
|
||||
chromeShadowLift,
|
||||
surfacePrimary:
|
||||
Color.lerp(surfacePrimary, other.surfacePrimary, t) ?? surfacePrimary,
|
||||
surfaceSecondary:
|
||||
|
||||
@ -54,6 +54,7 @@ class _AssistantFocusPanelState extends State<AssistantFocusPanel> {
|
||||
return SurfaceCard(
|
||||
borderRadius: 16,
|
||||
padding: EdgeInsets.zero,
|
||||
tone: SurfaceCardTone.chrome,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
@ -109,9 +110,17 @@ class _AssistantFocusPanelState extends State<AssistantFocusPanel> {
|
||||
width: 38,
|
||||
height: 38,
|
||||
decoration: BoxDecoration(
|
||||
color: palette.surfaceSecondary,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(alpha: 0.94),
|
||||
palette.chromeSurfacePressed,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: palette.strokeSoft),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: [palette.chromeShadowLift],
|
||||
),
|
||||
child: Icon(
|
||||
Icons.add_rounded,
|
||||
@ -289,10 +298,16 @@ class _AssistantFocusPreview extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return switch (destination) {
|
||||
WorkspaceDestination.tasks => _TasksFocusPreview(controller: controller),
|
||||
WorkspaceDestination.skills => _SkillsFocusPreview(controller: controller),
|
||||
WorkspaceDestination.skills => _SkillsFocusPreview(
|
||||
controller: controller,
|
||||
),
|
||||
WorkspaceDestination.nodes => _NodesFocusPreview(controller: controller),
|
||||
WorkspaceDestination.agents => _AgentsFocusPreview(controller: controller),
|
||||
WorkspaceDestination.mcpServer => _McpFocusPreview(controller: controller),
|
||||
WorkspaceDestination.agents => _AgentsFocusPreview(
|
||||
controller: controller,
|
||||
),
|
||||
WorkspaceDestination.mcpServer => _McpFocusPreview(
|
||||
controller: controller,
|
||||
),
|
||||
WorkspaceDestination.clawHub => _ClawHubFocusPreview(
|
||||
controller: controller,
|
||||
),
|
||||
@ -353,10 +368,14 @@ class _TasksFocusPreview extends StatelessWidget {
|
||||
const SizedBox(height: 12),
|
||||
if (items.isEmpty)
|
||||
_PreviewEmptyState(
|
||||
message: controller.connection.status ==
|
||||
message:
|
||||
controller.connection.status ==
|
||||
RuntimeConnectionStatus.connected
|
||||
? appText('当前没有任务摘要。', 'No task summary yet.')
|
||||
: appText('连接 Gateway 后这里会显示任务摘要。', 'Connect a gateway to load task summaries.'),
|
||||
: appText(
|
||||
'连接 Gateway 后这里会显示任务摘要。',
|
||||
'Connect a gateway to load task summaries.',
|
||||
),
|
||||
)
|
||||
else
|
||||
...items.map(
|
||||
@ -384,9 +403,16 @@ class _SkillsFocusPreview extends StatelessWidget {
|
||||
final items = controller.skills.take(4).toList(growable: false);
|
||||
if (items.isEmpty) {
|
||||
return _PreviewEmptyState(
|
||||
message: controller.connection.status == RuntimeConnectionStatus.connected
|
||||
? appText('当前代理没有已加载技能。', 'No skills are loaded for the active agent.')
|
||||
: appText('连接 Gateway 后可查看技能摘要。', 'Connect a gateway to inspect skills here.'),
|
||||
message:
|
||||
controller.connection.status == RuntimeConnectionStatus.connected
|
||||
? appText(
|
||||
'当前代理没有已加载技能。',
|
||||
'No skills are loaded for the active agent.',
|
||||
)
|
||||
: appText(
|
||||
'连接 Gateway 后可查看技能摘要。',
|
||||
'Connect a gateway to inspect skills here.',
|
||||
),
|
||||
);
|
||||
}
|
||||
return Column(
|
||||
@ -430,11 +456,11 @@ class _NodesFocusPreview extends StatelessWidget {
|
||||
title: instance.host?.trim().isNotEmpty == true
|
||||
? instance.host!
|
||||
: instance.id,
|
||||
subtitle: [
|
||||
instance.platform,
|
||||
instance.deviceFamily,
|
||||
instance.ip,
|
||||
].whereType<String>().where((item) => item.trim().isNotEmpty).join(' · '),
|
||||
subtitle:
|
||||
[instance.platform, instance.deviceFamily, instance.ip]
|
||||
.whereType<String>()
|
||||
.where((item) => item.trim().isNotEmpty)
|
||||
.join(' · '),
|
||||
trailing: instance.mode ?? appText('未知', 'Unknown'),
|
||||
),
|
||||
),
|
||||
@ -662,13 +688,19 @@ class _SettingsFocusPreview extends StatelessWidget {
|
||||
const SizedBox(height: 8),
|
||||
_FocusListTile(
|
||||
title: appText('执行目标', 'Execution target'),
|
||||
subtitle: appText('Assistant 默认运行位置', 'Default assistant execution target'),
|
||||
subtitle: appText(
|
||||
'Assistant 默认运行位置',
|
||||
'Default assistant execution target',
|
||||
),
|
||||
trailing: controller.assistantExecutionTarget.label,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_FocusListTile(
|
||||
title: appText('权限', 'Permissions'),
|
||||
subtitle: appText('Assistant 默认权限级别', 'Default assistant permission level'),
|
||||
subtitle: appText(
|
||||
'Assistant 默认权限级别',
|
||||
'Default assistant permission level',
|
||||
),
|
||||
trailing: controller.assistantPermissionLevel.label,
|
||||
),
|
||||
],
|
||||
|
||||
@ -97,9 +97,17 @@ class DesktopWorkspaceScaffold extends StatelessWidget {
|
||||
Expanded(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: palette.surfacePrimary,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(alpha: 0.96),
|
||||
palette.chromeSurface,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: palette.strokeSoft),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: [palette.chromeShadowAmbient],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
|
||||
@ -36,9 +36,17 @@ class SectionTabs extends StatelessWidget {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.xxs),
|
||||
decoration: BoxDecoration(
|
||||
color: palette.surfaceSecondary,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(alpha: 0.94),
|
||||
palette.chromeSurfacePressed,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppRadius.chip),
|
||||
border: Border.all(color: palette.strokeSoft),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: [palette.chromeShadowAmbient],
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
@ -92,15 +100,28 @@ class _SectionTabChipState extends State<_SectionTabChip> {
|
||||
duration: const Duration(milliseconds: 160),
|
||||
curve: Curves.easeInOut,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.selected
|
||||
? palette.surfacePrimary
|
||||
: _hovered
|
||||
? palette.surfaceTertiary
|
||||
: Colors.transparent,
|
||||
gradient: widget.selected || _hovered
|
||||
? LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(
|
||||
alpha: widget.selected ? 0.96 : 0.86,
|
||||
),
|
||||
widget.selected
|
||||
? palette.chromeSurface
|
||||
: palette.chromeSurfacePressed,
|
||||
],
|
||||
)
|
||||
: null,
|
||||
color: widget.selected || _hovered ? null : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(AppRadius.button),
|
||||
border: Border.all(
|
||||
color: widget.selected ? palette.stroke : Colors.transparent,
|
||||
color: widget.selected || _hovered
|
||||
? palette.chromeStroke
|
||||
: Colors.transparent,
|
||||
),
|
||||
boxShadow: widget.selected ? [palette.chromeShadowLift] : const [],
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
|
||||
@ -81,9 +81,17 @@ class SidebarNavigation extends StatelessWidget {
|
||||
height: double.infinity,
|
||||
margin: marginOverride ?? const EdgeInsets.fromLTRB(4, 4, 4, 0),
|
||||
decoration: BoxDecoration(
|
||||
color: palette.sidebar,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(alpha: 0.96),
|
||||
palette.chromeSurface,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppRadius.sidebar),
|
||||
border: Border.all(color: palette.sidebarBorder),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: [palette.chromeShadowAmbient],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),
|
||||
@ -184,8 +192,16 @@ class SidebarHeader extends StatelessWidget {
|
||||
height: isCollapsed ? 36 : 28,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: palette.surfaceSecondary,
|
||||
border: Border.all(color: palette.strokeSoft),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(alpha: 0.94),
|
||||
palette.chromeSurfacePressed,
|
||||
],
|
||||
),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: [palette.chromeShadowLift],
|
||||
),
|
||||
child: Icon(
|
||||
Icons.crop_square_rounded,
|
||||
@ -310,9 +326,9 @@ class _SidebarNavItemState extends State<_SidebarNavItem> {
|
||||
final theme = Theme.of(context);
|
||||
final isPrimary = widget.emphasis == _SidebarItemEmphasis.primary;
|
||||
final background = widget.selected
|
||||
? palette.surfacePrimary
|
||||
? palette.chromeSurface
|
||||
: _hovered
|
||||
? palette.surfaceTertiary
|
||||
? palette.chromeSurfacePressed
|
||||
: Colors.transparent;
|
||||
final iconColor = widget.selected
|
||||
? palette.textPrimary
|
||||
@ -328,11 +344,26 @@ class _SidebarNavItemState extends State<_SidebarNavItem> {
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 160),
|
||||
decoration: BoxDecoration(
|
||||
color: background,
|
||||
gradient: widget.selected || _hovered
|
||||
? LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(
|
||||
alpha: widget.selected ? 0.96 : 0.82,
|
||||
),
|
||||
background,
|
||||
],
|
||||
)
|
||||
: null,
|
||||
color: widget.selected || _hovered ? null : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(radius),
|
||||
border: Border.all(
|
||||
color: widget.selected ? palette.strokeSoft : Colors.transparent,
|
||||
color: widget.selected || _hovered
|
||||
? palette.chromeStroke
|
||||
: Colors.transparent,
|
||||
),
|
||||
boxShadow: widget.selected ? [palette.chromeShadowLift] : const [],
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
@ -509,7 +540,7 @@ class SidebarFooter extends StatelessWidget {
|
||||
children: [
|
||||
Container(
|
||||
height: 1,
|
||||
color: palette.sidebarBorder.withValues(alpha: 0.7),
|
||||
color: palette.chromeStroke.withValues(alpha: 0.9),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
_SidebarLanguageButton(
|
||||
@ -569,7 +600,7 @@ class SidebarFooter extends StatelessWidget {
|
||||
children: [
|
||||
Container(
|
||||
height: 1,
|
||||
color: palette.sidebarBorder.withValues(alpha: 0.7),
|
||||
color: palette.chromeStroke.withValues(alpha: 0.9),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
_SidebarNavItem(
|
||||
@ -665,8 +696,8 @@ class _SidebarActionButtonState extends State<_SidebarActionButton> {
|
||||
Widget build(BuildContext context) {
|
||||
final palette = context.palette;
|
||||
final resolvedBackground = _hovered
|
||||
? palette.surfaceTertiary
|
||||
: palette.surfaceSecondary;
|
||||
? palette.chromeSurfacePressed
|
||||
: palette.chromeSurface;
|
||||
|
||||
return Tooltip(
|
||||
message: widget.tooltip ?? '',
|
||||
@ -676,9 +707,21 @@ class _SidebarActionButtonState extends State<_SidebarActionButton> {
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 160),
|
||||
decoration: BoxDecoration(
|
||||
color: resolvedBackground,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(
|
||||
alpha: _hovered ? 0.94 : 0.88,
|
||||
),
|
||||
resolvedBackground,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppRadius.button),
|
||||
border: Border.all(color: palette.strokeSoft),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: [
|
||||
_hovered ? palette.chromeShadowLift : palette.chromeShadowAmbient,
|
||||
],
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
@ -732,9 +775,9 @@ class _SidebarAccountTileState extends State<_SidebarAccountTile> {
|
||||
Widget build(BuildContext context) {
|
||||
final palette = context.palette;
|
||||
final background = widget.selected
|
||||
? palette.surfacePrimary
|
||||
? palette.chromeSurface
|
||||
: _hovered
|
||||
? palette.surfaceTertiary
|
||||
? palette.chromeSurfacePressed
|
||||
: Colors.transparent;
|
||||
|
||||
return MouseRegion(
|
||||
@ -745,11 +788,26 @@ class _SidebarAccountTileState extends State<_SidebarAccountTile> {
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 160),
|
||||
decoration: BoxDecoration(
|
||||
color: background,
|
||||
gradient: widget.selected || _hovered
|
||||
? LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(
|
||||
alpha: widget.selected ? 0.96 : 0.84,
|
||||
),
|
||||
background,
|
||||
],
|
||||
)
|
||||
: null,
|
||||
color: widget.selected || _hovered ? null : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(AppRadius.button),
|
||||
border: Border.all(
|
||||
color: widget.selected ? palette.strokeSoft : Colors.transparent,
|
||||
color: widget.selected || _hovered
|
||||
? palette.chromeStroke
|
||||
: Colors.transparent,
|
||||
),
|
||||
boxShadow: widget.selected ? [palette.chromeShadowLift] : const [],
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
@ -858,11 +916,25 @@ class _SidebarLanguageButtonState extends State<_SidebarLanguageButton> {
|
||||
height: size,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: _hovered
|
||||
? palette.surfaceTertiary
|
||||
: palette.surfaceSecondary,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(
|
||||
alpha: _hovered ? 0.94 : 0.88,
|
||||
),
|
||||
_hovered
|
||||
? palette.chromeSurfacePressed
|
||||
: palette.chromeSurface,
|
||||
],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppRadius.button),
|
||||
border: Border.all(color: palette.strokeSoft),
|
||||
border: Border.all(color: palette.chromeStroke),
|
||||
boxShadow: [
|
||||
_hovered
|
||||
? palette.chromeShadowLift
|
||||
: palette.chromeShadowAmbient,
|
||||
],
|
||||
),
|
||||
child: Text(
|
||||
widget.appLanguage.compactLabel,
|
||||
|
||||
@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
|
||||
import '../theme/app_palette.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
|
||||
enum SurfaceCardTone { standard, chrome }
|
||||
|
||||
class SurfaceCard extends StatefulWidget {
|
||||
const SurfaceCard({
|
||||
super.key,
|
||||
@ -11,6 +13,7 @@ class SurfaceCard extends StatefulWidget {
|
||||
this.onTap,
|
||||
this.borderRadius = AppRadius.card,
|
||||
this.color,
|
||||
this.tone = SurfaceCardTone.standard,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
@ -18,6 +21,7 @@ class SurfaceCard extends StatefulWidget {
|
||||
final VoidCallback? onTap;
|
||||
final double borderRadius;
|
||||
final Color? color;
|
||||
final SurfaceCardTone tone;
|
||||
|
||||
@override
|
||||
State<SurfaceCard> createState() => _SurfaceCardState();
|
||||
@ -29,7 +33,41 @@ class _SurfaceCardState extends State<SurfaceCard> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final palette = context.palette;
|
||||
final baseColor = widget.color ?? palette.surfacePrimary;
|
||||
final baseColor = switch (widget.tone) {
|
||||
SurfaceCardTone.standard => widget.color ?? palette.surfacePrimary,
|
||||
SurfaceCardTone.chrome => widget.color ?? palette.chromeSurface,
|
||||
};
|
||||
final hoveredColor = switch (widget.tone) {
|
||||
SurfaceCardTone.standard => palette.surfaceSecondary,
|
||||
SurfaceCardTone.chrome => palette.chromeSurfacePressed,
|
||||
};
|
||||
final borderColor = switch (widget.tone) {
|
||||
SurfaceCardTone.standard => palette.strokeSoft,
|
||||
SurfaceCardTone.chrome => palette.chromeStroke,
|
||||
};
|
||||
final decoration = switch (widget.tone) {
|
||||
SurfaceCardTone.standard => BoxDecoration(
|
||||
color: _hovered && widget.onTap != null ? hoveredColor : baseColor,
|
||||
border: Border.all(color: borderColor),
|
||||
borderRadius: BorderRadius.circular(widget.borderRadius),
|
||||
),
|
||||
SurfaceCardTone.chrome => BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
palette.chromeHighlight.withValues(alpha: 0.94),
|
||||
_hovered && widget.onTap != null ? hoveredColor : baseColor,
|
||||
],
|
||||
),
|
||||
border: Border.all(color: borderColor),
|
||||
borderRadius: BorderRadius.circular(widget.borderRadius),
|
||||
boxShadow: [
|
||||
palette.chromeShadowAmbient,
|
||||
if (_hovered && widget.onTap != null) palette.chromeShadowLift,
|
||||
],
|
||||
),
|
||||
};
|
||||
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(() => _hovered = true),
|
||||
@ -37,13 +75,7 @@ class _SurfaceCardState extends State<SurfaceCard> {
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 180),
|
||||
curve: Curves.easeInOut,
|
||||
decoration: BoxDecoration(
|
||||
color: _hovered && widget.onTap != null
|
||||
? palette.surfaceSecondary
|
||||
: baseColor,
|
||||
border: Border.all(color: palette.strokeSoft),
|
||||
borderRadius: BorderRadius.circular(widget.borderRadius),
|
||||
),
|
||||
decoration: decoration,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user