diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 2b40b5c..1707f69 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -51,5 +51,16 @@
UIFileSharingEnabled
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+ peregrine_url
+ CFBundleURLSchemes
+
+ peregrine
+
+
+
diff --git a/lib/context_menus.dart b/lib/context_menus.dart
index ffc1355..7368251 100644
--- a/lib/context_menus.dart
+++ b/lib/context_menus.dart
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:super_context_menu/super_context_menu.dart' as scm;
import 'export_utils.dart';
@@ -69,6 +70,10 @@ scm.Menu buildEntryCardContextMenu({
title: 'Add as ancestor',
callback: () => addAncestorCallback(entryId),
),
+ scm.MenuAction(
+ title: 'Copy Entry URL',
+ callback: () => Clipboard.setData(ClipboardData(text: 'peregrine://peregrine/entry/$entryId')),
+ ),
scm.MenuAction(
title: isEncrypted ? 'Decrypt Entry' : 'Encrypt Entry',
callback: () => !isEncrypted ? toggleEncryptCallback() : null,
diff --git a/lib/main.dart b/lib/main.dart
index bbf2138..51d9d97 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,3 +1,4 @@
+import 'package:app_links/app_links.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:path_provider/path_provider.dart';
@@ -76,10 +77,13 @@ final currentAncestorsProvider =
StateNotifierProvider>(
(_) => CurrentAncestors(),
);
+final currentJumpIdProvider = StateProvider((ref) => '');
FocusNode entryBoxFocusNode = FocusNode();
FocusNode searchBoxFocusNode = FocusNode();
+final appLinks = AppLinks();
+
void main() async {
WidgetsFlutterBinding.ensureInitialized();
platformAppSupportDir = (await getApplicationDocumentsDirectory()).path;
@@ -107,8 +111,23 @@ class MyApp extends ConsumerWidget {
class PeregrineHomeView extends ConsumerWidget {
const PeregrineHomeView({super.key});
+ void _handleUrl(uri, ref) {
+ final uriParts = uri.pathSegments;
+ if (uriParts[0] != 'entry') return;
+
+ final entries = ref.read(filteredListProvider).keys.toList();
+ final entryIndex = entries.indexOf(uriParts[1]);
+ if (entryIndex == -1) {
+ ref.read(entryFilterProvider.notifier).setAllEntriesFilter();
+ }
+ ref
+ .read(currentJumpIdProvider.notifier)
+ .update((String state) => (uriParts[1]).toString());
+ }
+
@override
Widget build(BuildContext context, WidgetRef ref) {
+ appLinks.uriLinkStream.listen((uri) => _handleUrl(uri, ref));
final view = PretMainView(
leftSidebar: Sidebar(
searchBoxFocusNode: searchBoxFocusNode,
@@ -127,7 +146,7 @@ class PeregrineHomeView extends ConsumerWidget {
return Scaffold(
backgroundColor: const Color(0xffb69d7c),
body: PretCmdPaletteScope(
- searchItems: ref.read(tagsProvider).keys.toList(),
+ searchItems: ref.watch(tagsProvider).keys.toList(),
child: DesktopFrame(
entryBoxFocusNode: entryBoxFocusNode,
searchBoxFocusNode: searchBoxFocusNode,
diff --git a/lib/widgets/entry_list_view.dart b/lib/widgets/entry_list_view.dart
index c41833c..b26b539 100644
--- a/lib/widgets/entry_list_view.dart
+++ b/lib/widgets/entry_list_view.dart
@@ -27,6 +27,7 @@ class EntryListView extends ConsumerStatefulWidget {
class EntryListViewState extends ConsumerState {
final _scrollController = ScrollController();
+ final _listController = ListController();
void scrollToBottom() => _scrollController.jumpTo(
_scrollController.position.maxScrollExtent,
@@ -35,6 +36,18 @@ class EntryListViewState extends ConsumerState {
@override
Widget build(BuildContext context) {
var entries = ref.watch(filteredListProvider).keys.toList();
+ final currentJumpId = ref.watch(currentJumpIdProvider);
+
+ Future(() {
+ if (currentJumpId != '') {
+ ref.read(currentJumpIdProvider.notifier).update((state) => '');
+ _listController.jumpToItem(
+ index: entries.indexOf(currentJumpId),
+ scrollController: _scrollController,
+ alignment: 0.5);
+ }
+ });
+
final listView = buildEntryList(entries);
return Scaffold(
floatingActionButton: Container(
@@ -111,6 +124,7 @@ class EntryListViewState extends ConsumerState {
padding: EdgeInsets.all(PretConfig.defaultElementSpacing * 1.5),
),
SuperSliverList.builder(
+ listController: _listController,
itemCount: entries.length,
itemBuilder: (context, index) =>
PeregrineEntryCard(entryId: entries[index]))
diff --git a/lib/widgets/pret_command_palette.dart b/lib/widgets/pret_command_palette.dart
index da10784..885febf 100644
--- a/lib/widgets/pret_command_palette.dart
+++ b/lib/widgets/pret_command_palette.dart
@@ -33,6 +33,8 @@ class PretCmdPaletteScopeState extends ConsumerState {
void togglePalette() => setState(() {
_isPaletteShown = !_isPaletteShown;
+ _controller.clear();
+ filteredItems = widget.searchItems;
selectedIndex = 0;
_isPaletteShown ? paletteFocusNode.requestFocus() : null;
});
diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc
index ea69a16..5053509 100644
--- a/linux/flutter/generated_plugin_registrant.cc
+++ b/linux/flutter/generated_plugin_registrant.cc
@@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h"
#include
+#include
#include
#include
#include
@@ -15,6 +16,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) contextual_menu_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ContextualMenuPlugin");
contextual_menu_plugin_register_with_registrar(contextual_menu_registrar);
+ g_autoptr(FlPluginRegistrar) gtk_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin");
+ gtk_plugin_register_with_registrar(gtk_registrar);
g_autoptr(FlPluginRegistrar) irondash_engine_context_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "IrondashEngineContextPlugin");
irondash_engine_context_plugin_register_with_registrar(irondash_engine_context_registrar);
diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake
index eeb12bb..d479700 100644
--- a/linux/flutter/generated_plugins.cmake
+++ b/linux/flutter/generated_plugins.cmake
@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
contextual_menu
+ gtk
irondash_engine_context
super_native_extensions
url_launcher_linux
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
index 00de2ad..b488065 100644
--- a/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -5,6 +5,7 @@
import FlutterMacOS
import Foundation
+import app_links
import contextual_menu
import device_info_plus
import irondash_engine_context
@@ -13,6 +14,7 @@ import super_native_extensions
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+ AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
ContextualMenuPlugin.register(with: registry.registrar(forPlugin: "ContextualMenuPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin"))
diff --git a/macos/Podfile.lock b/macos/Podfile.lock
index 913ab43..67dae12 100644
--- a/macos/Podfile.lock
+++ b/macos/Podfile.lock
@@ -1,4 +1,6 @@
PODS:
+ - app_links (1.0.0):
+ - FlutterMacOS
- contextual_menu (0.0.1):
- FlutterMacOS
- device_info_plus (0.0.1):
@@ -15,6 +17,7 @@ PODS:
- FlutterMacOS
DEPENDENCIES:
+ - app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`)
- contextual_menu (from `Flutter/ephemeral/.symlinks/plugins/contextual_menu/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
@@ -24,6 +27,8 @@ DEPENDENCIES:
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
EXTERNAL SOURCES:
+ app_links:
+ :path: Flutter/ephemeral/.symlinks/plugins/app_links/macos
contextual_menu:
:path: Flutter/ephemeral/.symlinks/plugins/contextual_menu/macos
device_info_plus:
@@ -40,6 +45,7 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
SPEC CHECKSUMS:
+ app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a
contextual_menu: 6d35fb15942c0d8769f5eae8d3bda653994a6f5a
device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
@@ -50,4 +56,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
-COCOAPODS: 1.15.2
+COCOAPODS: 1.16.2
diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist
index 4789daa..c672ff1 100644
--- a/macos/Runner/Info.plist
+++ b/macos/Runner/Info.plist
@@ -28,5 +28,16 @@
MainMenu
NSPrincipalClass
NSApplication
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+ peregrine_url
+ CFBundleURLSchemes
+
+ peregrine
+
+
+
diff --git a/pubspec.lock b/pubspec.lock
index 438beed..c242301 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -30,6 +30,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.11.3"
+ app_links:
+ dependency: "direct main"
+ description:
+ name: app_links
+ sha256: "433df2e61b10519407475d7f69e470789d23d593f28224c38ba1068597be7950"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.3.3"
+ app_links_linux:
+ dependency: transitive
+ description:
+ name: app_links_linux
+ sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.3"
+ app_links_platform_interface:
+ dependency: transitive
+ description:
+ name: app_links_platform_interface
+ sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.2"
+ app_links_web:
+ dependency: transitive
+ description:
+ name: app_links_web
+ sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
archive:
dependency: transitive
description:
@@ -317,6 +349,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
+ gtk:
+ dependency: transitive
+ description:
+ name: gtk
+ sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
highlight:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 634cd62..d8f72c8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -48,6 +48,7 @@ dependencies:
file_picker: ^5.5.0
super_sliver_list: ^0.4.1
super_context_menu: ^0.8.15
+ app_links: ^6.3.3
dev_dependencies:
flutter_test:
diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc
index 5dcc333..53ffc77 100644
--- a/windows/flutter/generated_plugin_registrant.cc
+++ b/windows/flutter/generated_plugin_registrant.cc
@@ -6,12 +6,15 @@
#include "generated_plugin_registrant.h"
+#include
#include
#include
#include
#include
void RegisterPlugins(flutter::PluginRegistry* registry) {
+ AppLinksPluginCApiRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
ContextualMenuPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ContextualMenuPlugin"));
IrondashEngineContextPluginCApiRegisterWithRegistrar(
diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake
index b75e7fa..8053410 100644
--- a/windows/flutter/generated_plugins.cmake
+++ b/windows/flutter/generated_plugins.cmake
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
+ app_links
contextual_menu
irondash_engine_context
super_native_extensions