✨ Better command pattle
This commit is contained in:
@@ -9,6 +9,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:image_picker_android/image_picker_android.dart';
|
import 'package:image_picker_android/image_picker_android.dart';
|
||||||
import 'package:island/talker.dart';
|
import 'package:island/talker.dart';
|
||||||
import 'package:island/firebase_options.dart';
|
import 'package:island/firebase_options.dart';
|
||||||
@@ -53,7 +54,8 @@ void main() async {
|
|||||||
|
|
||||||
if (!kIsWeb && (Platform.isLinux || Platform.isMacOS || Platform.isWindows)) {
|
if (!kIsWeb && (Platform.isLinux || Platform.isMacOS || Platform.isWindows)) {
|
||||||
talker.info("[SplashScreen] Initializing desktop window manager...");
|
talker.info("[SplashScreen] Initializing desktop window manager...");
|
||||||
await protocolHandler.register('myprotocol');
|
await protocolHandler.register('solian');
|
||||||
|
await hotKeyManager.unregisterAll();
|
||||||
talker.info("[SplashScreen] Desktop window manager is ready!");
|
talker.info("[SplashScreen] Desktop window manager is ready!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:gap/gap.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:island/route.dart';
|
import 'package:island/route.dart';
|
||||||
import 'package:island/pods/userinfo.dart';
|
import 'package:island/pods/userinfo.dart';
|
||||||
@@ -39,7 +40,6 @@ class WindowScaffold extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final isMaximized = useState(false);
|
final isMaximized = useState(false);
|
||||||
final showPalette = useState(false);
|
final showPalette = useState(false);
|
||||||
final lastShiftTime = useState<DateTime?>(null);
|
|
||||||
final keyboardFocusNode = useFocusNode();
|
final keyboardFocusNode = useFocusNode();
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
@@ -111,29 +111,48 @@ class WindowScaffold extends HookConsumerWidget {
|
|||||||
const Gap(8),
|
const Gap(8),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
final popHotKey = HotKey(
|
||||||
|
identifier: 'return_previous_page',
|
||||||
|
key: PhysicalKeyboardKey.escape,
|
||||||
|
scope: HotKeyScope.inapp,
|
||||||
|
);
|
||||||
|
final cmpHotKey = HotKey(
|
||||||
|
identifier: 'open_command_pattle',
|
||||||
|
key: PhysicalKeyboardKey.tab,
|
||||||
|
modifiers: [HotKeyModifier.shift],
|
||||||
|
scope: HotKeyScope.inapp,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
hotKeyManager.register(
|
||||||
|
popHotKey,
|
||||||
|
keyDownHandler: (_) {
|
||||||
|
if (closeTopmostOverlayDialog()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no overlay to close, pop the route
|
||||||
|
if (ref.watch(routerProvider).canPop()) {
|
||||||
|
ref.read(routerProvider).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
hotKeyManager.register(
|
||||||
|
cmpHotKey,
|
||||||
|
keyDownHandler: (_) {
|
||||||
|
showPalette.value = true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return () {
|
||||||
|
hotKeyManager.unregister(popHotKey);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (!kIsWeb &&
|
if (!kIsWeb &&
|
||||||
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
|
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
|
||||||
return Shortcuts(
|
return Material(
|
||||||
shortcuts: <LogicalKeySet, Intent>{
|
|
||||||
LogicalKeySet(LogicalKeyboardKey.escape): const PopIntent(),
|
|
||||||
},
|
|
||||||
child: KeyboardListener(
|
|
||||||
focusNode: keyboardFocusNode,
|
|
||||||
onKeyEvent: (event) {
|
|
||||||
if (event is KeyDownEvent &&
|
|
||||||
(event.logicalKey == LogicalKeyboardKey.shiftLeft ||
|
|
||||||
event.logicalKey == LogicalKeyboardKey.shiftRight)) {
|
|
||||||
final now = DateTime.now();
|
|
||||||
if (lastShiftTime.value != null &&
|
|
||||||
now.difference(lastShiftTime.value!).inMilliseconds < 300) {
|
|
||||||
showPalette.value = true;
|
|
||||||
}
|
|
||||||
lastShiftTime.value = now;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Actions(
|
|
||||||
actions: <Type, Action<Intent>>{PopIntent: PopAction(ref)},
|
|
||||||
child: Material(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
@@ -158,9 +177,7 @@ class WindowScaffold extends HookConsumerWidget {
|
|||||||
'Solar Network',
|
'Solar Network',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
context,
|
|
||||||
).colorScheme.onSurface,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -231,38 +248,13 @@ class WindowScaffold extends HookConsumerWidget {
|
|||||||
_WebSocketIndicator(),
|
_WebSocketIndicator(),
|
||||||
const UploadOverlay(),
|
const UploadOverlay(),
|
||||||
if (showPalette.value)
|
if (showPalette.value)
|
||||||
CommandPattleWidget(
|
CommandPattleWidget(onDismiss: () => showPalette.value = false),
|
||||||
onDismiss: () => showPalette.value = false,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Shortcuts(
|
return Stack(
|
||||||
shortcuts: <LogicalKeySet, Intent>{
|
|
||||||
LogicalKeySet(LogicalKeyboardKey.escape): const PopIntent(),
|
|
||||||
},
|
|
||||||
child: KeyboardListener(
|
|
||||||
focusNode: keyboardFocusNode,
|
|
||||||
onKeyEvent: (event) {
|
|
||||||
if (event is KeyDownEvent &&
|
|
||||||
(event.logicalKey == LogicalKeyboardKey.shiftLeft ||
|
|
||||||
event.logicalKey == LogicalKeyboardKey.shiftRight)) {
|
|
||||||
final now = DateTime.now();
|
|
||||||
if (lastShiftTime.value != null &&
|
|
||||||
now.difference(lastShiftTime.value!).inMilliseconds < 300) {
|
|
||||||
showPalette.value = true;
|
|
||||||
}
|
|
||||||
lastShiftTime.value = now;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Actions(
|
|
||||||
actions: <Type, Action<Intent>>{PopIntent: PopAction(ref)},
|
|
||||||
child: Stack(
|
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(child: child),
|
Positioned.fill(child: child),
|
||||||
@@ -271,9 +263,6 @@ class WindowScaffold extends HookConsumerWidget {
|
|||||||
if (showPalette.value)
|
if (showPalette.value)
|
||||||
CommandPattleWidget(onDismiss: () => showPalette.value = false),
|
CommandPattleWidget(onDismiss: () => showPalette.value = false),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,29 +394,6 @@ class AppScaffold extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PopIntent extends Intent {
|
|
||||||
const PopIntent();
|
|
||||||
}
|
|
||||||
|
|
||||||
class PopAction extends Action<PopIntent> {
|
|
||||||
final WidgetRef ref;
|
|
||||||
|
|
||||||
PopAction(this.ref);
|
|
||||||
|
|
||||||
@override
|
|
||||||
void invoke(PopIntent intent) {
|
|
||||||
// First, try to close any overlay dialogs
|
|
||||||
if (closeTopmostOverlayDialog()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no overlay to close, pop the route
|
|
||||||
if (ref.watch(routerProvider).canPop()) {
|
|
||||||
ref.read(routerProvider).pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PageBackButton extends StatelessWidget {
|
class PageBackButton extends StatelessWidget {
|
||||||
final Color? color;
|
final Color? color;
|
||||||
final List<Shadow>? shadows;
|
final List<Shadow>? shadows;
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
final focusNode = useFocusNode();
|
final focusNode = useFocusNode();
|
||||||
final searchQuery = useState('');
|
final searchQuery = useState('');
|
||||||
final focusedIndex = useState<int?>(null);
|
final focusedIndex = useState<int?>(null);
|
||||||
|
final scrollController = useScrollController();
|
||||||
|
|
||||||
final animationController = useAnimationController(
|
final animationController = useAnimationController(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
@@ -271,6 +272,48 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
// Combine results: chats first, then routes
|
// Combine results: chats first, then routes
|
||||||
final allResults = [...filteredChats, ...filteredRoutes];
|
final allResults = [...filteredChats, ...filteredRoutes];
|
||||||
|
|
||||||
|
// Update focused index when results change
|
||||||
|
useEffect(() {
|
||||||
|
if (allResults.isNotEmpty && focusedIndex.value == null) {
|
||||||
|
focusedIndex.value = 0;
|
||||||
|
} else if (allResults.isEmpty) {
|
||||||
|
focusedIndex.value = null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [allResults]);
|
||||||
|
|
||||||
|
// Scroll to focused item
|
||||||
|
useEffect(() {
|
||||||
|
if (focusedIndex.value != null && allResults.isNotEmpty) {
|
||||||
|
// Wait for the next frame to ensure ScrollController is attached
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (scrollController.hasClients) {
|
||||||
|
// Estimate item height (ListTile is typically around 72-88 pixels)
|
||||||
|
const double estimatedItemHeight = 80.0;
|
||||||
|
final double itemTopOffset =
|
||||||
|
focusedIndex.value! * estimatedItemHeight;
|
||||||
|
final double viewportHeight =
|
||||||
|
scrollController.position.viewportDimension;
|
||||||
|
final double centeredOffset =
|
||||||
|
itemTopOffset -
|
||||||
|
(viewportHeight / 2) +
|
||||||
|
(estimatedItemHeight / 2);
|
||||||
|
|
||||||
|
// Animate scroll to center the focused item
|
||||||
|
scrollController.animateTo(
|
||||||
|
centeredOffset.clamp(
|
||||||
|
0.0,
|
||||||
|
scrollController.position.maxScrollExtent,
|
||||||
|
),
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
curve: Curves.easeOut,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [focusedIndex.value]);
|
||||||
|
|
||||||
return KeyboardListener(
|
return KeyboardListener(
|
||||||
focusNode: FocusNode(),
|
focusNode: FocusNode(),
|
||||||
onKeyEvent: (event) {
|
onKeyEvent: (event) {
|
||||||
@@ -280,15 +323,12 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
} else if (isDesktop()) {
|
} else if (isDesktop()) {
|
||||||
if (event.logicalKey == LogicalKeyboardKey.enter ||
|
if (event.logicalKey == LogicalKeyboardKey.enter ||
|
||||||
event.logicalKey == LogicalKeyboardKey.numpadEnter) {
|
event.logicalKey == LogicalKeyboardKey.numpadEnter) {
|
||||||
if (focusedIndex.value != null &&
|
final item = allResults[focusedIndex.value ?? 0];
|
||||||
focusedIndex.value! < allResults.length) {
|
|
||||||
final item = allResults[focusedIndex.value!];
|
|
||||||
if (item is SnChatRoom) {
|
if (item is SnChatRoom) {
|
||||||
_navigateToChat(context, ref, item);
|
_navigateToChat(context, ref, item);
|
||||||
} else if (item is RouteItem) {
|
} else if (item is RouteItem) {
|
||||||
_navigateToRoute(context, ref, item);
|
_navigateToRoute(context, ref, item);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
|
} else if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
|
||||||
if (allResults.isNotEmpty) {
|
if (allResults.isNotEmpty) {
|
||||||
if (focusedIndex.value == null) {
|
if (focusedIndex.value == null) {
|
||||||
@@ -358,16 +398,6 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
child: const Icon(Symbols.keyboard_command_key),
|
child: const Icon(Symbols.keyboard_command_key),
|
||||||
).padding(horizontal: 8),
|
).padding(horizontal: 8),
|
||||||
onSubmitted: (_) {
|
|
||||||
if (allResults.isNotEmpty) {
|
|
||||||
final item = allResults.first;
|
|
||||||
if (item is SnChatRoom) {
|
|
||||||
_navigateToChat(context, ref, item);
|
|
||||||
} else if (item is RouteItem) {
|
|
||||||
_navigateToRoute(context, ref, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
AnimatedSize(
|
AnimatedSize(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
@@ -378,6 +408,7 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
maxHeight: 300,
|
maxHeight: 300,
|
||||||
),
|
),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
controller: scrollController,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: allResults.length,
|
itemCount: allResults.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
@@ -426,6 +457,7 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
void _navigateToChat(BuildContext context, WidgetRef ref, SnChatRoom room) {
|
void _navigateToChat(BuildContext context, WidgetRef ref, SnChatRoom room) {
|
||||||
onDismiss();
|
onDismiss();
|
||||||
if (isWideScreen(context)) {
|
if (isWideScreen(context)) {
|
||||||
|
debugPrint('${room.name}');
|
||||||
ref
|
ref
|
||||||
.read(routerProvider)
|
.read(routerProvider)
|
||||||
.replaceNamed('chatRoom', pathParameters: {'id': room.id});
|
.replaceNamed('chatRoom', pathParameters: {'id': room.id});
|
||||||
@@ -455,18 +487,23 @@ class _RouteSearchResult extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListTile(
|
return Container(
|
||||||
tileColor: isFocused
|
decoration: BoxDecoration(
|
||||||
|
color: isFocused
|
||||||
? Theme.of(context).colorScheme.surfaceContainerHighest
|
? Theme.of(context).colorScheme.surfaceContainerHighest
|
||||||
: null,
|
: null,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(24)),
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
child: Icon(route.icon),
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
foregroundColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
foregroundColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
|
child: Icon(route.icon),
|
||||||
),
|
),
|
||||||
title: Text(route.name),
|
title: Text(route.name),
|
||||||
subtitle: Text(route.description),
|
subtitle: Text(route.description),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,10 +615,14 @@ class _ChatRoomSearchResult extends HookConsumerWidget {
|
|||||||
|
|
||||||
final isDirect = room.type == 1;
|
final isDirect = room.type == 1;
|
||||||
|
|
||||||
return ListTile(
|
return Container(
|
||||||
tileColor: isFocused
|
decoration: BoxDecoration(
|
||||||
|
color: isFocused
|
||||||
? Theme.of(context).colorScheme.surfaceContainerHighest
|
? Theme.of(context).colorScheme.surfaceContainerHighest
|
||||||
: null,
|
: null,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(24)),
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
leading: Badge(
|
leading: Badge(
|
||||||
isLabelVisible: summary.maybeWhen(
|
isLabelVisible: summary.maybeWhen(
|
||||||
data: (data) => (data?.unreadCount ?? 0) > 0,
|
data: (data) => (data?.unreadCount ?? 0) > 0,
|
||||||
@@ -602,6 +643,7 @@ class _ChatRoomSearchResult extends HookConsumerWidget {
|
|||||||
title: Text(titleText),
|
title: Text(titleText),
|
||||||
subtitle: buildSubtitle(),
|
subtitle: buildSubtitle(),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,6 +169,10 @@ PODS:
|
|||||||
- GoogleUtilities/UserDefaults (8.1.0):
|
- GoogleUtilities/UserDefaults (8.1.0):
|
||||||
- GoogleUtilities/Logger
|
- GoogleUtilities/Logger
|
||||||
- GoogleUtilities/Privacy
|
- GoogleUtilities/Privacy
|
||||||
|
- HotKey (0.2.1)
|
||||||
|
- hotkey_manager_macos (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- HotKey
|
||||||
- irondash_engine_context (0.0.1):
|
- irondash_engine_context (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- KeychainAccess (4.2.2)
|
- KeychainAccess (4.2.2)
|
||||||
@@ -274,6 +278,7 @@ DEPENDENCIES:
|
|||||||
- flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`)
|
- flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- gal (from `Flutter/ephemeral/.symlinks/plugins/gal/darwin`)
|
- gal (from `Flutter/ephemeral/.symlinks/plugins/gal/darwin`)
|
||||||
|
- hotkey_manager_macos (from `Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos`)
|
||||||
- irondash_engine_context (from `Flutter/ephemeral/.symlinks/plugins/irondash_engine_context/macos`)
|
- irondash_engine_context (from `Flutter/ephemeral/.symlinks/plugins/irondash_engine_context/macos`)
|
||||||
- livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`)
|
- livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`)
|
||||||
- local_auth_darwin (from `Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin`)
|
- local_auth_darwin (from `Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin`)
|
||||||
@@ -312,6 +317,7 @@ SPEC REPOS:
|
|||||||
- GoogleAppMeasurement
|
- GoogleAppMeasurement
|
||||||
- GoogleDataTransport
|
- GoogleDataTransport
|
||||||
- GoogleUtilities
|
- GoogleUtilities
|
||||||
|
- HotKey
|
||||||
- KeychainAccess
|
- KeychainAccess
|
||||||
- nanopb
|
- nanopb
|
||||||
- OrderedSet
|
- OrderedSet
|
||||||
@@ -359,6 +365,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral
|
:path: Flutter/ephemeral
|
||||||
gal:
|
gal:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/gal/darwin
|
:path: Flutter/ephemeral/.symlinks/plugins/gal/darwin
|
||||||
|
hotkey_manager_macos:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/hotkey_manager_macos/macos
|
||||||
irondash_engine_context:
|
irondash_engine_context:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/irondash_engine_context/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/irondash_engine_context/macos
|
||||||
livekit_client:
|
livekit_client:
|
||||||
@@ -437,6 +445,8 @@ SPEC CHECKSUMS:
|
|||||||
GoogleAppMeasurement: 3bf40aff49a601af5da1c3345702fcb4991d35ee
|
GoogleAppMeasurement: 3bf40aff49a601af5da1c3345702fcb4991d35ee
|
||||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||||
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
|
||||||
|
HotKey: 400beb7caa29054ea8d864c96f5ba7e5b4852277
|
||||||
|
hotkey_manager_macos: a4317849af96d2430fa89944d3c58977ca089fbe
|
||||||
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
|
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
|
||||||
KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51
|
KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51
|
||||||
livekit_client: 3df5a1787d64010ca56c4002959d9e47c03ba3fb
|
livekit_client: 3df5a1787d64010ca56c4002959d9e47c03ba3fb
|
||||||
|
|||||||
Reference in New Issue
Block a user