-
-
Notifications
You must be signed in to change notification settings - Fork 548
Reuse existing tabs in bookmarks plugin #4189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 5 commits
c4549d6
02248eb
20f898c
df900f5
c8bf643
9a2e340
382dd79
ecfe8c3
f2dd4d9
944e3d7
951ef81
f8c1780
7890102
b390645
876cbfd
1f45c02
1bac95d
0f7ef77
791ff47
a6acdd4
d1e5b70
9d27bcf
736e376
75a03d0
cdb2d9e
a472725
36b7799
42a91fb
9fadec3
6c5695b
680de61
a30180c
f901836
c75cb1a
37c7337
84ef5aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Jack251970 marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| [ | ||
| { | ||
| "PackageName": "BrowserTabs", | ||
| "PackageVersion": "0.2.0", | ||
| "PackageUrl": "https://github.com/jjw24/BrowserTabs", | ||
| "Copyright": "Jeremy Wu", | ||
| "Authors": [ "Jeremy Wu" ], | ||
| "Description": "Library for retrieving all opened browser tabs in Chromium-based and Firefox-based browsers", | ||
| "LicenseUrl": "https://licenses.nuget.org/Apache-2.0", | ||
| "LicenseType": "Apache-2.0", | ||
| "Repository": { | ||
| "Type": "", | ||
| "Url": "https://github.com/jjw24/BrowserTabs", | ||
| "Commit": "" | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }, | ||
| { | ||
| "PackageName": "CommunityToolkit.Mvvm", | ||
| "PackageVersion": "8.4.0", | ||
| "PackageUrl": "https://github.com/CommunityToolkit/dotnet", | ||
| "Copyright": "(c) .NET Foundation and Contributors. All rights reserved.", | ||
| "Authors": [ "Microsoft" ], | ||
| "Description": "This package includes a .NET MVVM library with helpers such as:\r\n - ObservableObject: a base class for objects implementing the INotifyPropertyChanged interface.\r\n - ObservableRecipient: a base class for observable objects with support for the IMessenger service.\r\n - ObservableValidator: a base class for objects implementing the INotifyDataErrorInfo interface.\r\n - RelayCommand: a simple delegate command implementing the ICommand interface.\r\n - AsyncRelayCommand: a delegate command supporting asynchronous operations and cancellation.\r\n - WeakReferenceMessenger: a messaging system to exchange messages through different loosely-coupled objects.\r\n - StrongReferenceMessenger: a high-performance messaging system that trades weak references for speed.\r\n - Ioc: a helper class to configure dependency injection service containers.", | ||
| "LicenseUrl": "https://licenses.nuget.org/MIT", | ||
| "LicenseType": "MIT", | ||
| "Repository": { | ||
| "Type": "git", | ||
| "Url": "https://github.com/CommunityToolkit/dotnet", | ||
| "Commit": "638b41dad30dffabb123a39aa38eabc7e3721371" | ||
| } | ||
| }, | ||
| { | ||
| "PackageName": "Flow.Launcher.Localization", | ||
| "PackageVersion": "0.0.6", | ||
| "PackageUrl": "", | ||
| "Copyright": "", | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "Authors": [ "Flow-Launcher" ], | ||
| "Description": "Localization toolkit for Flow Launcher and its plugins", | ||
| "LicenseUrl": "https://licenses.nuget.org/MIT", | ||
| "LicenseType": "MIT", | ||
| "Repository": { | ||
| "Type": "git", | ||
| "Url": "https://github.com/Flow-Launcher/Flow.Launcher.Localization", | ||
| "Commit": "456bdc7a986487d691a3ae8d36f8bce7b88b9bc7" | ||
| } | ||
| }, | ||
| { | ||
| "PackageName": "Microsoft.Data.Sqlite", | ||
| "PackageVersion": "10.0.0", | ||
| "PackageUrl": "https://docs.microsoft.com/dotnet/standard/data/sqlite/", | ||
| "Copyright": "© Microsoft Corporation. All rights reserved.", | ||
| "Authors": [ "Microsoft" ], | ||
| "Description": "Microsoft.Data.Sqlite is a lightweight ADO.NET provider for SQLite.\r\n\r\nCommonly Used Types:\r\nMicrosoft.Data.Sqlite.SqliteCommand\r\nMicrosoft.Data.Sqlite.SqliteConnection\r\nMicrosoft.Data.Sqlite.SqliteConnectionStringBuilder\r\nMicrosoft.Data.Sqlite.SqliteDataReader\r\nMicrosoft.Data.Sqlite.SqliteException\r\nMicrosoft.Data.Sqlite.SqliteFactory\r\nMicrosoft.Data.Sqlite.SqliteParameter\r\nMicrosoft.Data.Sqlite.SqliteTransaction", | ||
| "LicenseUrl": "https://licenses.nuget.org/MIT", | ||
| "LicenseType": "MIT", | ||
| "Repository": { | ||
| "Type": "git", | ||
| "Url": "https://github.com/dotnet/dotnet", | ||
| "Commit": "b0f34d51fccc69fd334253924abd8d6853fad7aa" | ||
| } | ||
| }, | ||
| { | ||
| "PackageName": "SkiaSharp", | ||
| "PackageVersion": "3.119.1", | ||
| "PackageUrl": "https://go.microsoft.com/fwlink/?linkid=868515", | ||
| "Copyright": "© Microsoft Corporation. All rights reserved.", | ||
| "Authors": [ "Microsoft" ], | ||
| "Description": "SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library.\r\nIt provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.", | ||
|
Check warning on line 68 in Plugins/Flow.Launcher.Plugin.BrowserBookmark/THIRD_PARTY_NOTICES.json
|
||
| "LicenseUrl": "https://licenses.nuget.org/MIT", | ||
| "LicenseType": "MIT", | ||
| "Repository": { | ||
| "Type": "git", | ||
| "Url": "https://go.microsoft.com/fwlink/?linkid=868515", | ||
| "Commit": "cc78b5933d23e6383db5d246e70db915770d55d6" | ||
| } | ||
| }, | ||
| { | ||
| "PackageName": "Svg.Skia", | ||
|
Check warning on line 78 in Plugins/Flow.Launcher.Plugin.BrowserBookmark/THIRD_PARTY_NOTICES.json
|
||
| "PackageVersion": "3.2.1", | ||
| "PackageUrl": "https://github.com/wieslawsoltes/Svg.Skia", | ||
| "Copyright": "Copyright © Wiesław Šoltés 2025", | ||
| "Authors": [ "Wiesław Šoltés" ], | ||
|
Check warning on line 82 in Plugins/Flow.Launcher.Plugin.BrowserBookmark/THIRD_PARTY_NOTICES.json
|
||
| "Description": "An SVG rendering library.", | ||
| "LicenseUrl": "https://licenses.nuget.org/MIT", | ||
| "LicenseType": "MIT", | ||
| "Repository": { | ||
| "Type": "git", | ||
| "Url": "https://github.com/wieslawsoltes/Svg.Skia", | ||
| "Commit": "0164d01769a8b577f6dcc678f25d4802a06ff8c0" | ||
| } | ||
| } | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # Third-party notices | ||
|
|
||
| This project uses third-party NuGet packages. | ||
|
|
||
| | Reference | Version | License Type | License | | ||
| |---------------------------------------------------------------------------------------------| | ||
| | BrowserTabs | 0.2.0 | Apache-2.0 | https://licenses.nuget.org/Apache-2.0 | | ||
| | CommunityToolkit.Mvvm | 8.4.0 | MIT | https://licenses.nuget.org/MIT | | ||
| | Flow.Launcher.Localization | 0.0.6 | MIT | https://licenses.nuget.org/MIT | | ||
| | Microsoft.Data.Sqlite | 10.0.0 | MIT | https://licenses.nuget.org/MIT | | ||
| | SkiaSharp | 3.119.1 | MIT | https://licenses.nuget.org/MIT | | ||
|
Check warning on line 11 in Plugins/Flow.Launcher.Plugin.BrowserBookmark/THIRD_PARTY_NOTICES.md
|
||
| | Svg.Skia | 3.2.1 | MIT | https://licenses.nuget.org/MIT | | ||
|
|
||
| Detailed information (package id, version, license, repository URL) is available in [THIRD_PARTY_NOTICES.json](THIRD_PARTY_NOTICES.json). | ||
|
|
||
| # Additional credits | ||
|
|
||
| Initially there was a plan to integrate [Browser Bookmarks plugin](https://github.com/Flow-Launcher/Flow.Launcher/tree/dev/Plugins/Flow.Launcher.Plugin.BrowserBookmark) and [Browser Tabs plugin](https://github.com/Flow-Launcher/Flow.Launcher.Plugin.BrowserTabs) but it looks like inter-plugin communication or integration of plugins is not possible. | ||
| Finally Browser Tabs plugin wasn't used but **it's code had great impact on this final solution**. | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # How to generate the list | ||
|
|
||
| 1. Install `dotnet-project-licenses` | ||
| 1. Use the tool as below | ||
| 1. Copy markdown above | ||
| 1. Rename `licenses.json` to `THIRD_PARTY_NOTICES.json` and format the json | ||
|
|
||
| ``` | ||
| dotnet tool install --global dotnet-project-licenses | ||
| dotnet-project-licenses --input Flow.Launcher.Plugin.BrowserBookmark.csproj --json | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # Context | ||
|
|
||
| Existing plugins focus on their areas of operation - e.g. Browser Bookmarks on bookmarks only, Browser Tabs on tabs only. | ||
| Why not join these two worlds to create synergy? | ||
| Especially if one works with **tens or hundreds of bookmarks and open tabs** (I do and I constantly struggle finding the correct tab. It is underestimated mental cost of finding, clicking several times, etc.). | ||
|
|
||
| That's where "Reuse tabs" setting in Browser Bookmarks plugin makes sense. | ||
|
|
||
| I believe it is in line with why Flow Launcher was created in the first place. | ||
| I strongly believe in a higher level concept of **"just take me to THIS place - as fast as possible, as easy as possible"**. | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Thus making bridges between plugins may sometimes produce huge value! BTW wouldn't it be nice to allow inter-plugin communication to create this kind of "bridges" more easily? | ||
|
|
||
|
|
||
| # How it works | ||
|
|
||
| The core is Browser Bookmarks plugin, unchanged by default. | ||
| You may enable "Reuse tabs" in the plugin settings. | ||
| Then, whenever one opens a bookmark, it also registers a new tab in its cache. | ||
| Next, each time the bookmark is triggered again, it just switches to the existing tab instead of launching a new one. | ||
| **It takes milliseconds instead of long seconds** (or sometimes close to half a minute in corporate environments where all is slow even if you have a high end laptop - you won't believe it until you live it!). | ||
|
|
||
| # Known issues | ||
|
|
||
| The extension bases on RuntimeId of AutomationElement from Microsoft UI Automation. | ||
| This is a weak spot as browsers are used to regenerate internal structures and even reuse RuntimeId. | ||
| It **rarely** happens that a bookmark activates a wrong tab. | ||
| Still **"just take me to THIS place in milliseconds"** works almost all of the time so it bring so much value that it is worthwhile to accepts the fact it fails sometimes. | ||
|
|
||
| The quickest workaround is: | ||
|
|
||
| - close the wrong tab | ||
| - rerun opening the bookmark which will create a new tab this time | ||
|
|
||
| # Alternatives | ||
|
|
||
| Reading URLs of existing tabs was tried. It would make mapping of bookmarks to tabs more reliable. | ||
| However due to security reasons it has several limitations: | ||
|
|
||
| - different browsers exposes internals differently | ||
Jack251970 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - it is not easily accessible (e.g. you cannot make Chrome expose internal details on a dev TCP port from default profile so user would have to take care about special settings). | ||
|
|
||
| _"Reuse tabs" setting was created initially by [Andrzej Martyna](https://github.com/andrzejmartyna)_ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| using System.Collections.Generic; | ||
| using System.Windows.Automation; | ||
|
|
||
| namespace Flow.Launcher.Plugin.BrowserBookmark.Tabs; | ||
|
|
||
| /// <summary> | ||
| /// Keeps record of all known browser's tabs. | ||
| /// It is used by TabsWalker to identify new tabs as they appear. | ||
| /// </summary> | ||
| internal class TabsCache | ||
| { | ||
| private readonly HashSet<string> _knownTabs = new(); | ||
| private readonly object sync = new(); | ||
andrzejmartyna marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| private static string RuntimeIdToKey(AutomationElement elem) => elem != null ? string.Join("-", elem.GetRuntimeId()) : "NULL"; | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| public bool Empty() | ||
| { | ||
| lock (sync) | ||
| { | ||
| return _knownTabs.Count == 0; | ||
| } | ||
| } | ||
|
|
||
| public void Add(AutomationElement tab) | ||
| { | ||
| lock (sync) | ||
| { | ||
| _knownTabs.Add(RuntimeIdToKey(tab)); | ||
| } | ||
| } | ||
|
|
||
| public void Add(IEnumerable<AutomationElement> tabs) | ||
| { | ||
| lock (sync) | ||
| { | ||
| foreach (var tab in tabs) | ||
| { | ||
| _knownTabs.Add(RuntimeIdToKey(tab)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public bool Contains(AutomationElement tab) | ||
| { | ||
| lock (sync) | ||
| { | ||
| return _knownTabs.Contains(RuntimeIdToKey(tab)); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.