Adopt document workspace visual baseline

This commit is contained in:
Haitao Pan 2026-03-26 20:31:24 +08:00
parent bd85386887
commit 1aab170290
9 changed files with 212 additions and 505 deletions

View File

@ -63,7 +63,6 @@ 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) &&
@ -287,75 +286,9 @@ class _AppShellState extends State<AppShell> {
),
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeBackground,
palette.canvas,
],
stops: const [0.0, 0.68],
),
),
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.14
: 0.42,
),
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.14
: 0.24,
),
palette.chromeInset.withValues(
alpha: 0,
),
],
),
),
),
),
),
_buildCurrentPage(controller.openDetail),
],
color: palette.canvas,
),
child: _buildCurrentPage(controller.openDetail),
),
),
),
@ -449,24 +382,13 @@ class _SidebarRevealRailState extends State<_SidebarRevealRail> {
duration: const Duration(milliseconds: 180),
width: _hovered ? 22 : 10,
decoration: BoxDecoration(
gradient: _hovered
? LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeHighlight.withValues(alpha: 0.92),
palette.chromeSurface,
],
)
: null,
color: _hovered ? null : Colors.transparent,
color: _hovered ? palette.surfacePrimary : Colors.transparent,
borderRadius: const BorderRadius.horizontal(
right: Radius.circular(14),
),
border: Border.all(
color: _hovered ? palette.chromeStroke : Colors.transparent,
color: _hovered ? palette.strokeSoft : Colors.transparent,
),
boxShadow: _hovered ? [palette.chromeShadowLift] : const [],
),
child: _hovered
? Icon(

View File

@ -251,38 +251,8 @@ class _WebShellBody extends StatelessWidget {
return Padding(
padding: const EdgeInsets.only(top: 4, right: 4),
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [palette.chromeBackground, palette.canvas],
stops: const [0.0, 0.68],
),
),
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: 0.32),
palette.chromeHighlight.withValues(alpha: 0),
],
),
),
),
),
),
child,
],
),
decoration: BoxDecoration(color: palette.canvas),
child: child,
),
);
}

View File

@ -1604,17 +1604,9 @@ class _AssistantSideTabRail extends StatelessWidget {
key: const Key('assistant-side-pane'),
width: 46,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeHighlight.withValues(alpha: 0.96),
palette.chromeSurface,
],
),
color: palette.chromeSurface,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: palette.chromeStroke),
boxShadow: [palette.chromeShadowAmbient],
border: Border.all(color: palette.strokeSoft),
),
child: Column(
children: [
@ -1636,7 +1628,7 @@ class _AssistantSideTabRail extends StatelessWidget {
),
if (favoriteDestinations.isNotEmpty) ...[
const SizedBox(height: 4),
Container(width: 24, height: 1, color: palette.chromeStroke),
Container(width: 24, height: 1, color: palette.strokeSoft),
const SizedBox(height: 4),
Expanded(
child: SingleChildScrollView(
@ -1673,9 +1665,9 @@ class _AssistantSideTabRail extends StatelessWidget {
: appText('收起侧板', 'Collapse side pane'),
onPressed: onToggleCollapsed,
style: IconButton.styleFrom(
backgroundColor: palette.chromeSurface,
backgroundColor: palette.surfacePrimary,
foregroundColor: palette.textSecondary,
side: BorderSide(color: palette.chromeStroke),
side: BorderSide(color: palette.strokeSoft),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
@ -1734,30 +1726,17 @@ class _AssistantSideTabButtonState extends State<_AssistantSideTabButton> {
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,
color: widget.selected
? palette.surfacePrimary
: _hovered
? palette.surfaceSecondary
: Colors.transparent,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: widget.selected || _hovered
? palette.chromeStroke
? palette.strokeSoft
: Colors.transparent,
),
boxShadow: widget.selected
? [palette.chromeShadowLift]
: const [],
),
child: Icon(
widget.icon,
@ -1903,22 +1882,20 @@ class _ConversationArea extends StatelessWidget {
children: [
Padding(
padding: EdgeInsets.fromLTRB(10, 8, 10 + topTrailingInset, 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Spacer(),
Row(
mainAxisSize: MainAxisSize.min,
children: [
_MessageViewModeChip(
value: messageViewMode,
onSelected: onMessageViewModeChanged,
),
const SizedBox(width: 6),
_ConnectionChip(controller: controller),
],
),
],
child: Align(
alignment: Alignment.centerRight,
child: Wrap(
spacing: 6,
runSpacing: 6,
alignment: WrapAlignment.end,
children: [
_MessageViewModeChip(
value: messageViewMode,
onSelected: onMessageViewModeChanged,
),
_ConnectionChip(controller: controller),
],
),
),
),
Divider(height: 1, color: palette.strokeSoft),
@ -3248,16 +3225,16 @@ class _ComposerBarState extends State<_ComposerBar> {
decoration: InputDecoration(
isCollapsed: true,
filled: true,
fillColor: palette.chromeSurface,
fillColor: palette.surfacePrimary,
contentPadding: const EdgeInsets.fromLTRB(10, 8, 10, 8),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(color: palette.chromeStroke),
borderSide: BorderSide(color: palette.strokeSoft),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: palette.accent.withValues(alpha: 0.18),
color: palette.accent.withValues(alpha: 0.24),
),
),
hintText: appText(
@ -3522,19 +3499,9 @@ class _ComposerIconButtonState extends State<_ComposerIconButton> {
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,
],
),
color: _hovered ? palette.surfaceSecondary : palette.surfacePrimary,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: palette.chromeStroke),
boxShadow: [
_hovered ? palette.chromeShadowLift : palette.chromeShadowAmbient,
],
border: Border.all(color: palette.strokeSoft),
),
child: Icon(widget.icon, size: 18, color: palette.textMuted),
),
@ -3580,21 +3547,9 @@ class _ComposerToolbarChipState extends State<_ComposerToolbarChip> {
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,
],
),
color: _hovered ? palette.surfaceSecondary : palette.surfacePrimary,
borderRadius: BorderRadius.circular(AppRadius.chip),
border: Border.all(color: palette.chromeStroke),
boxShadow: [
_hovered ? palette.chromeShadowLift : palette.chromeShadowAmbient,
],
border: Border.all(color: palette.strokeSoft),
),
child: Row(
mainAxisSize: MainAxisSize.min,
@ -3654,12 +3609,12 @@ class _SingleAgentProviderBadge extends StatelessWidget {
decoration: BoxDecoration(
color: isAuto
? palette.accent.withValues(alpha: 0.16)
: palette.chromeSurfacePressed,
: palette.surfaceSecondary,
borderRadius: BorderRadius.circular(999),
border: Border.all(
color: isAuto
? palette.accent.withValues(alpha: 0.4)
: palette.chromeStroke,
: palette.strokeSoft,
),
),
child: Text(
@ -3719,33 +3674,38 @@ class _MessageBubble extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
final palette = context.palette;
final borderColor = switch (tone) {
_BubbleTone.user => theme.colorScheme.primary.withValues(alpha: 0.10),
_BubbleTone.agent => theme.colorScheme.tertiary.withValues(alpha: 0.10),
_BubbleTone.assistant => palette.surfaceSecondary,
final backgroundColor = switch (tone) {
_BubbleTone.user => palette.surfaceSecondary,
_BubbleTone.agent => palette.surfaceTertiary.withValues(alpha: 0.78),
_BubbleTone.assistant => palette.surfacePrimary,
};
final labelColor = switch (tone) {
_BubbleTone.user => palette.textSecondary,
_BubbleTone.agent => palette.success,
_BubbleTone.assistant => palette.textMuted,
};
return Align(
alignment: alignRight ? Alignment.centerRight : Alignment.centerLeft,
alignment: Alignment.centerLeft,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 760),
child: Container(
padding: const EdgeInsets.all(12),
padding: const EdgeInsets.fromLTRB(14, 12, 14, 12),
decoration: BoxDecoration(
color: alignRight ? palette.accentMuted : palette.surfacePrimary,
color: backgroundColor,
borderRadius: BorderRadius.circular(AppRadius.card),
boxShadow: [
BoxShadow(
color: borderColor.withValues(alpha: 0.24),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
border: Border.all(color: palette.strokeSoft),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label, style: theme.textTheme.labelLarge),
Text(
label,
style: theme.textTheme.labelMedium?.copyWith(
color: labelColor,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),
_MessageBubbleBody(
text: text.isEmpty ? appText('暂无内容。', 'No content yet.') : text,
@ -3793,6 +3753,7 @@ class _MessageBubbleBodyState extends State<_MessageBubbleBody> {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final palette = context.palette;
if (!widget.renderMarkdown) {
final parsed = _PromptDebugSnapshot.fromMessage(widget.text);
final canCompactMetadata =
@ -3883,7 +3844,19 @@ class _MessageBubbleBodyState extends State<_MessageBubbleBody> {
final styleSheet = MarkdownStyleSheet.fromTheme(theme).copyWith(
p: theme.textTheme.bodyLarge?.copyWith(
color: theme.colorScheme.onSurface,
height: 1.45,
height: 1.55,
),
h1: theme.textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w600,
color: palette.textPrimary,
),
h2: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
color: palette.textPrimary,
),
h3: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w600,
color: palette.textPrimary,
),
code: theme.textTheme.bodyMedium?.copyWith(
fontFamily: 'Menlo',
@ -3892,10 +3865,12 @@ class _MessageBubbleBodyState extends State<_MessageBubbleBody> {
codeblockDecoration: BoxDecoration(
color: context.palette.surfaceSecondary,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: context.palette.strokeSoft),
),
blockquoteDecoration: BoxDecoration(
color: context.palette.surfaceSecondary.withValues(alpha: 0.72),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: context.palette.strokeSoft),
),
blockquotePadding: const EdgeInsets.symmetric(
horizontal: 10,
@ -4116,14 +4091,8 @@ class _TaskStatusCard extends StatelessWidget {
padding: const EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppRadius.card),
color: palette.surfacePrimary,
boxShadow: [
BoxShadow(
color: palette.shadow.withValues(alpha: 0.04),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
color: palette.surfaceSecondary.withValues(alpha: 0.82),
border: Border.all(color: palette.strokeSoft),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -4245,15 +4214,9 @@ class _ToolCallTileState extends State<_ToolCallTile> {
constraints: const BoxConstraints(maxWidth: 760),
child: Container(
decoration: BoxDecoration(
color: palette.surfacePrimary,
color: palette.surfaceSecondary.withValues(alpha: 0.82),
borderRadius: BorderRadius.circular(AppRadius.card),
boxShadow: [
BoxShadow(
color: palette.shadow.withValues(alpha: 0.04),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
border: Border.all(color: palette.strokeSoft),
),
child: Column(
children: [
@ -4380,13 +4343,7 @@ class _StatusPill extends StatelessWidget {
backgroundColor ??
Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(AppRadius.badge),
boxShadow: [
BoxShadow(
color: context.palette.shadow.withValues(alpha: 0.03),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
border: Border.all(color: context.palette.strokeSoft),
),
child: Text(
label,
@ -4430,13 +4387,7 @@ class _ConnectionChip extends StatelessWidget {
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(AppRadius.chip),
boxShadow: [
BoxShadow(
color: context.palette.shadow.withValues(alpha: 0.03),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
border: Border.all(color: context.palette.strokeSoft),
),
child: Text(
'${controller.assistantConnectionStatusLabel} · ${controller.assistantConnectionTargetLabel}',
@ -4482,13 +4433,7 @@ class _MessageViewModeChip extends StatelessWidget {
decoration: BoxDecoration(
color: palette.surfaceSecondary,
borderRadius: BorderRadius.circular(AppRadius.chip),
boxShadow: [
BoxShadow(
color: palette.shadow.withValues(alpha: 0.03),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
border: Border.all(color: palette.strokeSoft),
),
child: Row(
mainAxisSize: MainAxisSize.min,
@ -4725,13 +4670,7 @@ class _MetaPill extends StatelessWidget {
decoration: BoxDecoration(
color: palette.surfaceSecondary,
borderRadius: BorderRadius.circular(999),
boxShadow: [
BoxShadow(
color: palette.shadow.withValues(alpha: 0.03),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
border: Border.all(color: palette.strokeSoft),
),
child: Row(
mainAxisSize: MainAxisSize.min,
@ -5084,13 +5023,7 @@ class _SkillPickerPopover extends StatelessWidget {
color: palette.surfacePrimary,
borderRadius: BorderRadius.circular(24),
border: Border.all(color: palette.strokeSoft),
boxShadow: [
BoxShadow(
color: palette.shadow.withValues(alpha: 0.16),
blurRadius: 24,
offset: const Offset(0, 12),
),
],
boxShadow: [palette.chromeShadowAmbient],
),
child: Column(
children: [
@ -5213,13 +5146,7 @@ class _SkillPickerTile extends StatelessWidget {
? palette.surfaceSecondary
: palette.surfacePrimary,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: palette.shadow.withValues(alpha: 0.04),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
border: Border.all(color: palette.strokeSoft),
),
child: Row(
children: [

View File

@ -340,32 +340,10 @@ class _MobileShellState extends State<MobileShell> {
);
final detailPanel = widget.controller.detailPanel;
final palette = context.palette;
final isDark = Theme.of(context).brightness == Brightness.dark;
return Scaffold(
backgroundColor: palette.canvas,
body: Stack(
children: [
Positioned(
top: 100,
left: -80,
child: _GlowOrb(
size: 180,
color: palette.accentMuted.withValues(
alpha: isDark ? 0.22 : 0.42,
),
),
),
Positioned(
right: -90,
bottom: 220,
child: _GlowOrb(
size: 210,
color: palette.chromeHighlight.withValues(
alpha: isDark ? 0.12 : 0.28,
),
),
),
SafeArea(
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 12, 12, 0),
@ -384,18 +362,8 @@ class _MobileShellState extends State<MobileShell> {
),
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeHighlight.withValues(
alpha: isDark ? 0.14 : 0.72,
),
palette.chromeSurface.withValues(alpha: 0.94),
],
),
border: Border.all(color: palette.chromeStroke),
boxShadow: [palette.chromeShadowAmbient],
color: palette.chromeSurface,
border: Border.all(color: palette.strokeSoft),
),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 220),
@ -1508,17 +1476,9 @@ class _WorkspaceHero extends StatelessWidget {
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeHighlight.withValues(alpha: 0.86),
palette.surfacePrimary.withValues(alpha: 0.94),
],
),
color: palette.surfacePrimary,
borderRadius: BorderRadius.circular(AppRadius.card),
border: Border.all(color: palette.strokeSoft),
boxShadow: [palette.chromeShadowAmbient],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -1629,14 +1589,7 @@ class _WorkspaceShortcutCard extends StatelessWidget {
child: Ink(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeHighlight.withValues(alpha: 0.84),
palette.surfacePrimary.withValues(alpha: 0.94),
],
),
color: palette.surfacePrimary,
borderRadius: BorderRadius.circular(AppRadius.card),
border: Border.all(color: palette.strokeSoft),
),
@ -1745,7 +1698,6 @@ class _BottomPillNav extends StatelessWidget {
color: palette.surfacePrimary.withValues(alpha: 0.92),
borderRadius: BorderRadius.circular(AppRadius.dialog),
border: Border.all(color: palette.strokeSoft),
boxShadow: [palette.chromeShadowAmbient],
),
child: Row(
children: tabs
@ -1797,19 +1749,3 @@ class _BottomPillNav extends StatelessWidget {
);
}
}
class _GlowOrb extends StatelessWidget {
const _GlowOrb({required this.size, required this.color});
final double size;
final Color color;
@override
Widget build(BuildContext context) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
);
}
}

View File

@ -63,85 +63,85 @@ class AppPalette extends ThemeExtension<AppPalette> {
final Color hover;
static const AppPalette light = AppPalette(
canvas: Color(0xFFF8F9FA),
sidebar: Color(0xFFF1F4F8),
sidebarBorder: Color(0x26A6B4C8),
chromeBackground: Color(0xFFF4F7FA),
chromeSurface: Color(0xFFFDFEFF),
chromeSurfacePressed: Color(0xFFF1F5F9),
chromeHighlight: Color(0xFFFFFFFF),
chromeStroke: Color(0x26A6B4C8),
chromeInset: Color(0xFFF4F7FA),
canvas: Color(0xFFFAF8F4),
sidebar: Color(0xFFF6F2EC),
sidebarBorder: Color(0x147E7061),
chromeBackground: Color(0xFFF6F2EC),
chromeSurface: Color(0xFFFFFDF9),
chromeSurfacePressed: Color(0xFFF8F4EE),
chromeHighlight: Color(0xFFFFFEFB),
chromeStroke: Color(0x1F7E7061),
chromeInset: Color(0xFFF3EEE7),
chromeShadowAmbient: BoxShadow(
color: Color(0x140058BD),
blurRadius: 40,
offset: Offset(0, 12),
color: Color(0x0A2E2418),
blurRadius: 16,
offset: Offset(0, 3),
spreadRadius: -14,
),
chromeShadowLift: BoxShadow(
color: Color(0x180058BD),
blurRadius: 24,
offset: Offset(0, 10),
spreadRadius: -12,
),
surfacePrimary: Color(0xFFFFFFFF),
surfaceSecondary: Color(0xFFF2F5F8),
surfaceTertiary: Color(0xFFE9EEF4),
stroke: Color(0x33A6B4C8),
strokeSoft: Color(0x26A6B4C8),
accent: Color(0xFF0058BD),
accentHover: Color(0xFF1A6CCE),
accentMuted: Color(0xFFE8F0FB),
idle: Color(0xFF98A1B2),
success: Color(0xFF34A853),
warning: Color(0xFF8F4A00),
danger: Color(0xFFC3655C),
textPrimary: Color(0xFF1C1B1F),
textSecondary: Color(0xFF667085),
textMuted: Color(0xFF98A1B2),
shadow: Color(0x140058BD),
hover: Color(0xFFEFF4FA),
);
static const AppPalette dark = AppPalette(
canvas: Color(0xFF141422),
sidebar: Color(0xFF1A1D2A),
sidebarBorder: Color(0x33CAC4D0),
chromeBackground: Color(0xFF161A26),
chromeSurface: Color(0xFF1D2230),
chromeSurfacePressed: Color(0xFF23293A),
chromeHighlight: Color(0xFF2A3145),
chromeStroke: Color(0x33CAC4D0),
chromeInset: Color(0xFF1A1F2C),
chromeShadowAmbient: BoxShadow(
color: Color(0x4D000814),
blurRadius: 36,
offset: Offset(0, 12),
spreadRadius: -14,
),
chromeShadowLift: BoxShadow(
color: Color(0x660058BD),
blurRadius: 22,
color: Color(0x122E2418),
blurRadius: 20,
offset: Offset(0, 8),
spreadRadius: -12,
),
surfacePrimary: Color(0xFF171C28),
surfaceSecondary: Color(0xFF1E2433),
surfaceTertiary: Color(0xFF262D3F),
stroke: Color(0x40CAC4D0),
strokeSoft: Color(0x26CAC4D0),
accent: Color(0xFF4B8FE8),
accentHover: Color(0xFF78AFFF),
accentMuted: Color(0xFF1C3355),
idle: Color(0xFF8B95A8),
success: Color(0xFF5CB978),
warning: Color(0xFFE0AE5A),
danger: Color(0xFFEF9A9A),
textPrimary: Color(0xFFE6E1E5),
textSecondary: Color(0xFFB0B8C8),
textMuted: Color(0xFF8B95A8),
surfacePrimary: Color(0xFFFFFDF9),
surfaceSecondary: Color(0xFFF8F4EE),
surfaceTertiary: Color(0xFFF1EAE1),
stroke: Color(0x1F7E7061),
strokeSoft: Color(0x147E7061),
accent: Color(0xFF635BFF),
accentHover: Color(0xFF564EF0),
accentMuted: Color(0xFFECE9FF),
idle: Color(0xFF9D968C),
success: Color(0xFF2F7D57),
warning: Color(0xFF8A5A1F),
danger: Color(0xFFB65C4A),
textPrimary: Color(0xFF24211D),
textSecondary: Color(0xFF6E675F),
textMuted: Color(0xFF9D968C),
shadow: Color(0x102E2418),
hover: Color(0xFFF3EEE7),
);
static const AppPalette dark = AppPalette(
canvas: Color(0xFF171513),
sidebar: Color(0xFF1D1A17),
sidebarBorder: Color(0x14EEE3D6),
chromeBackground: Color(0xFF1D1A17),
chromeSurface: Color(0xFF24201C),
chromeSurfacePressed: Color(0xFF2B2621),
chromeHighlight: Color(0xFF2E2822),
chromeStroke: Color(0x1FEEE3D6),
chromeInset: Color(0xFF23201C),
chromeShadowAmbient: BoxShadow(
color: Color(0x22000000),
blurRadius: 22,
offset: Offset(0, 8),
spreadRadius: -14,
),
chromeShadowLift: BoxShadow(
color: Color(0x2B000000),
blurRadius: 20,
offset: Offset(0, 8),
spreadRadius: -12,
),
surfacePrimary: Color(0xFF24201C),
surfaceSecondary: Color(0xFF2B2621),
surfaceTertiary: Color(0xFF342E28),
stroke: Color(0x1FEEE3D6),
strokeSoft: Color(0x14EEE3D6),
accent: Color(0xFF8A83FF),
accentHover: Color(0xFF9A94FF),
accentMuted: Color(0x2E8A83FF),
idle: Color(0xFF958B80),
success: Color(0xFF66B78B),
warning: Color(0xFFD3A86C),
danger: Color(0xFFE58C79),
textPrimary: Color(0xFFF1E9DF),
textSecondary: Color(0xFFC6BAAD),
textMuted: Color(0xFF958B80),
shadow: Color(0x52000000),
hover: Color(0xFF23293A),
hover: Color(0xFF2B2621),
);
@override

View File

@ -21,12 +21,12 @@ class SimpleSpacing {
class SimpleRadius {
SimpleRadius._();
static const double card = 16.0;
static const double card = 18.0;
static const double button = 12.0;
static const double input = 14.0;
static const double chip = 12.0;
static const double input = 20.0;
static const double chip = 999.0;
static const double badge = 999.0;
static const double dialog = 18.0;
static const double dialog = 20.0;
static const double sidebar = 20.0;
static const double icon = 12.0;
}
@ -42,21 +42,21 @@ class SimpleTypography {
static const FontWeight titleWeight = FontWeight.w600;
static const double titleHeight = 24 / 20;
static const double sectionSize = 13.0;
static const double sectionSize = 15.0;
static const FontWeight sectionWeight = FontWeight.w600;
static const double sectionHeight = 14 / 13;
static const double sectionHeight = 20 / 15;
static const double bodySize = 13.0;
static const double bodySize = 15.0;
static const FontWeight bodyWeight = FontWeight.w400;
static const double bodyHeight = 15 / 13;
static const double bodyHeight = 24 / 15;
static const double compactBodySize = 13.0;
static const double compactBodySize = 14.0;
static const FontWeight compactBodyWeight = FontWeight.w400;
static const double compactBodyHeight = 15 / 13;
static const double compactBodyHeight = 22 / 14;
static const double emphasizedBodySize = 13.0;
static const FontWeight emphasizedBodyWeight = FontWeight.w600;
static const double emphasizedBodyHeight = 14 / 13;
static const double emphasizedBodyHeight = 18 / 13;
static const double captionSize = 12.0;
static const FontWeight captionWeight = FontWeight.w400;
@ -78,7 +78,7 @@ class SimpleSizes {
static const double toolbarHeight = 40.0;
static const double inputHeight = 40.0;
static const double buttonHeightDesktop = 30.0;
static const double buttonHeightDesktop = 34.0;
static const double buttonHeightMobile = 36.0;
}
@ -353,7 +353,7 @@ class AppTheme {
iconButtonTheme: IconButtonThemeData(
style: IconButton.styleFrom(
foregroundColor: palette.textSecondary,
backgroundColor: palette.surfaceSecondary.withValues(alpha: 0.88),
backgroundColor: palette.surfacePrimary.withValues(alpha: 0.94),
surfaceTintColor: Colors.transparent,
minimumSize: const Size(32, 32),
padding: const EdgeInsets.all(7),
@ -365,7 +365,7 @@ class AppTheme {
inputDecorationTheme: InputDecorationTheme(
isDense: true,
filled: true,
fillColor: palette.surfacePrimary.withValues(alpha: 0.92),
fillColor: palette.surfacePrimary,
hintStyle: tunedTextTheme.bodyMedium?.copyWith(
color: palette.textMuted,
),
@ -495,9 +495,9 @@ class AppTheme {
),
titleSmall: withUiFont(
base.titleSmall?.copyWith(
fontSize: AppTypography.bodySize,
fontSize: AppTypography.compactBodySize,
fontWeight: FontWeight.w600,
height: AppTypography.bodyHeight,
height: AppTypography.compactBodyHeight,
color: palette.textPrimary,
),
),
@ -527,9 +527,9 @@ class AppTheme {
),
labelLarge: withUiFont(
base.labelLarge?.copyWith(
fontSize: AppTypography.sectionSize,
fontSize: AppTypography.emphasizedBodySize,
fontWeight: AppTypography.emphasizedBodyWeight,
height: AppTypography.sectionHeight,
height: AppTypography.emphasizedBodyHeight,
color: palette.textPrimary,
),
),

View File

@ -106,17 +106,9 @@ class DesktopWorkspaceScaffold extends StatelessWidget {
Expanded(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeHighlight.withValues(alpha: 0.9),
palette.chromeSurface.withValues(alpha: 0.92),
],
),
color: palette.chromeSurface,
borderRadius: BorderRadius.circular(AppRadius.card),
border: Border.all(color: palette.chromeStroke),
boxShadow: [palette.chromeShadowAmbient],
border: Border.all(color: palette.strokeSoft),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(AppRadius.card),

View File

@ -91,17 +91,9 @@ class SidebarNavigation extends StatelessWidget {
height: double.infinity,
margin: marginOverride ?? const EdgeInsets.fromLTRB(4, 4, 4, 0),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeHighlight.withValues(alpha: 0.9),
palette.chromeSurface.withValues(alpha: 0.92),
],
),
color: palette.chromeSurface,
borderRadius: BorderRadius.circular(AppRadius.sidebar),
border: Border.all(color: palette.chromeStroke),
boxShadow: [palette.chromeShadowAmbient],
border: Border.all(color: palette.strokeSoft),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),
@ -265,17 +257,9 @@ class _SidebarHeaderChevron extends StatelessWidget {
width: size,
height: size,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeHighlight.withValues(alpha: 0.9),
palette.chromeSurface,
],
),
color: palette.surfacePrimary,
borderRadius: BorderRadius.circular(borderRadius),
border: Border.all(color: palette.chromeStroke),
boxShadow: [palette.chromeShadowAmbient],
border: Border.all(color: palette.strokeSoft),
),
child: Center(
child: Icon(
@ -345,8 +329,8 @@ class _SidebarSectionGroup extends StatelessWidget {
WorkspaceDestination.secrets => AssistantFocusEntry.secrets,
WorkspaceDestination.aiGateway => AssistantFocusEntry.aiGateway,
WorkspaceDestination.settings => AssistantFocusEntry.settings,
WorkspaceDestination.assistant || WorkspaceDestination.account =>
null,
WorkspaceDestination.assistant ||
WorkspaceDestination.account => null,
};
return Padding(
padding: const EdgeInsets.only(bottom: AppSpacing.xxs),
@ -355,12 +339,16 @@ class _SidebarSectionGroup extends StatelessWidget {
selected: currentSection == section,
collapsed: collapsed,
emphasis: emphasis,
favorite: focusEntry != null && favoriteDestinations.contains(focusEntry),
favorite:
focusEntry != null &&
favoriteDestinations.contains(focusEntry),
showFavoriteToggle:
!collapsed &&
focusEntry != null &&
onToggleFavorite != null &&
kAssistantNavigationDestinationCandidates.contains(focusEntry),
kAssistantNavigationDestinationCandidates.contains(
focusEntry,
),
labelOverride: useHomeShortcut
? appText('回到 APP首页', 'Back to app home')
: null,
@ -435,28 +423,15 @@ class _SidebarNavItemState extends State<_SidebarNavItem> {
child: AnimatedContainer(
duration: const Duration(milliseconds: 160),
decoration: BoxDecoration(
gradient: widget.selected || _hovered
? LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeHighlight.withValues(
alpha: widget.selected ? 0.84 : 0.7,
),
background.withValues(
alpha: widget.selected ? 0.96 : 0.9,
),
],
)
: null,
color: widget.selected || _hovered ? null : Colors.transparent,
color: widget.selected || _hovered
? background.withValues(alpha: widget.selected ? 1 : 0.96)
: Colors.transparent,
borderRadius: BorderRadius.circular(radius),
border: Border.all(
color: widget.selected || _hovered
? palette.chromeStroke
? palette.strokeSoft
: Colors.transparent,
),
boxShadow: widget.selected ? [palette.chromeShadowLift] : const [],
),
child: Material(
color: Colors.transparent,
@ -648,7 +623,9 @@ class SidebarFooter extends StatelessWidget {
compact: true,
tooltip: appText('切换语言', 'Toggle language'),
onPressed: onToggleLanguage,
favorite: favoriteDestinations.contains(AssistantFocusEntry.language),
favorite: favoriteDestinations.contains(
AssistantFocusEntry.language,
),
showFavoriteToggle: onToggleFavorite != null,
favoriteButtonKey: const ValueKey<String>(
'sidebar-favorite-language',
@ -753,7 +730,9 @@ class SidebarFooter extends StatelessWidget {
icon: chromeThemeToggleIcon(themeMode),
tooltip: themeToggleTooltip,
onPressed: onOpenThemeToggle,
favorite: favoriteDestinations.contains(AssistantFocusEntry.theme),
favorite: favoriteDestinations.contains(
AssistantFocusEntry.theme,
),
showFavoriteToggle: onToggleFavorite != null,
favoriteButtonKey: const ValueKey<String>(
'sidebar-favorite-theme',
@ -845,26 +824,15 @@ class _SidebarAccountTileState extends State<_SidebarAccountTile> {
child: AnimatedContainer(
duration: const Duration(milliseconds: 160),
decoration: BoxDecoration(
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,
color: widget.selected || _hovered
? background
: Colors.transparent,
borderRadius: BorderRadius.circular(AppRadius.button),
border: Border.all(
color: widget.selected || _hovered
? palette.chromeStroke
? palette.strokeSoft
: Colors.transparent,
),
boxShadow: widget.selected ? [palette.chromeShadowLift] : const [],
),
child: Material(
color: Colors.transparent,

View File

@ -48,28 +48,20 @@ class _SurfaceCardState extends State<SurfaceCard> {
final decoration = switch (widget.tone) {
SurfaceCardTone.standard => BoxDecoration(
color: (_hovered && widget.onTap != null ? hoveredColor : baseColor)
.withValues(alpha: 0.94),
.withValues(alpha: 0.98),
border: Border.all(color: borderColor),
borderRadius: BorderRadius.circular(widget.borderRadius),
boxShadow: widget.onTap != null && _hovered
? [palette.chromeShadowLift]
? [palette.chromeShadowAmbient]
: const [],
),
SurfaceCardTone.chrome => BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
palette.chromeHighlight.withValues(alpha: 0.92),
(_hovered && widget.onTap != null ? hoveredColor : baseColor)
.withValues(alpha: 0.9),
],
),
color: (_hovered && widget.onTap != null ? hoveredColor : baseColor)
.withValues(alpha: 0.98),
border: Border.all(color: borderColor),
borderRadius: BorderRadius.circular(widget.borderRadius),
boxShadow: [
palette.chromeShadowAmbient,
if (_hovered && widget.onTap != null) palette.chromeShadowLift,
if (_hovered && widget.onTap != null) palette.chromeShadowAmbient,
],
),
};