Some checks failed
Security / Security check (push) Failing after 1s
Settings dialog's Theme Plugin section is now a grid of swatched tiles: - Built-in (none) — falls back to FaiTheme.light/.dark - One tile per installed studio.theme.* plugin, each showing the plugin's primary/secondary/tertiary as live colour dots. Tile loads its preview lazily so a dozen installed themes don't block the picker. - Custom — opens a colour-picker dialog with 12 curated Material presets + a hex input + live preview. Selecting applies ColorScheme.fromSeed for both brightnesses. main.dart's _pluginThemes parses a 'custom:#RRGGBB' sigil in the same notifier slot as plugin capability ids, so the existing persistence + restoration paths cover the custom case with no new state. Bumps editor to 0.11.0 (type-checked port connections + dynamic card width fix + card-height border allowance) and Studio to 0.58.0. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
75 lines
2.2 KiB
Dart
75 lines
2.2 KiB
Dart
// FaiPill — small inline label. Used for capabilities, versions,
|
|
// permission scopes, status text. Replaces ad-hoc Container chips.
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../theme/theme.dart';
|
|
import '../theme/tokens.dart';
|
|
|
|
enum FaiPillTone { neutral, accent, success, warning, danger }
|
|
|
|
class FaiPill extends StatelessWidget {
|
|
final String label;
|
|
final FaiPillTone tone;
|
|
final IconData? icon;
|
|
final bool monospace;
|
|
|
|
const FaiPill({
|
|
super.key,
|
|
required this.label,
|
|
this.tone = FaiPillTone.neutral,
|
|
this.icon,
|
|
this.monospace = false,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
final (bg, fg) = _colors(theme.colorScheme);
|
|
final textStyle = monospace
|
|
? FaiTheme.mono(size: 11, weight: FontWeight.w500, color: fg)
|
|
: theme.textTheme.labelSmall?.copyWith(color: fg);
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: FaiSpace.sm, vertical: 3),
|
|
decoration: BoxDecoration(
|
|
color: bg,
|
|
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
if (icon != null) ...[
|
|
Icon(icon, size: 11, color: fg),
|
|
const SizedBox(width: 4),
|
|
],
|
|
Text(label, style: textStyle),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
(Color, Color) _colors(ColorScheme scheme) {
|
|
switch (tone) {
|
|
case FaiPillTone.neutral:
|
|
return (scheme.surfaceContainerHighest, scheme.onSurfaceVariant);
|
|
case FaiPillTone.accent:
|
|
return (scheme.primary.withValues(alpha: 0.15), scheme.primary);
|
|
case FaiPillTone.success:
|
|
return (
|
|
FaiColors.success.withValues(alpha: 0.15),
|
|
scheme.brightness == Brightness.dark
|
|
? FaiColors.success
|
|
: const Color(0xFF15803D),
|
|
);
|
|
case FaiPillTone.warning:
|
|
return (
|
|
FaiColors.warning.withValues(alpha: 0.15),
|
|
scheme.brightness == Brightness.dark
|
|
? FaiColors.warning
|
|
: const Color(0xFFB45309),
|
|
);
|
|
case FaiPillTone.danger:
|
|
return (scheme.errorContainer, scheme.onErrorContainer);
|
|
}
|
|
}
|
|
}
|