feat(store): group grid by canonical category + Modules/Studio segment

The store grid was a flat jumble. It now renders labelled sections per
canonical category (App-Store style, fixed order, with counts), and a
top 'Modules | Studio & Themes' segment splits flow modules from Studio
plugins/themes. StoreItem carries the hub's canonical_category(+label);
falls back to the raw category label for a pre-0.21 hub.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-07-09 17:18:40 +02:00
parent 07432055a8
commit 4b30589962
2 changed files with 156 additions and 24 deletions

View file

@ -787,6 +787,8 @@ class HubService {
provider: e.provider,
sourceKind: e.sourceKind,
source: e.source,
canonicalCategory: e.canonicalCategory,
canonicalCategoryLabel: e.canonicalCategoryLabel,
),
)
.toList();
@ -1800,6 +1802,20 @@ class StoreItem {
/// label and group modules by where they came from.
final String source;
/// Normalized category slug from the hub (e.g. "data",
/// "studio-themes"). The whole catalogue is mapped onto a fixed
/// taxonomy by capability namespace, so different stores' free-form
/// labels collapse into one bucket. Studio groups + filters by this,
/// not [category]. Empty from a pre-0.21 hub fall back to [category].
final String canonicalCategory;
/// Human-readable label for [canonicalCategory] (e.g. "Data & Formats").
final String canonicalCategoryLabel;
/// True iff this entry is a Studio plugin/theme rather than a flow
/// module drives the "Modules | Studio & Themes" store segment.
bool get isStudioPlugin => canonicalCategory == 'studio-themes';
bool get isFederated => kind == 'federated';
const StoreItem({
@ -1825,5 +1841,7 @@ class StoreItem {
required this.provider,
this.sourceKind = '',
this.source = '',
this.canonicalCategory = '',
this.canonicalCategoryLabel = '',
});
}