feat(studio): Cmd+K palette, recent-activity panel, icon/screenshot rendering (v0.21.0)
Three sweeps in a single bundle. Universal command palette (⌘K / ctrl+K): - Modal at top-of-window with a single search input. Indexes pages, installed modules, store entries, saved flows on open; filters client-side as the operator types. - Keyboard nav (↑/↓ + Enter), grouped sections, hover-to- highlight. Hit selection navigates / opens the right surface in one keystroke. Closes with Esc. - Designed to mirror the VSCode / Linear / 1Password ergonomics — gives non-CLI operators a "jump anywhere" affordance that scales with the number of installed modules. Modules page recent-activity panel: - Top-of-page strip lists the last 10 install / uninstall events from the audit log, color-coded by direction. Hidden when no relevant events exist (fresh installs). Same locale-unambiguous timestamp format used in audit. Store detail sheet now renders icons + screenshots: - `_ModuleIcon` widget loads the explicit `iconUrl` if provided, falls back to the category glyph on missing URL or load failure (no broken-image rectangle). - Screenshots strip below the description: 320×200 tiles, horizontally-scrollable, click opens full-size via the OS handler. Placeholder card on load failure. StoreItem extended with `iconUrl`, `screenshotUrls`, `docsUrl` so the new content paths through unchanged. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
fb7352d182
commit
11b0ac97a1
7 changed files with 803 additions and 30 deletions
|
|
@ -19,12 +19,13 @@ import 'pages/modules.dart';
|
|||
import 'pages/store.dart';
|
||||
import 'theme/theme.dart';
|
||||
import 'theme/tokens.dart';
|
||||
import 'widgets/fai_search_palette.dart';
|
||||
import 'widgets/widgets.dart';
|
||||
|
||||
/// Studio's own build version. Bump on every UI commit so the
|
||||
/// running app self-identifies — visible in the sidebar header
|
||||
/// and quick-glance proof that you're seeing the current build.
|
||||
const String kStudioVersion = '0.20.1';
|
||||
const String kStudioVersion = '0.21.0';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
|
@ -189,6 +190,33 @@ class _StudioShellState extends State<StudioShell> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
void _openSearchPalette() {
|
||||
final hits = <FaiSearchHit>[
|
||||
for (var i = 0; i < _pages.length; i++)
|
||||
FaiSearchHit(
|
||||
label: _pages[i].label,
|
||||
hint: 'page · ⌘${i + 1}',
|
||||
icon: _pages[i].icon,
|
||||
group: 'Pages',
|
||||
onSelect: () {
|
||||
Navigator.of(context).pop();
|
||||
setState(() => _selectedIndex = i);
|
||||
},
|
||||
),
|
||||
FaiSearchHit(
|
||||
label: 'Settings',
|
||||
hint: 'hub endpoint, channels, system AI · ⌘;',
|
||||
icon: Icons.settings_outlined,
|
||||
group: 'Pages',
|
||||
onSelect: () {
|
||||
Navigator.of(context).pop();
|
||||
FaiSettingsDialog.show(context);
|
||||
},
|
||||
),
|
||||
];
|
||||
FaiSearchPalette.show(context, staticHits: hits);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
|
@ -212,6 +240,12 @@ class _StudioShellState extends State<StudioShell> {
|
|||
const _OpenSettingsIntent(),
|
||||
const SingleActivator(LogicalKeyboardKey.comma, meta: true):
|
||||
const _OpenSettingsIntent(),
|
||||
// Universal command palette. Mirrors Cmd+K from VSCode /
|
||||
// Linear / 1Password — operators jump anywhere by typing.
|
||||
const SingleActivator(LogicalKeyboardKey.keyK, meta: true):
|
||||
const _OpenSearchIntent(),
|
||||
const SingleActivator(LogicalKeyboardKey.keyK, control: true):
|
||||
const _OpenSearchIntent(),
|
||||
},
|
||||
child: Actions(
|
||||
actions: <Type, Action<Intent>>{
|
||||
|
|
@ -227,6 +261,12 @@ class _StudioShellState extends State<StudioShell> {
|
|||
return null;
|
||||
},
|
||||
),
|
||||
_OpenSearchIntent: CallbackAction<_OpenSearchIntent>(
|
||||
onInvoke: (_) {
|
||||
_openSearchPalette();
|
||||
return null;
|
||||
},
|
||||
),
|
||||
},
|
||||
child: Focus(
|
||||
autofocus: true,
|
||||
|
|
@ -285,6 +325,10 @@ class _OpenSettingsIntent extends Intent {
|
|||
const _OpenSettingsIntent();
|
||||
}
|
||||
|
||||
class _OpenSearchIntent extends Intent {
|
||||
const _OpenSearchIntent();
|
||||
}
|
||||
|
||||
class _Sidebar extends StatelessWidget {
|
||||
final int selectedIndex;
|
||||
final ValueChanged<int> onSelect;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue