feat(studio): rich theme picker (presets + custom colour) + editor 0.11.0
Some checks failed
Security / Security check (push) Failing after 1s
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:
parent
69864da934
commit
c1c60d5434
30 changed files with 1280 additions and 846 deletions
|
|
@ -21,12 +21,15 @@ class FaiSearchHit {
|
|||
final String label;
|
||||
final String hint;
|
||||
final IconData icon;
|
||||
|
||||
/// Free-form group used for the section header in the
|
||||
/// results list ("Pages", "Modules", "Store", "Flows").
|
||||
final String group;
|
||||
|
||||
/// Lowercase haystack — name + hint + group. Filtered against
|
||||
/// the lowercased query.
|
||||
final String haystack;
|
||||
|
||||
/// Action to run when this hit is selected.
|
||||
final VoidCallback onSelect;
|
||||
|
||||
|
|
@ -103,60 +106,67 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
|
||||
final hits = <FaiSearchHit>[];
|
||||
for (final m in modules) {
|
||||
hits.add(FaiSearchHit(
|
||||
label: m.name,
|
||||
hint: l.searchInstalledModuleHint(m.version),
|
||||
icon: Icons.extension,
|
||||
group: l.searchGroupModules,
|
||||
onSelect: () {
|
||||
Navigator.pop(context);
|
||||
FaiModuleSheet.show(context, m.name);
|
||||
},
|
||||
));
|
||||
hits.add(
|
||||
FaiSearchHit(
|
||||
label: m.name,
|
||||
hint: l.searchInstalledModuleHint(m.version),
|
||||
icon: Icons.extension,
|
||||
group: l.searchGroupModules,
|
||||
onSelect: () {
|
||||
Navigator.pop(context);
|
||||
FaiModuleSheet.show(context, m.name);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
final storeGroup = l.searchGroupStore;
|
||||
final pagesGroup = l.searchGroupPages;
|
||||
for (final s in store) {
|
||||
hits.add(FaiSearchHit(
|
||||
label: s.name,
|
||||
hint: s.taglineEn.isEmpty
|
||||
? l.searchStoreHintWithCategory(
|
||||
s.category.isEmpty ? l.searchStoreUncategorized : s.category)
|
||||
: l.searchStoreHintWithTagline(s.taglineEn),
|
||||
icon: Icons.storefront_outlined,
|
||||
group: storeGroup,
|
||||
// Selecting a store hit just closes the palette and
|
||||
// focuses the Store tab; deep-linking to the detail
|
||||
// sheet would require thread-through plumbing we don't
|
||||
// have yet.
|
||||
onSelect: () {
|
||||
Navigator.pop(context);
|
||||
for (final h in widget.staticHits) {
|
||||
if (h.label == storeGroup && h.group == pagesGroup) {
|
||||
h.onSelect();
|
||||
return;
|
||||
hits.add(
|
||||
FaiSearchHit(
|
||||
label: s.name,
|
||||
hint: s.taglineEn.isEmpty
|
||||
? l.searchStoreHintWithCategory(
|
||||
s.category.isEmpty ? l.searchStoreUncategorized : s.category,
|
||||
)
|
||||
: l.searchStoreHintWithTagline(s.taglineEn),
|
||||
icon: Icons.storefront_outlined,
|
||||
group: storeGroup,
|
||||
// Selecting a store hit just closes the palette and
|
||||
// focuses the Store tab; deep-linking to the detail
|
||||
// sheet would require thread-through plumbing we don't
|
||||
// have yet.
|
||||
onSelect: () {
|
||||
Navigator.pop(context);
|
||||
for (final h in widget.staticHits) {
|
||||
if (h.label == storeGroup && h.group == pagesGroup) {
|
||||
h.onSelect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
final flowsGroup = l.searchGroupFlows;
|
||||
for (final f in flows) {
|
||||
hits.add(FaiSearchHit(
|
||||
label: f.name,
|
||||
hint: l.searchSavedFlowHint,
|
||||
icon: Icons.account_tree_outlined,
|
||||
group: flowsGroup,
|
||||
onSelect: () {
|
||||
Navigator.pop(context);
|
||||
for (final h in widget.staticHits) {
|
||||
if (h.label == flowsGroup && h.group == pagesGroup) {
|
||||
h.onSelect();
|
||||
return;
|
||||
hits.add(
|
||||
FaiSearchHit(
|
||||
label: f.name,
|
||||
hint: l.searchSavedFlowHint,
|
||||
icon: Icons.account_tree_outlined,
|
||||
group: flowsGroup,
|
||||
onSelect: () {
|
||||
Navigator.pop(context);
|
||||
for (final h in widget.staticHits) {
|
||||
if (h.label == flowsGroup && h.group == pagesGroup) {
|
||||
h.onSelect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
return hits;
|
||||
}
|
||||
|
|
@ -211,9 +221,7 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
return Dialog(
|
||||
alignment: Alignment.topCenter,
|
||||
insetPadding: const EdgeInsets.only(top: 80, left: 24, right: 24),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 640, maxHeight: 540),
|
||||
child: FutureBuilder<List<FaiSearchHit>>(
|
||||
|
|
@ -240,17 +248,17 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
controller: _query,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon:
|
||||
const Icon(Icons.search, size: 20),
|
||||
prefixIcon: const Icon(Icons.search, size: 20),
|
||||
hintText: l.searchHint,
|
||||
border: InputBorder.none,
|
||||
suffixIcon: snap.connectionState ==
|
||||
ConnectionState.waiting
|
||||
suffixIcon:
|
||||
snap.connectionState == ConnectionState.waiting
|
||||
? const SizedBox(
|
||||
width: 14,
|
||||
height: 14,
|
||||
child:
|
||||
CircularProgressIndicator(strokeWidth: 2),
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
|
|
@ -283,11 +291,10 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
itemBuilder: (context, i) {
|
||||
final h = hits[i];
|
||||
final highlighted = i == _highlight;
|
||||
final showHeader = i == 0 ||
|
||||
hits[i - 1].group != h.group;
|
||||
final showHeader =
|
||||
i == 0 || hits[i - 1].group != h.group;
|
||||
return Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (showHeader)
|
||||
Padding(
|
||||
|
|
@ -301,11 +308,12 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
h.group.toUpperCase(),
|
||||
style: theme.textTheme.labelSmall
|
||||
?.copyWith(
|
||||
color: theme
|
||||
.colorScheme.onSurfaceVariant,
|
||||
letterSpacing: 0.6,
|
||||
fontSize: 10,
|
||||
),
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
letterSpacing: 0.6,
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
|
|
@ -317,8 +325,9 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
},
|
||||
child: Container(
|
||||
color: highlighted
|
||||
? theme.colorScheme
|
||||
.surfaceContainerHigh
|
||||
? theme
|
||||
.colorScheme
|
||||
.surfaceContainerHigh
|
||||
: null,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
|
|
@ -340,21 +349,24 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
Text(
|
||||
h.label,
|
||||
style: theme
|
||||
.textTheme.bodyMedium
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
fontWeight:
|
||||
FontWeight.w500,
|
||||
),
|
||||
fontWeight:
|
||||
FontWeight.w500,
|
||||
),
|
||||
),
|
||||
if (h.hint.isNotEmpty)
|
||||
Text(
|
||||
h.hint,
|
||||
style: theme.textTheme
|
||||
style: theme
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(
|
||||
color: theme.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
color: theme
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow:
|
||||
TextOverflow.ellipsis,
|
||||
|
|
@ -367,7 +379,8 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
Icons.keyboard_return,
|
||||
size: 14,
|
||||
color: theme
|
||||
.colorScheme.onSurfaceVariant,
|
||||
.colorScheme
|
||||
.onSurfaceVariant,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue