Add plugin API to open URL in external browser (#340)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Gregory Schier
2026-01-03 13:53:07 -08:00
committed by GitHub
parent 118b2faa76
commit 35a57bf7f5
8 changed files with 51 additions and 5 deletions

File diff suppressed because one or more lines are too long

View File

@@ -53,6 +53,7 @@ export interface Context {
onClose?: () => void;
},
): Promise<{ close: () => void }>;
openExternalUrl(url: string): Promise<void>;
};
cookies: {
listNames(): Promise<ListCookieNamesResponse['names']>;

View File

@@ -646,6 +646,12 @@ export class PluginInstance {
},
};
},
openExternalUrl: async (url) => {
await this.#sendForReply(context, {
type: 'open_external_url_request',
url,
});
},
},
prompt: {
text: async (args) => {

View File

@@ -41,6 +41,9 @@ pub enum Error {
#[error(transparent)]
ClipboardError(#[from] tauri_plugin_clipboard_manager::Error),
#[error(transparent)]
OpenerError(#[from] tauri_plugin_opener::Error),
#[error("Updater error: {0}")]
UpdaterError(#[from] tauri_plugin_updater::Error),

View File

@@ -11,6 +11,7 @@ use cookie::Cookie;
use log::error;
use tauri::{AppHandle, Emitter, Manager, Runtime};
use tauri_plugin_clipboard_manager::ClipboardExt;
use tauri_plugin_opener::OpenerExt;
use yaak_common::window::WorkspaceWindowTrait;
use yaak_models::blob_manager::BlobManagerExt;
use yaak_models::models::{AnyModel, HttpResponse, Plugin};
@@ -370,6 +371,10 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
}
Ok(None)
}
InternalEventPayload::OpenExternalUrlRequest(req) => {
app_handle.opener().open_url(&req.url, None::<&str>)?;
Ok(Some(InternalEventPayload::OpenExternalUrlResponse(EmptyPayload {})))
}
InternalEventPayload::SetKeyValueRequest(req) => {
let name = plugin_handle.info().name;
app_handle.db().set_plugin_key_value(&name, &req.key, &req.value);

File diff suppressed because one or more lines are too long

View File

@@ -153,6 +153,9 @@ pub enum InternalEventPayload {
WindowCloseEvent,
CloseWindowRequest(CloseWindowRequest),
OpenExternalUrlRequest(OpenExternalUrlRequest),
OpenExternalUrlResponse(EmptyPayload),
ShowToastRequest(ShowToastRequest),
ShowToastResponse(EmptyPayload),
@@ -492,6 +495,13 @@ pub struct OpenWindowRequest {
pub data_dir_key: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "gen_events.ts")]
pub struct OpenExternalUrlRequest {
pub url: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
#[serde(default, rename_all = "camelCase")]
#[ts(export, export_to = "gen_events.ts")]

View File

@@ -195,7 +195,24 @@ impl PluginManager {
}
}
Ok(app_handle.db().list_plugins()?)
// Filter to only "available" plugins for this runtime context.
// The database stores all plugins globally (to persist enabled/disabled state),
// but we only want to load plugins that are actually present:
// 1. Bundled plugins from the current build/worktree
// 2. User-installed plugins (always available)
// This prevents duplicate plugin loading when switching between worktrees or builds.
let all_plugins = app_handle.db().list_plugins()?;
let available_plugins: Vec<Plugin> = all_plugins
.into_iter()
.filter(|plugin| {
let dir_path = Path::new(&plugin.directory);
let is_bundled = bundled_plugin_dirs.contains(&plugin.directory);
let is_installed = dir_path.starts_with(self.installed_plugin_dir.as_path());
is_bundled || is_installed
})
.collect();
Ok(available_plugins)
}
pub async fn uninstall(&self, plugin_context: &PluginContext, dir: &str) -> Result<()> {