0.3.6 — plugin registry, Unreal extracted to a plugin, in-app update check + fixes
- Official plugin registry hosted on Gitea; client fetches registry.json over HTTPS and installs plugins via backend plugin_write_file. - Auto-install autoInstall plugins on first connect; per-machine enabled state. - Extract all Unreal tooling (Build Solution / Unreal Log) into a separate "unreal" plugin (defaultEnabled) driven through the new host.ue bridge; core UI no longer hard-wires Unreal. - Settings → Plugins: "Official plugins" section with Install buttons. - Status bar: "Updates" button (right of Log) triggers an update check without restarting the client. - Fixes: NEW badge hard-right/centered without shifting text; History commit size no longer flickers (cached per change); giant icons in empty plugin/ People lists; Get Latest hover contrast. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@ -966,7 +966,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "exbyte-depot"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"serde",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "exbyte-depot"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
description = "Exbyte Depot — native Perforce client by Exbyte Studios"
|
||||
authors = ["Exbyte Studios"]
|
||||
edition = "2021"
|
||||
|
||||
@ -2923,7 +2923,10 @@ async fn plugin_list() -> Result<Vec<Value>, String> {
|
||||
let Ok(m) = serde_json::from_str::<Value>(&txt) else { continue };
|
||||
let id = m.get("id").and_then(|v| v.as_str()).map(String::from)
|
||||
.unwrap_or_else(|| e.file_name().to_string_lossy().to_string());
|
||||
let enabled = *en.get(&id).unwrap_or(&true);
|
||||
// off by default unless the manifest opts in (defaultEnabled) — official
|
||||
// plugins like Unreal ship on; the user's choice persists in enabled.json
|
||||
let default_on = m.get("defaultEnabled").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
let enabled = *en.get(&id).unwrap_or(&default_on);
|
||||
out.push(serde_json::json!({
|
||||
"id": id,
|
||||
"name": m.get("name").and_then(|v| v.as_str()).unwrap_or(""),
|
||||
@ -2967,6 +2970,23 @@ async fn plugin_set_enabled(id: String, enabled: bool) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write one file of a plugin into <config>/plugins/<id>/<name>. The frontend
|
||||
/// fetches the file bytes from the registry over HTTPS and hands the content here;
|
||||
/// this only touches the confined plugins folder (id + name are path-validated).
|
||||
#[tauri::command]
|
||||
async fn plugin_write_file(id: String, name: String, content: String) -> Result<(), String> {
|
||||
if id.is_empty() || !id.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.') || id.contains("..") {
|
||||
return Err("Invalid plugin id".into());
|
||||
}
|
||||
if name.is_empty() || name.contains('/') || name.contains('\\') || name.contains("..") {
|
||||
return Err("Invalid file name".into());
|
||||
}
|
||||
let dir = plugins_dir().ok_or("No config dir")?.join(&id);
|
||||
std::fs::create_dir_all(&dir).map_err(|e| e.to_string())?;
|
||||
std::fs::write(dir.join(&name), content).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_dir_recursive(from: &std::path::Path, to: &std::path::Path) -> std::io::Result<()> {
|
||||
std::fs::create_dir_all(to)?;
|
||||
for e in std::fs::read_dir(from)? {
|
||||
@ -3125,6 +3145,7 @@ pub fn run() {
|
||||
plugin_list,
|
||||
plugin_read_entry,
|
||||
plugin_set_enabled,
|
||||
plugin_write_file,
|
||||
plugin_install,
|
||||
plugin_remove,
|
||||
plugins_dir_path,
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Exbyte Depot",
|
||||
"mainBinaryName": "Exbyte Depot",
|
||||
"version": "0.3.5",
|
||||
"version": "0.3.6",
|
||||
"identifier": "com.bonchellon.exbyte-depot",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
@ -24,8 +24,8 @@
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": "default-src 'self'; script-src 'self' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: asset: http://asset.localhost; media-src 'self' blob:; font-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost https://openrouter.ai; worker-src 'self' blob:; object-src 'none'; base-uri 'self'",
|
||||
"devCsp": "default-src 'self'; script-src 'self' 'unsafe-inline' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: asset: http://asset.localhost; media-src 'self' blob:; font-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost ws://localhost:1420 http://localhost:1420 https://openrouter.ai; worker-src 'self' blob:; object-src 'none'; base-uri 'self'"
|
||||
"csp": "default-src 'self'; script-src 'self' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: asset: http://asset.localhost; media-src 'self' blob:; font-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost https://openrouter.ai https://gitea.exbytestudios.com; worker-src 'self' blob:; object-src 'none'; base-uri 'self'",
|
||||
"devCsp": "default-src 'self'; script-src 'self' 'unsafe-inline' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: asset: http://asset.localhost; media-src 'self' blob:; font-src 'self' data:; connect-src 'self' ipc: http://ipc.localhost ws://localhost:1420 http://localhost:1420 https://openrouter.ai https://gitea.exbytestudios.com; worker-src 'self' blob:; object-src 'none'; base-uri 'self'"
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
|
||||
Reference in New Issue
Block a user