Polish workspace theme and add Makefile tasks
This commit is contained in:
parent
7cabfa2e05
commit
390e14beef
47
Makefile
Normal file
47
Makefile
Normal file
@ -0,0 +1,47 @@
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
SHELL := /bin/bash
|
||||
|
||||
FLUTTER ?= flutter
|
||||
PNPM ?= pnpm
|
||||
DART ?= dart
|
||||
DEVICE ?= macos
|
||||
|
||||
.PHONY: help deps analyze test check format run build-macos build-ios-sim package-mac install-mac clean
|
||||
|
||||
help: ## Show available targets
|
||||
@grep -E '^[a-zA-Z0-9_.-]+:.*?## ' Makefile | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-18s %s\n", $$1, $$2}'
|
||||
|
||||
deps: ## Install Flutter dependencies
|
||||
$(FLUTTER) pub get
|
||||
|
||||
analyze: ## Run static analysis
|
||||
$(FLUTTER) analyze
|
||||
|
||||
test: ## Run Flutter tests
|
||||
$(FLUTTER) test
|
||||
|
||||
check: analyze test ## Run the standard validation suite
|
||||
|
||||
format: ## Format Dart sources
|
||||
$(DART) format lib test
|
||||
|
||||
run: ## Run the app on a device or desktop target (DEVICE=macos by default)
|
||||
$(FLUTTER) run -d $(DEVICE)
|
||||
|
||||
build-macos: ## Build the macOS app in release mode
|
||||
$(FLUTTER) build macos --release
|
||||
|
||||
build-ios-sim: ## Build the iOS app for the simulator
|
||||
$(FLUTTER) build ios --simulator
|
||||
|
||||
package-mac: ## Create the macOS .app and DMG
|
||||
bash scripts/package-flutter-mac-app.sh
|
||||
|
||||
install-mac: ## Package and install the macOS app into /Applications
|
||||
bash scripts/package-flutter-mac-app.sh
|
||||
bash scripts/install-flutter-mac-dmg.sh
|
||||
|
||||
clean: ## Remove generated artifacts
|
||||
$(FLUTTER) clean
|
||||
rm -rf build dist
|
||||
@ -12,7 +12,9 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
required this.stroke,
|
||||
required this.strokeSoft,
|
||||
required this.accent,
|
||||
required this.accentHover,
|
||||
required this.accentMuted,
|
||||
required this.idle,
|
||||
required this.success,
|
||||
required this.warning,
|
||||
required this.danger,
|
||||
@ -32,7 +34,9 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
final Color stroke;
|
||||
final Color strokeSoft;
|
||||
final Color accent;
|
||||
final Color accentHover;
|
||||
final Color accentMuted;
|
||||
final Color idle;
|
||||
final Color success;
|
||||
final Color warning;
|
||||
final Color danger;
|
||||
@ -43,45 +47,49 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
final Color hover;
|
||||
|
||||
static const AppPalette light = AppPalette(
|
||||
canvas: Color(0xFFF5F6F7),
|
||||
sidebar: Color(0xFFF3F4F6),
|
||||
sidebarBorder: Color(0xFFE6E8EB),
|
||||
canvas: Color(0xFFF8FAFC),
|
||||
sidebar: Color(0xFFF8FAFC),
|
||||
sidebarBorder: Color(0xFFE5E7EB),
|
||||
surfacePrimary: Color(0xFFFFFFFF),
|
||||
surfaceSecondary: Color(0xFFFAFAFB),
|
||||
surfaceTertiary: Color(0xFFF2F4F6),
|
||||
stroke: Color(0xFFE7E9EC),
|
||||
strokeSoft: Color(0xFFF1F3F5),
|
||||
accent: Color(0xFF247A66),
|
||||
accentMuted: Color(0xFFE6F3EF),
|
||||
success: Color(0xFF228163),
|
||||
warning: Color(0xFFC88A34),
|
||||
danger: Color(0xFFD15A5A),
|
||||
textPrimary: Color(0xFF13161A),
|
||||
textSecondary: Color(0xFF4F5A68),
|
||||
textMuted: Color(0xFF78808B),
|
||||
shadow: Color(0x14111822),
|
||||
hover: Color(0xFFF0F2F4),
|
||||
surfaceSecondary: Color(0xFFF8FAFC),
|
||||
surfaceTertiary: Color(0xFFF1F5F9),
|
||||
stroke: Color(0xFFE5E7EB),
|
||||
strokeSoft: Color(0xFFF1F5F9),
|
||||
accent: Color(0xFF3B82F6),
|
||||
accentHover: Color(0xFF2563EB),
|
||||
accentMuted: Color(0xFFDBEAFE),
|
||||
idle: Color(0xFF94A3B8),
|
||||
success: Color(0xFF22C55E),
|
||||
warning: Color(0xFFF59E0B),
|
||||
danger: Color(0xFFEF4444),
|
||||
textPrimary: Color(0xFF111827),
|
||||
textSecondary: Color(0xFF6B7280),
|
||||
textMuted: Color(0xFF64748B),
|
||||
shadow: Color(0x0F0F172A),
|
||||
hover: Color(0xFFEFF6FF),
|
||||
);
|
||||
|
||||
static const AppPalette dark = AppPalette(
|
||||
canvas: Color(0xFF121416),
|
||||
sidebar: Color(0xFF15181B),
|
||||
sidebarBorder: Color(0xFF23272C),
|
||||
surfacePrimary: Color(0xFF1A1D21),
|
||||
surfaceSecondary: Color(0xFF20242A),
|
||||
surfaceTertiary: Color(0xFF262B33),
|
||||
stroke: Color(0xFF2D333A),
|
||||
strokeSoft: Color(0xFF21262C),
|
||||
accent: Color(0xFF3AB08F),
|
||||
accentMuted: Color(0xFF16372E),
|
||||
success: Color(0xFF51C397),
|
||||
warning: Color(0xFFE2A14A),
|
||||
danger: Color(0xFFFF8585),
|
||||
textPrimary: Color(0xFFF4F6F8),
|
||||
textSecondary: Color(0xFFBCC3CC),
|
||||
textMuted: Color(0xFF9098A4),
|
||||
canvas: Color(0xFF0B1220),
|
||||
sidebar: Color(0xFF0F172A),
|
||||
sidebarBorder: Color(0xFF1E293B),
|
||||
surfacePrimary: Color(0xFF111827),
|
||||
surfaceSecondary: Color(0xFF0F172A),
|
||||
surfaceTertiary: Color(0xFF172033),
|
||||
stroke: Color(0xFF223046),
|
||||
strokeSoft: Color(0xFF162033),
|
||||
accent: Color(0xFF3B82F6),
|
||||
accentHover: Color(0xFF2563EB),
|
||||
accentMuted: Color(0xFF142B52),
|
||||
idle: Color(0xFF94A3B8),
|
||||
success: Color(0xFF22C55E),
|
||||
warning: Color(0xFFF59E0B),
|
||||
danger: Color(0xFFEF4444),
|
||||
textPrimary: Color(0xFFF8FAFC),
|
||||
textSecondary: Color(0xFF94A3B8),
|
||||
textMuted: Color(0xFF64748B),
|
||||
shadow: Color(0x52000000),
|
||||
hover: Color(0xFF232830),
|
||||
hover: Color(0xFF11213A),
|
||||
);
|
||||
|
||||
@override
|
||||
@ -95,7 +103,9 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
Color? stroke,
|
||||
Color? strokeSoft,
|
||||
Color? accent,
|
||||
Color? accentHover,
|
||||
Color? accentMuted,
|
||||
Color? idle,
|
||||
Color? success,
|
||||
Color? warning,
|
||||
Color? danger,
|
||||
@ -115,7 +125,9 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
stroke: stroke ?? this.stroke,
|
||||
strokeSoft: strokeSoft ?? this.strokeSoft,
|
||||
accent: accent ?? this.accent,
|
||||
accentHover: accentHover ?? this.accentHover,
|
||||
accentMuted: accentMuted ?? this.accentMuted,
|
||||
idle: idle ?? this.idle,
|
||||
success: success ?? this.success,
|
||||
warning: warning ?? this.warning,
|
||||
danger: danger ?? this.danger,
|
||||
@ -152,7 +164,9 @@ class AppPalette extends ThemeExtension<AppPalette> {
|
||||
stroke: Color.lerp(stroke, other.stroke, t) ?? stroke,
|
||||
strokeSoft: Color.lerp(strokeSoft, other.strokeSoft, t) ?? strokeSoft,
|
||||
accent: Color.lerp(accent, other.accent, t) ?? accent,
|
||||
accentHover: Color.lerp(accentHover, other.accentHover, t) ?? accentHover,
|
||||
accentMuted: Color.lerp(accentMuted, other.accentMuted, t) ?? accentMuted,
|
||||
idle: Color.lerp(idle, other.idle, t) ?? idle,
|
||||
success: Color.lerp(success, other.success, t) ?? success,
|
||||
warning: Color.lerp(warning, other.warning, t) ?? warning,
|
||||
danger: Color.lerp(danger, other.danger, t) ?? danger,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'app_palette.dart';
|
||||
@ -13,6 +14,11 @@ class AppTheme {
|
||||
required Brightness brightness,
|
||||
required AppPalette palette,
|
||||
}) {
|
||||
final platform = defaultTargetPlatform;
|
||||
final isDesktop =
|
||||
platform == TargetPlatform.macOS ||
|
||||
platform == TargetPlatform.windows ||
|
||||
platform == TargetPlatform.linux;
|
||||
final colorScheme =
|
||||
ColorScheme.fromSeed(
|
||||
seedColor: palette.accent,
|
||||
@ -43,47 +49,22 @@ class AppTheme {
|
||||
final base = ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: brightness,
|
||||
typography: Typography.material2021(platform: platform),
|
||||
colorScheme: colorScheme,
|
||||
scaffoldBackgroundColor: palette.canvas,
|
||||
extensions: [palette],
|
||||
);
|
||||
final tunedTextTheme = _textTheme(
|
||||
base.textTheme,
|
||||
palette: palette,
|
||||
isDesktop: isDesktop,
|
||||
);
|
||||
|
||||
return base.copyWith(
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
dividerColor: palette.strokeSoft,
|
||||
hoverColor: palette.hover,
|
||||
textTheme: base.textTheme.copyWith(
|
||||
displaySmall: base.textTheme.displaySmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.9,
|
||||
),
|
||||
headlineSmall: base.textTheme.headlineSmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.5,
|
||||
),
|
||||
titleLarge: base.textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.2,
|
||||
),
|
||||
titleMedium: base.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
bodyLarge: base.textTheme.bodyLarge?.copyWith(
|
||||
height: 1.45,
|
||||
color: palette.textPrimary,
|
||||
),
|
||||
bodyMedium: base.textTheme.bodyMedium?.copyWith(
|
||||
height: 1.4,
|
||||
color: palette.textSecondary,
|
||||
),
|
||||
bodySmall: base.textTheme.bodySmall?.copyWith(
|
||||
height: 1.35,
|
||||
color: palette.textMuted,
|
||||
),
|
||||
labelLarge: base.textTheme.labelLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
textTheme: tunedTextTheme,
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
@ -121,10 +102,12 @@ class AppTheme {
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: palette.surfaceSecondary,
|
||||
hintStyle: TextStyle(color: palette.textMuted),
|
||||
hintStyle: tunedTextTheme.bodyMedium?.copyWith(
|
||||
color: palette.textMuted,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 18,
|
||||
vertical: 18,
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
@ -170,4 +153,60 @@ class AppTheme {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static TextTheme _textTheme(
|
||||
TextTheme base, {
|
||||
required AppPalette palette,
|
||||
required bool isDesktop,
|
||||
}) {
|
||||
return base.copyWith(
|
||||
displaySmall: base.displaySmall?.copyWith(
|
||||
fontSize: isDesktop ? 32 : 34,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.9,
|
||||
),
|
||||
headlineSmall: base.headlineSmall?.copyWith(
|
||||
fontSize: isDesktop ? 22 : 24,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.45,
|
||||
),
|
||||
titleLarge: base.titleLarge?.copyWith(
|
||||
fontSize: isDesktop ? 18 : 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.2,
|
||||
),
|
||||
titleMedium: base.titleMedium?.copyWith(
|
||||
fontSize: isDesktop ? 15 : 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
titleSmall: base.titleSmall?.copyWith(
|
||||
fontSize: isDesktop ? 13 : 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
bodyLarge: base.bodyLarge?.copyWith(
|
||||
fontSize: isDesktop ? 14 : 15,
|
||||
height: 1.45,
|
||||
color: palette.textPrimary,
|
||||
),
|
||||
bodyMedium: base.bodyMedium?.copyWith(
|
||||
fontSize: isDesktop ? 13 : 14,
|
||||
height: 1.4,
|
||||
color: palette.textSecondary,
|
||||
),
|
||||
bodySmall: base.bodySmall?.copyWith(
|
||||
fontSize: isDesktop ? 12 : 12,
|
||||
height: 1.35,
|
||||
color: palette.textMuted,
|
||||
),
|
||||
labelLarge: base.labelLarge?.copyWith(
|
||||
fontSize: isDesktop ? 13 : 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
labelMedium: base.labelMedium?.copyWith(
|
||||
fontSize: isDesktop ? 12 : 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
labelSmall: base.labelSmall?.copyWith(fontSize: isDesktop ? 11 : 11),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,9 +6,9 @@ class SurfaceCard extends StatefulWidget {
|
||||
const SurfaceCard({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.padding = const EdgeInsets.all(20),
|
||||
this.padding = const EdgeInsets.all(16),
|
||||
this.onTap,
|
||||
this.borderRadius = 20,
|
||||
this.borderRadius = 16,
|
||||
this.color,
|
||||
});
|
||||
|
||||
@ -43,8 +43,8 @@ class _SurfaceCardState extends State<SurfaceCard> {
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: palette.shadow.withValues(alpha: _hovered ? 0.12 : 0.07),
|
||||
blurRadius: _hovered ? 22 : 16,
|
||||
offset: const Offset(0, 10),
|
||||
blurRadius: _hovered ? 12 : 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -23,12 +23,9 @@ class TopBar extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: Theme.of(context).textTheme.headlineSmall),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 6),
|
||||
Text(subtitle, style: Theme.of(context).textTheme.bodyLarge),
|
||||
if (trailing != null) ...[
|
||||
const SizedBox(height: 16),
|
||||
trailing!,
|
||||
],
|
||||
if (trailing != null) ...[const SizedBox(height: 12), trailing!],
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -41,13 +38,13 @@ class TopBar extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: Theme.of(context).textTheme.headlineSmall),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 6),
|
||||
Text(subtitle, style: Theme.of(context).textTheme.bodyLarge),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (trailing != null) ...[
|
||||
const SizedBox(width: 24),
|
||||
const SizedBox(width: 16),
|
||||
Flexible(child: trailing!),
|
||||
],
|
||||
],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user