feat(studio): rich theme picker (presets + custom colour) + editor 0.11.0
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>
This commit is contained in:
flemming-it 2026-06-02 01:29:34 +02:00
parent 69864da934
commit c1c60d5434
30 changed files with 1280 additions and 846 deletions

View file

@ -32,9 +32,9 @@ class _ModulesPageState extends State<ModulesPage> {
}
void _refresh() => setState(() {
_future = HubService.instance.listModules();
_historyFuture = _loadHistory();
});
_future = HubService.instance.listModules();
_historyFuture = _loadHistory();
});
@override
Widget build(BuildContext context) {
@ -87,8 +87,10 @@ class _ModulesPageState extends State<ModulesPage> {
if (i > 0) const SizedBox(height: FaiSpace.md),
GestureDetector(
onTap: () async {
final uninstalled =
await FaiModuleSheet.show(context, modules[i].name);
final uninstalled = await FaiModuleSheet.show(
context,
modules[i].name,
);
if (uninstalled) _refresh();
},
behavior: HitTestBehavior.opaque,
@ -171,7 +173,6 @@ class _ModuleCard extends StatelessWidget {
}
}
/// Compact "Recent activity" header listing the last few
/// install / uninstall events from the audit log. Lets
/// operators trace "wait, when did that module appear?"
@ -220,8 +221,9 @@ class _RecentActivityPanel extends StatelessWidget {
),
const SizedBox(width: FaiSpace.sm),
Text(
AppLocalizations.of(context)!
.modulesRecentActivityHint(events.length),
AppLocalizations.of(
context,
)!.modulesRecentActivityHint(events.length),
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
@ -292,7 +294,8 @@ String _formatTimestamp(DateTime local) {
final mm = local.minute.toString().padLeft(2, '0');
final ss = local.second.toString().padLeft(2, '0');
final time = '$hh:$mm:$ss';
final sameDay = local.year == now.year &&
final sameDay =
local.year == now.year &&
local.month == now.month &&
local.day == now.day;
if (sameDay) return time;